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 _WEP_ACTION(i, WR_INIT);
726 start_ammo_shells = max(0, start_ammo_shells);
727 start_ammo_nails = max(0, start_ammo_nails);
728 start_ammo_rockets = max(0, start_ammo_rockets);
729 start_ammo_cells = max(0, start_ammo_cells);
730 start_ammo_plasma = max(0, start_ammo_plasma);
731 start_ammo_fuel = max(0, start_ammo_fuel);
733 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
734 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
735 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
736 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
737 warmup_start_ammo_plasma = max(0, warmup_start_ammo_plasma);
738 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
741 float sound_allowed(float destin, entity e)
743 // sounds from world may always pass
746 if (e.classname == "body")
748 else if (e.realowner && e.realowner != e)
750 else if (e.owner && e.owner != e)
755 // sounds to self may always pass
756 if (destin == MSG_ONE)
759 // sounds by players can be removed
760 if (autocvar_bot_sound_monopoly)
761 if (IS_REAL_CLIENT(e))
763 // anything else may pass
767 void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float attenu)
771 if (!sound_allowed(_dest, e))
774 entno = num_for_edict(e);
775 idx = precache_sound_index(samp);
780 attenu = floor(attenu * 64);
781 vol = floor(vol * 255);
784 sflags |= SND_VOLUME;
786 sflags |= SND_ATTENUATION;
787 if (entno >= 8192 || chan < 0 || chan > 7)
788 sflags |= SND_LARGEENTITY;
790 sflags |= SND_LARGESOUND;
792 WriteByte(_dest, SVC_SOUND);
793 WriteByte(_dest, sflags);
794 if (sflags & SND_VOLUME)
795 WriteByte(_dest, vol);
796 if (sflags & SND_ATTENUATION)
797 WriteByte(_dest, attenu);
798 if (sflags & SND_LARGEENTITY)
800 WriteShort(_dest, entno);
801 WriteByte(_dest, chan);
805 WriteShort(_dest, entno * 8 + chan);
807 if (sflags & SND_LARGESOUND)
808 WriteShort(_dest, idx);
810 WriteByte(_dest, idx);
812 WriteCoord(_dest, o.x);
813 WriteCoord(_dest, o.y);
814 WriteCoord(_dest, o.z);
816 void soundto(float _dest, entity e, float chan, string samp, float vol, float _atten)
820 if (!sound_allowed(_dest, e))
823 o = e.origin + 0.5 * (e.mins + e.maxs);
824 soundtoat(_dest, e, o, chan, samp, vol, _atten);
826 void soundat(entity e, vector o, float chan, string samp, float vol, float _atten)
828 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, _atten);
830 void stopsoundto(float _dest, entity e, float chan)
834 if (!sound_allowed(_dest, e))
837 entno = num_for_edict(e);
839 if (entno >= 8192 || chan < 0 || chan > 7)
842 idx = precache_sound_index(SND(Null));
843 sflags = SND_LARGEENTITY;
845 sflags |= SND_LARGESOUND;
846 WriteByte(_dest, SVC_SOUND);
847 WriteByte(_dest, sflags);
848 WriteShort(_dest, entno);
849 WriteByte(_dest, chan);
850 if (sflags & SND_LARGESOUND)
851 WriteShort(_dest, idx);
853 WriteByte(_dest, idx);
854 WriteCoord(_dest, e.origin.x);
855 WriteCoord(_dest, e.origin.y);
856 WriteCoord(_dest, e.origin.z);
860 WriteByte(_dest, SVC_STOPSOUND);
861 WriteShort(_dest, entno * 8 + chan);
864 void stopsound(entity e, float chan)
866 if (!sound_allowed(MSG_BROADCAST, e))
869 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
870 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
873 void play2(entity e, string filename)
875 //stuffcmd(e, strcat("play2 ", filename, "\n"));
877 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTEN_NONE);
880 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
882 float spamsound(entity e, float chan, string samp, float vol, float _atten)
884 if (!sound_allowed(MSG_BROADCAST, e))
887 if (time > e.spamtime)
890 _sound(e, chan, samp, vol, _atten);
896 void play2team(float t, string filename)
900 if (autocvar_bot_sound_monopoly)
903 FOR_EACH_REALPLAYER(head)
906 play2(head, filename);
910 void play2all(string samp)
912 if (autocvar_bot_sound_monopoly)
915 _sound(world, CH_INFO, samp, VOL_BASE, ATTEN_NONE);
918 void PrecachePlayerSounds(string f);
919 void precache_playermodel(string m)
921 float globhandle, i, n;
924 if(substring(m, -9,5) == "_lod1")
926 if(substring(m, -9,5) == "_lod2")
929 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
932 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
936 globhandle = search_begin(strcat(m, "_*.sounds"), true, false);
939 n = search_getsize(globhandle);
940 for (i = 0; i < n; ++i)
942 //print(search_getfilename(globhandle, i), "\n");
943 f = search_getfilename(globhandle, i);
944 PrecachePlayerSounds(f);
946 search_end(globhandle);
948 void precache_all_playermodels(string pattern)
950 float globhandle, i, n;
953 globhandle = search_begin(pattern, true, false);
956 n = search_getsize(globhandle);
957 for (i = 0; i < n; ++i)
959 //print(search_getfilename(globhandle, i), "\n");
960 f = search_getfilename(globhandle, i);
961 precache_playermodel(f);
963 search_end(globhandle);
968 // gamemode related things
970 // Precache all player models if desired
971 if (autocvar_sv_precacheplayermodels)
973 PrecachePlayerSounds("sound/player/default.sounds");
974 precache_all_playermodels("models/player/*.zym");
975 precache_all_playermodels("models/player/*.dpm");
976 precache_all_playermodels("models/player/*.md3");
977 precache_all_playermodels("models/player/*.psk");
978 precache_all_playermodels("models/player/*.iqm");
981 if (autocvar_sv_defaultcharacter)
984 s = autocvar_sv_defaultplayermodel_red;
986 precache_playermodel(s);
987 s = autocvar_sv_defaultplayermodel_blue;
989 precache_playermodel(s);
990 s = autocvar_sv_defaultplayermodel_yellow;
992 precache_playermodel(s);
993 s = autocvar_sv_defaultplayermodel_pink;
995 precache_playermodel(s);
996 s = autocvar_sv_defaultplayermodel;
998 precache_playermodel(s);
1003 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1004 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1007 // gore and miscellaneous sounds
1008 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1009 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1012 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1014 if (!self.noise && self.music) // quake 3 uses the music field
1015 self.noise = self.music;
1017 // plays music for the level if there is any
1020 precache_sound (self.noise);
1021 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTEN_NONE);
1027 void make_safe_for_remove(entity e)
1029 if (e.initialize_entity)
1031 entity ent, prev = world;
1032 for (ent = initialize_entity_first; ent; )
1034 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1036 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1037 // skip it in linked list
1040 prev.initialize_entity_next = ent.initialize_entity_next;
1041 ent = prev.initialize_entity_next;
1045 initialize_entity_first = ent.initialize_entity_next;
1046 ent = initialize_entity_first;
1052 ent = ent.initialize_entity_next;
1058 void objerror(string s)
1060 make_safe_for_remove(self);
1061 builtin_objerror(s);
1064 .float remove_except_protected_forbidden;
1065 void remove_except_protected(entity e)
1067 if(e.remove_except_protected_forbidden)
1068 error("not allowed to remove this at this point");
1072 void remove_unsafely(entity e)
1074 if(e.classname == "spike")
1075 error("Removing spikes is forbidden (crylink bug), please report");
1079 void remove_safely(entity e)
1081 make_safe_for_remove(e);
1085 void InitializeEntity(entity e, void(void) func, float order)
1089 if (!e || e.initialize_entity)
1091 // make a proxy initializer entity
1093 e = new(initialize_entity);
1097 e.initialize_entity = func;
1098 e.initialize_entity_order = order;
1100 cur = initialize_entity_first;
1104 if (!cur || cur.initialize_entity_order > order)
1106 // insert between prev and cur
1108 prev.initialize_entity_next = e;
1110 initialize_entity_first = e;
1111 e.initialize_entity_next = cur;
1115 cur = cur.initialize_entity_next;
1118 void InitializeEntitiesRun()
1120 entity startoflist = initialize_entity_first;
1121 initialize_entity_first = NULL;
1122 remove = remove_except_protected;
1123 for (entity e = startoflist; e; e = e.initialize_entity_next)
1125 e.remove_except_protected_forbidden = 1;
1127 for (entity e = startoflist; e; )
1129 e.remove_except_protected_forbidden = 0;
1130 e.initialize_entity_order = 0;
1131 entity next = e.initialize_entity_next;
1132 e.initialize_entity_next = NULL;
1133 var void() func = e.initialize_entity;
1134 e.initialize_entity = func_null;
1135 if (e.classname == "initialize_entity")
1137 entity wrappee = e.enemy;
1141 //dprint("Delayed initialization: ", e.classname, "\n");
1144 WITH(entity, self, e, func());
1149 backtrace(strcat("Null function in: ", e.classname, "\n"));
1153 remove = remove_unsafely;
1156 void UncustomizeEntitiesRun()
1158 for (entity e = NULL; (e = findfloat(e, uncustomizeentityforclient_set, 1)); )
1160 WITH(entity, self, e, e.uncustomizeentityforclient());
1163 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1165 e.customizeentityforclient = customizer;
1166 e.uncustomizeentityforclient = uncustomizer;
1167 e.uncustomizeentityforclient_set = !!uncustomizer;
1170 void Net_LinkEntity(entity e, bool docull, float dt, bool(entity, int) sendfunc)
1174 if (e.classname == "")
1175 e.classname = "net_linked";
1177 if (e.model == "" || self.modelindex == 0)
1181 setmodel(e, MDL_Null);
1185 e.SendEntity = sendfunc;
1186 e.SendFlags = 0xFFFFFF;
1189 e.effects |= EF_NODEPTHTEST;
1193 e.nextthink = time + dt;
1194 e.think = SUB_Remove;
1199 .float(entity) isEliminated;
1200 float EliminatedPlayers_SendEntity(entity to, float sendflags)
1204 WriteByte(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS);
1205 WriteByte(MSG_ENTITY, sendflags);
1209 for(i = 1; i <= maxclients; i += 8)
1211 for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e))
1213 if(eliminatedPlayers.isEliminated(e))
1216 WriteByte(MSG_ENTITY, f);
1223 void EliminatedPlayers_Init(float(entity) isEliminated_func)
1225 if(eliminatedPlayers)
1227 backtrace("Can't spawn eliminatedPlayers again!");
1230 Net_LinkEntity(eliminatedPlayers = spawn(), false, 0, EliminatedPlayers_SendEntity);
1231 eliminatedPlayers.isEliminated = isEliminated_func;
1235 void adaptor_think2touch()
1244 void adaptor_think2use()
1256 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1258 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
1259 self.projectiledeathtype |= HITTYPE_SPLASH;
1260 adaptor_think2use();
1263 // deferred dropping
1264 void DropToFloor_Handler()
1266 builtin_droptofloor();
1267 self.dropped_origin = self.origin;
1272 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1277 float trace_hits_box_a0, trace_hits_box_a1;
1279 float trace_hits_box_1d(float end, float thmi, float thma)
1283 // just check if x is in range
1291 // do the trace with respect to x
1292 // 0 -> end has to stay in thmi -> thma
1293 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1294 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1295 if (trace_hits_box_a0 > trace_hits_box_a1)
1301 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1306 // now it is a trace from 0 to end
1308 trace_hits_box_a0 = 0;
1309 trace_hits_box_a1 = 1;
1311 if (!trace_hits_box_1d(end.x, thmi.x, thma.x))
1313 if (!trace_hits_box_1d(end.y, thmi.y, thma.y))
1315 if (!trace_hits_box_1d(end.z, thmi.z, thma.z))
1321 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1323 return trace_hits_box(start, end, thmi - ma, thma - mi);
1326 float SUB_NoImpactCheck()
1328 // zero hitcontents = this is not the real impact, but either the
1329 // mirror-impact of something hitting the projectile instead of the
1330 // projectile hitting the something, or a touchareagrid one. Neither of
1331 // these stop the projectile from moving, so...
1332 if(trace_dphitcontents == 0)
1334 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1335 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));
1338 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1340 if (other == world && self.size != '0 0 0')
1343 tic = self.velocity * sys_frametime;
1344 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1345 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1346 if (trace_fraction >= 1)
1348 LOG_TRACE("Odd... did not hit...?\n");
1350 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1352 LOG_TRACE("Detected and prevented the sky-grapple bug.\n");
1360 #define SUB_OwnerCheck() (other && (other == self.owner))
1362 void W_Crylink_Dequeue(entity e);
1363 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1365 if(SUB_OwnerCheck())
1367 if(SUB_NoImpactCheck())
1369 if(self.classname == "nade")
1370 return false; // no checks here
1371 else if(self.classname == "grapplinghook")
1372 RemoveGrapplingHook(self.realowner);
1373 else if(self.classname == "spike")
1375 W_Crylink_Dequeue(self);
1382 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1383 UpdateCSQCProjectile(self);
1388 void URI_Get_Callback(float id, float status, string data)
1390 if(url_URI_Get_Callback(id, status, data))
1394 else if (id == URI_GET_DISCARD)
1398 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
1401 Curl_URI_Get_Callback(id, status, data);
1403 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1406 OnlineBanList_URI_Get_Callback(id, status, data);
1410 LOG_INFO("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1414 string uid2name(string myuid) {
1416 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
1418 // FIXME remove this later after 0.6 release
1419 // convert old style broken records to correct style
1422 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
1425 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
1426 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
1431 s = "^1Unregistered Player";
1435 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1438 vector start, org, delta, end, enddown, mstart;
1441 m = e.dphitcontentsmask;
1442 e.dphitcontentsmask = goodcontents | badcontents;
1445 delta = world.maxs - world.mins;
1449 for (i = 0; i < attempts; ++i)
1451 start.x = org.x + random() * delta.x;
1452 start.y = org.y + random() * delta.y;
1453 start.z = org.z + random() * delta.z;
1455 // rule 1: start inside world bounds, and outside
1456 // solid, and don't start from somewhere where you can
1457 // fall down to evil
1458 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta.z, MOVE_NORMAL, e);
1459 if (trace_fraction >= 1)
1461 if (trace_startsolid)
1463 if (trace_dphitcontents & badcontents)
1465 if (trace_dphitq3surfaceflags & badsurfaceflags)
1468 // rule 2: if we are too high, lower the point
1469 if (trace_fraction * delta.z > maxaboveground)
1470 start = trace_endpos + '0 0 1' * maxaboveground;
1471 enddown = trace_endpos;
1473 // rule 3: make sure we aren't outside the map. This only works
1474 // for somewhat well formed maps. A good rule of thumb is that
1475 // the map should have a convex outside hull.
1476 // these can be traceLINES as we already verified the starting box
1477 mstart = start + 0.5 * (e.mins + e.maxs);
1478 traceline(mstart, mstart + '1 0 0' * delta.x, MOVE_NORMAL, e);
1479 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1481 traceline(mstart, mstart - '1 0 0' * delta.x, MOVE_NORMAL, e);
1482 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1484 traceline(mstart, mstart + '0 1 0' * delta.y, MOVE_NORMAL, e);
1485 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1487 traceline(mstart, mstart - '0 1 0' * delta.y, MOVE_NORMAL, e);
1488 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1490 traceline(mstart, mstart + '0 0 1' * delta.z, MOVE_NORMAL, e);
1491 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1494 // rule 4: we must "see" some spawnpoint or item
1495 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
1496 if(checkpvs(mstart, sp))
1497 if((traceline(mstart, sp.origin, MOVE_NORMAL, e), trace_fraction) >= 1)
1501 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
1502 if(checkpvs(mstart, sp))
1503 if((traceline(mstart, sp.origin + (sp.mins + sp.maxs) * 0.5, MOVE_NORMAL, e), trace_fraction) >= 1)
1509 // find a random vector to "look at"
1510 end.x = org.x + random() * delta.x;
1511 end.y = org.y + random() * delta.y;
1512 end.z = org.z + random() * delta.z;
1513 end = start + normalize(end - start) * vlen(delta);
1515 // rule 4: start TO end must not be too short
1516 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1517 if (trace_startsolid)
1519 if (trace_fraction < minviewdistance / vlen(delta))
1522 // rule 5: don't want to look at sky
1523 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
1526 // rule 6: we must not end up in trigger_hurt
1527 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
1533 e.dphitcontentsmask = m;
1537 setorigin(e, start);
1538 e.angles = vectoangles(end - start);
1539 LOG_TRACE("Needed ", ftos(i + 1), " attempts\n");
1546 void write_recordmarker(entity pl, float tstart, float dt)
1548 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
1550 // also write a marker into demo files for demotc-race-record-extractor to find
1553 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
1554 " ", ftos(tstart), " ", ftos(dt), "\n"));
1557 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
1570 if(allowcenter) // 2: allow center handedness
1583 if(allowcenter) // 2: allow center handedness
1599 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
1604 if (autocvar_g_shootfromeye)
1608 if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
1609 else { vecs.y = 0; vecs.z -= 2; }
1617 else if (autocvar_g_shootfromcenter)
1622 else if ((s = autocvar_g_shootfromfixedorigin) != "")
1632 else if (autocvar_g_shootfromclient)
1634 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
1639 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
1641 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
1645 void attach_sameorigin(entity e, entity to, string tag)
1647 vector org, t_forward, t_left, t_up, e_forward, e_up;
1650 org = e.origin - gettaginfo(to, gettagindex(to, tag));
1651 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
1652 t_forward = v_forward * tagscale;
1653 t_left = v_right * -tagscale;
1654 t_up = v_up * tagscale;
1656 e.origin_x = org * t_forward;
1657 e.origin_y = org * t_left;
1658 e.origin_z = org * t_up;
1660 // current forward and up directions
1661 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1662 e.angles = AnglesTransform_FromVAngles(e.angles);
1664 e.angles = AnglesTransform_FromAngles(e.angles);
1665 fixedmakevectors(e.angles);
1667 // untransform forward, up!
1668 e_forward.x = v_forward * t_forward;
1669 e_forward.y = v_forward * t_left;
1670 e_forward.z = v_forward * t_up;
1671 e_up.x = v_up * t_forward;
1672 e_up.y = v_up * t_left;
1673 e_up.z = v_up * t_up;
1675 e.angles = fixedvectoangles2(e_forward, e_up);
1676 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1677 e.angles = AnglesTransform_ToVAngles(e.angles);
1679 e.angles = AnglesTransform_ToAngles(e.angles);
1681 setattachment(e, to, tag);
1682 setorigin(e, e.origin);
1685 void detach_sameorigin(entity e)
1688 org = gettaginfo(e, 0);
1689 e.angles = fixedvectoangles2(v_forward, v_up);
1690 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1691 e.angles = AnglesTransform_ToVAngles(e.angles);
1693 e.angles = AnglesTransform_ToAngles(e.angles);
1695 setattachment(e, world, "");
1696 setorigin(e, e.origin);
1699 void follow_sameorigin(entity e, entity to)
1701 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
1702 e.aiment = to; // make the hole follow bmodel
1703 e.punchangle = to.angles; // the original angles of bmodel
1704 e.view_ofs = e.origin - to.origin; // relative origin
1705 e.v_angle = e.angles - to.angles; // relative angles
1708 void unfollow_sameorigin(entity e)
1710 e.movetype = MOVETYPE_NONE;
1713 entity gettaginfo_relative_ent;
1714 vector gettaginfo_relative(entity e, float tag)
1716 if (!gettaginfo_relative_ent)
1718 gettaginfo_relative_ent = spawn();
1719 gettaginfo_relative_ent.effects = EF_NODRAW;
1721 gettaginfo_relative_ent.model = e.model;
1722 gettaginfo_relative_ent.modelindex = e.modelindex;
1723 gettaginfo_relative_ent.frame = e.frame;
1724 return gettaginfo(gettaginfo_relative_ent, tag);
1729 float modeleffect_SendEntity(entity to, int sf)
1732 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
1735 if(self.velocity != '0 0 0')
1737 if(self.angles != '0 0 0')
1739 if(self.avelocity != '0 0 0')
1742 WriteByte(MSG_ENTITY, f);
1743 WriteShort(MSG_ENTITY, self.modelindex);
1744 WriteByte(MSG_ENTITY, self.skin);
1745 WriteByte(MSG_ENTITY, self.frame);
1746 WriteCoord(MSG_ENTITY, self.origin.x);
1747 WriteCoord(MSG_ENTITY, self.origin.y);
1748 WriteCoord(MSG_ENTITY, self.origin.z);
1751 WriteCoord(MSG_ENTITY, self.velocity.x);
1752 WriteCoord(MSG_ENTITY, self.velocity.y);
1753 WriteCoord(MSG_ENTITY, self.velocity.z);
1757 WriteCoord(MSG_ENTITY, self.angles.x);
1758 WriteCoord(MSG_ENTITY, self.angles.y);
1759 WriteCoord(MSG_ENTITY, self.angles.z);
1763 WriteCoord(MSG_ENTITY, self.avelocity.x);
1764 WriteCoord(MSG_ENTITY, self.avelocity.y);
1765 WriteCoord(MSG_ENTITY, self.avelocity.z);
1767 WriteShort(MSG_ENTITY, self.scale * 256.0);
1768 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
1769 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
1770 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
1771 WriteByte(MSG_ENTITY, self.alpha * 255.0);
1776 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)
1781 e.classname = "modeleffect";
1789 e.teleport_time = t1;
1793 e.scale = s0 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1797 e.scale2 = s2 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1800 sz = max(e.scale, e.scale2);
1801 setsize(e, e.mins * sz, e.maxs * sz);
1802 Net_LinkEntity(e, false, 0.1, modeleffect_SendEntity);
1805 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
1807 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
1810 float randombit(float bits)
1812 if(!(bits & (bits-1))) // this ONLY holds for powers of two!
1821 for(f = 1; f <= bits; f *= 2)
1830 r = (r - 1) / (n - 1);
1837 float randombits(float bits, float k, float error_return)
1841 while(k > 0 && bits != r)
1843 r += randombit(bits - r);
1852 void randombit_test(float bits, float iter)
1856 LOG_INFO(ftos(randombit(bits)), "\n");
1861 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
1863 if(halflifedist > 0)
1864 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
1865 else if(halflifedist < 0)
1866 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
1872 .string aiment_classname;
1873 .float aiment_deadflag;
1874 void SetMovetypeFollow(entity ent, entity e)
1876 // FIXME this may not be warpzone aware
1877 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
1878 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.
1879 ent.aiment = e; // make the hole follow bmodel
1880 ent.punchangle = e.angles; // the original angles of bmodel
1881 ent.view_ofs = ent.origin - e.origin; // relative origin
1882 ent.v_angle = ent.angles - e.angles; // relative angles
1883 ent.aiment_classname = strzone(e.classname);
1884 ent.aiment_deadflag = e.deadflag;
1886 void UnsetMovetypeFollow(entity ent)
1888 ent.movetype = MOVETYPE_FLY;
1889 PROJECTILE_MAKETRIGGER(ent);
1892 float LostMovetypeFollow(entity ent)
1895 if(ent.movetype != MOVETYPE_FOLLOW)
1901 if(ent.aiment.classname != ent.aiment_classname)
1903 if(ent.aiment.deadflag != ent.aiment_deadflag)
1909 float isPushable(entity e)
1920 case "droppedweapon":
1921 case "keepawayball":
1922 case "nexball_basketball":
1923 case "nexball_football":
1925 case "bullet": // antilagged bullets can't hit this either
1928 if (e.projectiledeathtype)