1 #include "miscfunctions.qh"
4 #include "command/common.qh"
5 #include "constants.qh"
8 #include "mutators/mutators_include.qh"
10 #include "weapons/accuracy.qh"
11 #include "weapons/csqcprojectile.qh"
12 #include "weapons/selection.qh"
13 #include "../common/command/generic.qh"
14 #include "../common/constants.qh"
15 #include "../common/deathtypes.qh"
16 #include "../common/mapinfo.qh"
17 #include "../common/notifications.qh"
18 #include "../common/playerstats.qh"
19 #include "../common/teams.qh"
20 #include "../common/triggers/subs.qh"
21 #include "../common/urllib.qh"
22 #include "../common/util.qh"
23 #include "../common/turrets/sv_turrets.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 & ITEM_Shells.m_itemid) replacement = "shells";
307 else if (self.items & ITEM_Bullets.m_itemid) replacement = "bullets";
308 else if (self.items & ITEM_Rockets.m_itemid) replacement = "rockets";
309 else if (self.items & ITEM_Cells.m_itemid) replacement = "cells";
310 else if (self.items & ITEM_Plasma.m_itemid) 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));
430 MUTATOR_CALLHOOK(GetCvars);
432 Notification_GetCvars();
434 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
435 GetCvars_handleFloat(s, f, cvar_cl_autoscreenshot, "cl_autoscreenshot");
436 GetCvars_handleFloat(s, f, cvar_cl_jetpack_jump, "cl_jetpack_jump");
437 GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
438 GetCvars_handleString(s, f, cvar_cl_physics, "cl_physics");
439 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
440 GetCvars_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
441 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
442 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
443 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
444 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
445 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
446 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
447 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
448 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
449 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
450 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
451 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
452 GetCvars_handleFloat(s, f, cvar_cl_weaponimpulsemode, "cl_weaponimpulsemode");
453 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
454 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
455 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
456 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
457 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
458 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
460 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
461 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
463 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
464 GetCvars_handleFloat(s, f, cvar_cl_allow_uid2name, "cl_allow_uid2name");
465 GetCvars_handleFloat(s, f, cvar_cl_allow_uidtracking, "cl_allow_uidtracking");
466 GetCvars_handleFloat(s, f, cvar_cl_movement_track_canjump, "cl_movement_track_canjump");
467 GetCvars_handleFloat(s, f, cvar_cl_newusekeysupported, "cl_newusekeysupported");
469 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
472 if (s == "cl_weaponpriority")
473 self.switchweapon = w_getbestweapon(self);
474 if (s == "cl_allow_uidtracking")
475 PlayerStats_GameReport_AddPlayer(self);
479 // decolorizes and team colors the player name when needed
480 string playername(entity p)
483 if (teamplay && !intermission_running && IS_PLAYER(p))
485 t = Team_ColorCode(p.team);
486 return strcat(t, strdecolorize(p.netname));
492 float want_weapon(entity weaponinfo, float allguns) // WEAPONTODO: what still needs done?
494 int i = weaponinfo.weapon;
500 if (g_lms || g_ca || allguns)
502 if(weaponinfo.spawnflags & WEP_FLAG_NORMAL)
508 d = (i == WEP_SHOTGUN.m_id);
510 d = 0; // weapon is set a few lines later
512 d = !(!weaponinfo.weaponstart);
514 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
515 d |= (i == WEP_HOOK.m_id);
516 if(!g_cts && (weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED)) // never default mutator blocked guns
519 float t = weaponinfo.weaponstartoverride;
521 //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n"));
526 // 4: is set by default?
535 void readplayerstartcvars()
541 // initialize starting values for players
542 start_weapons = '0 0 0';
543 start_weapons_default = '0 0 0';
544 start_weapons_defaultmask = '0 0 0';
546 start_ammo_shells = 0;
547 start_ammo_nails = 0;
548 start_ammo_rockets = 0;
549 start_ammo_cells = 0;
550 start_ammo_plasma = 0;
551 start_health = cvar("g_balance_health_start");
552 start_armorvalue = cvar("g_balance_armor_start");
555 g_weaponarena_weapons = '0 0 0';
557 s = cvar_string("g_weaponarena");
558 if (s == "0" || s == "")
560 if(g_ca || g_freezetag)
564 if (s == "0" || s == "")
570 // forcibly turn off weaponarena
572 else if (s == "all" || s == "1")
575 g_weaponarena_list = "All Weapons";
576 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
578 e = get_weaponinfo(j);
579 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
580 g_weaponarena_weapons |= WepSet_FromWeapon(j);
583 else if (s == "most")
586 g_weaponarena_list = "Most Weapons";
587 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
589 e = get_weaponinfo(j);
590 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
591 if (e.spawnflags & WEP_FLAG_NORMAL)
592 g_weaponarena_weapons |= WepSet_FromWeapon(j);
595 else if (s == "none")
598 g_weaponarena_list = "No Weapons";
603 t = tokenize_console(s);
604 g_weaponarena_list = "";
605 for (i = 0; i < t; ++i)
608 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
610 e = get_weaponinfo(j);
613 g_weaponarena_weapons |= WepSet_FromWeapon(j);
614 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
620 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
623 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
627 g_weaponarena_random = cvar("g_weaponarena_random");
629 g_weaponarena_random = 0;
630 g_weaponarena_random_with_blaster = cvar("g_weaponarena_random_with_blaster");
634 g_weapon_stay = 0; // incompatible
635 start_weapons = g_weaponarena_weapons;
636 start_items |= IT_UNLIMITED_AMMO;
640 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
642 e = get_weaponinfo(i);
643 int w = want_weapon(e, false);
645 start_weapons |= WepSet_FromWeapon(i);
647 start_weapons_default |= WepSet_FromWeapon(i);
649 start_weapons_defaultmask |= WepSet_FromWeapon(i);
653 if(!cvar("g_use_ammunition"))
654 start_items |= IT_UNLIMITED_AMMO;
656 if(start_items & IT_UNLIMITED_WEAPON_AMMO)
658 start_ammo_shells = 999;
659 start_ammo_nails = 999;
660 start_ammo_rockets = 999;
661 start_ammo_cells = 999;
662 start_ammo_plasma = 999;
663 start_ammo_fuel = 999;
667 start_ammo_shells = cvar("g_start_ammo_shells");
668 start_ammo_nails = cvar("g_start_ammo_nails");
669 start_ammo_rockets = cvar("g_start_ammo_rockets");
670 start_ammo_cells = cvar("g_start_ammo_cells");
671 start_ammo_plasma = cvar("g_start_ammo_plasma");
672 start_ammo_fuel = cvar("g_start_ammo_fuel");
677 warmup_start_ammo_shells = start_ammo_shells;
678 warmup_start_ammo_nails = start_ammo_nails;
679 warmup_start_ammo_rockets = start_ammo_rockets;
680 warmup_start_ammo_cells = start_ammo_cells;
681 warmup_start_ammo_plasma = start_ammo_plasma;
682 warmup_start_ammo_fuel = start_ammo_fuel;
683 warmup_start_health = start_health;
684 warmup_start_armorvalue = start_armorvalue;
685 warmup_start_weapons = start_weapons;
686 warmup_start_weapons_default = start_weapons_default;
687 warmup_start_weapons_defaultmask = start_weapons_defaultmask;
689 if (!g_weaponarena && !g_ca && !g_freezetag)
691 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
692 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
693 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
694 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
695 warmup_start_ammo_plasma = cvar("g_warmup_start_ammo_plasma");
696 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
697 warmup_start_health = cvar("g_warmup_start_health");
698 warmup_start_armorvalue = cvar("g_warmup_start_armor");
699 warmup_start_weapons = '0 0 0';
700 warmup_start_weapons_default = '0 0 0';
701 warmup_start_weapons_defaultmask = '0 0 0';
702 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
704 e = get_weaponinfo(i);
705 int w = want_weapon(e, g_warmup_allguns);
707 warmup_start_weapons |= WepSet_FromWeapon(i);
709 warmup_start_weapons_default |= WepSet_FromWeapon(i);
711 warmup_start_weapons_defaultmask |= WepSet_FromWeapon(i);
717 start_items |= ITEM_Jetpack.m_itemid;
719 MUTATOR_CALLHOOK(SetStartItems);
721 if ((start_items & ITEM_Jetpack.m_itemid) || (g_grappling_hook && (start_weapons & WEPSET_HOOK)))
723 start_items |= ITEM_JetpackRegen.m_itemid;
724 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
725 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
728 WepSet precache_weapons = start_weapons;
729 if (g_warmup_allguns != 1)
730 precache_weapons |= warmup_start_weapons;
731 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
733 e = get_weaponinfo(i);
734 if(precache_weapons & WepSet_FromWeapon(i))
735 WEP_ACTION(i, WR_INIT);
738 start_ammo_shells = max(0, start_ammo_shells);
739 start_ammo_nails = max(0, start_ammo_nails);
740 start_ammo_rockets = max(0, start_ammo_rockets);
741 start_ammo_cells = max(0, start_ammo_cells);
742 start_ammo_plasma = max(0, start_ammo_plasma);
743 start_ammo_fuel = max(0, start_ammo_fuel);
745 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
746 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
747 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
748 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
749 warmup_start_ammo_plasma = max(0, warmup_start_ammo_plasma);
750 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
753 float sound_allowed(float destin, entity e)
755 // sounds from world may always pass
758 if (e.classname == "body")
760 else if (e.realowner && e.realowner != e)
762 else if (e.owner && e.owner != e)
767 // sounds to self may always pass
768 if (destin == MSG_ONE)
771 // sounds by players can be removed
772 if (autocvar_bot_sound_monopoly)
773 if (IS_REAL_CLIENT(e))
775 // anything else may pass
780 void sound(entity e, float chan, string samp, float vol, float attenu)
782 if (!sound_allowed(MSG_BROADCAST, e))
784 sound7(e, chan, samp, vol, attenu, 0, 0);
787 void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float attenu)
791 if (!sound_allowed(_dest, e))
794 entno = num_for_edict(e);
795 idx = precache_sound_index(samp);
800 attenu = floor(attenu * 64);
801 vol = floor(vol * 255);
804 sflags |= SND_VOLUME;
806 sflags |= SND_ATTENUATION;
807 if (entno >= 8192 || chan < 0 || chan > 7)
808 sflags |= SND_LARGEENTITY;
810 sflags |= SND_LARGESOUND;
812 WriteByte(_dest, SVC_SOUND);
813 WriteByte(_dest, sflags);
814 if (sflags & SND_VOLUME)
815 WriteByte(_dest, vol);
816 if (sflags & SND_ATTENUATION)
817 WriteByte(_dest, attenu);
818 if (sflags & SND_LARGEENTITY)
820 WriteShort(_dest, entno);
821 WriteByte(_dest, chan);
825 WriteShort(_dest, entno * 8 + chan);
827 if (sflags & SND_LARGESOUND)
828 WriteShort(_dest, idx);
830 WriteByte(_dest, idx);
832 WriteCoord(_dest, o.x);
833 WriteCoord(_dest, o.y);
834 WriteCoord(_dest, o.z);
836 void soundto(float _dest, entity e, float chan, string samp, float vol, float _atten)
840 if (!sound_allowed(_dest, e))
843 o = e.origin + 0.5 * (e.mins + e.maxs);
844 soundtoat(_dest, e, o, chan, samp, vol, _atten);
846 void soundat(entity e, vector o, float chan, string samp, float vol, float _atten)
848 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, _atten);
850 void stopsoundto(float _dest, entity e, float chan)
854 if (!sound_allowed(_dest, e))
857 entno = num_for_edict(e);
859 if (entno >= 8192 || chan < 0 || chan > 7)
862 idx = precache_sound_index("misc/null.wav");
863 sflags = SND_LARGEENTITY;
865 sflags |= SND_LARGESOUND;
866 WriteByte(_dest, SVC_SOUND);
867 WriteByte(_dest, sflags);
868 WriteShort(_dest, entno);
869 WriteByte(_dest, chan);
870 if (sflags & SND_LARGESOUND)
871 WriteShort(_dest, idx);
873 WriteByte(_dest, idx);
874 WriteCoord(_dest, e.origin.x);
875 WriteCoord(_dest, e.origin.y);
876 WriteCoord(_dest, e.origin.z);
880 WriteByte(_dest, SVC_STOPSOUND);
881 WriteShort(_dest, entno * 8 + chan);
884 void stopsound(entity e, float chan)
886 if (!sound_allowed(MSG_BROADCAST, e))
889 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
890 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
893 void play2(entity e, string filename)
895 //stuffcmd(e, strcat("play2 ", filename, "\n"));
897 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTEN_NONE);
900 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
902 float spamsound(entity e, float chan, string samp, float vol, float _atten)
904 if (!sound_allowed(MSG_BROADCAST, e))
907 if (time > e.spamtime)
910 sound(e, chan, samp, vol, _atten);
916 void play2team(float t, string filename)
920 if (autocvar_bot_sound_monopoly)
923 FOR_EACH_REALPLAYER(head)
926 play2(head, filename);
930 void play2all(string samp)
932 if (autocvar_bot_sound_monopoly)
935 sound(world, CH_INFO, samp, VOL_BASE, ATTEN_NONE);
938 void PrecachePlayerSounds(string f);
939 void precache_playermodel(string m)
941 float globhandle, i, n;
944 if(substring(m, -9,5) == "_lod1")
946 if(substring(m, -9,5) == "_lod2")
949 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
952 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
956 globhandle = search_begin(strcat(m, "_*.sounds"), true, false);
959 n = search_getsize(globhandle);
960 for (i = 0; i < n; ++i)
962 //print(search_getfilename(globhandle, i), "\n");
963 f = search_getfilename(globhandle, i);
964 PrecachePlayerSounds(f);
966 search_end(globhandle);
968 void precache_all_playermodels(string pattern)
970 float globhandle, i, n;
973 globhandle = search_begin(pattern, true, false);
976 n = search_getsize(globhandle);
977 for (i = 0; i < n; ++i)
979 //print(search_getfilename(globhandle, i), "\n");
980 f = search_getfilename(globhandle, i);
981 precache_playermodel(f);
983 search_end(globhandle);
988 // gamemode related things
989 precache_model ("models/misc/chatbubble.spr");
990 precache_model("models/ice/ice.md3");
992 // Precache all player models if desired
993 if (autocvar_sv_precacheplayermodels)
995 PrecachePlayerSounds("sound/player/default.sounds");
996 precache_all_playermodels("models/player/*.zym");
997 precache_all_playermodels("models/player/*.dpm");
998 precache_all_playermodels("models/player/*.md3");
999 precache_all_playermodels("models/player/*.psk");
1000 precache_all_playermodels("models/player/*.iqm");
1003 if (autocvar_sv_defaultcharacter)
1006 s = autocvar_sv_defaultplayermodel_red;
1008 precache_playermodel(s);
1009 s = autocvar_sv_defaultplayermodel_blue;
1011 precache_playermodel(s);
1012 s = autocvar_sv_defaultplayermodel_yellow;
1014 precache_playermodel(s);
1015 s = autocvar_sv_defaultplayermodel_pink;
1017 precache_playermodel(s);
1018 s = autocvar_sv_defaultplayermodel;
1020 precache_playermodel(s);
1025 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1026 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1029 // gore and miscellaneous sounds
1030 //precache_sound ("misc/h2ohit.wav");
1031 precache_model ("models/hook.md3");
1032 precache_sound ("misc/armorimpact.wav");
1033 precache_sound ("misc/bodyimpact1.wav");
1034 precache_sound ("misc/bodyimpact2.wav");
1035 precache_sound ("misc/gib.wav");
1036 precache_sound ("misc/gib_splat01.wav");
1037 precache_sound ("misc/gib_splat02.wav");
1038 precache_sound ("misc/gib_splat03.wav");
1039 precache_sound ("misc/gib_splat04.wav");
1040 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1041 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1042 precache_sound ("misc/null.wav");
1043 precache_sound ("misc/spawn.wav");
1044 precache_sound ("misc/talk.wav");
1045 precache_sound ("misc/teleport.wav");
1046 precache_sound ("misc/poweroff.wav");
1047 precache_sound ("player/lava.wav");
1048 precache_sound ("player/slime.wav");
1050 precache_model ("models/sprites/0.spr32");
1051 precache_model ("models/sprites/1.spr32");
1052 precache_model ("models/sprites/2.spr32");
1053 precache_model ("models/sprites/3.spr32");
1054 precache_model ("models/sprites/4.spr32");
1055 precache_model ("models/sprites/5.spr32");
1056 precache_model ("models/sprites/6.spr32");
1057 precache_model ("models/sprites/7.spr32");
1058 precache_model ("models/sprites/8.spr32");
1059 precache_model ("models/sprites/9.spr32");
1060 precache_model ("models/sprites/10.spr32");
1062 // common weapon precaches
1063 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1064 precache_sound ("weapons/weapon_switch.wav");
1065 precache_sound ("weapons/weaponpickup.wav");
1066 precache_sound ("weapons/unavailable.wav");
1067 precache_sound ("weapons/dryfire.wav");
1068 if (g_grappling_hook)
1070 precache_sound ("weapons/hook_fire.wav"); // hook
1071 precache_sound ("weapons/hook_impact.wav"); // hook
1074 precache_model("models/elaser.mdl");
1075 precache_model("models/laser.mdl");
1076 precache_model("models/ebomb.mdl");
1079 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1081 if (!self.noise && self.music) // quake 3 uses the music field
1082 self.noise = self.music;
1084 // plays music for the level if there is any
1087 precache_sound (self.noise);
1088 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTEN_NONE);
1092 #include "precache-for-csqc.inc"
1096 void make_safe_for_remove(entity e)
1098 if (e.initialize_entity)
1100 entity ent, prev = world;
1101 for (ent = initialize_entity_first; ent; )
1103 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1105 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1106 // skip it in linked list
1109 prev.initialize_entity_next = ent.initialize_entity_next;
1110 ent = prev.initialize_entity_next;
1114 initialize_entity_first = ent.initialize_entity_next;
1115 ent = initialize_entity_first;
1121 ent = ent.initialize_entity_next;
1127 void objerror(string s)
1129 make_safe_for_remove(self);
1130 builtin_objerror(s);
1133 .float remove_except_protected_forbidden;
1134 void remove_except_protected(entity e)
1136 if(e.remove_except_protected_forbidden)
1137 error("not allowed to remove this at this point");
1141 void remove_unsafely(entity e)
1143 if(e.classname == "spike")
1144 error("Removing spikes is forbidden (crylink bug), please report");
1148 void remove_safely(entity e)
1150 make_safe_for_remove(e);
1154 void InitializeEntity(entity e, void(void) func, float order)
1158 if (!e || e.initialize_entity)
1160 // make a proxy initializer entity
1164 e.classname = "initialize_entity";
1168 e.initialize_entity = func;
1169 e.initialize_entity_order = order;
1171 cur = initialize_entity_first;
1175 if (!cur || cur.initialize_entity_order > order)
1177 // insert between prev and cur
1179 prev.initialize_entity_next = e;
1181 initialize_entity_first = e;
1182 e.initialize_entity_next = cur;
1186 cur = cur.initialize_entity_next;
1189 void InitializeEntitiesRun()
1192 startoflist = initialize_entity_first;
1193 initialize_entity_first = world;
1194 remove = remove_except_protected;
1195 for (self = startoflist; self; self = self.initialize_entity_next)
1197 self.remove_except_protected_forbidden = 1;
1199 for (self = startoflist; self; )
1202 var void(void) func;
1203 e = self.initialize_entity_next;
1204 func = self.initialize_entity;
1205 self.initialize_entity_order = 0;
1206 self.initialize_entity = func_null;
1207 self.initialize_entity_next = world;
1208 self.remove_except_protected_forbidden = 0;
1209 if (self.classname == "initialize_entity")
1213 builtin_remove(self);
1216 //dprint("Delayed initialization: ", self.classname, "\n");
1222 backtrace(strcat("Null function in: ", self.classname, "\n"));
1226 remove = remove_unsafely;
1229 void UncustomizeEntitiesRun()
1233 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1234 self.uncustomizeentityforclient();
1237 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1239 e.customizeentityforclient = customizer;
1240 e.uncustomizeentityforclient = uncustomizer;
1241 e.uncustomizeentityforclient_set = !!uncustomizer;
1244 void Net_LinkEntity(entity e, bool docull, float dt, bool(entity, int) sendfunc)
1248 if (e.classname == "")
1249 e.classname = "net_linked";
1251 if (e.model == "" || self.modelindex == 0)
1255 setmodel(e, "null");
1259 e.SendEntity = sendfunc;
1260 e.SendFlags = 0xFFFFFF;
1263 e.effects |= EF_NODEPTHTEST;
1267 e.nextthink = time + dt;
1268 e.think = SUB_Remove;
1273 .float(entity) isEliminated;
1274 float EliminatedPlayers_SendEntity(entity to, float sendflags)
1278 WriteByte(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS);
1279 WriteByte(MSG_ENTITY, sendflags);
1283 for(i = 1; i <= maxclients; i += 8)
1285 for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e))
1287 if(eliminatedPlayers.isEliminated(e))
1290 WriteByte(MSG_ENTITY, f);
1297 void EliminatedPlayers_Init(float(entity) isEliminated_func)
1299 if(eliminatedPlayers)
1301 backtrace("Can't spawn eliminatedPlayers again!");
1304 Net_LinkEntity(eliminatedPlayers = spawn(), false, 0, EliminatedPlayers_SendEntity);
1305 eliminatedPlayers.isEliminated = isEliminated_func;
1309 void adaptor_think2touch()
1318 void adaptor_think2use()
1330 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1332 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
1333 self.projectiledeathtype |= HITTYPE_SPLASH;
1334 adaptor_think2use();
1337 // deferred dropping
1338 void DropToFloor_Handler()
1340 builtin_droptofloor();
1341 self.dropped_origin = self.origin;
1346 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1351 float trace_hits_box_a0, trace_hits_box_a1;
1353 float trace_hits_box_1d(float end, float thmi, float thma)
1357 // just check if x is in range
1365 // do the trace with respect to x
1366 // 0 -> end has to stay in thmi -> thma
1367 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1368 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1369 if (trace_hits_box_a0 > trace_hits_box_a1)
1375 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1380 // now it is a trace from 0 to end
1382 trace_hits_box_a0 = 0;
1383 trace_hits_box_a1 = 1;
1385 if (!trace_hits_box_1d(end.x, thmi.x, thma.x))
1387 if (!trace_hits_box_1d(end.y, thmi.y, thma.y))
1389 if (!trace_hits_box_1d(end.z, thmi.z, thma.z))
1395 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1397 return trace_hits_box(start, end, thmi - ma, thma - mi);
1400 float SUB_NoImpactCheck()
1402 // zero hitcontents = this is not the real impact, but either the
1403 // mirror-impact of something hitting the projectile instead of the
1404 // projectile hitting the something, or a touchareagrid one. Neither of
1405 // these stop the projectile from moving, so...
1406 if(trace_dphitcontents == 0)
1408 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1409 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));
1412 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1414 if (other == world && self.size != '0 0 0')
1417 tic = self.velocity * sys_frametime;
1418 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1419 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1420 if (trace_fraction >= 1)
1422 dprint("Odd... did not hit...?\n");
1424 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1426 dprint("Detected and prevented the sky-grapple bug.\n");
1434 #define SUB_OwnerCheck() (other && (other == self.owner))
1436 void W_Crylink_Dequeue(entity e);
1437 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1439 if(SUB_OwnerCheck())
1441 if(SUB_NoImpactCheck())
1443 if(self.classname == "nade")
1444 return false; // no checks here
1445 else if(self.classname == "grapplinghook")
1446 RemoveGrapplingHook(self.realowner);
1447 else if(self.classname == "spike")
1449 W_Crylink_Dequeue(self);
1456 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1457 UpdateCSQCProjectile(self);
1462 void URI_Get_Callback(float id, float status, string data)
1464 if(url_URI_Get_Callback(id, status, data))
1468 else if (id == URI_GET_DISCARD)
1472 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
1475 Curl_URI_Get_Callback(id, status, data);
1477 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1480 OnlineBanList_URI_Get_Callback(id, status, data);
1484 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1488 string uid2name(string myuid) {
1490 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
1492 // FIXME remove this later after 0.6 release
1493 // convert old style broken records to correct style
1496 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
1499 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
1500 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
1505 s = "^1Unregistered Player";
1509 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1512 vector start, org, delta, end, enddown, mstart;
1515 m = e.dphitcontentsmask;
1516 e.dphitcontentsmask = goodcontents | badcontents;
1519 delta = world.maxs - world.mins;
1523 for (i = 0; i < attempts; ++i)
1525 start.x = org.x + random() * delta.x;
1526 start.y = org.y + random() * delta.y;
1527 start.z = org.z + random() * delta.z;
1529 // rule 1: start inside world bounds, and outside
1530 // solid, and don't start from somewhere where you can
1531 // fall down to evil
1532 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta.z, MOVE_NORMAL, e);
1533 if (trace_fraction >= 1)
1535 if (trace_startsolid)
1537 if (trace_dphitcontents & badcontents)
1539 if (trace_dphitq3surfaceflags & badsurfaceflags)
1542 // rule 2: if we are too high, lower the point
1543 if (trace_fraction * delta.z > maxaboveground)
1544 start = trace_endpos + '0 0 1' * maxaboveground;
1545 enddown = trace_endpos;
1547 // rule 3: make sure we aren't outside the map. This only works
1548 // for somewhat well formed maps. A good rule of thumb is that
1549 // the map should have a convex outside hull.
1550 // these can be traceLINES as we already verified the starting box
1551 mstart = start + 0.5 * (e.mins + e.maxs);
1552 traceline(mstart, mstart + '1 0 0' * delta.x, MOVE_NORMAL, e);
1553 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1555 traceline(mstart, mstart - '1 0 0' * delta.x, MOVE_NORMAL, e);
1556 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1558 traceline(mstart, mstart + '0 1 0' * delta.y, MOVE_NORMAL, e);
1559 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1561 traceline(mstart, mstart - '0 1 0' * delta.y, MOVE_NORMAL, e);
1562 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1564 traceline(mstart, mstart + '0 0 1' * delta.z, MOVE_NORMAL, e);
1565 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1568 // rule 4: we must "see" some spawnpoint or item
1569 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
1570 if(checkpvs(mstart, sp))
1571 if((traceline(mstart, sp.origin, MOVE_NORMAL, e), trace_fraction) >= 1)
1575 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
1576 if(checkpvs(mstart, sp))
1577 if((traceline(mstart, sp.origin + (sp.mins + sp.maxs) * 0.5, MOVE_NORMAL, e), trace_fraction) >= 1)
1583 // find a random vector to "look at"
1584 end.x = org.x + random() * delta.x;
1585 end.y = org.y + random() * delta.y;
1586 end.z = org.z + random() * delta.z;
1587 end = start + normalize(end - start) * vlen(delta);
1589 // rule 4: start TO end must not be too short
1590 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1591 if (trace_startsolid)
1593 if (trace_fraction < minviewdistance / vlen(delta))
1596 // rule 5: don't want to look at sky
1597 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
1600 // rule 6: we must not end up in trigger_hurt
1601 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
1607 e.dphitcontentsmask = m;
1611 setorigin(e, start);
1612 e.angles = vectoangles(end - start);
1613 dprint("Needed ", ftos(i + 1), " attempts\n");
1620 void write_recordmarker(entity pl, float tstart, float dt)
1622 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
1624 // also write a marker into demo files for demotc-race-record-extractor to find
1627 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
1628 " ", ftos(tstart), " ", ftos(dt), "\n"));
1631 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
1644 if(allowcenter) // 2: allow center handedness
1657 if(allowcenter) // 2: allow center handedness
1673 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
1678 if (autocvar_g_shootfromeye)
1682 if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
1683 else { vecs.y = 0; vecs.z -= 2; }
1691 else if (autocvar_g_shootfromcenter)
1696 else if ((s = autocvar_g_shootfromfixedorigin) != "")
1706 else if (autocvar_g_shootfromclient)
1708 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
1713 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
1715 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
1719 void attach_sameorigin(entity e, entity to, string tag)
1721 vector org, t_forward, t_left, t_up, e_forward, e_up;
1724 org = e.origin - gettaginfo(to, gettagindex(to, tag));
1725 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
1726 t_forward = v_forward * tagscale;
1727 t_left = v_right * -tagscale;
1728 t_up = v_up * tagscale;
1730 e.origin_x = org * t_forward;
1731 e.origin_y = org * t_left;
1732 e.origin_z = org * t_up;
1734 // current forward and up directions
1735 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1736 e.angles = AnglesTransform_FromVAngles(e.angles);
1738 e.angles = AnglesTransform_FromAngles(e.angles);
1739 fixedmakevectors(e.angles);
1741 // untransform forward, up!
1742 e_forward.x = v_forward * t_forward;
1743 e_forward.y = v_forward * t_left;
1744 e_forward.z = v_forward * t_up;
1745 e_up.x = v_up * t_forward;
1746 e_up.y = v_up * t_left;
1747 e_up.z = v_up * t_up;
1749 e.angles = fixedvectoangles2(e_forward, e_up);
1750 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1751 e.angles = AnglesTransform_ToVAngles(e.angles);
1753 e.angles = AnglesTransform_ToAngles(e.angles);
1755 setattachment(e, to, tag);
1756 setorigin(e, e.origin);
1759 void detach_sameorigin(entity e)
1762 org = gettaginfo(e, 0);
1763 e.angles = fixedvectoangles2(v_forward, v_up);
1764 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1765 e.angles = AnglesTransform_ToVAngles(e.angles);
1767 e.angles = AnglesTransform_ToAngles(e.angles);
1769 setattachment(e, world, "");
1770 setorigin(e, e.origin);
1773 void follow_sameorigin(entity e, entity to)
1775 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
1776 e.aiment = to; // make the hole follow bmodel
1777 e.punchangle = to.angles; // the original angles of bmodel
1778 e.view_ofs = e.origin - to.origin; // relative origin
1779 e.v_angle = e.angles - to.angles; // relative angles
1782 void unfollow_sameorigin(entity e)
1784 e.movetype = MOVETYPE_NONE;
1787 entity gettaginfo_relative_ent;
1788 vector gettaginfo_relative(entity e, float tag)
1790 if (!gettaginfo_relative_ent)
1792 gettaginfo_relative_ent = spawn();
1793 gettaginfo_relative_ent.effects = EF_NODRAW;
1795 gettaginfo_relative_ent.model = e.model;
1796 gettaginfo_relative_ent.modelindex = e.modelindex;
1797 gettaginfo_relative_ent.frame = e.frame;
1798 return gettaginfo(gettaginfo_relative_ent, tag);
1803 float modeleffect_SendEntity(entity to, int sf)
1806 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
1809 if(self.velocity != '0 0 0')
1811 if(self.angles != '0 0 0')
1813 if(self.avelocity != '0 0 0')
1816 WriteByte(MSG_ENTITY, f);
1817 WriteShort(MSG_ENTITY, self.modelindex);
1818 WriteByte(MSG_ENTITY, self.skin);
1819 WriteByte(MSG_ENTITY, self.frame);
1820 WriteCoord(MSG_ENTITY, self.origin.x);
1821 WriteCoord(MSG_ENTITY, self.origin.y);
1822 WriteCoord(MSG_ENTITY, self.origin.z);
1825 WriteCoord(MSG_ENTITY, self.velocity.x);
1826 WriteCoord(MSG_ENTITY, self.velocity.y);
1827 WriteCoord(MSG_ENTITY, self.velocity.z);
1831 WriteCoord(MSG_ENTITY, self.angles.x);
1832 WriteCoord(MSG_ENTITY, self.angles.y);
1833 WriteCoord(MSG_ENTITY, self.angles.z);
1837 WriteCoord(MSG_ENTITY, self.avelocity.x);
1838 WriteCoord(MSG_ENTITY, self.avelocity.y);
1839 WriteCoord(MSG_ENTITY, self.avelocity.z);
1841 WriteShort(MSG_ENTITY, self.scale * 256.0);
1842 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
1843 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
1844 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
1845 WriteByte(MSG_ENTITY, self.alpha * 255.0);
1850 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)
1855 e.classname = "modeleffect";
1863 e.teleport_time = t1;
1867 e.scale = s0 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1871 e.scale2 = s2 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1874 sz = max(e.scale, e.scale2);
1875 setsize(e, e.mins * sz, e.maxs * sz);
1876 Net_LinkEntity(e, false, 0.1, modeleffect_SendEntity);
1879 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
1881 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
1884 float randombit(float bits)
1886 if(!(bits & (bits-1))) // this ONLY holds for powers of two!
1895 for(f = 1; f <= bits; f *= 2)
1904 r = (r - 1) / (n - 1);
1911 float randombits(float bits, float k, float error_return)
1915 while(k > 0 && bits != r)
1917 r += randombit(bits - r);
1926 void randombit_test(float bits, float iter)
1930 print(ftos(randombit(bits)), "\n");
1935 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
1937 if(halflifedist > 0)
1938 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
1939 else if(halflifedist < 0)
1940 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
1952 oself.think = SUB_Remove;
1953 oself.nextthink = time;
1959 Execute func() after time + fdelay.
1960 self when func is executed = self when defer is called
1962 void defer(float fdelay, void() func)
1969 e.think = defer_think;
1970 e.nextthink = time + fdelay;
1973 .string aiment_classname;
1974 .float aiment_deadflag;
1975 void SetMovetypeFollow(entity ent, entity e)
1977 // FIXME this may not be warpzone aware
1978 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
1979 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.
1980 ent.aiment = e; // make the hole follow bmodel
1981 ent.punchangle = e.angles; // the original angles of bmodel
1982 ent.view_ofs = ent.origin - e.origin; // relative origin
1983 ent.v_angle = ent.angles - e.angles; // relative angles
1984 ent.aiment_classname = strzone(e.classname);
1985 ent.aiment_deadflag = e.deadflag;
1987 void UnsetMovetypeFollow(entity ent)
1989 ent.movetype = MOVETYPE_FLY;
1990 PROJECTILE_MAKETRIGGER(ent);
1993 float LostMovetypeFollow(entity ent)
1996 if(ent.movetype != MOVETYPE_FOLLOW)
2002 if(ent.aiment.classname != ent.aiment_classname)
2004 if(ent.aiment.deadflag != ent.aiment_deadflag)
2010 float isPushable(entity e)
2019 case "droppedweapon":
2020 case "keepawayball":
2021 case "nexball_basketball":
2022 case "nexball_football":
2024 case "bullet": // antilagged bullets can't hit this either
2027 if (e.projectiledeathtype)