1 #include "miscfunctions.qh"
4 #include "command/common.qh"
5 #include "constants.qh"
8 #include "mutators/mutators_include.qh"
10 #include "weapons/accuracy.qh"
11 #include "weapons/csqcprojectile.qh"
12 #include "weapons/selection.qh"
13 #include "../common/command/generic.qh"
14 #include "../common/constants.qh"
15 #include "../common/deathtypes.qh"
16 #include "../common/mapinfo.qh"
17 #include "../common/notifications.qh"
18 #include "../common/playerstats.qh"
19 #include "../common/teams.qh"
20 #include "../common/triggers/subs.qh"
21 #include "../common/util.qh"
22 #include "../common/turrets/sv_turrets.qh"
23 #include "../common/weapons/all.qh"
24 #include "../csqcmodellib/sv_model.qh"
25 #include "../warpzonelib/anglestransform.qh"
26 #include "../warpzonelib/server.qh"
28 void crosshair_trace(entity pl)
30 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));
32 void crosshair_trace_plusvisibletriggers(entity pl)
36 first = findchainfloat(solid, SOLID_TRIGGER);
38 for (e = first; e; e = e.chain)
44 for (e = first; e; e = e.chain)
45 e.solid = SOLID_TRIGGER;
47 void WarpZone_crosshair_trace(entity pl)
49 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));
53 string admin_name(void)
55 if(autocvar_sv_adminnick != "")
56 return autocvar_sv_adminnick;
58 return "SERVER ADMIN";
61 void DistributeEvenly_Init(float amount, float totalweight)
63 if (DistributeEvenly_amount)
65 LOG_TRACE("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
66 LOG_TRACE(ftos(DistributeEvenly_totalweight), " left!)\n");
69 DistributeEvenly_amount = 0;
71 DistributeEvenly_amount = amount;
72 DistributeEvenly_totalweight = totalweight;
74 float DistributeEvenly_Get(float weight)
79 f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
80 DistributeEvenly_totalweight -= weight;
81 DistributeEvenly_amount -= f;
84 float DistributeEvenly_GetRandomized(float weight)
89 f = floor(random() + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
90 DistributeEvenly_totalweight -= weight;
91 DistributeEvenly_amount -= f;
96 void GameLogEcho(string s)
101 if (autocvar_sv_eventlog_files)
106 matches = autocvar_sv_eventlog_files_counter + 1;
107 cvar_set("sv_eventlog_files_counter", itos(matches));
110 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
111 fn = strcat(autocvar_sv_eventlog_files_nameprefix, fn, autocvar_sv_eventlog_files_namesuffix);
112 logfile = fopen(fn, FILE_APPEND);
113 fputs(logfile, ":logversion:3\n");
117 if (autocvar_sv_eventlog_files_timestamps)
118 fputs(logfile, strcat(":time:", strftime(true, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
120 fputs(logfile, strcat(s, "\n"));
123 if (autocvar_sv_eventlog_console)
132 // will be opened later
137 if (logfile_open && logfile >= 0)
144 entity findnearest(vector point, .string field, string value, vector axismod)
155 localhead = find(world, field, value);
158 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
159 dist = localhead.oldorigin;
161 dist = localhead.origin;
163 dist = dist.x * axismod.x * '1 0 0' + dist.y * axismod.y * '0 1 0' + dist.z * axismod.z * '0 0 1';
166 for (i = 0; i < num_nearest; ++i)
168 if (len < nearest_length[i])
172 // now i tells us where to insert at
173 // INSERTION SORT! YOU'VE SEEN IT! RUN!
174 if (i < NUM_NEAREST_ENTITIES)
176 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
178 nearest_length[j + 1] = nearest_length[j];
179 nearest_entity[j + 1] = nearest_entity[j];
181 nearest_length[i] = len;
182 nearest_entity[i] = localhead;
183 if (num_nearest < NUM_NEAREST_ENTITIES)
184 num_nearest = num_nearest + 1;
187 localhead = find(localhead, field, value);
190 // now use the first one from our list that we can see
191 for (i = 0; i < num_nearest; ++i)
193 traceline(point, nearest_entity[i].origin, true, world);
194 if (trace_fraction == 1)
198 LOG_TRACE("Nearest point (");
199 LOG_TRACE(nearest_entity[0].netname);
200 LOG_TRACE(") is not visible, using a visible one.\n");
202 return nearest_entity[i];
206 if (num_nearest == 0)
209 LOG_TRACE("Not seeing any location point, using nearest as fallback.\n");
211 dprint("Candidates were: ");
212 for(j = 0; j < num_nearest; ++j)
216 dprint(nearest_entity[j].netname);
221 return nearest_entity[0];
224 string NearestLocation(vector p)
229 loc = findnearest(p, classname, "target_location", '1 1 1');
236 loc = findnearest(p, target, "###item###", '1 1 4');
243 string formatmessage(string msg)
255 ammoitems = "batteries";
256 if(self.items & ITEM_Plasma.m_itemid) ammoitems = ITEM_Plasma.m_name;
257 if(self.items & ITEM_Cells.m_itemid) ammoitems = ITEM_Cells.m_name;
258 if(self.items & ITEM_Rockets.m_itemid) ammoitems = ITEM_Rockets.m_name;
259 if(self.items & ITEM_Shells.m_itemid) ammoitems = ITEM_Shells.m_name;
261 WarpZone_crosshair_trace(self);
262 cursor = trace_endpos;
263 cursor_ent = trace_ent;
267 break; // too many replacements
270 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
271 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
284 replacement = substring(msg, p, 2);
285 escape = substring(msg, p + 1, 1);
289 case "%": replacement = "%"; break;
290 case "\\":replacement = "\\"; break;
291 case "n": replacement = "\n"; break;
292 case "a": replacement = ftos(floor(self.armorvalue)); break;
293 case "h": replacement = ftos(floor(self.health)); break;
294 case "l": replacement = NearestLocation(self.origin); break;
295 case "y": replacement = NearestLocation(cursor); break;
296 case "d": replacement = NearestLocation(self.death_origin); break;
297 case "w": replacement = WEP_NAME((!self.weapon) ? (!self.switchweapon ? self.cnt : self.switchweapon) : self.weapon); break;
298 case "W": replacement = ammoitems; break;
299 case "x": replacement = ((cursor_ent.netname == "" || !cursor_ent) ? "nothing" : cursor_ent.netname); break;
300 case "s": replacement = ftos(vlen(self.velocity - self.velocity_z * '0 0 1')); break;
301 case "S": replacement = ftos(vlen(self.velocity)); break;
304 MUTATOR_CALLHOOK(FormatMessage, escape, replacement, msg);
305 escape = format_escape;
306 replacement = format_replacement;
311 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
312 p = p + strlen(replacement);
323 >0: receives a cvar from name=argv(f) value=argv(f+1)
325 void GetCvars_handleString(string thisname, float f, .string field, string name)
330 strunzone(self.(field));
331 self.(field) = string_null;
335 if (thisname == name)
338 strunzone(self.(field));
339 self.(field) = strzone(argv(f + 1));
343 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
345 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
347 GetCvars_handleString(thisname, f, field, name);
348 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
349 if (thisname == name)
351 string s = func(strcat1(self.(field)));
352 if (s != self.(field))
354 strunzone(self.(field));
355 self.(field) = strzone(s);
359 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
366 if (thisname == name)
367 self.(field) = stof(argv(f + 1));
370 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
372 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
379 if (thisname == name)
383 self.(field) = stof(argv(f + 1));
392 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
395 string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(string wo)
398 o = W_FixWeaponOrder_ForceComplete(wo);
399 if(self.weaponorder_byimpulse)
401 strunzone(self.weaponorder_byimpulse);
402 self.weaponorder_byimpulse = string_null;
404 self.weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(o));
407 void GetCvars(float f)
409 string s = string_null;
412 s = strcat1(argv(f));
416 MUTATOR_CALLHOOK(GetCvars);
418 Notification_GetCvars();
420 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
421 GetCvars_handleFloat(s, f, cvar_cl_autoscreenshot, "cl_autoscreenshot");
422 GetCvars_handleFloat(s, f, cvar_cl_jetpack_jump, "cl_jetpack_jump");
423 GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
424 GetCvars_handleString(s, f, cvar_cl_physics, "cl_physics");
425 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
426 GetCvars_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
427 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
428 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
429 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
430 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
431 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
432 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
433 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
434 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
435 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
436 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
437 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
438 GetCvars_handleFloat(s, f, cvar_cl_weaponimpulsemode, "cl_weaponimpulsemode");
439 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
440 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
441 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
442 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
443 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
444 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
446 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
447 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
449 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
450 GetCvars_handleFloat(s, f, cvar_cl_allow_uid2name, "cl_allow_uid2name");
451 GetCvars_handleFloat(s, f, cvar_cl_allow_uidtracking, "cl_allow_uidtracking");
452 GetCvars_handleFloat(s, f, cvar_cl_movement_track_canjump, "cl_movement_track_canjump");
453 GetCvars_handleFloat(s, f, cvar_cl_newusekeysupported, "cl_newusekeysupported");
455 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
458 if (s == "cl_weaponpriority")
459 self.switchweapon = w_getbestweapon(self);
460 if (s == "cl_allow_uidtracking")
461 PlayerStats_GameReport_AddPlayer(self);
465 // decolorizes and team colors the player name when needed
466 string playername(entity p)
469 if (teamplay && !intermission_running && IS_PLAYER(p))
471 t = Team_ColorCode(p.team);
472 return strcat(t, strdecolorize(p.netname));
478 float want_weapon(entity weaponinfo, float allguns) // WEAPONTODO: what still needs done?
480 int i = weaponinfo.weapon;
486 if (g_lms || g_ca || allguns)
488 if(weaponinfo.spawnflags & WEP_FLAG_NORMAL)
494 d = (i == WEP_SHOTGUN.m_id);
496 d = 0; // weapon is set a few lines later
498 d = !(!weaponinfo.weaponstart);
500 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
501 d |= (i == WEP_HOOK.m_id);
502 if(!g_cts && (weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED)) // never default mutator blocked guns
505 float t = weaponinfo.weaponstartoverride;
507 //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n"));
512 // 4: is set by default?
521 void readplayerstartcvars()
527 // initialize starting values for players
528 start_weapons = '0 0 0';
529 start_weapons_default = '0 0 0';
530 start_weapons_defaultmask = '0 0 0';
532 start_ammo_shells = 0;
533 start_ammo_nails = 0;
534 start_ammo_rockets = 0;
535 start_ammo_cells = 0;
536 start_ammo_plasma = 0;
537 start_health = cvar("g_balance_health_start");
538 start_armorvalue = cvar("g_balance_armor_start");
541 g_weaponarena_weapons = '0 0 0';
543 s = cvar_string("g_weaponarena");
544 if (s == "0" || s == "")
546 if(g_ca || g_freezetag)
550 if (s == "0" || s == "")
556 // forcibly turn off weaponarena
558 else if (s == "all" || s == "1")
561 g_weaponarena_list = "All Weapons";
562 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
564 e = get_weaponinfo(j);
565 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
566 g_weaponarena_weapons |= WepSet_FromWeapon(j);
569 else if (s == "most")
572 g_weaponarena_list = "Most Weapons";
573 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
575 e = get_weaponinfo(j);
576 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
577 if (e.spawnflags & WEP_FLAG_NORMAL)
578 g_weaponarena_weapons |= WepSet_FromWeapon(j);
581 else if (s == "none")
584 g_weaponarena_list = "No Weapons";
589 t = tokenize_console(s);
590 g_weaponarena_list = "";
591 for (i = 0; i < t; ++i)
594 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
596 e = get_weaponinfo(j);
599 g_weaponarena_weapons |= WepSet_FromWeapon(j);
600 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
606 LOG_INFO("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
609 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
613 g_weaponarena_random = cvar("g_weaponarena_random");
615 g_weaponarena_random = 0;
616 g_weaponarena_random_with_blaster = cvar("g_weaponarena_random_with_blaster");
620 g_weapon_stay = 0; // incompatible
621 start_weapons = g_weaponarena_weapons;
622 start_items |= IT_UNLIMITED_AMMO;
626 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
628 e = get_weaponinfo(i);
629 int w = want_weapon(e, false);
631 start_weapons |= WepSet_FromWeapon(i);
633 start_weapons_default |= WepSet_FromWeapon(i);
635 start_weapons_defaultmask |= WepSet_FromWeapon(i);
639 if(!cvar("g_use_ammunition"))
640 start_items |= IT_UNLIMITED_AMMO;
642 if(start_items & IT_UNLIMITED_WEAPON_AMMO)
644 start_ammo_shells = 999;
645 start_ammo_nails = 999;
646 start_ammo_rockets = 999;
647 start_ammo_cells = 999;
648 start_ammo_plasma = 999;
649 start_ammo_fuel = 999;
653 start_ammo_shells = cvar("g_start_ammo_shells");
654 start_ammo_nails = cvar("g_start_ammo_nails");
655 start_ammo_rockets = cvar("g_start_ammo_rockets");
656 start_ammo_cells = cvar("g_start_ammo_cells");
657 start_ammo_plasma = cvar("g_start_ammo_plasma");
658 start_ammo_fuel = cvar("g_start_ammo_fuel");
663 warmup_start_ammo_shells = start_ammo_shells;
664 warmup_start_ammo_nails = start_ammo_nails;
665 warmup_start_ammo_rockets = start_ammo_rockets;
666 warmup_start_ammo_cells = start_ammo_cells;
667 warmup_start_ammo_plasma = start_ammo_plasma;
668 warmup_start_ammo_fuel = start_ammo_fuel;
669 warmup_start_health = start_health;
670 warmup_start_armorvalue = start_armorvalue;
671 warmup_start_weapons = start_weapons;
672 warmup_start_weapons_default = start_weapons_default;
673 warmup_start_weapons_defaultmask = start_weapons_defaultmask;
675 if (!g_weaponarena && !g_ca && !g_freezetag)
677 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
678 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
679 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
680 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
681 warmup_start_ammo_plasma = cvar("g_warmup_start_ammo_plasma");
682 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
683 warmup_start_health = cvar("g_warmup_start_health");
684 warmup_start_armorvalue = cvar("g_warmup_start_armor");
685 warmup_start_weapons = '0 0 0';
686 warmup_start_weapons_default = '0 0 0';
687 warmup_start_weapons_defaultmask = '0 0 0';
688 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
690 e = get_weaponinfo(i);
691 int w = want_weapon(e, g_warmup_allguns);
693 warmup_start_weapons |= WepSet_FromWeapon(i);
695 warmup_start_weapons_default |= WepSet_FromWeapon(i);
697 warmup_start_weapons_defaultmask |= WepSet_FromWeapon(i);
703 start_items |= ITEM_Jetpack.m_itemid;
705 MUTATOR_CALLHOOK(SetStartItems);
707 if ((start_items & ITEM_Jetpack.m_itemid) || (g_grappling_hook && (start_weapons & WEPSET_HOOK)))
709 start_items |= ITEM_JetpackRegen.m_itemid;
710 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
711 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
714 WepSet precache_weapons = start_weapons;
715 if (g_warmup_allguns != 1)
716 precache_weapons |= warmup_start_weapons;
717 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
719 e = get_weaponinfo(i);
720 if(precache_weapons & WepSet_FromWeapon(i))
721 WEP_ACTION(i, WR_INIT);
724 start_ammo_shells = max(0, start_ammo_shells);
725 start_ammo_nails = max(0, start_ammo_nails);
726 start_ammo_rockets = max(0, start_ammo_rockets);
727 start_ammo_cells = max(0, start_ammo_cells);
728 start_ammo_plasma = max(0, start_ammo_plasma);
729 start_ammo_fuel = max(0, start_ammo_fuel);
731 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
732 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
733 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
734 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
735 warmup_start_ammo_plasma = max(0, warmup_start_ammo_plasma);
736 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
739 float sound_allowed(float destin, entity e)
741 // sounds from world may always pass
744 if (e.classname == "body")
746 else if (e.realowner && e.realowner != e)
748 else if (e.owner && e.owner != e)
753 // sounds to self may always pass
754 if (destin == MSG_ONE)
757 // sounds by players can be removed
758 if (autocvar_bot_sound_monopoly)
759 if (IS_REAL_CLIENT(e))
761 // anything else may pass
766 void sound(entity e, float chan, string samp, float vol, float attenu)
768 if (!sound_allowed(MSG_BROADCAST, e))
770 sound7(e, chan, samp, vol, attenu, 0, 0);
773 void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float attenu)
777 if (!sound_allowed(_dest, e))
780 entno = num_for_edict(e);
781 idx = precache_sound_index(samp);
786 attenu = floor(attenu * 64);
787 vol = floor(vol * 255);
790 sflags |= SND_VOLUME;
792 sflags |= SND_ATTENUATION;
793 if (entno >= 8192 || chan < 0 || chan > 7)
794 sflags |= SND_LARGEENTITY;
796 sflags |= SND_LARGESOUND;
798 WriteByte(_dest, SVC_SOUND);
799 WriteByte(_dest, sflags);
800 if (sflags & SND_VOLUME)
801 WriteByte(_dest, vol);
802 if (sflags & SND_ATTENUATION)
803 WriteByte(_dest, attenu);
804 if (sflags & SND_LARGEENTITY)
806 WriteShort(_dest, entno);
807 WriteByte(_dest, chan);
811 WriteShort(_dest, entno * 8 + chan);
813 if (sflags & SND_LARGESOUND)
814 WriteShort(_dest, idx);
816 WriteByte(_dest, idx);
818 WriteCoord(_dest, o.x);
819 WriteCoord(_dest, o.y);
820 WriteCoord(_dest, o.z);
822 void soundto(float _dest, entity e, float chan, string samp, float vol, float _atten)
826 if (!sound_allowed(_dest, e))
829 o = e.origin + 0.5 * (e.mins + e.maxs);
830 soundtoat(_dest, e, o, chan, samp, vol, _atten);
832 void soundat(entity e, vector o, float chan, string samp, float vol, float _atten)
834 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, _atten);
836 void stopsoundto(float _dest, entity e, float chan)
840 if (!sound_allowed(_dest, e))
843 entno = num_for_edict(e);
845 if (entno >= 8192 || chan < 0 || chan > 7)
848 idx = precache_sound_index("misc/null.wav");
849 sflags = SND_LARGEENTITY;
851 sflags |= SND_LARGESOUND;
852 WriteByte(_dest, SVC_SOUND);
853 WriteByte(_dest, sflags);
854 WriteShort(_dest, entno);
855 WriteByte(_dest, chan);
856 if (sflags & SND_LARGESOUND)
857 WriteShort(_dest, idx);
859 WriteByte(_dest, idx);
860 WriteCoord(_dest, e.origin.x);
861 WriteCoord(_dest, e.origin.y);
862 WriteCoord(_dest, e.origin.z);
866 WriteByte(_dest, SVC_STOPSOUND);
867 WriteShort(_dest, entno * 8 + chan);
870 void stopsound(entity e, float chan)
872 if (!sound_allowed(MSG_BROADCAST, e))
875 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
876 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
879 void play2(entity e, string filename)
881 //stuffcmd(e, strcat("play2 ", filename, "\n"));
883 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTEN_NONE);
886 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
888 float spamsound(entity e, float chan, string samp, float vol, float _atten)
890 if (!sound_allowed(MSG_BROADCAST, e))
893 if (time > e.spamtime)
896 sound(e, chan, samp, vol, _atten);
902 void play2team(float t, string filename)
906 if (autocvar_bot_sound_monopoly)
909 FOR_EACH_REALPLAYER(head)
912 play2(head, filename);
916 void play2all(string samp)
918 if (autocvar_bot_sound_monopoly)
921 sound(world, CH_INFO, samp, VOL_BASE, ATTEN_NONE);
924 void PrecachePlayerSounds(string f);
925 void precache_playermodel(string m)
927 float globhandle, i, n;
930 if(substring(m, -9,5) == "_lod1")
932 if(substring(m, -9,5) == "_lod2")
935 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
938 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
942 globhandle = search_begin(strcat(m, "_*.sounds"), true, false);
945 n = search_getsize(globhandle);
946 for (i = 0; i < n; ++i)
948 //print(search_getfilename(globhandle, i), "\n");
949 f = search_getfilename(globhandle, i);
950 PrecachePlayerSounds(f);
952 search_end(globhandle);
954 void precache_all_playermodels(string pattern)
956 float globhandle, i, n;
959 globhandle = search_begin(pattern, true, false);
962 n = search_getsize(globhandle);
963 for (i = 0; i < n; ++i)
965 //print(search_getfilename(globhandle, i), "\n");
966 f = search_getfilename(globhandle, i);
967 precache_playermodel(f);
969 search_end(globhandle);
974 // gamemode related things
975 precache_model ("models/misc/chatbubble.spr");
976 precache_model("models/ice/ice.md3");
978 // Precache all player models if desired
979 if (autocvar_sv_precacheplayermodels)
981 PrecachePlayerSounds("sound/player/default.sounds");
982 precache_all_playermodels("models/player/*.zym");
983 precache_all_playermodels("models/player/*.dpm");
984 precache_all_playermodels("models/player/*.md3");
985 precache_all_playermodels("models/player/*.psk");
986 precache_all_playermodels("models/player/*.iqm");
989 if (autocvar_sv_defaultcharacter)
992 s = autocvar_sv_defaultplayermodel_red;
994 precache_playermodel(s);
995 s = autocvar_sv_defaultplayermodel_blue;
997 precache_playermodel(s);
998 s = autocvar_sv_defaultplayermodel_yellow;
1000 precache_playermodel(s);
1001 s = autocvar_sv_defaultplayermodel_pink;
1003 precache_playermodel(s);
1004 s = autocvar_sv_defaultplayermodel;
1006 precache_playermodel(s);
1011 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1012 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1015 // gore and miscellaneous sounds
1016 //precache_sound ("misc/h2ohit.wav");
1017 precache_model ("models/hook.md3");
1018 precache_sound ("misc/armorimpact.wav");
1019 precache_sound ("misc/bodyimpact1.wav");
1020 precache_sound ("misc/bodyimpact2.wav");
1021 precache_sound ("misc/gib.wav");
1022 precache_sound ("misc/gib_splat01.wav");
1023 precache_sound ("misc/gib_splat02.wav");
1024 precache_sound ("misc/gib_splat03.wav");
1025 precache_sound ("misc/gib_splat04.wav");
1026 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1027 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1028 precache_sound ("misc/null.wav");
1029 precache_sound ("misc/spawn.wav");
1030 precache_sound ("misc/talk.wav");
1031 precache_sound ("misc/teleport.wav");
1032 precache_sound ("misc/poweroff.wav");
1033 precache_sound ("player/lava.wav");
1034 precache_sound ("player/slime.wav");
1036 precache_model ("models/sprites/0.spr32");
1037 precache_model ("models/sprites/1.spr32");
1038 precache_model ("models/sprites/2.spr32");
1039 precache_model ("models/sprites/3.spr32");
1040 precache_model ("models/sprites/4.spr32");
1041 precache_model ("models/sprites/5.spr32");
1042 precache_model ("models/sprites/6.spr32");
1043 precache_model ("models/sprites/7.spr32");
1044 precache_model ("models/sprites/8.spr32");
1045 precache_model ("models/sprites/9.spr32");
1046 precache_model ("models/sprites/10.spr32");
1048 // common weapon precaches
1049 precache_sound (W_Sound("reload")); // until weapons have individual reload sounds, precache the reload sound here
1050 precache_sound (W_Sound("weapon_switch"));
1051 precache_sound (W_Sound("weaponpickup"));
1052 precache_sound (W_Sound("unavailable"));
1053 precache_sound (W_Sound("dryfire"));
1054 if (g_grappling_hook)
1056 precache_sound (W_Sound("hook_fire")); // hook
1057 precache_sound (W_Sound("hook_impact")); // hook
1060 precache_model("models/elaser.mdl");
1061 precache_model("models/laser.mdl");
1062 precache_model("models/ebomb.mdl");
1065 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1067 if (!self.noise && self.music) // quake 3 uses the music field
1068 self.noise = self.music;
1070 // plays music for the level if there is any
1073 precache_sound (self.noise);
1074 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTEN_NONE);
1078 #include "precache-for-csqc.inc"
1082 void make_safe_for_remove(entity e)
1084 if (e.initialize_entity)
1086 entity ent, prev = world;
1087 for (ent = initialize_entity_first; ent; )
1089 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1091 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1092 // skip it in linked list
1095 prev.initialize_entity_next = ent.initialize_entity_next;
1096 ent = prev.initialize_entity_next;
1100 initialize_entity_first = ent.initialize_entity_next;
1101 ent = initialize_entity_first;
1107 ent = ent.initialize_entity_next;
1113 void objerror(string s)
1115 make_safe_for_remove(self);
1116 builtin_objerror(s);
1119 .float remove_except_protected_forbidden;
1120 void remove_except_protected(entity e)
1122 if(e.remove_except_protected_forbidden)
1123 error("not allowed to remove this at this point");
1127 void remove_unsafely(entity e)
1129 if(e.classname == "spike")
1130 error("Removing spikes is forbidden (crylink bug), please report");
1134 void remove_safely(entity e)
1136 make_safe_for_remove(e);
1140 void InitializeEntity(entity e, void(void) func, float order)
1144 if (!e || e.initialize_entity)
1146 // make a proxy initializer entity
1148 e = new(initialize_entity);
1152 e.initialize_entity = func;
1153 e.initialize_entity_order = order;
1155 cur = initialize_entity_first;
1159 if (!cur || cur.initialize_entity_order > order)
1161 // insert between prev and cur
1163 prev.initialize_entity_next = e;
1165 initialize_entity_first = e;
1166 e.initialize_entity_next = cur;
1170 cur = cur.initialize_entity_next;
1173 void InitializeEntitiesRun()
1175 entity startoflist = initialize_entity_first;
1176 initialize_entity_first = NULL;
1177 remove = remove_except_protected;
1178 for (entity e = startoflist; e; e = e.initialize_entity_next)
1180 e.remove_except_protected_forbidden = 1;
1182 for (entity e = startoflist; e; )
1184 e.remove_except_protected_forbidden = 0;
1185 e.initialize_entity_order = 0;
1186 entity next = e.initialize_entity_next;
1187 e.initialize_entity_next = NULL;
1188 var void() func = e.initialize_entity;
1189 e.initialize_entity = func_null;
1190 if (e.classname == "initialize_entity")
1192 entity wrappee = e.enemy;
1196 //dprint("Delayed initialization: ", e.classname, "\n");
1199 WITH(entity, self, e, func());
1204 backtrace(strcat("Null function in: ", e.classname, "\n"));
1208 remove = remove_unsafely;
1211 void UncustomizeEntitiesRun()
1213 for (entity e = NULL; (e = findfloat(e, uncustomizeentityforclient_set, 1)); )
1215 WITH(entity, self, e, e.uncustomizeentityforclient());
1218 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1220 e.customizeentityforclient = customizer;
1221 e.uncustomizeentityforclient = uncustomizer;
1222 e.uncustomizeentityforclient_set = !!uncustomizer;
1225 void Net_LinkEntity(entity e, bool docull, float dt, bool(entity, int) sendfunc)
1229 if (e.classname == "")
1230 e.classname = "net_linked";
1232 if (e.model == "" || self.modelindex == 0)
1236 setmodel(e, "null");
1240 e.SendEntity = sendfunc;
1241 e.SendFlags = 0xFFFFFF;
1244 e.effects |= EF_NODEPTHTEST;
1248 e.nextthink = time + dt;
1249 e.think = SUB_Remove;
1254 .float(entity) isEliminated;
1255 float EliminatedPlayers_SendEntity(entity to, float sendflags)
1259 WriteByte(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS);
1260 WriteByte(MSG_ENTITY, sendflags);
1264 for(i = 1; i <= maxclients; i += 8)
1266 for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e))
1268 if(eliminatedPlayers.isEliminated(e))
1271 WriteByte(MSG_ENTITY, f);
1278 void EliminatedPlayers_Init(float(entity) isEliminated_func)
1280 if(eliminatedPlayers)
1282 backtrace("Can't spawn eliminatedPlayers again!");
1285 Net_LinkEntity(eliminatedPlayers = spawn(), false, 0, EliminatedPlayers_SendEntity);
1286 eliminatedPlayers.isEliminated = isEliminated_func;
1290 void adaptor_think2touch()
1299 void adaptor_think2use()
1311 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1313 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
1314 self.projectiledeathtype |= HITTYPE_SPLASH;
1315 adaptor_think2use();
1318 // deferred dropping
1319 void DropToFloor_Handler()
1321 builtin_droptofloor();
1322 self.dropped_origin = self.origin;
1327 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1332 float trace_hits_box_a0, trace_hits_box_a1;
1334 float trace_hits_box_1d(float end, float thmi, float thma)
1338 // just check if x is in range
1346 // do the trace with respect to x
1347 // 0 -> end has to stay in thmi -> thma
1348 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1349 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1350 if (trace_hits_box_a0 > trace_hits_box_a1)
1356 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1361 // now it is a trace from 0 to end
1363 trace_hits_box_a0 = 0;
1364 trace_hits_box_a1 = 1;
1366 if (!trace_hits_box_1d(end.x, thmi.x, thma.x))
1368 if (!trace_hits_box_1d(end.y, thmi.y, thma.y))
1370 if (!trace_hits_box_1d(end.z, thmi.z, thma.z))
1376 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1378 return trace_hits_box(start, end, thmi - ma, thma - mi);
1381 float SUB_NoImpactCheck()
1383 // zero hitcontents = this is not the real impact, but either the
1384 // mirror-impact of something hitting the projectile instead of the
1385 // projectile hitting the something, or a touchareagrid one. Neither of
1386 // these stop the projectile from moving, so...
1387 if(trace_dphitcontents == 0)
1389 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1390 LOG_TRACEF("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));
1393 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1395 if (other == world && self.size != '0 0 0')
1398 tic = self.velocity * sys_frametime;
1399 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1400 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1401 if (trace_fraction >= 1)
1403 LOG_TRACE("Odd... did not hit...?\n");
1405 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1407 LOG_TRACE("Detected and prevented the sky-grapple bug.\n");
1415 #define SUB_OwnerCheck() (other && (other == self.owner))
1417 void W_Crylink_Dequeue(entity e);
1418 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1420 if(SUB_OwnerCheck())
1422 if(SUB_NoImpactCheck())
1424 if(self.classname == "nade")
1425 return false; // no checks here
1426 else if(self.classname == "grapplinghook")
1427 RemoveGrapplingHook(self.realowner);
1428 else if(self.classname == "spike")
1430 W_Crylink_Dequeue(self);
1437 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1438 UpdateCSQCProjectile(self);
1443 void URI_Get_Callback(float id, float status, string data)
1445 if(url_URI_Get_Callback(id, status, data))
1449 else if (id == URI_GET_DISCARD)
1453 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
1456 Curl_URI_Get_Callback(id, status, data);
1458 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1461 OnlineBanList_URI_Get_Callback(id, status, data);
1465 LOG_INFO("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1469 string uid2name(string myuid) {
1471 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
1473 // FIXME remove this later after 0.6 release
1474 // convert old style broken records to correct style
1477 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
1480 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
1481 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
1486 s = "^1Unregistered Player";
1490 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1493 vector start, org, delta, end, enddown, mstart;
1496 m = e.dphitcontentsmask;
1497 e.dphitcontentsmask = goodcontents | badcontents;
1500 delta = world.maxs - world.mins;
1504 for (i = 0; i < attempts; ++i)
1506 start.x = org.x + random() * delta.x;
1507 start.y = org.y + random() * delta.y;
1508 start.z = org.z + random() * delta.z;
1510 // rule 1: start inside world bounds, and outside
1511 // solid, and don't start from somewhere where you can
1512 // fall down to evil
1513 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta.z, MOVE_NORMAL, e);
1514 if (trace_fraction >= 1)
1516 if (trace_startsolid)
1518 if (trace_dphitcontents & badcontents)
1520 if (trace_dphitq3surfaceflags & badsurfaceflags)
1523 // rule 2: if we are too high, lower the point
1524 if (trace_fraction * delta.z > maxaboveground)
1525 start = trace_endpos + '0 0 1' * maxaboveground;
1526 enddown = trace_endpos;
1528 // rule 3: make sure we aren't outside the map. This only works
1529 // for somewhat well formed maps. A good rule of thumb is that
1530 // the map should have a convex outside hull.
1531 // these can be traceLINES as we already verified the starting box
1532 mstart = start + 0.5 * (e.mins + e.maxs);
1533 traceline(mstart, mstart + '1 0 0' * delta.x, MOVE_NORMAL, e);
1534 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1536 traceline(mstart, mstart - '1 0 0' * delta.x, MOVE_NORMAL, e);
1537 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1539 traceline(mstart, mstart + '0 1 0' * delta.y, MOVE_NORMAL, e);
1540 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1542 traceline(mstart, mstart - '0 1 0' * delta.y, MOVE_NORMAL, e);
1543 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1545 traceline(mstart, mstart + '0 0 1' * delta.z, MOVE_NORMAL, e);
1546 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1549 // rule 4: we must "see" some spawnpoint or item
1550 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
1551 if(checkpvs(mstart, sp))
1552 if((traceline(mstart, sp.origin, MOVE_NORMAL, e), trace_fraction) >= 1)
1556 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
1557 if(checkpvs(mstart, sp))
1558 if((traceline(mstart, sp.origin + (sp.mins + sp.maxs) * 0.5, MOVE_NORMAL, e), trace_fraction) >= 1)
1564 // find a random vector to "look at"
1565 end.x = org.x + random() * delta.x;
1566 end.y = org.y + random() * delta.y;
1567 end.z = org.z + random() * delta.z;
1568 end = start + normalize(end - start) * vlen(delta);
1570 // rule 4: start TO end must not be too short
1571 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1572 if (trace_startsolid)
1574 if (trace_fraction < minviewdistance / vlen(delta))
1577 // rule 5: don't want to look at sky
1578 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
1581 // rule 6: we must not end up in trigger_hurt
1582 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
1588 e.dphitcontentsmask = m;
1592 setorigin(e, start);
1593 e.angles = vectoangles(end - start);
1594 LOG_TRACE("Needed ", ftos(i + 1), " attempts\n");
1601 void write_recordmarker(entity pl, float tstart, float dt)
1603 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
1605 // also write a marker into demo files for demotc-race-record-extractor to find
1608 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
1609 " ", ftos(tstart), " ", ftos(dt), "\n"));
1612 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
1625 if(allowcenter) // 2: allow center handedness
1638 if(allowcenter) // 2: allow center handedness
1654 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
1659 if (autocvar_g_shootfromeye)
1663 if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
1664 else { vecs.y = 0; vecs.z -= 2; }
1672 else if (autocvar_g_shootfromcenter)
1677 else if ((s = autocvar_g_shootfromfixedorigin) != "")
1687 else if (autocvar_g_shootfromclient)
1689 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
1694 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
1696 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
1700 void attach_sameorigin(entity e, entity to, string tag)
1702 vector org, t_forward, t_left, t_up, e_forward, e_up;
1705 org = e.origin - gettaginfo(to, gettagindex(to, tag));
1706 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
1707 t_forward = v_forward * tagscale;
1708 t_left = v_right * -tagscale;
1709 t_up = v_up * tagscale;
1711 e.origin_x = org * t_forward;
1712 e.origin_y = org * t_left;
1713 e.origin_z = org * t_up;
1715 // current forward and up directions
1716 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1717 e.angles = AnglesTransform_FromVAngles(e.angles);
1719 e.angles = AnglesTransform_FromAngles(e.angles);
1720 fixedmakevectors(e.angles);
1722 // untransform forward, up!
1723 e_forward.x = v_forward * t_forward;
1724 e_forward.y = v_forward * t_left;
1725 e_forward.z = v_forward * t_up;
1726 e_up.x = v_up * t_forward;
1727 e_up.y = v_up * t_left;
1728 e_up.z = v_up * t_up;
1730 e.angles = fixedvectoangles2(e_forward, e_up);
1731 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1732 e.angles = AnglesTransform_ToVAngles(e.angles);
1734 e.angles = AnglesTransform_ToAngles(e.angles);
1736 setattachment(e, to, tag);
1737 setorigin(e, e.origin);
1740 void detach_sameorigin(entity e)
1743 org = gettaginfo(e, 0);
1744 e.angles = fixedvectoangles2(v_forward, v_up);
1745 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1746 e.angles = AnglesTransform_ToVAngles(e.angles);
1748 e.angles = AnglesTransform_ToAngles(e.angles);
1750 setattachment(e, world, "");
1751 setorigin(e, e.origin);
1754 void follow_sameorigin(entity e, entity to)
1756 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
1757 e.aiment = to; // make the hole follow bmodel
1758 e.punchangle = to.angles; // the original angles of bmodel
1759 e.view_ofs = e.origin - to.origin; // relative origin
1760 e.v_angle = e.angles - to.angles; // relative angles
1763 void unfollow_sameorigin(entity e)
1765 e.movetype = MOVETYPE_NONE;
1768 entity gettaginfo_relative_ent;
1769 vector gettaginfo_relative(entity e, float tag)
1771 if (!gettaginfo_relative_ent)
1773 gettaginfo_relative_ent = spawn();
1774 gettaginfo_relative_ent.effects = EF_NODRAW;
1776 gettaginfo_relative_ent.model = e.model;
1777 gettaginfo_relative_ent.modelindex = e.modelindex;
1778 gettaginfo_relative_ent.frame = e.frame;
1779 return gettaginfo(gettaginfo_relative_ent, tag);
1784 float modeleffect_SendEntity(entity to, int sf)
1787 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
1790 if(self.velocity != '0 0 0')
1792 if(self.angles != '0 0 0')
1794 if(self.avelocity != '0 0 0')
1797 WriteByte(MSG_ENTITY, f);
1798 WriteShort(MSG_ENTITY, self.modelindex);
1799 WriteByte(MSG_ENTITY, self.skin);
1800 WriteByte(MSG_ENTITY, self.frame);
1801 WriteCoord(MSG_ENTITY, self.origin.x);
1802 WriteCoord(MSG_ENTITY, self.origin.y);
1803 WriteCoord(MSG_ENTITY, self.origin.z);
1806 WriteCoord(MSG_ENTITY, self.velocity.x);
1807 WriteCoord(MSG_ENTITY, self.velocity.y);
1808 WriteCoord(MSG_ENTITY, self.velocity.z);
1812 WriteCoord(MSG_ENTITY, self.angles.x);
1813 WriteCoord(MSG_ENTITY, self.angles.y);
1814 WriteCoord(MSG_ENTITY, self.angles.z);
1818 WriteCoord(MSG_ENTITY, self.avelocity.x);
1819 WriteCoord(MSG_ENTITY, self.avelocity.y);
1820 WriteCoord(MSG_ENTITY, self.avelocity.z);
1822 WriteShort(MSG_ENTITY, self.scale * 256.0);
1823 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
1824 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
1825 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
1826 WriteByte(MSG_ENTITY, self.alpha * 255.0);
1831 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)
1836 e.classname = "modeleffect";
1844 e.teleport_time = t1;
1848 e.scale = s0 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1852 e.scale2 = s2 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1855 sz = max(e.scale, e.scale2);
1856 setsize(e, e.mins * sz, e.maxs * sz);
1857 Net_LinkEntity(e, false, 0.1, modeleffect_SendEntity);
1860 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
1862 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
1865 float randombit(float bits)
1867 if(!(bits & (bits-1))) // this ONLY holds for powers of two!
1876 for(f = 1; f <= bits; f *= 2)
1885 r = (r - 1) / (n - 1);
1892 float randombits(float bits, float k, float error_return)
1896 while(k > 0 && bits != r)
1898 r += randombit(bits - r);
1907 void randombit_test(float bits, float iter)
1911 LOG_INFO(ftos(randombit(bits)), "\n");
1916 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
1918 if(halflifedist > 0)
1919 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
1920 else if(halflifedist < 0)
1921 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
1927 .string aiment_classname;
1928 .float aiment_deadflag;
1929 void SetMovetypeFollow(entity ent, entity e)
1931 // FIXME this may not be warpzone aware
1932 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
1933 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.
1934 ent.aiment = e; // make the hole follow bmodel
1935 ent.punchangle = e.angles; // the original angles of bmodel
1936 ent.view_ofs = ent.origin - e.origin; // relative origin
1937 ent.v_angle = ent.angles - e.angles; // relative angles
1938 ent.aiment_classname = strzone(e.classname);
1939 ent.aiment_deadflag = e.deadflag;
1941 void UnsetMovetypeFollow(entity ent)
1943 ent.movetype = MOVETYPE_FLY;
1944 PROJECTILE_MAKETRIGGER(ent);
1947 float LostMovetypeFollow(entity ent)
1950 if(ent.movetype != MOVETYPE_FOLLOW)
1956 if(ent.aiment.classname != ent.aiment_classname)
1958 if(ent.aiment.deadflag != ent.aiment_deadflag)
1964 float isPushable(entity e)
1975 case "droppedweapon":
1976 case "keepawayball":
1977 case "nexball_basketball":
1978 case "nexball_football":
1980 case "bullet": // antilagged bullets can't hit this either
1983 if (e.projectiledeathtype)