1 var void remove(entity e);
\r
2 void objerror(string s);
\r
4 .vector dropped_origin;
\r
6 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag);
\r
7 void crosshair_trace(entity pl)
\r
9 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));
\r
11 void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag);
\r
12 void WarpZone_crosshair_trace(entity pl)
\r
14 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));
\r
17 void() spawnfunc_info_player_deathmatch; // needed for the other spawnpoints
\r
18 void() spawnpoint_use;
\r
19 float race_GetTime(float pos);
\r
20 string race_GetName(float pos);
\r
21 string race_PlaceName(float pos);
\r
22 string GetMapname();
\r
23 string ColoredTeamName(float t);
\r
25 string admin_name(void)
\r
27 if(cvar_string("sv_adminnick") != "")
\r
28 return cvar_string("sv_adminnick");
\r
30 return "SERVER ADMIN";
\r
33 float DistributeEvenly_amount;
\r
34 float DistributeEvenly_totalweight;
\r
35 void DistributeEvenly_Init(float amount, float totalweight)
\r
37 if (DistributeEvenly_amount)
\r
39 dprint("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
\r
40 dprint(ftos(DistributeEvenly_totalweight), " left!)\n");
\r
42 if (totalweight == 0)
\r
43 DistributeEvenly_amount = 0;
\r
45 DistributeEvenly_amount = amount;
\r
46 DistributeEvenly_totalweight = totalweight;
\r
48 float DistributeEvenly_Get(float weight)
\r
53 f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
\r
54 DistributeEvenly_totalweight -= weight;
\r
55 DistributeEvenly_amount -= f;
\r
59 #define move_out_of_solid(e) WarpZoneLib_MoveOutOfSolid(e)
\r
62 string STR_PLAYER = "player";
\r
63 string STR_SPECTATOR = "spectator";
\r
64 string STR_OBSERVER = "observer";
\r
67 #define FOR_EACH_CLIENT(v) for(v = world; (v = findflags(v, flags, FL_CLIENT)) != world; )
\r
68 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
\r
69 #define FOR_EACH_PLAYER(v) for(v = world; (v = find(v, classname, STR_PLAYER)) != world; )
\r
70 #define FOR_EACH_REALPLAYER(v) FOR_EACH_PLAYER(v) if(clienttype(v) == CLIENTTYPE_REAL)
\r
72 #define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); )
\r
73 #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(v.flags & FL_CLIENT)
\r
74 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
\r
75 #define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(v.classname == STR_PLAYER)
\r
76 #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(v.classname == STR_PLAYER)
\r
79 // copies a string to a tempstring (so one can strunzone it)
\r
80 string strcat1(string s) = #115; // FRIK_FILE
\r
85 string GetAdvancedDeathReports(entity enPlayer) // Extra fragmessage information
\r
87 local float nPlayerHealth = rint(enPlayer.health);
\r
88 local float nPlayerArmor = rint(enPlayer.armorvalue);
\r
89 local float nPlayerHandicap = enPlayer.cvar_cl_handicap;
\r
90 local float nPlayerPing = rint(enPlayer.ping);
\r
91 local string strPlayerPingColor;
\r
92 local string strMessage;
\r
93 if(nPlayerPing >= 150)
\r
94 strPlayerPingColor = "^1";
\r
96 strPlayerPingColor = "^2";
\r
98 if((cvar("sv_fragmessage_information_stats")) && (enPlayer.health >= 1))
\r
99 strMessage = strcat(strMessage, "\n^7(Health ^1", ftos(nPlayerHealth), "^7 / Armor ^2", ftos(nPlayerArmor), "^7)");
\r
101 if(cvar("sv_fragmessage_information_ping")) {
\r
102 if(clienttype(enPlayer) == CLIENTTYPE_BOT) // Bots have no ping
\r
103 strMessage = strcat(strMessage, "\n^7(^2Bot");
\r
105 strMessage = strcat(strMessage, "\n^7(Ping ", strPlayerPingColor, ftos(nPlayerPing), "ms");
\r
106 if(cvar("sv_fragmessage_information_handicap"))
\r
107 if(cvar("sv_fragmessage_information_handicap") == 2)
\r
108 if(nPlayerHandicap <= 1)
\r
109 strMessage = strcat(strMessage, "^7 / Handicap ^2Off^7)");
\r
111 strMessage = strcat(strMessage, "^7 / Handicap ^2", ftos(nPlayerHandicap), "^7)");
\r
112 else if not(nPlayerHandicap <= 1)
\r
113 strMessage = strcat(strMessage, "^7 / Handicap ^2", ftos(nPlayerHandicap), "^7)");
\r
115 strMessage = strcat(strMessage, "^7)");
\r
116 } else if(cvar("sv_fragmessage_information_handicap")) {
\r
117 if(cvar("sv_fragmessage_information_handicap") == 2)
\r
118 if(nPlayerHandicap <= 1)
\r
119 strMessage = strcat(strMessage, "\n^7(Handicap ^2Off^7)");
\r
121 strMessage = strcat(strMessage, "\n^7(Handicap ^2", ftos(nPlayerHandicap), "^7)");
\r
122 else if(nPlayerHandicap > 1)
\r
123 strMessage = strcat(strMessage, "\n^7(Handicap ^2", ftos(nPlayerHandicap), "^7)");
\r
127 void bcenterprint(string s)
\r
129 // TODO replace by MSG_ALL (would show it to spectators too, though)?
\r
131 FOR_EACH_PLAYER(head)
\r
132 if (clienttype(head) == CLIENTTYPE_REAL)
\r
133 centerprint(head, s);
\r
136 void GameLogEcho(string s)
\r
141 if (cvar("sv_eventlog_files"))
\r
145 logfile_open = TRUE;
\r
146 matches = cvar("sv_eventlog_files_counter") + 1;
\r
147 cvar_set("sv_eventlog_files_counter", ftos(matches));
\r
148 fn = ftos(matches);
\r
149 if (strlen(fn) < 8)
\r
150 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
\r
151 fn = strcat(cvar_string("sv_eventlog_files_nameprefix"), fn, cvar_string("sv_eventlog_files_namesuffix"));
\r
152 logfile = fopen(fn, FILE_APPEND);
\r
153 fputs(logfile, ":logversion:3\n");
\r
157 if (cvar("sv_eventlog_files_timestamps"))
\r
158 fputs(logfile, strcat(":time:", strftime(TRUE, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
\r
160 fputs(logfile, strcat(s, "\n"));
\r
163 if (cvar("sv_eventlog_console"))
\r
172 // will be opened later
\r
175 void GameLogClose()
\r
177 if (logfile_open && logfile >= 0)
\r
184 vector PL_VIEW_OFS;
\r
187 vector PL_CROUCH_VIEW_OFS;
\r
188 vector PL_CROUCH_MIN;
\r
189 vector PL_CROUCH_MAX;
\r
191 float spawnpoint_nag;
\r
192 void relocate_spawnpoint()
\r
194 PL_VIEW_OFS = stov(cvar_string("sv_player_viewoffset"));
\r
195 PL_MIN = stov(cvar_string("sv_player_mins"));
\r
196 PL_MAX = stov(cvar_string("sv_player_maxs"));
\r
197 PL_CROUCH_VIEW_OFS = stov(cvar_string("sv_player_crouch_viewoffset"));
\r
198 PL_CROUCH_MIN = stov(cvar_string("sv_player_crouch_mins"));
\r
199 PL_CROUCH_MAX = stov(cvar_string("sv_player_crouch_maxs"));
\r
201 // nudge off the floor
\r
202 setorigin(self, self.origin + '0 0 1');
\r
204 tracebox(self.origin, PL_MIN, PL_MAX, self.origin, TRUE, self);
\r
205 if (trace_startsolid)
\r
209 self.mins = PL_MIN;
\r
210 self.maxs = PL_MAX;
\r
211 if (!move_out_of_solid(self))
\r
212 objerror("could not get out of solid at all!");
\r
213 print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1'));
\r
214 print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x));
\r
215 print(" ", ftos(self.origin_y - o_y));
\r
216 print(" ", ftos(self.origin_z - o_z), "'\n");
\r
217 if (cvar("g_spawnpoints_auto_move_out_of_solid"))
\r
219 if (!spawnpoint_nag)
\r
220 print("\{1}^1NOTE: this map needs FIXING (it contains spawnpoints in solid, see server log)\n");
\r
221 spawnpoint_nag = 1;
\r
225 setorigin(self, o);
\r
226 self.mins = self.maxs = '0 0 0';
\r
227 objerror("player spawn point in solid, mapper sucks!\n");
\r
232 if (cvar("g_spawnpoints_autodrop"))
\r
234 setsize(self, PL_MIN, PL_MAX);
\r
238 self.use = spawnpoint_use;
\r
239 self.team_saved = self.team;
\r
243 if (have_team_spawns != 0)
\r
245 have_team_spawns = 1;
\r
247 if (cvar("r_showbboxes"))
\r
249 // show where spawnpoints point at too
\r
250 makevectors(self.angles);
\r
253 e.classname = "info_player_foo";
\r
254 setorigin(e, self.origin + v_forward * 24);
\r
255 setsize(e, '-8 -8 -8', '8 8 8');
\r
256 e.solid = SOLID_TRIGGER;
\r
260 #define strstr strstrofs
\r
262 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
\r
263 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
\r
264 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
\r
265 // BE CONSTANT OR strzoneD!
\r
266 float strstr(string haystack, string needle, float offset)
\r
270 len = strlen(needle);
\r
271 endpos = strlen(haystack) - len;
\r
272 while(offset <= endpos)
\r
274 found = substring(haystack, offset, len);
\r
275 if(found == needle)
\r
277 offset = offset + 1;
\r
283 float NUM_NEAREST_ENTITIES = 4;
\r
284 entity nearest_entity[NUM_NEAREST_ENTITIES];
\r
285 float nearest_length[NUM_NEAREST_ENTITIES];
\r
286 entity findnearest(vector point, .string field, string value, vector axismod)
\r
297 localhead = find(world, field, value);
\r
300 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
\r
301 dist = localhead.oldorigin;
\r
303 dist = localhead.origin;
\r
304 dist = dist - point;
\r
305 dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
\r
308 for (i = 0; i < num_nearest; ++i)
\r
310 if (len < nearest_length[i])
\r
314 // now i tells us where to insert at
\r
315 // INSERTION SORT! YOU'VE SEEN IT! RUN!
\r
316 if (i < NUM_NEAREST_ENTITIES)
\r
318 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
\r
320 nearest_length[j + 1] = nearest_length[j];
\r
321 nearest_entity[j + 1] = nearest_entity[j];
\r
323 nearest_length[i] = len;
\r
324 nearest_entity[i] = localhead;
\r
325 if (num_nearest < NUM_NEAREST_ENTITIES)
\r
326 num_nearest = num_nearest + 1;
\r
329 localhead = find(localhead, field, value);
\r
332 // now use the first one from our list that we can see
\r
333 for (i = 0; i < num_nearest; ++i)
\r
335 traceline(point, nearest_entity[i].origin, TRUE, world);
\r
336 if (trace_fraction == 1)
\r
340 dprint("Nearest point (");
\r
341 dprint(nearest_entity[0].netname);
\r
342 dprint(") is not visible, using a visible one.\n");
\r
344 return nearest_entity[i];
\r
348 if (num_nearest == 0)
\r
351 dprint("Not seeing any location point, using nearest as fallback.\n");
\r
353 dprint("Candidates were: ");
\r
354 for(j = 0; j < num_nearest; ++j)
\r
358 dprint(nearest_entity[j].netname);
\r
363 return nearest_entity[0];
\r
366 void spawnfunc_target_location()
\r
368 self.classname = "target_location";
\r
369 // location name in netname
\r
370 // eventually support: count, teamgame selectors, line of sight?
\r
373 void spawnfunc_info_location()
\r
375 self.classname = "target_location";
\r
376 self.message = self.netname;
\r
379 string NearestLocation(vector p)
\r
384 loc = findnearest(p, classname, "target_location", '1 1 1');
\r
391 loc = findnearest(p, target, "###item###", '1 1 4');
\r
398 string formatmessage(string msg)
\r
405 string replacement;
\r
409 WarpZone_crosshair_trace(self);
\r
410 cursor = trace_endpos;
\r
411 cursor_ent = trace_ent;
\r
415 break; // too many replacements
\r
418 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
\r
419 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
\r
432 replacement = substring(msg, p, 2);
\r
433 escape = substring(msg, p + 1, 1);
\r
437 else if (escape == "\\")
\r
438 replacement = "\\";
\r
439 else if (escape == "n")
\r
440 replacement = "\n";
\r
441 else if (escape == "a")
\r
442 replacement = ftos(floor(self.armorvalue));
\r
443 else if (escape == "h")
\r
444 replacement = ftos(floor(self.health));
\r
445 else if (escape == "l")
\r
446 replacement = NearestLocation(self.origin);
\r
447 else if (escape == "y")
\r
448 replacement = NearestLocation(cursor);
\r
449 else if (escape == "d")
\r
450 replacement = NearestLocation(self.death_origin);
\r
451 else if (escape == "w") {
\r
455 wep = self.switchweapon;
\r
458 replacement = W_Name(wep);
\r
459 } else if (escape == "W") {
\r
460 replacement = "batteries"; // ;)
\r
461 } else if (escape == "x") {
\r
462 replacement = cursor_ent.netname;
\r
463 if (!replacement || !cursor_ent)
\r
464 replacement = "nothing";
\r
465 } else if (escape == "p") {
\r
466 if (self.last_selected_player)
\r
467 replacement = self.last_selected_player.netname;
\r
469 replacement = "(nobody)";
\r
470 } else if (escape == "s")
\r
471 replacement = ftos(vlen(self.velocity - self.velocity_z * '0 0 1'));
\r
472 else if (escape == "S")
\r
473 replacement = ftos(vlen(self.velocity));
\r
474 else if (escape == "v") {
\r
475 float weapon_number;
\r
476 local entity stats;
\r
478 if(self.classname == "spectator")
\r
479 stats = self.enemy;
\r
483 weapon_number = stats.weapon;
\r
485 if (!weapon_number)
\r
486 weapon_number = stats.switchweapon;
\r
488 if (!weapon_number)
\r
489 weapon_number = stats.cnt;
\r
491 if(stats.cvar_cl_accuracy_data_share && stats.stats_fired[weapon_number - 1])
\r
492 replacement = ftos(bound(0, floor(100 * stats.stats_hit[weapon_number - 1] / stats.stats_fired[weapon_number - 1]), 100));
\r
494 replacement = "~"; // or something to indicate NULL, not available
\r
497 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
\r
498 p = p + strlen(replacement);
\r
503 float boolean(float value) { // if value is 0 return FALSE (0), otherwise return TRUE (1)
\r
504 return (value == 0) ? FALSE : TRUE;
\r
512 0: sends the request
\r
513 >0: receives a cvar from name=argv(f) value=argv(f+1)
\r
515 void GetCvars_handleString(string thisname, float f, .string field, string name)
\r
520 strunzone(self.field);
\r
521 self.field = string_null;
\r
525 if (thisname == name)
\r
528 strunzone(self.field);
\r
529 self.field = strzone(argv(f + 1));
\r
533 stuffcmd(self, strcat("sendcvar ", name, "\n"));
\r
535 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
\r
537 GetCvars_handleString(thisname, f, field, name);
\r
538 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
\r
539 if (thisname == name)
\r
542 s = func(strcat1(self.field));
\r
543 if (s != self.field)
\r
545 strunzone(self.field);
\r
546 self.field = strzone(s);
\r
550 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
\r
557 if (thisname == name)
\r
558 self.field = stof(argv(f + 1));
\r
561 stuffcmd(self, strcat("sendcvar ", name, "\n"));
\r
563 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
\r
570 if (thisname == name)
\r
574 self.field = stof(argv(f + 1));
\r
583 stuffcmd(self, strcat("sendcvar ", name, "\n"));
\r
586 string W_FixWeaponOrder_ForceComplete(string s);
\r
587 string W_FixWeaponOrder_AllowIncomplete(string s);
\r
588 float w_getbestweapon(entity e);
\r
589 void GetCvars(float f)
\r
593 s = strcat1(argv(f));
\r
594 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
\r
595 GetCvars_handleFloat(s, f, cvar_scr_centertime, "scr_centertime");
\r
596 GetCvars_handleFloat(s, f, cvar_cl_shownames, "cl_shownames");
\r
597 GetCvars_handleString(s, f, cvar_g_voretournamentversion, "g_voretournamentversion");
\r
598 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
\r
599 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete);
\r
600 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
\r
601 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
\r
602 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
\r
603 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
\r
604 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
\r
605 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
\r
606 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
\r
607 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
\r
608 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
\r
609 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
\r
610 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
\r
611 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
\r
612 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
\r
613 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
\r
614 GetCvars_handleFloat(s, f, cvar_cl_hitsound, "cl_hitsound");
\r
615 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
\r
616 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
\r
617 GetCvars_handleFloat(s, f, cvar_chase_active, "chase_active");
\r
619 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
\r
620 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
\r
622 #ifdef ALLOW_FORCEMODELS
\r
623 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
\r
624 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromvoretournament, "cl_forceplayermodelsfromvoretournament");
\r
626 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
\r
628 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
\r
631 if (s == "cl_weaponpriority")
\r
632 self.switchweapon = w_getbestweapon(self);
\r
636 float fexists(string f)
\r
639 fh = fopen(f, FILE_READ);
\r
646 void backtrace(string msg)
\r
649 dev = cvar("developer");
\r
650 war = cvar("prvm_backtraceforwarnings");
\r
651 cvar_set("developer", "1");
\r
652 cvar_set("prvm_backtraceforwarnings", "1");
\r
654 print("--- CUT HERE ---\nWARNING: ");
\r
657 remove(world); // isn't there any better way to cause a backtrace?
\r
658 print("\n--- CUT UNTIL HERE ---\n");
\r
659 cvar_set("developer", ftos(dev));
\r
660 cvar_set("prvm_backtraceforwarnings", ftos(war));
\r
663 string Team_ColorCode(float teamid)
\r
665 if (teamid == COLOR_TEAM1)
\r
667 else if (teamid == COLOR_TEAM2)
\r
669 else if (teamid == COLOR_TEAM3)
\r
671 else if (teamid == COLOR_TEAM4)
\r
677 string Team_ColorName(float t)
\r
679 // fixme: Search for team entities and get their .netname's!
\r
680 if (t == COLOR_TEAM1)
\r
682 if (t == COLOR_TEAM2)
\r
684 if (t == COLOR_TEAM3)
\r
686 if (t == COLOR_TEAM4)
\r
691 string Team_ColorNameLowerCase(float t)
\r
693 // fixme: Search for team entities and get their .netname's!
\r
694 if (t == COLOR_TEAM1)
\r
696 if (t == COLOR_TEAM2)
\r
698 if (t == COLOR_TEAM3)
\r
700 if (t == COLOR_TEAM4)
\r
705 float ColourToNumber(string team_colour)
\r
707 if (team_colour == "red")
\r
708 return COLOR_TEAM1;
\r
710 if (team_colour == "blue")
\r
711 return COLOR_TEAM2;
\r
713 if (team_colour == "yellow")
\r
714 return COLOR_TEAM3;
\r
716 if (team_colour == "pink")
\r
717 return COLOR_TEAM4;
\r
719 if (team_colour == "auto")
\r
725 float NumberToTeamNumber(float number)
\r
728 return COLOR_TEAM1;
\r
731 return COLOR_TEAM2;
\r
734 return COLOR_TEAM3;
\r
737 return COLOR_TEAM4;
\r
742 #define CENTERPRIO_POINT 1
\r
743 #define CENTERPRIO_SPAM 2
\r
744 #define CENTERPRIO_VOTE 4
\r
745 #define CENTERPRIO_NORMAL 5
\r
746 #define CENTERPRIO_SHIELDING 7
\r
747 #define CENTERPRIO_MAPVOTE 9
\r
748 #define CENTERPRIO_IDLEKICK 50
\r
749 #define CENTERPRIO_ADMIN 99
\r
750 .float centerprint_priority;
\r
751 .float centerprint_expires;
\r
752 void centerprint_atprio(entity e, float prio, string s)
\r
754 if (intermission_running)
\r
755 if (prio < CENTERPRIO_MAPVOTE)
\r
757 if (time > e.centerprint_expires)
\r
758 e.centerprint_priority = 0;
\r
759 if (prio >= e.centerprint_priority)
\r
761 e.centerprint_priority = prio;
\r
762 if (timeoutStatus == 2)
\r
763 e.centerprint_expires = time + (e.cvar_scr_centertime * TIMEOUT_SLOWMO_VALUE);
\r
765 e.centerprint_expires = time + e.cvar_scr_centertime;
\r
766 centerprint_builtin(e, s);
\r
769 void centerprint_expire(entity e, float prio)
\r
771 if (prio == e.centerprint_priority)
\r
773 e.centerprint_priority = 0;
\r
774 centerprint_builtin(e, "");
\r
777 void centerprint(entity e, string s)
\r
779 centerprint_atprio(e, CENTERPRIO_NORMAL, s);
\r
782 // decolorizes and team colors the player name when needed
\r
783 string playername(entity p)
\r
786 if (teams_matter && !intermission_running && p.classname == "player")
\r
788 t = Team_ColorCode(p.team);
\r
789 return strcat(t, strdecolorize(p.netname));
\r
795 vector randompos(vector m1, vector m2)
\r
799 v_x = m2_x * random() + m1_x;
\r
800 v_y = m2_y * random() + m1_y;
\r
801 v_z = m2_z * random() + m1_z;
\r
805 float g_pickup_fuel;
\r
806 float g_pickup_fuel_jetpack;
\r
807 float g_pickup_fuel_max;
\r
808 float g_pickup_armorsmall;
\r
809 float g_pickup_armorsmall_max;
\r
810 float g_pickup_armormedium;
\r
811 float g_pickup_armormedium_max;
\r
812 float g_pickup_armorbig;
\r
813 float g_pickup_armorbig_max;
\r
814 float g_pickup_armorlarge;
\r
815 float g_pickup_armorlarge_max;
\r
816 float g_pickup_healthsmall;
\r
817 float g_pickup_healthsmall_max;
\r
818 float g_pickup_healthmedium;
\r
819 float g_pickup_healthmedium_max;
\r
820 float g_pickup_healthlarge;
\r
821 float g_pickup_healthlarge_max;
\r
822 float g_pickup_healthmega;
\r
823 float g_pickup_healthmega_max;
\r
824 float g_weaponspeedfactor;
\r
825 float g_weaponratefactor;
\r
826 float g_weapondamagefactor;
\r
827 float g_weaponforcefactor;
\r
828 float g_weaponspreadfactor;
\r
830 float start_weapons;
\r
832 float start_ammo_fuel;
\r
833 float start_health;
\r
834 float start_armorvalue;
\r
835 float warmup_start_weapons;
\r
836 float warmup_start_ammo_fuel;
\r
837 float warmup_start_health;
\r
838 float warmup_start_armorvalue;
\r
839 float g_weapon_stay;
\r
840 float g_ghost_items;
\r
842 entity get_weaponinfo(float w);
\r
844 float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
\r
846 var float i = weaponinfo.weapon;
\r
851 var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
\r
853 if (t < 0) // "default" weapon selection
\r
854 t = (i == WEP_GRABBER);
\r
859 void readplayerstartcvars()
\r
864 // initialize starting values for players
\r
867 start_health = cvar("g_balance_health_start");
\r
868 start_armorvalue = cvar("g_balance_armor_start");
\r
872 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
\r
873 start_health = cvar("g_lms_start_health");
\r
874 start_armorvalue = cvar("g_lms_start_armor");
\r
876 else if (cvar("g_use_ammunition"))
\r
878 start_ammo_fuel = cvar("g_start_ammo_fuel");
\r
882 start_ammo_fuel = cvar("g_pickup_fuel_max");
\r
883 start_items |= IT_UNLIMITED_AMMO;
\r
886 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
\r
888 e = get_weaponinfo(i);
\r
889 if(want_weapon("g_start_weapon_", e, FALSE))
\r
891 start_weapons |= e.weapons;
\r
892 weapon_action(e.weapon, WR_PRECACHE);
\r
898 warmup_start_ammo_fuel = start_ammo_fuel;
\r
899 warmup_start_health = start_health;
\r
900 warmup_start_armorvalue = start_armorvalue;
\r
901 warmup_start_weapons = start_weapons;
\r
905 if (cvar("g_use_ammunition"))
\r
907 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
\r
909 warmup_start_health = cvar("g_warmup_start_health");
\r
910 warmup_start_armorvalue = cvar("g_warmup_start_armor");
\r
911 warmup_start_weapons = 0;
\r
912 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
\r
914 e = get_weaponinfo(i);
\r
915 if(want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns")))
\r
917 warmup_start_weapons |= e.weapons;
\r
918 weapon_action(e.weapon, WR_PRECACHE);
\r
926 start_items |= IT_FUEL_REGEN;
\r
927 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
\r
928 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
\r
932 start_items |= IT_JETPACK;
\r
934 if (g_weapon_stay == 2)
\r
936 if (!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
\r
937 if (!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
\r
940 start_ammo_fuel = max(0, start_ammo_fuel);
\r
942 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
\r
946 float g_bugrigs_planar_movement;
\r
947 float g_bugrigs_planar_movement_car_jumping;
\r
948 float g_bugrigs_reverse_spinning;
\r
949 float g_bugrigs_reverse_speeding;
\r
950 float g_bugrigs_reverse_stopping;
\r
951 float g_bugrigs_air_steering;
\r
952 float g_bugrigs_angle_smoothing;
\r
953 float g_bugrigs_friction_floor;
\r
954 float g_bugrigs_friction_brake;
\r
955 float g_bugrigs_friction_air;
\r
956 float g_bugrigs_accel;
\r
957 float g_bugrigs_speed_ref;
\r
958 float g_bugrigs_speed_pow;
\r
959 float g_bugrigs_steer;
\r
961 float g_touchexplode;
\r
962 float g_touchexplode_radius;
\r
963 float g_touchexplode_damage;
\r
964 float g_touchexplode_edgedamage;
\r
965 float g_touchexplode_force;
\r
967 float sv_autotaunt;
\r
970 float sv_pitch_min;
\r
971 float sv_pitch_max;
\r
972 float sv_pitch_fixyaw;
\r
974 float sv_accuracy_data_share;
\r
976 void readlevelcvars(void)
\r
978 g_bugrigs = cvar("g_bugrigs");
\r
979 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
\r
980 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
\r
981 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
\r
982 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
\r
983 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
\r
984 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
\r
985 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
\r
986 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
\r
987 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
\r
988 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
\r
989 g_bugrigs_accel = cvar("g_bugrigs_accel");
\r
990 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
\r
991 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
\r
992 g_bugrigs_steer = cvar("g_bugrigs_steer");
\r
994 g_touchexplode = cvar("g_touchexplode");
\r
995 g_touchexplode_radius = cvar("g_touchexplode_radius");
\r
996 g_touchexplode_damage = cvar("g_touchexplode_damage");
\r
997 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
\r
998 g_touchexplode_force = cvar("g_touchexplode_force");
\r
1000 #ifdef ALLOW_FORCEMODELS
\r
1001 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
\r
1004 sv_clones = cvar("sv_clones");
\r
1005 sv_gentle = cvar("sv_gentle");
\r
1006 sv_foginterval = cvar("sv_foginterval");
\r
1007 g_cloaked = cvar("g_cloaked");
\r
1008 g_jump_grunt = cvar("g_jump_grunt");
\r
1009 g_footsteps = cvar("g_footsteps");
\r
1010 g_jetpack = cvar("g_jetpack");
\r
1011 g_midair = cvar("g_midair");
\r
1012 g_norecoil = cvar("g_norecoil");
\r
1013 g_vampire = cvar("g_vampire");
\r
1014 g_bloodloss = cvar("g_bloodloss");
\r
1015 sv_maxidle = cvar("sv_maxidle");
\r
1016 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
\r
1017 sv_pogostick = cvar("sv_pogostick");
\r
1018 sv_doublejump = cvar("sv_doublejump");
\r
1019 g_ctf_reverse = cvar("g_ctf_reverse");
\r
1020 sv_autotaunt = cvar("sv_autotaunt");
\r
1021 sv_taunt = cvar("sv_taunt");
\r
1023 inWarmupStage = cvar("g_warmup");
\r
1024 g_warmup_limit = cvar("g_warmup_limit");
\r
1025 g_warmup_allguns = cvar("g_warmup_allguns");
\r
1026 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
\r
1028 if ((g_race && g_race_qualifying == 2) || g_arena || g_assault || cvar("g_campaign"))
\r
1029 inWarmupStage = 0; // these modes cannot work together, sorry
\r
1031 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
\r
1032 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
\r
1033 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
\r
1034 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
\r
1035 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
\r
1036 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
\r
1037 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
\r
1038 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
\r
1039 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
\r
1040 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
\r
1041 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
\r
1042 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
\r
1044 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
\r
1045 g_weaponratefactor = cvar("g_weaponratefactor");
\r
1046 g_weapondamagefactor = cvar("g_weapondamagefactor");
\r
1047 g_weaponforcefactor = cvar("g_weaponforcefactor");
\r
1048 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
\r
1050 g_pickup_fuel = cvar("g_pickup_fuel");
\r
1051 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
\r
1052 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
\r
1053 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
\r
1054 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
\r
1055 g_pickup_armormedium = cvar("g_pickup_armormedium");
\r
1056 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
\r
1057 g_pickup_armorbig = cvar("g_pickup_armorbig");
\r
1058 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
\r
1059 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
\r
1060 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
\r
1061 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
\r
1062 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
\r
1063 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
\r
1064 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
\r
1065 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
\r
1066 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
\r
1067 g_pickup_healthmega = cvar("g_pickup_healthmega");
\r
1068 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
\r
1070 g_weapon_stay = cvar("g_weapon_stay");
\r
1072 if (!g_weapon_stay && (cvar("deathmatch") == 2))
\r
1073 g_weapon_stay = 1;
\r
1075 g_ghost_items = cvar("g_ghost_items");
\r
1077 if(g_ghost_items >= 1)
\r
1078 g_ghost_items = 0.25; // default alpha value
\r
1080 if not(inWarmupStage && !g_ca)
\r
1081 game_starttime = cvar("g_start_delay");
\r
1083 sv_pitch_min = cvar("sv_pitch_min");
\r
1084 sv_pitch_max = cvar("sv_pitch_max");
\r
1085 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
\r
1087 sv_accuracy_data_share = boolean(cvar("sv_accuracy_data_share"));
\r
1089 readplayerstartcvars();
\r
1093 // TODO sound pack system
\r
1096 string precache_sound_builtin (string s) = #19;
\r
1097 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
\r
1098 string precache_sound(string s)
\r
1100 return precache_sound_builtin(strcat(soundpack, s));
\r
1102 void play2(entity e, string filename)
\r
1104 stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
\r
1106 void sound(entity e, float chan, string samp, float vol, float atten)
\r
1108 sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
\r
1112 // Sound functions
\r
1113 string precache_sound (string s) = #19;
\r
1114 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
\r
1115 float precache_sound_index (string s) = #19;
\r
1117 #define SND_VOLUME 1
\r
1118 #define SND_ATTENUATION 2
\r
1119 #define SND_LARGEENTITY 8
\r
1120 #define SND_LARGESOUND 16
\r
1122 float sound_allowed(float dest, entity e)
\r
1124 // sounds from world may always pass
\r
1127 if (e.classname == "body")
\r
1129 if (e.owner && e.owner != e)
\r
1134 // sounds to self may always pass
\r
1135 if (dest == MSG_ONE)
\r
1136 if (e == msg_entity)
\r
1138 // sounds by players can be removed
\r
1139 if (cvar("bot_sound_monopoly"))
\r
1140 if (clienttype(e) == CLIENTTYPE_REAL)
\r
1142 // anything else may pass
\r
1146 void sound(entity e, float chan, string samp, float vol, float atten)
\r
1148 if (!sound_allowed(MSG_BROADCAST, e))
\r
1150 sound_builtin(e, chan, samp, vol, atten);
\r
1152 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
\r
1156 if (!sound_allowed(dest, e))
\r
1159 entno = num_for_edict(e);
\r
1160 idx = precache_sound_index(samp);
\r
1165 atten = floor(atten * 64);
\r
1166 vol = floor(vol * 255);
\r
1169 sflags |= SND_VOLUME;
\r
1171 sflags |= SND_ATTENUATION;
\r
1172 if (entno >= 8192)
\r
1173 sflags |= SND_LARGEENTITY;
\r
1175 sflags |= SND_LARGESOUND;
\r
1177 WriteByte(dest, SVC_SOUND);
\r
1178 WriteByte(dest, sflags);
\r
1179 if (sflags & SND_VOLUME)
\r
1180 WriteByte(dest, vol);
\r
1181 if (sflags & SND_ATTENUATION)
\r
1182 WriteByte(dest, atten);
\r
1183 if (sflags & SND_LARGEENTITY)
\r
1185 WriteShort(dest, entno);
\r
1186 WriteByte(dest, chan);
\r
1190 WriteShort(dest, entno * 8 + chan);
\r
1192 if (sflags & SND_LARGESOUND)
\r
1193 WriteShort(dest, idx);
\r
1195 WriteByte(dest, idx);
\r
1197 WriteCoord(dest, o_x);
\r
1198 WriteCoord(dest, o_y);
\r
1199 WriteCoord(dest, o_z);
\r
1201 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
\r
1205 if (!sound_allowed(dest, e))
\r
1208 o = e.origin + 0.5 * (e.mins + e.maxs);
\r
1209 soundtoat(dest, e, o, chan, samp, vol, atten);
\r
1211 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
\r
1213 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
\r
1215 void stopsoundto(float dest, entity e, float chan)
\r
1219 if (!sound_allowed(dest, e))
\r
1222 entno = num_for_edict(e);
\r
1224 if (entno >= 8192)
\r
1226 float idx, sflags;
\r
1227 idx = precache_sound_index("misc/null.wav");
\r
1228 sflags = SND_LARGEENTITY;
\r
1230 sflags |= SND_LARGESOUND;
\r
1231 WriteByte(dest, SVC_SOUND);
\r
1232 WriteByte(dest, sflags);
\r
1233 WriteShort(dest, entno);
\r
1234 WriteByte(dest, chan);
\r
1235 if (sflags & SND_LARGESOUND)
\r
1236 WriteShort(dest, idx);
\r
1238 WriteByte(dest, idx);
\r
1239 WriteCoord(dest, e.origin_x);
\r
1240 WriteCoord(dest, e.origin_y);
\r
1241 WriteCoord(dest, e.origin_z);
\r
1245 WriteByte(dest, SVC_STOPSOUND);
\r
1246 WriteShort(dest, entno * 8 + chan);
\r
1249 void stopsound(entity e, float chan)
\r
1251 if (!sound_allowed(MSG_BROADCAST, e))
\r
1254 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
\r
1255 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
\r
1258 void play2(entity e, string filename)
\r
1260 //stuffcmd(e, strcat("play2 ", filename, "\n"));
\r
1262 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
\r
1265 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
\r
1267 float spamsound(entity e, float chan, string samp, float vol, float atten)
\r
1269 if (!sound_allowed(MSG_BROADCAST, e))
\r
1272 if (time > e.spamtime)
\r
1274 e.spamtime = time;
\r
1275 sound(e, chan, samp, vol, atten);
\r
1281 void play2team(float t, string filename)
\r
1283 local entity head;
\r
1285 if (cvar("bot_sound_monopoly"))
\r
1288 FOR_EACH_REALPLAYER(head)
\r
1290 if (head.team == t)
\r
1291 play2(head, filename);
\r
1295 void play2all(string samp)
\r
1297 if (cvar("bot_sound_monopoly"))
\r
1300 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
\r
1303 void PrecachePlayerSounds(string f);
\r
1304 void precache_all_models(string pattern)
\r
1306 float globhandle, i, n;
\r
1309 globhandle = search_begin(pattern, TRUE, FALSE);
\r
1310 if (globhandle < 0)
\r
1312 n = search_getsize(globhandle);
\r
1313 for (i = 0; i < n; ++i)
\r
1315 //print(search_getfilename(globhandle, i), "\n");
\r
1316 f = search_getfilename(globhandle, i);
\r
1317 precache_model(f);
\r
1318 PrecachePlayerSounds(strcat(f, ".sounds"));
\r
1320 search_end(globhandle);
\r
1325 // gamemode related things
\r
1326 precache_model ("models/misc/chatbubble.spr");
\r
1327 precache_model ("models/misc/teambubble.spr");
\r
1329 // used by the waypoint editor
\r
1330 precache_model ("models/rune.mdl");
\r
1332 #ifdef TTURRETS_ENABLED
\r
1333 if (cvar("g_turrets"))
\r
1334 turrets_precash();
\r
1337 // Precache all player models if desired
\r
1338 if (cvar("sv_precacheplayermodels"))
\r
1340 PrecachePlayerSounds("sound/player/default.sounds");
\r
1341 precache_all_models("models/player/*.zym");
\r
1342 precache_all_models("models/player/*.dpm");
\r
1343 precache_all_models("models/player/*.md3");
\r
1344 precache_all_models("models/player/*.psk");
\r
1345 //precache_model("models/player/vixen.zym");
\r
1348 if (cvar("sv_defaultcharacter"))
\r
1351 s = cvar_string("sv_defaultplayermodel_red");
\r
1354 precache_model(s);
\r
1355 PrecachePlayerSounds(strcat(s, ".sounds"));
\r
1357 s = cvar_string("sv_defaultplayermodel_blue");
\r
1360 precache_model(s);
\r
1361 PrecachePlayerSounds(strcat(s, ".sounds"));
\r
1363 s = cvar_string("sv_defaultplayermodel_yellow");
\r
1366 precache_model(s);
\r
1367 PrecachePlayerSounds(strcat(s, ".sounds"));
\r
1369 s = cvar_string("sv_defaultplayermodel_pink");
\r
1372 precache_model(s);
\r
1373 PrecachePlayerSounds(strcat(s, ".sounds"));
\r
1375 s = cvar_string("sv_defaultplayermodel");
\r
1378 precache_model(s);
\r
1379 PrecachePlayerSounds(strcat(s, ".sounds"));
\r
1385 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
\r
1386 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
\r
1389 // gore and miscellaneous sounds
\r
1390 //precache_sound ("misc/h2ohit.wav");
\r
1391 precache_model ("models/grabber.md3");
\r
1392 precache_sound ("misc/armorimpact.wav");
\r
1393 precache_sound ("misc/bodyimpact1.wav");
\r
1394 precache_sound ("misc/bodyimpact2.wav");
\r
1395 precache_sound ("misc/gib.wav");
\r
1396 precache_sound ("misc/gib_splat01.wav");
\r
1397 precache_sound ("misc/gib_splat02.wav");
\r
1398 precache_sound ("misc/gib_splat03.wav");
\r
1399 precache_sound ("misc/gib_splat04.wav");
\r
1400 precache_sound ("misc/hit.wav");
\r
1401 precache_sound ("misc/typehit.wav");
\r
1402 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
\r
1403 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
\r
1404 precache_sound ("misc/null.wav");
\r
1405 precache_sound ("misc/spawn.wav");
\r
1406 precache_sound ("misc/talk.wav");
\r
1407 precache_sound ("misc/teleport.wav");
\r
1408 precache_sound ("misc/poweroff.wav");
\r
1409 precache_sound ("player/lava.wav");
\r
1410 precache_sound ("player/slime.wav");
\r
1411 precache_sound ("player/digest.wav");
\r
1414 precache_sound ("misc/jetpack_fly.wav");
\r
1416 precache_model ("models/sprites/0.spr32");
\r
1417 precache_model ("models/sprites/1.spr32");
\r
1418 precache_model ("models/sprites/2.spr32");
\r
1419 precache_model ("models/sprites/3.spr32");
\r
1420 precache_model ("models/sprites/4.spr32");
\r
1421 precache_model ("models/sprites/5.spr32");
\r
1422 precache_model ("models/sprites/6.spr32");
\r
1423 precache_model ("models/sprites/7.spr32");
\r
1424 precache_model ("models/sprites/8.spr32");
\r
1425 precache_model ("models/sprites/9.spr32");
\r
1426 precache_model ("models/sprites/10.spr32");
\r
1428 // common weapon precaches
\r
1429 precache_sound ("weapons/weapon_switch.wav");
\r
1430 precache_sound ("weapons/weaponpickup.wav");
\r
1431 precache_sound ("weapons/unavailable.wav");
\r
1432 precache_sound ("weapons/grabber_fire.wav"); // grabber
\r
1433 precache_sound ("weapons/grabber_impact.wav"); // grabber
\r
1434 precache_sound ("weapons/stomachkick.ogg");
\r
1436 if (cvar("sv_precacheweapons"))
\r
1438 //precache weapon models/sounds
\r
1441 while (wep <= WEP_LAST)
\r
1443 weapon_action(wep, WR_PRECACHE);
\r
1448 precache_model("models/elaser.mdl");
\r
1449 precache_model("models/laser.mdl");
\r
1450 precache_model("models/ebomb.mdl");
\r
1453 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
\r
1455 if (!self.noise && self.music) // quake 3 uses the music field
\r
1456 self.noise = self.music;
\r
1458 // plays music for the level if there is any
\r
1461 precache_sound (self.noise);
\r
1462 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
\r
1467 // sorry, but using \ in macros breaks line numbers
\r
1468 #define WRITESPECTATABLE_MSG_ONE_VARNAME(varname,statement) entity varname; varname = msg_entity; FOR_EACH_REALCLIENT(msg_entity) if(msg_entity == varname || (msg_entity.classname == STR_SPECTATOR && msg_entity.enemy == varname)) statement msg_entity = varname
\r
1469 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
\r
1470 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
\r
1472 // WARNING: this kills the trace globals
\r
1473 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
\r
1474 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
\r
1476 #define INITPRIO_FIRST 0
\r
1477 #define INITPRIO_GAMETYPE 0
\r
1478 #define INITPRIO_GAMETYPE_FALLBACK 1
\r
1479 #define INITPRIO_CVARS 5
\r
1480 #define INITPRIO_FINDTARGET 10
\r
1481 #define INITPRIO_DROPTOFLOOR 20
\r
1482 #define INITPRIO_SETLOCATION 90
\r
1483 #define INITPRIO_LINKDOORS 91
\r
1484 #define INITPRIO_LAST 99
\r
1486 .void(void) initialize_entity;
\r
1487 .float initialize_entity_order;
\r
1488 .entity initialize_entity_next;
\r
1489 entity initialize_entity_first;
\r
1491 void make_safe_for_remove(entity e)
\r
1493 if (e.initialize_entity)
\r
1496 for (ent = initialize_entity_first; ent; )
\r
1498 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
\r
1500 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
\r
1501 // skip it in linked list
\r
1504 prev.initialize_entity_next = ent.initialize_entity_next;
\r
1505 ent = prev.initialize_entity_next;
\r
1509 initialize_entity_first = ent.initialize_entity_next;
\r
1510 ent = initialize_entity_first;
\r
1516 ent = ent.initialize_entity_next;
\r
1522 void objerror(string s)
\r
1524 make_safe_for_remove(self);
\r
1525 objerror_builtin(s);
\r
1528 void remove_unsafely(entity e)
\r
1530 remove_builtin(e);
\r
1533 void remove_safely(entity e)
\r
1535 make_safe_for_remove(e);
\r
1536 remove_builtin(e);
\r
1539 void InitializeEntity(entity e, void(void) func, float order)
\r
1543 if (!e || e.initialize_entity)
\r
1545 // make a proxy initializer entity
\r
1549 e.classname = "initialize_entity";
\r
1553 e.initialize_entity = func;
\r
1554 e.initialize_entity_order = order;
\r
1556 cur = initialize_entity_first;
\r
1559 if (!cur || cur.initialize_entity_order > order)
\r
1561 // insert between prev and cur
\r
1563 prev.initialize_entity_next = e;
\r
1565 initialize_entity_first = e;
\r
1566 e.initialize_entity_next = cur;
\r
1570 cur = cur.initialize_entity_next;
\r
1573 void InitializeEntitiesRun()
\r
1575 entity startoflist;
\r
1576 startoflist = initialize_entity_first;
\r
1577 initialize_entity_first = world;
\r
1578 for (self = startoflist; self; )
\r
1581 var void(void) func;
\r
1582 e = self.initialize_entity_next;
\r
1583 func = self.initialize_entity;
\r
1584 self.initialize_entity_order = 0;
\r
1585 self.initialize_entity = func_null;
\r
1586 self.initialize_entity_next = world;
\r
1587 if (self.classname == "initialize_entity")
\r
1590 e_old = self.enemy;
\r
1591 remove_builtin(self);
\r
1594 //dprint("Delayed initialization: ", self.classname, "\n");
\r
1600 .float uncustomizeentityforclient_set;
\r
1601 .void(void) uncustomizeentityforclient;
\r
1602 void(void) SUB_Nullpointer = #0;
\r
1603 void UncustomizeEntitiesRun()
\r
1607 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
\r
1608 self.uncustomizeentityforclient();
\r
1611 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
\r
1613 e.customizeentityforclient = customizer;
\r
1614 e.uncustomizeentityforclient = uncustomizer;
\r
1615 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
\r
1618 .float nottargeted;
\r
1619 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
\r
1621 void() SUB_Remove;
\r
1622 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
\r
1626 if (e.classname == "")
\r
1627 e.classname = "net_linked";
\r
1629 if (e.model == "" || self.modelindex == 0)
\r
1633 setmodel(e, "null");
\r
1634 setsize(e, mi, ma);
\r
1637 e.SendEntity = sendfunc;
\r
1638 e.SendFlags = 0xFFFFFF;
\r
1641 e.effects |= EF_NODEPTHTEST;
\r
1645 e.nextthink = time + dt;
\r
1646 e.think = SUB_Remove;
\r
1650 void adaptor_think2touch()
\r
1659 void adaptor_think2use()
\r
1664 activator = world;
\r
1671 // deferred dropping
\r
1672 void DropToFloor_Handler()
\r
1674 droptofloor_builtin();
\r
1675 self.dropped_origin = self.origin;
\r
1678 void droptofloor()
\r
1680 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
\r
1685 float trace_hits_box_a0, trace_hits_box_a1;
\r
1687 float trace_hits_box_1d(float end, float thmi, float thma)
\r
1691 // just check if x is in range
\r
1699 // do the trace with respect to x
\r
1700 // 0 -> end has to stay in thmi -> thma
\r
1701 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
\r
1702 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
\r
1703 if (trace_hits_box_a0 > trace_hits_box_a1)
\r
1709 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
\r
1714 // now it is a trace from 0 to end
\r
1716 trace_hits_box_a0 = 0;
\r
1717 trace_hits_box_a1 = 1;
\r
1719 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
\r
1721 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
\r
1723 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
\r
1729 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
\r
1731 return trace_hits_box(start, end, thmi - ma, thma - mi);
\r
1734 float SUB_NoImpactCheck()
\r
1736 // zero hitcontents = this is not the real impact, but either the
\r
1737 // mirror-impact of something hitting the projectile instead of the
\r
1738 // projectile hitting the something, or a touchareagrid one. Neither of
\r
1739 // these stop the projectile from moving, so...
\r
1740 if(trace_dphitcontents == 0)
\r
1742 dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
\r
1745 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
\r
1747 if (other == world && self.size != '0 0 0')
\r
1750 tic = self.velocity * sys_frametime;
\r
1751 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
\r
1752 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
\r
1753 if (trace_fraction >= 1)
\r
1755 dprint("Odd... did not hit...?\n");
\r
1757 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
\r
1759 dprint("Detected and prevented the sky-grapple bug.\n");
\r
1767 #define SUB_OwnerCheck() (other && (other == self.owner))
\r
1769 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
\r
1771 if(SUB_OwnerCheck())
\r
1773 if(SUB_NoImpactCheck())
\r
1778 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
\r
1779 UpdateCSQCProjectileNextFrame(self);
\r
1782 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
\r
1784 float MAX_IPBAN_URIS = 16;
\r
1786 float URI_GET_DISCARD = 0;
\r
1787 float URI_GET_IPBAN = 1;
\r
1788 float URI_GET_IPBAN_END = 16;
\r
1790 void URI_Get_Callback(float id, float status, string data)
\r
1792 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
\r
1794 dprint("\nEnd of data.\n");
\r
1796 if (id == URI_GET_DISCARD)
\r
1800 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
\r
1802 // online ban list
\r
1803 OnlineBanList_URI_Get_Callback(id, status, data);
\r
1807 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
\r
1811 void print_to(entity e, string s)
\r
1814 sprint(e, strcat(s, "\n"));
\r
1819 string getrecords(float page) // 50 records per page
\r
1833 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
\r
1835 if (MapInfo_Get_ByID(i))
\r
1837 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
\r
1840 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
\r
1841 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
\r
1849 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
\r
1851 if (MapInfo_Get_ByID(i))
\r
1853 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "time")));
\r
1856 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "netname"));
\r
1857 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
\r
1865 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
\r
1867 if (MapInfo_Get_ByID(i))
\r
1869 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "time")));
\r
1872 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "netname"));
\r
1873 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
\r
1879 MapInfo_ClearTemps();
\r
1881 if (s == "" && page == 0)
\r
1882 return "No records are available on this server.\n";
\r
1887 string getrankings()
\r
1898 map = GetMapname();
\r
1900 for (i = 1; i <= RANKINGS_CNT; ++i)
\r
1902 t = race_GetTime(i);
\r
1905 n = race_GetName(i);
\r
1906 p = race_PlaceName(i);
\r
1907 s = strcat(s, strpad(8, p), " ", strpad(-8, TIME_ENCODED_TOSTRING(t)), " ", n, "\n");
\r
1910 MapInfo_ClearTemps();
\r
1913 return strcat("No records are available for the map: ", map, "\n");
\r
1915 return strcat("Records for ", map, ":\n", s);
\r
1918 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
\r
1921 vector start, org, delta, end, enddown, mstart;
\r
1923 m = e.dphitcontentsmask;
\r
1924 e.dphitcontentsmask = goodcontents | badcontents;
\r
1927 delta = world.maxs - world.mins;
\r
1929 for (i = 0; i < attempts; ++i)
\r
1931 start_x = org_x + random() * delta_x;
\r
1932 start_y = org_y + random() * delta_y;
\r
1933 start_z = org_z + random() * delta_z;
\r
1935 // rule 1: start inside world bounds, and outside
\r
1936 // solid, and don't start from somewhere where you can
\r
1937 // fall down to evil
\r
1938 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
\r
1939 if (trace_fraction >= 1)
\r
1941 if (trace_startsolid)
\r
1943 if (trace_dphitcontents & badcontents)
\r
1945 if (trace_dphitq3surfaceflags & badsurfaceflags)
\r
1948 // rule 2: if we are too high, lower the point
\r
1949 if (trace_fraction * delta_z > maxaboveground)
\r
1950 start = trace_endpos + '0 0 1' * maxaboveground;
\r
1951 enddown = trace_endpos;
\r
1953 // rule 3: make sure we aren't outside the map. This only works
\r
1954 // for somewhat well formed maps. A good rule of thumb is that
\r
1955 // the map should have a convex outside hull.
\r
1956 // these can be traceLINES as we already verified the starting box
\r
1957 mstart = start + 0.5 * (e.mins + e.maxs);
\r
1958 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
\r
1959 if (trace_fraction >= 1)
\r
1961 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
\r
1962 if (trace_fraction >= 1)
\r
1964 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
\r
1965 if (trace_fraction >= 1)
\r
1967 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
\r
1968 if (trace_fraction >= 1)
\r
1970 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
\r
1971 if (trace_fraction >= 1)
\r
1974 // find a random vector to "look at"
\r
1975 end_x = org_x + random() * delta_x;
\r
1976 end_y = org_y + random() * delta_y;
\r
1977 end_z = org_z + random() * delta_z;
\r
1978 end = start + normalize(end - start) * vlen(delta);
\r
1980 // rule 4: start TO end must not be too short
\r
1981 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
\r
1982 if (trace_startsolid)
\r
1984 if (trace_fraction < minviewdistance / vlen(delta))
\r
1987 // rule 5: don't want to look at sky
\r
1988 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
\r
1991 // rule 6: we must not end up in trigger_hurt
\r
1992 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
\r
1994 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
\r
2001 e.dphitcontentsmask = m;
\r
2005 setorigin(e, start);
\r
2006 e.angles = vectoangles(end - start);
\r
2007 dprint("Needed ", ftos(i + 1), " attempts\n");
\r
2014 float zcurveparticles_effectno;
\r
2015 vector zcurveparticles_start;
\r
2016 float zcurveparticles_spd;
\r
2018 void endzcurveparticles()
\r
2020 if(zcurveparticles_effectno)
\r
2023 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
\r
2025 zcurveparticles_effectno = 0;
\r
2028 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
\r
2030 spd = bound(0, floor(spd / 16), 32767);
\r
2031 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
\r
2033 endzcurveparticles();
\r
2034 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
\r
2035 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
\r
2036 WriteShort(MSG_BROADCAST, effectno);
\r
2037 WriteCoord(MSG_BROADCAST, start_x);
\r
2038 WriteCoord(MSG_BROADCAST, start_y);
\r
2039 WriteCoord(MSG_BROADCAST, start_z);
\r
2040 zcurveparticles_effectno = effectno;
\r
2041 zcurveparticles_start = start;
\r
2044 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
\r
2045 WriteCoord(MSG_BROADCAST, end_x);
\r
2046 WriteCoord(MSG_BROADCAST, end_y);
\r
2047 WriteCoord(MSG_BROADCAST, end_z);
\r
2048 WriteCoord(MSG_BROADCAST, end_dz);
\r
2049 zcurveparticles_spd = spd;
\r
2052 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
\r
2055 vector vecxy, velxy;
\r
2057 vecxy = end - start;
\r
2062 if (vlen(velxy) < 0.000001 * fabs(vel_z))
\r
2064 endzcurveparticles();
\r
2065 trailparticles(world, effectno, start, end);
\r
2069 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
\r
2070 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
\r
2073 string GetGametype(); // g_world.qc
\r
2074 void write_recordmarker(entity pl, float tstart, float dt)
\r
2076 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
\r
2078 // also write a marker into demo files for demotc-race-record-extractor to find
\r
2081 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
\r
2082 " ", ftos(tstart), " ", ftos(dt), "\n"));
\r
2085 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter)
\r
2087 switch(self.owner.cvar_cl_gunalign)
\r
2098 if(allowcenter) // 2: allow center handedness
\r
2111 if(allowcenter) // 2: allow center handedness
\r
2127 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
\r
2132 if (cvar("g_shootfromeye"))
\r
2136 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
\r
2144 else if (cvar("g_shootfromcenter"))
\r
2148 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
\r
2156 else if ((s = cvar_string("g_shootfromfixedorigin")) != "")
\r
2166 else if (cvar("g_shootfromclient"))
\r
2168 vecs = shotorg_adjustfromclient(vecs, y_is_right, (cvar("g_shootfromclient") >= 2));
\r
2175 void attach_sameorigin(entity e, entity to, string tag)
\r
2177 vector org, t_forward, t_left, t_up, e_forward, e_up;
\r
2178 vector org0, ang0;
\r
2184 org = e.origin - gettaginfo(to, gettagindex(to, tag));
\r
2185 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
\r
2186 t_forward = v_forward * tagscale;
\r
2187 t_left = v_right * -tagscale;
\r
2188 t_up = v_up * tagscale;
\r
2190 e.origin_x = org * t_forward;
\r
2191 e.origin_y = org * t_left;
\r
2192 e.origin_z = org * t_up;
\r
2194 // current forward and up directions
\r
2195 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
\r
2196 e.angles = AnglesTransform_FromVAngles(e.angles);
\r
2198 e.angles = AnglesTransform_FromAngles(e.angles);
\r
2199 fixedmakevectors(e.angles);
\r
2201 // untransform forward, up!
\r
2202 e_forward_x = v_forward * t_forward;
\r
2203 e_forward_y = v_forward * t_left;
\r
2204 e_forward_z = v_forward * t_up;
\r
2205 e_up_x = v_up * t_forward;
\r
2206 e_up_y = v_up * t_left;
\r
2207 e_up_z = v_up * t_up;
\r
2209 e.angles = fixedvectoangles2(e_forward, e_up);
\r
2210 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
\r
2211 e.angles = AnglesTransform_ToVAngles(e.angles);
\r
2213 e.angles = AnglesTransform_ToAngles(e.angles);
\r
2215 setattachment(e, to, tag);
\r
2216 setorigin(e, e.origin);
\r
2219 void detach_sameorigin(entity e)
\r
2222 org = gettaginfo(e, 0);
\r
2223 e.angles = fixedvectoangles2(v_forward, v_up);
\r
2224 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
\r
2225 e.angles = AnglesTransform_ToVAngles(e.angles);
\r
2227 e.angles = AnglesTransform_ToAngles(e.angles);
\r
2228 setorigin(e, org);
\r
2229 setattachment(e, world, "");
\r
2230 setorigin(e, e.origin);
\r
2233 void follow_sameorigin(entity e, entity to)
\r
2235 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
\r
2236 e.aiment = to; // make the hole follow bmodel
\r
2237 e.punchangle = to.angles; // the original angles of bmodel
\r
2238 e.view_ofs = e.origin - to.origin; // relative origin
\r
2239 e.v_angle = e.angles - to.angles; // relative angles
\r
2242 void unfollow_sameorigin(entity e)
\r
2244 e.movetype = MOVETYPE_NONE;
\r
2247 entity gettaginfo_relative_ent;
\r
2248 vector gettaginfo_relative(entity e, float tag)
\r
2250 if (!gettaginfo_relative_ent)
\r
2252 gettaginfo_relative_ent = spawn();
\r
2253 gettaginfo_relative_ent.effects = EF_NODRAW;
\r
2255 gettaginfo_relative_ent.model = e.model;
\r
2256 gettaginfo_relative_ent.modelindex = e.modelindex;
\r
2257 gettaginfo_relative_ent.frame = e.frame;
\r
2258 return gettaginfo(gettaginfo_relative_ent, tag);
\r
2261 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
\r
2265 if (pl.soundentity.cnt & p)
\r
2267 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
\r
2268 pl.soundentity.cnt |= p;
\r
2271 void SoundEntity_StopSound(entity pl, float chan)
\r
2275 if (pl.soundentity.cnt & p)
\r
2277 stopsoundto(MSG_ALL, pl.soundentity, chan);
\r
2278 pl.soundentity.cnt &~= p;
\r
2282 void SoundEntity_Attach(entity pl)
\r
2284 pl.soundentity = spawn();
\r
2285 pl.soundentity.classname = "soundentity";
\r
2286 pl.soundentity.owner = pl;
\r
2287 setattachment(pl.soundentity, pl, "");
\r
2288 setmodel(pl.soundentity, "null");
\r
2291 void SoundEntity_Detach(entity pl)
\r
2294 for (i = 0; i <= 7; ++i)
\r
2295 SoundEntity_StopSound(pl, i);
\r
2299 float ParseCommandPlayerSlotTarget_firsttoken;
\r
2300 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
\r
2308 ParseCommandPlayerSlotTarget_firsttoken = -1;
\r
2312 if (substring(argv(idx), 0, 1) == "#")
\r
2314 s = substring(argv(idx), 1, -1);
\r
2322 ParseCommandPlayerSlotTarget_firsttoken = idx;
\r
2323 if (s == ftos(stof(s)))
\r
2325 e = edict_num(stof(s));
\r
2326 if (e.flags & FL_CLIENT)
\r
2332 // it must be a nick name
\r
2335 ParseCommandPlayerSlotTarget_firsttoken = idx;
\r
2338 FOR_EACH_CLIENT(head)
\r
2339 if (head.netname == s)
\r
2347 s = strdecolorize(s);
\r
2349 FOR_EACH_CLIENT(head)
\r
2350 if (strdecolorize(head.netname) == s)
\r
2365 float modeleffect_SendEntity(entity to, float sf)
\r
2368 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
\r
2371 if(self.velocity != '0 0 0')
\r
2373 if(self.angles != '0 0 0')
\r
2375 if(self.avelocity != '0 0 0')
\r
2378 WriteByte(MSG_ENTITY, f);
\r
2379 WriteShort(MSG_ENTITY, self.modelindex);
\r
2380 WriteByte(MSG_ENTITY, self.skin);
\r
2381 WriteByte(MSG_ENTITY, self.frame);
\r
2382 WriteCoord(MSG_ENTITY, self.origin_x);
\r
2383 WriteCoord(MSG_ENTITY, self.origin_y);
\r
2384 WriteCoord(MSG_ENTITY, self.origin_z);
\r
2387 WriteCoord(MSG_ENTITY, self.velocity_x);
\r
2388 WriteCoord(MSG_ENTITY, self.velocity_y);
\r
2389 WriteCoord(MSG_ENTITY, self.velocity_z);
\r
2393 WriteCoord(MSG_ENTITY, self.angles_x);
\r
2394 WriteCoord(MSG_ENTITY, self.angles_y);
\r
2395 WriteCoord(MSG_ENTITY, self.angles_z);
\r
2399 WriteCoord(MSG_ENTITY, self.avelocity_x);
\r
2400 WriteCoord(MSG_ENTITY, self.avelocity_y);
\r
2401 WriteCoord(MSG_ENTITY, self.avelocity_z);
\r
2403 WriteShort(MSG_ENTITY, self.scale * 256.0);
\r
2404 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
\r
2405 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
\r
2406 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
\r
2407 WriteByte(MSG_ENTITY, self.alpha * 255.0);
\r
2412 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)
\r
2417 e.classname = "modeleffect";
\r
2423 e.avelocity = angv;
\r
2425 e.teleport_time = t1;
\r
2429 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
\r
2433 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
\r
2436 sz = max(e.scale, e.scale2);
\r
2437 setsize(e, e.mins * sz, e.maxs * sz);
\r
2438 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
\r
2441 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
\r
2443 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
\r
2446 float randombit(float bits)
\r
2448 if not(bits & (bits-1)) // this ONLY holds for powers of two!
\r
2457 for(f = 1; f <= bits; f *= 2)
\r
2466 r = (r - 1) / (n - 1);
\r
2473 float randombits(float bits, float k, float error_return)
\r
2477 while(k > 0 && bits != r)
\r
2479 r += randombit(bits - r);
\r
2488 void randombit_test(float bits, float iter)
\r
2492 print(ftos(randombit(bits)), "\n");
\r
2497 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
\r
2499 if(halflifedist > 0)
\r
2500 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
\r
2501 else if(halflifedist < 0)
\r
2502 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
\r
2511 #define cvar_string_normal cvar_string_builtin
\r
2512 #define cvar_normal cvar_builtin
\r
2514 string cvar_string_normal(string n)
\r
2516 if not(cvar_type(n) & 1)
\r
2517 backtrace(strcat("Attempt to access undefined cvar: ", n));
\r
2518 return cvar_string_builtin(n);
\r
2521 float cvar_normal(string n)
\r
2523 return stof(cvar_string_normal(n));
\r
2526 #define cvar_set_normal cvar_set_builtin
\r
2528 void defer_think()
\r
2533 self = self.owner;
\r
2534 oself.think = SUB_Remove;
\r
2535 oself.nextthink = time;
\r
2541 Execute func() after time + fdelay.
\r
2542 self when func is executed = self when defer is called
\r
2544 void defer(float fdelay, void() func)
\r
2551 e.think = defer_think;
\r
2552 e.nextthink = time + fdelay;
\r