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_handleString(s, f, cvar_cl_physics, "cl_physics");
453 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
454 GetCvars_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
455 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
456 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
457 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
458 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
459 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
460 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
461 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
462 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
463 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
464 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
465 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
466 GetCvars_handleFloat(s, f, cvar_cl_weaponimpulsemode, "cl_weaponimpulsemode");
467 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
468 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
469 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
470 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
471 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
472 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
474 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
475 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
477 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
478 GetCvars_handleFloat(s, f, cvar_cl_allow_uid2name, "cl_allow_uid2name");
479 GetCvars_handleFloat(s, f, cvar_cl_allow_uidtracking, "cl_allow_uidtracking");
480 GetCvars_handleFloat(s, f, cvar_cl_movement_track_canjump, "cl_movement_track_canjump");
481 GetCvars_handleFloat(s, f, cvar_cl_newusekeysupported, "cl_newusekeysupported");
483 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
486 if (s == "cl_weaponpriority")
487 self.switchweapon = w_getbestweapon(self);
488 if (s == "cl_allow_uidtracking")
489 PlayerStats_GameReport_AddPlayer(self);
493 // decolorizes and team colors the player name when needed
494 string playername(entity p)
497 if (teamplay && !intermission_running && IS_PLAYER(p))
499 t = Team_ColorCode(p.team);
500 return strcat(t, strdecolorize(p.netname));
506 vector randompos(vector m1, vector m2)
510 v.x = m2_x * random() + m1_x;
511 v.y = m2_y * random() + m1_y;
512 v.z = m2_z * random() + m1_z;
516 float want_weapon(entity weaponinfo, float allguns) // WEAPONTODO: what still needs done?
518 int i = weaponinfo.weapon;
524 if (g_lms || g_ca || allguns)
526 if(weaponinfo.spawnflags & WEP_FLAG_NORMAL)
532 d = (i == WEP_SHOTGUN);
534 d = 0; // weapon is set a few lines later
536 d = !(!weaponinfo.weaponstart);
538 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
539 d |= (i == WEP_HOOK);
540 if(!g_cts && (weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED)) // never default mutator blocked guns
543 float t = weaponinfo.weaponstartoverride;
545 //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n"));
550 // 4: is set by default?
559 void readplayerstartcvars()
565 // initialize starting values for players
566 start_weapons = '0 0 0';
567 start_weapons_default = '0 0 0';
568 start_weapons_defaultmask = '0 0 0';
570 start_ammo_shells = 0;
571 start_ammo_nails = 0;
572 start_ammo_rockets = 0;
573 start_ammo_cells = 0;
574 start_ammo_plasma = 0;
575 start_health = cvar("g_balance_health_start");
576 start_armorvalue = cvar("g_balance_armor_start");
579 g_weaponarena_weapons = '0 0 0';
581 s = cvar_string("g_weaponarena");
582 if (s == "0" || s == "")
584 if(g_ca || g_freezetag)
588 if (s == "0" || s == "")
594 // forcibly turn off weaponarena
596 else if (s == "all" || s == "1")
599 g_weaponarena_list = "All Weapons";
600 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
602 e = get_weaponinfo(j);
603 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
604 g_weaponarena_weapons |= WepSet_FromWeapon(j);
607 else if (s == "most")
610 g_weaponarena_list = "Most Weapons";
611 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
613 e = get_weaponinfo(j);
614 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
615 if (e.spawnflags & WEP_FLAG_NORMAL)
616 g_weaponarena_weapons |= WepSet_FromWeapon(j);
619 else if (s == "none")
622 g_weaponarena_list = "No Weapons";
627 t = tokenize_console(s);
628 g_weaponarena_list = "";
629 for (i = 0; i < t; ++i)
632 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
634 e = get_weaponinfo(j);
637 g_weaponarena_weapons |= WepSet_FromWeapon(j);
638 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
644 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
647 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
651 g_weaponarena_random = cvar("g_weaponarena_random");
653 g_weaponarena_random = 0;
654 g_weaponarena_random_with_blaster = cvar("g_weaponarena_random_with_blaster");
658 g_weapon_stay = 0; // incompatible
659 start_weapons = g_weaponarena_weapons;
660 start_items |= IT_UNLIMITED_AMMO;
664 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
666 e = get_weaponinfo(i);
667 int w = want_weapon(e, false);
669 start_weapons |= WepSet_FromWeapon(i);
671 start_weapons_default |= WepSet_FromWeapon(i);
673 start_weapons_defaultmask |= WepSet_FromWeapon(i);
677 if(!cvar("g_use_ammunition"))
678 start_items |= IT_UNLIMITED_AMMO;
680 if(start_items & IT_UNLIMITED_WEAPON_AMMO)
682 start_ammo_shells = 999;
683 start_ammo_nails = 999;
684 start_ammo_rockets = 999;
685 start_ammo_cells = 999;
686 start_ammo_plasma = 999;
687 start_ammo_fuel = 999;
691 start_ammo_shells = cvar("g_start_ammo_shells");
692 start_ammo_nails = cvar("g_start_ammo_nails");
693 start_ammo_rockets = cvar("g_start_ammo_rockets");
694 start_ammo_cells = cvar("g_start_ammo_cells");
695 start_ammo_plasma = cvar("g_start_ammo_plasma");
696 start_ammo_fuel = cvar("g_start_ammo_fuel");
701 warmup_start_ammo_shells = start_ammo_shells;
702 warmup_start_ammo_nails = start_ammo_nails;
703 warmup_start_ammo_rockets = start_ammo_rockets;
704 warmup_start_ammo_cells = start_ammo_cells;
705 warmup_start_ammo_plasma = start_ammo_plasma;
706 warmup_start_ammo_fuel = start_ammo_fuel;
707 warmup_start_health = start_health;
708 warmup_start_armorvalue = start_armorvalue;
709 warmup_start_weapons = start_weapons;
710 warmup_start_weapons_default = start_weapons_default;
711 warmup_start_weapons_defaultmask = start_weapons_defaultmask;
713 if (!g_weaponarena && !g_ca && !g_freezetag)
715 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
716 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
717 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
718 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
719 warmup_start_ammo_plasma = cvar("g_warmup_start_ammo_plasma");
720 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
721 warmup_start_health = cvar("g_warmup_start_health");
722 warmup_start_armorvalue = cvar("g_warmup_start_armor");
723 warmup_start_weapons = '0 0 0';
724 warmup_start_weapons_default = '0 0 0';
725 warmup_start_weapons_defaultmask = '0 0 0';
726 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
728 e = get_weaponinfo(i);
729 int w = want_weapon(e, g_warmup_allguns);
731 warmup_start_weapons |= WepSet_FromWeapon(i);
733 warmup_start_weapons_default |= WepSet_FromWeapon(i);
735 warmup_start_weapons_defaultmask |= WepSet_FromWeapon(i);
741 start_items |= IT_JETPACK;
743 MUTATOR_CALLHOOK(SetStartItems);
745 if ((start_items & IT_JETPACK) || (g_grappling_hook && (start_weapons & WEPSET_HOOK)))
747 start_items |= IT_FUEL_REGEN;
748 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
749 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
752 WepSet precache_weapons = start_weapons;
753 if (g_warmup_allguns != 1)
754 precache_weapons |= warmup_start_weapons;
755 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
757 e = get_weaponinfo(i);
758 if(precache_weapons & WepSet_FromWeapon(i))
759 WEP_ACTION(i, WR_INIT);
762 start_ammo_shells = max(0, start_ammo_shells);
763 start_ammo_nails = max(0, start_ammo_nails);
764 start_ammo_rockets = max(0, start_ammo_rockets);
765 start_ammo_cells = max(0, start_ammo_cells);
766 start_ammo_plasma = max(0, start_ammo_plasma);
767 start_ammo_fuel = max(0, start_ammo_fuel);
769 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
770 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
771 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
772 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
773 warmup_start_ammo_plasma = max(0, warmup_start_ammo_plasma);
774 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
777 float sound_allowed(float _dest, entity e)
779 // sounds from world may always pass
782 if (e.classname == "body")
784 else if (e.realowner && e.realowner != e)
786 else if (e.owner && e.owner != e)
791 // sounds to self may always pass
792 if (_dest == MSG_ONE)
795 // sounds by players can be removed
796 if (autocvar_bot_sound_monopoly)
797 if (IS_REAL_CLIENT(e))
799 // anything else may pass
804 void sound(entity e, float chan, string samp, float vol, float _atten)
806 if (!sound_allowed(MSG_BROADCAST, e))
808 sound7(e, chan, samp, vol, _atten, 0, 0);
811 void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float _atten)
815 if (!sound_allowed(_dest, e))
818 entno = num_for_edict(e);
819 idx = precache_sound_index(samp);
824 _atten = floor(_atten * 64);
825 vol = floor(vol * 255);
828 sflags |= SND_VOLUME;
830 sflags |= SND_ATTENUATION;
831 if (entno >= 8192 || chan < 0 || chan > 7)
832 sflags |= SND_LARGEENTITY;
834 sflags |= SND_LARGESOUND;
836 WriteByte(_dest, SVC_SOUND);
837 WriteByte(_dest, sflags);
838 if (sflags & SND_VOLUME)
839 WriteByte(_dest, vol);
840 if (sflags & SND_ATTENUATION)
841 WriteByte(_dest, _atten);
842 if (sflags & SND_LARGEENTITY)
844 WriteShort(_dest, entno);
845 WriteByte(_dest, chan);
849 WriteShort(_dest, entno * 8 + chan);
851 if (sflags & SND_LARGESOUND)
852 WriteShort(_dest, idx);
854 WriteByte(_dest, idx);
856 WriteCoord(_dest, o.x);
857 WriteCoord(_dest, o.y);
858 WriteCoord(_dest, o.z);
860 void soundto(float _dest, entity e, float chan, string samp, float vol, float _atten)
864 if (!sound_allowed(_dest, e))
867 o = e.origin + 0.5 * (e.mins + e.maxs);
868 soundtoat(_dest, e, o, chan, samp, vol, _atten);
870 void soundat(entity e, vector o, float chan, string samp, float vol, float _atten)
872 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, _atten);
874 void stopsoundto(float _dest, entity e, float chan)
878 if (!sound_allowed(_dest, e))
881 entno = num_for_edict(e);
883 if (entno >= 8192 || chan < 0 || chan > 7)
886 idx = precache_sound_index("misc/null.wav");
887 sflags = SND_LARGEENTITY;
889 sflags |= SND_LARGESOUND;
890 WriteByte(_dest, SVC_SOUND);
891 WriteByte(_dest, sflags);
892 WriteShort(_dest, entno);
893 WriteByte(_dest, chan);
894 if (sflags & SND_LARGESOUND)
895 WriteShort(_dest, idx);
897 WriteByte(_dest, idx);
898 WriteCoord(_dest, e.origin.x);
899 WriteCoord(_dest, e.origin.y);
900 WriteCoord(_dest, e.origin.z);
904 WriteByte(_dest, SVC_STOPSOUND);
905 WriteShort(_dest, entno * 8 + chan);
908 void stopsound(entity e, float chan)
910 if (!sound_allowed(MSG_BROADCAST, e))
913 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
914 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
917 void play2(entity e, string filename)
919 //stuffcmd(e, strcat("play2 ", filename, "\n"));
921 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTEN_NONE);
924 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
926 float spamsound(entity e, float chan, string samp, float vol, float _atten)
928 if (!sound_allowed(MSG_BROADCAST, e))
931 if (time > e.spamtime)
934 sound(e, chan, samp, vol, _atten);
940 void play2team(float t, string filename)
944 if (autocvar_bot_sound_monopoly)
947 FOR_EACH_REALPLAYER(head)
950 play2(head, filename);
954 void play2all(string samp)
956 if (autocvar_bot_sound_monopoly)
959 sound(world, CH_INFO, samp, VOL_BASE, ATTEN_NONE);
962 void PrecachePlayerSounds(string f);
963 void precache_playermodel(string m)
965 float globhandle, i, n;
968 if(substring(m, -9,5) == "_lod1")
970 if(substring(m, -9,5) == "_lod2")
973 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
976 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
980 globhandle = search_begin(strcat(m, "_*.sounds"), true, false);
983 n = search_getsize(globhandle);
984 for (i = 0; i < n; ++i)
986 //print(search_getfilename(globhandle, i), "\n");
987 f = search_getfilename(globhandle, i);
988 PrecachePlayerSounds(f);
990 search_end(globhandle);
992 void precache_all_playermodels(string pattern)
994 float globhandle, i, n;
997 globhandle = search_begin(pattern, true, false);
1000 n = search_getsize(globhandle);
1001 for (i = 0; i < n; ++i)
1003 //print(search_getfilename(globhandle, i), "\n");
1004 f = search_getfilename(globhandle, i);
1005 precache_playermodel(f);
1007 search_end(globhandle);
1012 // gamemode related things
1013 precache_model ("models/misc/chatbubble.spr");
1014 precache_model("models/ice/ice.md3");
1016 #ifdef TTURRETS_ENABLED
1017 if (autocvar_g_turrets)
1021 // Precache all player models if desired
1022 if (autocvar_sv_precacheplayermodels)
1024 PrecachePlayerSounds("sound/player/default.sounds");
1025 precache_all_playermodels("models/player/*.zym");
1026 precache_all_playermodels("models/player/*.dpm");
1027 precache_all_playermodels("models/player/*.md3");
1028 precache_all_playermodels("models/player/*.psk");
1029 precache_all_playermodels("models/player/*.iqm");
1032 if (autocvar_sv_defaultcharacter)
1035 s = autocvar_sv_defaultplayermodel_red;
1037 precache_playermodel(s);
1038 s = autocvar_sv_defaultplayermodel_blue;
1040 precache_playermodel(s);
1041 s = autocvar_sv_defaultplayermodel_yellow;
1043 precache_playermodel(s);
1044 s = autocvar_sv_defaultplayermodel_pink;
1046 precache_playermodel(s);
1047 s = autocvar_sv_defaultplayermodel;
1049 precache_playermodel(s);
1054 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1055 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1058 // gore and miscellaneous sounds
1059 //precache_sound ("misc/h2ohit.wav");
1060 precache_model ("models/hook.md3");
1061 precache_sound ("misc/armorimpact.wav");
1062 precache_sound ("misc/bodyimpact1.wav");
1063 precache_sound ("misc/bodyimpact2.wav");
1064 precache_sound ("misc/gib.wav");
1065 precache_sound ("misc/gib_splat01.wav");
1066 precache_sound ("misc/gib_splat02.wav");
1067 precache_sound ("misc/gib_splat03.wav");
1068 precache_sound ("misc/gib_splat04.wav");
1069 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1070 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1071 precache_sound ("misc/null.wav");
1072 precache_sound ("misc/spawn.wav");
1073 precache_sound ("misc/talk.wav");
1074 precache_sound ("misc/teleport.wav");
1075 precache_sound ("misc/poweroff.wav");
1076 precache_sound ("player/lava.wav");
1077 precache_sound ("player/slime.wav");
1079 precache_model ("models/sprites/0.spr32");
1080 precache_model ("models/sprites/1.spr32");
1081 precache_model ("models/sprites/2.spr32");
1082 precache_model ("models/sprites/3.spr32");
1083 precache_model ("models/sprites/4.spr32");
1084 precache_model ("models/sprites/5.spr32");
1085 precache_model ("models/sprites/6.spr32");
1086 precache_model ("models/sprites/7.spr32");
1087 precache_model ("models/sprites/8.spr32");
1088 precache_model ("models/sprites/9.spr32");
1089 precache_model ("models/sprites/10.spr32");
1091 // common weapon precaches
1092 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1093 precache_sound ("weapons/weapon_switch.wav");
1094 precache_sound ("weapons/weaponpickup.wav");
1095 precache_sound ("weapons/unavailable.wav");
1096 precache_sound ("weapons/dryfire.wav");
1097 if (g_grappling_hook)
1099 precache_sound ("weapons/hook_fire.wav"); // hook
1100 precache_sound ("weapons/hook_impact.wav"); // hook
1103 precache_model("models/elaser.mdl");
1104 precache_model("models/laser.mdl");
1105 precache_model("models/ebomb.mdl");
1108 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1110 if (!self.noise && self.music) // quake 3 uses the music field
1111 self.noise = self.music;
1113 // plays music for the level if there is any
1116 precache_sound (self.noise);
1117 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTEN_NONE);
1121 #include "precache-for-csqc.inc"
1125 void make_safe_for_remove(entity e)
1127 if (e.initialize_entity)
1129 entity ent, prev = world;
1130 for (ent = initialize_entity_first; ent; )
1132 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1134 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1135 // skip it in linked list
1138 prev.initialize_entity_next = ent.initialize_entity_next;
1139 ent = prev.initialize_entity_next;
1143 initialize_entity_first = ent.initialize_entity_next;
1144 ent = initialize_entity_first;
1150 ent = ent.initialize_entity_next;
1156 void objerror(string s)
1158 make_safe_for_remove(self);
1159 builtin_objerror(s);
1162 .float remove_except_protected_forbidden;
1163 void remove_except_protected(entity e)
1165 if(e.remove_except_protected_forbidden)
1166 error("not allowed to remove this at this point");
1170 void remove_unsafely(entity e)
1172 if(e.classname == "spike")
1173 error("Removing spikes is forbidden (crylink bug), please report");
1177 void remove_safely(entity e)
1179 make_safe_for_remove(e);
1183 void InitializeEntity(entity e, void(void) func, float order)
1187 if (!e || e.initialize_entity)
1189 // make a proxy initializer entity
1193 e.classname = "initialize_entity";
1197 e.initialize_entity = func;
1198 e.initialize_entity_order = order;
1200 cur = initialize_entity_first;
1204 if (!cur || cur.initialize_entity_order > order)
1206 // insert between prev and cur
1208 prev.initialize_entity_next = e;
1210 initialize_entity_first = e;
1211 e.initialize_entity_next = cur;
1215 cur = cur.initialize_entity_next;
1218 void InitializeEntitiesRun()
1221 startoflist = initialize_entity_first;
1222 initialize_entity_first = world;
1223 remove = remove_except_protected;
1224 for (self = startoflist; self; self = self.initialize_entity_next)
1226 self.remove_except_protected_forbidden = 1;
1228 for (self = startoflist; self; )
1231 var void(void) func;
1232 e = self.initialize_entity_next;
1233 func = self.initialize_entity;
1234 self.initialize_entity_order = 0;
1235 self.initialize_entity = func_null;
1236 self.initialize_entity_next = world;
1237 self.remove_except_protected_forbidden = 0;
1238 if (self.classname == "initialize_entity")
1242 builtin_remove(self);
1245 //dprint("Delayed initialization: ", self.classname, "\n");
1251 backtrace(strcat("Null function in: ", self.classname, "\n"));
1255 remove = remove_unsafely;
1258 void UncustomizeEntitiesRun()
1262 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1263 self.uncustomizeentityforclient();
1266 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1268 e.customizeentityforclient = customizer;
1269 e.uncustomizeentityforclient = uncustomizer;
1270 e.uncustomizeentityforclient_set = !!uncustomizer;
1274 void Net_LinkEntity(entity e, bool docull, float dt, bool(entity, int) sendfunc)
1278 if (e.classname == "")
1279 e.classname = "net_linked";
1281 if (e.model == "" || self.modelindex == 0)
1285 setmodel(e, "null");
1289 e.SendEntity = sendfunc;
1290 e.SendFlags = 0xFFFFFF;
1293 e.effects |= EF_NODEPTHTEST;
1297 e.nextthink = time + dt;
1298 e.think = SUB_Remove;
1303 .float(entity) isEliminated;
1304 float EliminatedPlayers_SendEntity(entity to, float sendflags)
1308 WriteByte(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS);
1309 WriteByte(MSG_ENTITY, sendflags);
1313 for(i = 1; i <= maxclients; i += 8)
1315 for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e))
1317 if(eliminatedPlayers.isEliminated(e))
1320 WriteByte(MSG_ENTITY, f);
1327 void EliminatedPlayers_Init(float(entity) isEliminated_func)
1329 if(eliminatedPlayers)
1331 backtrace("Can't spawn eliminatedPlayers again!");
1334 Net_LinkEntity(eliminatedPlayers = spawn(), false, 0, EliminatedPlayers_SendEntity);
1335 eliminatedPlayers.isEliminated = isEliminated_func;
1339 void adaptor_think2touch()
1348 void adaptor_think2use()
1360 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1362 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
1363 self.projectiledeathtype |= HITTYPE_SPLASH;
1364 adaptor_think2use();
1367 // deferred dropping
1368 void DropToFloor_Handler()
1370 builtin_droptofloor();
1371 self.dropped_origin = self.origin;
1376 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1381 float trace_hits_box_a0, trace_hits_box_a1;
1383 float trace_hits_box_1d(float end, float thmi, float thma)
1387 // just check if x is in range
1395 // do the trace with respect to x
1396 // 0 -> end has to stay in thmi -> thma
1397 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1398 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1399 if (trace_hits_box_a0 > trace_hits_box_a1)
1405 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1410 // now it is a trace from 0 to end
1412 trace_hits_box_a0 = 0;
1413 trace_hits_box_a1 = 1;
1415 if (!trace_hits_box_1d(end.x, thmi.x, thma.x))
1417 if (!trace_hits_box_1d(end.y, thmi.y, thma.y))
1419 if (!trace_hits_box_1d(end.z, thmi.z, thma.z))
1425 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1427 return trace_hits_box(start, end, thmi - ma, thma - mi);
1430 float SUB_NoImpactCheck()
1432 // zero hitcontents = this is not the real impact, but either the
1433 // mirror-impact of something hitting the projectile instead of the
1434 // projectile hitting the something, or a touchareagrid one. Neither of
1435 // these stop the projectile from moving, so...
1436 if(trace_dphitcontents == 0)
1438 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1439 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));
1442 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1444 if (other == world && self.size != '0 0 0')
1447 tic = self.velocity * sys_frametime;
1448 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1449 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1450 if (trace_fraction >= 1)
1452 dprint("Odd... did not hit...?\n");
1454 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1456 dprint("Detected and prevented the sky-grapple bug.\n");
1464 #define SUB_OwnerCheck() (other && (other == self.owner))
1466 void W_Crylink_Dequeue(entity e);
1467 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1469 if(SUB_OwnerCheck())
1471 if(SUB_NoImpactCheck())
1473 if(self.classname == "nade")
1474 return false; // no checks here
1475 else if(self.classname == "grapplinghook")
1476 RemoveGrapplingHook(self.realowner);
1477 else if(self.classname == "spike")
1479 W_Crylink_Dequeue(self);
1486 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1487 UpdateCSQCProjectile(self);
1492 void URI_Get_Callback(float id, float status, string data)
1494 if(url_URI_Get_Callback(id, status, data))
1498 else if (id == URI_GET_DISCARD)
1502 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
1505 Curl_URI_Get_Callback(id, status, data);
1507 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1510 OnlineBanList_URI_Get_Callback(id, status, data);
1514 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1518 string uid2name(string myuid) {
1520 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
1522 // FIXME remove this later after 0.6 release
1523 // convert old style broken records to correct style
1526 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
1529 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
1530 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
1535 s = "^1Unregistered Player";
1539 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1542 vector start, org, delta, end, enddown, mstart;
1545 m = e.dphitcontentsmask;
1546 e.dphitcontentsmask = goodcontents | badcontents;
1549 delta = world.maxs - world.mins;
1553 for (i = 0; i < attempts; ++i)
1555 start.x = org.x + random() * delta.x;
1556 start.y = org.y + random() * delta.y;
1557 start.z = org.z + random() * delta.z;
1559 // rule 1: start inside world bounds, and outside
1560 // solid, and don't start from somewhere where you can
1561 // fall down to evil
1562 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta.z, MOVE_NORMAL, e);
1563 if (trace_fraction >= 1)
1565 if (trace_startsolid)
1567 if (trace_dphitcontents & badcontents)
1569 if (trace_dphitq3surfaceflags & badsurfaceflags)
1572 // rule 2: if we are too high, lower the point
1573 if (trace_fraction * delta.z > maxaboveground)
1574 start = trace_endpos + '0 0 1' * maxaboveground;
1575 enddown = trace_endpos;
1577 // rule 3: make sure we aren't outside the map. This only works
1578 // for somewhat well formed maps. A good rule of thumb is that
1579 // the map should have a convex outside hull.
1580 // these can be traceLINES as we already verified the starting box
1581 mstart = start + 0.5 * (e.mins + e.maxs);
1582 traceline(mstart, mstart + '1 0 0' * delta.x, MOVE_NORMAL, e);
1583 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1585 traceline(mstart, mstart - '1 0 0' * delta.x, MOVE_NORMAL, e);
1586 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1588 traceline(mstart, mstart + '0 1 0' * delta.y, MOVE_NORMAL, e);
1589 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1591 traceline(mstart, mstart - '0 1 0' * delta.y, MOVE_NORMAL, e);
1592 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1594 traceline(mstart, mstart + '0 0 1' * delta.z, MOVE_NORMAL, e);
1595 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1598 // rule 4: we must "see" some spawnpoint or item
1599 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
1600 if(checkpvs(mstart, sp))
1601 if((traceline(mstart, sp.origin, MOVE_NORMAL, e), trace_fraction) >= 1)
1605 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
1606 if(checkpvs(mstart, sp))
1607 if((traceline(mstart, sp.origin + (sp.mins + sp.maxs) * 0.5, MOVE_NORMAL, e), trace_fraction) >= 1)
1613 // find a random vector to "look at"
1614 end.x = org.x + random() * delta.x;
1615 end.y = org.y + random() * delta.y;
1616 end.z = org.z + random() * delta.z;
1617 end = start + normalize(end - start) * vlen(delta);
1619 // rule 4: start TO end must not be too short
1620 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1621 if (trace_startsolid)
1623 if (trace_fraction < minviewdistance / vlen(delta))
1626 // rule 5: don't want to look at sky
1627 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
1630 // rule 6: we must not end up in trigger_hurt
1631 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
1637 e.dphitcontentsmask = m;
1641 setorigin(e, start);
1642 e.angles = vectoangles(end - start);
1643 dprint("Needed ", ftos(i + 1), " attempts\n");
1650 void write_recordmarker(entity pl, float tstart, float dt)
1652 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
1654 // also write a marker into demo files for demotc-race-record-extractor to find
1657 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
1658 " ", ftos(tstart), " ", ftos(dt), "\n"));
1661 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
1674 if(allowcenter) // 2: allow center handedness
1687 if(allowcenter) // 2: allow center handedness
1703 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
1708 if (autocvar_g_shootfromeye)
1712 if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
1713 else { vecs.y = 0; vecs.z -= 2; }
1721 else if (autocvar_g_shootfromcenter)
1726 else if ((s = autocvar_g_shootfromfixedorigin) != "")
1736 else if (autocvar_g_shootfromclient)
1738 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
1743 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
1745 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
1749 void attach_sameorigin(entity e, entity to, string tag)
1751 vector org, t_forward, t_left, t_up, e_forward, e_up;
1754 org = e.origin - gettaginfo(to, gettagindex(to, tag));
1755 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
1756 t_forward = v_forward * tagscale;
1757 t_left = v_right * -tagscale;
1758 t_up = v_up * tagscale;
1760 e.origin_x = org * t_forward;
1761 e.origin_y = org * t_left;
1762 e.origin_z = org * t_up;
1764 // current forward and up directions
1765 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1766 e.angles = AnglesTransform_FromVAngles(e.angles);
1768 e.angles = AnglesTransform_FromAngles(e.angles);
1769 fixedmakevectors(e.angles);
1771 // untransform forward, up!
1772 e_forward.x = v_forward * t_forward;
1773 e_forward.y = v_forward * t_left;
1774 e_forward.z = v_forward * t_up;
1775 e_up.x = v_up * t_forward;
1776 e_up.y = v_up * t_left;
1777 e_up.z = v_up * t_up;
1779 e.angles = fixedvectoangles2(e_forward, e_up);
1780 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1781 e.angles = AnglesTransform_ToVAngles(e.angles);
1783 e.angles = AnglesTransform_ToAngles(e.angles);
1785 setattachment(e, to, tag);
1786 setorigin(e, e.origin);
1789 void detach_sameorigin(entity e)
1792 org = gettaginfo(e, 0);
1793 e.angles = fixedvectoangles2(v_forward, v_up);
1794 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1795 e.angles = AnglesTransform_ToVAngles(e.angles);
1797 e.angles = AnglesTransform_ToAngles(e.angles);
1799 setattachment(e, world, "");
1800 setorigin(e, e.origin);
1803 void follow_sameorigin(entity e, entity to)
1805 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
1806 e.aiment = to; // make the hole follow bmodel
1807 e.punchangle = to.angles; // the original angles of bmodel
1808 e.view_ofs = e.origin - to.origin; // relative origin
1809 e.v_angle = e.angles - to.angles; // relative angles
1812 void unfollow_sameorigin(entity e)
1814 e.movetype = MOVETYPE_NONE;
1817 entity gettaginfo_relative_ent;
1818 vector gettaginfo_relative(entity e, float tag)
1820 if (!gettaginfo_relative_ent)
1822 gettaginfo_relative_ent = spawn();
1823 gettaginfo_relative_ent.effects = EF_NODRAW;
1825 gettaginfo_relative_ent.model = e.model;
1826 gettaginfo_relative_ent.modelindex = e.modelindex;
1827 gettaginfo_relative_ent.frame = e.frame;
1828 return gettaginfo(gettaginfo_relative_ent, tag);
1833 float modeleffect_SendEntity(entity to, int sf)
1836 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
1839 if(self.velocity != '0 0 0')
1841 if(self.angles != '0 0 0')
1843 if(self.avelocity != '0 0 0')
1846 WriteByte(MSG_ENTITY, f);
1847 WriteShort(MSG_ENTITY, self.modelindex);
1848 WriteByte(MSG_ENTITY, self.skin);
1849 WriteByte(MSG_ENTITY, self.frame);
1850 WriteCoord(MSG_ENTITY, self.origin.x);
1851 WriteCoord(MSG_ENTITY, self.origin.y);
1852 WriteCoord(MSG_ENTITY, self.origin.z);
1855 WriteCoord(MSG_ENTITY, self.velocity.x);
1856 WriteCoord(MSG_ENTITY, self.velocity.y);
1857 WriteCoord(MSG_ENTITY, self.velocity.z);
1861 WriteCoord(MSG_ENTITY, self.angles.x);
1862 WriteCoord(MSG_ENTITY, self.angles.y);
1863 WriteCoord(MSG_ENTITY, self.angles.z);
1867 WriteCoord(MSG_ENTITY, self.avelocity.x);
1868 WriteCoord(MSG_ENTITY, self.avelocity.y);
1869 WriteCoord(MSG_ENTITY, self.avelocity.z);
1871 WriteShort(MSG_ENTITY, self.scale * 256.0);
1872 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
1873 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
1874 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
1875 WriteByte(MSG_ENTITY, self.alpha * 255.0);
1880 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)
1885 e.classname = "modeleffect";
1893 e.teleport_time = t1;
1897 e.scale = s0 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1901 e.scale2 = s2 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1904 sz = max(e.scale, e.scale2);
1905 setsize(e, e.mins * sz, e.maxs * sz);
1906 Net_LinkEntity(e, false, 0.1, modeleffect_SendEntity);
1909 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
1911 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
1914 float randombit(float bits)
1916 if(!(bits & (bits-1))) // this ONLY holds for powers of two!
1925 for(f = 1; f <= bits; f *= 2)
1934 r = (r - 1) / (n - 1);
1941 float randombits(float bits, float k, float error_return)
1945 while(k > 0 && bits != r)
1947 r += randombit(bits - r);
1956 void randombit_test(float bits, float iter)
1960 print(ftos(randombit(bits)), "\n");
1965 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
1967 if(halflifedist > 0)
1968 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
1969 else if(halflifedist < 0)
1970 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
1982 oself.think = SUB_Remove;
1983 oself.nextthink = time;
1989 Execute func() after time + fdelay.
1990 self when func is executed = self when defer is called
1992 void defer(float fdelay, void() func)
1999 e.think = defer_think;
2000 e.nextthink = time + fdelay;
2003 .string aiment_classname;
2004 .float aiment_deadflag;
2005 void SetMovetypeFollow(entity ent, entity e)
2007 // FIXME this may not be warpzone aware
2008 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
2009 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.
2010 ent.aiment = e; // make the hole follow bmodel
2011 ent.punchangle = e.angles; // the original angles of bmodel
2012 ent.view_ofs = ent.origin - e.origin; // relative origin
2013 ent.v_angle = ent.angles - e.angles; // relative angles
2014 ent.aiment_classname = strzone(e.classname);
2015 ent.aiment_deadflag = e.deadflag;
2017 void UnsetMovetypeFollow(entity ent)
2019 ent.movetype = MOVETYPE_FLY;
2020 PROJECTILE_MAKETRIGGER(ent);
2023 float LostMovetypeFollow(entity ent)
2026 if(ent.movetype != MOVETYPE_FOLLOW)
2032 if(ent.aiment.classname != ent.aiment_classname)
2034 if(ent.aiment.deadflag != ent.aiment_deadflag)
2040 float isPushable(entity e)
2049 case "droppedweapon":
2050 case "keepawayball":
2051 case "nexball_basketball":
2052 case "nexball_football":
2054 case "bullet": // antilagged bullets can't hit this either
2057 if (e.projectiledeathtype)