1 #include "miscfunctions.qh"
5 #include "command/common.qh"
6 #include "constants.qh"
9 #include "mutators/mutators_include.qh"
10 #include "tturrets/include/turrets_early.qh"
12 #include "weapons/accuracy.qh"
13 #include "weapons/csqcprojectile.qh"
14 #include "weapons/selection.qh"
15 #include "../common/command/generic.qh"
16 #include "../common/constants.qh"
17 #include "../common/deathtypes.qh"
18 #include "../common/mapinfo.qh"
19 #include "../common/notifications.qh"
20 #include "../common/playerstats.qh"
21 #include "../common/teams.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 void spawnfunc_target_location()
227 self.classname = "target_location";
228 // location name in netname
229 // eventually support: count, teamgame selectors, line of sight?
232 void spawnfunc_info_location()
234 self.classname = "target_location";
235 self.message = self.netname;
238 string NearestLocation(vector p)
243 loc = findnearest(p, classname, "target_location", '1 1 1');
250 loc = findnearest(p, target, "###item###", '1 1 4');
257 string formatmessage(string msg)
268 WarpZone_crosshair_trace(self);
269 cursor = trace_endpos;
270 cursor_ent = trace_ent;
274 break; // too many replacements
277 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
278 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
291 replacement = substring(msg, p, 2);
292 escape = substring(msg, p + 1, 1);
296 else if (escape == "\\")
298 else if (escape == "n")
300 else if (escape == "a")
301 replacement = ftos(floor(self.armorvalue));
302 else if (escape == "h")
303 replacement = ftos(floor(self.health));
304 else if (escape == "l")
305 replacement = NearestLocation(self.origin);
306 else if (escape == "y")
307 replacement = NearestLocation(cursor);
308 else if (escape == "d")
309 replacement = NearestLocation(self.death_origin);
310 else if (escape == "w") {
314 wep = self.switchweapon;
317 replacement = WEP_NAME(wep);
318 } else if (escape == "W") {
319 if (self.items & IT_SHELLS) replacement = "shells";
320 else if (self.items & IT_NAILS) replacement = "bullets";
321 else if (self.items & IT_ROCKETS) replacement = "rockets";
322 else if (self.items & IT_CELLS) replacement = "cells";
323 else if (self.items & IT_PLASMA) replacement = "plasma";
324 else replacement = "batteries"; // ;)
325 } else if (escape == "x") {
326 replacement = cursor_ent.netname;
327 if (replacement == "" || !cursor_ent)
328 replacement = "nothing";
329 } else if (escape == "s")
330 replacement = ftos(vlen(self.velocity - self.velocity.z * '0 0 1'));
331 else if (escape == "S")
332 replacement = ftos(vlen(self.velocity));
334 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
335 p = p + strlen(replacement);
340 float boolean(float value) { // if value is 0 return false (0), otherwise return true (1)
341 return (value == 0) ? false : true;
350 >0: receives a cvar from name=argv(f) value=argv(f+1)
352 void GetCvars_handleString(string thisname, float f, .string field, string name)
357 strunzone(self.(field));
358 self.(field) = string_null;
362 if (thisname == name)
365 strunzone(self.(field));
366 self.(field) = strzone(argv(f + 1));
370 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
372 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
374 GetCvars_handleString(thisname, f, field, name);
375 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
376 if (thisname == name)
378 string s = func(strcat1(self.(field)));
379 if (s != self.(field))
381 strunzone(self.(field));
382 self.(field) = strzone(s);
386 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
393 if (thisname == name)
394 self.(field) = stof(argv(f + 1));
397 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
399 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
406 if (thisname == name)
410 self.(field) = stof(argv(f + 1));
419 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
422 string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(string wo)
425 o = W_FixWeaponOrder_ForceComplete(wo);
426 if(self.weaponorder_byimpulse)
428 strunzone(self.weaponorder_byimpulse);
429 self.weaponorder_byimpulse = string_null;
431 self.weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(o));
434 void GetCvars(float f)
436 string s = string_null;
439 s = strcat1(argv(f));
444 MUTATOR_CALLHOOK(GetCvars);
446 Notification_GetCvars();
448 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
449 GetCvars_handleFloat(s, f, cvar_cl_autoscreenshot, "cl_autoscreenshot");
450 GetCvars_handleFloat(s, f, cvar_cl_jetpack_jump, "cl_jetpack_jump");
451 GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
452 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
453 GetCvars_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
454 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
455 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
456 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
457 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
458 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
459 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
460 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
461 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
462 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
463 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
464 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
465 GetCvars_handleFloat(s, f, cvar_cl_weaponimpulsemode, "cl_weaponimpulsemode");
466 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
467 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
468 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
469 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
470 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
471 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
473 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
474 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
476 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
477 GetCvars_handleFloat(s, f, cvar_cl_allow_uid2name, "cl_allow_uid2name");
478 GetCvars_handleFloat(s, f, cvar_cl_allow_uidtracking, "cl_allow_uidtracking");
479 GetCvars_handleFloat(s, f, cvar_cl_movement_track_canjump, "cl_movement_track_canjump");
480 GetCvars_handleFloat(s, f, cvar_cl_newusekeysupported, "cl_newusekeysupported");
482 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
485 if (s == "cl_weaponpriority")
486 self.switchweapon = w_getbestweapon(self);
487 if (s == "cl_allow_uidtracking")
488 PlayerStats_GameReport_AddPlayer(self);
492 // decolorizes and team colors the player name when needed
493 string playername(entity p)
496 if (teamplay && !intermission_running && IS_PLAYER(p))
498 t = Team_ColorCode(p.team);
499 return strcat(t, strdecolorize(p.netname));
505 vector randompos(vector m1, vector m2)
509 v.x = m2_x * random() + m1_x;
510 v.y = m2_y * random() + m1_y;
511 v.z = m2_z * random() + m1_z;
515 float want_weapon(entity weaponinfo, float allguns) // WEAPONTODO: what still needs done?
517 int i = weaponinfo.weapon;
523 if (g_lms || g_ca || allguns)
525 if(weaponinfo.spawnflags & WEP_FLAG_NORMAL)
531 d = (i == WEP_SHOTGUN);
533 d = 0; // weapon is set a few lines later
535 d = !(!weaponinfo.weaponstart);
537 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
538 d |= (i == WEP_HOOK);
539 if(!g_cts && (weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED)) // never default mutator blocked guns
542 float t = weaponinfo.weaponstartoverride;
544 //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n"));
549 // 4: is set by default?
558 void readplayerstartcvars()
564 // initialize starting values for players
565 start_weapons = '0 0 0';
566 start_weapons_default = '0 0 0';
567 start_weapons_defaultmask = '0 0 0';
569 start_ammo_shells = 0;
570 start_ammo_nails = 0;
571 start_ammo_rockets = 0;
572 start_ammo_cells = 0;
573 start_ammo_plasma = 0;
574 start_health = cvar("g_balance_health_start");
575 start_armorvalue = cvar("g_balance_armor_start");
578 g_weaponarena_weapons = '0 0 0';
580 s = cvar_string("g_weaponarena");
581 if (s == "0" || s == "")
587 if (s == "0" || s == "")
593 // forcibly turn off weaponarena
595 else if (s == "all" || s == "1")
598 g_weaponarena_list = "All Weapons";
599 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
601 e = get_weaponinfo(j);
602 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
603 g_weaponarena_weapons |= WepSet_FromWeapon(j);
606 else if (s == "most")
609 g_weaponarena_list = "Most Weapons";
610 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
612 e = get_weaponinfo(j);
613 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
614 if (e.spawnflags & WEP_FLAG_NORMAL)
615 g_weaponarena_weapons |= WepSet_FromWeapon(j);
618 else if (s == "none")
621 g_weaponarena_list = "No Weapons";
626 t = tokenize_console(s);
627 g_weaponarena_list = "";
628 for (i = 0; i < t; ++i)
631 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
633 e = get_weaponinfo(j);
636 g_weaponarena_weapons |= WepSet_FromWeapon(j);
637 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
643 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
646 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
650 g_weaponarena_random = cvar("g_weaponarena_random");
652 g_weaponarena_random = 0;
653 g_weaponarena_random_with_blaster = cvar("g_weaponarena_random_with_blaster");
657 g_weapon_stay = 0; // incompatible
658 start_weapons = g_weaponarena_weapons;
659 start_items |= IT_UNLIMITED_AMMO;
663 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
665 e = get_weaponinfo(i);
666 int w = want_weapon(e, false);
668 start_weapons |= WepSet_FromWeapon(i);
670 start_weapons_default |= WepSet_FromWeapon(i);
672 start_weapons_defaultmask |= WepSet_FromWeapon(i);
676 if(!cvar("g_use_ammunition"))
677 start_items |= IT_UNLIMITED_AMMO;
679 if(start_items & IT_UNLIMITED_WEAPON_AMMO)
681 start_ammo_shells = 999;
682 start_ammo_nails = 999;
683 start_ammo_rockets = 999;
684 start_ammo_cells = 999;
685 start_ammo_plasma = 999;
686 start_ammo_fuel = 999;
690 start_ammo_shells = cvar("g_start_ammo_shells");
691 start_ammo_nails = cvar("g_start_ammo_nails");
692 start_ammo_rockets = cvar("g_start_ammo_rockets");
693 start_ammo_cells = cvar("g_start_ammo_cells");
694 start_ammo_plasma = cvar("g_start_ammo_plasma");
695 start_ammo_fuel = cvar("g_start_ammo_fuel");
700 warmup_start_ammo_shells = start_ammo_shells;
701 warmup_start_ammo_nails = start_ammo_nails;
702 warmup_start_ammo_rockets = start_ammo_rockets;
703 warmup_start_ammo_cells = start_ammo_cells;
704 warmup_start_ammo_plasma = start_ammo_plasma;
705 warmup_start_ammo_fuel = start_ammo_fuel;
706 warmup_start_health = start_health;
707 warmup_start_armorvalue = start_armorvalue;
708 warmup_start_weapons = start_weapons;
709 warmup_start_weapons_default = start_weapons_default;
710 warmup_start_weapons_defaultmask = start_weapons_defaultmask;
712 if (!g_weaponarena && !g_ca)
714 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
715 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
716 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
717 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
718 warmup_start_ammo_plasma = cvar("g_warmup_start_ammo_plasma");
719 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
720 warmup_start_health = cvar("g_warmup_start_health");
721 warmup_start_armorvalue = cvar("g_warmup_start_armor");
722 warmup_start_weapons = '0 0 0';
723 warmup_start_weapons_default = '0 0 0';
724 warmup_start_weapons_defaultmask = '0 0 0';
725 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
727 e = get_weaponinfo(i);
728 int w = want_weapon(e, g_warmup_allguns);
730 warmup_start_weapons |= WepSet_FromWeapon(i);
732 warmup_start_weapons_default |= WepSet_FromWeapon(i);
734 warmup_start_weapons_defaultmask |= WepSet_FromWeapon(i);
740 start_items |= IT_JETPACK;
742 MUTATOR_CALLHOOK(SetStartItems);
744 if ((start_items & IT_JETPACK) || (g_grappling_hook && (start_weapons & WEPSET_HOOK)))
746 start_items |= IT_FUEL_REGEN;
747 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
748 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
751 WepSet precache_weapons = start_weapons;
752 if (g_warmup_allguns != 1)
753 precache_weapons |= warmup_start_weapons;
754 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
756 e = get_weaponinfo(i);
757 if(precache_weapons & WepSet_FromWeapon(i))
758 WEP_ACTION(i, WR_INIT);
761 start_ammo_shells = max(0, start_ammo_shells);
762 start_ammo_nails = max(0, start_ammo_nails);
763 start_ammo_rockets = max(0, start_ammo_rockets);
764 start_ammo_cells = max(0, start_ammo_cells);
765 start_ammo_plasma = max(0, start_ammo_plasma);
766 start_ammo_fuel = max(0, start_ammo_fuel);
768 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
769 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
770 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
771 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
772 warmup_start_ammo_plasma = max(0, warmup_start_ammo_plasma);
773 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
776 float sound_allowed(float _dest, entity e)
778 // sounds from world may always pass
781 if (e.classname == "body")
783 else if (e.realowner && e.realowner != e)
785 else if (e.owner && e.owner != e)
790 // sounds to self may always pass
791 if (_dest == MSG_ONE)
794 // sounds by players can be removed
795 if (autocvar_bot_sound_monopoly)
796 if (IS_REAL_CLIENT(e))
798 // anything else may pass
803 void sound(entity e, float chan, string samp, float vol, float _atten)
805 if (!sound_allowed(MSG_BROADCAST, e))
807 sound7(e, chan, samp, vol, _atten, 0, 0);
810 void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float _atten)
814 if (!sound_allowed(_dest, e))
817 entno = num_for_edict(e);
818 idx = precache_sound_index(samp);
823 _atten = floor(_atten * 64);
824 vol = floor(vol * 255);
827 sflags |= SND_VOLUME;
829 sflags |= SND_ATTENUATION;
830 if (entno >= 8192 || chan < 0 || chan > 7)
831 sflags |= SND_LARGEENTITY;
833 sflags |= SND_LARGESOUND;
835 WriteByte(_dest, SVC_SOUND);
836 WriteByte(_dest, sflags);
837 if (sflags & SND_VOLUME)
838 WriteByte(_dest, vol);
839 if (sflags & SND_ATTENUATION)
840 WriteByte(_dest, _atten);
841 if (sflags & SND_LARGEENTITY)
843 WriteShort(_dest, entno);
844 WriteByte(_dest, chan);
848 WriteShort(_dest, entno * 8 + chan);
850 if (sflags & SND_LARGESOUND)
851 WriteShort(_dest, idx);
853 WriteByte(_dest, idx);
855 WriteCoord(_dest, o.x);
856 WriteCoord(_dest, o.y);
857 WriteCoord(_dest, o.z);
859 void soundto(float _dest, entity e, float chan, string samp, float vol, float _atten)
863 if (!sound_allowed(_dest, e))
866 o = e.origin + 0.5 * (e.mins + e.maxs);
867 soundtoat(_dest, e, o, chan, samp, vol, _atten);
869 void soundat(entity e, vector o, float chan, string samp, float vol, float _atten)
871 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, _atten);
873 void stopsoundto(float _dest, entity e, float chan)
877 if (!sound_allowed(_dest, e))
880 entno = num_for_edict(e);
882 if (entno >= 8192 || chan < 0 || chan > 7)
885 idx = precache_sound_index("misc/null.wav");
886 sflags = SND_LARGEENTITY;
888 sflags |= SND_LARGESOUND;
889 WriteByte(_dest, SVC_SOUND);
890 WriteByte(_dest, sflags);
891 WriteShort(_dest, entno);
892 WriteByte(_dest, chan);
893 if (sflags & SND_LARGESOUND)
894 WriteShort(_dest, idx);
896 WriteByte(_dest, idx);
897 WriteCoord(_dest, e.origin.x);
898 WriteCoord(_dest, e.origin.y);
899 WriteCoord(_dest, e.origin.z);
903 WriteByte(_dest, SVC_STOPSOUND);
904 WriteShort(_dest, entno * 8 + chan);
907 void stopsound(entity e, float chan)
909 if (!sound_allowed(MSG_BROADCAST, e))
912 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
913 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
916 void play2(entity e, string filename)
918 //stuffcmd(e, strcat("play2 ", filename, "\n"));
920 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTEN_NONE);
923 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
925 float spamsound(entity e, float chan, string samp, float vol, float _atten)
927 if (!sound_allowed(MSG_BROADCAST, e))
930 if (time > e.spamtime)
933 sound(e, chan, samp, vol, _atten);
939 void play2team(float t, string filename)
943 if (autocvar_bot_sound_monopoly)
946 FOR_EACH_REALPLAYER(head)
949 play2(head, filename);
953 void play2all(string samp)
955 if (autocvar_bot_sound_monopoly)
958 sound(world, CH_INFO, samp, VOL_BASE, ATTEN_NONE);
961 void PrecachePlayerSounds(string f);
962 void precache_playermodel(string m)
964 float globhandle, i, n;
967 if(substring(m, -9,5) == "_lod1")
969 if(substring(m, -9,5) == "_lod2")
972 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
975 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
979 globhandle = search_begin(strcat(m, "_*.sounds"), true, false);
982 n = search_getsize(globhandle);
983 for (i = 0; i < n; ++i)
985 //print(search_getfilename(globhandle, i), "\n");
986 f = search_getfilename(globhandle, i);
987 PrecachePlayerSounds(f);
989 search_end(globhandle);
991 void precache_all_playermodels(string pattern)
993 float globhandle, i, n;
996 globhandle = search_begin(pattern, true, false);
999 n = search_getsize(globhandle);
1000 for (i = 0; i < n; ++i)
1002 //print(search_getfilename(globhandle, i), "\n");
1003 f = search_getfilename(globhandle, i);
1004 precache_playermodel(f);
1006 search_end(globhandle);
1011 // gamemode related things
1012 precache_model ("models/misc/chatbubble.spr");
1013 precache_model("models/ice/ice.md3");
1015 #ifdef TTURRETS_ENABLED
1016 if (autocvar_g_turrets)
1020 // Precache all player models if desired
1021 if (autocvar_sv_precacheplayermodels)
1023 PrecachePlayerSounds("sound/player/default.sounds");
1024 precache_all_playermodels("models/player/*.zym");
1025 precache_all_playermodels("models/player/*.dpm");
1026 precache_all_playermodels("models/player/*.md3");
1027 precache_all_playermodels("models/player/*.psk");
1028 precache_all_playermodels("models/player/*.iqm");
1031 if (autocvar_sv_defaultcharacter)
1034 s = autocvar_sv_defaultplayermodel_red;
1036 precache_playermodel(s);
1037 s = autocvar_sv_defaultplayermodel_blue;
1039 precache_playermodel(s);
1040 s = autocvar_sv_defaultplayermodel_yellow;
1042 precache_playermodel(s);
1043 s = autocvar_sv_defaultplayermodel_pink;
1045 precache_playermodel(s);
1046 s = autocvar_sv_defaultplayermodel;
1048 precache_playermodel(s);
1053 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1054 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1057 // gore and miscellaneous sounds
1058 //precache_sound ("misc/h2ohit.wav");
1059 precache_model ("models/hook.md3");
1060 precache_sound ("misc/armorimpact.wav");
1061 precache_sound ("misc/bodyimpact1.wav");
1062 precache_sound ("misc/bodyimpact2.wav");
1063 precache_sound ("misc/gib.wav");
1064 precache_sound ("misc/gib_splat01.wav");
1065 precache_sound ("misc/gib_splat02.wav");
1066 precache_sound ("misc/gib_splat03.wav");
1067 precache_sound ("misc/gib_splat04.wav");
1068 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1069 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1070 precache_sound ("misc/null.wav");
1071 precache_sound ("misc/spawn.wav");
1072 precache_sound ("misc/talk.wav");
1073 precache_sound ("misc/teleport.wav");
1074 precache_sound ("misc/poweroff.wav");
1075 precache_sound ("player/lava.wav");
1076 precache_sound ("player/slime.wav");
1078 precache_model ("models/sprites/0.spr32");
1079 precache_model ("models/sprites/1.spr32");
1080 precache_model ("models/sprites/2.spr32");
1081 precache_model ("models/sprites/3.spr32");
1082 precache_model ("models/sprites/4.spr32");
1083 precache_model ("models/sprites/5.spr32");
1084 precache_model ("models/sprites/6.spr32");
1085 precache_model ("models/sprites/7.spr32");
1086 precache_model ("models/sprites/8.spr32");
1087 precache_model ("models/sprites/9.spr32");
1088 precache_model ("models/sprites/10.spr32");
1090 // common weapon precaches
1091 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1092 precache_sound ("weapons/weapon_switch.wav");
1093 precache_sound ("weapons/weaponpickup.wav");
1094 precache_sound ("weapons/unavailable.wav");
1095 precache_sound ("weapons/dryfire.wav");
1096 if (g_grappling_hook)
1098 precache_sound ("weapons/hook_fire.wav"); // hook
1099 precache_sound ("weapons/hook_impact.wav"); // hook
1102 precache_model("models/elaser.mdl");
1103 precache_model("models/laser.mdl");
1104 precache_model("models/ebomb.mdl");
1107 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1109 if (!self.noise && self.music) // quake 3 uses the music field
1110 self.noise = self.music;
1112 // plays music for the level if there is any
1115 precache_sound (self.noise);
1116 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTEN_NONE);
1120 #include "precache-for-csqc.inc"
1124 void make_safe_for_remove(entity e)
1126 if (e.initialize_entity)
1128 entity ent, prev = world;
1129 for (ent = initialize_entity_first; ent; )
1131 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1133 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1134 // skip it in linked list
1137 prev.initialize_entity_next = ent.initialize_entity_next;
1138 ent = prev.initialize_entity_next;
1142 initialize_entity_first = ent.initialize_entity_next;
1143 ent = initialize_entity_first;
1149 ent = ent.initialize_entity_next;
1155 void objerror(string s)
1157 make_safe_for_remove(self);
1158 builtin_objerror(s);
1161 .float remove_except_protected_forbidden;
1162 void remove_except_protected(entity e)
1164 if(e.remove_except_protected_forbidden)
1165 error("not allowed to remove this at this point");
1169 void remove_unsafely(entity e)
1171 if(e.classname == "spike")
1172 error("Removing spikes is forbidden (crylink bug), please report");
1176 void remove_safely(entity e)
1178 make_safe_for_remove(e);
1182 void InitializeEntity(entity e, void(void) func, float order)
1186 if (!e || e.initialize_entity)
1188 // make a proxy initializer entity
1192 e.classname = "initialize_entity";
1196 e.initialize_entity = func;
1197 e.initialize_entity_order = order;
1199 cur = initialize_entity_first;
1203 if (!cur || cur.initialize_entity_order > order)
1205 // insert between prev and cur
1207 prev.initialize_entity_next = e;
1209 initialize_entity_first = e;
1210 e.initialize_entity_next = cur;
1214 cur = cur.initialize_entity_next;
1217 void InitializeEntitiesRun()
1220 startoflist = initialize_entity_first;
1221 initialize_entity_first = world;
1222 remove = remove_except_protected;
1223 for (self = startoflist; self; self = self.initialize_entity_next)
1225 self.remove_except_protected_forbidden = 1;
1227 for (self = startoflist; self; )
1230 var void(void) func;
1231 e = self.initialize_entity_next;
1232 func = self.initialize_entity;
1233 self.initialize_entity_order = 0;
1234 self.initialize_entity = func_null;
1235 self.initialize_entity_next = world;
1236 self.remove_except_protected_forbidden = 0;
1237 if (self.classname == "initialize_entity")
1241 builtin_remove(self);
1244 //dprint("Delayed initialization: ", self.classname, "\n");
1250 backtrace(strcat("Null function in: ", self.classname, "\n"));
1254 remove = remove_unsafely;
1257 void UncustomizeEntitiesRun()
1261 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1262 self.uncustomizeentityforclient();
1265 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1267 e.customizeentityforclient = customizer;
1268 e.uncustomizeentityforclient = uncustomizer;
1269 e.uncustomizeentityforclient_set = !!uncustomizer;
1273 void Net_LinkEntity(entity e, float docull, float dt, bool(entity, int) sendfunc)
1277 if (e.classname == "")
1278 e.classname = "net_linked";
1280 if (e.model == "" || self.modelindex == 0)
1284 setmodel(e, "null");
1288 e.SendEntity = sendfunc;
1289 e.SendFlags = 0xFFFFFF;
1292 e.effects |= EF_NODEPTHTEST;
1296 e.nextthink = time + dt;
1297 e.think = SUB_Remove;
1302 .float(entity) isEliminated;
1303 float EliminatedPlayers_SendEntity(entity to, float sendflags)
1307 WriteByte(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS);
1308 WriteByte(MSG_ENTITY, sendflags);
1312 for(i = 1; i <= maxclients; i += 8)
1314 for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e))
1316 if(eliminatedPlayers.isEliminated(e))
1319 WriteByte(MSG_ENTITY, f);
1326 void EliminatedPlayers_Init(float(entity) isEliminated_func)
1328 if(eliminatedPlayers)
1330 backtrace("Can't spawn eliminatedPlayers again!");
1333 Net_LinkEntity(eliminatedPlayers = spawn(), false, 0, EliminatedPlayers_SendEntity);
1334 eliminatedPlayers.isEliminated = isEliminated_func;
1338 void adaptor_think2touch()
1347 void adaptor_think2use()
1359 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1361 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
1362 self.projectiledeathtype |= HITTYPE_SPLASH;
1363 adaptor_think2use();
1366 // deferred dropping
1367 void DropToFloor_Handler()
1369 builtin_droptofloor();
1370 self.dropped_origin = self.origin;
1375 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1380 float trace_hits_box_a0, trace_hits_box_a1;
1382 float trace_hits_box_1d(float end, float thmi, float thma)
1386 // just check if x is in range
1394 // do the trace with respect to x
1395 // 0 -> end has to stay in thmi -> thma
1396 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1397 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1398 if (trace_hits_box_a0 > trace_hits_box_a1)
1404 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1409 // now it is a trace from 0 to end
1411 trace_hits_box_a0 = 0;
1412 trace_hits_box_a1 = 1;
1414 if (!trace_hits_box_1d(end.x, thmi.x, thma.x))
1416 if (!trace_hits_box_1d(end.y, thmi.y, thma.y))
1418 if (!trace_hits_box_1d(end.z, thmi.z, thma.z))
1424 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1426 return trace_hits_box(start, end, thmi - ma, thma - mi);
1429 float SUB_NoImpactCheck()
1431 // zero hitcontents = this is not the real impact, but either the
1432 // mirror-impact of something hitting the projectile instead of the
1433 // projectile hitting the something, or a touchareagrid one. Neither of
1434 // these stop the projectile from moving, so...
1435 if(trace_dphitcontents == 0)
1437 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1438 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));
1441 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1443 if (other == world && self.size != '0 0 0')
1446 tic = self.velocity * sys_frametime;
1447 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1448 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1449 if (trace_fraction >= 1)
1451 dprint("Odd... did not hit...?\n");
1453 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1455 dprint("Detected and prevented the sky-grapple bug.\n");
1463 #define SUB_OwnerCheck() (other && (other == self.owner))
1465 void W_Crylink_Dequeue(entity e);
1466 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1468 if(SUB_OwnerCheck())
1470 if(SUB_NoImpactCheck())
1472 if(self.classname == "nade")
1473 return false; // no checks here
1474 else if(self.classname == "grapplinghook")
1475 RemoveGrapplingHook(self.realowner);
1476 else if(self.classname == "spike")
1478 W_Crylink_Dequeue(self);
1485 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1486 UpdateCSQCProjectile(self);
1491 void URI_Get_Callback(float id, float status, string data)
1493 if(url_URI_Get_Callback(id, status, data))
1497 else if (id == URI_GET_DISCARD)
1501 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
1504 Curl_URI_Get_Callback(id, status, data);
1506 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1509 OnlineBanList_URI_Get_Callback(id, status, data);
1513 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1517 string uid2name(string myuid) {
1519 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
1521 // FIXME remove this later after 0.6 release
1522 // convert old style broken records to correct style
1525 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
1528 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
1529 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
1534 s = "^1Unregistered Player";
1538 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1541 vector start, org, delta, end, enddown, mstart;
1544 m = e.dphitcontentsmask;
1545 e.dphitcontentsmask = goodcontents | badcontents;
1548 delta = world.maxs - world.mins;
1552 for (i = 0; i < attempts; ++i)
1554 start.x = org.x + random() * delta.x;
1555 start.y = org.y + random() * delta.y;
1556 start.z = org.z + random() * delta.z;
1558 // rule 1: start inside world bounds, and outside
1559 // solid, and don't start from somewhere where you can
1560 // fall down to evil
1561 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta.z, MOVE_NORMAL, e);
1562 if (trace_fraction >= 1)
1564 if (trace_startsolid)
1566 if (trace_dphitcontents & badcontents)
1568 if (trace_dphitq3surfaceflags & badsurfaceflags)
1571 // rule 2: if we are too high, lower the point
1572 if (trace_fraction * delta.z > maxaboveground)
1573 start = trace_endpos + '0 0 1' * maxaboveground;
1574 enddown = trace_endpos;
1576 // rule 3: make sure we aren't outside the map. This only works
1577 // for somewhat well formed maps. A good rule of thumb is that
1578 // the map should have a convex outside hull.
1579 // these can be traceLINES as we already verified the starting box
1580 mstart = start + 0.5 * (e.mins + e.maxs);
1581 traceline(mstart, mstart + '1 0 0' * delta.x, MOVE_NORMAL, e);
1582 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1584 traceline(mstart, mstart - '1 0 0' * delta.x, MOVE_NORMAL, e);
1585 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1587 traceline(mstart, mstart + '0 1 0' * delta.y, MOVE_NORMAL, e);
1588 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1590 traceline(mstart, mstart - '0 1 0' * delta.y, MOVE_NORMAL, e);
1591 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1593 traceline(mstart, mstart + '0 0 1' * delta.z, MOVE_NORMAL, e);
1594 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1597 // rule 4: we must "see" some spawnpoint or item
1598 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
1599 if(checkpvs(mstart, sp))
1600 if((traceline(mstart, sp.origin, MOVE_NORMAL, e), trace_fraction) >= 1)
1604 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
1605 if(checkpvs(mstart, sp))
1606 if((traceline(mstart, sp.origin + (sp.mins + sp.maxs) * 0.5, MOVE_NORMAL, e), trace_fraction) >= 1)
1612 // find a random vector to "look at"
1613 end.x = org.x + random() * delta.x;
1614 end.y = org.y + random() * delta.y;
1615 end.z = org.z + random() * delta.z;
1616 end = start + normalize(end - start) * vlen(delta);
1618 // rule 4: start TO end must not be too short
1619 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1620 if (trace_startsolid)
1622 if (trace_fraction < minviewdistance / vlen(delta))
1625 // rule 5: don't want to look at sky
1626 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
1629 // rule 6: we must not end up in trigger_hurt
1630 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
1636 e.dphitcontentsmask = m;
1640 setorigin(e, start);
1641 e.angles = vectoangles(end - start);
1642 dprint("Needed ", ftos(i + 1), " attempts\n");
1649 void write_recordmarker(entity pl, float tstart, float dt)
1651 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
1653 // also write a marker into demo files for demotc-race-record-extractor to find
1656 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
1657 " ", ftos(tstart), " ", ftos(dt), "\n"));
1660 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
1673 if(allowcenter) // 2: allow center handedness
1686 if(allowcenter) // 2: allow center handedness
1702 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
1707 if (autocvar_g_shootfromeye)
1711 if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
1712 else { vecs.y = 0; vecs.z -= 2; }
1720 else if (autocvar_g_shootfromcenter)
1725 else if ((s = autocvar_g_shootfromfixedorigin) != "")
1735 else if (autocvar_g_shootfromclient)
1737 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
1742 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
1744 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
1748 void attach_sameorigin(entity e, entity to, string tag)
1750 vector org, t_forward, t_left, t_up, e_forward, e_up;
1753 org = e.origin - gettaginfo(to, gettagindex(to, tag));
1754 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
1755 t_forward = v_forward * tagscale;
1756 t_left = v_right * -tagscale;
1757 t_up = v_up * tagscale;
1759 e.origin_x = org * t_forward;
1760 e.origin_y = org * t_left;
1761 e.origin_z = org * t_up;
1763 // current forward and up directions
1764 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1765 e.angles = AnglesTransform_FromVAngles(e.angles);
1767 e.angles = AnglesTransform_FromAngles(e.angles);
1768 fixedmakevectors(e.angles);
1770 // untransform forward, up!
1771 e_forward.x = v_forward * t_forward;
1772 e_forward.y = v_forward * t_left;
1773 e_forward.z = v_forward * t_up;
1774 e_up.x = v_up * t_forward;
1775 e_up.y = v_up * t_left;
1776 e_up.z = v_up * t_up;
1778 e.angles = fixedvectoangles2(e_forward, e_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, to, tag);
1785 setorigin(e, e.origin);
1788 void detach_sameorigin(entity e)
1791 org = gettaginfo(e, 0);
1792 e.angles = fixedvectoangles2(v_forward, v_up);
1793 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1794 e.angles = AnglesTransform_ToVAngles(e.angles);
1796 e.angles = AnglesTransform_ToAngles(e.angles);
1798 setattachment(e, world, "");
1799 setorigin(e, e.origin);
1802 void follow_sameorigin(entity e, entity to)
1804 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
1805 e.aiment = to; // make the hole follow bmodel
1806 e.punchangle = to.angles; // the original angles of bmodel
1807 e.view_ofs = e.origin - to.origin; // relative origin
1808 e.v_angle = e.angles - to.angles; // relative angles
1811 void unfollow_sameorigin(entity e)
1813 e.movetype = MOVETYPE_NONE;
1816 entity gettaginfo_relative_ent;
1817 vector gettaginfo_relative(entity e, float tag)
1819 if (!gettaginfo_relative_ent)
1821 gettaginfo_relative_ent = spawn();
1822 gettaginfo_relative_ent.effects = EF_NODRAW;
1824 gettaginfo_relative_ent.model = e.model;
1825 gettaginfo_relative_ent.modelindex = e.modelindex;
1826 gettaginfo_relative_ent.frame = e.frame;
1827 return gettaginfo(gettaginfo_relative_ent, tag);
1832 float modeleffect_SendEntity(entity to, int sf)
1835 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
1838 if(self.velocity != '0 0 0')
1840 if(self.angles != '0 0 0')
1842 if(self.avelocity != '0 0 0')
1845 WriteByte(MSG_ENTITY, f);
1846 WriteShort(MSG_ENTITY, self.modelindex);
1847 WriteByte(MSG_ENTITY, self.skin);
1848 WriteByte(MSG_ENTITY, self.frame);
1849 WriteCoord(MSG_ENTITY, self.origin.x);
1850 WriteCoord(MSG_ENTITY, self.origin.y);
1851 WriteCoord(MSG_ENTITY, self.origin.z);
1854 WriteCoord(MSG_ENTITY, self.velocity.x);
1855 WriteCoord(MSG_ENTITY, self.velocity.y);
1856 WriteCoord(MSG_ENTITY, self.velocity.z);
1860 WriteCoord(MSG_ENTITY, self.angles.x);
1861 WriteCoord(MSG_ENTITY, self.angles.y);
1862 WriteCoord(MSG_ENTITY, self.angles.z);
1866 WriteCoord(MSG_ENTITY, self.avelocity.x);
1867 WriteCoord(MSG_ENTITY, self.avelocity.y);
1868 WriteCoord(MSG_ENTITY, self.avelocity.z);
1870 WriteShort(MSG_ENTITY, self.scale * 256.0);
1871 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
1872 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
1873 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
1874 WriteByte(MSG_ENTITY, self.alpha * 255.0);
1879 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)
1884 e.classname = "modeleffect";
1892 e.teleport_time = t1;
1896 e.scale = s0 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1900 e.scale2 = s2 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1903 sz = max(e.scale, e.scale2);
1904 setsize(e, e.mins * sz, e.maxs * sz);
1905 Net_LinkEntity(e, false, 0.1, modeleffect_SendEntity);
1908 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
1910 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
1913 float randombit(float bits)
1915 if(!(bits & (bits-1))) // this ONLY holds for powers of two!
1924 for(f = 1; f <= bits; f *= 2)
1933 r = (r - 1) / (n - 1);
1940 float randombits(float bits, float k, float error_return)
1944 while(k > 0 && bits != r)
1946 r += randombit(bits - r);
1955 void randombit_test(float bits, float iter)
1959 print(ftos(randombit(bits)), "\n");
1964 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
1966 if(halflifedist > 0)
1967 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
1968 else if(halflifedist < 0)
1969 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
1981 oself.think = SUB_Remove;
1982 oself.nextthink = time;
1988 Execute func() after time + fdelay.
1989 self when func is executed = self when defer is called
1991 void defer(float fdelay, void() func)
1998 e.think = defer_think;
1999 e.nextthink = time + fdelay;
2002 .string aiment_classname;
2003 .float aiment_deadflag;
2004 void SetMovetypeFollow(entity ent, entity e)
2006 // FIXME this may not be warpzone aware
2007 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
2008 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.
2009 ent.aiment = e; // make the hole follow bmodel
2010 ent.punchangle = e.angles; // the original angles of bmodel
2011 ent.view_ofs = ent.origin - e.origin; // relative origin
2012 ent.v_angle = ent.angles - e.angles; // relative angles
2013 ent.aiment_classname = strzone(e.classname);
2014 ent.aiment_deadflag = e.deadflag;
2016 void UnsetMovetypeFollow(entity ent)
2018 ent.movetype = MOVETYPE_FLY;
2019 PROJECTILE_MAKETRIGGER(ent);
2022 float LostMovetypeFollow(entity ent)
2025 if(ent.movetype != MOVETYPE_FOLLOW)
2031 if(ent.aiment.classname != ent.aiment_classname)
2033 if(ent.aiment.deadflag != ent.aiment_deadflag)
2039 float isPushable(entity e)
2048 case "droppedweapon":
2049 case "keepawayball":
2050 case "nexball_basketball":
2051 case "nexball_football":
2053 case "bullet": // antilagged bullets can't hit this either
2056 if (e.projectiledeathtype)