1 #include "miscfunctions.qh"
4 #include "command/common.qh"
5 #include "constants.qh"
8 #include "mutators/mutators_include.qh"
10 #include "weapons/accuracy.qh"
11 #include "weapons/csqcprojectile.qh"
12 #include "weapons/selection.qh"
13 #include "../common/command/generic.qh"
14 #include "../common/constants.qh"
15 #include "../common/deathtypes.qh"
16 #include "../common/mapinfo.qh"
17 #include "../common/notifications.qh"
18 #include "../common/playerstats.qh"
19 #include "../common/teams.qh"
20 #include "../common/triggers/subs.qh"
21 #include "../common/urllib.qh"
22 #include "../common/util.qh"
23 #include "../common/turrets/sv_turrets.qh"
24 #include "../common/weapons/all.qh"
25 #include "../csqcmodellib/sv_model.qh"
26 #include "../warpzonelib/anglestransform.qh"
27 #include "../warpzonelib/server.qh"
29 void crosshair_trace(entity pl)
31 traceline_antilag(pl, pl.cursor_trace_start, pl.cursor_trace_start + normalize(pl.cursor_trace_endpos - pl.cursor_trace_start) * MAX_SHOT_DISTANCE, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl));
33 void crosshair_trace_plusvisibletriggers(entity pl)
37 first = findchainfloat(solid, SOLID_TRIGGER);
39 for (e = first; e; e = e.chain)
45 for (e = first; e; e = e.chain)
46 e.solid = SOLID_TRIGGER;
48 void WarpZone_crosshair_trace(entity pl)
50 WarpZone_traceline_antilag(pl, pl.cursor_trace_start, pl.cursor_trace_start + normalize(pl.cursor_trace_endpos - pl.cursor_trace_start) * MAX_SHOT_DISTANCE, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl));
54 string admin_name(void)
56 if(autocvar_sv_adminnick != "")
57 return autocvar_sv_adminnick;
59 return "SERVER ADMIN";
62 void DistributeEvenly_Init(float amount, float totalweight)
64 if (DistributeEvenly_amount)
66 dprint("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
67 dprint(ftos(DistributeEvenly_totalweight), " left!)\n");
70 DistributeEvenly_amount = 0;
72 DistributeEvenly_amount = amount;
73 DistributeEvenly_totalweight = totalweight;
75 float DistributeEvenly_Get(float weight)
80 f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
81 DistributeEvenly_totalweight -= weight;
82 DistributeEvenly_amount -= f;
85 float DistributeEvenly_GetRandomized(float weight)
90 f = floor(random() + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
91 DistributeEvenly_totalweight -= weight;
92 DistributeEvenly_amount -= f;
97 void GameLogEcho(string s)
102 if (autocvar_sv_eventlog_files)
107 matches = autocvar_sv_eventlog_files_counter + 1;
108 cvar_set("sv_eventlog_files_counter", itos(matches));
111 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
112 fn = strcat(autocvar_sv_eventlog_files_nameprefix, fn, autocvar_sv_eventlog_files_namesuffix);
113 logfile = fopen(fn, FILE_APPEND);
114 fputs(logfile, ":logversion:3\n");
118 if (autocvar_sv_eventlog_files_timestamps)
119 fputs(logfile, strcat(":time:", strftime(true, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
121 fputs(logfile, strcat(s, "\n"));
124 if (autocvar_sv_eventlog_console)
133 // will be opened later
138 if (logfile_open && logfile >= 0)
145 entity findnearest(vector point, .string field, string value, vector axismod)
156 localhead = find(world, field, value);
159 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
160 dist = localhead.oldorigin;
162 dist = localhead.origin;
164 dist = dist.x * axismod.x * '1 0 0' + dist.y * axismod.y * '0 1 0' + dist.z * axismod.z * '0 0 1';
167 for (i = 0; i < num_nearest; ++i)
169 if (len < nearest_length[i])
173 // now i tells us where to insert at
174 // INSERTION SORT! YOU'VE SEEN IT! RUN!
175 if (i < NUM_NEAREST_ENTITIES)
177 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
179 nearest_length[j + 1] = nearest_length[j];
180 nearest_entity[j + 1] = nearest_entity[j];
182 nearest_length[i] = len;
183 nearest_entity[i] = localhead;
184 if (num_nearest < NUM_NEAREST_ENTITIES)
185 num_nearest = num_nearest + 1;
188 localhead = find(localhead, field, value);
191 // now use the first one from our list that we can see
192 for (i = 0; i < num_nearest; ++i)
194 traceline(point, nearest_entity[i].origin, true, world);
195 if (trace_fraction == 1)
199 dprint("Nearest point (");
200 dprint(nearest_entity[0].netname);
201 dprint(") is not visible, using a visible one.\n");
203 return nearest_entity[i];
207 if (num_nearest == 0)
210 dprint("Not seeing any location point, using nearest as fallback.\n");
212 dprint("Candidates were: ");
213 for(j = 0; j < num_nearest; ++j)
217 dprint(nearest_entity[j].netname);
222 return nearest_entity[0];
225 string NearestLocation(vector p)
230 loc = findnearest(p, classname, "target_location", '1 1 1');
237 loc = findnearest(p, target, "###item###", '1 1 4');
244 string formatmessage(string msg)
255 WarpZone_crosshair_trace(self);
256 cursor = trace_endpos;
257 cursor_ent = trace_ent;
261 break; // too many replacements
264 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
265 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
278 replacement = substring(msg, p, 2);
279 escape = substring(msg, p + 1, 1);
283 else if (escape == "\\")
285 else if (escape == "n")
287 else if (escape == "a")
288 replacement = ftos(floor(self.armorvalue));
289 else if (escape == "h")
290 replacement = ftos(floor(self.health));
291 else if (escape == "l")
292 replacement = NearestLocation(self.origin);
293 else if (escape == "y")
294 replacement = NearestLocation(cursor);
295 else if (escape == "d")
296 replacement = NearestLocation(self.death_origin);
297 else if (escape == "w") {
301 wep = self.switchweapon;
304 replacement = WEP_NAME(wep);
305 } else if (escape == "W") {
306 if (self.items & ITEM_Shells.m_itemid) replacement = "shells";
307 else if (self.items & ITEM_Bullets.m_itemid) replacement = "bullets";
308 else if (self.items & ITEM_Rockets.m_itemid) replacement = "rockets";
309 else if (self.items & ITEM_Cells.m_itemid) replacement = "cells";
310 else if (self.items & ITEM_Plasma.m_itemid) replacement = "plasma";
311 else replacement = "batteries"; // ;)
312 } else if (escape == "x") {
313 replacement = cursor_ent.netname;
314 if (replacement == "" || !cursor_ent)
315 replacement = "nothing";
316 } else if (escape == "s")
317 replacement = ftos(vlen(self.velocity - self.velocity.z * '0 0 1'));
318 else if (escape == "S")
319 replacement = ftos(vlen(self.velocity));
321 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
322 p = p + strlen(replacement);
327 float boolean(float value) { // if value is 0 return false (0), otherwise return true (1)
328 return (value == 0) ? false : true;
337 >0: receives a cvar from name=argv(f) value=argv(f+1)
339 void GetCvars_handleString(string thisname, float f, .string field, string name)
344 strunzone(self.(field));
345 self.(field) = string_null;
349 if (thisname == name)
352 strunzone(self.(field));
353 self.(field) = strzone(argv(f + 1));
357 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
359 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
361 GetCvars_handleString(thisname, f, field, name);
362 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
363 if (thisname == name)
365 string s = func(strcat1(self.(field)));
366 if (s != self.(field))
368 strunzone(self.(field));
369 self.(field) = strzone(s);
373 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
380 if (thisname == name)
381 self.(field) = stof(argv(f + 1));
384 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
386 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
393 if (thisname == name)
397 self.(field) = stof(argv(f + 1));
406 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
409 string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(string wo)
412 o = W_FixWeaponOrder_ForceComplete(wo);
413 if(self.weaponorder_byimpulse)
415 strunzone(self.weaponorder_byimpulse);
416 self.weaponorder_byimpulse = string_null;
418 self.weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(o));
421 void GetCvars(float f)
423 string s = string_null;
426 s = strcat1(argv(f));
430 MUTATOR_CALLHOOK(GetCvars);
432 Notification_GetCvars();
434 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
435 GetCvars_handleFloat(s, f, cvar_cl_autoscreenshot, "cl_autoscreenshot");
436 GetCvars_handleFloat(s, f, cvar_cl_jetpack_jump, "cl_jetpack_jump");
437 GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
438 GetCvars_handleString(s, f, cvar_cl_physics, "cl_physics");
439 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
440 GetCvars_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
441 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
442 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
443 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
444 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
445 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
446 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
447 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
448 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
449 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
450 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
451 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
452 GetCvars_handleFloat(s, f, cvar_cl_weaponimpulsemode, "cl_weaponimpulsemode");
453 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
454 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
455 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
456 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
457 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
458 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
460 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
461 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
463 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
464 GetCvars_handleFloat(s, f, cvar_cl_allow_uid2name, "cl_allow_uid2name");
465 GetCvars_handleFloat(s, f, cvar_cl_allow_uidtracking, "cl_allow_uidtracking");
466 GetCvars_handleFloat(s, f, cvar_cl_movement_track_canjump, "cl_movement_track_canjump");
467 GetCvars_handleFloat(s, f, cvar_cl_newusekeysupported, "cl_newusekeysupported");
469 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
472 if (s == "cl_weaponpriority")
473 self.switchweapon = w_getbestweapon(self);
474 if (s == "cl_allow_uidtracking")
475 PlayerStats_GameReport_AddPlayer(self);
479 // decolorizes and team colors the player name when needed
480 string playername(entity p)
483 if (teamplay && !intermission_running && IS_PLAYER(p))
485 t = Team_ColorCode(p.team);
486 return strcat(t, strdecolorize(p.netname));
492 vector randompos(vector m1, vector m2)
496 v.x = m2_x * random() + m1_x;
497 v.y = m2_y * random() + m1_y;
498 v.z = m2_z * random() + m1_z;
502 float want_weapon(entity weaponinfo, float allguns) // WEAPONTODO: what still needs done?
504 int i = weaponinfo.weapon;
510 if (g_lms || g_ca || allguns)
512 if(weaponinfo.spawnflags & WEP_FLAG_NORMAL)
518 d = (i == WEP_SHOTGUN.m_id);
520 d = 0; // weapon is set a few lines later
522 d = !(!weaponinfo.weaponstart);
524 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
525 d |= (i == WEP_HOOK.m_id);
526 if(!g_cts && (weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED)) // never default mutator blocked guns
529 float t = weaponinfo.weaponstartoverride;
531 //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n"));
536 // 4: is set by default?
545 void readplayerstartcvars()
551 // initialize starting values for players
552 start_weapons = '0 0 0';
553 start_weapons_default = '0 0 0';
554 start_weapons_defaultmask = '0 0 0';
556 start_ammo_shells = 0;
557 start_ammo_nails = 0;
558 start_ammo_rockets = 0;
559 start_ammo_cells = 0;
560 start_ammo_plasma = 0;
561 start_health = cvar("g_balance_health_start");
562 start_armorvalue = cvar("g_balance_armor_start");
565 g_weaponarena_weapons = '0 0 0';
567 s = cvar_string("g_weaponarena");
568 if (s == "0" || s == "")
570 if(g_ca || g_freezetag)
574 if (s == "0" || s == "")
580 // forcibly turn off weaponarena
582 else if (s == "all" || s == "1")
585 g_weaponarena_list = "All Weapons";
586 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
588 e = get_weaponinfo(j);
589 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
590 g_weaponarena_weapons |= WepSet_FromWeapon(j);
593 else if (s == "most")
596 g_weaponarena_list = "Most Weapons";
597 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
599 e = get_weaponinfo(j);
600 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
601 if (e.spawnflags & WEP_FLAG_NORMAL)
602 g_weaponarena_weapons |= WepSet_FromWeapon(j);
605 else if (s == "none")
608 g_weaponarena_list = "No Weapons";
613 t = tokenize_console(s);
614 g_weaponarena_list = "";
615 for (i = 0; i < t; ++i)
618 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
620 e = get_weaponinfo(j);
623 g_weaponarena_weapons |= WepSet_FromWeapon(j);
624 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
630 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
633 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
637 g_weaponarena_random = cvar("g_weaponarena_random");
639 g_weaponarena_random = 0;
640 g_weaponarena_random_with_blaster = cvar("g_weaponarena_random_with_blaster");
644 g_weapon_stay = 0; // incompatible
645 start_weapons = g_weaponarena_weapons;
646 start_items |= IT_UNLIMITED_AMMO;
650 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
652 e = get_weaponinfo(i);
653 int w = want_weapon(e, false);
655 start_weapons |= WepSet_FromWeapon(i);
657 start_weapons_default |= WepSet_FromWeapon(i);
659 start_weapons_defaultmask |= WepSet_FromWeapon(i);
663 if(!cvar("g_use_ammunition"))
664 start_items |= IT_UNLIMITED_AMMO;
666 if(start_items & IT_UNLIMITED_WEAPON_AMMO)
668 start_ammo_shells = 999;
669 start_ammo_nails = 999;
670 start_ammo_rockets = 999;
671 start_ammo_cells = 999;
672 start_ammo_plasma = 999;
673 start_ammo_fuel = 999;
677 start_ammo_shells = cvar("g_start_ammo_shells");
678 start_ammo_nails = cvar("g_start_ammo_nails");
679 start_ammo_rockets = cvar("g_start_ammo_rockets");
680 start_ammo_cells = cvar("g_start_ammo_cells");
681 start_ammo_plasma = cvar("g_start_ammo_plasma");
682 start_ammo_fuel = cvar("g_start_ammo_fuel");
687 warmup_start_ammo_shells = start_ammo_shells;
688 warmup_start_ammo_nails = start_ammo_nails;
689 warmup_start_ammo_rockets = start_ammo_rockets;
690 warmup_start_ammo_cells = start_ammo_cells;
691 warmup_start_ammo_plasma = start_ammo_plasma;
692 warmup_start_ammo_fuel = start_ammo_fuel;
693 warmup_start_health = start_health;
694 warmup_start_armorvalue = start_armorvalue;
695 warmup_start_weapons = start_weapons;
696 warmup_start_weapons_default = start_weapons_default;
697 warmup_start_weapons_defaultmask = start_weapons_defaultmask;
699 if (!g_weaponarena && !g_ca && !g_freezetag)
701 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
702 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
703 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
704 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
705 warmup_start_ammo_plasma = cvar("g_warmup_start_ammo_plasma");
706 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
707 warmup_start_health = cvar("g_warmup_start_health");
708 warmup_start_armorvalue = cvar("g_warmup_start_armor");
709 warmup_start_weapons = '0 0 0';
710 warmup_start_weapons_default = '0 0 0';
711 warmup_start_weapons_defaultmask = '0 0 0';
712 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
714 e = get_weaponinfo(i);
715 int w = want_weapon(e, g_warmup_allguns);
717 warmup_start_weapons |= WepSet_FromWeapon(i);
719 warmup_start_weapons_default |= WepSet_FromWeapon(i);
721 warmup_start_weapons_defaultmask |= WepSet_FromWeapon(i);
727 start_items |= ITEM_Jetpack.m_itemid;
729 MUTATOR_CALLHOOK(SetStartItems);
731 if ((start_items & ITEM_Jetpack.m_itemid) || (g_grappling_hook && (start_weapons & WEPSET_HOOK)))
733 start_items |= ITEM_JetpackRegen.m_itemid;
734 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
735 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
738 WepSet precache_weapons = start_weapons;
739 if (g_warmup_allguns != 1)
740 precache_weapons |= warmup_start_weapons;
741 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
743 e = get_weaponinfo(i);
744 if(precache_weapons & WepSet_FromWeapon(i))
745 WEP_ACTION(i, WR_INIT);
748 start_ammo_shells = max(0, start_ammo_shells);
749 start_ammo_nails = max(0, start_ammo_nails);
750 start_ammo_rockets = max(0, start_ammo_rockets);
751 start_ammo_cells = max(0, start_ammo_cells);
752 start_ammo_plasma = max(0, start_ammo_plasma);
753 start_ammo_fuel = max(0, start_ammo_fuel);
755 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
756 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
757 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
758 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
759 warmup_start_ammo_plasma = max(0, warmup_start_ammo_plasma);
760 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
763 float sound_allowed(float destin, entity e)
765 // sounds from world may always pass
768 if (e.classname == "body")
770 else if (e.realowner && e.realowner != e)
772 else if (e.owner && e.owner != e)
777 // sounds to self may always pass
778 if (destin == MSG_ONE)
781 // sounds by players can be removed
782 if (autocvar_bot_sound_monopoly)
783 if (IS_REAL_CLIENT(e))
785 // anything else may pass
790 void sound(entity e, float chan, string samp, float vol, float attenu)
792 if (!sound_allowed(MSG_BROADCAST, e))
794 sound7(e, chan, samp, vol, attenu, 0, 0);
797 void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float attenu)
801 if (!sound_allowed(_dest, e))
804 entno = num_for_edict(e);
805 idx = precache_sound_index(samp);
810 attenu = floor(attenu * 64);
811 vol = floor(vol * 255);
814 sflags |= SND_VOLUME;
816 sflags |= SND_ATTENUATION;
817 if (entno >= 8192 || chan < 0 || chan > 7)
818 sflags |= SND_LARGEENTITY;
820 sflags |= SND_LARGESOUND;
822 WriteByte(_dest, SVC_SOUND);
823 WriteByte(_dest, sflags);
824 if (sflags & SND_VOLUME)
825 WriteByte(_dest, vol);
826 if (sflags & SND_ATTENUATION)
827 WriteByte(_dest, attenu);
828 if (sflags & SND_LARGEENTITY)
830 WriteShort(_dest, entno);
831 WriteByte(_dest, chan);
835 WriteShort(_dest, entno * 8 + chan);
837 if (sflags & SND_LARGESOUND)
838 WriteShort(_dest, idx);
840 WriteByte(_dest, idx);
842 WriteCoord(_dest, o.x);
843 WriteCoord(_dest, o.y);
844 WriteCoord(_dest, o.z);
846 void soundto(float _dest, entity e, float chan, string samp, float vol, float _atten)
850 if (!sound_allowed(_dest, e))
853 o = e.origin + 0.5 * (e.mins + e.maxs);
854 soundtoat(_dest, e, o, chan, samp, vol, _atten);
856 void soundat(entity e, vector o, float chan, string samp, float vol, float _atten)
858 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, _atten);
860 void stopsoundto(float _dest, entity e, float chan)
864 if (!sound_allowed(_dest, e))
867 entno = num_for_edict(e);
869 if (entno >= 8192 || chan < 0 || chan > 7)
872 idx = precache_sound_index("misc/null.wav");
873 sflags = SND_LARGEENTITY;
875 sflags |= SND_LARGESOUND;
876 WriteByte(_dest, SVC_SOUND);
877 WriteByte(_dest, sflags);
878 WriteShort(_dest, entno);
879 WriteByte(_dest, chan);
880 if (sflags & SND_LARGESOUND)
881 WriteShort(_dest, idx);
883 WriteByte(_dest, idx);
884 WriteCoord(_dest, e.origin.x);
885 WriteCoord(_dest, e.origin.y);
886 WriteCoord(_dest, e.origin.z);
890 WriteByte(_dest, SVC_STOPSOUND);
891 WriteShort(_dest, entno * 8 + chan);
894 void stopsound(entity e, float chan)
896 if (!sound_allowed(MSG_BROADCAST, e))
899 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
900 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
903 void play2(entity e, string filename)
905 //stuffcmd(e, strcat("play2 ", filename, "\n"));
907 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTEN_NONE);
910 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
912 float spamsound(entity e, float chan, string samp, float vol, float _atten)
914 if (!sound_allowed(MSG_BROADCAST, e))
917 if (time > e.spamtime)
920 sound(e, chan, samp, vol, _atten);
926 void play2team(float t, string filename)
930 if (autocvar_bot_sound_monopoly)
933 FOR_EACH_REALPLAYER(head)
936 play2(head, filename);
940 void play2all(string samp)
942 if (autocvar_bot_sound_monopoly)
945 sound(world, CH_INFO, samp, VOL_BASE, ATTEN_NONE);
948 void PrecachePlayerSounds(string f);
949 void precache_playermodel(string m)
951 float globhandle, i, n;
954 if(substring(m, -9,5) == "_lod1")
956 if(substring(m, -9,5) == "_lod2")
959 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
962 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
966 globhandle = search_begin(strcat(m, "_*.sounds"), true, false);
969 n = search_getsize(globhandle);
970 for (i = 0; i < n; ++i)
972 //print(search_getfilename(globhandle, i), "\n");
973 f = search_getfilename(globhandle, i);
974 PrecachePlayerSounds(f);
976 search_end(globhandle);
978 void precache_all_playermodels(string pattern)
980 float globhandle, i, n;
983 globhandle = search_begin(pattern, true, false);
986 n = search_getsize(globhandle);
987 for (i = 0; i < n; ++i)
989 //print(search_getfilename(globhandle, i), "\n");
990 f = search_getfilename(globhandle, i);
991 precache_playermodel(f);
993 search_end(globhandle);
998 // gamemode related things
999 precache_model ("models/misc/chatbubble.spr");
1000 precache_model("models/ice/ice.md3");
1002 // Precache all player models if desired
1003 if (autocvar_sv_precacheplayermodels)
1005 PrecachePlayerSounds("sound/player/default.sounds");
1006 precache_all_playermodels("models/player/*.zym");
1007 precache_all_playermodels("models/player/*.dpm");
1008 precache_all_playermodels("models/player/*.md3");
1009 precache_all_playermodels("models/player/*.psk");
1010 precache_all_playermodels("models/player/*.iqm");
1013 if (autocvar_sv_defaultcharacter)
1016 s = autocvar_sv_defaultplayermodel_red;
1018 precache_playermodel(s);
1019 s = autocvar_sv_defaultplayermodel_blue;
1021 precache_playermodel(s);
1022 s = autocvar_sv_defaultplayermodel_yellow;
1024 precache_playermodel(s);
1025 s = autocvar_sv_defaultplayermodel_pink;
1027 precache_playermodel(s);
1028 s = autocvar_sv_defaultplayermodel;
1030 precache_playermodel(s);
1035 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1036 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1039 // gore and miscellaneous sounds
1040 //precache_sound ("misc/h2ohit.wav");
1041 precache_model ("models/hook.md3");
1042 precache_sound ("misc/armorimpact.wav");
1043 precache_sound ("misc/bodyimpact1.wav");
1044 precache_sound ("misc/bodyimpact2.wav");
1045 precache_sound ("misc/gib.wav");
1046 precache_sound ("misc/gib_splat01.wav");
1047 precache_sound ("misc/gib_splat02.wav");
1048 precache_sound ("misc/gib_splat03.wav");
1049 precache_sound ("misc/gib_splat04.wav");
1050 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1051 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1052 precache_sound ("misc/null.wav");
1053 precache_sound ("misc/spawn.wav");
1054 precache_sound ("misc/talk.wav");
1055 precache_sound ("misc/teleport.wav");
1056 precache_sound ("misc/poweroff.wav");
1057 precache_sound ("player/lava.wav");
1058 precache_sound ("player/slime.wav");
1060 precache_model ("models/sprites/0.spr32");
1061 precache_model ("models/sprites/1.spr32");
1062 precache_model ("models/sprites/2.spr32");
1063 precache_model ("models/sprites/3.spr32");
1064 precache_model ("models/sprites/4.spr32");
1065 precache_model ("models/sprites/5.spr32");
1066 precache_model ("models/sprites/6.spr32");
1067 precache_model ("models/sprites/7.spr32");
1068 precache_model ("models/sprites/8.spr32");
1069 precache_model ("models/sprites/9.spr32");
1070 precache_model ("models/sprites/10.spr32");
1072 // common weapon precaches
1073 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1074 precache_sound ("weapons/weapon_switch.wav");
1075 precache_sound ("weapons/weaponpickup.wav");
1076 precache_sound ("weapons/unavailable.wav");
1077 precache_sound ("weapons/dryfire.wav");
1078 if (g_grappling_hook)
1080 precache_sound ("weapons/hook_fire.wav"); // hook
1081 precache_sound ("weapons/hook_impact.wav"); // hook
1084 precache_model("models/elaser.mdl");
1085 precache_model("models/laser.mdl");
1086 precache_model("models/ebomb.mdl");
1089 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1091 if (!self.noise && self.music) // quake 3 uses the music field
1092 self.noise = self.music;
1094 // plays music for the level if there is any
1097 precache_sound (self.noise);
1098 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTEN_NONE);
1102 #include "precache-for-csqc.inc"
1106 void make_safe_for_remove(entity e)
1108 if (e.initialize_entity)
1110 entity ent, prev = world;
1111 for (ent = initialize_entity_first; ent; )
1113 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1115 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1116 // skip it in linked list
1119 prev.initialize_entity_next = ent.initialize_entity_next;
1120 ent = prev.initialize_entity_next;
1124 initialize_entity_first = ent.initialize_entity_next;
1125 ent = initialize_entity_first;
1131 ent = ent.initialize_entity_next;
1137 void objerror(string s)
1139 make_safe_for_remove(self);
1140 builtin_objerror(s);
1143 .float remove_except_protected_forbidden;
1144 void remove_except_protected(entity e)
1146 if(e.remove_except_protected_forbidden)
1147 error("not allowed to remove this at this point");
1151 void remove_unsafely(entity e)
1153 if(e.classname == "spike")
1154 error("Removing spikes is forbidden (crylink bug), please report");
1158 void remove_safely(entity e)
1160 make_safe_for_remove(e);
1164 void InitializeEntity(entity e, void(void) func, float order)
1168 if (!e || e.initialize_entity)
1170 // make a proxy initializer entity
1174 e.classname = "initialize_entity";
1178 e.initialize_entity = func;
1179 e.initialize_entity_order = order;
1181 cur = initialize_entity_first;
1185 if (!cur || cur.initialize_entity_order > order)
1187 // insert between prev and cur
1189 prev.initialize_entity_next = e;
1191 initialize_entity_first = e;
1192 e.initialize_entity_next = cur;
1196 cur = cur.initialize_entity_next;
1199 void InitializeEntitiesRun()
1202 startoflist = initialize_entity_first;
1203 initialize_entity_first = world;
1204 remove = remove_except_protected;
1205 for (self = startoflist; self; self = self.initialize_entity_next)
1207 self.remove_except_protected_forbidden = 1;
1209 for (self = startoflist; self; )
1212 var void(void) func;
1213 e = self.initialize_entity_next;
1214 func = self.initialize_entity;
1215 self.initialize_entity_order = 0;
1216 self.initialize_entity = func_null;
1217 self.initialize_entity_next = world;
1218 self.remove_except_protected_forbidden = 0;
1219 if (self.classname == "initialize_entity")
1223 builtin_remove(self);
1226 //dprint("Delayed initialization: ", self.classname, "\n");
1232 backtrace(strcat("Null function in: ", self.classname, "\n"));
1236 remove = remove_unsafely;
1239 void UncustomizeEntitiesRun()
1243 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1244 self.uncustomizeentityforclient();
1247 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1249 e.customizeentityforclient = customizer;
1250 e.uncustomizeentityforclient = uncustomizer;
1251 e.uncustomizeentityforclient_set = !!uncustomizer;
1254 void Net_LinkEntity(entity e, bool docull, float dt, bool(entity, int) sendfunc)
1258 if (e.classname == "")
1259 e.classname = "net_linked";
1261 if (e.model == "" || self.modelindex == 0)
1265 setmodel(e, "null");
1269 e.SendEntity = sendfunc;
1270 e.SendFlags = 0xFFFFFF;
1273 e.effects |= EF_NODEPTHTEST;
1277 e.nextthink = time + dt;
1278 e.think = SUB_Remove;
1283 .float(entity) isEliminated;
1284 float EliminatedPlayers_SendEntity(entity to, float sendflags)
1288 WriteByte(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS);
1289 WriteByte(MSG_ENTITY, sendflags);
1293 for(i = 1; i <= maxclients; i += 8)
1295 for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e))
1297 if(eliminatedPlayers.isEliminated(e))
1300 WriteByte(MSG_ENTITY, f);
1307 void EliminatedPlayers_Init(float(entity) isEliminated_func)
1309 if(eliminatedPlayers)
1311 backtrace("Can't spawn eliminatedPlayers again!");
1314 Net_LinkEntity(eliminatedPlayers = spawn(), false, 0, EliminatedPlayers_SendEntity);
1315 eliminatedPlayers.isEliminated = isEliminated_func;
1319 void adaptor_think2touch()
1328 void adaptor_think2use()
1340 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1342 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
1343 self.projectiledeathtype |= HITTYPE_SPLASH;
1344 adaptor_think2use();
1347 // deferred dropping
1348 void DropToFloor_Handler()
1350 builtin_droptofloor();
1351 self.dropped_origin = self.origin;
1356 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1361 float trace_hits_box_a0, trace_hits_box_a1;
1363 float trace_hits_box_1d(float end, float thmi, float thma)
1367 // just check if x is in range
1375 // do the trace with respect to x
1376 // 0 -> end has to stay in thmi -> thma
1377 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1378 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1379 if (trace_hits_box_a0 > trace_hits_box_a1)
1385 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1390 // now it is a trace from 0 to end
1392 trace_hits_box_a0 = 0;
1393 trace_hits_box_a1 = 1;
1395 if (!trace_hits_box_1d(end.x, thmi.x, thma.x))
1397 if (!trace_hits_box_1d(end.y, thmi.y, thma.y))
1399 if (!trace_hits_box_1d(end.z, thmi.z, thma.z))
1405 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1407 return trace_hits_box(start, end, thmi - ma, thma - mi);
1410 float SUB_NoImpactCheck()
1412 // zero hitcontents = this is not the real impact, but either the
1413 // mirror-impact of something hitting the projectile instead of the
1414 // projectile hitting the something, or a touchareagrid one. Neither of
1415 // these stop the projectile from moving, so...
1416 if(trace_dphitcontents == 0)
1418 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1419 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));
1422 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1424 if (other == world && self.size != '0 0 0')
1427 tic = self.velocity * sys_frametime;
1428 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1429 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1430 if (trace_fraction >= 1)
1432 dprint("Odd... did not hit...?\n");
1434 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1436 dprint("Detected and prevented the sky-grapple bug.\n");
1444 #define SUB_OwnerCheck() (other && (other == self.owner))
1446 void W_Crylink_Dequeue(entity e);
1447 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1449 if(SUB_OwnerCheck())
1451 if(SUB_NoImpactCheck())
1453 if(self.classname == "nade")
1454 return false; // no checks here
1455 else if(self.classname == "grapplinghook")
1456 RemoveGrapplingHook(self.realowner);
1457 else if(self.classname == "spike")
1459 W_Crylink_Dequeue(self);
1466 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1467 UpdateCSQCProjectile(self);
1472 void URI_Get_Callback(float id, float status, string data)
1474 if(url_URI_Get_Callback(id, status, data))
1478 else if (id == URI_GET_DISCARD)
1482 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
1485 Curl_URI_Get_Callback(id, status, data);
1487 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1490 OnlineBanList_URI_Get_Callback(id, status, data);
1494 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1498 string uid2name(string myuid) {
1500 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
1502 // FIXME remove this later after 0.6 release
1503 // convert old style broken records to correct style
1506 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
1509 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
1510 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
1515 s = "^1Unregistered Player";
1519 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1522 vector start, org, delta, end, enddown, mstart;
1525 m = e.dphitcontentsmask;
1526 e.dphitcontentsmask = goodcontents | badcontents;
1529 delta = world.maxs - world.mins;
1533 for (i = 0; i < attempts; ++i)
1535 start.x = org.x + random() * delta.x;
1536 start.y = org.y + random() * delta.y;
1537 start.z = org.z + random() * delta.z;
1539 // rule 1: start inside world bounds, and outside
1540 // solid, and don't start from somewhere where you can
1541 // fall down to evil
1542 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta.z, MOVE_NORMAL, e);
1543 if (trace_fraction >= 1)
1545 if (trace_startsolid)
1547 if (trace_dphitcontents & badcontents)
1549 if (trace_dphitq3surfaceflags & badsurfaceflags)
1552 // rule 2: if we are too high, lower the point
1553 if (trace_fraction * delta.z > maxaboveground)
1554 start = trace_endpos + '0 0 1' * maxaboveground;
1555 enddown = trace_endpos;
1557 // rule 3: make sure we aren't outside the map. This only works
1558 // for somewhat well formed maps. A good rule of thumb is that
1559 // the map should have a convex outside hull.
1560 // these can be traceLINES as we already verified the starting box
1561 mstart = start + 0.5 * (e.mins + e.maxs);
1562 traceline(mstart, mstart + '1 0 0' * delta.x, MOVE_NORMAL, e);
1563 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1565 traceline(mstart, mstart - '1 0 0' * delta.x, MOVE_NORMAL, e);
1566 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1568 traceline(mstart, mstart + '0 1 0' * delta.y, MOVE_NORMAL, e);
1569 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1571 traceline(mstart, mstart - '0 1 0' * delta.y, MOVE_NORMAL, e);
1572 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1574 traceline(mstart, mstart + '0 0 1' * delta.z, MOVE_NORMAL, e);
1575 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1578 // rule 4: we must "see" some spawnpoint or item
1579 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
1580 if(checkpvs(mstart, sp))
1581 if((traceline(mstart, sp.origin, MOVE_NORMAL, e), trace_fraction) >= 1)
1585 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
1586 if(checkpvs(mstart, sp))
1587 if((traceline(mstart, sp.origin + (sp.mins + sp.maxs) * 0.5, MOVE_NORMAL, e), trace_fraction) >= 1)
1593 // find a random vector to "look at"
1594 end.x = org.x + random() * delta.x;
1595 end.y = org.y + random() * delta.y;
1596 end.z = org.z + random() * delta.z;
1597 end = start + normalize(end - start) * vlen(delta);
1599 // rule 4: start TO end must not be too short
1600 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1601 if (trace_startsolid)
1603 if (trace_fraction < minviewdistance / vlen(delta))
1606 // rule 5: don't want to look at sky
1607 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
1610 // rule 6: we must not end up in trigger_hurt
1611 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
1617 e.dphitcontentsmask = m;
1621 setorigin(e, start);
1622 e.angles = vectoangles(end - start);
1623 dprint("Needed ", ftos(i + 1), " attempts\n");
1630 void write_recordmarker(entity pl, float tstart, float dt)
1632 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
1634 // also write a marker into demo files for demotc-race-record-extractor to find
1637 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
1638 " ", ftos(tstart), " ", ftos(dt), "\n"));
1641 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
1654 if(allowcenter) // 2: allow center handedness
1667 if(allowcenter) // 2: allow center handedness
1683 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
1688 if (autocvar_g_shootfromeye)
1692 if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
1693 else { vecs.y = 0; vecs.z -= 2; }
1701 else if (autocvar_g_shootfromcenter)
1706 else if ((s = autocvar_g_shootfromfixedorigin) != "")
1716 else if (autocvar_g_shootfromclient)
1718 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
1723 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
1725 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
1729 void attach_sameorigin(entity e, entity to, string tag)
1731 vector org, t_forward, t_left, t_up, e_forward, e_up;
1734 org = e.origin - gettaginfo(to, gettagindex(to, tag));
1735 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
1736 t_forward = v_forward * tagscale;
1737 t_left = v_right * -tagscale;
1738 t_up = v_up * tagscale;
1740 e.origin_x = org * t_forward;
1741 e.origin_y = org * t_left;
1742 e.origin_z = org * t_up;
1744 // current forward and up directions
1745 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1746 e.angles = AnglesTransform_FromVAngles(e.angles);
1748 e.angles = AnglesTransform_FromAngles(e.angles);
1749 fixedmakevectors(e.angles);
1751 // untransform forward, up!
1752 e_forward.x = v_forward * t_forward;
1753 e_forward.y = v_forward * t_left;
1754 e_forward.z = v_forward * t_up;
1755 e_up.x = v_up * t_forward;
1756 e_up.y = v_up * t_left;
1757 e_up.z = v_up * t_up;
1759 e.angles = fixedvectoangles2(e_forward, e_up);
1760 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1761 e.angles = AnglesTransform_ToVAngles(e.angles);
1763 e.angles = AnglesTransform_ToAngles(e.angles);
1765 setattachment(e, to, tag);
1766 setorigin(e, e.origin);
1769 void detach_sameorigin(entity e)
1772 org = gettaginfo(e, 0);
1773 e.angles = fixedvectoangles2(v_forward, v_up);
1774 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1775 e.angles = AnglesTransform_ToVAngles(e.angles);
1777 e.angles = AnglesTransform_ToAngles(e.angles);
1779 setattachment(e, world, "");
1780 setorigin(e, e.origin);
1783 void follow_sameorigin(entity e, entity to)
1785 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
1786 e.aiment = to; // make the hole follow bmodel
1787 e.punchangle = to.angles; // the original angles of bmodel
1788 e.view_ofs = e.origin - to.origin; // relative origin
1789 e.v_angle = e.angles - to.angles; // relative angles
1792 void unfollow_sameorigin(entity e)
1794 e.movetype = MOVETYPE_NONE;
1797 entity gettaginfo_relative_ent;
1798 vector gettaginfo_relative(entity e, float tag)
1800 if (!gettaginfo_relative_ent)
1802 gettaginfo_relative_ent = spawn();
1803 gettaginfo_relative_ent.effects = EF_NODRAW;
1805 gettaginfo_relative_ent.model = e.model;
1806 gettaginfo_relative_ent.modelindex = e.modelindex;
1807 gettaginfo_relative_ent.frame = e.frame;
1808 return gettaginfo(gettaginfo_relative_ent, tag);
1813 float modeleffect_SendEntity(entity to, int sf)
1816 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
1819 if(self.velocity != '0 0 0')
1821 if(self.angles != '0 0 0')
1823 if(self.avelocity != '0 0 0')
1826 WriteByte(MSG_ENTITY, f);
1827 WriteShort(MSG_ENTITY, self.modelindex);
1828 WriteByte(MSG_ENTITY, self.skin);
1829 WriteByte(MSG_ENTITY, self.frame);
1830 WriteCoord(MSG_ENTITY, self.origin.x);
1831 WriteCoord(MSG_ENTITY, self.origin.y);
1832 WriteCoord(MSG_ENTITY, self.origin.z);
1835 WriteCoord(MSG_ENTITY, self.velocity.x);
1836 WriteCoord(MSG_ENTITY, self.velocity.y);
1837 WriteCoord(MSG_ENTITY, self.velocity.z);
1841 WriteCoord(MSG_ENTITY, self.angles.x);
1842 WriteCoord(MSG_ENTITY, self.angles.y);
1843 WriteCoord(MSG_ENTITY, self.angles.z);
1847 WriteCoord(MSG_ENTITY, self.avelocity.x);
1848 WriteCoord(MSG_ENTITY, self.avelocity.y);
1849 WriteCoord(MSG_ENTITY, self.avelocity.z);
1851 WriteShort(MSG_ENTITY, self.scale * 256.0);
1852 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
1853 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
1854 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
1855 WriteByte(MSG_ENTITY, self.alpha * 255.0);
1860 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)
1865 e.classname = "modeleffect";
1873 e.teleport_time = t1;
1877 e.scale = s0 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1881 e.scale2 = s2 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1884 sz = max(e.scale, e.scale2);
1885 setsize(e, e.mins * sz, e.maxs * sz);
1886 Net_LinkEntity(e, false, 0.1, modeleffect_SendEntity);
1889 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
1891 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
1894 float randombit(float bits)
1896 if(!(bits & (bits-1))) // this ONLY holds for powers of two!
1905 for(f = 1; f <= bits; f *= 2)
1914 r = (r - 1) / (n - 1);
1921 float randombits(float bits, float k, float error_return)
1925 while(k > 0 && bits != r)
1927 r += randombit(bits - r);
1936 void randombit_test(float bits, float iter)
1940 print(ftos(randombit(bits)), "\n");
1945 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
1947 if(halflifedist > 0)
1948 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
1949 else if(halflifedist < 0)
1950 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
1962 oself.think = SUB_Remove;
1963 oself.nextthink = time;
1969 Execute func() after time + fdelay.
1970 self when func is executed = self when defer is called
1972 void defer(float fdelay, void() func)
1979 e.think = defer_think;
1980 e.nextthink = time + fdelay;
1983 .string aiment_classname;
1984 .float aiment_deadflag;
1985 void SetMovetypeFollow(entity ent, entity e)
1987 // FIXME this may not be warpzone aware
1988 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
1989 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.
1990 ent.aiment = e; // make the hole follow bmodel
1991 ent.punchangle = e.angles; // the original angles of bmodel
1992 ent.view_ofs = ent.origin - e.origin; // relative origin
1993 ent.v_angle = ent.angles - e.angles; // relative angles
1994 ent.aiment_classname = strzone(e.classname);
1995 ent.aiment_deadflag = e.deadflag;
1997 void UnsetMovetypeFollow(entity ent)
1999 ent.movetype = MOVETYPE_FLY;
2000 PROJECTILE_MAKETRIGGER(ent);
2003 float LostMovetypeFollow(entity ent)
2006 if(ent.movetype != MOVETYPE_FOLLOW)
2012 if(ent.aiment.classname != ent.aiment_classname)
2014 if(ent.aiment.deadflag != ent.aiment_deadflag)
2020 float isPushable(entity e)
2029 case "droppedweapon":
2030 case "keepawayball":
2031 case "nexball_basketball":
2032 case "nexball_football":
2034 case "bullet": // antilagged bullets can't hit this either
2037 if (e.projectiledeathtype)