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 float want_weapon(entity weaponinfo, float allguns) // WEAPONTODO: what still needs done?
495 int i = weaponinfo.weapon;
501 if (g_lms || g_ca || allguns)
503 if(weaponinfo.spawnflags & WEP_FLAG_NORMAL)
509 d = (i == WEP_SHOTGUN);
511 d = 0; // weapon is set a few lines later
513 d = !(!weaponinfo.weaponstart);
515 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
516 d |= (i == WEP_HOOK);
517 if(!g_cts && (weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED)) // never default mutator blocked guns
520 float t = weaponinfo.weaponstartoverride;
522 //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n"));
527 // 4: is set by default?
536 void readplayerstartcvars()
542 // initialize starting values for players
543 start_weapons = '0 0 0';
544 start_weapons_default = '0 0 0';
545 start_weapons_defaultmask = '0 0 0';
547 start_ammo_shells = 0;
548 start_ammo_nails = 0;
549 start_ammo_rockets = 0;
550 start_ammo_cells = 0;
551 start_ammo_plasma = 0;
552 start_health = cvar("g_balance_health_start");
553 start_armorvalue = cvar("g_balance_armor_start");
556 g_weaponarena_weapons = '0 0 0';
558 s = cvar_string("g_weaponarena");
559 if (s == "0" || s == "")
561 if(g_ca || g_freezetag)
565 if (s == "0" || s == "")
571 // forcibly turn off weaponarena
573 else if (s == "all" || s == "1")
576 g_weaponarena_list = "All Weapons";
577 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
579 e = get_weaponinfo(j);
580 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
581 g_weaponarena_weapons |= WepSet_FromWeapon(j);
584 else if (s == "most")
587 g_weaponarena_list = "Most Weapons";
588 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
590 e = get_weaponinfo(j);
591 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
592 if (e.spawnflags & WEP_FLAG_NORMAL)
593 g_weaponarena_weapons |= WepSet_FromWeapon(j);
596 else if (s == "none")
599 g_weaponarena_list = "No Weapons";
604 t = tokenize_console(s);
605 g_weaponarena_list = "";
606 for (i = 0; i < t; ++i)
609 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
611 e = get_weaponinfo(j);
614 g_weaponarena_weapons |= WepSet_FromWeapon(j);
615 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
621 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
624 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
628 g_weaponarena_random = cvar("g_weaponarena_random");
630 g_weaponarena_random = 0;
631 g_weaponarena_random_with_blaster = cvar("g_weaponarena_random_with_blaster");
635 g_weapon_stay = 0; // incompatible
636 start_weapons = g_weaponarena_weapons;
637 start_items |= IT_UNLIMITED_AMMO;
641 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
643 e = get_weaponinfo(i);
644 int w = want_weapon(e, false);
646 start_weapons |= WepSet_FromWeapon(i);
648 start_weapons_default |= WepSet_FromWeapon(i);
650 start_weapons_defaultmask |= WepSet_FromWeapon(i);
654 if(!cvar("g_use_ammunition"))
655 start_items |= IT_UNLIMITED_AMMO;
657 if(start_items & IT_UNLIMITED_WEAPON_AMMO)
659 start_ammo_shells = 999;
660 start_ammo_nails = 999;
661 start_ammo_rockets = 999;
662 start_ammo_cells = 999;
663 start_ammo_plasma = 999;
664 start_ammo_fuel = 999;
668 start_ammo_shells = cvar("g_start_ammo_shells");
669 start_ammo_nails = cvar("g_start_ammo_nails");
670 start_ammo_rockets = cvar("g_start_ammo_rockets");
671 start_ammo_cells = cvar("g_start_ammo_cells");
672 start_ammo_plasma = cvar("g_start_ammo_plasma");
673 start_ammo_fuel = cvar("g_start_ammo_fuel");
678 warmup_start_ammo_shells = start_ammo_shells;
679 warmup_start_ammo_nails = start_ammo_nails;
680 warmup_start_ammo_rockets = start_ammo_rockets;
681 warmup_start_ammo_cells = start_ammo_cells;
682 warmup_start_ammo_plasma = start_ammo_plasma;
683 warmup_start_ammo_fuel = start_ammo_fuel;
684 warmup_start_health = start_health;
685 warmup_start_armorvalue = start_armorvalue;
686 warmup_start_weapons = start_weapons;
687 warmup_start_weapons_default = start_weapons_default;
688 warmup_start_weapons_defaultmask = start_weapons_defaultmask;
690 if (!g_weaponarena && !g_ca && !g_freezetag)
692 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
693 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
694 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
695 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
696 warmup_start_ammo_plasma = cvar("g_warmup_start_ammo_plasma");
697 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
698 warmup_start_health = cvar("g_warmup_start_health");
699 warmup_start_armorvalue = cvar("g_warmup_start_armor");
700 warmup_start_weapons = '0 0 0';
701 warmup_start_weapons_default = '0 0 0';
702 warmup_start_weapons_defaultmask = '0 0 0';
703 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
705 e = get_weaponinfo(i);
706 int w = want_weapon(e, g_warmup_allguns);
708 warmup_start_weapons |= WepSet_FromWeapon(i);
710 warmup_start_weapons_default |= WepSet_FromWeapon(i);
712 warmup_start_weapons_defaultmask |= WepSet_FromWeapon(i);
718 start_items |= IT_JETPACK;
720 MUTATOR_CALLHOOK(SetStartItems);
722 if ((start_items & IT_JETPACK) || (g_grappling_hook && (start_weapons & WEPSET_HOOK)))
724 start_items |= IT_FUEL_REGEN;
725 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
726 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
729 WepSet precache_weapons = start_weapons;
730 if (g_warmup_allguns != 1)
731 precache_weapons |= warmup_start_weapons;
732 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
734 e = get_weaponinfo(i);
735 if(precache_weapons & WepSet_FromWeapon(i))
736 WEP_ACTION(i, WR_INIT);
739 start_ammo_shells = max(0, start_ammo_shells);
740 start_ammo_nails = max(0, start_ammo_nails);
741 start_ammo_rockets = max(0, start_ammo_rockets);
742 start_ammo_cells = max(0, start_ammo_cells);
743 start_ammo_plasma = max(0, start_ammo_plasma);
744 start_ammo_fuel = max(0, start_ammo_fuel);
746 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
747 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
748 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
749 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
750 warmup_start_ammo_plasma = max(0, warmup_start_ammo_plasma);
751 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
754 float sound_allowed(float destin, entity e)
756 // sounds from world may always pass
759 if (e.classname == "body")
761 else if (e.realowner && e.realowner != e)
763 else if (e.owner && e.owner != e)
768 // sounds to self may always pass
769 if (destin == MSG_ONE)
772 // sounds by players can be removed
773 if (autocvar_bot_sound_monopoly)
774 if (IS_REAL_CLIENT(e))
776 // anything else may pass
781 void sound(entity e, float chan, string samp, float vol, float attenu)
783 if (!sound_allowed(MSG_BROADCAST, e))
785 sound7(e, chan, samp, vol, attenu, 0, 0);
788 void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float attenu)
792 if (!sound_allowed(_dest, e))
795 entno = num_for_edict(e);
796 idx = precache_sound_index(samp);
801 attenu = floor(attenu * 64);
802 vol = floor(vol * 255);
805 sflags |= SND_VOLUME;
807 sflags |= SND_ATTENUATION;
808 if (entno >= 8192 || chan < 0 || chan > 7)
809 sflags |= SND_LARGEENTITY;
811 sflags |= SND_LARGESOUND;
813 WriteByte(_dest, SVC_SOUND);
814 WriteByte(_dest, sflags);
815 if (sflags & SND_VOLUME)
816 WriteByte(_dest, vol);
817 if (sflags & SND_ATTENUATION)
818 WriteByte(_dest, attenu);
819 if (sflags & SND_LARGEENTITY)
821 WriteShort(_dest, entno);
822 WriteByte(_dest, chan);
826 WriteShort(_dest, entno * 8 + chan);
828 if (sflags & SND_LARGESOUND)
829 WriteShort(_dest, idx);
831 WriteByte(_dest, idx);
833 WriteCoord(_dest, o.x);
834 WriteCoord(_dest, o.y);
835 WriteCoord(_dest, o.z);
837 void soundto(float _dest, entity e, float chan, string samp, float vol, float _atten)
841 if (!sound_allowed(_dest, e))
844 o = e.origin + 0.5 * (e.mins + e.maxs);
845 soundtoat(_dest, e, o, chan, samp, vol, _atten);
847 void soundat(entity e, vector o, float chan, string samp, float vol, float _atten)
849 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, _atten);
851 void stopsoundto(float _dest, entity e, float chan)
855 if (!sound_allowed(_dest, e))
858 entno = num_for_edict(e);
860 if (entno >= 8192 || chan < 0 || chan > 7)
863 idx = precache_sound_index("misc/null.wav");
864 sflags = SND_LARGEENTITY;
866 sflags |= SND_LARGESOUND;
867 WriteByte(_dest, SVC_SOUND);
868 WriteByte(_dest, sflags);
869 WriteShort(_dest, entno);
870 WriteByte(_dest, chan);
871 if (sflags & SND_LARGESOUND)
872 WriteShort(_dest, idx);
874 WriteByte(_dest, idx);
875 WriteCoord(_dest, e.origin.x);
876 WriteCoord(_dest, e.origin.y);
877 WriteCoord(_dest, e.origin.z);
881 WriteByte(_dest, SVC_STOPSOUND);
882 WriteShort(_dest, entno * 8 + chan);
885 void stopsound(entity e, float chan)
887 if (!sound_allowed(MSG_BROADCAST, e))
890 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
891 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
894 void play2(entity e, string filename)
896 //stuffcmd(e, strcat("play2 ", filename, "\n"));
898 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTEN_NONE);
901 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
903 float spamsound(entity e, float chan, string samp, float vol, float _atten)
905 if (!sound_allowed(MSG_BROADCAST, e))
908 if (time > e.spamtime)
911 sound(e, chan, samp, vol, _atten);
917 void play2team(float t, string filename)
921 if (autocvar_bot_sound_monopoly)
924 FOR_EACH_REALPLAYER(head)
927 play2(head, filename);
931 void play2all(string samp)
933 if (autocvar_bot_sound_monopoly)
936 sound(world, CH_INFO, samp, VOL_BASE, ATTEN_NONE);
939 void PrecachePlayerSounds(string f);
940 void precache_playermodel(string m)
942 float globhandle, i, n;
945 if(substring(m, -9,5) == "_lod1")
947 if(substring(m, -9,5) == "_lod2")
950 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
953 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
957 globhandle = search_begin(strcat(m, "_*.sounds"), true, false);
960 n = search_getsize(globhandle);
961 for (i = 0; i < n; ++i)
963 //print(search_getfilename(globhandle, i), "\n");
964 f = search_getfilename(globhandle, i);
965 PrecachePlayerSounds(f);
967 search_end(globhandle);
969 void precache_all_playermodels(string pattern)
971 float globhandle, i, n;
974 globhandle = search_begin(pattern, true, false);
977 n = search_getsize(globhandle);
978 for (i = 0; i < n; ++i)
980 //print(search_getfilename(globhandle, i), "\n");
981 f = search_getfilename(globhandle, i);
982 precache_playermodel(f);
984 search_end(globhandle);
989 // gamemode related things
990 precache_model ("models/misc/chatbubble.spr");
991 precache_model("models/ice/ice.md3");
993 #ifdef TTURRETS_ENABLED
994 if (autocvar_g_turrets)
998 // Precache all player models if desired
999 if (autocvar_sv_precacheplayermodels)
1001 PrecachePlayerSounds("sound/player/default.sounds");
1002 precache_all_playermodels("models/player/*.zym");
1003 precache_all_playermodels("models/player/*.dpm");
1004 precache_all_playermodels("models/player/*.md3");
1005 precache_all_playermodels("models/player/*.psk");
1006 precache_all_playermodels("models/player/*.iqm");
1009 if (autocvar_sv_defaultcharacter)
1012 s = autocvar_sv_defaultplayermodel_red;
1014 precache_playermodel(s);
1015 s = autocvar_sv_defaultplayermodel_blue;
1017 precache_playermodel(s);
1018 s = autocvar_sv_defaultplayermodel_yellow;
1020 precache_playermodel(s);
1021 s = autocvar_sv_defaultplayermodel_pink;
1023 precache_playermodel(s);
1024 s = autocvar_sv_defaultplayermodel;
1026 precache_playermodel(s);
1031 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1032 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1035 // gore and miscellaneous sounds
1036 //precache_sound ("misc/h2ohit.wav");
1037 precache_model ("models/hook.md3");
1038 precache_sound ("misc/armorimpact.wav");
1039 precache_sound ("misc/bodyimpact1.wav");
1040 precache_sound ("misc/bodyimpact2.wav");
1041 precache_sound ("misc/gib.wav");
1042 precache_sound ("misc/gib_splat01.wav");
1043 precache_sound ("misc/gib_splat02.wav");
1044 precache_sound ("misc/gib_splat03.wav");
1045 precache_sound ("misc/gib_splat04.wav");
1046 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1047 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1048 precache_sound ("misc/null.wav");
1049 precache_sound ("misc/spawn.wav");
1050 precache_sound ("misc/talk.wav");
1051 precache_sound ("misc/teleport.wav");
1052 precache_sound ("misc/poweroff.wav");
1053 precache_sound ("player/lava.wav");
1054 precache_sound ("player/slime.wav");
1056 precache_model ("models/sprites/0.spr32");
1057 precache_model ("models/sprites/1.spr32");
1058 precache_model ("models/sprites/2.spr32");
1059 precache_model ("models/sprites/3.spr32");
1060 precache_model ("models/sprites/4.spr32");
1061 precache_model ("models/sprites/5.spr32");
1062 precache_model ("models/sprites/6.spr32");
1063 precache_model ("models/sprites/7.spr32");
1064 precache_model ("models/sprites/8.spr32");
1065 precache_model ("models/sprites/9.spr32");
1066 precache_model ("models/sprites/10.spr32");
1068 // common weapon precaches
1069 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1070 precache_sound ("weapons/weapon_switch.wav");
1071 precache_sound ("weapons/weaponpickup.wav");
1072 precache_sound ("weapons/unavailable.wav");
1073 precache_sound ("weapons/dryfire.wav");
1074 if (g_grappling_hook)
1076 precache_sound ("weapons/hook_fire.wav"); // hook
1077 precache_sound ("weapons/hook_impact.wav"); // hook
1080 precache_model("models/elaser.mdl");
1081 precache_model("models/laser.mdl");
1082 precache_model("models/ebomb.mdl");
1085 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1087 if (!self.noise && self.music) // quake 3 uses the music field
1088 self.noise = self.music;
1090 // plays music for the level if there is any
1093 precache_sound (self.noise);
1094 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTEN_NONE);
1098 #include "precache-for-csqc.inc"
1102 void make_safe_for_remove(entity e)
1104 if (e.initialize_entity)
1106 entity ent, prev = world;
1107 for (ent = initialize_entity_first; ent; )
1109 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1111 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1112 // skip it in linked list
1115 prev.initialize_entity_next = ent.initialize_entity_next;
1116 ent = prev.initialize_entity_next;
1120 initialize_entity_first = ent.initialize_entity_next;
1121 ent = initialize_entity_first;
1127 ent = ent.initialize_entity_next;
1133 void objerror(string s)
1135 make_safe_for_remove(self);
1136 builtin_objerror(s);
1139 .float remove_except_protected_forbidden;
1140 void remove_except_protected(entity e)
1142 if(e.remove_except_protected_forbidden)
1143 error("not allowed to remove this at this point");
1147 void remove_unsafely(entity e)
1149 if(e.classname == "spike")
1150 error("Removing spikes is forbidden (crylink bug), please report");
1154 void remove_safely(entity e)
1156 make_safe_for_remove(e);
1160 void InitializeEntity(entity e, void(void) func, float order)
1164 if (!e || e.initialize_entity)
1166 // make a proxy initializer entity
1170 e.classname = "initialize_entity";
1174 e.initialize_entity = func;
1175 e.initialize_entity_order = order;
1177 cur = initialize_entity_first;
1181 if (!cur || cur.initialize_entity_order > order)
1183 // insert between prev and cur
1185 prev.initialize_entity_next = e;
1187 initialize_entity_first = e;
1188 e.initialize_entity_next = cur;
1192 cur = cur.initialize_entity_next;
1195 void InitializeEntitiesRun()
1198 startoflist = initialize_entity_first;
1199 initialize_entity_first = world;
1200 remove = remove_except_protected;
1201 for (self = startoflist; self; self = self.initialize_entity_next)
1203 self.remove_except_protected_forbidden = 1;
1205 for (self = startoflist; self; )
1208 var void(void) func;
1209 e = self.initialize_entity_next;
1210 func = self.initialize_entity;
1211 self.initialize_entity_order = 0;
1212 self.initialize_entity = func_null;
1213 self.initialize_entity_next = world;
1214 self.remove_except_protected_forbidden = 0;
1215 if (self.classname == "initialize_entity")
1219 builtin_remove(self);
1222 //dprint("Delayed initialization: ", self.classname, "\n");
1228 backtrace(strcat("Null function in: ", self.classname, "\n"));
1232 remove = remove_unsafely;
1235 void UncustomizeEntitiesRun()
1239 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1240 self.uncustomizeentityforclient();
1243 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1245 e.customizeentityforclient = customizer;
1246 e.uncustomizeentityforclient = uncustomizer;
1247 e.uncustomizeentityforclient_set = !!uncustomizer;
1250 void Net_LinkEntity(entity e, bool docull, float dt, bool(entity, int) sendfunc)
1254 if (e.classname == "")
1255 e.classname = "net_linked";
1257 if (e.model == "" || self.modelindex == 0)
1261 setmodel(e, "null");
1265 e.SendEntity = sendfunc;
1266 e.SendFlags = 0xFFFFFF;
1269 e.effects |= EF_NODEPTHTEST;
1273 e.nextthink = time + dt;
1274 e.think = SUB_Remove;
1279 .float(entity) isEliminated;
1280 float EliminatedPlayers_SendEntity(entity to, float sendflags)
1284 WriteByte(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS);
1285 WriteByte(MSG_ENTITY, sendflags);
1289 for(i = 1; i <= maxclients; i += 8)
1291 for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e))
1293 if(eliminatedPlayers.isEliminated(e))
1296 WriteByte(MSG_ENTITY, f);
1303 void EliminatedPlayers_Init(float(entity) isEliminated_func)
1305 if(eliminatedPlayers)
1307 backtrace("Can't spawn eliminatedPlayers again!");
1310 Net_LinkEntity(eliminatedPlayers = spawn(), false, 0, EliminatedPlayers_SendEntity);
1311 eliminatedPlayers.isEliminated = isEliminated_func;
1315 void adaptor_think2touch()
1324 void adaptor_think2use()
1336 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1338 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
1339 self.projectiledeathtype |= HITTYPE_SPLASH;
1340 adaptor_think2use();
1343 // deferred dropping
1344 void DropToFloor_Handler()
1346 builtin_droptofloor();
1347 self.dropped_origin = self.origin;
1352 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1357 float trace_hits_box_a0, trace_hits_box_a1;
1359 float trace_hits_box_1d(float end, float thmi, float thma)
1363 // just check if x is in range
1371 // do the trace with respect to x
1372 // 0 -> end has to stay in thmi -> thma
1373 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1374 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1375 if (trace_hits_box_a0 > trace_hits_box_a1)
1381 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1386 // now it is a trace from 0 to end
1388 trace_hits_box_a0 = 0;
1389 trace_hits_box_a1 = 1;
1391 if (!trace_hits_box_1d(end.x, thmi.x, thma.x))
1393 if (!trace_hits_box_1d(end.y, thmi.y, thma.y))
1395 if (!trace_hits_box_1d(end.z, thmi.z, thma.z))
1401 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1403 return trace_hits_box(start, end, thmi - ma, thma - mi);
1406 float SUB_NoImpactCheck()
1408 // zero hitcontents = this is not the real impact, but either the
1409 // mirror-impact of something hitting the projectile instead of the
1410 // projectile hitting the something, or a touchareagrid one. Neither of
1411 // these stop the projectile from moving, so...
1412 if(trace_dphitcontents == 0)
1414 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1415 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));
1418 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1420 if (other == world && self.size != '0 0 0')
1423 tic = self.velocity * sys_frametime;
1424 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1425 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1426 if (trace_fraction >= 1)
1428 dprint("Odd... did not hit...?\n");
1430 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1432 dprint("Detected and prevented the sky-grapple bug.\n");
1440 #define SUB_OwnerCheck() (other && (other == self.owner))
1442 void W_Crylink_Dequeue(entity e);
1443 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1445 if(SUB_OwnerCheck())
1447 if(SUB_NoImpactCheck())
1449 if(self.classname == "nade")
1450 return false; // no checks here
1451 else if(self.classname == "grapplinghook")
1452 RemoveGrapplingHook(self.realowner);
1453 else if(self.classname == "spike")
1455 W_Crylink_Dequeue(self);
1462 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1463 UpdateCSQCProjectile(self);
1468 void URI_Get_Callback(float id, float status, string data)
1470 if(url_URI_Get_Callback(id, status, data))
1474 else if (id == URI_GET_DISCARD)
1478 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
1481 Curl_URI_Get_Callback(id, status, data);
1483 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1486 OnlineBanList_URI_Get_Callback(id, status, data);
1490 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1494 string uid2name(string myuid) {
1496 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
1498 // FIXME remove this later after 0.6 release
1499 // convert old style broken records to correct style
1502 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
1505 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
1506 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
1511 s = "^1Unregistered Player";
1515 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1518 vector start, org, delta, end, enddown, mstart;
1521 m = e.dphitcontentsmask;
1522 e.dphitcontentsmask = goodcontents | badcontents;
1525 delta = world.maxs - world.mins;
1529 for (i = 0; i < attempts; ++i)
1531 start.x = org.x + random() * delta.x;
1532 start.y = org.y + random() * delta.y;
1533 start.z = org.z + random() * delta.z;
1535 // rule 1: start inside world bounds, and outside
1536 // solid, and don't start from somewhere where you can
1537 // fall down to evil
1538 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta.z, MOVE_NORMAL, e);
1539 if (trace_fraction >= 1)
1541 if (trace_startsolid)
1543 if (trace_dphitcontents & badcontents)
1545 if (trace_dphitq3surfaceflags & badsurfaceflags)
1548 // rule 2: if we are too high, lower the point
1549 if (trace_fraction * delta.z > maxaboveground)
1550 start = trace_endpos + '0 0 1' * maxaboveground;
1551 enddown = trace_endpos;
1553 // rule 3: make sure we aren't outside the map. This only works
1554 // for somewhat well formed maps. A good rule of thumb is that
1555 // the map should have a convex outside hull.
1556 // these can be traceLINES as we already verified the starting box
1557 mstart = start + 0.5 * (e.mins + e.maxs);
1558 traceline(mstart, mstart + '1 0 0' * delta.x, MOVE_NORMAL, e);
1559 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1561 traceline(mstart, mstart - '1 0 0' * delta.x, MOVE_NORMAL, e);
1562 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1564 traceline(mstart, mstart + '0 1 0' * delta.y, MOVE_NORMAL, e);
1565 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1567 traceline(mstart, mstart - '0 1 0' * delta.y, MOVE_NORMAL, e);
1568 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1570 traceline(mstart, mstart + '0 0 1' * delta.z, MOVE_NORMAL, e);
1571 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1574 // rule 4: we must "see" some spawnpoint or item
1575 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
1576 if(checkpvs(mstart, sp))
1577 if((traceline(mstart, sp.origin, MOVE_NORMAL, e), trace_fraction) >= 1)
1581 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
1582 if(checkpvs(mstart, sp))
1583 if((traceline(mstart, sp.origin + (sp.mins + sp.maxs) * 0.5, MOVE_NORMAL, e), trace_fraction) >= 1)
1589 // find a random vector to "look at"
1590 end.x = org.x + random() * delta.x;
1591 end.y = org.y + random() * delta.y;
1592 end.z = org.z + random() * delta.z;
1593 end = start + normalize(end - start) * vlen(delta);
1595 // rule 4: start TO end must not be too short
1596 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1597 if (trace_startsolid)
1599 if (trace_fraction < minviewdistance / vlen(delta))
1602 // rule 5: don't want to look at sky
1603 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
1606 // rule 6: we must not end up in trigger_hurt
1607 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
1613 e.dphitcontentsmask = m;
1617 setorigin(e, start);
1618 e.angles = vectoangles(end - start);
1619 dprint("Needed ", ftos(i + 1), " attempts\n");
1626 void write_recordmarker(entity pl, float tstart, float dt)
1628 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
1630 // also write a marker into demo files for demotc-race-record-extractor to find
1633 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
1634 " ", ftos(tstart), " ", ftos(dt), "\n"));
1637 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
1650 if(allowcenter) // 2: allow center handedness
1663 if(allowcenter) // 2: allow center handedness
1679 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
1684 if (autocvar_g_shootfromeye)
1688 if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
1689 else { vecs.y = 0; vecs.z -= 2; }
1697 else if (autocvar_g_shootfromcenter)
1702 else if ((s = autocvar_g_shootfromfixedorigin) != "")
1712 else if (autocvar_g_shootfromclient)
1714 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
1719 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
1721 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
1725 void attach_sameorigin(entity e, entity to, string tag)
1727 vector org, t_forward, t_left, t_up, e_forward, e_up;
1730 org = e.origin - gettaginfo(to, gettagindex(to, tag));
1731 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
1732 t_forward = v_forward * tagscale;
1733 t_left = v_right * -tagscale;
1734 t_up = v_up * tagscale;
1736 e.origin_x = org * t_forward;
1737 e.origin_y = org * t_left;
1738 e.origin_z = org * t_up;
1740 // current forward and up directions
1741 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1742 e.angles = AnglesTransform_FromVAngles(e.angles);
1744 e.angles = AnglesTransform_FromAngles(e.angles);
1745 fixedmakevectors(e.angles);
1747 // untransform forward, up!
1748 e_forward.x = v_forward * t_forward;
1749 e_forward.y = v_forward * t_left;
1750 e_forward.z = v_forward * t_up;
1751 e_up.x = v_up * t_forward;
1752 e_up.y = v_up * t_left;
1753 e_up.z = v_up * t_up;
1755 e.angles = fixedvectoangles2(e_forward, e_up);
1756 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1757 e.angles = AnglesTransform_ToVAngles(e.angles);
1759 e.angles = AnglesTransform_ToAngles(e.angles);
1761 setattachment(e, to, tag);
1762 setorigin(e, e.origin);
1765 void detach_sameorigin(entity e)
1768 org = gettaginfo(e, 0);
1769 e.angles = fixedvectoangles2(v_forward, v_up);
1770 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1771 e.angles = AnglesTransform_ToVAngles(e.angles);
1773 e.angles = AnglesTransform_ToAngles(e.angles);
1775 setattachment(e, world, "");
1776 setorigin(e, e.origin);
1779 void follow_sameorigin(entity e, entity to)
1781 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
1782 e.aiment = to; // make the hole follow bmodel
1783 e.punchangle = to.angles; // the original angles of bmodel
1784 e.view_ofs = e.origin - to.origin; // relative origin
1785 e.v_angle = e.angles - to.angles; // relative angles
1788 void unfollow_sameorigin(entity e)
1790 e.movetype = MOVETYPE_NONE;
1793 entity gettaginfo_relative_ent;
1794 vector gettaginfo_relative(entity e, float tag)
1796 if (!gettaginfo_relative_ent)
1798 gettaginfo_relative_ent = spawn();
1799 gettaginfo_relative_ent.effects = EF_NODRAW;
1801 gettaginfo_relative_ent.model = e.model;
1802 gettaginfo_relative_ent.modelindex = e.modelindex;
1803 gettaginfo_relative_ent.frame = e.frame;
1804 return gettaginfo(gettaginfo_relative_ent, tag);
1809 float modeleffect_SendEntity(entity to, int sf)
1812 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
1815 if(self.velocity != '0 0 0')
1817 if(self.angles != '0 0 0')
1819 if(self.avelocity != '0 0 0')
1822 WriteByte(MSG_ENTITY, f);
1823 WriteShort(MSG_ENTITY, self.modelindex);
1824 WriteByte(MSG_ENTITY, self.skin);
1825 WriteByte(MSG_ENTITY, self.frame);
1826 WriteCoord(MSG_ENTITY, self.origin.x);
1827 WriteCoord(MSG_ENTITY, self.origin.y);
1828 WriteCoord(MSG_ENTITY, self.origin.z);
1831 WriteCoord(MSG_ENTITY, self.velocity.x);
1832 WriteCoord(MSG_ENTITY, self.velocity.y);
1833 WriteCoord(MSG_ENTITY, self.velocity.z);
1837 WriteCoord(MSG_ENTITY, self.angles.x);
1838 WriteCoord(MSG_ENTITY, self.angles.y);
1839 WriteCoord(MSG_ENTITY, self.angles.z);
1843 WriteCoord(MSG_ENTITY, self.avelocity.x);
1844 WriteCoord(MSG_ENTITY, self.avelocity.y);
1845 WriteCoord(MSG_ENTITY, self.avelocity.z);
1847 WriteShort(MSG_ENTITY, self.scale * 256.0);
1848 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
1849 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
1850 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
1851 WriteByte(MSG_ENTITY, self.alpha * 255.0);
1856 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)
1861 e.classname = "modeleffect";
1869 e.teleport_time = t1;
1873 e.scale = s0 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1877 e.scale2 = s2 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1880 sz = max(e.scale, e.scale2);
1881 setsize(e, e.mins * sz, e.maxs * sz);
1882 Net_LinkEntity(e, false, 0.1, modeleffect_SendEntity);
1885 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
1887 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
1890 float randombit(float bits)
1892 if(!(bits & (bits-1))) // this ONLY holds for powers of two!
1901 for(f = 1; f <= bits; f *= 2)
1910 r = (r - 1) / (n - 1);
1917 float randombits(float bits, float k, float error_return)
1921 while(k > 0 && bits != r)
1923 r += randombit(bits - r);
1932 void randombit_test(float bits, float iter)
1936 print(ftos(randombit(bits)), "\n");
1941 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
1943 if(halflifedist > 0)
1944 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
1945 else if(halflifedist < 0)
1946 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
1958 oself.think = SUB_Remove;
1959 oself.nextthink = time;
1965 Execute func() after time + fdelay.
1966 self when func is executed = self when defer is called
1968 void defer(float fdelay, void() func)
1975 e.think = defer_think;
1976 e.nextthink = time + fdelay;
1979 .string aiment_classname;
1980 .float aiment_deadflag;
1981 void SetMovetypeFollow(entity ent, entity e)
1983 // FIXME this may not be warpzone aware
1984 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
1985 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.
1986 ent.aiment = e; // make the hole follow bmodel
1987 ent.punchangle = e.angles; // the original angles of bmodel
1988 ent.view_ofs = ent.origin - e.origin; // relative origin
1989 ent.v_angle = ent.angles - e.angles; // relative angles
1990 ent.aiment_classname = strzone(e.classname);
1991 ent.aiment_deadflag = e.deadflag;
1993 void UnsetMovetypeFollow(entity ent)
1995 ent.movetype = MOVETYPE_FLY;
1996 PROJECTILE_MAKETRIGGER(ent);
1999 float LostMovetypeFollow(entity ent)
2002 if(ent.movetype != MOVETYPE_FOLLOW)
2008 if(ent.aiment.classname != ent.aiment_classname)
2010 if(ent.aiment.deadflag != ent.aiment_deadflag)
2016 float isPushable(entity e)
2025 case "droppedweapon":
2026 case "keepawayball":
2027 case "nexball_basketball":
2028 case "nexball_football":
2030 case "bullet": // antilagged bullets can't hit this either
2033 if (e.projectiledeathtype)