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));
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 vector randompos(vector m1, vector m2)
496 v.x = m2_x * random() + m1_x;
497 v.y = m2_y * random() + m1_y;
498 v.z = m2_z * random() + m1_z;
502 float want_weapon(entity weaponinfo, float allguns) // WEAPONTODO: what still needs done?
504 int i = weaponinfo.weapon;
510 if (g_lms || g_ca || allguns)
512 if(weaponinfo.spawnflags & WEP_FLAG_NORMAL)
518 d = (i == WEP_SHOTGUN);
520 d = 0; // weapon is set a few lines later
522 d = !(!weaponinfo.weaponstart);
524 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
525 d |= (i == WEP_HOOK);
526 if(!g_cts && (weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED)) // never default mutator blocked guns
529 float t = weaponinfo.weaponstartoverride;
531 //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n"));
536 // 4: is set by default?
545 void readplayerstartcvars()
551 // initialize starting values for players
552 start_weapons = '0 0 0';
553 start_weapons_default = '0 0 0';
554 start_weapons_defaultmask = '0 0 0';
556 start_ammo_shells = 0;
557 start_ammo_nails = 0;
558 start_ammo_rockets = 0;
559 start_ammo_cells = 0;
560 start_ammo_plasma = 0;
561 start_health = cvar("g_balance_health_start");
562 start_armorvalue = cvar("g_balance_armor_start");
565 g_weaponarena_weapons = '0 0 0';
567 s = cvar_string("g_weaponarena");
568 if (s == "0" || s == "")
570 if(g_ca || g_freezetag)
574 if (s == "0" || s == "")
580 // forcibly turn off weaponarena
582 else if (s == "all" || s == "1")
585 g_weaponarena_list = "All Weapons";
586 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
588 e = get_weaponinfo(j);
589 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
590 g_weaponarena_weapons |= WepSet_FromWeapon(j);
593 else if (s == "most")
596 g_weaponarena_list = "Most Weapons";
597 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
599 e = get_weaponinfo(j);
600 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
601 if (e.spawnflags & WEP_FLAG_NORMAL)
602 g_weaponarena_weapons |= WepSet_FromWeapon(j);
605 else if (s == "none")
608 g_weaponarena_list = "No Weapons";
613 t = tokenize_console(s);
614 g_weaponarena_list = "";
615 for (i = 0; i < t; ++i)
618 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
620 e = get_weaponinfo(j);
623 g_weaponarena_weapons |= WepSet_FromWeapon(j);
624 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
630 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
633 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
637 g_weaponarena_random = cvar("g_weaponarena_random");
639 g_weaponarena_random = 0;
640 g_weaponarena_random_with_blaster = cvar("g_weaponarena_random_with_blaster");
644 g_weapon_stay = 0; // incompatible
645 start_weapons = g_weaponarena_weapons;
646 start_items |= IT_UNLIMITED_AMMO;
650 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
652 e = get_weaponinfo(i);
653 int w = want_weapon(e, false);
655 start_weapons |= WepSet_FromWeapon(i);
657 start_weapons_default |= WepSet_FromWeapon(i);
659 start_weapons_defaultmask |= WepSet_FromWeapon(i);
663 if(!cvar("g_use_ammunition"))
664 start_items |= IT_UNLIMITED_AMMO;
666 if(start_items & IT_UNLIMITED_WEAPON_AMMO)
668 start_ammo_shells = 999;
669 start_ammo_nails = 999;
670 start_ammo_rockets = 999;
671 start_ammo_cells = 999;
672 start_ammo_plasma = 999;
673 start_ammo_fuel = 999;
677 start_ammo_shells = cvar("g_start_ammo_shells");
678 start_ammo_nails = cvar("g_start_ammo_nails");
679 start_ammo_rockets = cvar("g_start_ammo_rockets");
680 start_ammo_cells = cvar("g_start_ammo_cells");
681 start_ammo_plasma = cvar("g_start_ammo_plasma");
682 start_ammo_fuel = cvar("g_start_ammo_fuel");
687 warmup_start_ammo_shells = start_ammo_shells;
688 warmup_start_ammo_nails = start_ammo_nails;
689 warmup_start_ammo_rockets = start_ammo_rockets;
690 warmup_start_ammo_cells = start_ammo_cells;
691 warmup_start_ammo_plasma = start_ammo_plasma;
692 warmup_start_ammo_fuel = start_ammo_fuel;
693 warmup_start_health = start_health;
694 warmup_start_armorvalue = start_armorvalue;
695 warmup_start_weapons = start_weapons;
696 warmup_start_weapons_default = start_weapons_default;
697 warmup_start_weapons_defaultmask = start_weapons_defaultmask;
699 if (!g_weaponarena && !g_ca && !g_freezetag)
701 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
702 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
703 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
704 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
705 warmup_start_ammo_plasma = cvar("g_warmup_start_ammo_plasma");
706 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
707 warmup_start_health = cvar("g_warmup_start_health");
708 warmup_start_armorvalue = cvar("g_warmup_start_armor");
709 warmup_start_weapons = '0 0 0';
710 warmup_start_weapons_default = '0 0 0';
711 warmup_start_weapons_defaultmask = '0 0 0';
712 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
714 e = get_weaponinfo(i);
715 int w = want_weapon(e, g_warmup_allguns);
717 warmup_start_weapons |= WepSet_FromWeapon(i);
719 warmup_start_weapons_default |= WepSet_FromWeapon(i);
721 warmup_start_weapons_defaultmask |= WepSet_FromWeapon(i);
727 start_items |= IT_JETPACK;
729 MUTATOR_CALLHOOK(SetStartItems);
731 if ((start_items & IT_JETPACK) || (g_grappling_hook && (start_weapons & WEPSET_HOOK)))
733 start_items |= IT_FUEL_REGEN;
734 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
735 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
738 WepSet precache_weapons = start_weapons;
739 if (g_warmup_allguns != 1)
740 precache_weapons |= warmup_start_weapons;
741 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
743 e = get_weaponinfo(i);
744 if(precache_weapons & WepSet_FromWeapon(i))
745 WEP_ACTION(i, WR_INIT);
748 start_ammo_shells = max(0, start_ammo_shells);
749 start_ammo_nails = max(0, start_ammo_nails);
750 start_ammo_rockets = max(0, start_ammo_rockets);
751 start_ammo_cells = max(0, start_ammo_cells);
752 start_ammo_plasma = max(0, start_ammo_plasma);
753 start_ammo_fuel = max(0, start_ammo_fuel);
755 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
756 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
757 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
758 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
759 warmup_start_ammo_plasma = max(0, warmup_start_ammo_plasma);
760 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
763 float sound_allowed(float destin, entity e)
765 // sounds from world may always pass
768 if (e.classname == "body")
770 else if (e.realowner && e.realowner != e)
772 else if (e.owner && e.owner != e)
777 // sounds to self may always pass
778 if (destin == MSG_ONE)
781 // sounds by players can be removed
782 if (autocvar_bot_sound_monopoly)
783 if (IS_REAL_CLIENT(e))
785 // anything else may pass
790 void sound(entity e, float chan, string samp, float vol, float attenu)
792 if (!sound_allowed(MSG_BROADCAST, e))
794 sound7(e, chan, samp, vol, attenu, 0, 0);
797 void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float attenu)
801 if (!sound_allowed(_dest, e))
804 entno = num_for_edict(e);
805 idx = precache_sound_index(samp);
810 attenu = floor(attenu * 64);
811 vol = floor(vol * 255);
814 sflags |= SND_VOLUME;
816 sflags |= SND_ATTENUATION;
817 if (entno >= 8192 || chan < 0 || chan > 7)
818 sflags |= SND_LARGEENTITY;
820 sflags |= SND_LARGESOUND;
822 WriteByte(_dest, SVC_SOUND);
823 WriteByte(_dest, sflags);
824 if (sflags & SND_VOLUME)
825 WriteByte(_dest, vol);
826 if (sflags & SND_ATTENUATION)
827 WriteByte(_dest, attenu);
828 if (sflags & SND_LARGEENTITY)
830 WriteShort(_dest, entno);
831 WriteByte(_dest, chan);
835 WriteShort(_dest, entno * 8 + chan);
837 if (sflags & SND_LARGESOUND)
838 WriteShort(_dest, idx);
840 WriteByte(_dest, idx);
842 WriteCoord(_dest, o.x);
843 WriteCoord(_dest, o.y);
844 WriteCoord(_dest, o.z);
846 void soundto(float _dest, entity e, float chan, string samp, float vol, float _atten)
850 if (!sound_allowed(_dest, e))
853 o = e.origin + 0.5 * (e.mins + e.maxs);
854 soundtoat(_dest, e, o, chan, samp, vol, _atten);
856 void soundat(entity e, vector o, float chan, string samp, float vol, float _atten)
858 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, _atten);
860 void stopsoundto(float _dest, entity e, float chan)
864 if (!sound_allowed(_dest, e))
867 entno = num_for_edict(e);
869 if (entno >= 8192 || chan < 0 || chan > 7)
872 idx = precache_sound_index("misc/null.wav");
873 sflags = SND_LARGEENTITY;
875 sflags |= SND_LARGESOUND;
876 WriteByte(_dest, SVC_SOUND);
877 WriteByte(_dest, sflags);
878 WriteShort(_dest, entno);
879 WriteByte(_dest, chan);
880 if (sflags & SND_LARGESOUND)
881 WriteShort(_dest, idx);
883 WriteByte(_dest, idx);
884 WriteCoord(_dest, e.origin.x);
885 WriteCoord(_dest, e.origin.y);
886 WriteCoord(_dest, e.origin.z);
890 WriteByte(_dest, SVC_STOPSOUND);
891 WriteShort(_dest, entno * 8 + chan);
894 void stopsound(entity e, float chan)
896 if (!sound_allowed(MSG_BROADCAST, e))
899 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
900 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
903 void play2(entity e, string filename)
905 //stuffcmd(e, strcat("play2 ", filename, "\n"));
907 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTEN_NONE);
910 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
912 float spamsound(entity e, float chan, string samp, float vol, float _atten)
914 if (!sound_allowed(MSG_BROADCAST, e))
917 if (time > e.spamtime)
920 sound(e, chan, samp, vol, _atten);
926 void play2team(float t, string filename)
930 if (autocvar_bot_sound_monopoly)
933 FOR_EACH_REALPLAYER(head)
936 play2(head, filename);
940 void play2all(string samp)
942 if (autocvar_bot_sound_monopoly)
945 sound(world, CH_INFO, samp, VOL_BASE, ATTEN_NONE);
948 void PrecachePlayerSounds(string f);
949 void precache_playermodel(string m)
951 float globhandle, i, n;
954 if(substring(m, -9,5) == "_lod1")
956 if(substring(m, -9,5) == "_lod2")
959 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
962 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
966 globhandle = search_begin(strcat(m, "_*.sounds"), true, false);
969 n = search_getsize(globhandle);
970 for (i = 0; i < n; ++i)
972 //print(search_getfilename(globhandle, i), "\n");
973 f = search_getfilename(globhandle, i);
974 PrecachePlayerSounds(f);
976 search_end(globhandle);
978 void precache_all_playermodels(string pattern)
980 float globhandle, i, n;
983 globhandle = search_begin(pattern, true, false);
986 n = search_getsize(globhandle);
987 for (i = 0; i < n; ++i)
989 //print(search_getfilename(globhandle, i), "\n");
990 f = search_getfilename(globhandle, i);
991 precache_playermodel(f);
993 search_end(globhandle);
998 // gamemode related things
999 precache_model ("models/misc/chatbubble.spr");
1000 precache_model("models/ice/ice.md3");
1002 #ifdef TTURRETS_ENABLED
1003 if (autocvar_g_turrets)
1007 // Precache all player models if desired
1008 if (autocvar_sv_precacheplayermodels)
1010 PrecachePlayerSounds("sound/player/default.sounds");
1011 precache_all_playermodels("models/player/*.zym");
1012 precache_all_playermodels("models/player/*.dpm");
1013 precache_all_playermodels("models/player/*.md3");
1014 precache_all_playermodels("models/player/*.psk");
1015 precache_all_playermodels("models/player/*.iqm");
1018 if (autocvar_sv_defaultcharacter)
1021 s = autocvar_sv_defaultplayermodel_red;
1023 precache_playermodel(s);
1024 s = autocvar_sv_defaultplayermodel_blue;
1026 precache_playermodel(s);
1027 s = autocvar_sv_defaultplayermodel_yellow;
1029 precache_playermodel(s);
1030 s = autocvar_sv_defaultplayermodel_pink;
1032 precache_playermodel(s);
1033 s = autocvar_sv_defaultplayermodel;
1035 precache_playermodel(s);
1040 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1041 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1044 // gore and miscellaneous sounds
1045 //precache_sound ("misc/h2ohit.wav");
1046 precache_model ("models/hook.md3");
1047 precache_sound ("misc/armorimpact.wav");
1048 precache_sound ("misc/bodyimpact1.wav");
1049 precache_sound ("misc/bodyimpact2.wav");
1050 precache_sound ("misc/gib.wav");
1051 precache_sound ("misc/gib_splat01.wav");
1052 precache_sound ("misc/gib_splat02.wav");
1053 precache_sound ("misc/gib_splat03.wav");
1054 precache_sound ("misc/gib_splat04.wav");
1055 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1056 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1057 precache_sound ("misc/null.wav");
1058 precache_sound ("misc/spawn.wav");
1059 precache_sound ("misc/talk.wav");
1060 precache_sound ("misc/teleport.wav");
1061 precache_sound ("misc/poweroff.wav");
1062 precache_sound ("player/lava.wav");
1063 precache_sound ("player/slime.wav");
1065 precache_model ("models/sprites/0.spr32");
1066 precache_model ("models/sprites/1.spr32");
1067 precache_model ("models/sprites/2.spr32");
1068 precache_model ("models/sprites/3.spr32");
1069 precache_model ("models/sprites/4.spr32");
1070 precache_model ("models/sprites/5.spr32");
1071 precache_model ("models/sprites/6.spr32");
1072 precache_model ("models/sprites/7.spr32");
1073 precache_model ("models/sprites/8.spr32");
1074 precache_model ("models/sprites/9.spr32");
1075 precache_model ("models/sprites/10.spr32");
1077 // common weapon precaches
1078 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1079 precache_sound ("weapons/weapon_switch.wav");
1080 precache_sound ("weapons/weaponpickup.wav");
1081 precache_sound ("weapons/unavailable.wav");
1082 precache_sound ("weapons/dryfire.wav");
1083 if (g_grappling_hook)
1085 precache_sound ("weapons/hook_fire.wav"); // hook
1086 precache_sound ("weapons/hook_impact.wav"); // hook
1089 precache_model("models/elaser.mdl");
1090 precache_model("models/laser.mdl");
1091 precache_model("models/ebomb.mdl");
1094 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1096 if (!self.noise && self.music) // quake 3 uses the music field
1097 self.noise = self.music;
1099 // plays music for the level if there is any
1102 precache_sound (self.noise);
1103 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTEN_NONE);
1107 #include "precache-for-csqc.inc"
1111 void make_safe_for_remove(entity e)
1113 if (e.initialize_entity)
1115 entity ent, prev = world;
1116 for (ent = initialize_entity_first; ent; )
1118 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1120 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1121 // skip it in linked list
1124 prev.initialize_entity_next = ent.initialize_entity_next;
1125 ent = prev.initialize_entity_next;
1129 initialize_entity_first = ent.initialize_entity_next;
1130 ent = initialize_entity_first;
1136 ent = ent.initialize_entity_next;
1142 void objerror(string s)
1144 make_safe_for_remove(self);
1145 builtin_objerror(s);
1148 .float remove_except_protected_forbidden;
1149 void remove_except_protected(entity e)
1151 if(e.remove_except_protected_forbidden)
1152 error("not allowed to remove this at this point");
1156 void remove_unsafely(entity e)
1158 if(e.classname == "spike")
1159 error("Removing spikes is forbidden (crylink bug), please report");
1163 void remove_safely(entity e)
1165 make_safe_for_remove(e);
1169 void InitializeEntity(entity e, void(void) func, float order)
1173 if (!e || e.initialize_entity)
1175 // make a proxy initializer entity
1179 e.classname = "initialize_entity";
1183 e.initialize_entity = func;
1184 e.initialize_entity_order = order;
1186 cur = initialize_entity_first;
1190 if (!cur || cur.initialize_entity_order > order)
1192 // insert between prev and cur
1194 prev.initialize_entity_next = e;
1196 initialize_entity_first = e;
1197 e.initialize_entity_next = cur;
1201 cur = cur.initialize_entity_next;
1204 void InitializeEntitiesRun()
1207 startoflist = initialize_entity_first;
1208 initialize_entity_first = world;
1209 remove = remove_except_protected;
1210 for (self = startoflist; self; self = self.initialize_entity_next)
1212 self.remove_except_protected_forbidden = 1;
1214 for (self = startoflist; self; )
1217 var void(void) func;
1218 e = self.initialize_entity_next;
1219 func = self.initialize_entity;
1220 self.initialize_entity_order = 0;
1221 self.initialize_entity = func_null;
1222 self.initialize_entity_next = world;
1223 self.remove_except_protected_forbidden = 0;
1224 if (self.classname == "initialize_entity")
1228 builtin_remove(self);
1231 //dprint("Delayed initialization: ", self.classname, "\n");
1237 backtrace(strcat("Null function in: ", self.classname, "\n"));
1241 remove = remove_unsafely;
1244 void UncustomizeEntitiesRun()
1248 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1249 self.uncustomizeentityforclient();
1252 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1254 e.customizeentityforclient = customizer;
1255 e.uncustomizeentityforclient = uncustomizer;
1256 e.uncustomizeentityforclient_set = !!uncustomizer;
1259 void Net_LinkEntity(entity e, bool docull, float dt, bool(entity, int) sendfunc)
1263 if (e.classname == "")
1264 e.classname = "net_linked";
1266 if (e.model == "" || self.modelindex == 0)
1270 setmodel(e, "null");
1274 e.SendEntity = sendfunc;
1275 e.SendFlags = 0xFFFFFF;
1278 e.effects |= EF_NODEPTHTEST;
1282 e.nextthink = time + dt;
1283 e.think = SUB_Remove;
1288 .float(entity) isEliminated;
1289 float EliminatedPlayers_SendEntity(entity to, float sendflags)
1293 WriteByte(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS);
1294 WriteByte(MSG_ENTITY, sendflags);
1298 for(i = 1; i <= maxclients; i += 8)
1300 for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e))
1302 if(eliminatedPlayers.isEliminated(e))
1305 WriteByte(MSG_ENTITY, f);
1312 void EliminatedPlayers_Init(float(entity) isEliminated_func)
1314 if(eliminatedPlayers)
1316 backtrace("Can't spawn eliminatedPlayers again!");
1319 Net_LinkEntity(eliminatedPlayers = spawn(), false, 0, EliminatedPlayers_SendEntity);
1320 eliminatedPlayers.isEliminated = isEliminated_func;
1324 void adaptor_think2touch()
1333 void adaptor_think2use()
1345 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1347 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
1348 self.projectiledeathtype |= HITTYPE_SPLASH;
1349 adaptor_think2use();
1352 // deferred dropping
1353 void DropToFloor_Handler()
1355 builtin_droptofloor();
1356 self.dropped_origin = self.origin;
1361 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1366 float trace_hits_box_a0, trace_hits_box_a1;
1368 float trace_hits_box_1d(float end, float thmi, float thma)
1372 // just check if x is in range
1380 // do the trace with respect to x
1381 // 0 -> end has to stay in thmi -> thma
1382 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1383 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1384 if (trace_hits_box_a0 > trace_hits_box_a1)
1390 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1395 // now it is a trace from 0 to end
1397 trace_hits_box_a0 = 0;
1398 trace_hits_box_a1 = 1;
1400 if (!trace_hits_box_1d(end.x, thmi.x, thma.x))
1402 if (!trace_hits_box_1d(end.y, thmi.y, thma.y))
1404 if (!trace_hits_box_1d(end.z, thmi.z, thma.z))
1410 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1412 return trace_hits_box(start, end, thmi - ma, thma - mi);
1415 float SUB_NoImpactCheck()
1417 // zero hitcontents = this is not the real impact, but either the
1418 // mirror-impact of something hitting the projectile instead of the
1419 // projectile hitting the something, or a touchareagrid one. Neither of
1420 // these stop the projectile from moving, so...
1421 if(trace_dphitcontents == 0)
1423 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1424 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));
1427 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1429 if (other == world && self.size != '0 0 0')
1432 tic = self.velocity * sys_frametime;
1433 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1434 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1435 if (trace_fraction >= 1)
1437 dprint("Odd... did not hit...?\n");
1439 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1441 dprint("Detected and prevented the sky-grapple bug.\n");
1449 #define SUB_OwnerCheck() (other && (other == self.owner))
1451 void W_Crylink_Dequeue(entity e);
1452 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1454 if(SUB_OwnerCheck())
1456 if(SUB_NoImpactCheck())
1458 if(self.classname == "nade")
1459 return false; // no checks here
1460 else if(self.classname == "grapplinghook")
1461 RemoveGrapplingHook(self.realowner);
1462 else if(self.classname == "spike")
1464 W_Crylink_Dequeue(self);
1471 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1472 UpdateCSQCProjectile(self);
1477 void URI_Get_Callback(float id, float status, string data)
1479 if(url_URI_Get_Callback(id, status, data))
1483 else if (id == URI_GET_DISCARD)
1487 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
1490 Curl_URI_Get_Callback(id, status, data);
1492 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1495 OnlineBanList_URI_Get_Callback(id, status, data);
1499 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1503 string uid2name(string myuid) {
1505 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
1507 // FIXME remove this later after 0.6 release
1508 // convert old style broken records to correct style
1511 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
1514 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
1515 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
1520 s = "^1Unregistered Player";
1524 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1527 vector start, org, delta, end, enddown, mstart;
1530 m = e.dphitcontentsmask;
1531 e.dphitcontentsmask = goodcontents | badcontents;
1534 delta = world.maxs - world.mins;
1538 for (i = 0; i < attempts; ++i)
1540 start.x = org.x + random() * delta.x;
1541 start.y = org.y + random() * delta.y;
1542 start.z = org.z + random() * delta.z;
1544 // rule 1: start inside world bounds, and outside
1545 // solid, and don't start from somewhere where you can
1546 // fall down to evil
1547 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta.z, MOVE_NORMAL, e);
1548 if (trace_fraction >= 1)
1550 if (trace_startsolid)
1552 if (trace_dphitcontents & badcontents)
1554 if (trace_dphitq3surfaceflags & badsurfaceflags)
1557 // rule 2: if we are too high, lower the point
1558 if (trace_fraction * delta.z > maxaboveground)
1559 start = trace_endpos + '0 0 1' * maxaboveground;
1560 enddown = trace_endpos;
1562 // rule 3: make sure we aren't outside the map. This only works
1563 // for somewhat well formed maps. A good rule of thumb is that
1564 // the map should have a convex outside hull.
1565 // these can be traceLINES as we already verified the starting box
1566 mstart = start + 0.5 * (e.mins + e.maxs);
1567 traceline(mstart, mstart + '1 0 0' * delta.x, MOVE_NORMAL, e);
1568 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1570 traceline(mstart, mstart - '1 0 0' * delta.x, MOVE_NORMAL, e);
1571 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1573 traceline(mstart, mstart + '0 1 0' * delta.y, MOVE_NORMAL, e);
1574 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1576 traceline(mstart, mstart - '0 1 0' * delta.y, MOVE_NORMAL, e);
1577 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1579 traceline(mstart, mstart + '0 0 1' * delta.z, MOVE_NORMAL, e);
1580 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1583 // rule 4: we must "see" some spawnpoint or item
1584 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
1585 if(checkpvs(mstart, sp))
1586 if((traceline(mstart, sp.origin, MOVE_NORMAL, e), trace_fraction) >= 1)
1590 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
1591 if(checkpvs(mstart, sp))
1592 if((traceline(mstart, sp.origin + (sp.mins + sp.maxs) * 0.5, MOVE_NORMAL, e), trace_fraction) >= 1)
1598 // find a random vector to "look at"
1599 end.x = org.x + random() * delta.x;
1600 end.y = org.y + random() * delta.y;
1601 end.z = org.z + random() * delta.z;
1602 end = start + normalize(end - start) * vlen(delta);
1604 // rule 4: start TO end must not be too short
1605 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1606 if (trace_startsolid)
1608 if (trace_fraction < minviewdistance / vlen(delta))
1611 // rule 5: don't want to look at sky
1612 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
1615 // rule 6: we must not end up in trigger_hurt
1616 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
1622 e.dphitcontentsmask = m;
1626 setorigin(e, start);
1627 e.angles = vectoangles(end - start);
1628 dprint("Needed ", ftos(i + 1), " attempts\n");
1635 void write_recordmarker(entity pl, float tstart, float dt)
1637 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
1639 // also write a marker into demo files for demotc-race-record-extractor to find
1642 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
1643 " ", ftos(tstart), " ", ftos(dt), "\n"));
1646 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
1659 if(allowcenter) // 2: allow center handedness
1672 if(allowcenter) // 2: allow center handedness
1688 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
1693 if (autocvar_g_shootfromeye)
1697 if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
1698 else { vecs.y = 0; vecs.z -= 2; }
1706 else if (autocvar_g_shootfromcenter)
1711 else if ((s = autocvar_g_shootfromfixedorigin) != "")
1721 else if (autocvar_g_shootfromclient)
1723 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
1728 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
1730 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
1734 void attach_sameorigin(entity e, entity to, string tag)
1736 vector org, t_forward, t_left, t_up, e_forward, e_up;
1739 org = e.origin - gettaginfo(to, gettagindex(to, tag));
1740 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
1741 t_forward = v_forward * tagscale;
1742 t_left = v_right * -tagscale;
1743 t_up = v_up * tagscale;
1745 e.origin_x = org * t_forward;
1746 e.origin_y = org * t_left;
1747 e.origin_z = org * t_up;
1749 // current forward and up directions
1750 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1751 e.angles = AnglesTransform_FromVAngles(e.angles);
1753 e.angles = AnglesTransform_FromAngles(e.angles);
1754 fixedmakevectors(e.angles);
1756 // untransform forward, up!
1757 e_forward.x = v_forward * t_forward;
1758 e_forward.y = v_forward * t_left;
1759 e_forward.z = v_forward * t_up;
1760 e_up.x = v_up * t_forward;
1761 e_up.y = v_up * t_left;
1762 e_up.z = v_up * t_up;
1764 e.angles = fixedvectoangles2(e_forward, e_up);
1765 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1766 e.angles = AnglesTransform_ToVAngles(e.angles);
1768 e.angles = AnglesTransform_ToAngles(e.angles);
1770 setattachment(e, to, tag);
1771 setorigin(e, e.origin);
1774 void detach_sameorigin(entity e)
1777 org = gettaginfo(e, 0);
1778 e.angles = fixedvectoangles2(v_forward, v_up);
1779 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1780 e.angles = AnglesTransform_ToVAngles(e.angles);
1782 e.angles = AnglesTransform_ToAngles(e.angles);
1784 setattachment(e, world, "");
1785 setorigin(e, e.origin);
1788 void follow_sameorigin(entity e, entity to)
1790 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
1791 e.aiment = to; // make the hole follow bmodel
1792 e.punchangle = to.angles; // the original angles of bmodel
1793 e.view_ofs = e.origin - to.origin; // relative origin
1794 e.v_angle = e.angles - to.angles; // relative angles
1797 void unfollow_sameorigin(entity e)
1799 e.movetype = MOVETYPE_NONE;
1802 entity gettaginfo_relative_ent;
1803 vector gettaginfo_relative(entity e, float tag)
1805 if (!gettaginfo_relative_ent)
1807 gettaginfo_relative_ent = spawn();
1808 gettaginfo_relative_ent.effects = EF_NODRAW;
1810 gettaginfo_relative_ent.model = e.model;
1811 gettaginfo_relative_ent.modelindex = e.modelindex;
1812 gettaginfo_relative_ent.frame = e.frame;
1813 return gettaginfo(gettaginfo_relative_ent, tag);
1818 float modeleffect_SendEntity(entity to, int sf)
1821 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
1824 if(self.velocity != '0 0 0')
1826 if(self.angles != '0 0 0')
1828 if(self.avelocity != '0 0 0')
1831 WriteByte(MSG_ENTITY, f);
1832 WriteShort(MSG_ENTITY, self.modelindex);
1833 WriteByte(MSG_ENTITY, self.skin);
1834 WriteByte(MSG_ENTITY, self.frame);
1835 WriteCoord(MSG_ENTITY, self.origin.x);
1836 WriteCoord(MSG_ENTITY, self.origin.y);
1837 WriteCoord(MSG_ENTITY, self.origin.z);
1840 WriteCoord(MSG_ENTITY, self.velocity.x);
1841 WriteCoord(MSG_ENTITY, self.velocity.y);
1842 WriteCoord(MSG_ENTITY, self.velocity.z);
1846 WriteCoord(MSG_ENTITY, self.angles.x);
1847 WriteCoord(MSG_ENTITY, self.angles.y);
1848 WriteCoord(MSG_ENTITY, self.angles.z);
1852 WriteCoord(MSG_ENTITY, self.avelocity.x);
1853 WriteCoord(MSG_ENTITY, self.avelocity.y);
1854 WriteCoord(MSG_ENTITY, self.avelocity.z);
1856 WriteShort(MSG_ENTITY, self.scale * 256.0);
1857 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
1858 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
1859 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
1860 WriteByte(MSG_ENTITY, self.alpha * 255.0);
1865 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)
1870 e.classname = "modeleffect";
1878 e.teleport_time = t1;
1882 e.scale = s0 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1886 e.scale2 = s2 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1889 sz = max(e.scale, e.scale2);
1890 setsize(e, e.mins * sz, e.maxs * sz);
1891 Net_LinkEntity(e, false, 0.1, modeleffect_SendEntity);
1894 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
1896 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
1899 float randombit(float bits)
1901 if(!(bits & (bits-1))) // this ONLY holds for powers of two!
1910 for(f = 1; f <= bits; f *= 2)
1919 r = (r - 1) / (n - 1);
1926 float randombits(float bits, float k, float error_return)
1930 while(k > 0 && bits != r)
1932 r += randombit(bits - r);
1941 void randombit_test(float bits, float iter)
1945 print(ftos(randombit(bits)), "\n");
1950 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
1952 if(halflifedist > 0)
1953 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
1954 else if(halflifedist < 0)
1955 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
1967 oself.think = SUB_Remove;
1968 oself.nextthink = time;
1974 Execute func() after time + fdelay.
1975 self when func is executed = self when defer is called
1977 void defer(float fdelay, void() func)
1984 e.think = defer_think;
1985 e.nextthink = time + fdelay;
1988 .string aiment_classname;
1989 .float aiment_deadflag;
1990 void SetMovetypeFollow(entity ent, entity e)
1992 // FIXME this may not be warpzone aware
1993 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
1994 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.
1995 ent.aiment = e; // make the hole follow bmodel
1996 ent.punchangle = e.angles; // the original angles of bmodel
1997 ent.view_ofs = ent.origin - e.origin; // relative origin
1998 ent.v_angle = ent.angles - e.angles; // relative angles
1999 ent.aiment_classname = strzone(e.classname);
2000 ent.aiment_deadflag = e.deadflag;
2002 void UnsetMovetypeFollow(entity ent)
2004 ent.movetype = MOVETYPE_FLY;
2005 PROJECTILE_MAKETRIGGER(ent);
2008 float LostMovetypeFollow(entity ent)
2011 if(ent.movetype != MOVETYPE_FOLLOW)
2017 if(ent.aiment.classname != ent.aiment_classname)
2019 if(ent.aiment.deadflag != ent.aiment_deadflag)
2025 float isPushable(entity e)
2034 case "droppedweapon":
2035 case "keepawayball":
2036 case "nexball_basketball":
2037 case "nexball_football":
2039 case "bullet": // antilagged bullets can't hit this either
2042 if (e.projectiledeathtype)