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");
551 MUTATOR_CALLHOOK(SetWeaponArena, s);
554 if (s == "0" || s == "")
560 // forcibly turn off weaponarena
562 else if (s == "all" || s == "1")
565 g_weaponarena_list = "All Weapons";
566 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
568 e = get_weaponinfo(j);
569 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
570 g_weaponarena_weapons |= WepSet_FromWeapon(j);
573 else if (s == "most")
576 g_weaponarena_list = "Most Weapons";
577 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
579 e = get_weaponinfo(j);
580 if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
581 if (e.spawnflags & WEP_FLAG_NORMAL)
582 g_weaponarena_weapons |= WepSet_FromWeapon(j);
585 else if (s == "none")
588 g_weaponarena_list = "No Weapons";
593 t = tokenize_console(s);
594 g_weaponarena_list = "";
595 for (i = 0; i < t; ++i)
598 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
600 e = get_weaponinfo(j);
603 g_weaponarena_weapons |= WepSet_FromWeapon(j);
604 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
610 LOG_INFO("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
613 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
617 g_weaponarena_random = cvar("g_weaponarena_random");
619 g_weaponarena_random = 0;
620 g_weaponarena_random_with_blaster = cvar("g_weaponarena_random_with_blaster");
624 g_weapon_stay = 0; // incompatible
625 start_weapons = g_weaponarena_weapons;
626 start_items |= IT_UNLIMITED_AMMO;
630 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
632 e = get_weaponinfo(i);
633 int w = want_weapon(e, false);
635 start_weapons |= WepSet_FromWeapon(i);
637 start_weapons_default |= WepSet_FromWeapon(i);
639 start_weapons_defaultmask |= WepSet_FromWeapon(i);
643 if(!cvar("g_use_ammunition"))
644 start_items |= IT_UNLIMITED_AMMO;
646 if(start_items & IT_UNLIMITED_WEAPON_AMMO)
648 start_ammo_shells = 999;
649 start_ammo_nails = 999;
650 start_ammo_rockets = 999;
651 start_ammo_cells = 999;
652 start_ammo_plasma = 999;
653 start_ammo_fuel = 999;
657 start_ammo_shells = cvar("g_start_ammo_shells");
658 start_ammo_nails = cvar("g_start_ammo_nails");
659 start_ammo_rockets = cvar("g_start_ammo_rockets");
660 start_ammo_cells = cvar("g_start_ammo_cells");
661 start_ammo_plasma = cvar("g_start_ammo_plasma");
662 start_ammo_fuel = cvar("g_start_ammo_fuel");
667 warmup_start_ammo_shells = start_ammo_shells;
668 warmup_start_ammo_nails = start_ammo_nails;
669 warmup_start_ammo_rockets = start_ammo_rockets;
670 warmup_start_ammo_cells = start_ammo_cells;
671 warmup_start_ammo_plasma = start_ammo_plasma;
672 warmup_start_ammo_fuel = start_ammo_fuel;
673 warmup_start_health = start_health;
674 warmup_start_armorvalue = start_armorvalue;
675 warmup_start_weapons = start_weapons;
676 warmup_start_weapons_default = start_weapons_default;
677 warmup_start_weapons_defaultmask = start_weapons_defaultmask;
679 if (!g_weaponarena && !g_ca && !g_freezetag)
681 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
682 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
683 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
684 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
685 warmup_start_ammo_plasma = cvar("g_warmup_start_ammo_plasma");
686 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
687 warmup_start_health = cvar("g_warmup_start_health");
688 warmup_start_armorvalue = cvar("g_warmup_start_armor");
689 warmup_start_weapons = '0 0 0';
690 warmup_start_weapons_default = '0 0 0';
691 warmup_start_weapons_defaultmask = '0 0 0';
692 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
694 e = get_weaponinfo(i);
695 int w = want_weapon(e, g_warmup_allguns);
697 warmup_start_weapons |= WepSet_FromWeapon(i);
699 warmup_start_weapons_default |= WepSet_FromWeapon(i);
701 warmup_start_weapons_defaultmask |= WepSet_FromWeapon(i);
707 start_items |= ITEM_Jetpack.m_itemid;
709 MUTATOR_CALLHOOK(SetStartItems);
711 if (start_items & ITEM_Jetpack.m_itemid)
713 start_items |= ITEM_JetpackRegen.m_itemid;
714 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
715 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
718 WepSet precache_weapons = start_weapons;
719 if (g_warmup_allguns != 1)
720 precache_weapons |= warmup_start_weapons;
721 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
723 e = get_weaponinfo(i);
724 if(precache_weapons & WepSet_FromWeapon(i)) {
725 Weapon w = get_weaponinfo(i);
730 start_ammo_shells = max(0, start_ammo_shells);
731 start_ammo_nails = max(0, start_ammo_nails);
732 start_ammo_rockets = max(0, start_ammo_rockets);
733 start_ammo_cells = max(0, start_ammo_cells);
734 start_ammo_plasma = max(0, start_ammo_plasma);
735 start_ammo_fuel = max(0, start_ammo_fuel);
737 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
738 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
739 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
740 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
741 warmup_start_ammo_plasma = max(0, warmup_start_ammo_plasma);
742 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
745 float sound_allowed(float destin, entity e)
747 // sounds from world may always pass
750 if (e.classname == "body")
752 else if (e.realowner && e.realowner != e)
754 else if (e.owner && e.owner != e)
759 // sounds to self may always pass
760 if (destin == MSG_ONE)
763 // sounds by players can be removed
764 if (autocvar_bot_sound_monopoly)
765 if (IS_REAL_CLIENT(e))
767 // anything else may pass
771 void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float attenu)
775 if (!sound_allowed(_dest, e))
778 entno = num_for_edict(e);
779 idx = precache_sound_index(samp);
784 attenu = floor(attenu * 64);
785 vol = floor(vol * 255);
788 sflags |= SND_VOLUME;
790 sflags |= SND_ATTENUATION;
791 if (entno >= 8192 || chan < 0 || chan > 7)
792 sflags |= SND_LARGEENTITY;
794 sflags |= SND_LARGESOUND;
796 WriteByte(_dest, SVC_SOUND);
797 WriteByte(_dest, sflags);
798 if (sflags & SND_VOLUME)
799 WriteByte(_dest, vol);
800 if (sflags & SND_ATTENUATION)
801 WriteByte(_dest, attenu);
802 if (sflags & SND_LARGEENTITY)
804 WriteShort(_dest, entno);
805 WriteByte(_dest, chan);
809 WriteShort(_dest, entno * 8 + chan);
811 if (sflags & SND_LARGESOUND)
812 WriteShort(_dest, idx);
814 WriteByte(_dest, idx);
816 WriteCoord(_dest, o.x);
817 WriteCoord(_dest, o.y);
818 WriteCoord(_dest, o.z);
820 void soundto(float _dest, entity e, float chan, string samp, float vol, float _atten)
824 if (!sound_allowed(_dest, e))
827 o = e.origin + 0.5 * (e.mins + e.maxs);
828 soundtoat(_dest, e, o, chan, samp, vol, _atten);
830 void soundat(entity e, vector o, float chan, string samp, float vol, float _atten)
832 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, _atten);
834 void stopsoundto(float _dest, entity e, float chan)
838 if (!sound_allowed(_dest, e))
841 entno = num_for_edict(e);
843 if (entno >= 8192 || chan < 0 || chan > 7)
846 idx = precache_sound_index(SND(Null));
847 sflags = SND_LARGEENTITY;
849 sflags |= SND_LARGESOUND;
850 WriteByte(_dest, SVC_SOUND);
851 WriteByte(_dest, sflags);
852 WriteShort(_dest, entno);
853 WriteByte(_dest, chan);
854 if (sflags & SND_LARGESOUND)
855 WriteShort(_dest, idx);
857 WriteByte(_dest, idx);
858 WriteCoord(_dest, e.origin.x);
859 WriteCoord(_dest, e.origin.y);
860 WriteCoord(_dest, e.origin.z);
864 WriteByte(_dest, SVC_STOPSOUND);
865 WriteShort(_dest, entno * 8 + chan);
868 void stopsound(entity e, float chan)
870 if (!sound_allowed(MSG_BROADCAST, e))
873 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
874 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
877 void play2(entity e, string filename)
879 //stuffcmd(e, strcat("play2 ", filename, "\n"));
881 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTEN_NONE);
884 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
886 float spamsound(entity e, float chan, string samp, float vol, float _atten)
888 if (!sound_allowed(MSG_BROADCAST, e))
891 if (time > e.spamtime)
894 _sound(e, chan, samp, vol, _atten);
900 void play2team(float t, string filename)
904 if (autocvar_bot_sound_monopoly)
907 FOR_EACH_REALPLAYER(head)
910 play2(head, filename);
914 void play2all(string samp)
916 if (autocvar_bot_sound_monopoly)
919 _sound(world, CH_INFO, samp, VOL_BASE, ATTEN_NONE);
922 void PrecachePlayerSounds(string f);
923 void precache_playermodel(string m)
925 float globhandle, i, n;
928 if(substring(m, -9,5) == "_lod1")
930 if(substring(m, -9,5) == "_lod2")
933 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
936 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
940 globhandle = search_begin(strcat(m, "_*.sounds"), true, false);
943 n = search_getsize(globhandle);
944 for (i = 0; i < n; ++i)
946 //print(search_getfilename(globhandle, i), "\n");
947 f = search_getfilename(globhandle, i);
948 PrecachePlayerSounds(f);
950 search_end(globhandle);
952 void precache_all_playermodels(string pattern)
954 float globhandle, i, n;
957 globhandle = search_begin(pattern, true, false);
960 n = search_getsize(globhandle);
961 for (i = 0; i < n; ++i)
963 //print(search_getfilename(globhandle, i), "\n");
964 f = search_getfilename(globhandle, i);
965 precache_playermodel(f);
967 search_end(globhandle);
970 void precache_playermodels(string s)
974 int n = tokenize_console(s);
975 precache_playermodel(argv(0));
977 for (int i = 1; i < n; ++i)
978 precache_model(argv(i));
984 // gamemode related things
986 // Precache all player models if desired
987 if (autocvar_sv_precacheplayermodels)
989 PrecachePlayerSounds("sound/player/default.sounds");
990 precache_all_playermodels("models/player/*.zym");
991 precache_all_playermodels("models/player/*.dpm");
992 precache_all_playermodels("models/player/*.md3");
993 precache_all_playermodels("models/player/*.psk");
994 precache_all_playermodels("models/player/*.iqm");
997 if (autocvar_sv_defaultcharacter)
999 precache_playermodels(autocvar_sv_defaultplayermodel_red);
1000 precache_playermodels(autocvar_sv_defaultplayermodel_blue);
1001 precache_playermodels(autocvar_sv_defaultplayermodel_yellow);
1002 precache_playermodels(autocvar_sv_defaultplayermodel_pink);
1003 precache_playermodels(autocvar_sv_defaultplayermodel);
1008 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1009 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1012 // gore and miscellaneous sounds
1013 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1014 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1017 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1019 if (!self.noise && self.music) // quake 3 uses the music field
1020 self.noise = self.music;
1022 // plays music for the level if there is any
1025 precache_sound (self.noise);
1026 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTEN_NONE);
1032 void make_safe_for_remove(entity e)
1034 if (e.initialize_entity)
1036 entity ent, prev = world;
1037 for (ent = initialize_entity_first; ent; )
1039 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1041 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1042 // skip it in linked list
1045 prev.initialize_entity_next = ent.initialize_entity_next;
1046 ent = prev.initialize_entity_next;
1050 initialize_entity_first = ent.initialize_entity_next;
1051 ent = initialize_entity_first;
1057 ent = ent.initialize_entity_next;
1063 void objerror(string s)
1065 make_safe_for_remove(self);
1066 builtin_objerror(s);
1069 .float remove_except_protected_forbidden;
1070 void remove_except_protected(entity e)
1072 if(e.remove_except_protected_forbidden)
1073 error("not allowed to remove this at this point");
1077 void remove_unsafely(entity e)
1079 if(e.classname == "spike")
1080 error("Removing spikes is forbidden (crylink bug), please report");
1084 void remove_safely(entity e)
1086 make_safe_for_remove(e);
1090 void InitializeEntity(entity e, void(void) func, float order)
1094 if (!e || e.initialize_entity)
1096 // make a proxy initializer entity
1098 e = new(initialize_entity);
1102 e.initialize_entity = func;
1103 e.initialize_entity_order = order;
1105 cur = initialize_entity_first;
1109 if (!cur || cur.initialize_entity_order > order)
1111 // insert between prev and cur
1113 prev.initialize_entity_next = e;
1115 initialize_entity_first = e;
1116 e.initialize_entity_next = cur;
1120 cur = cur.initialize_entity_next;
1123 void InitializeEntitiesRun()
1125 entity startoflist = initialize_entity_first;
1126 initialize_entity_first = NULL;
1127 remove = remove_except_protected;
1128 for (entity e = startoflist; e; e = e.initialize_entity_next)
1130 e.remove_except_protected_forbidden = 1;
1132 for (entity e = startoflist; e; )
1134 e.remove_except_protected_forbidden = 0;
1135 e.initialize_entity_order = 0;
1136 entity next = e.initialize_entity_next;
1137 e.initialize_entity_next = NULL;
1138 var void() func = e.initialize_entity;
1139 e.initialize_entity = func_null;
1140 if (e.classname == "initialize_entity")
1142 entity wrappee = e.enemy;
1146 //dprint("Delayed initialization: ", e.classname, "\n");
1149 WITH(entity, self, e, func());
1154 backtrace(strcat("Null function in: ", e.classname, "\n"));
1158 remove = remove_unsafely;
1161 .float(entity) isEliminated;
1162 bool EliminatedPlayers_SendEntity(entity this, entity to, float sendflags)
1166 WriteByte(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS);
1167 WriteByte(MSG_ENTITY, sendflags);
1171 for(i = 1; i <= maxclients; i += 8)
1173 for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e))
1175 if(eliminatedPlayers.isEliminated(e))
1178 WriteByte(MSG_ENTITY, f);
1185 void EliminatedPlayers_Init(float(entity) isEliminated_func)
1187 if(eliminatedPlayers)
1189 backtrace("Can't spawn eliminatedPlayers again!");
1192 Net_LinkEntity(eliminatedPlayers = spawn(), false, 0, EliminatedPlayers_SendEntity);
1193 eliminatedPlayers.isEliminated = isEliminated_func;
1197 void adaptor_think2touch()
1206 void adaptor_think2use()
1218 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1220 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
1221 self.projectiledeathtype |= HITTYPE_SPLASH;
1222 adaptor_think2use();
1225 // deferred dropping
1226 void DropToFloor_Handler()
1228 builtin_droptofloor();
1229 self.dropped_origin = self.origin;
1234 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1239 float trace_hits_box_a0, trace_hits_box_a1;
1241 float trace_hits_box_1d(float end, float thmi, float thma)
1245 // just check if x is in range
1253 // do the trace with respect to x
1254 // 0 -> end has to stay in thmi -> thma
1255 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1256 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1257 if (trace_hits_box_a0 > trace_hits_box_a1)
1263 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1268 // now it is a trace from 0 to end
1270 trace_hits_box_a0 = 0;
1271 trace_hits_box_a1 = 1;
1273 if (!trace_hits_box_1d(end.x, thmi.x, thma.x))
1275 if (!trace_hits_box_1d(end.y, thmi.y, thma.y))
1277 if (!trace_hits_box_1d(end.z, thmi.z, thma.z))
1283 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1285 return trace_hits_box(start, end, thmi - ma, thma - mi);
1288 float SUB_NoImpactCheck()
1290 // zero hitcontents = this is not the real impact, but either the
1291 // mirror-impact of something hitting the projectile instead of the
1292 // projectile hitting the something, or a touchareagrid one. Neither of
1293 // these stop the projectile from moving, so...
1294 if(trace_dphitcontents == 0)
1296 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1297 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));
1300 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1302 if (other == world && self.size != '0 0 0')
1305 tic = self.velocity * sys_frametime;
1306 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1307 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1308 if (trace_fraction >= 1)
1310 LOG_TRACE("Odd... did not hit...?\n");
1312 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1314 LOG_TRACE("Detected and prevented the sky-grapple bug.\n");
1322 #define SUB_OwnerCheck() (other && (other == self.owner))
1324 void W_Crylink_Dequeue(entity e);
1325 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1327 if(SUB_OwnerCheck())
1329 if(SUB_NoImpactCheck())
1331 if(self.classname == "nade")
1332 return false; // no checks here
1333 else if(self.classname == "grapplinghook")
1334 RemoveGrapplingHook(self.realowner);
1335 else if(self.classname == "spike")
1337 W_Crylink_Dequeue(self);
1344 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1345 UpdateCSQCProjectile(self);
1350 void URI_Get_Callback(float id, float status, string data)
1352 if(url_URI_Get_Callback(id, status, data))
1356 else if (id == URI_GET_DISCARD)
1360 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
1363 Curl_URI_Get_Callback(id, status, data);
1365 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1368 OnlineBanList_URI_Get_Callback(id, status, data);
1372 LOG_INFO("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1376 string uid2name(string myuid) {
1378 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
1380 // FIXME remove this later after 0.6 release
1381 // convert old style broken records to correct style
1384 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
1387 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
1388 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
1393 s = "^1Unregistered Player";
1397 float MoveToRandomLocationWithinBounds(entity e, vector boundmin, vector boundmax, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1400 vector start, org, delta, end, enddown, mstart;
1403 m = e.dphitcontentsmask;
1404 e.dphitcontentsmask = goodcontents | badcontents;
1407 delta = boundmax - boundmin;
1411 for (i = 0; i < attempts; ++i)
1413 start.x = org.x + random() * delta.x;
1414 start.y = org.y + random() * delta.y;
1415 start.z = org.z + random() * delta.z;
1417 // rule 1: start inside world bounds, and outside
1418 // solid, and don't start from somewhere where you can
1419 // fall down to evil
1420 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta.z, MOVE_NORMAL, e);
1421 if (trace_fraction >= 1)
1423 if (trace_startsolid)
1425 if (trace_dphitcontents & badcontents)
1427 if (trace_dphitq3surfaceflags & badsurfaceflags)
1430 // rule 2: if we are too high, lower the point
1431 if (trace_fraction * delta.z > maxaboveground)
1432 start = trace_endpos + '0 0 1' * maxaboveground;
1433 enddown = trace_endpos;
1435 // rule 3: make sure we aren't outside the map. This only works
1436 // for somewhat well formed maps. A good rule of thumb is that
1437 // the map should have a convex outside hull.
1438 // these can be traceLINES as we already verified the starting box
1439 mstart = start + 0.5 * (e.mins + e.maxs);
1440 traceline(mstart, mstart + '1 0 0' * delta.x, MOVE_NORMAL, e);
1441 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1443 traceline(mstart, mstart - '1 0 0' * delta.x, MOVE_NORMAL, e);
1444 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1446 traceline(mstart, mstart + '0 1 0' * delta.y, MOVE_NORMAL, e);
1447 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1449 traceline(mstart, mstart - '0 1 0' * delta.y, MOVE_NORMAL, e);
1450 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1452 traceline(mstart, mstart + '0 0 1' * delta.z, MOVE_NORMAL, e);
1453 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1456 // rule 4: we must "see" some spawnpoint or item
1457 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
1458 if(checkpvs(mstart, sp))
1459 if((traceline(mstart, sp.origin, MOVE_NORMAL, e), trace_fraction) >= 1)
1463 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
1464 if(checkpvs(mstart, sp))
1465 if((traceline(mstart, sp.origin + (sp.mins + sp.maxs) * 0.5, MOVE_NORMAL, e), trace_fraction) >= 1)
1471 // find a random vector to "look at"
1472 end.x = org.x + random() * delta.x;
1473 end.y = org.y + random() * delta.y;
1474 end.z = org.z + random() * delta.z;
1475 end = start + normalize(end - start) * vlen(delta);
1477 // rule 4: start TO end must not be too short
1478 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1479 if (trace_startsolid)
1481 if (trace_fraction < minviewdistance / vlen(delta))
1484 // rule 5: don't want to look at sky
1485 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
1488 // rule 6: we must not end up in trigger_hurt
1489 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
1495 e.dphitcontentsmask = m;
1499 setorigin(e, start);
1500 e.angles = vectoangles(end - start);
1501 LOG_TRACE("Needed ", ftos(i + 1), " attempts\n");
1508 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1510 return MoveToRandomLocationWithinBounds(e, world.mins, world.maxs, goodcontents, badcontents, badsurfaceflags, attempts, maxaboveground, minviewdistance);
1513 void write_recordmarker(entity pl, float tstart, float dt)
1515 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
1517 // also write a marker into demo files for demotc-race-record-extractor to find
1520 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
1521 " ", ftos(tstart), " ", ftos(dt), "\n"));
1524 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
1537 if(allowcenter) // 2: allow center handedness
1550 if(allowcenter) // 2: allow center handedness
1566 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
1571 if (autocvar_g_shootfromeye)
1575 if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
1576 else { vecs.y = 0; vecs.z -= 2; }
1584 else if (autocvar_g_shootfromcenter)
1589 else if ((s = autocvar_g_shootfromfixedorigin) != "")
1599 else if (autocvar_g_shootfromclient)
1601 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
1606 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
1608 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
1612 void attach_sameorigin(entity e, entity to, string tag)
1614 vector org, t_forward, t_left, t_up, e_forward, e_up;
1617 org = e.origin - gettaginfo(to, gettagindex(to, tag));
1618 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
1619 t_forward = v_forward * tagscale;
1620 t_left = v_right * -tagscale;
1621 t_up = v_up * tagscale;
1623 e.origin_x = org * t_forward;
1624 e.origin_y = org * t_left;
1625 e.origin_z = org * t_up;
1627 // current forward and up directions
1628 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1629 e.angles = AnglesTransform_FromVAngles(e.angles);
1631 e.angles = AnglesTransform_FromAngles(e.angles);
1632 fixedmakevectors(e.angles);
1634 // untransform forward, up!
1635 e_forward.x = v_forward * t_forward;
1636 e_forward.y = v_forward * t_left;
1637 e_forward.z = v_forward * t_up;
1638 e_up.x = v_up * t_forward;
1639 e_up.y = v_up * t_left;
1640 e_up.z = v_up * t_up;
1642 e.angles = fixedvectoangles2(e_forward, e_up);
1643 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1644 e.angles = AnglesTransform_ToVAngles(e.angles);
1646 e.angles = AnglesTransform_ToAngles(e.angles);
1648 setattachment(e, to, tag);
1649 setorigin(e, e.origin);
1652 void detach_sameorigin(entity e)
1655 org = gettaginfo(e, 0);
1656 e.angles = fixedvectoangles2(v_forward, v_up);
1657 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1658 e.angles = AnglesTransform_ToVAngles(e.angles);
1660 e.angles = AnglesTransform_ToAngles(e.angles);
1662 setattachment(e, world, "");
1663 setorigin(e, e.origin);
1666 void follow_sameorigin(entity e, entity to)
1668 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
1669 e.aiment = to; // make the hole follow bmodel
1670 e.punchangle = to.angles; // the original angles of bmodel
1671 e.view_ofs = e.origin - to.origin; // relative origin
1672 e.v_angle = e.angles - to.angles; // relative angles
1675 void unfollow_sameorigin(entity e)
1677 e.movetype = MOVETYPE_NONE;
1680 entity gettaginfo_relative_ent;
1681 vector gettaginfo_relative(entity e, float tag)
1683 if (!gettaginfo_relative_ent)
1685 gettaginfo_relative_ent = spawn();
1686 gettaginfo_relative_ent.effects = EF_NODRAW;
1688 gettaginfo_relative_ent.model = e.model;
1689 gettaginfo_relative_ent.modelindex = e.modelindex;
1690 gettaginfo_relative_ent.frame = e.frame;
1691 return gettaginfo(gettaginfo_relative_ent, tag);
1696 bool modeleffect_SendEntity(entity this, entity to, int sf)
1699 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
1702 if(self.velocity != '0 0 0')
1704 if(self.angles != '0 0 0')
1706 if(self.avelocity != '0 0 0')
1709 WriteByte(MSG_ENTITY, f);
1710 WriteShort(MSG_ENTITY, self.modelindex);
1711 WriteByte(MSG_ENTITY, self.skin);
1712 WriteByte(MSG_ENTITY, self.frame);
1713 WriteCoord(MSG_ENTITY, self.origin.x);
1714 WriteCoord(MSG_ENTITY, self.origin.y);
1715 WriteCoord(MSG_ENTITY, self.origin.z);
1718 WriteCoord(MSG_ENTITY, self.velocity.x);
1719 WriteCoord(MSG_ENTITY, self.velocity.y);
1720 WriteCoord(MSG_ENTITY, self.velocity.z);
1724 WriteCoord(MSG_ENTITY, self.angles.x);
1725 WriteCoord(MSG_ENTITY, self.angles.y);
1726 WriteCoord(MSG_ENTITY, self.angles.z);
1730 WriteCoord(MSG_ENTITY, self.avelocity.x);
1731 WriteCoord(MSG_ENTITY, self.avelocity.y);
1732 WriteCoord(MSG_ENTITY, self.avelocity.z);
1734 WriteShort(MSG_ENTITY, self.scale * 256.0);
1735 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
1736 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
1737 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
1738 WriteByte(MSG_ENTITY, self.alpha * 255.0);
1743 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)
1748 e.classname = "modeleffect";
1756 e.teleport_time = t1;
1760 e.scale = s0 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1764 e.scale2 = s2 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1767 sz = max(e.scale, e.scale2);
1768 setsize(e, e.mins * sz, e.maxs * sz);
1769 Net_LinkEntity(e, false, 0.1, modeleffect_SendEntity);
1772 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
1774 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
1777 float randombit(float bits)
1779 if(!(bits & (bits-1))) // this ONLY holds for powers of two!
1788 for(f = 1; f <= bits; f *= 2)
1797 r = (r - 1) / (n - 1);
1804 float randombits(float bits, float k, float error_return)
1808 while(k > 0 && bits != r)
1810 r += randombit(bits - r);
1819 void randombit_test(float bits, float iter)
1823 LOG_INFO(ftos(randombit(bits)), "\n");
1828 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
1830 if(halflifedist > 0)
1831 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
1832 else if(halflifedist < 0)
1833 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
1839 .string aiment_classname;
1840 .float aiment_deadflag;
1841 void SetMovetypeFollow(entity ent, entity e)
1843 // FIXME this may not be warpzone aware
1844 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
1845 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.
1846 ent.aiment = e; // make the hole follow bmodel
1847 ent.punchangle = e.angles; // the original angles of bmodel
1848 ent.view_ofs = ent.origin - e.origin; // relative origin
1849 ent.v_angle = ent.angles - e.angles; // relative angles
1850 ent.aiment_classname = strzone(e.classname);
1851 ent.aiment_deadflag = e.deadflag;
1853 void UnsetMovetypeFollow(entity ent)
1855 ent.movetype = MOVETYPE_FLY;
1856 PROJECTILE_MAKETRIGGER(ent);
1859 float LostMovetypeFollow(entity ent)
1862 if(ent.movetype != MOVETYPE_FOLLOW)
1868 if(ent.aiment.classname != ent.aiment_classname)
1870 if(ent.aiment.deadflag != ent.aiment_deadflag)
1876 float isPushable(entity e)
1887 case "droppedweapon":
1888 case "keepawayball":
1889 case "nexball_basketball":
1890 case "nexball_football":
1892 case "bullet": // antilagged bullets can't hit this either
1895 if (e.projectiledeathtype)