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 ReplicateVars(this, s, f);
422 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
423 GetCvars_handleFloat(s, f, cvar_cl_autoscreenshot, "cl_autoscreenshot");
424 GetCvars_handleFloat(s, f, cvar_cl_jetpack_jump, "cl_jetpack_jump");
425 GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
426 GetCvars_handleString(s, f, cvar_cl_physics, "cl_physics");
427 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
428 GetCvars_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
429 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
430 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
431 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
432 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
433 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
434 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
435 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
436 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
437 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
438 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
439 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
440 GetCvars_handleFloat(s, f, cvar_cl_weaponimpulsemode, "cl_weaponimpulsemode");
441 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
442 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
443 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
444 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
445 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
446 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
448 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
449 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
451 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
452 GetCvars_handleFloat(s, f, cvar_cl_allow_uid2name, "cl_allow_uid2name");
453 GetCvars_handleFloat(s, f, cvar_cl_allow_uidtracking, "cl_allow_uidtracking");
454 GetCvars_handleFloat(s, f, cvar_cl_movement_track_canjump, "cl_movement_track_canjump");
455 GetCvars_handleFloat(s, f, cvar_cl_newusekeysupported, "cl_newusekeysupported");
457 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
460 if (s == "cl_weaponpriority")
461 self.switchweapon = w_getbestweapon(self);
462 if (s == "cl_allow_uidtracking")
463 PlayerStats_GameReport_AddPlayer(self);
467 // decolorizes and team colors the player name when needed
468 string playername(entity p)
471 if (teamplay && !intermission_running && IS_PLAYER(p))
473 t = Team_ColorCode(p.team);
474 return strcat(t, strdecolorize(p.netname));
480 float want_weapon(entity weaponinfo, float allguns) // WEAPONTODO: what still needs done?
482 int i = weaponinfo.weapon;
488 if (g_lms || g_ca || allguns)
490 if(weaponinfo.spawnflags & WEP_FLAG_NORMAL)
496 d = (i == WEP_SHOTGUN.m_id);
498 d = 0; // weapon is set a few lines later
500 d = !(!weaponinfo.weaponstart);
502 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
503 d |= (i == WEP_HOOK.m_id);
504 if(!g_cts && (weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED)) // never default mutator blocked guns
507 float t = weaponinfo.weaponstartoverride;
509 //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n"));
514 // 4: is set by default?
523 void readplayerstartcvars()
529 // initialize starting values for players
530 start_weapons = '0 0 0';
531 start_weapons_default = '0 0 0';
532 start_weapons_defaultmask = '0 0 0';
534 start_ammo_shells = 0;
535 start_ammo_nails = 0;
536 start_ammo_rockets = 0;
537 start_ammo_cells = 0;
538 start_ammo_plasma = 0;
539 start_health = cvar("g_balance_health_start");
540 start_armorvalue = cvar("g_balance_armor_start");
543 g_weaponarena_weapons = '0 0 0';
545 s = cvar_string("g_weaponarena");
546 if (s == "0" || s == "")
548 if(g_ca || g_freezetag)
552 if (s == "0" || s == "")
558 // forcibly turn off weaponarena
560 else if (s == "all" || s == "1")
563 g_weaponarena_list = "All Weapons";
564 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
566 e = get_weaponinfo(j);
567 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
568 g_weaponarena_weapons |= WepSet_FromWeapon(j);
571 else if (s == "most")
574 g_weaponarena_list = "Most Weapons";
575 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
577 e = get_weaponinfo(j);
578 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
579 if (e.spawnflags & WEP_FLAG_NORMAL)
580 g_weaponarena_weapons |= WepSet_FromWeapon(j);
583 else if (s == "none")
586 g_weaponarena_list = "No Weapons";
591 t = tokenize_console(s);
592 g_weaponarena_list = "";
593 for (i = 0; i < t; ++i)
596 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
598 e = get_weaponinfo(j);
601 g_weaponarena_weapons |= WepSet_FromWeapon(j);
602 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
608 LOG_INFO("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
611 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
615 g_weaponarena_random = cvar("g_weaponarena_random");
617 g_weaponarena_random = 0;
618 g_weaponarena_random_with_blaster = cvar("g_weaponarena_random_with_blaster");
622 g_weapon_stay = 0; // incompatible
623 start_weapons = g_weaponarena_weapons;
624 start_items |= IT_UNLIMITED_AMMO;
628 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
630 e = get_weaponinfo(i);
631 int w = want_weapon(e, false);
633 start_weapons |= WepSet_FromWeapon(i);
635 start_weapons_default |= WepSet_FromWeapon(i);
637 start_weapons_defaultmask |= WepSet_FromWeapon(i);
641 if(!cvar("g_use_ammunition"))
642 start_items |= IT_UNLIMITED_AMMO;
644 if(start_items & IT_UNLIMITED_WEAPON_AMMO)
646 start_ammo_shells = 999;
647 start_ammo_nails = 999;
648 start_ammo_rockets = 999;
649 start_ammo_cells = 999;
650 start_ammo_plasma = 999;
651 start_ammo_fuel = 999;
655 start_ammo_shells = cvar("g_start_ammo_shells");
656 start_ammo_nails = cvar("g_start_ammo_nails");
657 start_ammo_rockets = cvar("g_start_ammo_rockets");
658 start_ammo_cells = cvar("g_start_ammo_cells");
659 start_ammo_plasma = cvar("g_start_ammo_plasma");
660 start_ammo_fuel = cvar("g_start_ammo_fuel");
665 warmup_start_ammo_shells = start_ammo_shells;
666 warmup_start_ammo_nails = start_ammo_nails;
667 warmup_start_ammo_rockets = start_ammo_rockets;
668 warmup_start_ammo_cells = start_ammo_cells;
669 warmup_start_ammo_plasma = start_ammo_plasma;
670 warmup_start_ammo_fuel = start_ammo_fuel;
671 warmup_start_health = start_health;
672 warmup_start_armorvalue = start_armorvalue;
673 warmup_start_weapons = start_weapons;
674 warmup_start_weapons_default = start_weapons_default;
675 warmup_start_weapons_defaultmask = start_weapons_defaultmask;
677 if (!g_weaponarena && !g_ca && !g_freezetag)
679 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
680 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
681 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
682 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
683 warmup_start_ammo_plasma = cvar("g_warmup_start_ammo_plasma");
684 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
685 warmup_start_health = cvar("g_warmup_start_health");
686 warmup_start_armorvalue = cvar("g_warmup_start_armor");
687 warmup_start_weapons = '0 0 0';
688 warmup_start_weapons_default = '0 0 0';
689 warmup_start_weapons_defaultmask = '0 0 0';
690 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
692 e = get_weaponinfo(i);
693 int w = want_weapon(e, g_warmup_allguns);
695 warmup_start_weapons |= WepSet_FromWeapon(i);
697 warmup_start_weapons_default |= WepSet_FromWeapon(i);
699 warmup_start_weapons_defaultmask |= WepSet_FromWeapon(i);
705 start_items |= ITEM_Jetpack.m_itemid;
707 MUTATOR_CALLHOOK(SetStartItems);
709 if (start_items & ITEM_Jetpack.m_itemid)
711 start_items |= ITEM_JetpackRegen.m_itemid;
712 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
713 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
716 WepSet precache_weapons = start_weapons;
717 if (g_warmup_allguns != 1)
718 precache_weapons |= warmup_start_weapons;
719 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
721 e = get_weaponinfo(i);
722 if(precache_weapons & WepSet_FromWeapon(i)) {
723 Weapon w = get_weaponinfo(i);
728 start_ammo_shells = max(0, start_ammo_shells);
729 start_ammo_nails = max(0, start_ammo_nails);
730 start_ammo_rockets = max(0, start_ammo_rockets);
731 start_ammo_cells = max(0, start_ammo_cells);
732 start_ammo_plasma = max(0, start_ammo_plasma);
733 start_ammo_fuel = max(0, start_ammo_fuel);
735 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
736 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
737 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
738 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
739 warmup_start_ammo_plasma = max(0, warmup_start_ammo_plasma);
740 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
743 float sound_allowed(float destin, entity e)
745 // sounds from world may always pass
748 if (e.classname == "body")
750 else if (e.realowner && e.realowner != e)
752 else if (e.owner && e.owner != e)
757 // sounds to self may always pass
758 if (destin == MSG_ONE)
761 // sounds by players can be removed
762 if (autocvar_bot_sound_monopoly)
763 if (IS_REAL_CLIENT(e))
765 // anything else may pass
769 void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float attenu)
773 if (!sound_allowed(_dest, e))
776 entno = num_for_edict(e);
777 idx = precache_sound_index(samp);
782 attenu = floor(attenu * 64);
783 vol = floor(vol * 255);
786 sflags |= SND_VOLUME;
788 sflags |= SND_ATTENUATION;
789 if (entno >= 8192 || chan < 0 || chan > 7)
790 sflags |= SND_LARGEENTITY;
792 sflags |= SND_LARGESOUND;
794 WriteByte(_dest, SVC_SOUND);
795 WriteByte(_dest, sflags);
796 if (sflags & SND_VOLUME)
797 WriteByte(_dest, vol);
798 if (sflags & SND_ATTENUATION)
799 WriteByte(_dest, attenu);
800 if (sflags & SND_LARGEENTITY)
802 WriteShort(_dest, entno);
803 WriteByte(_dest, chan);
807 WriteShort(_dest, entno * 8 + chan);
809 if (sflags & SND_LARGESOUND)
810 WriteShort(_dest, idx);
812 WriteByte(_dest, idx);
814 WriteCoord(_dest, o.x);
815 WriteCoord(_dest, o.y);
816 WriteCoord(_dest, o.z);
818 void soundto(float _dest, entity e, float chan, string samp, float vol, float _atten)
822 if (!sound_allowed(_dest, e))
825 o = e.origin + 0.5 * (e.mins + e.maxs);
826 soundtoat(_dest, e, o, chan, samp, vol, _atten);
828 void soundat(entity e, vector o, float chan, string samp, float vol, float _atten)
830 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, _atten);
832 void stopsoundto(float _dest, entity e, float chan)
836 if (!sound_allowed(_dest, e))
839 entno = num_for_edict(e);
841 if (entno >= 8192 || chan < 0 || chan > 7)
844 idx = precache_sound_index(SND(Null));
845 sflags = SND_LARGEENTITY;
847 sflags |= SND_LARGESOUND;
848 WriteByte(_dest, SVC_SOUND);
849 WriteByte(_dest, sflags);
850 WriteShort(_dest, entno);
851 WriteByte(_dest, chan);
852 if (sflags & SND_LARGESOUND)
853 WriteShort(_dest, idx);
855 WriteByte(_dest, idx);
856 WriteCoord(_dest, e.origin.x);
857 WriteCoord(_dest, e.origin.y);
858 WriteCoord(_dest, e.origin.z);
862 WriteByte(_dest, SVC_STOPSOUND);
863 WriteShort(_dest, entno * 8 + chan);
866 void stopsound(entity e, float chan)
868 if (!sound_allowed(MSG_BROADCAST, e))
871 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
872 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
875 void play2(entity e, string filename)
877 //stuffcmd(e, strcat("play2 ", filename, "\n"));
879 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTEN_NONE);
882 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
884 float spamsound(entity e, float chan, string samp, float vol, float _atten)
886 if (!sound_allowed(MSG_BROADCAST, e))
889 if (time > e.spamtime)
892 _sound(e, chan, samp, vol, _atten);
898 void play2team(float t, string filename)
902 if (autocvar_bot_sound_monopoly)
905 FOR_EACH_REALPLAYER(head)
908 play2(head, filename);
912 void play2all(string samp)
914 if (autocvar_bot_sound_monopoly)
917 _sound(world, CH_INFO, samp, VOL_BASE, ATTEN_NONE);
920 void PrecachePlayerSounds(string f);
921 void precache_playermodel(string m)
923 float globhandle, i, n;
926 if(substring(m, -9,5) == "_lod1")
928 if(substring(m, -9,5) == "_lod2")
931 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
934 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
938 globhandle = search_begin(strcat(m, "_*.sounds"), true, false);
941 n = search_getsize(globhandle);
942 for (i = 0; i < n; ++i)
944 //print(search_getfilename(globhandle, i), "\n");
945 f = search_getfilename(globhandle, i);
946 PrecachePlayerSounds(f);
948 search_end(globhandle);
950 void precache_all_playermodels(string pattern)
952 float globhandle, i, n;
955 globhandle = search_begin(pattern, true, false);
958 n = search_getsize(globhandle);
959 for (i = 0; i < n; ++i)
961 //print(search_getfilename(globhandle, i), "\n");
962 f = search_getfilename(globhandle, i);
963 precache_playermodel(f);
965 search_end(globhandle);
970 // gamemode related things
972 // Precache all player models if desired
973 if (autocvar_sv_precacheplayermodels)
975 PrecachePlayerSounds("sound/player/default.sounds");
976 precache_all_playermodels("models/player/*.zym");
977 precache_all_playermodels("models/player/*.dpm");
978 precache_all_playermodels("models/player/*.md3");
979 precache_all_playermodels("models/player/*.psk");
980 precache_all_playermodels("models/player/*.iqm");
983 if (autocvar_sv_defaultcharacter)
986 s = autocvar_sv_defaultplayermodel_red;
988 precache_playermodel(s);
989 s = autocvar_sv_defaultplayermodel_blue;
991 precache_playermodel(s);
992 s = autocvar_sv_defaultplayermodel_yellow;
994 precache_playermodel(s);
995 s = autocvar_sv_defaultplayermodel_pink;
997 precache_playermodel(s);
998 s = autocvar_sv_defaultplayermodel;
1000 precache_playermodel(s);
1005 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1006 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1009 // gore and miscellaneous sounds
1010 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1011 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1014 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1016 if (!self.noise && self.music) // quake 3 uses the music field
1017 self.noise = self.music;
1019 // plays music for the level if there is any
1022 precache_sound (self.noise);
1023 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTEN_NONE);
1029 void make_safe_for_remove(entity e)
1031 if (e.initialize_entity)
1033 entity ent, prev = world;
1034 for (ent = initialize_entity_first; ent; )
1036 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1038 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1039 // skip it in linked list
1042 prev.initialize_entity_next = ent.initialize_entity_next;
1043 ent = prev.initialize_entity_next;
1047 initialize_entity_first = ent.initialize_entity_next;
1048 ent = initialize_entity_first;
1054 ent = ent.initialize_entity_next;
1060 void objerror(string s)
1062 make_safe_for_remove(self);
1063 builtin_objerror(s);
1066 .float remove_except_protected_forbidden;
1067 void remove_except_protected(entity e)
1069 if(e.remove_except_protected_forbidden)
1070 error("not allowed to remove this at this point");
1074 void remove_unsafely(entity e)
1076 if(e.classname == "spike")
1077 error("Removing spikes is forbidden (crylink bug), please report");
1081 void remove_safely(entity e)
1083 make_safe_for_remove(e);
1087 void InitializeEntity(entity e, void(void) func, float order)
1091 if (!e || e.initialize_entity)
1093 // make a proxy initializer entity
1095 e = new(initialize_entity);
1099 e.initialize_entity = func;
1100 e.initialize_entity_order = order;
1102 cur = initialize_entity_first;
1106 if (!cur || cur.initialize_entity_order > order)
1108 // insert between prev and cur
1110 prev.initialize_entity_next = e;
1112 initialize_entity_first = e;
1113 e.initialize_entity_next = cur;
1117 cur = cur.initialize_entity_next;
1120 void InitializeEntitiesRun()
1122 entity startoflist = initialize_entity_first;
1123 initialize_entity_first = NULL;
1124 remove = remove_except_protected;
1125 for (entity e = startoflist; e; e = e.initialize_entity_next)
1127 e.remove_except_protected_forbidden = 1;
1129 for (entity e = startoflist; e; )
1131 e.remove_except_protected_forbidden = 0;
1132 e.initialize_entity_order = 0;
1133 entity next = e.initialize_entity_next;
1134 e.initialize_entity_next = NULL;
1135 var void() func = e.initialize_entity;
1136 e.initialize_entity = func_null;
1137 if (e.classname == "initialize_entity")
1139 entity wrappee = e.enemy;
1143 //dprint("Delayed initialization: ", e.classname, "\n");
1146 WITH(entity, self, e, func());
1151 backtrace(strcat("Null function in: ", e.classname, "\n"));
1155 remove = remove_unsafely;
1158 void UncustomizeEntitiesRun()
1160 for (entity e = NULL; (e = findfloat(e, uncustomizeentityforclient_set, 1)); )
1162 WITH(entity, self, e, e.uncustomizeentityforclient());
1165 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1167 e.customizeentityforclient = customizer;
1168 e.uncustomizeentityforclient = uncustomizer;
1169 e.uncustomizeentityforclient_set = !!uncustomizer;
1172 void Net_LinkEntity(entity e, bool docull, float dt, bool(entity, int) sendfunc)
1176 if (e.classname == "")
1177 e.classname = "net_linked";
1179 if (e.model == "" || self.modelindex == 0)
1183 setmodel(e, MDL_Null);
1187 e.SendEntity = sendfunc;
1188 e.SendFlags = 0xFFFFFF;
1191 e.effects |= EF_NODEPTHTEST;
1195 e.nextthink = time + dt;
1196 e.think = SUB_Remove;
1201 .float(entity) isEliminated;
1202 float EliminatedPlayers_SendEntity(entity to, float sendflags)
1206 WriteByte(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS);
1207 WriteByte(MSG_ENTITY, sendflags);
1211 for(i = 1; i <= maxclients; i += 8)
1213 for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e))
1215 if(eliminatedPlayers.isEliminated(e))
1218 WriteByte(MSG_ENTITY, f);
1225 void EliminatedPlayers_Init(float(entity) isEliminated_func)
1227 if(eliminatedPlayers)
1229 backtrace("Can't spawn eliminatedPlayers again!");
1232 Net_LinkEntity(eliminatedPlayers = spawn(), false, 0, EliminatedPlayers_SendEntity);
1233 eliminatedPlayers.isEliminated = isEliminated_func;
1237 void adaptor_think2touch()
1246 void adaptor_think2use()
1258 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1260 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
1261 self.projectiledeathtype |= HITTYPE_SPLASH;
1262 adaptor_think2use();
1265 // deferred dropping
1266 void DropToFloor_Handler()
1268 builtin_droptofloor();
1269 self.dropped_origin = self.origin;
1274 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1279 float trace_hits_box_a0, trace_hits_box_a1;
1281 float trace_hits_box_1d(float end, float thmi, float thma)
1285 // just check if x is in range
1293 // do the trace with respect to x
1294 // 0 -> end has to stay in thmi -> thma
1295 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1296 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1297 if (trace_hits_box_a0 > trace_hits_box_a1)
1303 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1308 // now it is a trace from 0 to end
1310 trace_hits_box_a0 = 0;
1311 trace_hits_box_a1 = 1;
1313 if (!trace_hits_box_1d(end.x, thmi.x, thma.x))
1315 if (!trace_hits_box_1d(end.y, thmi.y, thma.y))
1317 if (!trace_hits_box_1d(end.z, thmi.z, thma.z))
1323 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1325 return trace_hits_box(start, end, thmi - ma, thma - mi);
1328 float SUB_NoImpactCheck()
1330 // zero hitcontents = this is not the real impact, but either the
1331 // mirror-impact of something hitting the projectile instead of the
1332 // projectile hitting the something, or a touchareagrid one. Neither of
1333 // these stop the projectile from moving, so...
1334 if(trace_dphitcontents == 0)
1336 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1337 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));
1340 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1342 if (other == world && self.size != '0 0 0')
1345 tic = self.velocity * sys_frametime;
1346 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1347 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1348 if (trace_fraction >= 1)
1350 LOG_TRACE("Odd... did not hit...?\n");
1352 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1354 LOG_TRACE("Detected and prevented the sky-grapple bug.\n");
1362 #define SUB_OwnerCheck() (other && (other == self.owner))
1364 void W_Crylink_Dequeue(entity e);
1365 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1367 if(SUB_OwnerCheck())
1369 if(SUB_NoImpactCheck())
1371 if(self.classname == "nade")
1372 return false; // no checks here
1373 else if(self.classname == "grapplinghook")
1374 RemoveGrapplingHook(self.realowner);
1375 else if(self.classname == "spike")
1377 W_Crylink_Dequeue(self);
1384 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1385 UpdateCSQCProjectile(self);
1390 void URI_Get_Callback(float id, float status, string data)
1392 if(url_URI_Get_Callback(id, status, data))
1396 else if (id == URI_GET_DISCARD)
1400 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
1403 Curl_URI_Get_Callback(id, status, data);
1405 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1408 OnlineBanList_URI_Get_Callback(id, status, data);
1412 LOG_INFO("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1416 string uid2name(string myuid) {
1418 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
1420 // FIXME remove this later after 0.6 release
1421 // convert old style broken records to correct style
1424 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
1427 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
1428 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
1433 s = "^1Unregistered Player";
1437 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1440 vector start, org, delta, end, enddown, mstart;
1443 m = e.dphitcontentsmask;
1444 e.dphitcontentsmask = goodcontents | badcontents;
1447 delta = world.maxs - world.mins;
1451 for (i = 0; i < attempts; ++i)
1453 start.x = org.x + random() * delta.x;
1454 start.y = org.y + random() * delta.y;
1455 start.z = org.z + random() * delta.z;
1457 // rule 1: start inside world bounds, and outside
1458 // solid, and don't start from somewhere where you can
1459 // fall down to evil
1460 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta.z, MOVE_NORMAL, e);
1461 if (trace_fraction >= 1)
1463 if (trace_startsolid)
1465 if (trace_dphitcontents & badcontents)
1467 if (trace_dphitq3surfaceflags & badsurfaceflags)
1470 // rule 2: if we are too high, lower the point
1471 if (trace_fraction * delta.z > maxaboveground)
1472 start = trace_endpos + '0 0 1' * maxaboveground;
1473 enddown = trace_endpos;
1475 // rule 3: make sure we aren't outside the map. This only works
1476 // for somewhat well formed maps. A good rule of thumb is that
1477 // the map should have a convex outside hull.
1478 // these can be traceLINES as we already verified the starting box
1479 mstart = start + 0.5 * (e.mins + e.maxs);
1480 traceline(mstart, mstart + '1 0 0' * delta.x, MOVE_NORMAL, e);
1481 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1483 traceline(mstart, mstart - '1 0 0' * delta.x, MOVE_NORMAL, e);
1484 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1486 traceline(mstart, mstart + '0 1 0' * delta.y, MOVE_NORMAL, e);
1487 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1489 traceline(mstart, mstart - '0 1 0' * delta.y, MOVE_NORMAL, e);
1490 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1492 traceline(mstart, mstart + '0 0 1' * delta.z, MOVE_NORMAL, e);
1493 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1496 // rule 4: we must "see" some spawnpoint or item
1497 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
1498 if(checkpvs(mstart, sp))
1499 if((traceline(mstart, sp.origin, MOVE_NORMAL, e), trace_fraction) >= 1)
1503 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
1504 if(checkpvs(mstart, sp))
1505 if((traceline(mstart, sp.origin + (sp.mins + sp.maxs) * 0.5, MOVE_NORMAL, e), trace_fraction) >= 1)
1511 // find a random vector to "look at"
1512 end.x = org.x + random() * delta.x;
1513 end.y = org.y + random() * delta.y;
1514 end.z = org.z + random() * delta.z;
1515 end = start + normalize(end - start) * vlen(delta);
1517 // rule 4: start TO end must not be too short
1518 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1519 if (trace_startsolid)
1521 if (trace_fraction < minviewdistance / vlen(delta))
1524 // rule 5: don't want to look at sky
1525 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
1528 // rule 6: we must not end up in trigger_hurt
1529 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
1535 e.dphitcontentsmask = m;
1539 setorigin(e, start);
1540 e.angles = vectoangles(end - start);
1541 LOG_TRACE("Needed ", ftos(i + 1), " attempts\n");
1548 void write_recordmarker(entity pl, float tstart, float dt)
1550 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
1552 // also write a marker into demo files for demotc-race-record-extractor to find
1555 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
1556 " ", ftos(tstart), " ", ftos(dt), "\n"));
1559 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
1572 if(allowcenter) // 2: allow center handedness
1585 if(allowcenter) // 2: allow center handedness
1601 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
1606 if (autocvar_g_shootfromeye)
1610 if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
1611 else { vecs.y = 0; vecs.z -= 2; }
1619 else if (autocvar_g_shootfromcenter)
1624 else if ((s = autocvar_g_shootfromfixedorigin) != "")
1634 else if (autocvar_g_shootfromclient)
1636 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
1641 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
1643 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
1647 void attach_sameorigin(entity e, entity to, string tag)
1649 vector org, t_forward, t_left, t_up, e_forward, e_up;
1652 org = e.origin - gettaginfo(to, gettagindex(to, tag));
1653 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
1654 t_forward = v_forward * tagscale;
1655 t_left = v_right * -tagscale;
1656 t_up = v_up * tagscale;
1658 e.origin_x = org * t_forward;
1659 e.origin_y = org * t_left;
1660 e.origin_z = org * t_up;
1662 // current forward and up directions
1663 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1664 e.angles = AnglesTransform_FromVAngles(e.angles);
1666 e.angles = AnglesTransform_FromAngles(e.angles);
1667 fixedmakevectors(e.angles);
1669 // untransform forward, up!
1670 e_forward.x = v_forward * t_forward;
1671 e_forward.y = v_forward * t_left;
1672 e_forward.z = v_forward * t_up;
1673 e_up.x = v_up * t_forward;
1674 e_up.y = v_up * t_left;
1675 e_up.z = v_up * t_up;
1677 e.angles = fixedvectoangles2(e_forward, e_up);
1678 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1679 e.angles = AnglesTransform_ToVAngles(e.angles);
1681 e.angles = AnglesTransform_ToAngles(e.angles);
1683 setattachment(e, to, tag);
1684 setorigin(e, e.origin);
1687 void detach_sameorigin(entity e)
1690 org = gettaginfo(e, 0);
1691 e.angles = fixedvectoangles2(v_forward, v_up);
1692 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1693 e.angles = AnglesTransform_ToVAngles(e.angles);
1695 e.angles = AnglesTransform_ToAngles(e.angles);
1697 setattachment(e, world, "");
1698 setorigin(e, e.origin);
1701 void follow_sameorigin(entity e, entity to)
1703 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
1704 e.aiment = to; // make the hole follow bmodel
1705 e.punchangle = to.angles; // the original angles of bmodel
1706 e.view_ofs = e.origin - to.origin; // relative origin
1707 e.v_angle = e.angles - to.angles; // relative angles
1710 void unfollow_sameorigin(entity e)
1712 e.movetype = MOVETYPE_NONE;
1715 entity gettaginfo_relative_ent;
1716 vector gettaginfo_relative(entity e, float tag)
1718 if (!gettaginfo_relative_ent)
1720 gettaginfo_relative_ent = spawn();
1721 gettaginfo_relative_ent.effects = EF_NODRAW;
1723 gettaginfo_relative_ent.model = e.model;
1724 gettaginfo_relative_ent.modelindex = e.modelindex;
1725 gettaginfo_relative_ent.frame = e.frame;
1726 return gettaginfo(gettaginfo_relative_ent, tag);
1731 float modeleffect_SendEntity(entity to, int sf)
1734 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
1737 if(self.velocity != '0 0 0')
1739 if(self.angles != '0 0 0')
1741 if(self.avelocity != '0 0 0')
1744 WriteByte(MSG_ENTITY, f);
1745 WriteShort(MSG_ENTITY, self.modelindex);
1746 WriteByte(MSG_ENTITY, self.skin);
1747 WriteByte(MSG_ENTITY, self.frame);
1748 WriteCoord(MSG_ENTITY, self.origin.x);
1749 WriteCoord(MSG_ENTITY, self.origin.y);
1750 WriteCoord(MSG_ENTITY, self.origin.z);
1753 WriteCoord(MSG_ENTITY, self.velocity.x);
1754 WriteCoord(MSG_ENTITY, self.velocity.y);
1755 WriteCoord(MSG_ENTITY, self.velocity.z);
1759 WriteCoord(MSG_ENTITY, self.angles.x);
1760 WriteCoord(MSG_ENTITY, self.angles.y);
1761 WriteCoord(MSG_ENTITY, self.angles.z);
1765 WriteCoord(MSG_ENTITY, self.avelocity.x);
1766 WriteCoord(MSG_ENTITY, self.avelocity.y);
1767 WriteCoord(MSG_ENTITY, self.avelocity.z);
1769 WriteShort(MSG_ENTITY, self.scale * 256.0);
1770 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
1771 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
1772 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
1773 WriteByte(MSG_ENTITY, self.alpha * 255.0);
1778 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)
1783 e.classname = "modeleffect";
1791 e.teleport_time = t1;
1795 e.scale = s0 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1799 e.scale2 = s2 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1802 sz = max(e.scale, e.scale2);
1803 setsize(e, e.mins * sz, e.maxs * sz);
1804 Net_LinkEntity(e, false, 0.1, modeleffect_SendEntity);
1807 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
1809 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
1812 float randombit(float bits)
1814 if(!(bits & (bits-1))) // this ONLY holds for powers of two!
1823 for(f = 1; f <= bits; f *= 2)
1832 r = (r - 1) / (n - 1);
1839 float randombits(float bits, float k, float error_return)
1843 while(k > 0 && bits != r)
1845 r += randombit(bits - r);
1854 void randombit_test(float bits, float iter)
1858 LOG_INFO(ftos(randombit(bits)), "\n");
1863 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
1865 if(halflifedist > 0)
1866 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
1867 else if(halflifedist < 0)
1868 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
1874 .string aiment_classname;
1875 .float aiment_deadflag;
1876 void SetMovetypeFollow(entity ent, entity e)
1878 // FIXME this may not be warpzone aware
1879 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
1880 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.
1881 ent.aiment = e; // make the hole follow bmodel
1882 ent.punchangle = e.angles; // the original angles of bmodel
1883 ent.view_ofs = ent.origin - e.origin; // relative origin
1884 ent.v_angle = ent.angles - e.angles; // relative angles
1885 ent.aiment_classname = strzone(e.classname);
1886 ent.aiment_deadflag = e.deadflag;
1888 void UnsetMovetypeFollow(entity ent)
1890 ent.movetype = MOVETYPE_FLY;
1891 PROJECTILE_MAKETRIGGER(ent);
1894 float LostMovetypeFollow(entity ent)
1897 if(ent.movetype != MOVETYPE_FOLLOW)
1903 if(ent.aiment.classname != ent.aiment_classname)
1905 if(ent.aiment.deadflag != ent.aiment_deadflag)
1911 float isPushable(entity e)
1922 case "droppedweapon":
1923 case "keepawayball":
1924 case "nexball_basketball":
1925 case "nexball_football":
1927 case "bullet": // antilagged bullets can't hit this either
1930 if (e.projectiledeathtype)