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
976 // Precache all player models if desired
977 if (autocvar_sv_precacheplayermodels)
979 PrecachePlayerSounds("sound/player/default.sounds");
980 precache_all_playermodels("models/player/*.zym");
981 precache_all_playermodels("models/player/*.dpm");
982 precache_all_playermodels("models/player/*.md3");
983 precache_all_playermodels("models/player/*.psk");
984 precache_all_playermodels("models/player/*.iqm");
987 if (autocvar_sv_defaultcharacter)
990 s = autocvar_sv_defaultplayermodel_red;
992 precache_playermodel(s);
993 s = autocvar_sv_defaultplayermodel_blue;
995 precache_playermodel(s);
996 s = autocvar_sv_defaultplayermodel_yellow;
998 precache_playermodel(s);
999 s = autocvar_sv_defaultplayermodel_pink;
1001 precache_playermodel(s);
1002 s = autocvar_sv_defaultplayermodel;
1004 precache_playermodel(s);
1009 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1010 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1013 // gore and miscellaneous sounds
1014 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1015 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1018 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1020 if (!self.noise && self.music) // quake 3 uses the music field
1021 self.noise = self.music;
1023 // plays music for the level if there is any
1026 precache_sound (self.noise);
1027 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTEN_NONE);
1033 void make_safe_for_remove(entity e)
1035 if (e.initialize_entity)
1037 entity ent, prev = world;
1038 for (ent = initialize_entity_first; ent; )
1040 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1042 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1043 // skip it in linked list
1046 prev.initialize_entity_next = ent.initialize_entity_next;
1047 ent = prev.initialize_entity_next;
1051 initialize_entity_first = ent.initialize_entity_next;
1052 ent = initialize_entity_first;
1058 ent = ent.initialize_entity_next;
1064 void objerror(string s)
1066 make_safe_for_remove(self);
1067 builtin_objerror(s);
1070 .float remove_except_protected_forbidden;
1071 void remove_except_protected(entity e)
1073 if(e.remove_except_protected_forbidden)
1074 error("not allowed to remove this at this point");
1078 void remove_unsafely(entity e)
1080 if(e.classname == "spike")
1081 error("Removing spikes is forbidden (crylink bug), please report");
1085 void remove_safely(entity e)
1087 make_safe_for_remove(e);
1091 void InitializeEntity(entity e, void(void) func, float order)
1095 if (!e || e.initialize_entity)
1097 // make a proxy initializer entity
1099 e = new(initialize_entity);
1103 e.initialize_entity = func;
1104 e.initialize_entity_order = order;
1106 cur = initialize_entity_first;
1110 if (!cur || cur.initialize_entity_order > order)
1112 // insert between prev and cur
1114 prev.initialize_entity_next = e;
1116 initialize_entity_first = e;
1117 e.initialize_entity_next = cur;
1121 cur = cur.initialize_entity_next;
1124 void InitializeEntitiesRun()
1126 entity startoflist = initialize_entity_first;
1127 initialize_entity_first = NULL;
1128 remove = remove_except_protected;
1129 for (entity e = startoflist; e; e = e.initialize_entity_next)
1131 e.remove_except_protected_forbidden = 1;
1133 for (entity e = startoflist; e; )
1135 e.remove_except_protected_forbidden = 0;
1136 e.initialize_entity_order = 0;
1137 entity next = e.initialize_entity_next;
1138 e.initialize_entity_next = NULL;
1139 var void() func = e.initialize_entity;
1140 e.initialize_entity = func_null;
1141 if (e.classname == "initialize_entity")
1143 entity wrappee = e.enemy;
1147 //dprint("Delayed initialization: ", e.classname, "\n");
1150 WITH(entity, self, e, func());
1155 backtrace(strcat("Null function in: ", e.classname, "\n"));
1159 remove = remove_unsafely;
1162 void UncustomizeEntitiesRun()
1164 for (entity e = NULL; (e = findfloat(e, uncustomizeentityforclient_set, 1)); )
1166 WITH(entity, self, e, e.uncustomizeentityforclient());
1169 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1171 e.customizeentityforclient = customizer;
1172 e.uncustomizeentityforclient = uncustomizer;
1173 e.uncustomizeentityforclient_set = !!uncustomizer;
1176 void Net_LinkEntity(entity e, bool docull, float dt, bool(entity, int) sendfunc)
1180 if (e.classname == "")
1181 e.classname = "net_linked";
1183 if (e.model == "" || self.modelindex == 0)
1187 setmodel(e, MDL_Null);
1191 e.SendEntity = sendfunc;
1192 e.SendFlags = 0xFFFFFF;
1195 e.effects |= EF_NODEPTHTEST;
1199 e.nextthink = time + dt;
1200 e.think = SUB_Remove;
1205 .float(entity) isEliminated;
1206 float EliminatedPlayers_SendEntity(entity to, float sendflags)
1210 WriteByte(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS);
1211 WriteByte(MSG_ENTITY, sendflags);
1215 for(i = 1; i <= maxclients; i += 8)
1217 for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e))
1219 if(eliminatedPlayers.isEliminated(e))
1222 WriteByte(MSG_ENTITY, f);
1229 void EliminatedPlayers_Init(float(entity) isEliminated_func)
1231 if(eliminatedPlayers)
1233 backtrace("Can't spawn eliminatedPlayers again!");
1236 Net_LinkEntity(eliminatedPlayers = spawn(), false, 0, EliminatedPlayers_SendEntity);
1237 eliminatedPlayers.isEliminated = isEliminated_func;
1241 void adaptor_think2touch()
1250 void adaptor_think2use()
1262 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1264 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
1265 self.projectiledeathtype |= HITTYPE_SPLASH;
1266 adaptor_think2use();
1269 // deferred dropping
1270 void DropToFloor_Handler()
1272 builtin_droptofloor();
1273 self.dropped_origin = self.origin;
1278 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1283 float trace_hits_box_a0, trace_hits_box_a1;
1285 float trace_hits_box_1d(float end, float thmi, float thma)
1289 // just check if x is in range
1297 // do the trace with respect to x
1298 // 0 -> end has to stay in thmi -> thma
1299 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1300 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1301 if (trace_hits_box_a0 > trace_hits_box_a1)
1307 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1312 // now it is a trace from 0 to end
1314 trace_hits_box_a0 = 0;
1315 trace_hits_box_a1 = 1;
1317 if (!trace_hits_box_1d(end.x, thmi.x, thma.x))
1319 if (!trace_hits_box_1d(end.y, thmi.y, thma.y))
1321 if (!trace_hits_box_1d(end.z, thmi.z, thma.z))
1327 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1329 return trace_hits_box(start, end, thmi - ma, thma - mi);
1332 float SUB_NoImpactCheck()
1334 // zero hitcontents = this is not the real impact, but either the
1335 // mirror-impact of something hitting the projectile instead of the
1336 // projectile hitting the something, or a touchareagrid one. Neither of
1337 // these stop the projectile from moving, so...
1338 if(trace_dphitcontents == 0)
1340 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1341 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));
1344 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1346 if (other == world && self.size != '0 0 0')
1349 tic = self.velocity * sys_frametime;
1350 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1351 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1352 if (trace_fraction >= 1)
1354 LOG_TRACE("Odd... did not hit...?\n");
1356 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1358 LOG_TRACE("Detected and prevented the sky-grapple bug.\n");
1366 #define SUB_OwnerCheck() (other && (other == self.owner))
1368 void W_Crylink_Dequeue(entity e);
1369 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1371 if(SUB_OwnerCheck())
1373 if(SUB_NoImpactCheck())
1375 if(self.classname == "nade")
1376 return false; // no checks here
1377 else if(self.classname == "grapplinghook")
1378 RemoveGrapplingHook(self.realowner);
1379 else if(self.classname == "spike")
1381 W_Crylink_Dequeue(self);
1388 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1389 UpdateCSQCProjectile(self);
1394 void URI_Get_Callback(float id, float status, string data)
1396 if(url_URI_Get_Callback(id, status, data))
1400 else if (id == URI_GET_DISCARD)
1404 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
1407 Curl_URI_Get_Callback(id, status, data);
1409 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1412 OnlineBanList_URI_Get_Callback(id, status, data);
1416 LOG_INFO("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1420 string uid2name(string myuid) {
1422 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
1424 // FIXME remove this later after 0.6 release
1425 // convert old style broken records to correct style
1428 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
1431 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
1432 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
1437 s = "^1Unregistered Player";
1441 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1444 vector start, org, delta, end, enddown, mstart;
1447 m = e.dphitcontentsmask;
1448 e.dphitcontentsmask = goodcontents | badcontents;
1451 delta = world.maxs - world.mins;
1455 for (i = 0; i < attempts; ++i)
1457 start.x = org.x + random() * delta.x;
1458 start.y = org.y + random() * delta.y;
1459 start.z = org.z + random() * delta.z;
1461 // rule 1: start inside world bounds, and outside
1462 // solid, and don't start from somewhere where you can
1463 // fall down to evil
1464 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta.z, MOVE_NORMAL, e);
1465 if (trace_fraction >= 1)
1467 if (trace_startsolid)
1469 if (trace_dphitcontents & badcontents)
1471 if (trace_dphitq3surfaceflags & badsurfaceflags)
1474 // rule 2: if we are too high, lower the point
1475 if (trace_fraction * delta.z > maxaboveground)
1476 start = trace_endpos + '0 0 1' * maxaboveground;
1477 enddown = trace_endpos;
1479 // rule 3: make sure we aren't outside the map. This only works
1480 // for somewhat well formed maps. A good rule of thumb is that
1481 // the map should have a convex outside hull.
1482 // these can be traceLINES as we already verified the starting box
1483 mstart = start + 0.5 * (e.mins + e.maxs);
1484 traceline(mstart, mstart + '1 0 0' * delta.x, MOVE_NORMAL, e);
1485 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1487 traceline(mstart, mstart - '1 0 0' * delta.x, MOVE_NORMAL, e);
1488 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1490 traceline(mstart, mstart + '0 1 0' * delta.y, MOVE_NORMAL, e);
1491 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1493 traceline(mstart, mstart - '0 1 0' * delta.y, MOVE_NORMAL, e);
1494 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1496 traceline(mstart, mstart + '0 0 1' * delta.z, MOVE_NORMAL, e);
1497 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1500 // rule 4: we must "see" some spawnpoint or item
1501 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
1502 if(checkpvs(mstart, sp))
1503 if((traceline(mstart, sp.origin, MOVE_NORMAL, e), trace_fraction) >= 1)
1507 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
1508 if(checkpvs(mstart, sp))
1509 if((traceline(mstart, sp.origin + (sp.mins + sp.maxs) * 0.5, MOVE_NORMAL, e), trace_fraction) >= 1)
1515 // find a random vector to "look at"
1516 end.x = org.x + random() * delta.x;
1517 end.y = org.y + random() * delta.y;
1518 end.z = org.z + random() * delta.z;
1519 end = start + normalize(end - start) * vlen(delta);
1521 // rule 4: start TO end must not be too short
1522 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1523 if (trace_startsolid)
1525 if (trace_fraction < minviewdistance / vlen(delta))
1528 // rule 5: don't want to look at sky
1529 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
1532 // rule 6: we must not end up in trigger_hurt
1533 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
1539 e.dphitcontentsmask = m;
1543 setorigin(e, start);
1544 e.angles = vectoangles(end - start);
1545 LOG_TRACE("Needed ", ftos(i + 1), " attempts\n");
1552 void write_recordmarker(entity pl, float tstart, float dt)
1554 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
1556 // also write a marker into demo files for demotc-race-record-extractor to find
1559 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
1560 " ", ftos(tstart), " ", ftos(dt), "\n"));
1563 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
1576 if(allowcenter) // 2: allow center handedness
1589 if(allowcenter) // 2: allow center handedness
1605 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
1610 if (autocvar_g_shootfromeye)
1614 if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
1615 else { vecs.y = 0; vecs.z -= 2; }
1623 else if (autocvar_g_shootfromcenter)
1628 else if ((s = autocvar_g_shootfromfixedorigin) != "")
1638 else if (autocvar_g_shootfromclient)
1640 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
1645 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
1647 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
1651 void attach_sameorigin(entity e, entity to, string tag)
1653 vector org, t_forward, t_left, t_up, e_forward, e_up;
1656 org = e.origin - gettaginfo(to, gettagindex(to, tag));
1657 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
1658 t_forward = v_forward * tagscale;
1659 t_left = v_right * -tagscale;
1660 t_up = v_up * tagscale;
1662 e.origin_x = org * t_forward;
1663 e.origin_y = org * t_left;
1664 e.origin_z = org * t_up;
1666 // current forward and up directions
1667 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1668 e.angles = AnglesTransform_FromVAngles(e.angles);
1670 e.angles = AnglesTransform_FromAngles(e.angles);
1671 fixedmakevectors(e.angles);
1673 // untransform forward, up!
1674 e_forward.x = v_forward * t_forward;
1675 e_forward.y = v_forward * t_left;
1676 e_forward.z = v_forward * t_up;
1677 e_up.x = v_up * t_forward;
1678 e_up.y = v_up * t_left;
1679 e_up.z = v_up * t_up;
1681 e.angles = fixedvectoangles2(e_forward, e_up);
1682 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1683 e.angles = AnglesTransform_ToVAngles(e.angles);
1685 e.angles = AnglesTransform_ToAngles(e.angles);
1687 setattachment(e, to, tag);
1688 setorigin(e, e.origin);
1691 void detach_sameorigin(entity e)
1694 org = gettaginfo(e, 0);
1695 e.angles = fixedvectoangles2(v_forward, v_up);
1696 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1697 e.angles = AnglesTransform_ToVAngles(e.angles);
1699 e.angles = AnglesTransform_ToAngles(e.angles);
1701 setattachment(e, world, "");
1702 setorigin(e, e.origin);
1705 void follow_sameorigin(entity e, entity to)
1707 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
1708 e.aiment = to; // make the hole follow bmodel
1709 e.punchangle = to.angles; // the original angles of bmodel
1710 e.view_ofs = e.origin - to.origin; // relative origin
1711 e.v_angle = e.angles - to.angles; // relative angles
1714 void unfollow_sameorigin(entity e)
1716 e.movetype = MOVETYPE_NONE;
1719 entity gettaginfo_relative_ent;
1720 vector gettaginfo_relative(entity e, float tag)
1722 if (!gettaginfo_relative_ent)
1724 gettaginfo_relative_ent = spawn();
1725 gettaginfo_relative_ent.effects = EF_NODRAW;
1727 gettaginfo_relative_ent.model = e.model;
1728 gettaginfo_relative_ent.modelindex = e.modelindex;
1729 gettaginfo_relative_ent.frame = e.frame;
1730 return gettaginfo(gettaginfo_relative_ent, tag);
1735 float modeleffect_SendEntity(entity to, int sf)
1738 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
1741 if(self.velocity != '0 0 0')
1743 if(self.angles != '0 0 0')
1745 if(self.avelocity != '0 0 0')
1748 WriteByte(MSG_ENTITY, f);
1749 WriteShort(MSG_ENTITY, self.modelindex);
1750 WriteByte(MSG_ENTITY, self.skin);
1751 WriteByte(MSG_ENTITY, self.frame);
1752 WriteCoord(MSG_ENTITY, self.origin.x);
1753 WriteCoord(MSG_ENTITY, self.origin.y);
1754 WriteCoord(MSG_ENTITY, self.origin.z);
1757 WriteCoord(MSG_ENTITY, self.velocity.x);
1758 WriteCoord(MSG_ENTITY, self.velocity.y);
1759 WriteCoord(MSG_ENTITY, self.velocity.z);
1763 WriteCoord(MSG_ENTITY, self.angles.x);
1764 WriteCoord(MSG_ENTITY, self.angles.y);
1765 WriteCoord(MSG_ENTITY, self.angles.z);
1769 WriteCoord(MSG_ENTITY, self.avelocity.x);
1770 WriteCoord(MSG_ENTITY, self.avelocity.y);
1771 WriteCoord(MSG_ENTITY, self.avelocity.z);
1773 WriteShort(MSG_ENTITY, self.scale * 256.0);
1774 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
1775 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
1776 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
1777 WriteByte(MSG_ENTITY, self.alpha * 255.0);
1782 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)
1787 e.classname = "modeleffect";
1795 e.teleport_time = t1;
1799 e.scale = s0 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1803 e.scale2 = s2 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1806 sz = max(e.scale, e.scale2);
1807 setsize(e, e.mins * sz, e.maxs * sz);
1808 Net_LinkEntity(e, false, 0.1, modeleffect_SendEntity);
1811 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
1813 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
1816 float randombit(float bits)
1818 if(!(bits & (bits-1))) // this ONLY holds for powers of two!
1827 for(f = 1; f <= bits; f *= 2)
1836 r = (r - 1) / (n - 1);
1843 float randombits(float bits, float k, float error_return)
1847 while(k > 0 && bits != r)
1849 r += randombit(bits - r);
1858 void randombit_test(float bits, float iter)
1862 LOG_INFO(ftos(randombit(bits)), "\n");
1867 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
1869 if(halflifedist > 0)
1870 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
1871 else if(halflifedist < 0)
1872 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
1878 .string aiment_classname;
1879 .float aiment_deadflag;
1880 void SetMovetypeFollow(entity ent, entity e)
1882 // FIXME this may not be warpzone aware
1883 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
1884 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.
1885 ent.aiment = e; // make the hole follow bmodel
1886 ent.punchangle = e.angles; // the original angles of bmodel
1887 ent.view_ofs = ent.origin - e.origin; // relative origin
1888 ent.v_angle = ent.angles - e.angles; // relative angles
1889 ent.aiment_classname = strzone(e.classname);
1890 ent.aiment_deadflag = e.deadflag;
1892 void UnsetMovetypeFollow(entity ent)
1894 ent.movetype = MOVETYPE_FLY;
1895 PROJECTILE_MAKETRIGGER(ent);
1898 float LostMovetypeFollow(entity ent)
1901 if(ent.movetype != MOVETYPE_FOLLOW)
1907 if(ent.aiment.classname != ent.aiment_classname)
1909 if(ent.aiment.deadflag != ent.aiment_deadflag)
1915 float isPushable(entity e)
1926 case "droppedweapon":
1927 case "keepawayball":
1928 case "nexball_basketball":
1929 case "nexball_football":
1931 case "bullet": // antilagged bullets can't hit this either
1934 if (e.projectiledeathtype)