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
765 void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float attenu)
769 if (!sound_allowed(_dest, e))
772 entno = num_for_edict(e);
773 idx = precache_sound_index(samp);
778 attenu = floor(attenu * 64);
779 vol = floor(vol * 255);
782 sflags |= SND_VOLUME;
784 sflags |= SND_ATTENUATION;
785 if (entno >= 8192 || chan < 0 || chan > 7)
786 sflags |= SND_LARGEENTITY;
788 sflags |= SND_LARGESOUND;
790 WriteByte(_dest, SVC_SOUND);
791 WriteByte(_dest, sflags);
792 if (sflags & SND_VOLUME)
793 WriteByte(_dest, vol);
794 if (sflags & SND_ATTENUATION)
795 WriteByte(_dest, attenu);
796 if (sflags & SND_LARGEENTITY)
798 WriteShort(_dest, entno);
799 WriteByte(_dest, chan);
803 WriteShort(_dest, entno * 8 + chan);
805 if (sflags & SND_LARGESOUND)
806 WriteShort(_dest, idx);
808 WriteByte(_dest, idx);
810 WriteCoord(_dest, o.x);
811 WriteCoord(_dest, o.y);
812 WriteCoord(_dest, o.z);
814 void soundto(float _dest, entity e, float chan, string samp, float vol, float _atten)
818 if (!sound_allowed(_dest, e))
821 o = e.origin + 0.5 * (e.mins + e.maxs);
822 soundtoat(_dest, e, o, chan, samp, vol, _atten);
824 void soundat(entity e, vector o, float chan, string samp, float vol, float _atten)
826 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, _atten);
828 void stopsoundto(float _dest, entity e, float chan)
832 if (!sound_allowed(_dest, e))
835 entno = num_for_edict(e);
837 if (entno >= 8192 || chan < 0 || chan > 7)
840 idx = precache_sound_index(SND(Null));
841 sflags = SND_LARGEENTITY;
843 sflags |= SND_LARGESOUND;
844 WriteByte(_dest, SVC_SOUND);
845 WriteByte(_dest, sflags);
846 WriteShort(_dest, entno);
847 WriteByte(_dest, chan);
848 if (sflags & SND_LARGESOUND)
849 WriteShort(_dest, idx);
851 WriteByte(_dest, idx);
852 WriteCoord(_dest, e.origin.x);
853 WriteCoord(_dest, e.origin.y);
854 WriteCoord(_dest, e.origin.z);
858 WriteByte(_dest, SVC_STOPSOUND);
859 WriteShort(_dest, entno * 8 + chan);
862 void stopsound(entity e, float chan)
864 if (!sound_allowed(MSG_BROADCAST, e))
867 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
868 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
871 void play2(entity e, string filename)
873 //stuffcmd(e, strcat("play2 ", filename, "\n"));
875 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTEN_NONE);
878 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
880 float spamsound(entity e, float chan, string samp, float vol, float _atten)
882 if (!sound_allowed(MSG_BROADCAST, e))
885 if (time > e.spamtime)
888 _sound(e, chan, samp, vol, _atten);
894 void play2team(float t, string filename)
898 if (autocvar_bot_sound_monopoly)
901 FOR_EACH_REALPLAYER(head)
904 play2(head, filename);
908 void play2all(string samp)
910 if (autocvar_bot_sound_monopoly)
913 _sound(world, CH_INFO, samp, VOL_BASE, ATTEN_NONE);
916 void PrecachePlayerSounds(string f);
917 void precache_playermodel(string m)
919 float globhandle, i, n;
922 if(substring(m, -9,5) == "_lod1")
924 if(substring(m, -9,5) == "_lod2")
927 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
930 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
934 globhandle = search_begin(strcat(m, "_*.sounds"), true, false);
937 n = search_getsize(globhandle);
938 for (i = 0; i < n; ++i)
940 //print(search_getfilename(globhandle, i), "\n");
941 f = search_getfilename(globhandle, i);
942 PrecachePlayerSounds(f);
944 search_end(globhandle);
946 void precache_all_playermodels(string pattern)
948 float globhandle, i, n;
951 globhandle = search_begin(pattern, true, false);
954 n = search_getsize(globhandle);
955 for (i = 0; i < n; ++i)
957 //print(search_getfilename(globhandle, i), "\n");
958 f = search_getfilename(globhandle, i);
959 precache_playermodel(f);
961 search_end(globhandle);
966 // gamemode related things
968 // Precache all player models if desired
969 if (autocvar_sv_precacheplayermodels)
971 PrecachePlayerSounds("sound/player/default.sounds");
972 precache_all_playermodels("models/player/*.zym");
973 precache_all_playermodels("models/player/*.dpm");
974 precache_all_playermodels("models/player/*.md3");
975 precache_all_playermodels("models/player/*.psk");
976 precache_all_playermodels("models/player/*.iqm");
979 if (autocvar_sv_defaultcharacter)
982 s = autocvar_sv_defaultplayermodel_red;
984 precache_playermodel(s);
985 s = autocvar_sv_defaultplayermodel_blue;
987 precache_playermodel(s);
988 s = autocvar_sv_defaultplayermodel_yellow;
990 precache_playermodel(s);
991 s = autocvar_sv_defaultplayermodel_pink;
993 precache_playermodel(s);
994 s = autocvar_sv_defaultplayermodel;
996 precache_playermodel(s);
1001 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1002 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1005 // gore and miscellaneous sounds
1006 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1007 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1010 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1012 if (!self.noise && self.music) // quake 3 uses the music field
1013 self.noise = self.music;
1015 // plays music for the level if there is any
1018 precache_sound (self.noise);
1019 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTEN_NONE);
1025 void make_safe_for_remove(entity e)
1027 if (e.initialize_entity)
1029 entity ent, prev = world;
1030 for (ent = initialize_entity_first; ent; )
1032 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1034 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1035 // skip it in linked list
1038 prev.initialize_entity_next = ent.initialize_entity_next;
1039 ent = prev.initialize_entity_next;
1043 initialize_entity_first = ent.initialize_entity_next;
1044 ent = initialize_entity_first;
1050 ent = ent.initialize_entity_next;
1056 void objerror(string s)
1058 make_safe_for_remove(self);
1059 builtin_objerror(s);
1062 .float remove_except_protected_forbidden;
1063 void remove_except_protected(entity e)
1065 if(e.remove_except_protected_forbidden)
1066 error("not allowed to remove this at this point");
1070 void remove_unsafely(entity e)
1072 if(e.classname == "spike")
1073 error("Removing spikes is forbidden (crylink bug), please report");
1077 void remove_safely(entity e)
1079 make_safe_for_remove(e);
1083 void InitializeEntity(entity e, void(void) func, float order)
1087 if (!e || e.initialize_entity)
1089 // make a proxy initializer entity
1091 e = new(initialize_entity);
1095 e.initialize_entity = func;
1096 e.initialize_entity_order = order;
1098 cur = initialize_entity_first;
1102 if (!cur || cur.initialize_entity_order > order)
1104 // insert between prev and cur
1106 prev.initialize_entity_next = e;
1108 initialize_entity_first = e;
1109 e.initialize_entity_next = cur;
1113 cur = cur.initialize_entity_next;
1116 void InitializeEntitiesRun()
1118 entity startoflist = initialize_entity_first;
1119 initialize_entity_first = NULL;
1120 remove = remove_except_protected;
1121 for (entity e = startoflist; e; e = e.initialize_entity_next)
1123 e.remove_except_protected_forbidden = 1;
1125 for (entity e = startoflist; e; )
1127 e.remove_except_protected_forbidden = 0;
1128 e.initialize_entity_order = 0;
1129 entity next = e.initialize_entity_next;
1130 e.initialize_entity_next = NULL;
1131 var void() func = e.initialize_entity;
1132 e.initialize_entity = func_null;
1133 if (e.classname == "initialize_entity")
1135 entity wrappee = e.enemy;
1139 //dprint("Delayed initialization: ", e.classname, "\n");
1142 WITH(entity, self, e, func());
1147 backtrace(strcat("Null function in: ", e.classname, "\n"));
1151 remove = remove_unsafely;
1154 void UncustomizeEntitiesRun()
1156 for (entity e = NULL; (e = findfloat(e, uncustomizeentityforclient_set, 1)); )
1158 WITH(entity, self, e, e.uncustomizeentityforclient());
1161 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1163 e.customizeentityforclient = customizer;
1164 e.uncustomizeentityforclient = uncustomizer;
1165 e.uncustomizeentityforclient_set = !!uncustomizer;
1168 void Net_LinkEntity(entity e, bool docull, float dt, bool(entity, int) sendfunc)
1172 if (e.classname == "")
1173 e.classname = "net_linked";
1175 if (e.model == "" || self.modelindex == 0)
1179 setmodel(e, MDL_Null);
1183 e.SendEntity = sendfunc;
1184 e.SendFlags = 0xFFFFFF;
1187 e.effects |= EF_NODEPTHTEST;
1191 e.nextthink = time + dt;
1192 e.think = SUB_Remove;
1197 .float(entity) isEliminated;
1198 float EliminatedPlayers_SendEntity(entity to, float sendflags)
1202 WriteByte(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS);
1203 WriteByte(MSG_ENTITY, sendflags);
1207 for(i = 1; i <= maxclients; i += 8)
1209 for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e))
1211 if(eliminatedPlayers.isEliminated(e))
1214 WriteByte(MSG_ENTITY, f);
1221 void EliminatedPlayers_Init(float(entity) isEliminated_func)
1223 if(eliminatedPlayers)
1225 backtrace("Can't spawn eliminatedPlayers again!");
1228 Net_LinkEntity(eliminatedPlayers = spawn(), false, 0, EliminatedPlayers_SendEntity);
1229 eliminatedPlayers.isEliminated = isEliminated_func;
1233 void adaptor_think2touch()
1242 void adaptor_think2use()
1254 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1256 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
1257 self.projectiledeathtype |= HITTYPE_SPLASH;
1258 adaptor_think2use();
1261 // deferred dropping
1262 void DropToFloor_Handler()
1264 builtin_droptofloor();
1265 self.dropped_origin = self.origin;
1270 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1275 float trace_hits_box_a0, trace_hits_box_a1;
1277 float trace_hits_box_1d(float end, float thmi, float thma)
1281 // just check if x is in range
1289 // do the trace with respect to x
1290 // 0 -> end has to stay in thmi -> thma
1291 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1292 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1293 if (trace_hits_box_a0 > trace_hits_box_a1)
1299 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1304 // now it is a trace from 0 to end
1306 trace_hits_box_a0 = 0;
1307 trace_hits_box_a1 = 1;
1309 if (!trace_hits_box_1d(end.x, thmi.x, thma.x))
1311 if (!trace_hits_box_1d(end.y, thmi.y, thma.y))
1313 if (!trace_hits_box_1d(end.z, thmi.z, thma.z))
1319 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1321 return trace_hits_box(start, end, thmi - ma, thma - mi);
1324 float SUB_NoImpactCheck()
1326 // zero hitcontents = this is not the real impact, but either the
1327 // mirror-impact of something hitting the projectile instead of the
1328 // projectile hitting the something, or a touchareagrid one. Neither of
1329 // these stop the projectile from moving, so...
1330 if(trace_dphitcontents == 0)
1332 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1333 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));
1336 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1338 if (other == world && self.size != '0 0 0')
1341 tic = self.velocity * sys_frametime;
1342 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1343 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1344 if (trace_fraction >= 1)
1346 LOG_TRACE("Odd... did not hit...?\n");
1348 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1350 LOG_TRACE("Detected and prevented the sky-grapple bug.\n");
1358 #define SUB_OwnerCheck() (other && (other == self.owner))
1360 void W_Crylink_Dequeue(entity e);
1361 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1363 if(SUB_OwnerCheck())
1365 if(SUB_NoImpactCheck())
1367 if(self.classname == "nade")
1368 return false; // no checks here
1369 else if(self.classname == "grapplinghook")
1370 RemoveGrapplingHook(self.realowner);
1371 else if(self.classname == "spike")
1373 W_Crylink_Dequeue(self);
1380 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1381 UpdateCSQCProjectile(self);
1386 void URI_Get_Callback(float id, float status, string data)
1388 if(url_URI_Get_Callback(id, status, data))
1392 else if (id == URI_GET_DISCARD)
1396 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
1399 Curl_URI_Get_Callback(id, status, data);
1401 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1404 OnlineBanList_URI_Get_Callback(id, status, data);
1408 LOG_INFO("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1412 string uid2name(string myuid) {
1414 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
1416 // FIXME remove this later after 0.6 release
1417 // convert old style broken records to correct style
1420 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
1423 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
1424 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
1429 s = "^1Unregistered Player";
1433 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1436 vector start, org, delta, end, enddown, mstart;
1439 m = e.dphitcontentsmask;
1440 e.dphitcontentsmask = goodcontents | badcontents;
1443 delta = world.maxs - world.mins;
1447 for (i = 0; i < attempts; ++i)
1449 start.x = org.x + random() * delta.x;
1450 start.y = org.y + random() * delta.y;
1451 start.z = org.z + random() * delta.z;
1453 // rule 1: start inside world bounds, and outside
1454 // solid, and don't start from somewhere where you can
1455 // fall down to evil
1456 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta.z, MOVE_NORMAL, e);
1457 if (trace_fraction >= 1)
1459 if (trace_startsolid)
1461 if (trace_dphitcontents & badcontents)
1463 if (trace_dphitq3surfaceflags & badsurfaceflags)
1466 // rule 2: if we are too high, lower the point
1467 if (trace_fraction * delta.z > maxaboveground)
1468 start = trace_endpos + '0 0 1' * maxaboveground;
1469 enddown = trace_endpos;
1471 // rule 3: make sure we aren't outside the map. This only works
1472 // for somewhat well formed maps. A good rule of thumb is that
1473 // the map should have a convex outside hull.
1474 // these can be traceLINES as we already verified the starting box
1475 mstart = start + 0.5 * (e.mins + e.maxs);
1476 traceline(mstart, mstart + '1 0 0' * delta.x, MOVE_NORMAL, e);
1477 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1479 traceline(mstart, mstart - '1 0 0' * delta.x, MOVE_NORMAL, e);
1480 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1482 traceline(mstart, mstart + '0 1 0' * delta.y, MOVE_NORMAL, e);
1483 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1485 traceline(mstart, mstart - '0 1 0' * delta.y, MOVE_NORMAL, e);
1486 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1488 traceline(mstart, mstart + '0 0 1' * delta.z, MOVE_NORMAL, e);
1489 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1492 // rule 4: we must "see" some spawnpoint or item
1493 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
1494 if(checkpvs(mstart, sp))
1495 if((traceline(mstart, sp.origin, MOVE_NORMAL, e), trace_fraction) >= 1)
1499 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
1500 if(checkpvs(mstart, sp))
1501 if((traceline(mstart, sp.origin + (sp.mins + sp.maxs) * 0.5, MOVE_NORMAL, e), trace_fraction) >= 1)
1507 // find a random vector to "look at"
1508 end.x = org.x + random() * delta.x;
1509 end.y = org.y + random() * delta.y;
1510 end.z = org.z + random() * delta.z;
1511 end = start + normalize(end - start) * vlen(delta);
1513 // rule 4: start TO end must not be too short
1514 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1515 if (trace_startsolid)
1517 if (trace_fraction < minviewdistance / vlen(delta))
1520 // rule 5: don't want to look at sky
1521 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
1524 // rule 6: we must not end up in trigger_hurt
1525 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
1531 e.dphitcontentsmask = m;
1535 setorigin(e, start);
1536 e.angles = vectoangles(end - start);
1537 LOG_TRACE("Needed ", ftos(i + 1), " attempts\n");
1544 void write_recordmarker(entity pl, float tstart, float dt)
1546 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
1548 // also write a marker into demo files for demotc-race-record-extractor to find
1551 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
1552 " ", ftos(tstart), " ", ftos(dt), "\n"));
1555 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
1568 if(allowcenter) // 2: allow center handedness
1581 if(allowcenter) // 2: allow center handedness
1597 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
1602 if (autocvar_g_shootfromeye)
1606 if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
1607 else { vecs.y = 0; vecs.z -= 2; }
1615 else if (autocvar_g_shootfromcenter)
1620 else if ((s = autocvar_g_shootfromfixedorigin) != "")
1630 else if (autocvar_g_shootfromclient)
1632 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
1637 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
1639 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
1643 void attach_sameorigin(entity e, entity to, string tag)
1645 vector org, t_forward, t_left, t_up, e_forward, e_up;
1648 org = e.origin - gettaginfo(to, gettagindex(to, tag));
1649 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
1650 t_forward = v_forward * tagscale;
1651 t_left = v_right * -tagscale;
1652 t_up = v_up * tagscale;
1654 e.origin_x = org * t_forward;
1655 e.origin_y = org * t_left;
1656 e.origin_z = org * t_up;
1658 // current forward and up directions
1659 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1660 e.angles = AnglesTransform_FromVAngles(e.angles);
1662 e.angles = AnglesTransform_FromAngles(e.angles);
1663 fixedmakevectors(e.angles);
1665 // untransform forward, up!
1666 e_forward.x = v_forward * t_forward;
1667 e_forward.y = v_forward * t_left;
1668 e_forward.z = v_forward * t_up;
1669 e_up.x = v_up * t_forward;
1670 e_up.y = v_up * t_left;
1671 e_up.z = v_up * t_up;
1673 e.angles = fixedvectoangles2(e_forward, e_up);
1674 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1675 e.angles = AnglesTransform_ToVAngles(e.angles);
1677 e.angles = AnglesTransform_ToAngles(e.angles);
1679 setattachment(e, to, tag);
1680 setorigin(e, e.origin);
1683 void detach_sameorigin(entity e)
1686 org = gettaginfo(e, 0);
1687 e.angles = fixedvectoangles2(v_forward, v_up);
1688 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1689 e.angles = AnglesTransform_ToVAngles(e.angles);
1691 e.angles = AnglesTransform_ToAngles(e.angles);
1693 setattachment(e, world, "");
1694 setorigin(e, e.origin);
1697 void follow_sameorigin(entity e, entity to)
1699 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
1700 e.aiment = to; // make the hole follow bmodel
1701 e.punchangle = to.angles; // the original angles of bmodel
1702 e.view_ofs = e.origin - to.origin; // relative origin
1703 e.v_angle = e.angles - to.angles; // relative angles
1706 void unfollow_sameorigin(entity e)
1708 e.movetype = MOVETYPE_NONE;
1711 entity gettaginfo_relative_ent;
1712 vector gettaginfo_relative(entity e, float tag)
1714 if (!gettaginfo_relative_ent)
1716 gettaginfo_relative_ent = spawn();
1717 gettaginfo_relative_ent.effects = EF_NODRAW;
1719 gettaginfo_relative_ent.model = e.model;
1720 gettaginfo_relative_ent.modelindex = e.modelindex;
1721 gettaginfo_relative_ent.frame = e.frame;
1722 return gettaginfo(gettaginfo_relative_ent, tag);
1727 float modeleffect_SendEntity(entity to, int sf)
1730 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
1733 if(self.velocity != '0 0 0')
1735 if(self.angles != '0 0 0')
1737 if(self.avelocity != '0 0 0')
1740 WriteByte(MSG_ENTITY, f);
1741 WriteShort(MSG_ENTITY, self.modelindex);
1742 WriteByte(MSG_ENTITY, self.skin);
1743 WriteByte(MSG_ENTITY, self.frame);
1744 WriteCoord(MSG_ENTITY, self.origin.x);
1745 WriteCoord(MSG_ENTITY, self.origin.y);
1746 WriteCoord(MSG_ENTITY, self.origin.z);
1749 WriteCoord(MSG_ENTITY, self.velocity.x);
1750 WriteCoord(MSG_ENTITY, self.velocity.y);
1751 WriteCoord(MSG_ENTITY, self.velocity.z);
1755 WriteCoord(MSG_ENTITY, self.angles.x);
1756 WriteCoord(MSG_ENTITY, self.angles.y);
1757 WriteCoord(MSG_ENTITY, self.angles.z);
1761 WriteCoord(MSG_ENTITY, self.avelocity.x);
1762 WriteCoord(MSG_ENTITY, self.avelocity.y);
1763 WriteCoord(MSG_ENTITY, self.avelocity.z);
1765 WriteShort(MSG_ENTITY, self.scale * 256.0);
1766 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
1767 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
1768 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
1769 WriteByte(MSG_ENTITY, self.alpha * 255.0);
1774 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)
1779 e.classname = "modeleffect";
1787 e.teleport_time = t1;
1791 e.scale = s0 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1795 e.scale2 = s2 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1798 sz = max(e.scale, e.scale2);
1799 setsize(e, e.mins * sz, e.maxs * sz);
1800 Net_LinkEntity(e, false, 0.1, modeleffect_SendEntity);
1803 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
1805 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
1808 float randombit(float bits)
1810 if(!(bits & (bits-1))) // this ONLY holds for powers of two!
1819 for(f = 1; f <= bits; f *= 2)
1828 r = (r - 1) / (n - 1);
1835 float randombits(float bits, float k, float error_return)
1839 while(k > 0 && bits != r)
1841 r += randombit(bits - r);
1850 void randombit_test(float bits, float iter)
1854 LOG_INFO(ftos(randombit(bits)), "\n");
1859 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
1861 if(halflifedist > 0)
1862 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
1863 else if(halflifedist < 0)
1864 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
1870 .string aiment_classname;
1871 .float aiment_deadflag;
1872 void SetMovetypeFollow(entity ent, entity e)
1874 // FIXME this may not be warpzone aware
1875 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
1876 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.
1877 ent.aiment = e; // make the hole follow bmodel
1878 ent.punchangle = e.angles; // the original angles of bmodel
1879 ent.view_ofs = ent.origin - e.origin; // relative origin
1880 ent.v_angle = ent.angles - e.angles; // relative angles
1881 ent.aiment_classname = strzone(e.classname);
1882 ent.aiment_deadflag = e.deadflag;
1884 void UnsetMovetypeFollow(entity ent)
1886 ent.movetype = MOVETYPE_FLY;
1887 PROJECTILE_MAKETRIGGER(ent);
1890 float LostMovetypeFollow(entity ent)
1893 if(ent.movetype != MOVETYPE_FOLLOW)
1899 if(ent.aiment.classname != ent.aiment_classname)
1901 if(ent.aiment.deadflag != ent.aiment_deadflag)
1907 float isPushable(entity e)
1918 case "droppedweapon":
1919 case "keepawayball":
1920 case "nexball_basketball":
1921 case "nexball_football":
1923 case "bullet": // antilagged bullets can't hit this either
1926 if (e.projectiledeathtype)