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 "../common/triggers/subs.qh"
26 #include "mutators/mutators_include.qh"
27 #include "tturrets/include/turrets_early.qh"
28 #include "../common/mapinfo.qh"
29 #include "command/common.qh"
30 #include "../csqcmodellib/sv_model.qh"
34 void crosshair_trace(entity pl)
36 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));
38 void crosshair_trace_plusvisibletriggers(entity pl)
42 first = findchainfloat(solid, SOLID_TRIGGER);
44 for (e = first; e; e = e.chain)
50 for (e = first; e; e = e.chain)
51 e.solid = SOLID_TRIGGER;
53 void WarpZone_crosshair_trace(entity pl)
55 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));
59 string admin_name(void)
61 if(autocvar_sv_adminnick != "")
62 return autocvar_sv_adminnick;
64 return "SERVER ADMIN";
67 void DistributeEvenly_Init(float amount, float totalweight)
69 if (DistributeEvenly_amount)
71 dprint("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
72 dprint(ftos(DistributeEvenly_totalweight), " left!)\n");
75 DistributeEvenly_amount = 0;
77 DistributeEvenly_amount = amount;
78 DistributeEvenly_totalweight = totalweight;
80 float DistributeEvenly_Get(float weight)
85 f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
86 DistributeEvenly_totalweight -= weight;
87 DistributeEvenly_amount -= f;
90 float DistributeEvenly_GetRandomized(float weight)
95 f = floor(random() + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
96 DistributeEvenly_totalweight -= weight;
97 DistributeEvenly_amount -= f;
102 void GameLogEcho(string s)
107 if (autocvar_sv_eventlog_files)
112 matches = autocvar_sv_eventlog_files_counter + 1;
113 cvar_set("sv_eventlog_files_counter", itos(matches));
116 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
117 fn = strcat(autocvar_sv_eventlog_files_nameprefix, fn, autocvar_sv_eventlog_files_namesuffix);
118 logfile = fopen(fn, FILE_APPEND);
119 fputs(logfile, ":logversion:3\n");
123 if (autocvar_sv_eventlog_files_timestamps)
124 fputs(logfile, strcat(":time:", strftime(true, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
126 fputs(logfile, strcat(s, "\n"));
129 if (autocvar_sv_eventlog_console)
138 // will be opened later
143 if (logfile_open && logfile >= 0)
150 entity findnearest(vector point, .string field, string value, vector axismod)
161 localhead = find(world, field, value);
164 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
165 dist = localhead.oldorigin;
167 dist = localhead.origin;
169 dist = dist.x * axismod.x * '1 0 0' + dist.y * axismod.y * '0 1 0' + dist.z * axismod.z * '0 0 1';
172 for (i = 0; i < num_nearest; ++i)
174 if (len < nearest_length[i])
178 // now i tells us where to insert at
179 // INSERTION SORT! YOU'VE SEEN IT! RUN!
180 if (i < NUM_NEAREST_ENTITIES)
182 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
184 nearest_length[j + 1] = nearest_length[j];
185 nearest_entity[j + 1] = nearest_entity[j];
187 nearest_length[i] = len;
188 nearest_entity[i] = localhead;
189 if (num_nearest < NUM_NEAREST_ENTITIES)
190 num_nearest = num_nearest + 1;
193 localhead = find(localhead, field, value);
196 // now use the first one from our list that we can see
197 for (i = 0; i < num_nearest; ++i)
199 traceline(point, nearest_entity[i].origin, true, world);
200 if (trace_fraction == 1)
204 dprint("Nearest point (");
205 dprint(nearest_entity[0].netname);
206 dprint(") is not visible, using a visible one.\n");
208 return nearest_entity[i];
212 if (num_nearest == 0)
215 dprint("Not seeing any location point, using nearest as fallback.\n");
217 dprint("Candidates were: ");
218 for(j = 0; j < num_nearest; ++j)
222 dprint(nearest_entity[j].netname);
227 return nearest_entity[0];
230 string NearestLocation(vector p)
235 loc = findnearest(p, classname, "target_location", '1 1 1');
242 loc = findnearest(p, target, "###item###", '1 1 4');
249 string formatmessage(string msg)
260 WarpZone_crosshair_trace(self);
261 cursor = trace_endpos;
262 cursor_ent = trace_ent;
266 break; // too many replacements
269 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
270 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
283 replacement = substring(msg, p, 2);
284 escape = substring(msg, p + 1, 1);
288 else if (escape == "\\")
290 else if (escape == "n")
292 else if (escape == "a")
293 replacement = ftos(floor(self.armorvalue));
294 else if (escape == "h")
295 replacement = ftos(floor(self.health));
296 else if (escape == "l")
297 replacement = NearestLocation(self.origin);
298 else if (escape == "y")
299 replacement = NearestLocation(cursor);
300 else if (escape == "d")
301 replacement = NearestLocation(self.death_origin);
302 else if (escape == "w") {
306 wep = self.switchweapon;
309 replacement = WEP_NAME(wep);
310 } else if (escape == "W") {
311 if (self.items & IT_SHELLS) replacement = "shells";
312 else if (self.items & IT_NAILS) replacement = "bullets";
313 else if (self.items & IT_ROCKETS) replacement = "rockets";
314 else if (self.items & IT_CELLS) replacement = "cells";
315 else if (self.items & IT_PLASMA) replacement = "plasma";
316 else replacement = "batteries"; // ;)
317 } else if (escape == "x") {
318 replacement = cursor_ent.netname;
319 if (replacement == "" || !cursor_ent)
320 replacement = "nothing";
321 } else if (escape == "s")
322 replacement = ftos(vlen(self.velocity - self.velocity.z * '0 0 1'));
323 else if (escape == "S")
324 replacement = ftos(vlen(self.velocity));
326 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
327 p = p + strlen(replacement);
332 float boolean(float value) { // if value is 0 return false (0), otherwise return true (1)
333 return (value == 0) ? false : true;
342 >0: receives a cvar from name=argv(f) value=argv(f+1)
344 void GetCvars_handleString(string thisname, float f, .string field, string name)
349 strunzone(self.field);
350 self.field = string_null;
354 if (thisname == name)
357 strunzone(self.field);
358 self.field = strzone(argv(f + 1));
362 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
364 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
366 GetCvars_handleString(thisname, f, field, name);
367 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
368 if (thisname == name)
371 s = func(strcat1(self.field));
374 strunzone(self.field);
375 self.field = strzone(s);
379 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
386 if (thisname == name)
387 self.field = stof(argv(f + 1));
390 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
392 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
399 if (thisname == name)
403 self.field = stof(argv(f + 1));
412 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
415 string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(string wo)
418 o = W_FixWeaponOrder_ForceComplete(wo);
419 if(self.weaponorder_byimpulse)
421 strunzone(self.weaponorder_byimpulse);
422 self.weaponorder_byimpulse = string_null;
424 self.weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(o));
427 void GetCvars(float f)
429 string s = string_null;
432 s = strcat1(argv(f));
437 MUTATOR_CALLHOOK(GetCvars);
439 Notification_GetCvars();
441 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
442 GetCvars_handleFloat(s, f, cvar_cl_autoscreenshot, "cl_autoscreenshot");
443 GetCvars_handleFloat(s, f, cvar_cl_jetpack_jump, "cl_jetpack_jump");
444 GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
445 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
446 GetCvars_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
447 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
448 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
449 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
450 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
451 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
452 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
453 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
454 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
455 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
456 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
457 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
458 GetCvars_handleFloat(s, f, cvar_cl_weaponimpulsemode, "cl_weaponimpulsemode");
459 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
460 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
461 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
462 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
463 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
464 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
466 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
467 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
469 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
470 GetCvars_handleFloat(s, f, cvar_cl_allow_uid2name, "cl_allow_uid2name");
471 GetCvars_handleFloat(s, f, cvar_cl_allow_uidtracking, "cl_allow_uidtracking");
472 GetCvars_handleFloat(s, f, cvar_cl_movement_track_canjump, "cl_movement_track_canjump");
473 GetCvars_handleFloat(s, f, cvar_cl_newusekeysupported, "cl_newusekeysupported");
475 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
478 if (s == "cl_weaponpriority")
479 self.switchweapon = w_getbestweapon(self);
480 if (s == "cl_allow_uidtracking")
481 PlayerStats_GameReport_AddPlayer(self);
485 // decolorizes and team colors the player name when needed
486 string playername(entity p)
489 if (teamplay && !intermission_running && IS_PLAYER(p))
491 t = Team_ColorCode(p.team);
492 return strcat(t, strdecolorize(p.netname));
498 vector randompos(vector m1, vector m2)
502 v.x = m2_x * random() + m1_x;
503 v.y = m2_y * random() + m1_y;
504 v.z = m2_z * random() + m1_z;
508 float want_weapon(entity weaponinfo, float allguns) // WEAPONTODO: what still needs done?
510 int i = weaponinfo.weapon;
516 if (g_lms || g_ca || allguns)
518 if(weaponinfo.spawnflags & WEP_FLAG_NORMAL)
524 d = (i == WEP_SHOTGUN);
526 d = 0; // weapon is set a few lines later
528 d = !(!weaponinfo.weaponstart);
530 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
531 d |= (i == WEP_HOOK);
532 if(!g_cts && (weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED)) // never default mutator blocked guns
535 float t = weaponinfo.weaponstartoverride;
537 //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n"));
542 // 4: is set by default?
551 void readplayerstartcvars()
557 // initialize starting values for players
558 start_weapons = '0 0 0';
559 start_weapons_default = '0 0 0';
560 start_weapons_defaultmask = '0 0 0';
562 start_ammo_shells = 0;
563 start_ammo_nails = 0;
564 start_ammo_rockets = 0;
565 start_ammo_cells = 0;
566 start_ammo_plasma = 0;
567 start_health = cvar("g_balance_health_start");
568 start_armorvalue = cvar("g_balance_armor_start");
571 g_weaponarena_weapons = '0 0 0';
573 s = cvar_string("g_weaponarena");
574 if (s == "0" || s == "")
580 if (s == "0" || s == "")
586 // forcibly turn off weaponarena
588 else if (s == "all" || s == "1")
591 g_weaponarena_list = "All Weapons";
592 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
594 e = get_weaponinfo(j);
595 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
596 g_weaponarena_weapons |= WepSet_FromWeapon(j);
599 else if (s == "most")
602 g_weaponarena_list = "Most Weapons";
603 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
605 e = get_weaponinfo(j);
606 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
607 if (e.spawnflags & WEP_FLAG_NORMAL)
608 g_weaponarena_weapons |= WepSet_FromWeapon(j);
611 else if (s == "none")
614 g_weaponarena_list = "No Weapons";
619 t = tokenize_console(s);
620 g_weaponarena_list = "";
621 for (i = 0; i < t; ++i)
624 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
626 e = get_weaponinfo(j);
629 g_weaponarena_weapons |= WepSet_FromWeapon(j);
630 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
636 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
639 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
643 g_weaponarena_random = cvar("g_weaponarena_random");
645 g_weaponarena_random = 0;
646 g_weaponarena_random_with_blaster = cvar("g_weaponarena_random_with_blaster");
650 g_weapon_stay = 0; // incompatible
651 start_weapons = g_weaponarena_weapons;
652 start_items |= IT_UNLIMITED_AMMO;
656 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
658 e = get_weaponinfo(i);
659 int w = want_weapon(e, false);
661 start_weapons |= WepSet_FromWeapon(i);
663 start_weapons_default |= WepSet_FromWeapon(i);
665 start_weapons_defaultmask |= WepSet_FromWeapon(i);
669 if(!cvar("g_use_ammunition"))
670 start_items |= IT_UNLIMITED_AMMO;
672 if(start_items & IT_UNLIMITED_WEAPON_AMMO)
674 start_ammo_shells = 999;
675 start_ammo_nails = 999;
676 start_ammo_rockets = 999;
677 start_ammo_cells = 999;
678 start_ammo_plasma = 999;
679 start_ammo_fuel = 999;
683 start_ammo_shells = cvar("g_start_ammo_shells");
684 start_ammo_nails = cvar("g_start_ammo_nails");
685 start_ammo_rockets = cvar("g_start_ammo_rockets");
686 start_ammo_cells = cvar("g_start_ammo_cells");
687 start_ammo_plasma = cvar("g_start_ammo_plasma");
688 start_ammo_fuel = cvar("g_start_ammo_fuel");
693 warmup_start_ammo_shells = start_ammo_shells;
694 warmup_start_ammo_nails = start_ammo_nails;
695 warmup_start_ammo_rockets = start_ammo_rockets;
696 warmup_start_ammo_cells = start_ammo_cells;
697 warmup_start_ammo_plasma = start_ammo_plasma;
698 warmup_start_ammo_fuel = start_ammo_fuel;
699 warmup_start_health = start_health;
700 warmup_start_armorvalue = start_armorvalue;
701 warmup_start_weapons = start_weapons;
702 warmup_start_weapons_default = start_weapons_default;
703 warmup_start_weapons_defaultmask = start_weapons_defaultmask;
705 if (!g_weaponarena && !g_ca)
707 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
708 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
709 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
710 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
711 warmup_start_ammo_plasma = cvar("g_warmup_start_ammo_plasma");
712 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
713 warmup_start_health = cvar("g_warmup_start_health");
714 warmup_start_armorvalue = cvar("g_warmup_start_armor");
715 warmup_start_weapons = '0 0 0';
716 warmup_start_weapons_default = '0 0 0';
717 warmup_start_weapons_defaultmask = '0 0 0';
718 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
720 e = get_weaponinfo(i);
721 int w = want_weapon(e, g_warmup_allguns);
723 warmup_start_weapons |= WepSet_FromWeapon(i);
725 warmup_start_weapons_default |= WepSet_FromWeapon(i);
727 warmup_start_weapons_defaultmask |= WepSet_FromWeapon(i);
733 start_items |= IT_JETPACK;
735 MUTATOR_CALLHOOK(SetStartItems);
737 if ((start_items & IT_JETPACK) || (g_grappling_hook && (start_weapons & WEPSET_HOOK)))
739 start_items |= IT_FUEL_REGEN;
740 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
741 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
744 WepSet precache_weapons = start_weapons;
745 if (g_warmup_allguns != 1)
746 precache_weapons |= warmup_start_weapons;
747 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
749 e = get_weaponinfo(i);
750 if(precache_weapons & WepSet_FromWeapon(i))
751 WEP_ACTION(i, WR_INIT);
754 start_ammo_shells = max(0, start_ammo_shells);
755 start_ammo_nails = max(0, start_ammo_nails);
756 start_ammo_rockets = max(0, start_ammo_rockets);
757 start_ammo_cells = max(0, start_ammo_cells);
758 start_ammo_plasma = max(0, start_ammo_plasma);
759 start_ammo_fuel = max(0, start_ammo_fuel);
761 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
762 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
763 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
764 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
765 warmup_start_ammo_plasma = max(0, warmup_start_ammo_plasma);
766 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
769 float sound_allowed(float destin, entity e)
771 // sounds from world may always pass
774 if (e.classname == "body")
776 else if (e.realowner && e.realowner != e)
778 else if (e.owner && e.owner != e)
783 // sounds to self may always pass
784 if (destin == MSG_ONE)
787 // sounds by players can be removed
788 if (autocvar_bot_sound_monopoly)
789 if (IS_REAL_CLIENT(e))
791 // anything else may pass
796 void sound(entity e, float chan, string samp, float vol, float attenu)
798 if (!sound_allowed(MSG_BROADCAST, e))
800 sound7(e, chan, samp, vol, attenu, 0, 0);
803 void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float attenu)
807 if (!sound_allowed(_dest, e))
810 entno = num_for_edict(e);
811 idx = precache_sound_index(samp);
816 attenu = floor(attenu * 64);
817 vol = floor(vol * 255);
820 sflags |= SND_VOLUME;
822 sflags |= SND_ATTENUATION;
823 if (entno >= 8192 || chan < 0 || chan > 7)
824 sflags |= SND_LARGEENTITY;
826 sflags |= SND_LARGESOUND;
828 WriteByte(_dest, SVC_SOUND);
829 WriteByte(_dest, sflags);
830 if (sflags & SND_VOLUME)
831 WriteByte(_dest, vol);
832 if (sflags & SND_ATTENUATION)
833 WriteByte(_dest, attenu);
834 if (sflags & SND_LARGEENTITY)
836 WriteShort(_dest, entno);
837 WriteByte(_dest, chan);
841 WriteShort(_dest, entno * 8 + chan);
843 if (sflags & SND_LARGESOUND)
844 WriteShort(_dest, idx);
846 WriteByte(_dest, idx);
848 WriteCoord(_dest, o.x);
849 WriteCoord(_dest, o.y);
850 WriteCoord(_dest, o.z);
852 void soundto(float _dest, entity e, float chan, string samp, float vol, float _atten)
856 if (!sound_allowed(_dest, e))
859 o = e.origin + 0.5 * (e.mins + e.maxs);
860 soundtoat(_dest, e, o, chan, samp, vol, _atten);
862 void soundat(entity e, vector o, float chan, string samp, float vol, float _atten)
864 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, _atten);
866 void stopsoundto(float _dest, entity e, float chan)
870 if (!sound_allowed(_dest, e))
873 entno = num_for_edict(e);
875 if (entno >= 8192 || chan < 0 || chan > 7)
878 idx = precache_sound_index("misc/null.wav");
879 sflags = SND_LARGEENTITY;
881 sflags |= SND_LARGESOUND;
882 WriteByte(_dest, SVC_SOUND);
883 WriteByte(_dest, sflags);
884 WriteShort(_dest, entno);
885 WriteByte(_dest, chan);
886 if (sflags & SND_LARGESOUND)
887 WriteShort(_dest, idx);
889 WriteByte(_dest, idx);
890 WriteCoord(_dest, e.origin.x);
891 WriteCoord(_dest, e.origin.y);
892 WriteCoord(_dest, e.origin.z);
896 WriteByte(_dest, SVC_STOPSOUND);
897 WriteShort(_dest, entno * 8 + chan);
900 void stopsound(entity e, float chan)
902 if (!sound_allowed(MSG_BROADCAST, e))
905 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
906 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
909 void play2(entity e, string filename)
911 //stuffcmd(e, strcat("play2 ", filename, "\n"));
913 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTEN_NONE);
916 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
918 float spamsound(entity e, float chan, string samp, float vol, float _atten)
920 if (!sound_allowed(MSG_BROADCAST, e))
923 if (time > e.spamtime)
926 sound(e, chan, samp, vol, _atten);
932 void play2team(float t, string filename)
936 if (autocvar_bot_sound_monopoly)
939 FOR_EACH_REALPLAYER(head)
942 play2(head, filename);
946 void play2all(string samp)
948 if (autocvar_bot_sound_monopoly)
951 sound(world, CH_INFO, samp, VOL_BASE, ATTEN_NONE);
954 void PrecachePlayerSounds(string f);
955 void precache_playermodel(string m)
957 float globhandle, i, n;
960 if(substring(m, -9,5) == "_lod1")
962 if(substring(m, -9,5) == "_lod2")
965 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
968 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
972 globhandle = search_begin(strcat(m, "_*.sounds"), true, false);
975 n = search_getsize(globhandle);
976 for (i = 0; i < n; ++i)
978 //print(search_getfilename(globhandle, i), "\n");
979 f = search_getfilename(globhandle, i);
980 PrecachePlayerSounds(f);
982 search_end(globhandle);
984 void precache_all_playermodels(string pattern)
986 float globhandle, i, n;
989 globhandle = search_begin(pattern, true, false);
992 n = search_getsize(globhandle);
993 for (i = 0; i < n; ++i)
995 //print(search_getfilename(globhandle, i), "\n");
996 f = search_getfilename(globhandle, i);
997 precache_playermodel(f);
999 search_end(globhandle);
1004 // gamemode related things
1005 precache_model ("models/misc/chatbubble.spr");
1006 precache_model("models/ice/ice.md3");
1008 #ifdef TTURRETS_ENABLED
1009 if (autocvar_g_turrets)
1013 // Precache all player models if desired
1014 if (autocvar_sv_precacheplayermodels)
1016 PrecachePlayerSounds("sound/player/default.sounds");
1017 precache_all_playermodels("models/player/*.zym");
1018 precache_all_playermodels("models/player/*.dpm");
1019 precache_all_playermodels("models/player/*.md3");
1020 precache_all_playermodels("models/player/*.psk");
1021 precache_all_playermodels("models/player/*.iqm");
1024 if (autocvar_sv_defaultcharacter)
1027 s = autocvar_sv_defaultplayermodel_red;
1029 precache_playermodel(s);
1030 s = autocvar_sv_defaultplayermodel_blue;
1032 precache_playermodel(s);
1033 s = autocvar_sv_defaultplayermodel_yellow;
1035 precache_playermodel(s);
1036 s = autocvar_sv_defaultplayermodel_pink;
1038 precache_playermodel(s);
1039 s = autocvar_sv_defaultplayermodel;
1041 precache_playermodel(s);
1046 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1047 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1050 // gore and miscellaneous sounds
1051 //precache_sound ("misc/h2ohit.wav");
1052 precache_model ("models/hook.md3");
1053 precache_sound ("misc/armorimpact.wav");
1054 precache_sound ("misc/bodyimpact1.wav");
1055 precache_sound ("misc/bodyimpact2.wav");
1056 precache_sound ("misc/gib.wav");
1057 precache_sound ("misc/gib_splat01.wav");
1058 precache_sound ("misc/gib_splat02.wav");
1059 precache_sound ("misc/gib_splat03.wav");
1060 precache_sound ("misc/gib_splat04.wav");
1061 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1062 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1063 precache_sound ("misc/null.wav");
1064 precache_sound ("misc/spawn.wav");
1065 precache_sound ("misc/talk.wav");
1066 precache_sound ("misc/teleport.wav");
1067 precache_sound ("misc/poweroff.wav");
1068 precache_sound ("player/lava.wav");
1069 precache_sound ("player/slime.wav");
1071 precache_model ("models/sprites/0.spr32");
1072 precache_model ("models/sprites/1.spr32");
1073 precache_model ("models/sprites/2.spr32");
1074 precache_model ("models/sprites/3.spr32");
1075 precache_model ("models/sprites/4.spr32");
1076 precache_model ("models/sprites/5.spr32");
1077 precache_model ("models/sprites/6.spr32");
1078 precache_model ("models/sprites/7.spr32");
1079 precache_model ("models/sprites/8.spr32");
1080 precache_model ("models/sprites/9.spr32");
1081 precache_model ("models/sprites/10.spr32");
1083 // common weapon precaches
1084 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1085 precache_sound ("weapons/weapon_switch.wav");
1086 precache_sound ("weapons/weaponpickup.wav");
1087 precache_sound ("weapons/unavailable.wav");
1088 precache_sound ("weapons/dryfire.wav");
1089 if (g_grappling_hook)
1091 precache_sound ("weapons/hook_fire.wav"); // hook
1092 precache_sound ("weapons/hook_impact.wav"); // hook
1095 precache_model("models/elaser.mdl");
1096 precache_model("models/laser.mdl");
1097 precache_model("models/ebomb.mdl");
1100 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1102 if (!self.noise && self.music) // quake 3 uses the music field
1103 self.noise = self.music;
1105 // plays music for the level if there is any
1108 precache_sound (self.noise);
1109 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTEN_NONE);
1113 #include "precache-for-csqc.inc"
1117 void make_safe_for_remove(entity e)
1119 if (e.initialize_entity)
1121 entity ent, prev = world;
1122 for (ent = initialize_entity_first; ent; )
1124 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1126 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1127 // skip it in linked list
1130 prev.initialize_entity_next = ent.initialize_entity_next;
1131 ent = prev.initialize_entity_next;
1135 initialize_entity_first = ent.initialize_entity_next;
1136 ent = initialize_entity_first;
1142 ent = ent.initialize_entity_next;
1148 void objerror(string s)
1150 make_safe_for_remove(self);
1151 builtin_objerror(s);
1154 .float remove_except_protected_forbidden;
1155 void remove_except_protected(entity e)
1157 if(e.remove_except_protected_forbidden)
1158 error("not allowed to remove this at this point");
1162 void remove_unsafely(entity e)
1164 if(e.classname == "spike")
1165 error("Removing spikes is forbidden (crylink bug), please report");
1169 void remove_safely(entity e)
1171 make_safe_for_remove(e);
1175 void InitializeEntity(entity e, void(void) func, float order)
1179 if (!e || e.initialize_entity)
1181 // make a proxy initializer entity
1185 e.classname = "initialize_entity";
1189 e.initialize_entity = func;
1190 e.initialize_entity_order = order;
1192 cur = initialize_entity_first;
1196 if (!cur || cur.initialize_entity_order > order)
1198 // insert between prev and cur
1200 prev.initialize_entity_next = e;
1202 initialize_entity_first = e;
1203 e.initialize_entity_next = cur;
1207 cur = cur.initialize_entity_next;
1210 void InitializeEntitiesRun()
1213 startoflist = initialize_entity_first;
1214 initialize_entity_first = world;
1215 remove = remove_except_protected;
1216 for (self = startoflist; self; self = self.initialize_entity_next)
1218 self.remove_except_protected_forbidden = 1;
1220 for (self = startoflist; self; )
1223 var void(void) func;
1224 e = self.initialize_entity_next;
1225 func = self.initialize_entity;
1226 self.initialize_entity_order = 0;
1227 self.initialize_entity = func_null;
1228 self.initialize_entity_next = world;
1229 self.remove_except_protected_forbidden = 0;
1230 if (self.classname == "initialize_entity")
1234 builtin_remove(self);
1237 //dprint("Delayed initialization: ", self.classname, "\n");
1243 backtrace(strcat("Null function in: ", self.classname, "\n"));
1247 remove = remove_unsafely;
1250 void UncustomizeEntitiesRun()
1254 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1255 self.uncustomizeentityforclient();
1258 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1260 e.customizeentityforclient = customizer;
1261 e.uncustomizeentityforclient = uncustomizer;
1262 e.uncustomizeentityforclient_set = !!uncustomizer;
1265 void Net_LinkEntity(entity e, float docull, float dt, bool(entity, int) sendfunc)
1269 if (e.classname == "")
1270 e.classname = "net_linked";
1272 if (e.model == "" || self.modelindex == 0)
1276 setmodel(e, "null");
1280 e.SendEntity = sendfunc;
1281 e.SendFlags = 0xFFFFFF;
1284 e.effects |= EF_NODEPTHTEST;
1288 e.nextthink = time + dt;
1289 e.think = SUB_Remove;
1294 entity eliminatedPlayers;
1295 .float(entity) isEliminated;
1296 float EliminatedPlayers_SendEntity(entity to, float sendflags)
1300 WriteByte(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS);
1301 WriteByte(MSG_ENTITY, sendflags);
1305 for(i = 1; i <= maxclients; i += 8)
1307 for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e))
1309 if(eliminatedPlayers.isEliminated(e))
1312 WriteByte(MSG_ENTITY, f);
1319 void EliminatedPlayers_Init(float(entity) isEliminated_func)
1321 if(eliminatedPlayers)
1323 backtrace("Can't spawn eliminatedPlayers again!");
1326 Net_LinkEntity(eliminatedPlayers = spawn(), false, 0, EliminatedPlayers_SendEntity);
1327 eliminatedPlayers.isEliminated = isEliminated_func;
1331 void adaptor_think2touch()
1340 void adaptor_think2use()
1352 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1354 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
1355 self.projectiledeathtype |= HITTYPE_SPLASH;
1356 adaptor_think2use();
1359 // deferred dropping
1360 void DropToFloor_Handler()
1362 builtin_droptofloor();
1363 self.dropped_origin = self.origin;
1368 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1373 float trace_hits_box_a0, trace_hits_box_a1;
1375 float trace_hits_box_1d(float end, float thmi, float thma)
1379 // just check if x is in range
1387 // do the trace with respect to x
1388 // 0 -> end has to stay in thmi -> thma
1389 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1390 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1391 if (trace_hits_box_a0 > trace_hits_box_a1)
1397 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1402 // now it is a trace from 0 to end
1404 trace_hits_box_a0 = 0;
1405 trace_hits_box_a1 = 1;
1407 if (!trace_hits_box_1d(end.x, thmi.x, thma.x))
1409 if (!trace_hits_box_1d(end.y, thmi.y, thma.y))
1411 if (!trace_hits_box_1d(end.z, thmi.z, thma.z))
1417 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1419 return trace_hits_box(start, end, thmi - ma, thma - mi);
1422 float SUB_NoImpactCheck()
1424 // zero hitcontents = this is not the real impact, but either the
1425 // mirror-impact of something hitting the projectile instead of the
1426 // projectile hitting the something, or a touchareagrid one. Neither of
1427 // these stop the projectile from moving, so...
1428 if(trace_dphitcontents == 0)
1430 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1431 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));
1434 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1436 if (other == world && self.size != '0 0 0')
1439 tic = self.velocity * sys_frametime;
1440 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1441 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1442 if (trace_fraction >= 1)
1444 dprint("Odd... did not hit...?\n");
1446 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1448 dprint("Detected and prevented the sky-grapple bug.\n");
1456 #define SUB_OwnerCheck() (other && (other == self.owner))
1458 void RemoveGrapplingHook(entity pl);
1459 void W_Crylink_Dequeue(entity e);
1460 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1462 if(SUB_OwnerCheck())
1464 if(SUB_NoImpactCheck())
1466 if(self.classname == "nade")
1467 return false; // no checks here
1468 else if(self.classname == "grapplinghook")
1469 RemoveGrapplingHook(self.realowner);
1470 else if(self.classname == "spike")
1472 W_Crylink_Dequeue(self);
1479 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1480 UpdateCSQCProjectile(self);
1485 void URI_Get_Callback(float id, float status, string data)
1487 if(url_URI_Get_Callback(id, status, data))
1491 else if (id == URI_GET_DISCARD)
1495 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
1498 Curl_URI_Get_Callback(id, status, data);
1500 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1503 OnlineBanList_URI_Get_Callback(id, status, data);
1507 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1511 string uid2name(string myuid) {
1513 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
1515 // FIXME remove this later after 0.6 release
1516 // convert old style broken records to correct style
1519 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
1522 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
1523 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
1528 s = "^1Unregistered Player";
1532 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1535 vector start, org, delta, end, enddown, mstart;
1538 m = e.dphitcontentsmask;
1539 e.dphitcontentsmask = goodcontents | badcontents;
1542 delta = world.maxs - world.mins;
1546 for (i = 0; i < attempts; ++i)
1548 start.x = org.x + random() * delta.x;
1549 start.y = org.y + random() * delta.y;
1550 start.z = org.z + random() * delta.z;
1552 // rule 1: start inside world bounds, and outside
1553 // solid, and don't start from somewhere where you can
1554 // fall down to evil
1555 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta.z, MOVE_NORMAL, e);
1556 if (trace_fraction >= 1)
1558 if (trace_startsolid)
1560 if (trace_dphitcontents & badcontents)
1562 if (trace_dphitq3surfaceflags & badsurfaceflags)
1565 // rule 2: if we are too high, lower the point
1566 if (trace_fraction * delta.z > maxaboveground)
1567 start = trace_endpos + '0 0 1' * maxaboveground;
1568 enddown = trace_endpos;
1570 // rule 3: make sure we aren't outside the map. This only works
1571 // for somewhat well formed maps. A good rule of thumb is that
1572 // the map should have a convex outside hull.
1573 // these can be traceLINES as we already verified the starting box
1574 mstart = start + 0.5 * (e.mins + e.maxs);
1575 traceline(mstart, mstart + '1 0 0' * delta.x, MOVE_NORMAL, e);
1576 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
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 + '0 1 0' * delta.y, 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 0 1' * delta.z, MOVE_NORMAL, e);
1588 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1591 // rule 4: we must "see" some spawnpoint or item
1592 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
1593 if(checkpvs(mstart, sp))
1594 if((traceline(mstart, sp.origin, MOVE_NORMAL, e), trace_fraction) >= 1)
1598 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
1599 if(checkpvs(mstart, sp))
1600 if((traceline(mstart, sp.origin + (sp.mins + sp.maxs) * 0.5, MOVE_NORMAL, e), trace_fraction) >= 1)
1606 // find a random vector to "look at"
1607 end.x = org.x + random() * delta.x;
1608 end.y = org.y + random() * delta.y;
1609 end.z = org.z + random() * delta.z;
1610 end = start + normalize(end - start) * vlen(delta);
1612 // rule 4: start TO end must not be too short
1613 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1614 if (trace_startsolid)
1616 if (trace_fraction < minviewdistance / vlen(delta))
1619 // rule 5: don't want to look at sky
1620 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
1623 // rule 6: we must not end up in trigger_hurt
1624 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
1630 e.dphitcontentsmask = m;
1634 setorigin(e, start);
1635 e.angles = vectoangles(end - start);
1636 dprint("Needed ", ftos(i + 1), " attempts\n");
1643 void write_recordmarker(entity pl, float tstart, float dt)
1645 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
1647 // also write a marker into demo files for demotc-race-record-extractor to find
1650 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
1651 " ", ftos(tstart), " ", ftos(dt), "\n"));
1654 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
1667 if(allowcenter) // 2: allow center handedness
1680 if(allowcenter) // 2: allow center handedness
1696 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
1701 if (autocvar_g_shootfromeye)
1705 if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
1706 else { vecs.y = 0; vecs.z -= 2; }
1714 else if (autocvar_g_shootfromcenter)
1719 else if ((s = autocvar_g_shootfromfixedorigin) != "")
1729 else if (autocvar_g_shootfromclient)
1731 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
1736 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
1738 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
1742 void attach_sameorigin(entity e, entity to, string tag)
1744 vector org, t_forward, t_left, t_up, e_forward, e_up;
1747 org = e.origin - gettaginfo(to, gettagindex(to, tag));
1748 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
1749 t_forward = v_forward * tagscale;
1750 t_left = v_right * -tagscale;
1751 t_up = v_up * tagscale;
1753 e.origin_x = org * t_forward;
1754 e.origin_y = org * t_left;
1755 e.origin_z = org * t_up;
1757 // current forward and up directions
1758 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1759 e.angles = AnglesTransform_FromVAngles(e.angles);
1761 e.angles = AnglesTransform_FromAngles(e.angles);
1762 fixedmakevectors(e.angles);
1764 // untransform forward, up!
1765 e_forward.x = v_forward * t_forward;
1766 e_forward.y = v_forward * t_left;
1767 e_forward.z = v_forward * t_up;
1768 e_up.x = v_up * t_forward;
1769 e_up.y = v_up * t_left;
1770 e_up.z = v_up * t_up;
1772 e.angles = fixedvectoangles2(e_forward, e_up);
1773 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1774 e.angles = AnglesTransform_ToVAngles(e.angles);
1776 e.angles = AnglesTransform_ToAngles(e.angles);
1778 setattachment(e, to, tag);
1779 setorigin(e, e.origin);
1782 void detach_sameorigin(entity e)
1785 org = gettaginfo(e, 0);
1786 e.angles = fixedvectoangles2(v_forward, v_up);
1787 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1788 e.angles = AnglesTransform_ToVAngles(e.angles);
1790 e.angles = AnglesTransform_ToAngles(e.angles);
1792 setattachment(e, world, "");
1793 setorigin(e, e.origin);
1796 void follow_sameorigin(entity e, entity to)
1798 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
1799 e.aiment = to; // make the hole follow bmodel
1800 e.punchangle = to.angles; // the original angles of bmodel
1801 e.view_ofs = e.origin - to.origin; // relative origin
1802 e.v_angle = e.angles - to.angles; // relative angles
1805 void unfollow_sameorigin(entity e)
1807 e.movetype = MOVETYPE_NONE;
1810 entity gettaginfo_relative_ent;
1811 vector gettaginfo_relative(entity e, float tag)
1813 if (!gettaginfo_relative_ent)
1815 gettaginfo_relative_ent = spawn();
1816 gettaginfo_relative_ent.effects = EF_NODRAW;
1818 gettaginfo_relative_ent.model = e.model;
1819 gettaginfo_relative_ent.modelindex = e.modelindex;
1820 gettaginfo_relative_ent.frame = e.frame;
1821 return gettaginfo(gettaginfo_relative_ent, tag);
1826 float modeleffect_SendEntity(entity to, int sf)
1829 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
1832 if(self.velocity != '0 0 0')
1834 if(self.angles != '0 0 0')
1836 if(self.avelocity != '0 0 0')
1839 WriteByte(MSG_ENTITY, f);
1840 WriteShort(MSG_ENTITY, self.modelindex);
1841 WriteByte(MSG_ENTITY, self.skin);
1842 WriteByte(MSG_ENTITY, self.frame);
1843 WriteCoord(MSG_ENTITY, self.origin.x);
1844 WriteCoord(MSG_ENTITY, self.origin.y);
1845 WriteCoord(MSG_ENTITY, self.origin.z);
1848 WriteCoord(MSG_ENTITY, self.velocity.x);
1849 WriteCoord(MSG_ENTITY, self.velocity.y);
1850 WriteCoord(MSG_ENTITY, self.velocity.z);
1854 WriteCoord(MSG_ENTITY, self.angles.x);
1855 WriteCoord(MSG_ENTITY, self.angles.y);
1856 WriteCoord(MSG_ENTITY, self.angles.z);
1860 WriteCoord(MSG_ENTITY, self.avelocity.x);
1861 WriteCoord(MSG_ENTITY, self.avelocity.y);
1862 WriteCoord(MSG_ENTITY, self.avelocity.z);
1864 WriteShort(MSG_ENTITY, self.scale * 256.0);
1865 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
1866 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
1867 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
1868 WriteByte(MSG_ENTITY, self.alpha * 255.0);
1873 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)
1878 e.classname = "modeleffect";
1886 e.teleport_time = t1;
1890 e.scale = s0 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1894 e.scale2 = s2 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1897 sz = max(e.scale, e.scale2);
1898 setsize(e, e.mins * sz, e.maxs * sz);
1899 Net_LinkEntity(e, false, 0.1, modeleffect_SendEntity);
1902 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
1904 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
1907 float randombit(float bits)
1909 if(!(bits & (bits-1))) // this ONLY holds for powers of two!
1918 for(f = 1; f <= bits; f *= 2)
1927 r = (r - 1) / (n - 1);
1934 float randombits(float bits, float k, float error_return)
1938 while(k > 0 && bits != r)
1940 r += randombit(bits - r);
1949 void randombit_test(float bits, float iter)
1953 print(ftos(randombit(bits)), "\n");
1958 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
1960 if(halflifedist > 0)
1961 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
1962 else if(halflifedist < 0)
1963 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
1975 oself.think = SUB_Remove;
1976 oself.nextthink = time;
1982 Execute func() after time + fdelay.
1983 self when func is executed = self when defer is called
1985 void defer(float fdelay, void() func)
1992 e.think = defer_think;
1993 e.nextthink = time + fdelay;
1996 .string aiment_classname;
1997 .float aiment_deadflag;
1998 void SetMovetypeFollow(entity ent, entity e)
2000 // FIXME this may not be warpzone aware
2001 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
2002 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.
2003 ent.aiment = e; // make the hole follow bmodel
2004 ent.punchangle = e.angles; // the original angles of bmodel
2005 ent.view_ofs = ent.origin - e.origin; // relative origin
2006 ent.v_angle = ent.angles - e.angles; // relative angles
2007 ent.aiment_classname = strzone(e.classname);
2008 ent.aiment_deadflag = e.deadflag;
2010 void UnsetMovetypeFollow(entity ent)
2012 ent.movetype = MOVETYPE_FLY;
2013 PROJECTILE_MAKETRIGGER(ent);
2016 float LostMovetypeFollow(entity ent)
2019 if(ent.movetype != MOVETYPE_FOLLOW)
2025 if(ent.aiment.classname != ent.aiment_classname)
2027 if(ent.aiment.deadflag != ent.aiment_deadflag)
2033 float isPushable(entity e)
2042 case "droppedweapon":
2043 case "keepawayball":
2044 case "nexball_basketball":
2045 case "nexball_football":
2047 case "bullet": // antilagged bullets can't hit this either
2050 if (e.projectiledeathtype)