1 #include "miscfunctions.qh"
3 #include "command/common.qh"
4 #include "constants.qh"
7 #include "mutators/mutators_include.qh"
9 #include "weapons/accuracy.qh"
10 #include "weapons/csqcprojectile.qh"
11 #include "weapons/selection.qh"
12 #include "../common/command/generic.qh"
13 #include "../common/constants.qh"
14 #include "../common/deathtypes/all.qh"
15 #include "../common/mapinfo.qh"
16 #include "../common/notifications.qh"
17 #include "../common/playerstats.qh"
18 #include "../common/teams.qh"
19 #include "../common/triggers/subs.qh"
20 #include "../common/util.qh"
21 #include "../common/turrets/sv_turrets.qh"
22 #include "../common/weapons/all.qh"
23 #include "../common/vehicles/sv_vehicles.qh"
24 #include "../common/vehicles/vehicle.qh"
25 #include "../common/items/all.qc"
26 #include "../lib/csqcmodel/sv_model.qh"
27 #include "../lib/warpzone/anglestransform.qh"
28 #include "../lib/warpzone/server.qh"
30 void crosshair_trace(entity pl)
32 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));
34 void crosshair_trace_plusvisibletriggers(entity pl)
38 first = findchainfloat(solid, SOLID_TRIGGER);
40 for (e = first; e; e = e.chain)
46 for (e = first; e; e = e.chain)
47 e.solid = SOLID_TRIGGER;
49 void WarpZone_crosshair_trace(entity pl)
51 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));
55 string admin_name(void)
57 if(autocvar_sv_adminnick != "")
58 return autocvar_sv_adminnick;
60 return "SERVER ADMIN";
63 void DistributeEvenly_Init(float amount, float totalweight)
65 if (DistributeEvenly_amount)
67 LOG_TRACE("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
68 LOG_TRACE(ftos(DistributeEvenly_totalweight), " left!)\n");
71 DistributeEvenly_amount = 0;
73 DistributeEvenly_amount = amount;
74 DistributeEvenly_totalweight = totalweight;
76 float DistributeEvenly_Get(float weight)
81 f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
82 DistributeEvenly_totalweight -= weight;
83 DistributeEvenly_amount -= f;
86 float DistributeEvenly_GetRandomized(float weight)
91 f = floor(random() + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
92 DistributeEvenly_totalweight -= weight;
93 DistributeEvenly_amount -= f;
98 void GameLogEcho(string s)
103 if (autocvar_sv_eventlog_files)
108 matches = autocvar_sv_eventlog_files_counter + 1;
109 cvar_set("sv_eventlog_files_counter", itos(matches));
112 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
113 fn = strcat(autocvar_sv_eventlog_files_nameprefix, fn, autocvar_sv_eventlog_files_namesuffix);
114 logfile = fopen(fn, FILE_APPEND);
115 fputs(logfile, ":logversion:3\n");
119 if (autocvar_sv_eventlog_files_timestamps)
120 fputs(logfile, strcat(":time:", strftime(true, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
122 fputs(logfile, strcat(s, "\n"));
125 if (autocvar_sv_eventlog_console)
134 // will be opened later
139 if (logfile_open && logfile >= 0)
146 entity findnearest(vector point, .string field, string value, vector axismod)
157 localhead = find(world, field, value);
160 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
161 dist = localhead.oldorigin;
163 dist = localhead.origin;
165 dist = dist.x * axismod.x * '1 0 0' + dist.y * axismod.y * '0 1 0' + dist.z * axismod.z * '0 0 1';
168 for (i = 0; i < num_nearest; ++i)
170 if (len < nearest_length[i])
174 // now i tells us where to insert at
175 // INSERTION SORT! YOU'VE SEEN IT! RUN!
176 if (i < NUM_NEAREST_ENTITIES)
178 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
180 nearest_length[j + 1] = nearest_length[j];
181 nearest_entity[j + 1] = nearest_entity[j];
183 nearest_length[i] = len;
184 nearest_entity[i] = localhead;
185 if (num_nearest < NUM_NEAREST_ENTITIES)
186 num_nearest = num_nearest + 1;
189 localhead = find(localhead, field, value);
192 // now use the first one from our list that we can see
193 for (i = 0; i < num_nearest; ++i)
195 traceline(point, nearest_entity[i].origin, true, world);
196 if (trace_fraction == 1)
200 LOG_TRACE("Nearest point (");
201 LOG_TRACE(nearest_entity[0].netname);
202 LOG_TRACE(") is not visible, using a visible one.\n");
204 return nearest_entity[i];
208 if (num_nearest == 0)
211 LOG_TRACE("Not seeing any location point, using nearest as fallback.\n");
213 dprint("Candidates were: ");
214 for(j = 0; j < num_nearest; ++j)
218 dprint(nearest_entity[j].netname);
223 return nearest_entity[0];
226 string NearestLocation(vector p)
231 loc = findnearest(p, classname, "target_location", '1 1 1');
238 loc = findnearest(p, target, "###item###", '1 1 4');
245 string formatmessage(string msg)
257 ammoitems = "batteries";
258 if(self.items & ITEM_Plasma.m_itemid) ammoitems = ITEM_Plasma.m_name;
259 if(self.items & ITEM_Cells.m_itemid) ammoitems = ITEM_Cells.m_name;
260 if(self.items & ITEM_Rockets.m_itemid) ammoitems = ITEM_Rockets.m_name;
261 if(self.items & ITEM_Shells.m_itemid) ammoitems = ITEM_Shells.m_name;
263 WarpZone_crosshair_trace(self);
264 cursor = trace_endpos;
265 cursor_ent = trace_ent;
269 break; // too many replacements
272 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
273 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
286 replacement = substring(msg, p, 2);
287 escape = substring(msg, p + 1, 1);
291 case "%": replacement = "%"; break;
292 case "\\":replacement = "\\"; break;
293 case "n": replacement = "\n"; break;
294 case "a": replacement = ftos(floor(self.armorvalue)); break;
295 case "h": replacement = ftos(floor(self.health)); break;
296 case "l": replacement = NearestLocation(self.origin); break;
297 case "y": replacement = NearestLocation(cursor); break;
298 case "d": replacement = NearestLocation(self.death_origin); break;
299 case "w": replacement = WEP_NAME((!self.weapon) ? (!self.switchweapon ? self.cnt : self.switchweapon) : self.weapon); break;
300 case "W": replacement = ammoitems; break;
301 case "x": replacement = ((cursor_ent.netname == "" || !cursor_ent) ? "nothing" : cursor_ent.netname); break;
302 case "s": replacement = ftos(vlen(self.velocity - self.velocity_z * '0 0 1')); break;
303 case "S": replacement = ftos(vlen(self.velocity)); break;
304 case "t": replacement = seconds_tostring(ceil(max(0, autocvar_timelimit * 60 + game_starttime - time))); break;
305 case "T": replacement = seconds_tostring(floor(time - game_starttime)); break;
308 MUTATOR_CALLHOOK(FormatMessage, escape, replacement, msg);
309 escape = format_escape;
310 replacement = format_replacement;
315 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
316 p = p + strlen(replacement);
327 >0: receives a cvar from name=argv(f) value=argv(f+1)
329 void GetCvars_handleString(string thisname, float f, .string field, string name)
334 strunzone(self.(field));
335 self.(field) = string_null;
339 if (thisname == name)
342 strunzone(self.(field));
343 self.(field) = strzone(argv(f + 1));
347 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
349 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
351 GetCvars_handleString(thisname, f, field, name);
352 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
353 if (thisname == name)
355 string s = func(strcat1(self.(field)));
356 if (s != self.(field))
358 strunzone(self.(field));
359 self.(field) = strzone(s);
363 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
370 if (thisname == name)
371 self.(field) = stof(argv(f + 1));
374 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
376 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
383 if (thisname == name)
387 self.(field) = stof(argv(f + 1));
396 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
399 string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(string wo)
402 o = W_FixWeaponOrder_ForceComplete(wo);
403 if(self.weaponorder_byimpulse)
405 strunzone(self.weaponorder_byimpulse);
406 self.weaponorder_byimpulse = string_null;
408 self.weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(o));
411 void GetCvars(float f)
413 string s = string_null;
416 s = strcat1(argv(f));
420 MUTATOR_CALLHOOK(GetCvars);
422 Notification_GetCvars();
424 ReplicateVars(this, s, f);
426 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
427 GetCvars_handleFloat(s, f, cvar_cl_autoscreenshot, "cl_autoscreenshot");
428 GetCvars_handleFloat(s, f, cvar_cl_jetpack_jump, "cl_jetpack_jump");
429 GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
430 GetCvars_handleString(s, f, cvar_cl_physics, "cl_physics");
431 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
432 GetCvars_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
433 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
434 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
435 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
436 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
437 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
438 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
439 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
440 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
441 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
442 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
443 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
444 GetCvars_handleFloat(s, f, cvar_cl_weaponimpulsemode, "cl_weaponimpulsemode");
445 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
446 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
447 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
448 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
449 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
450 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
452 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
453 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
455 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
456 GetCvars_handleFloat(s, f, cvar_cl_allow_uid2name, "cl_allow_uid2name");
457 GetCvars_handleFloat(s, f, cvar_cl_allow_uidtracking, "cl_allow_uidtracking");
458 GetCvars_handleFloat(s, f, cvar_cl_movement_track_canjump, "cl_movement_track_canjump");
459 GetCvars_handleFloat(s, f, cvar_cl_newusekeysupported, "cl_newusekeysupported");
461 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
464 if (s == "cl_weaponpriority")
465 self.switchweapon = w_getbestweapon(self);
466 if (s == "cl_allow_uidtracking")
467 PlayerStats_GameReport_AddPlayer(self);
471 // decolorizes and team colors the player name when needed
472 string playername(entity p)
475 if (teamplay && !intermission_running && IS_PLAYER(p))
477 t = Team_ColorCode(p.team);
478 return strcat(t, strdecolorize(p.netname));
484 float want_weapon(entity weaponinfo, float allguns) // WEAPONTODO: what still needs done?
486 int i = weaponinfo.weapon;
488 bool allow_mutatorblocked = false;
493 bool mutator_returnvalue = MUTATOR_CALLHOOK(WantWeapon, weaponinfo, d, allguns, allow_mutatorblocked);
495 allguns = want_allguns;
496 allow_mutatorblocked = false;
500 if(weaponinfo.spawnflags & WEP_FLAG_NORMAL)
505 else if(!mutator_returnvalue)
506 d = !(!weaponinfo.weaponstart);
508 if(!allow_mutatorblocked && (weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED)) // never default mutator blocked guns
511 float t = weaponinfo.weaponstartoverride;
513 //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n"));
518 // 4: is set by default?
527 void readplayerstartcvars()
533 // initialize starting values for players
534 start_weapons = '0 0 0';
535 start_weapons_default = '0 0 0';
536 start_weapons_defaultmask = '0 0 0';
538 start_ammo_shells = 0;
539 start_ammo_nails = 0;
540 start_ammo_rockets = 0;
541 start_ammo_cells = 0;
542 start_ammo_plasma = 0;
543 start_health = cvar("g_balance_health_start");
544 start_armorvalue = cvar("g_balance_armor_start");
547 g_weaponarena_weapons = '0 0 0';
549 s = cvar_string("g_weaponarena");
550 if (s == "0" || s == "")
552 if(g_ca || g_freezetag)
556 if (s == "0" || s == "")
562 // forcibly turn off weaponarena
564 else if (s == "all" || s == "1")
567 g_weaponarena_list = "All Weapons";
568 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
570 e = get_weaponinfo(j);
571 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
572 g_weaponarena_weapons |= WepSet_FromWeapon(j);
575 else if (s == "most")
578 g_weaponarena_list = "Most Weapons";
579 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
581 e = get_weaponinfo(j);
582 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
583 if (e.spawnflags & WEP_FLAG_NORMAL)
584 g_weaponarena_weapons |= WepSet_FromWeapon(j);
587 else if (s == "none")
590 g_weaponarena_list = "No Weapons";
595 t = tokenize_console(s);
596 g_weaponarena_list = "";
597 for (i = 0; i < t; ++i)
600 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
602 e = get_weaponinfo(j);
605 g_weaponarena_weapons |= WepSet_FromWeapon(j);
606 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
612 LOG_INFO("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
615 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
619 g_weaponarena_random = cvar("g_weaponarena_random");
621 g_weaponarena_random = 0;
622 g_weaponarena_random_with_blaster = cvar("g_weaponarena_random_with_blaster");
626 g_weapon_stay = 0; // incompatible
627 start_weapons = g_weaponarena_weapons;
628 start_items |= IT_UNLIMITED_AMMO;
632 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
634 e = get_weaponinfo(i);
635 int w = want_weapon(e, false);
637 start_weapons |= WepSet_FromWeapon(i);
639 start_weapons_default |= WepSet_FromWeapon(i);
641 start_weapons_defaultmask |= WepSet_FromWeapon(i);
645 if(!cvar("g_use_ammunition"))
646 start_items |= IT_UNLIMITED_AMMO;
648 if(start_items & IT_UNLIMITED_WEAPON_AMMO)
650 start_ammo_shells = 999;
651 start_ammo_nails = 999;
652 start_ammo_rockets = 999;
653 start_ammo_cells = 999;
654 start_ammo_plasma = 999;
655 start_ammo_fuel = 999;
659 start_ammo_shells = cvar("g_start_ammo_shells");
660 start_ammo_nails = cvar("g_start_ammo_nails");
661 start_ammo_rockets = cvar("g_start_ammo_rockets");
662 start_ammo_cells = cvar("g_start_ammo_cells");
663 start_ammo_plasma = cvar("g_start_ammo_plasma");
664 start_ammo_fuel = cvar("g_start_ammo_fuel");
669 warmup_start_ammo_shells = start_ammo_shells;
670 warmup_start_ammo_nails = start_ammo_nails;
671 warmup_start_ammo_rockets = start_ammo_rockets;
672 warmup_start_ammo_cells = start_ammo_cells;
673 warmup_start_ammo_plasma = start_ammo_plasma;
674 warmup_start_ammo_fuel = start_ammo_fuel;
675 warmup_start_health = start_health;
676 warmup_start_armorvalue = start_armorvalue;
677 warmup_start_weapons = start_weapons;
678 warmup_start_weapons_default = start_weapons_default;
679 warmup_start_weapons_defaultmask = start_weapons_defaultmask;
681 if (!g_weaponarena && !g_ca && !g_freezetag)
683 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
684 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
685 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
686 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
687 warmup_start_ammo_plasma = cvar("g_warmup_start_ammo_plasma");
688 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
689 warmup_start_health = cvar("g_warmup_start_health");
690 warmup_start_armorvalue = cvar("g_warmup_start_armor");
691 warmup_start_weapons = '0 0 0';
692 warmup_start_weapons_default = '0 0 0';
693 warmup_start_weapons_defaultmask = '0 0 0';
694 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
696 e = get_weaponinfo(i);
697 int w = want_weapon(e, g_warmup_allguns);
699 warmup_start_weapons |= WepSet_FromWeapon(i);
701 warmup_start_weapons_default |= WepSet_FromWeapon(i);
703 warmup_start_weapons_defaultmask |= WepSet_FromWeapon(i);
709 start_items |= ITEM_Jetpack.m_itemid;
711 MUTATOR_CALLHOOK(SetStartItems);
713 if (start_items & ITEM_Jetpack.m_itemid)
715 start_items |= ITEM_JetpackRegen.m_itemid;
716 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
717 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
720 WepSet precache_weapons = start_weapons;
721 if (g_warmup_allguns != 1)
722 precache_weapons |= warmup_start_weapons;
723 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
725 e = get_weaponinfo(i);
726 if(precache_weapons & WepSet_FromWeapon(i)) {
727 Weapon w = get_weaponinfo(i);
732 start_ammo_shells = max(0, start_ammo_shells);
733 start_ammo_nails = max(0, start_ammo_nails);
734 start_ammo_rockets = max(0, start_ammo_rockets);
735 start_ammo_cells = max(0, start_ammo_cells);
736 start_ammo_plasma = max(0, start_ammo_plasma);
737 start_ammo_fuel = max(0, start_ammo_fuel);
739 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
740 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
741 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
742 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
743 warmup_start_ammo_plasma = max(0, warmup_start_ammo_plasma);
744 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
747 float sound_allowed(float destin, entity e)
749 // sounds from world may always pass
752 if (e.classname == "body")
754 else if (e.realowner && e.realowner != e)
756 else if (e.owner && e.owner != e)
761 // sounds to self may always pass
762 if (destin == MSG_ONE)
765 // sounds by players can be removed
766 if (autocvar_bot_sound_monopoly)
767 if (IS_REAL_CLIENT(e))
769 // anything else may pass
773 void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float attenu)
777 if (!sound_allowed(_dest, e))
780 entno = num_for_edict(e);
781 idx = precache_sound_index(samp);
786 attenu = floor(attenu * 64);
787 vol = floor(vol * 255);
790 sflags |= SND_VOLUME;
792 sflags |= SND_ATTENUATION;
793 if (entno >= 8192 || chan < 0 || chan > 7)
794 sflags |= SND_LARGEENTITY;
796 sflags |= SND_LARGESOUND;
798 WriteByte(_dest, SVC_SOUND);
799 WriteByte(_dest, sflags);
800 if (sflags & SND_VOLUME)
801 WriteByte(_dest, vol);
802 if (sflags & SND_ATTENUATION)
803 WriteByte(_dest, attenu);
804 if (sflags & SND_LARGEENTITY)
806 WriteShort(_dest, entno);
807 WriteByte(_dest, chan);
811 WriteShort(_dest, entno * 8 + chan);
813 if (sflags & SND_LARGESOUND)
814 WriteShort(_dest, idx);
816 WriteByte(_dest, idx);
818 WriteCoord(_dest, o.x);
819 WriteCoord(_dest, o.y);
820 WriteCoord(_dest, o.z);
822 void soundto(float _dest, entity e, float chan, string samp, float vol, float _atten)
826 if (!sound_allowed(_dest, e))
829 o = e.origin + 0.5 * (e.mins + e.maxs);
830 soundtoat(_dest, e, o, chan, samp, vol, _atten);
832 void soundat(entity e, vector o, float chan, string samp, float vol, float _atten)
834 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, _atten);
836 void stopsoundto(float _dest, entity e, float chan)
840 if (!sound_allowed(_dest, e))
843 entno = num_for_edict(e);
845 if (entno >= 8192 || chan < 0 || chan > 7)
848 idx = precache_sound_index(SND(Null));
849 sflags = SND_LARGEENTITY;
851 sflags |= SND_LARGESOUND;
852 WriteByte(_dest, SVC_SOUND);
853 WriteByte(_dest, sflags);
854 WriteShort(_dest, entno);
855 WriteByte(_dest, chan);
856 if (sflags & SND_LARGESOUND)
857 WriteShort(_dest, idx);
859 WriteByte(_dest, idx);
860 WriteCoord(_dest, e.origin.x);
861 WriteCoord(_dest, e.origin.y);
862 WriteCoord(_dest, e.origin.z);
866 WriteByte(_dest, SVC_STOPSOUND);
867 WriteShort(_dest, entno * 8 + chan);
870 void stopsound(entity e, float chan)
872 if (!sound_allowed(MSG_BROADCAST, e))
875 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
876 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
879 void play2(entity e, string filename)
881 //stuffcmd(e, strcat("play2 ", filename, "\n"));
883 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTEN_NONE);
886 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
888 float spamsound(entity e, float chan, string samp, float vol, float _atten)
890 if (!sound_allowed(MSG_BROADCAST, e))
893 if (time > e.spamtime)
896 _sound(e, chan, samp, vol, _atten);
902 void play2team(float t, string filename)
906 if (autocvar_bot_sound_monopoly)
909 FOR_EACH_REALPLAYER(head)
912 play2(head, filename);
916 void play2all(string samp)
918 if (autocvar_bot_sound_monopoly)
921 _sound(world, CH_INFO, samp, VOL_BASE, ATTEN_NONE);
924 void PrecachePlayerSounds(string f);
925 void precache_playermodel(string m)
927 float globhandle, i, n;
930 if(substring(m, -9,5) == "_lod1")
932 if(substring(m, -9,5) == "_lod2")
935 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
938 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
942 globhandle = search_begin(strcat(m, "_*.sounds"), true, false);
945 n = search_getsize(globhandle);
946 for (i = 0; i < n; ++i)
948 //print(search_getfilename(globhandle, i), "\n");
949 f = search_getfilename(globhandle, i);
950 PrecachePlayerSounds(f);
952 search_end(globhandle);
954 void precache_all_playermodels(string pattern)
956 float globhandle, i, n;
959 globhandle = search_begin(pattern, true, false);
962 n = search_getsize(globhandle);
963 for (i = 0; i < n; ++i)
965 //print(search_getfilename(globhandle, i), "\n");
966 f = search_getfilename(globhandle, i);
967 precache_playermodel(f);
969 search_end(globhandle);
972 void precache_playermodels(string s)
976 int n = tokenize_console(s);
977 precache_playermodel(argv(0));
979 for (int i = 1; i < n; ++i)
980 precache_model(argv(i));
986 // gamemode related things
988 // Precache all player models if desired
989 if (autocvar_sv_precacheplayermodels)
991 PrecachePlayerSounds("sound/player/default.sounds");
992 precache_all_playermodels("models/player/*.zym");
993 precache_all_playermodels("models/player/*.dpm");
994 precache_all_playermodels("models/player/*.md3");
995 precache_all_playermodels("models/player/*.psk");
996 precache_all_playermodels("models/player/*.iqm");
999 if (autocvar_sv_defaultcharacter)
1001 precache_playermodels(autocvar_sv_defaultplayermodel_red);
1002 precache_playermodels(autocvar_sv_defaultplayermodel_blue);
1003 precache_playermodels(autocvar_sv_defaultplayermodel_yellow);
1004 precache_playermodels(autocvar_sv_defaultplayermodel_pink);
1005 precache_playermodels(autocvar_sv_defaultplayermodel);
1010 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1011 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1014 // gore and miscellaneous sounds
1015 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1016 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1019 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1021 if (!self.noise && self.music) // quake 3 uses the music field
1022 self.noise = self.music;
1024 // plays music for the level if there is any
1027 precache_sound (self.noise);
1028 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTEN_NONE);
1034 void make_safe_for_remove(entity e)
1036 if (e.initialize_entity)
1038 entity ent, prev = world;
1039 for (ent = initialize_entity_first; ent; )
1041 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1043 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1044 // skip it in linked list
1047 prev.initialize_entity_next = ent.initialize_entity_next;
1048 ent = prev.initialize_entity_next;
1052 initialize_entity_first = ent.initialize_entity_next;
1053 ent = initialize_entity_first;
1059 ent = ent.initialize_entity_next;
1065 void objerror(string s)
1067 make_safe_for_remove(self);
1068 builtin_objerror(s);
1071 .float remove_except_protected_forbidden;
1072 void remove_except_protected(entity e)
1074 if(e.remove_except_protected_forbidden)
1075 error("not allowed to remove this at this point");
1079 void remove_unsafely(entity e)
1081 if(e.classname == "spike")
1082 error("Removing spikes is forbidden (crylink bug), please report");
1086 void remove_safely(entity e)
1088 make_safe_for_remove(e);
1092 void InitializeEntity(entity e, void(void) func, float order)
1096 if (!e || e.initialize_entity)
1098 // make a proxy initializer entity
1100 e = new(initialize_entity);
1104 e.initialize_entity = func;
1105 e.initialize_entity_order = order;
1107 cur = initialize_entity_first;
1111 if (!cur || cur.initialize_entity_order > order)
1113 // insert between prev and cur
1115 prev.initialize_entity_next = e;
1117 initialize_entity_first = e;
1118 e.initialize_entity_next = cur;
1122 cur = cur.initialize_entity_next;
1125 void InitializeEntitiesRun()
1127 entity startoflist = initialize_entity_first;
1128 initialize_entity_first = NULL;
1129 remove = remove_except_protected;
1130 for (entity e = startoflist; e; e = e.initialize_entity_next)
1132 e.remove_except_protected_forbidden = 1;
1134 for (entity e = startoflist; e; )
1136 e.remove_except_protected_forbidden = 0;
1137 e.initialize_entity_order = 0;
1138 entity next = e.initialize_entity_next;
1139 e.initialize_entity_next = NULL;
1140 var void() func = e.initialize_entity;
1141 e.initialize_entity = func_null;
1142 if (e.classname == "initialize_entity")
1144 entity wrappee = e.enemy;
1148 //dprint("Delayed initialization: ", e.classname, "\n");
1151 WITH(entity, self, e, func());
1156 backtrace(strcat("Null function in: ", e.classname, "\n"));
1160 remove = remove_unsafely;
1163 .float(entity) isEliminated;
1164 bool EliminatedPlayers_SendEntity(entity this, entity to, float sendflags)
1168 WriteByte(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS);
1169 WriteByte(MSG_ENTITY, sendflags);
1173 for(i = 1; i <= maxclients; i += 8)
1175 for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e))
1177 if(eliminatedPlayers.isEliminated(e))
1180 WriteByte(MSG_ENTITY, f);
1187 void EliminatedPlayers_Init(float(entity) isEliminated_func)
1189 if(eliminatedPlayers)
1191 backtrace("Can't spawn eliminatedPlayers again!");
1194 Net_LinkEntity(eliminatedPlayers = spawn(), false, 0, EliminatedPlayers_SendEntity);
1195 eliminatedPlayers.isEliminated = isEliminated_func;
1199 void adaptor_think2touch()
1208 void adaptor_think2use()
1220 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1222 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
1223 self.projectiledeathtype |= HITTYPE_SPLASH;
1224 adaptor_think2use();
1227 // deferred dropping
1228 void DropToFloor_Handler()
1230 builtin_droptofloor();
1231 self.dropped_origin = self.origin;
1236 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1241 float trace_hits_box_a0, trace_hits_box_a1;
1243 float trace_hits_box_1d(float end, float thmi, float thma)
1247 // just check if x is in range
1255 // do the trace with respect to x
1256 // 0 -> end has to stay in thmi -> thma
1257 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1258 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1259 if (trace_hits_box_a0 > trace_hits_box_a1)
1265 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1270 // now it is a trace from 0 to end
1272 trace_hits_box_a0 = 0;
1273 trace_hits_box_a1 = 1;
1275 if (!trace_hits_box_1d(end.x, thmi.x, thma.x))
1277 if (!trace_hits_box_1d(end.y, thmi.y, thma.y))
1279 if (!trace_hits_box_1d(end.z, thmi.z, thma.z))
1285 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1287 return trace_hits_box(start, end, thmi - ma, thma - mi);
1290 float SUB_NoImpactCheck()
1292 // zero hitcontents = this is not the real impact, but either the
1293 // mirror-impact of something hitting the projectile instead of the
1294 // projectile hitting the something, or a touchareagrid one. Neither of
1295 // these stop the projectile from moving, so...
1296 if(trace_dphitcontents == 0)
1298 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1299 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));
1302 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1304 if (other == world && self.size != '0 0 0')
1307 tic = self.velocity * sys_frametime;
1308 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1309 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1310 if (trace_fraction >= 1)
1312 LOG_TRACE("Odd... did not hit...?\n");
1314 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1316 LOG_TRACE("Detected and prevented the sky-grapple bug.\n");
1324 #define SUB_OwnerCheck() (other && (other == self.owner))
1326 void W_Crylink_Dequeue(entity e);
1327 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1329 if(SUB_OwnerCheck())
1331 if(SUB_NoImpactCheck())
1333 if(self.classname == "nade")
1334 return false; // no checks here
1335 else if(self.classname == "grapplinghook")
1336 RemoveGrapplingHook(self.realowner);
1337 else if(self.classname == "spike")
1339 W_Crylink_Dequeue(self);
1346 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1347 UpdateCSQCProjectile(self);
1352 void URI_Get_Callback(float id, float status, string data)
1354 if(url_URI_Get_Callback(id, status, data))
1358 else if (id == URI_GET_DISCARD)
1362 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
1365 Curl_URI_Get_Callback(id, status, data);
1367 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1370 OnlineBanList_URI_Get_Callback(id, status, data);
1374 LOG_INFO("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1378 string uid2name(string myuid) {
1380 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
1382 // FIXME remove this later after 0.6 release
1383 // convert old style broken records to correct style
1386 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
1389 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
1390 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
1395 s = "^1Unregistered Player";
1399 float MoveToRandomLocationWithinBounds(entity e, vector boundmin, vector boundmax, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1402 vector start, org, delta, end, enddown, mstart;
1405 m = e.dphitcontentsmask;
1406 e.dphitcontentsmask = goodcontents | badcontents;
1409 delta = boundmax - boundmin;
1413 for (i = 0; i < attempts; ++i)
1415 start.x = org.x + random() * delta.x;
1416 start.y = org.y + random() * delta.y;
1417 start.z = org.z + random() * delta.z;
1419 // rule 1: start inside world bounds, and outside
1420 // solid, and don't start from somewhere where you can
1421 // fall down to evil
1422 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta.z, MOVE_NORMAL, e);
1423 if (trace_fraction >= 1)
1425 if (trace_startsolid)
1427 if (trace_dphitcontents & badcontents)
1429 if (trace_dphitq3surfaceflags & badsurfaceflags)
1432 // rule 2: if we are too high, lower the point
1433 if (trace_fraction * delta.z > maxaboveground)
1434 start = trace_endpos + '0 0 1' * maxaboveground;
1435 enddown = trace_endpos;
1437 // rule 3: make sure we aren't outside the map. This only works
1438 // for somewhat well formed maps. A good rule of thumb is that
1439 // the map should have a convex outside hull.
1440 // these can be traceLINES as we already verified the starting box
1441 mstart = start + 0.5 * (e.mins + e.maxs);
1442 traceline(mstart, mstart + '1 0 0' * delta.x, MOVE_NORMAL, e);
1443 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1445 traceline(mstart, mstart - '1 0 0' * delta.x, MOVE_NORMAL, e);
1446 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1448 traceline(mstart, mstart + '0 1 0' * delta.y, MOVE_NORMAL, e);
1449 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1451 traceline(mstart, mstart - '0 1 0' * delta.y, MOVE_NORMAL, e);
1452 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1454 traceline(mstart, mstart + '0 0 1' * delta.z, MOVE_NORMAL, e);
1455 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1458 // rule 4: we must "see" some spawnpoint or item
1459 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
1460 if(checkpvs(mstart, sp))
1461 if((traceline(mstart, sp.origin, MOVE_NORMAL, e), trace_fraction) >= 1)
1465 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
1466 if(checkpvs(mstart, sp))
1467 if((traceline(mstart, sp.origin + (sp.mins + sp.maxs) * 0.5, MOVE_NORMAL, e), trace_fraction) >= 1)
1473 // find a random vector to "look at"
1474 end.x = org.x + random() * delta.x;
1475 end.y = org.y + random() * delta.y;
1476 end.z = org.z + random() * delta.z;
1477 end = start + normalize(end - start) * vlen(delta);
1479 // rule 4: start TO end must not be too short
1480 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1481 if (trace_startsolid)
1483 if (trace_fraction < minviewdistance / vlen(delta))
1486 // rule 5: don't want to look at sky
1487 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
1490 // rule 6: we must not end up in trigger_hurt
1491 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
1497 e.dphitcontentsmask = m;
1501 setorigin(e, start);
1502 e.angles = vectoangles(end - start);
1503 LOG_TRACE("Needed ", ftos(i + 1), " attempts\n");
1510 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1512 return MoveToRandomLocationWithinBounds(e, world.mins, world.maxs, goodcontents, badcontents, badsurfaceflags, attempts, maxaboveground, minviewdistance);
1515 void write_recordmarker(entity pl, float tstart, float dt)
1517 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
1519 // also write a marker into demo files for demotc-race-record-extractor to find
1522 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
1523 " ", ftos(tstart), " ", ftos(dt), "\n"));
1526 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
1539 if(allowcenter) // 2: allow center handedness
1552 if(allowcenter) // 2: allow center handedness
1568 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
1573 if (autocvar_g_shootfromeye)
1577 if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
1578 else { vecs.y = 0; vecs.z -= 2; }
1586 else if (autocvar_g_shootfromcenter)
1591 else if ((s = autocvar_g_shootfromfixedorigin) != "")
1601 else if (autocvar_g_shootfromclient)
1603 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
1608 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
1610 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
1614 void attach_sameorigin(entity e, entity to, string tag)
1616 vector org, t_forward, t_left, t_up, e_forward, e_up;
1619 org = e.origin - gettaginfo(to, gettagindex(to, tag));
1620 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
1621 t_forward = v_forward * tagscale;
1622 t_left = v_right * -tagscale;
1623 t_up = v_up * tagscale;
1625 e.origin_x = org * t_forward;
1626 e.origin_y = org * t_left;
1627 e.origin_z = org * t_up;
1629 // current forward and up directions
1630 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1631 e.angles = AnglesTransform_FromVAngles(e.angles);
1633 e.angles = AnglesTransform_FromAngles(e.angles);
1634 fixedmakevectors(e.angles);
1636 // untransform forward, up!
1637 e_forward.x = v_forward * t_forward;
1638 e_forward.y = v_forward * t_left;
1639 e_forward.z = v_forward * t_up;
1640 e_up.x = v_up * t_forward;
1641 e_up.y = v_up * t_left;
1642 e_up.z = v_up * t_up;
1644 e.angles = fixedvectoangles2(e_forward, e_up);
1645 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1646 e.angles = AnglesTransform_ToVAngles(e.angles);
1648 e.angles = AnglesTransform_ToAngles(e.angles);
1650 setattachment(e, to, tag);
1651 setorigin(e, e.origin);
1654 void detach_sameorigin(entity e)
1657 org = gettaginfo(e, 0);
1658 e.angles = fixedvectoangles2(v_forward, v_up);
1659 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1660 e.angles = AnglesTransform_ToVAngles(e.angles);
1662 e.angles = AnglesTransform_ToAngles(e.angles);
1664 setattachment(e, world, "");
1665 setorigin(e, e.origin);
1668 void follow_sameorigin(entity e, entity to)
1670 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
1671 e.aiment = to; // make the hole follow bmodel
1672 e.punchangle = to.angles; // the original angles of bmodel
1673 e.view_ofs = e.origin - to.origin; // relative origin
1674 e.v_angle = e.angles - to.angles; // relative angles
1677 void unfollow_sameorigin(entity e)
1679 e.movetype = MOVETYPE_NONE;
1682 entity gettaginfo_relative_ent;
1683 vector gettaginfo_relative(entity e, float tag)
1685 if (!gettaginfo_relative_ent)
1687 gettaginfo_relative_ent = spawn();
1688 gettaginfo_relative_ent.effects = EF_NODRAW;
1690 gettaginfo_relative_ent.model = e.model;
1691 gettaginfo_relative_ent.modelindex = e.modelindex;
1692 gettaginfo_relative_ent.frame = e.frame;
1693 return gettaginfo(gettaginfo_relative_ent, tag);
1698 bool modeleffect_SendEntity(entity this, entity to, int sf)
1701 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
1704 if(self.velocity != '0 0 0')
1706 if(self.angles != '0 0 0')
1708 if(self.avelocity != '0 0 0')
1711 WriteByte(MSG_ENTITY, f);
1712 WriteShort(MSG_ENTITY, self.modelindex);
1713 WriteByte(MSG_ENTITY, self.skin);
1714 WriteByte(MSG_ENTITY, self.frame);
1715 WriteCoord(MSG_ENTITY, self.origin.x);
1716 WriteCoord(MSG_ENTITY, self.origin.y);
1717 WriteCoord(MSG_ENTITY, self.origin.z);
1720 WriteCoord(MSG_ENTITY, self.velocity.x);
1721 WriteCoord(MSG_ENTITY, self.velocity.y);
1722 WriteCoord(MSG_ENTITY, self.velocity.z);
1726 WriteCoord(MSG_ENTITY, self.angles.x);
1727 WriteCoord(MSG_ENTITY, self.angles.y);
1728 WriteCoord(MSG_ENTITY, self.angles.z);
1732 WriteCoord(MSG_ENTITY, self.avelocity.x);
1733 WriteCoord(MSG_ENTITY, self.avelocity.y);
1734 WriteCoord(MSG_ENTITY, self.avelocity.z);
1736 WriteShort(MSG_ENTITY, self.scale * 256.0);
1737 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
1738 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
1739 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
1740 WriteByte(MSG_ENTITY, self.alpha * 255.0);
1745 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)
1750 e.classname = "modeleffect";
1758 e.teleport_time = t1;
1762 e.scale = s0 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1766 e.scale2 = s2 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1769 sz = max(e.scale, e.scale2);
1770 setsize(e, e.mins * sz, e.maxs * sz);
1771 Net_LinkEntity(e, false, 0.1, modeleffect_SendEntity);
1774 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
1776 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
1779 float randombit(float bits)
1781 if(!(bits & (bits-1))) // this ONLY holds for powers of two!
1790 for(f = 1; f <= bits; f *= 2)
1799 r = (r - 1) / (n - 1);
1806 float randombits(float bits, float k, float error_return)
1810 while(k > 0 && bits != r)
1812 r += randombit(bits - r);
1821 void randombit_test(float bits, float iter)
1825 LOG_INFO(ftos(randombit(bits)), "\n");
1830 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
1832 if(halflifedist > 0)
1833 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
1834 else if(halflifedist < 0)
1835 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
1841 .string aiment_classname;
1842 .float aiment_deadflag;
1843 void SetMovetypeFollow(entity ent, entity e)
1845 // FIXME this may not be warpzone aware
1846 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
1847 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.
1848 ent.aiment = e; // make the hole follow bmodel
1849 ent.punchangle = e.angles; // the original angles of bmodel
1850 ent.view_ofs = ent.origin - e.origin; // relative origin
1851 ent.v_angle = ent.angles - e.angles; // relative angles
1852 ent.aiment_classname = strzone(e.classname);
1853 ent.aiment_deadflag = e.deadflag;
1855 void UnsetMovetypeFollow(entity ent)
1857 ent.movetype = MOVETYPE_FLY;
1858 PROJECTILE_MAKETRIGGER(ent);
1861 float LostMovetypeFollow(entity ent)
1864 if(ent.movetype != MOVETYPE_FOLLOW)
1870 if(ent.aiment.classname != ent.aiment_classname)
1872 if(ent.aiment.deadflag != ent.aiment_deadflag)
1878 float isPushable(entity e)
1889 case "droppedweapon":
1890 case "keepawayball":
1891 case "nexball_basketball":
1892 case "nexball_football":
1894 case "bullet": // antilagged bullets can't hit this either
1897 if (e.projectiledeathtype)