1 #include "miscfunctions.qh"
4 #include "command/common.qh"
5 #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/triggers/subs.qh"
22 #include "../common/urllib.qh"
23 #include "../common/util.qh"
24 #include "../common/weapons/all.qh"
25 #include "../csqcmodellib/sv_model.qh"
26 #include "../warpzonelib/anglestransform.qh"
27 #include "../warpzonelib/server.qh"
29 void crosshair_trace(entity pl)
31 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));
33 void crosshair_trace_plusvisibletriggers(entity pl)
37 first = findchainfloat(solid, SOLID_TRIGGER);
39 for (e = first; e; e = e.chain)
45 for (e = first; e; e = e.chain)
46 e.solid = SOLID_TRIGGER;
48 void WarpZone_crosshair_trace(entity pl)
50 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));
54 string admin_name(void)
56 if(autocvar_sv_adminnick != "")
57 return autocvar_sv_adminnick;
59 return "SERVER ADMIN";
62 void DistributeEvenly_Init(float amount, float totalweight)
64 if (DistributeEvenly_amount)
66 dprint("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
67 dprint(ftos(DistributeEvenly_totalweight), " left!)\n");
70 DistributeEvenly_amount = 0;
72 DistributeEvenly_amount = amount;
73 DistributeEvenly_totalweight = totalweight;
75 float DistributeEvenly_Get(float weight)
80 f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
81 DistributeEvenly_totalweight -= weight;
82 DistributeEvenly_amount -= f;
85 float DistributeEvenly_GetRandomized(float weight)
90 f = floor(random() + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
91 DistributeEvenly_totalweight -= weight;
92 DistributeEvenly_amount -= f;
97 void GameLogEcho(string s)
102 if (autocvar_sv_eventlog_files)
107 matches = autocvar_sv_eventlog_files_counter + 1;
108 cvar_set("sv_eventlog_files_counter", itos(matches));
111 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
112 fn = strcat(autocvar_sv_eventlog_files_nameprefix, fn, autocvar_sv_eventlog_files_namesuffix);
113 logfile = fopen(fn, FILE_APPEND);
114 fputs(logfile, ":logversion:3\n");
118 if (autocvar_sv_eventlog_files_timestamps)
119 fputs(logfile, strcat(":time:", strftime(true, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
121 fputs(logfile, strcat(s, "\n"));
124 if (autocvar_sv_eventlog_console)
133 // will be opened later
138 if (logfile_open && logfile >= 0)
145 entity findnearest(vector point, .string field, string value, vector axismod)
156 localhead = find(world, field, value);
159 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
160 dist = localhead.oldorigin;
162 dist = localhead.origin;
164 dist = dist.x * axismod.x * '1 0 0' + dist.y * axismod.y * '0 1 0' + dist.z * axismod.z * '0 0 1';
167 for (i = 0; i < num_nearest; ++i)
169 if (len < nearest_length[i])
173 // now i tells us where to insert at
174 // INSERTION SORT! YOU'VE SEEN IT! RUN!
175 if (i < NUM_NEAREST_ENTITIES)
177 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
179 nearest_length[j + 1] = nearest_length[j];
180 nearest_entity[j + 1] = nearest_entity[j];
182 nearest_length[i] = len;
183 nearest_entity[i] = localhead;
184 if (num_nearest < NUM_NEAREST_ENTITIES)
185 num_nearest = num_nearest + 1;
188 localhead = find(localhead, field, value);
191 // now use the first one from our list that we can see
192 for (i = 0; i < num_nearest; ++i)
194 traceline(point, nearest_entity[i].origin, true, world);
195 if (trace_fraction == 1)
199 dprint("Nearest point (");
200 dprint(nearest_entity[0].netname);
201 dprint(") is not visible, using a visible one.\n");
203 return nearest_entity[i];
207 if (num_nearest == 0)
210 dprint("Not seeing any location point, using nearest as fallback.\n");
212 dprint("Candidates were: ");
213 for(j = 0; j < num_nearest; ++j)
217 dprint(nearest_entity[j].netname);
222 return nearest_entity[0];
225 string NearestLocation(vector p)
230 loc = findnearest(p, classname, "target_location", '1 1 1');
237 loc = findnearest(p, target, "###item###", '1 1 4');
244 string formatmessage(string msg)
255 WarpZone_crosshair_trace(self);
256 cursor = trace_endpos;
257 cursor_ent = trace_ent;
261 break; // too many replacements
264 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
265 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
278 replacement = substring(msg, p, 2);
279 escape = substring(msg, p + 1, 1);
283 else if (escape == "\\")
285 else if (escape == "n")
287 else if (escape == "a")
288 replacement = ftos(floor(self.armorvalue));
289 else if (escape == "h")
290 replacement = ftos(floor(self.health));
291 else if (escape == "l")
292 replacement = NearestLocation(self.origin);
293 else if (escape == "y")
294 replacement = NearestLocation(cursor);
295 else if (escape == "d")
296 replacement = NearestLocation(self.death_origin);
297 else if (escape == "w") {
301 wep = self.switchweapon;
304 replacement = WEP_NAME(wep);
305 } else if (escape == "W") {
306 if (self.items & IT_SHELLS) replacement = "shells";
307 else if (self.items & IT_NAILS) replacement = "bullets";
308 else if (self.items & IT_ROCKETS) replacement = "rockets";
309 else if (self.items & IT_CELLS) replacement = "cells";
310 else if (self.items & IT_PLASMA) replacement = "plasma";
311 else replacement = "batteries"; // ;)
312 } else if (escape == "x") {
313 replacement = cursor_ent.netname;
314 if (replacement == "" || !cursor_ent)
315 replacement = "nothing";
316 } else if (escape == "s")
317 replacement = ftos(vlen(self.velocity - self.velocity.z * '0 0 1'));
318 else if (escape == "S")
319 replacement = ftos(vlen(self.velocity));
321 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
322 p = p + strlen(replacement);
327 float boolean(float value) { // if value is 0 return false (0), otherwise return true (1)
328 return (value == 0) ? false : true;
337 >0: receives a cvar from name=argv(f) value=argv(f+1)
339 void GetCvars_handleString(string thisname, float f, .string field, string name)
344 strunzone(self.(field));
345 self.(field) = string_null;
349 if (thisname == name)
352 strunzone(self.(field));
353 self.(field) = strzone(argv(f + 1));
357 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
359 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
361 GetCvars_handleString(thisname, f, field, name);
362 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
363 if (thisname == name)
365 string s = func(strcat1(self.(field)));
366 if (s != self.(field))
368 strunzone(self.(field));
369 self.(field) = strzone(s);
373 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
380 if (thisname == name)
381 self.(field) = stof(argv(f + 1));
384 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
386 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
393 if (thisname == name)
397 self.(field) = stof(argv(f + 1));
406 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
409 string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(string wo)
412 o = W_FixWeaponOrder_ForceComplete(wo);
413 if(self.weaponorder_byimpulse)
415 strunzone(self.weaponorder_byimpulse);
416 self.weaponorder_byimpulse = string_null;
418 self.weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(o));
421 void GetCvars(float f)
423 string s = string_null;
426 s = strcat1(argv(f));
431 MUTATOR_CALLHOOK(GetCvars);
433 Notification_GetCvars();
435 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
436 GetCvars_handleFloat(s, f, cvar_cl_autoscreenshot, "cl_autoscreenshot");
437 GetCvars_handleFloat(s, f, cvar_cl_jetpack_jump, "cl_jetpack_jump");
438 GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
439 GetCvars_handleString(s, f, cvar_cl_physics, "cl_physics");
440 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
441 GetCvars_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
442 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
443 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
444 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
445 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
446 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
447 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
448 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
449 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
450 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
451 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
452 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
453 GetCvars_handleFloat(s, f, cvar_cl_weaponimpulsemode, "cl_weaponimpulsemode");
454 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
455 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
456 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
457 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
458 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
459 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
461 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
462 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
464 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
465 GetCvars_handleFloat(s, f, cvar_cl_allow_uid2name, "cl_allow_uid2name");
466 GetCvars_handleFloat(s, f, cvar_cl_allow_uidtracking, "cl_allow_uidtracking");
467 GetCvars_handleFloat(s, f, cvar_cl_movement_track_canjump, "cl_movement_track_canjump");
468 GetCvars_handleFloat(s, f, cvar_cl_newusekeysupported, "cl_newusekeysupported");
470 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
473 if (s == "cl_weaponpriority")
474 self.switchweapon = w_getbestweapon(self);
475 if (s == "cl_allow_uidtracking")
476 PlayerStats_GameReport_AddPlayer(self);
480 // decolorizes and team colors the player name when needed
481 string playername(entity p)
484 if (teamplay && !intermission_running && IS_PLAYER(p))
486 t = Team_ColorCode(p.team);
487 return strcat(t, strdecolorize(p.netname));
493 vector randompos(vector m1, vector m2)
497 v.x = m2_x * random() + m1_x;
498 v.y = m2_y * random() + m1_y;
499 v.z = m2_z * random() + m1_z;
503 float want_weapon(entity weaponinfo, float allguns) // WEAPONTODO: what still needs done?
505 int i = weaponinfo.weapon;
511 if (g_lms || g_ca || allguns)
513 if(weaponinfo.spawnflags & WEP_FLAG_NORMAL)
519 d = (i == WEP_SHOTGUN);
521 d = 0; // weapon is set a few lines later
523 d = !(!weaponinfo.weaponstart);
525 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
526 d |= (i == WEP_HOOK);
527 if(!g_cts && (weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED)) // never default mutator blocked guns
530 float t = weaponinfo.weaponstartoverride;
532 //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n"));
537 // 4: is set by default?
546 void readplayerstartcvars()
552 // initialize starting values for players
553 start_weapons = '0 0 0';
554 start_weapons_default = '0 0 0';
555 start_weapons_defaultmask = '0 0 0';
557 start_ammo_shells = 0;
558 start_ammo_nails = 0;
559 start_ammo_rockets = 0;
560 start_ammo_cells = 0;
561 start_ammo_plasma = 0;
562 start_health = cvar("g_balance_health_start");
563 start_armorvalue = cvar("g_balance_armor_start");
566 g_weaponarena_weapons = '0 0 0';
568 s = cvar_string("g_weaponarena");
569 if (s == "0" || s == "")
571 if(g_ca || g_freezetag)
575 if (s == "0" || s == "")
581 // forcibly turn off weaponarena
583 else if (s == "all" || s == "1")
586 g_weaponarena_list = "All Weapons";
587 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
589 e = get_weaponinfo(j);
590 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
591 g_weaponarena_weapons |= WepSet_FromWeapon(j);
594 else if (s == "most")
597 g_weaponarena_list = "Most Weapons";
598 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
600 e = get_weaponinfo(j);
601 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
602 if (e.spawnflags & WEP_FLAG_NORMAL)
603 g_weaponarena_weapons |= WepSet_FromWeapon(j);
606 else if (s == "none")
609 g_weaponarena_list = "No Weapons";
614 t = tokenize_console(s);
615 g_weaponarena_list = "";
616 for (i = 0; i < t; ++i)
619 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
621 e = get_weaponinfo(j);
624 g_weaponarena_weapons |= WepSet_FromWeapon(j);
625 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
631 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
634 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
638 g_weaponarena_random = cvar("g_weaponarena_random");
640 g_weaponarena_random = 0;
641 g_weaponarena_random_with_blaster = cvar("g_weaponarena_random_with_blaster");
645 g_weapon_stay = 0; // incompatible
646 start_weapons = g_weaponarena_weapons;
647 start_items |= IT_UNLIMITED_AMMO;
651 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
653 e = get_weaponinfo(i);
654 int w = want_weapon(e, false);
656 start_weapons |= WepSet_FromWeapon(i);
658 start_weapons_default |= WepSet_FromWeapon(i);
660 start_weapons_defaultmask |= WepSet_FromWeapon(i);
664 if(!cvar("g_use_ammunition"))
665 start_items |= IT_UNLIMITED_AMMO;
667 if(start_items & IT_UNLIMITED_WEAPON_AMMO)
669 start_ammo_shells = 999;
670 start_ammo_nails = 999;
671 start_ammo_rockets = 999;
672 start_ammo_cells = 999;
673 start_ammo_plasma = 999;
674 start_ammo_fuel = 999;
678 start_ammo_shells = cvar("g_start_ammo_shells");
679 start_ammo_nails = cvar("g_start_ammo_nails");
680 start_ammo_rockets = cvar("g_start_ammo_rockets");
681 start_ammo_cells = cvar("g_start_ammo_cells");
682 start_ammo_plasma = cvar("g_start_ammo_plasma");
683 start_ammo_fuel = cvar("g_start_ammo_fuel");
688 warmup_start_ammo_shells = start_ammo_shells;
689 warmup_start_ammo_nails = start_ammo_nails;
690 warmup_start_ammo_rockets = start_ammo_rockets;
691 warmup_start_ammo_cells = start_ammo_cells;
692 warmup_start_ammo_plasma = start_ammo_plasma;
693 warmup_start_ammo_fuel = start_ammo_fuel;
694 warmup_start_health = start_health;
695 warmup_start_armorvalue = start_armorvalue;
696 warmup_start_weapons = start_weapons;
697 warmup_start_weapons_default = start_weapons_default;
698 warmup_start_weapons_defaultmask = start_weapons_defaultmask;
700 if (!g_weaponarena && !g_ca && !g_freezetag)
702 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
703 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
704 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
705 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
706 warmup_start_ammo_plasma = cvar("g_warmup_start_ammo_plasma");
707 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
708 warmup_start_health = cvar("g_warmup_start_health");
709 warmup_start_armorvalue = cvar("g_warmup_start_armor");
710 warmup_start_weapons = '0 0 0';
711 warmup_start_weapons_default = '0 0 0';
712 warmup_start_weapons_defaultmask = '0 0 0';
713 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
715 e = get_weaponinfo(i);
716 int w = want_weapon(e, g_warmup_allguns);
718 warmup_start_weapons |= WepSet_FromWeapon(i);
720 warmup_start_weapons_default |= WepSet_FromWeapon(i);
722 warmup_start_weapons_defaultmask |= WepSet_FromWeapon(i);
728 start_items |= IT_JETPACK;
730 MUTATOR_CALLHOOK(SetStartItems);
732 if ((start_items & IT_JETPACK) || (g_grappling_hook && (start_weapons & WEPSET_HOOK)))
734 start_items |= IT_FUEL_REGEN;
735 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
736 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
739 WepSet precache_weapons = start_weapons;
740 if (g_warmup_allguns != 1)
741 precache_weapons |= warmup_start_weapons;
742 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
744 e = get_weaponinfo(i);
745 if(precache_weapons & WepSet_FromWeapon(i))
746 WEP_ACTION(i, WR_INIT);
749 start_ammo_shells = max(0, start_ammo_shells);
750 start_ammo_nails = max(0, start_ammo_nails);
751 start_ammo_rockets = max(0, start_ammo_rockets);
752 start_ammo_cells = max(0, start_ammo_cells);
753 start_ammo_plasma = max(0, start_ammo_plasma);
754 start_ammo_fuel = max(0, start_ammo_fuel);
756 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
757 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
758 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
759 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
760 warmup_start_ammo_plasma = max(0, warmup_start_ammo_plasma);
761 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
764 float sound_allowed(float destin, entity e)
766 // sounds from world may always pass
769 if (e.classname == "body")
771 else if (e.realowner && e.realowner != e)
773 else if (e.owner && e.owner != e)
778 // sounds to self may always pass
779 if (destin == MSG_ONE)
782 // sounds by players can be removed
783 if (autocvar_bot_sound_monopoly)
784 if (IS_REAL_CLIENT(e))
786 // anything else may pass
791 void sound(entity e, float chan, string samp, float vol, float attenu)
793 if (!sound_allowed(MSG_BROADCAST, e))
795 sound7(e, chan, samp, vol, attenu, 0, 0);
798 void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float attenu)
802 if (!sound_allowed(_dest, e))
805 entno = num_for_edict(e);
806 idx = precache_sound_index(samp);
811 attenu = floor(attenu * 64);
812 vol = floor(vol * 255);
815 sflags |= SND_VOLUME;
817 sflags |= SND_ATTENUATION;
818 if (entno >= 8192 || chan < 0 || chan > 7)
819 sflags |= SND_LARGEENTITY;
821 sflags |= SND_LARGESOUND;
823 WriteByte(_dest, SVC_SOUND);
824 WriteByte(_dest, sflags);
825 if (sflags & SND_VOLUME)
826 WriteByte(_dest, vol);
827 if (sflags & SND_ATTENUATION)
828 WriteByte(_dest, attenu);
829 if (sflags & SND_LARGEENTITY)
831 WriteShort(_dest, entno);
832 WriteByte(_dest, chan);
836 WriteShort(_dest, entno * 8 + chan);
838 if (sflags & SND_LARGESOUND)
839 WriteShort(_dest, idx);
841 WriteByte(_dest, idx);
843 WriteCoord(_dest, o.x);
844 WriteCoord(_dest, o.y);
845 WriteCoord(_dest, o.z);
847 void soundto(float _dest, entity e, float chan, string samp, float vol, float _atten)
851 if (!sound_allowed(_dest, e))
854 o = e.origin + 0.5 * (e.mins + e.maxs);
855 soundtoat(_dest, e, o, chan, samp, vol, _atten);
857 void soundat(entity e, vector o, float chan, string samp, float vol, float _atten)
859 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, _atten);
861 void stopsoundto(float _dest, entity e, float chan)
865 if (!sound_allowed(_dest, e))
868 entno = num_for_edict(e);
870 if (entno >= 8192 || chan < 0 || chan > 7)
873 idx = precache_sound_index("misc/null.wav");
874 sflags = SND_LARGEENTITY;
876 sflags |= SND_LARGESOUND;
877 WriteByte(_dest, SVC_SOUND);
878 WriteByte(_dest, sflags);
879 WriteShort(_dest, entno);
880 WriteByte(_dest, chan);
881 if (sflags & SND_LARGESOUND)
882 WriteShort(_dest, idx);
884 WriteByte(_dest, idx);
885 WriteCoord(_dest, e.origin.x);
886 WriteCoord(_dest, e.origin.y);
887 WriteCoord(_dest, e.origin.z);
891 WriteByte(_dest, SVC_STOPSOUND);
892 WriteShort(_dest, entno * 8 + chan);
895 void stopsound(entity e, float chan)
897 if (!sound_allowed(MSG_BROADCAST, e))
900 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
901 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
904 void play2(entity e, string filename)
906 //stuffcmd(e, strcat("play2 ", filename, "\n"));
908 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTEN_NONE);
911 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
913 float spamsound(entity e, float chan, string samp, float vol, float _atten)
915 if (!sound_allowed(MSG_BROADCAST, e))
918 if (time > e.spamtime)
921 sound(e, chan, samp, vol, _atten);
927 void play2team(float t, string filename)
931 if (autocvar_bot_sound_monopoly)
934 FOR_EACH_REALPLAYER(head)
937 play2(head, filename);
941 void play2all(string samp)
943 if (autocvar_bot_sound_monopoly)
946 sound(world, CH_INFO, samp, VOL_BASE, ATTEN_NONE);
949 void PrecachePlayerSounds(string f);
950 void precache_playermodel(string m)
952 float globhandle, i, n;
955 if(substring(m, -9,5) == "_lod1")
957 if(substring(m, -9,5) == "_lod2")
960 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
963 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
967 globhandle = search_begin(strcat(m, "_*.sounds"), true, false);
970 n = search_getsize(globhandle);
971 for (i = 0; i < n; ++i)
973 //print(search_getfilename(globhandle, i), "\n");
974 f = search_getfilename(globhandle, i);
975 PrecachePlayerSounds(f);
977 search_end(globhandle);
979 void precache_all_playermodels(string pattern)
981 float globhandle, i, n;
984 globhandle = search_begin(pattern, true, false);
987 n = search_getsize(globhandle);
988 for (i = 0; i < n; ++i)
990 //print(search_getfilename(globhandle, i), "\n");
991 f = search_getfilename(globhandle, i);
992 precache_playermodel(f);
994 search_end(globhandle);
999 // gamemode related things
1000 precache_model ("models/misc/chatbubble.spr");
1001 precache_model("models/ice/ice.md3");
1003 #ifdef TTURRETS_ENABLED
1004 if (autocvar_g_turrets)
1008 // Precache all player models if desired
1009 if (autocvar_sv_precacheplayermodels)
1011 PrecachePlayerSounds("sound/player/default.sounds");
1012 precache_all_playermodels("models/player/*.zym");
1013 precache_all_playermodels("models/player/*.dpm");
1014 precache_all_playermodels("models/player/*.md3");
1015 precache_all_playermodels("models/player/*.psk");
1016 precache_all_playermodels("models/player/*.iqm");
1019 if (autocvar_sv_defaultcharacter)
1022 s = autocvar_sv_defaultplayermodel_red;
1024 precache_playermodel(s);
1025 s = autocvar_sv_defaultplayermodel_blue;
1027 precache_playermodel(s);
1028 s = autocvar_sv_defaultplayermodel_yellow;
1030 precache_playermodel(s);
1031 s = autocvar_sv_defaultplayermodel_pink;
1033 precache_playermodel(s);
1034 s = autocvar_sv_defaultplayermodel;
1036 precache_playermodel(s);
1041 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1042 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1045 // gore and miscellaneous sounds
1046 //precache_sound ("misc/h2ohit.wav");
1047 precache_model ("models/hook.md3");
1048 precache_sound ("misc/armorimpact.wav");
1049 precache_sound ("misc/bodyimpact1.wav");
1050 precache_sound ("misc/bodyimpact2.wav");
1051 precache_sound ("misc/gib.wav");
1052 precache_sound ("misc/gib_splat01.wav");
1053 precache_sound ("misc/gib_splat02.wav");
1054 precache_sound ("misc/gib_splat03.wav");
1055 precache_sound ("misc/gib_splat04.wav");
1056 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1057 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1058 precache_sound ("misc/null.wav");
1059 precache_sound ("misc/spawn.wav");
1060 precache_sound ("misc/talk.wav");
1061 precache_sound ("misc/teleport.wav");
1062 precache_sound ("misc/poweroff.wav");
1063 precache_sound ("player/lava.wav");
1064 precache_sound ("player/slime.wav");
1066 precache_model ("models/sprites/0.spr32");
1067 precache_model ("models/sprites/1.spr32");
1068 precache_model ("models/sprites/2.spr32");
1069 precache_model ("models/sprites/3.spr32");
1070 precache_model ("models/sprites/4.spr32");
1071 precache_model ("models/sprites/5.spr32");
1072 precache_model ("models/sprites/6.spr32");
1073 precache_model ("models/sprites/7.spr32");
1074 precache_model ("models/sprites/8.spr32");
1075 precache_model ("models/sprites/9.spr32");
1076 precache_model ("models/sprites/10.spr32");
1078 // common weapon precaches
1079 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1080 precache_sound ("weapons/weapon_switch.wav");
1081 precache_sound ("weapons/weaponpickup.wav");
1082 precache_sound ("weapons/unavailable.wav");
1083 precache_sound ("weapons/dryfire.wav");
1084 if (g_grappling_hook)
1086 precache_sound ("weapons/hook_fire.wav"); // hook
1087 precache_sound ("weapons/hook_impact.wav"); // hook
1090 precache_model("models/elaser.mdl");
1091 precache_model("models/laser.mdl");
1092 precache_model("models/ebomb.mdl");
1095 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1097 if (!self.noise && self.music) // quake 3 uses the music field
1098 self.noise = self.music;
1100 // plays music for the level if there is any
1103 precache_sound (self.noise);
1104 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTEN_NONE);
1108 #include "precache-for-csqc.inc"
1112 void make_safe_for_remove(entity e)
1114 if (e.initialize_entity)
1116 entity ent, prev = world;
1117 for (ent = initialize_entity_first; ent; )
1119 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1121 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1122 // skip it in linked list
1125 prev.initialize_entity_next = ent.initialize_entity_next;
1126 ent = prev.initialize_entity_next;
1130 initialize_entity_first = ent.initialize_entity_next;
1131 ent = initialize_entity_first;
1137 ent = ent.initialize_entity_next;
1143 void objerror(string s)
1145 make_safe_for_remove(self);
1146 builtin_objerror(s);
1149 .float remove_except_protected_forbidden;
1150 void remove_except_protected(entity e)
1152 if(e.remove_except_protected_forbidden)
1153 error("not allowed to remove this at this point");
1157 void remove_unsafely(entity e)
1159 if(e.classname == "spike")
1160 error("Removing spikes is forbidden (crylink bug), please report");
1164 void remove_safely(entity e)
1166 make_safe_for_remove(e);
1170 void InitializeEntity(entity e, void(void) func, float order)
1174 if (!e || e.initialize_entity)
1176 // make a proxy initializer entity
1180 e.classname = "initialize_entity";
1184 e.initialize_entity = func;
1185 e.initialize_entity_order = order;
1187 cur = initialize_entity_first;
1191 if (!cur || cur.initialize_entity_order > order)
1193 // insert between prev and cur
1195 prev.initialize_entity_next = e;
1197 initialize_entity_first = e;
1198 e.initialize_entity_next = cur;
1202 cur = cur.initialize_entity_next;
1205 void InitializeEntitiesRun()
1208 startoflist = initialize_entity_first;
1209 initialize_entity_first = world;
1210 remove = remove_except_protected;
1211 for (self = startoflist; self; self = self.initialize_entity_next)
1213 self.remove_except_protected_forbidden = 1;
1215 for (self = startoflist; self; )
1218 var void(void) func;
1219 e = self.initialize_entity_next;
1220 func = self.initialize_entity;
1221 self.initialize_entity_order = 0;
1222 self.initialize_entity = func_null;
1223 self.initialize_entity_next = world;
1224 self.remove_except_protected_forbidden = 0;
1225 if (self.classname == "initialize_entity")
1229 builtin_remove(self);
1232 //dprint("Delayed initialization: ", self.classname, "\n");
1238 backtrace(strcat("Null function in: ", self.classname, "\n"));
1242 remove = remove_unsafely;
1245 void UncustomizeEntitiesRun()
1249 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1250 self.uncustomizeentityforclient();
1253 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1255 e.customizeentityforclient = customizer;
1256 e.uncustomizeentityforclient = uncustomizer;
1257 e.uncustomizeentityforclient_set = !!uncustomizer;
1260 void Net_LinkEntity(entity e, bool docull, float dt, bool(entity, int) sendfunc)
1264 if (e.classname == "")
1265 e.classname = "net_linked";
1267 if (e.model == "" || self.modelindex == 0)
1271 setmodel(e, "null");
1275 e.SendEntity = sendfunc;
1276 e.SendFlags = 0xFFFFFF;
1279 e.effects |= EF_NODEPTHTEST;
1283 e.nextthink = time + dt;
1284 e.think = SUB_Remove;
1289 .float(entity) isEliminated;
1290 float EliminatedPlayers_SendEntity(entity to, float sendflags)
1294 WriteByte(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS);
1295 WriteByte(MSG_ENTITY, sendflags);
1299 for(i = 1; i <= maxclients; i += 8)
1301 for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e))
1303 if(eliminatedPlayers.isEliminated(e))
1306 WriteByte(MSG_ENTITY, f);
1313 void EliminatedPlayers_Init(float(entity) isEliminated_func)
1315 if(eliminatedPlayers)
1317 backtrace("Can't spawn eliminatedPlayers again!");
1320 Net_LinkEntity(eliminatedPlayers = spawn(), false, 0, EliminatedPlayers_SendEntity);
1321 eliminatedPlayers.isEliminated = isEliminated_func;
1325 void adaptor_think2touch()
1334 void adaptor_think2use()
1346 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1348 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
1349 self.projectiledeathtype |= HITTYPE_SPLASH;
1350 adaptor_think2use();
1353 // deferred dropping
1354 void DropToFloor_Handler()
1356 builtin_droptofloor();
1357 self.dropped_origin = self.origin;
1362 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1367 float trace_hits_box_a0, trace_hits_box_a1;
1369 float trace_hits_box_1d(float end, float thmi, float thma)
1373 // just check if x is in range
1381 // do the trace with respect to x
1382 // 0 -> end has to stay in thmi -> thma
1383 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1384 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1385 if (trace_hits_box_a0 > trace_hits_box_a1)
1391 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1396 // now it is a trace from 0 to end
1398 trace_hits_box_a0 = 0;
1399 trace_hits_box_a1 = 1;
1401 if (!trace_hits_box_1d(end.x, thmi.x, thma.x))
1403 if (!trace_hits_box_1d(end.y, thmi.y, thma.y))
1405 if (!trace_hits_box_1d(end.z, thmi.z, thma.z))
1411 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1413 return trace_hits_box(start, end, thmi - ma, thma - mi);
1416 float SUB_NoImpactCheck()
1418 // zero hitcontents = this is not the real impact, but either the
1419 // mirror-impact of something hitting the projectile instead of the
1420 // projectile hitting the something, or a touchareagrid one. Neither of
1421 // these stop the projectile from moving, so...
1422 if(trace_dphitcontents == 0)
1424 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1425 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));
1428 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1430 if (other == world && self.size != '0 0 0')
1433 tic = self.velocity * sys_frametime;
1434 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1435 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1436 if (trace_fraction >= 1)
1438 dprint("Odd... did not hit...?\n");
1440 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1442 dprint("Detected and prevented the sky-grapple bug.\n");
1450 #define SUB_OwnerCheck() (other && (other == self.owner))
1452 void W_Crylink_Dequeue(entity e);
1453 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1455 if(SUB_OwnerCheck())
1457 if(SUB_NoImpactCheck())
1459 if(self.classname == "nade")
1460 return false; // no checks here
1461 else if(self.classname == "grapplinghook")
1462 RemoveGrapplingHook(self.realowner);
1463 else if(self.classname == "spike")
1465 W_Crylink_Dequeue(self);
1472 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1473 UpdateCSQCProjectile(self);
1478 void URI_Get_Callback(float id, float status, string data)
1480 if(url_URI_Get_Callback(id, status, data))
1484 else if (id == URI_GET_DISCARD)
1488 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
1491 Curl_URI_Get_Callback(id, status, data);
1493 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1496 OnlineBanList_URI_Get_Callback(id, status, data);
1500 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1504 string uid2name(string myuid) {
1506 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
1508 // FIXME remove this later after 0.6 release
1509 // convert old style broken records to correct style
1512 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
1515 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
1516 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
1521 s = "^1Unregistered Player";
1525 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1528 vector start, org, delta, end, enddown, mstart;
1531 m = e.dphitcontentsmask;
1532 e.dphitcontentsmask = goodcontents | badcontents;
1535 delta = world.maxs - world.mins;
1539 for (i = 0; i < attempts; ++i)
1541 start.x = org.x + random() * delta.x;
1542 start.y = org.y + random() * delta.y;
1543 start.z = org.z + random() * delta.z;
1545 // rule 1: start inside world bounds, and outside
1546 // solid, and don't start from somewhere where you can
1547 // fall down to evil
1548 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta.z, MOVE_NORMAL, e);
1549 if (trace_fraction >= 1)
1551 if (trace_startsolid)
1553 if (trace_dphitcontents & badcontents)
1555 if (trace_dphitq3surfaceflags & badsurfaceflags)
1558 // rule 2: if we are too high, lower the point
1559 if (trace_fraction * delta.z > maxaboveground)
1560 start = trace_endpos + '0 0 1' * maxaboveground;
1561 enddown = trace_endpos;
1563 // rule 3: make sure we aren't outside the map. This only works
1564 // for somewhat well formed maps. A good rule of thumb is that
1565 // the map should have a convex outside hull.
1566 // these can be traceLINES as we already verified the starting box
1567 mstart = start + 0.5 * (e.mins + e.maxs);
1568 traceline(mstart, mstart + '1 0 0' * delta.x, MOVE_NORMAL, e);
1569 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1571 traceline(mstart, mstart - '1 0 0' * delta.x, MOVE_NORMAL, e);
1572 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1574 traceline(mstart, mstart + '0 1 0' * delta.y, MOVE_NORMAL, e);
1575 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1577 traceline(mstart, mstart - '0 1 0' * delta.y, MOVE_NORMAL, e);
1578 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1580 traceline(mstart, mstart + '0 0 1' * delta.z, MOVE_NORMAL, e);
1581 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1584 // rule 4: we must "see" some spawnpoint or item
1585 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
1586 if(checkpvs(mstart, sp))
1587 if((traceline(mstart, sp.origin, MOVE_NORMAL, e), trace_fraction) >= 1)
1591 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
1592 if(checkpvs(mstart, sp))
1593 if((traceline(mstart, sp.origin + (sp.mins + sp.maxs) * 0.5, MOVE_NORMAL, e), trace_fraction) >= 1)
1599 // find a random vector to "look at"
1600 end.x = org.x + random() * delta.x;
1601 end.y = org.y + random() * delta.y;
1602 end.z = org.z + random() * delta.z;
1603 end = start + normalize(end - start) * vlen(delta);
1605 // rule 4: start TO end must not be too short
1606 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1607 if (trace_startsolid)
1609 if (trace_fraction < minviewdistance / vlen(delta))
1612 // rule 5: don't want to look at sky
1613 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
1616 // rule 6: we must not end up in trigger_hurt
1617 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
1623 e.dphitcontentsmask = m;
1627 setorigin(e, start);
1628 e.angles = vectoangles(end - start);
1629 dprint("Needed ", ftos(i + 1), " attempts\n");
1636 void write_recordmarker(entity pl, float tstart, float dt)
1638 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
1640 // also write a marker into demo files for demotc-race-record-extractor to find
1643 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
1644 " ", ftos(tstart), " ", ftos(dt), "\n"));
1647 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
1660 if(allowcenter) // 2: allow center handedness
1673 if(allowcenter) // 2: allow center handedness
1689 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
1694 if (autocvar_g_shootfromeye)
1698 if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
1699 else { vecs.y = 0; vecs.z -= 2; }
1707 else if (autocvar_g_shootfromcenter)
1712 else if ((s = autocvar_g_shootfromfixedorigin) != "")
1722 else if (autocvar_g_shootfromclient)
1724 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
1729 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
1731 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
1735 void attach_sameorigin(entity e, entity to, string tag)
1737 vector org, t_forward, t_left, t_up, e_forward, e_up;
1740 org = e.origin - gettaginfo(to, gettagindex(to, tag));
1741 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
1742 t_forward = v_forward * tagscale;
1743 t_left = v_right * -tagscale;
1744 t_up = v_up * tagscale;
1746 e.origin_x = org * t_forward;
1747 e.origin_y = org * t_left;
1748 e.origin_z = org * t_up;
1750 // current forward and up directions
1751 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1752 e.angles = AnglesTransform_FromVAngles(e.angles);
1754 e.angles = AnglesTransform_FromAngles(e.angles);
1755 fixedmakevectors(e.angles);
1757 // untransform forward, up!
1758 e_forward.x = v_forward * t_forward;
1759 e_forward.y = v_forward * t_left;
1760 e_forward.z = v_forward * t_up;
1761 e_up.x = v_up * t_forward;
1762 e_up.y = v_up * t_left;
1763 e_up.z = v_up * t_up;
1765 e.angles = fixedvectoangles2(e_forward, e_up);
1766 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1767 e.angles = AnglesTransform_ToVAngles(e.angles);
1769 e.angles = AnglesTransform_ToAngles(e.angles);
1771 setattachment(e, to, tag);
1772 setorigin(e, e.origin);
1775 void detach_sameorigin(entity e)
1778 org = gettaginfo(e, 0);
1779 e.angles = fixedvectoangles2(v_forward, v_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, world, "");
1786 setorigin(e, e.origin);
1789 void follow_sameorigin(entity e, entity to)
1791 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
1792 e.aiment = to; // make the hole follow bmodel
1793 e.punchangle = to.angles; // the original angles of bmodel
1794 e.view_ofs = e.origin - to.origin; // relative origin
1795 e.v_angle = e.angles - to.angles; // relative angles
1798 void unfollow_sameorigin(entity e)
1800 e.movetype = MOVETYPE_NONE;
1803 entity gettaginfo_relative_ent;
1804 vector gettaginfo_relative(entity e, float tag)
1806 if (!gettaginfo_relative_ent)
1808 gettaginfo_relative_ent = spawn();
1809 gettaginfo_relative_ent.effects = EF_NODRAW;
1811 gettaginfo_relative_ent.model = e.model;
1812 gettaginfo_relative_ent.modelindex = e.modelindex;
1813 gettaginfo_relative_ent.frame = e.frame;
1814 return gettaginfo(gettaginfo_relative_ent, tag);
1819 float modeleffect_SendEntity(entity to, int sf)
1822 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
1825 if(self.velocity != '0 0 0')
1827 if(self.angles != '0 0 0')
1829 if(self.avelocity != '0 0 0')
1832 WriteByte(MSG_ENTITY, f);
1833 WriteShort(MSG_ENTITY, self.modelindex);
1834 WriteByte(MSG_ENTITY, self.skin);
1835 WriteByte(MSG_ENTITY, self.frame);
1836 WriteCoord(MSG_ENTITY, self.origin.x);
1837 WriteCoord(MSG_ENTITY, self.origin.y);
1838 WriteCoord(MSG_ENTITY, self.origin.z);
1841 WriteCoord(MSG_ENTITY, self.velocity.x);
1842 WriteCoord(MSG_ENTITY, self.velocity.y);
1843 WriteCoord(MSG_ENTITY, self.velocity.z);
1847 WriteCoord(MSG_ENTITY, self.angles.x);
1848 WriteCoord(MSG_ENTITY, self.angles.y);
1849 WriteCoord(MSG_ENTITY, self.angles.z);
1853 WriteCoord(MSG_ENTITY, self.avelocity.x);
1854 WriteCoord(MSG_ENTITY, self.avelocity.y);
1855 WriteCoord(MSG_ENTITY, self.avelocity.z);
1857 WriteShort(MSG_ENTITY, self.scale * 256.0);
1858 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
1859 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
1860 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
1861 WriteByte(MSG_ENTITY, self.alpha * 255.0);
1866 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)
1871 e.classname = "modeleffect";
1879 e.teleport_time = t1;
1883 e.scale = s0 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1887 e.scale2 = s2 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1890 sz = max(e.scale, e.scale2);
1891 setsize(e, e.mins * sz, e.maxs * sz);
1892 Net_LinkEntity(e, false, 0.1, modeleffect_SendEntity);
1895 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
1897 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
1900 float randombit(float bits)
1902 if(!(bits & (bits-1))) // this ONLY holds for powers of two!
1911 for(f = 1; f <= bits; f *= 2)
1920 r = (r - 1) / (n - 1);
1927 float randombits(float bits, float k, float error_return)
1931 while(k > 0 && bits != r)
1933 r += randombit(bits - r);
1942 void randombit_test(float bits, float iter)
1946 print(ftos(randombit(bits)), "\n");
1951 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
1953 if(halflifedist > 0)
1954 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
1955 else if(halflifedist < 0)
1956 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
1968 oself.think = SUB_Remove;
1969 oself.nextthink = time;
1975 Execute func() after time + fdelay.
1976 self when func is executed = self when defer is called
1978 void defer(float fdelay, void() func)
1985 e.think = defer_think;
1986 e.nextthink = time + fdelay;
1989 .string aiment_classname;
1990 .float aiment_deadflag;
1991 void SetMovetypeFollow(entity ent, entity e)
1993 // FIXME this may not be warpzone aware
1994 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
1995 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.
1996 ent.aiment = e; // make the hole follow bmodel
1997 ent.punchangle = e.angles; // the original angles of bmodel
1998 ent.view_ofs = ent.origin - e.origin; // relative origin
1999 ent.v_angle = ent.angles - e.angles; // relative angles
2000 ent.aiment_classname = strzone(e.classname);
2001 ent.aiment_deadflag = e.deadflag;
2003 void UnsetMovetypeFollow(entity ent)
2005 ent.movetype = MOVETYPE_FLY;
2006 PROJECTILE_MAKETRIGGER(ent);
2009 float LostMovetypeFollow(entity ent)
2012 if(ent.movetype != MOVETYPE_FOLLOW)
2018 if(ent.aiment.classname != ent.aiment_classname)
2020 if(ent.aiment.deadflag != ent.aiment_deadflag)
2026 float isPushable(entity e)
2035 case "droppedweapon":
2036 case "keepawayball":
2037 case "nexball_basketball":
2038 case "nexball_football":
2040 case "bullet": // antilagged bullets can't hit this either
2043 if (e.projectiledeathtype)