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
618 GetCvars_handleFloat(s, f, cvar_cl_vore_stomachmodel, "cl_vore_stomachmodel");
\r
619 GetCvars_handleFloat(s, f, cvar_cl_vore_cameraspeed, "cl_vore_cameraspeed");
\r
620 GetCvars_handleFloat(s, f, cvar_cl_vore_autodigest, "cl_vore_autodigest");
\r
622 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
\r
623 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
\r
625 #ifdef ALLOW_FORCEMODELS
\r
626 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
\r
627 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromvoretournament, "cl_forceplayermodelsfromvoretournament");
\r
629 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
\r
631 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
\r
634 if (s == "cl_weaponpriority")
\r
635 self.switchweapon = w_getbestweapon(self);
\r
639 float fexists(string f)
\r
642 fh = fopen(f, FILE_READ);
\r
649 void backtrace(string msg)
\r
652 dev = cvar("developer");
\r
653 war = cvar("prvm_backtraceforwarnings");
\r
654 cvar_set("developer", "1");
\r
655 cvar_set("prvm_backtraceforwarnings", "1");
\r
657 print("--- CUT HERE ---\nWARNING: ");
\r
660 remove(world); // isn't there any better way to cause a backtrace?
\r
661 print("\n--- CUT UNTIL HERE ---\n");
\r
662 cvar_set("developer", ftos(dev));
\r
663 cvar_set("prvm_backtraceforwarnings", ftos(war));
\r
666 string Team_ColorCode(float teamid)
\r
668 if (teamid == COLOR_TEAM1)
\r
670 else if (teamid == COLOR_TEAM2)
\r
672 else if (teamid == COLOR_TEAM3)
\r
674 else if (teamid == COLOR_TEAM4)
\r
680 string Team_ColorName(float t)
\r
682 // fixme: Search for team entities and get their .netname's!
\r
683 if (t == COLOR_TEAM1)
\r
685 if (t == COLOR_TEAM2)
\r
687 if (t == COLOR_TEAM3)
\r
689 if (t == COLOR_TEAM4)
\r
694 string Team_ColorNameLowerCase(float t)
\r
696 // fixme: Search for team entities and get their .netname's!
\r
697 if (t == COLOR_TEAM1)
\r
699 if (t == COLOR_TEAM2)
\r
701 if (t == COLOR_TEAM3)
\r
703 if (t == COLOR_TEAM4)
\r
708 float ColourToNumber(string team_colour)
\r
710 if (team_colour == "red")
\r
711 return COLOR_TEAM1;
\r
713 if (team_colour == "blue")
\r
714 return COLOR_TEAM2;
\r
716 if (team_colour == "yellow")
\r
717 return COLOR_TEAM3;
\r
719 if (team_colour == "pink")
\r
720 return COLOR_TEAM4;
\r
722 if (team_colour == "auto")
\r
728 float NumberToTeamNumber(float number)
\r
731 return COLOR_TEAM1;
\r
734 return COLOR_TEAM2;
\r
737 return COLOR_TEAM3;
\r
740 return COLOR_TEAM4;
\r
745 #define CENTERPRIO_POINT 1
\r
746 #define CENTERPRIO_SPAM 2
\r
747 #define CENTERPRIO_VOTE 4
\r
748 #define CENTERPRIO_NORMAL 5
\r
749 #define CENTERPRIO_SHIELDING 7
\r
750 #define CENTERPRIO_MAPVOTE 9
\r
751 #define CENTERPRIO_IDLEKICK 50
\r
752 #define CENTERPRIO_ADMIN 99
\r
753 .float centerprint_priority;
\r
754 .float centerprint_expires;
\r
755 void centerprint_atprio(entity e, float prio, string s)
\r
757 if (intermission_running)
\r
758 if (prio < CENTERPRIO_MAPVOTE)
\r
760 if (time > e.centerprint_expires)
\r
761 e.centerprint_priority = 0;
\r
762 if (prio >= e.centerprint_priority)
\r
764 e.centerprint_priority = prio;
\r
765 if (timeoutStatus == 2)
\r
766 e.centerprint_expires = time + (e.cvar_scr_centertime * TIMEOUT_SLOWMO_VALUE);
\r
768 e.centerprint_expires = time + e.cvar_scr_centertime;
\r
769 centerprint_builtin(e, s);
\r
772 void centerprint_expire(entity e, float prio)
\r
774 if (prio == e.centerprint_priority)
\r
776 e.centerprint_priority = 0;
\r
777 centerprint_builtin(e, "");
\r
780 void centerprint(entity e, string s)
\r
782 centerprint_atprio(e, CENTERPRIO_NORMAL, s);
\r
785 // decolorizes and team colors the player name when needed
\r
786 string playername(entity p)
\r
789 if (teams_matter && !intermission_running && p.classname == "player")
\r
791 t = Team_ColorCode(p.team);
\r
792 return strcat(t, strdecolorize(p.netname));
\r
798 vector randompos(vector m1, vector m2)
\r
802 v_x = m2_x * random() + m1_x;
\r
803 v_y = m2_y * random() + m1_y;
\r
804 v_z = m2_z * random() + m1_z;
\r
808 float g_pickup_fuel;
\r
809 float g_pickup_fuel_jetpack;
\r
810 float g_pickup_fuel_max;
\r
811 float g_pickup_armorsmall;
\r
812 float g_pickup_armorsmall_max;
\r
813 float g_pickup_armormedium;
\r
814 float g_pickup_armormedium_max;
\r
815 float g_pickup_armorbig;
\r
816 float g_pickup_armorbig_max;
\r
817 float g_pickup_armorlarge;
\r
818 float g_pickup_armorlarge_max;
\r
819 float g_pickup_healthsmall;
\r
820 float g_pickup_healthsmall_max;
\r
821 float g_pickup_healthmedium;
\r
822 float g_pickup_healthmedium_max;
\r
823 float g_pickup_healthlarge;
\r
824 float g_pickup_healthlarge_max;
\r
825 float g_pickup_healthmega;
\r
826 float g_pickup_healthmega_max;
\r
827 float g_weaponspeedfactor;
\r
828 float g_weaponratefactor;
\r
829 float g_weapondamagefactor;
\r
830 float g_weaponforcefactor;
\r
831 float g_weaponspreadfactor;
\r
833 float start_weapons;
\r
835 float start_ammo_fuel;
\r
836 float start_health;
\r
837 float start_armorvalue;
\r
838 float warmup_start_weapons;
\r
839 float warmup_start_ammo_fuel;
\r
840 float warmup_start_health;
\r
841 float warmup_start_armorvalue;
\r
842 float g_weapon_stay;
\r
843 float g_ghost_items;
\r
845 entity get_weaponinfo(float w);
\r
847 float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
\r
849 var float i = weaponinfo.weapon;
\r
854 var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
\r
856 if (t < 0) // "default" weapon selection
\r
858 if(g_rpg) // no start weapons in RPG by default
\r
861 t = (i == WEP_GRABBER);
\r
867 void readplayerstartcvars()
\r
872 // initialize starting values for players
\r
875 start_health = cvar("g_balance_health_start");
\r
876 start_armorvalue = cvar("g_balance_armor_start");
\r
880 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
\r
881 start_health = cvar("g_lms_start_health");
\r
882 start_armorvalue = cvar("g_lms_start_armor");
\r
884 else if (cvar("g_use_ammunition"))
\r
886 start_ammo_fuel = cvar("g_start_ammo_fuel");
\r
890 start_ammo_fuel = cvar("g_pickup_fuel_max");
\r
891 start_items |= IT_UNLIMITED_AMMO;
\r
894 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
\r
896 e = get_weaponinfo(i);
\r
897 if(want_weapon("g_start_weapon_", e, FALSE))
\r
899 start_weapons |= e.weapons;
\r
900 weapon_action(e.weapon, WR_PRECACHE);
\r
906 warmup_start_ammo_fuel = start_ammo_fuel;
\r
907 warmup_start_health = start_health;
\r
908 warmup_start_armorvalue = start_armorvalue;
\r
909 warmup_start_weapons = start_weapons;
\r
911 if (cvar("g_use_ammunition"))
\r
913 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
\r
915 warmup_start_health = cvar("g_warmup_start_health");
\r
916 warmup_start_armorvalue = cvar("g_warmup_start_armor");
\r
917 warmup_start_weapons = 0;
\r
918 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
\r
920 e = get_weaponinfo(i);
\r
921 if(want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns")))
\r
923 warmup_start_weapons |= e.weapons;
\r
924 weapon_action(e.weapon, WR_PRECACHE);
\r
931 start_items |= IT_FUEL_REGEN;
\r
932 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
\r
933 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
\r
937 start_items |= IT_JETPACK;
\r
939 if (g_weapon_stay == 2)
\r
941 if (!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
\r
942 if (!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
\r
945 start_ammo_fuel = max(0, start_ammo_fuel);
\r
947 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
\r
951 float g_bugrigs_planar_movement;
\r
952 float g_bugrigs_planar_movement_car_jumping;
\r
953 float g_bugrigs_reverse_spinning;
\r
954 float g_bugrigs_reverse_speeding;
\r
955 float g_bugrigs_reverse_stopping;
\r
956 float g_bugrigs_air_steering;
\r
957 float g_bugrigs_angle_smoothing;
\r
958 float g_bugrigs_friction_floor;
\r
959 float g_bugrigs_friction_brake;
\r
960 float g_bugrigs_friction_air;
\r
961 float g_bugrigs_accel;
\r
962 float g_bugrigs_speed_ref;
\r
963 float g_bugrigs_speed_pow;
\r
964 float g_bugrigs_steer;
\r
966 float g_touchexplode;
\r
967 float g_touchexplode_radius;
\r
968 float g_touchexplode_damage;
\r
969 float g_touchexplode_edgedamage;
\r
970 float g_touchexplode_force;
\r
972 float sv_autotaunt;
\r
975 float sv_pitch_min;
\r
976 float sv_pitch_max;
\r
977 float sv_pitch_fixyaw;
\r
979 float sv_accuracy_data_share;
\r
981 void readlevelcvars(void)
\r
983 g_bugrigs = cvar("g_bugrigs");
\r
984 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
\r
985 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
\r
986 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
\r
987 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
\r
988 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
\r
989 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
\r
990 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
\r
991 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
\r
992 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
\r
993 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
\r
994 g_bugrigs_accel = cvar("g_bugrigs_accel");
\r
995 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
\r
996 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
\r
997 g_bugrigs_steer = cvar("g_bugrigs_steer");
\r
999 g_touchexplode = cvar("g_touchexplode");
\r
1000 g_touchexplode_radius = cvar("g_touchexplode_radius");
\r
1001 g_touchexplode_damage = cvar("g_touchexplode_damage");
\r
1002 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
\r
1003 g_touchexplode_force = cvar("g_touchexplode_force");
\r
1005 #ifdef ALLOW_FORCEMODELS
\r
1006 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
\r
1009 sv_clones = cvar("sv_clones");
\r
1010 sv_gentle = cvar("sv_gentle");
\r
1011 sv_foginterval = cvar("sv_foginterval");
\r
1012 g_cloaked = cvar("g_cloaked");
\r
1013 g_jump_grunt = cvar("g_jump_grunt");
\r
1014 g_footsteps = cvar("g_footsteps");
\r
1015 g_jetpack = cvar("g_jetpack");
\r
1016 g_midair = cvar("g_midair");
\r
1017 g_norecoil = cvar("g_norecoil");
\r
1018 g_vampire = cvar("g_vampire");
\r
1019 g_bloodloss = cvar("g_bloodloss");
\r
1020 sv_maxidle = cvar("sv_maxidle");
\r
1021 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
\r
1022 sv_pogostick = cvar("sv_pogostick");
\r
1023 sv_doublejump = cvar("sv_doublejump");
\r
1024 g_ctf_reverse = cvar("g_ctf_reverse");
\r
1025 sv_autotaunt = cvar("sv_autotaunt");
\r
1026 sv_taunt = cvar("sv_taunt");
\r
1028 inWarmupStage = cvar("g_warmup");
\r
1029 g_warmup_limit = cvar("g_warmup_limit");
\r
1030 g_warmup_allguns = cvar("g_warmup_allguns");
\r
1031 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
\r
1033 if ((g_race && g_race_qualifying == 2) || g_arena || g_assault || cvar("g_campaign"))
\r
1034 inWarmupStage = 0; // these modes cannot work together, sorry
\r
1036 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
\r
1037 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
\r
1038 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
\r
1039 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
\r
1040 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
\r
1041 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
\r
1042 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
\r
1043 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
\r
1044 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
\r
1045 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
\r
1046 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
\r
1047 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
\r
1049 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
\r
1050 g_weaponratefactor = cvar("g_weaponratefactor");
\r
1051 g_weapondamagefactor = cvar("g_weapondamagefactor");
\r
1052 g_weaponforcefactor = cvar("g_weaponforcefactor");
\r
1053 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
\r
1055 g_pickup_fuel = cvar("g_pickup_fuel");
\r
1056 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
\r
1057 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
\r
1058 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
\r
1059 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
\r
1060 g_pickup_armormedium = cvar("g_pickup_armormedium");
\r
1061 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
\r
1062 g_pickup_armorbig = cvar("g_pickup_armorbig");
\r
1063 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
\r
1064 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
\r
1065 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
\r
1066 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
\r
1067 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
\r
1068 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
\r
1069 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
\r
1070 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
\r
1071 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
\r
1072 g_pickup_healthmega = cvar("g_pickup_healthmega");
\r
1073 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
\r
1075 g_weapon_stay = cvar("g_weapon_stay");
\r
1077 if (!g_weapon_stay && (cvar("deathmatch") == 2))
\r
1078 g_weapon_stay = 1;
\r
1080 g_ghost_items = cvar("g_ghost_items");
\r
1082 if(g_ghost_items >= 1)
\r
1083 g_ghost_items = 0.25; // default alpha value
\r
1085 if not(inWarmupStage && !g_ca)
\r
1086 game_starttime = cvar("g_start_delay");
\r
1088 sv_pitch_min = cvar("sv_pitch_min");
\r
1089 sv_pitch_max = cvar("sv_pitch_max");
\r
1090 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
\r
1092 sv_accuracy_data_share = boolean(cvar("sv_accuracy_data_share"));
\r
1094 readplayerstartcvars();
\r
1098 // TODO sound pack system
\r
1101 string precache_sound_builtin (string s) = #19;
\r
1102 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
\r
1103 string precache_sound(string s)
\r
1105 return precache_sound_builtin(strcat(soundpack, s));
\r
1107 void play2(entity e, string filename)
\r
1109 stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
\r
1111 void sound(entity e, float chan, string samp, float vol, float atten)
\r
1113 sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
\r
1117 // Sound functions
\r
1118 string precache_sound (string s) = #19;
\r
1119 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
\r
1120 float precache_sound_index (string s) = #19;
\r
1122 #define SND_VOLUME 1
\r
1123 #define SND_ATTENUATION 2
\r
1124 #define SND_LARGEENTITY 8
\r
1125 #define SND_LARGESOUND 16
\r
1127 float sound_allowed(float dest, entity e)
\r
1129 // sounds from world may always pass
\r
1132 if (e.classname == "body")
\r
1134 if (e.owner && e.owner != e)
\r
1139 // sounds to self may always pass
\r
1140 if (dest == MSG_ONE)
\r
1141 if (e == msg_entity)
\r
1143 // sounds by players can be removed
\r
1144 if (cvar("bot_sound_monopoly"))
\r
1145 if (clienttype(e) == CLIENTTYPE_REAL)
\r
1147 // anything else may pass
\r
1151 void sound(entity e, float chan, string samp, float vol, float atten)
\r
1153 if (!sound_allowed(MSG_BROADCAST, e))
\r
1155 sound_builtin(e, chan, samp, vol, atten);
\r
1157 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
\r
1161 if (!sound_allowed(dest, e))
\r
1164 entno = num_for_edict(e);
\r
1165 idx = precache_sound_index(samp);
\r
1170 atten = floor(atten * 64);
\r
1171 vol = floor(vol * 255);
\r
1174 sflags |= SND_VOLUME;
\r
1176 sflags |= SND_ATTENUATION;
\r
1177 if (entno >= 8192)
\r
1178 sflags |= SND_LARGEENTITY;
\r
1180 sflags |= SND_LARGESOUND;
\r
1182 WriteByte(dest, SVC_SOUND);
\r
1183 WriteByte(dest, sflags);
\r
1184 if (sflags & SND_VOLUME)
\r
1185 WriteByte(dest, vol);
\r
1186 if (sflags & SND_ATTENUATION)
\r
1187 WriteByte(dest, atten);
\r
1188 if (sflags & SND_LARGEENTITY)
\r
1190 WriteShort(dest, entno);
\r
1191 WriteByte(dest, chan);
\r
1195 WriteShort(dest, entno * 8 + chan);
\r
1197 if (sflags & SND_LARGESOUND)
\r
1198 WriteShort(dest, idx);
\r
1200 WriteByte(dest, idx);
\r
1202 WriteCoord(dest, o_x);
\r
1203 WriteCoord(dest, o_y);
\r
1204 WriteCoord(dest, o_z);
\r
1206 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
\r
1210 if (!sound_allowed(dest, e))
\r
1213 o = e.origin + 0.5 * (e.mins + e.maxs);
\r
1214 soundtoat(dest, e, o, chan, samp, vol, atten);
\r
1216 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
\r
1218 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
\r
1220 void stopsoundto(float dest, entity e, float chan)
\r
1224 if (!sound_allowed(dest, e))
\r
1227 entno = num_for_edict(e);
\r
1229 if (entno >= 8192)
\r
1231 float idx, sflags;
\r
1232 idx = precache_sound_index("misc/null.wav");
\r
1233 sflags = SND_LARGEENTITY;
\r
1235 sflags |= SND_LARGESOUND;
\r
1236 WriteByte(dest, SVC_SOUND);
\r
1237 WriteByte(dest, sflags);
\r
1238 WriteShort(dest, entno);
\r
1239 WriteByte(dest, chan);
\r
1240 if (sflags & SND_LARGESOUND)
\r
1241 WriteShort(dest, idx);
\r
1243 WriteByte(dest, idx);
\r
1244 WriteCoord(dest, e.origin_x);
\r
1245 WriteCoord(dest, e.origin_y);
\r
1246 WriteCoord(dest, e.origin_z);
\r
1250 WriteByte(dest, SVC_STOPSOUND);
\r
1251 WriteShort(dest, entno * 8 + chan);
\r
1254 void stopsound(entity e, float chan)
\r
1256 if (!sound_allowed(MSG_BROADCAST, e))
\r
1259 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
\r
1260 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
\r
1263 void play2(entity e, string filename)
\r
1265 //stuffcmd(e, strcat("play2 ", filename, "\n"));
\r
1266 if (clienttype(e) == CLIENTTYPE_REAL)
\r
1269 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
\r
1273 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
\r
1275 float spamsound(entity e, float chan, string samp, float vol, float atten)
\r
1277 if (!sound_allowed(MSG_BROADCAST, e))
\r
1280 if (time > e.spamtime)
\r
1282 e.spamtime = time;
\r
1283 sound(e, chan, samp, vol, atten);
\r
1289 void play2team(float t, string filename)
\r
1291 local entity head;
\r
1293 if (cvar("bot_sound_monopoly"))
\r
1296 FOR_EACH_REALPLAYER(head)
\r
1298 if (head.team == t)
\r
1299 play2(head, filename);
\r
1303 void play2all(string samp)
\r
1305 if (cvar("bot_sound_monopoly"))
\r
1308 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
\r
1311 void PrecachePlayerSounds(string f);
\r
1312 void precache_all_models(string pattern)
\r
1314 float globhandle, i, n;
\r
1317 globhandle = search_begin(pattern, TRUE, FALSE);
\r
1318 if (globhandle < 0)
\r
1320 n = search_getsize(globhandle);
\r
1321 for (i = 0; i < n; ++i)
\r
1323 //print(search_getfilename(globhandle, i), "\n");
\r
1324 f = search_getfilename(globhandle, i);
\r
1325 precache_model(f);
\r
1326 PrecachePlayerSounds(strcat(f, ".sounds"));
\r
1328 search_end(globhandle);
\r
1333 // gamemode related things
\r
1334 precache_model ("models/misc/chatbubble.spr");
\r
1335 precache_model ("models/misc/teambubble.spr");
\r
1336 precache_model ("models/misc/teambubbleheal.spr");
\r
1338 // used by the waypoint editor
\r
1339 precache_model ("models/rune.mdl");
\r
1341 #ifdef TTURRETS_ENABLED
\r
1342 if (cvar("g_turrets"))
\r
1343 turrets_precash();
\r
1346 // Precache all player models if desired
\r
1347 if (cvar("sv_precacheplayermodels"))
\r
1349 PrecachePlayerSounds("sound/player/default.sounds");
\r
1350 precache_all_models("models/player/*.zym");
\r
1351 precache_all_models("models/player/*.dpm");
\r
1352 precache_all_models("models/player/*.md3");
\r
1353 precache_all_models("models/player/*.psk");
\r
1354 precache_all_models("models/player/*.iqm");
\r
1355 //precache_model("models/player/vixen.zym");
\r
1358 if (cvar("sv_defaultcharacter"))
\r
1361 s = cvar_string("sv_defaultplayermodel_red");
\r
1364 precache_model(s);
\r
1365 PrecachePlayerSounds(strcat(s, ".sounds"));
\r
1367 s = cvar_string("sv_defaultplayermodel_blue");
\r
1370 precache_model(s);
\r
1371 PrecachePlayerSounds(strcat(s, ".sounds"));
\r
1373 s = cvar_string("sv_defaultplayermodel_yellow");
\r
1376 precache_model(s);
\r
1377 PrecachePlayerSounds(strcat(s, ".sounds"));
\r
1379 s = cvar_string("sv_defaultplayermodel_pink");
\r
1382 precache_model(s);
\r
1383 PrecachePlayerSounds(strcat(s, ".sounds"));
\r
1385 s = cvar_string("sv_defaultplayermodel");
\r
1388 precache_model(s);
\r
1389 PrecachePlayerSounds(strcat(s, ".sounds"));
\r
1395 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
\r
1396 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
\r
1399 // gore and miscellaneous sounds
\r
1400 //precache_sound ("misc/h2ohit.wav");
\r
1401 precache_model ("models/grabber.md3");
\r
1402 precache_sound ("misc/armorimpact.wav");
\r
1403 precache_sound ("misc/bodyimpact1.wav");
\r
1404 precache_sound ("misc/bodyimpact2.wav");
\r
1405 precache_sound ("misc/gib.wav");
\r
1406 precache_sound ("misc/gib_splat01.wav");
\r
1407 precache_sound ("misc/gib_splat02.wav");
\r
1408 precache_sound ("misc/gib_splat03.wav");
\r
1409 precache_sound ("misc/gib_splat04.wav");
\r
1410 precache_sound ("misc/hit.wav");
\r
1411 precache_sound ("misc/typehit.wav");
\r
1412 precache_sound ("misc/unavailable.wav");
\r
1413 precache_sound ("misc/forbidden.wav");
\r
1414 precache_sound ("misc/beep.wav");
\r
1415 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
\r
1416 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
\r
1417 precache_sound ("misc/null.wav");
\r
1418 precache_sound ("misc/spawn.wav");
\r
1419 precache_sound ("misc/talk.wav");
\r
1420 precache_sound ("misc/teleport.wav");
\r
1421 precache_sound ("misc/poweroff.wav");
\r
1422 precache_sound ("player/lava.wav");
\r
1423 precache_sound ("player/slime.wav");
\r
1424 precache_sound ("player/digest.wav");
\r
1425 precache_sound ("misc/health_regen.wav");
\r
1426 precache_sound ("misc/armor_regen.wav");
\r
1429 precache_sound ("misc/jetpack_fly.wav");
\r
1431 precache_model ("models/sprites/0.spr32");
\r
1432 precache_model ("models/sprites/1.spr32");
\r
1433 precache_model ("models/sprites/2.spr32");
\r
1434 precache_model ("models/sprites/3.spr32");
\r
1435 precache_model ("models/sprites/4.spr32");
\r
1436 precache_model ("models/sprites/5.spr32");
\r
1437 precache_model ("models/sprites/6.spr32");
\r
1438 precache_model ("models/sprites/7.spr32");
\r
1439 precache_model ("models/sprites/8.spr32");
\r
1440 precache_model ("models/sprites/9.spr32");
\r
1441 precache_model ("models/sprites/10.spr32");
\r
1443 // common weapon precaches
\r
1444 precache_sound ("weapons/weapon_switch.wav");
\r
1445 precache_sound ("weapons/weaponpickup.wav");
\r
1446 precache_sound ("weapons/grabber_fire.wav"); // grabber
\r
1447 precache_sound ("weapons/grabber_altfire.wav"); // grabber
\r
1448 precache_sound ("weapons/grabber_impact.wav"); // grabber
\r
1449 precache_sound ("weapons/stomachkick.wav");
\r
1451 if (cvar("sv_precacheweapons"))
\r
1453 //precache weapon models/sounds
\r
1456 while (wep <= WEP_LAST)
\r
1458 weapon_action(wep, WR_PRECACHE);
\r
1463 precache_model("models/elaser.mdl");
\r
1464 precache_model("models/laser.mdl");
\r
1465 precache_model("models/ebomb.mdl");
\r
1468 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
\r
1470 if (!self.noise && self.music) // quake 3 uses the music field
\r
1471 self.noise = self.music;
\r
1473 // plays music for the level if there is any
\r
1476 precache_sound (self.noise);
\r
1477 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
\r
1482 // sorry, but using \ in macros breaks line numbers
\r
1483 #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
1484 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
\r
1485 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
\r
1487 // WARNING: this kills the trace globals
\r
1488 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
\r
1489 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
\r
1491 #define INITPRIO_FIRST 0
\r
1492 #define INITPRIO_GAMETYPE 0
\r
1493 #define INITPRIO_GAMETYPE_FALLBACK 1
\r
1494 #define INITPRIO_CVARS 5
\r
1495 #define INITPRIO_FINDTARGET 10
\r
1496 #define INITPRIO_DROPTOFLOOR 20
\r
1497 #define INITPRIO_SETLOCATION 90
\r
1498 #define INITPRIO_LINKDOORS 91
\r
1499 #define INITPRIO_LAST 99
\r
1501 .void(void) initialize_entity;
\r
1502 .float initialize_entity_order;
\r
1503 .entity initialize_entity_next;
\r
1504 entity initialize_entity_first;
\r
1506 void make_safe_for_remove(entity e)
\r
1508 if (e.initialize_entity)
\r
1511 for (ent = initialize_entity_first; ent; )
\r
1513 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
\r
1515 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
\r
1516 // skip it in linked list
\r
1519 prev.initialize_entity_next = ent.initialize_entity_next;
\r
1520 ent = prev.initialize_entity_next;
\r
1524 initialize_entity_first = ent.initialize_entity_next;
\r
1525 ent = initialize_entity_first;
\r
1531 ent = ent.initialize_entity_next;
\r
1537 void objerror(string s)
\r
1539 make_safe_for_remove(self);
\r
1540 objerror_builtin(s);
\r
1543 void remove_unsafely(entity e)
\r
1545 remove_builtin(e);
\r
1548 void remove_safely(entity e)
\r
1550 make_safe_for_remove(e);
\r
1551 remove_builtin(e);
\r
1554 void InitializeEntity(entity e, void(void) func, float order)
\r
1558 if (!e || e.initialize_entity)
\r
1560 // make a proxy initializer entity
\r
1564 e.classname = "initialize_entity";
\r
1568 e.initialize_entity = func;
\r
1569 e.initialize_entity_order = order;
\r
1571 cur = initialize_entity_first;
\r
1574 if (!cur || cur.initialize_entity_order > order)
\r
1576 // insert between prev and cur
\r
1578 prev.initialize_entity_next = e;
\r
1580 initialize_entity_first = e;
\r
1581 e.initialize_entity_next = cur;
\r
1585 cur = cur.initialize_entity_next;
\r
1588 void InitializeEntitiesRun()
\r
1590 entity startoflist;
\r
1591 startoflist = initialize_entity_first;
\r
1592 initialize_entity_first = world;
\r
1593 for (self = startoflist; self; )
\r
1596 var void(void) func;
\r
1597 e = self.initialize_entity_next;
\r
1598 func = self.initialize_entity;
\r
1599 self.initialize_entity_order = 0;
\r
1600 self.initialize_entity = func_null;
\r
1601 self.initialize_entity_next = world;
\r
1602 if (self.classname == "initialize_entity")
\r
1605 e_old = self.enemy;
\r
1606 remove_builtin(self);
\r
1609 //dprint("Delayed initialization: ", self.classname, "\n");
\r
1615 .float uncustomizeentityforclient_set;
\r
1616 .void(void) uncustomizeentityforclient;
\r
1617 void(void) SUB_Nullpointer = #0;
\r
1618 void UncustomizeEntitiesRun()
\r
1622 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
\r
1623 self.uncustomizeentityforclient();
\r
1626 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
\r
1628 e.customizeentityforclient = customizer;
\r
1629 e.uncustomizeentityforclient = uncustomizer;
\r
1630 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
\r
1633 .float nottargeted;
\r
1634 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
\r
1636 void() SUB_Remove;
\r
1637 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
\r
1641 if (e.classname == "")
\r
1642 e.classname = "net_linked";
\r
1644 if (e.model == "" || self.modelindex == 0)
\r
1648 setmodel(e, "null");
\r
1649 setsize(e, mi, ma);
\r
1652 e.SendEntity = sendfunc;
\r
1653 e.SendFlags = 0xFFFFFF;
\r
1656 e.effects |= EF_NODEPTHTEST;
\r
1660 e.nextthink = time + dt;
\r
1661 e.think = SUB_Remove;
\r
1665 void adaptor_think2touch()
\r
1674 void adaptor_think2use()
\r
1679 activator = world;
\r
1686 // deferred dropping
\r
1687 void DropToFloor_Handler()
\r
1689 droptofloor_builtin();
\r
1690 self.dropped_origin = self.origin;
\r
1693 void droptofloor()
\r
1695 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
\r
1700 float trace_hits_box_a0, trace_hits_box_a1;
\r
1702 float trace_hits_box_1d(float end, float thmi, float thma)
\r
1706 // just check if x is in range
\r
1714 // do the trace with respect to x
\r
1715 // 0 -> end has to stay in thmi -> thma
\r
1716 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
\r
1717 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
\r
1718 if (trace_hits_box_a0 > trace_hits_box_a1)
\r
1724 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
\r
1729 // now it is a trace from 0 to end
\r
1731 trace_hits_box_a0 = 0;
\r
1732 trace_hits_box_a1 = 1;
\r
1734 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
\r
1736 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
\r
1738 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
\r
1744 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
\r
1746 return trace_hits_box(start, end, thmi - ma, thma - mi);
\r
1749 float SUB_NoImpactCheck()
\r
1751 // zero hitcontents = this is not the real impact, but either the
\r
1752 // mirror-impact of something hitting the projectile instead of the
\r
1753 // projectile hitting the something, or a touchareagrid one. Neither of
\r
1754 // these stop the projectile from moving, so...
\r
1755 if(trace_dphitcontents == 0)
\r
1757 dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
\r
1760 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
\r
1762 if (other == world && self.size != '0 0 0')
\r
1765 tic = self.velocity * sys_frametime;
\r
1766 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
\r
1767 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
\r
1768 if (trace_fraction >= 1)
\r
1770 dprint("Odd... did not hit...?\n");
\r
1772 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
\r
1774 dprint("Detected and prevented the sky-grapple bug.\n");
\r
1782 #define SUB_OwnerCheck() (other && (other == self.owner))
\r
1784 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
\r
1786 if(SUB_OwnerCheck())
\r
1788 if(SUB_NoImpactCheck())
\r
1793 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
\r
1794 UpdateCSQCProjectileNextFrame(self);
\r
1797 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
\r
1799 float MAX_IPBAN_URIS = 16;
\r
1801 float URI_GET_DISCARD = 0;
\r
1802 float URI_GET_IPBAN = 1;
\r
1803 float URI_GET_IPBAN_END = 16;
\r
1805 void URI_Get_Callback(float id, float status, string data)
\r
1807 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
\r
1809 dprint("\nEnd of data.\n");
\r
1811 if (id == URI_GET_DISCARD)
\r
1815 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
\r
1817 // online ban list
\r
1818 OnlineBanList_URI_Get_Callback(id, status, data);
\r
1822 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
\r
1826 void print_to(entity e, string s)
\r
1829 sprint(e, strcat(s, "\n"));
\r
1834 string getrecords(float page) // 50 records per page
\r
1848 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
\r
1850 if (MapInfo_Get_ByID(i))
\r
1852 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
\r
1855 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
\r
1856 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
\r
1864 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
\r
1866 if (MapInfo_Get_ByID(i))
\r
1868 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "time")));
\r
1871 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "netname"));
\r
1872 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
\r
1880 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
\r
1882 if (MapInfo_Get_ByID(i))
\r
1884 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "time")));
\r
1887 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "netname"));
\r
1888 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
\r
1894 MapInfo_ClearTemps();
\r
1896 if (s == "" && page == 0)
\r
1897 return "No records are available on this server.\n";
\r
1902 string getrankings()
\r
1913 map = GetMapname();
\r
1915 for (i = 1; i <= RANKINGS_CNT; ++i)
\r
1917 t = race_GetTime(i);
\r
1920 n = race_GetName(i);
\r
1921 p = race_PlaceName(i);
\r
1922 s = strcat(s, strpad(8, p), " ", strpad(-8, TIME_ENCODED_TOSTRING(t)), " ", n, "\n");
\r
1925 MapInfo_ClearTemps();
\r
1928 return strcat("No records are available for the map: ", map, "\n");
\r
1930 return strcat("Records for ", map, ":\n", s);
\r
1933 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
\r
1936 vector start, org, delta, end, enddown, mstart;
\r
1938 m = e.dphitcontentsmask;
\r
1939 e.dphitcontentsmask = goodcontents | badcontents;
\r
1942 delta = world.maxs - world.mins;
\r
1944 for (i = 0; i < attempts; ++i)
\r
1946 start_x = org_x + random() * delta_x;
\r
1947 start_y = org_y + random() * delta_y;
\r
1948 start_z = org_z + random() * delta_z;
\r
1950 // rule 1: start inside world bounds, and outside
\r
1951 // solid, and don't start from somewhere where you can
\r
1952 // fall down to evil
\r
1953 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
\r
1954 if (trace_fraction >= 1)
\r
1956 if (trace_startsolid)
\r
1958 if (trace_dphitcontents & badcontents)
\r
1960 if (trace_dphitq3surfaceflags & badsurfaceflags)
\r
1963 // rule 2: if we are too high, lower the point
\r
1964 if (trace_fraction * delta_z > maxaboveground)
\r
1965 start = trace_endpos + '0 0 1' * maxaboveground;
\r
1966 enddown = trace_endpos;
\r
1968 // rule 3: make sure we aren't outside the map. This only works
\r
1969 // for somewhat well formed maps. A good rule of thumb is that
\r
1970 // the map should have a convex outside hull.
\r
1971 // these can be traceLINES as we already verified the starting box
\r
1972 mstart = start + 0.5 * (e.mins + e.maxs);
\r
1973 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
\r
1974 if (trace_fraction >= 1)
\r
1976 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
\r
1977 if (trace_fraction >= 1)
\r
1979 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
\r
1980 if (trace_fraction >= 1)
\r
1982 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
\r
1983 if (trace_fraction >= 1)
\r
1985 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
\r
1986 if (trace_fraction >= 1)
\r
1989 // find a random vector to "look at"
\r
1990 end_x = org_x + random() * delta_x;
\r
1991 end_y = org_y + random() * delta_y;
\r
1992 end_z = org_z + random() * delta_z;
\r
1993 end = start + normalize(end - start) * vlen(delta);
\r
1995 // rule 4: start TO end must not be too short
\r
1996 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
\r
1997 if (trace_startsolid)
\r
1999 if (trace_fraction < minviewdistance / vlen(delta))
\r
2002 // rule 5: don't want to look at sky
\r
2003 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
\r
2006 // rule 6: we must not end up in trigger_hurt
\r
2007 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
\r
2009 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
\r
2016 e.dphitcontentsmask = m;
\r
2020 setorigin(e, start);
\r
2021 e.angles = vectoangles(end - start);
\r
2022 dprint("Needed ", ftos(i + 1), " attempts\n");
\r
2029 float zcurveparticles_effectno;
\r
2030 vector zcurveparticles_start;
\r
2031 float zcurveparticles_spd;
\r
2033 void endzcurveparticles()
\r
2035 if(zcurveparticles_effectno)
\r
2038 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
\r
2040 zcurveparticles_effectno = 0;
\r
2043 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
\r
2045 spd = bound(0, floor(spd / 16), 32767);
\r
2046 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
\r
2048 endzcurveparticles();
\r
2049 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
\r
2050 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
\r
2051 WriteShort(MSG_BROADCAST, effectno);
\r
2052 WriteCoord(MSG_BROADCAST, start_x);
\r
2053 WriteCoord(MSG_BROADCAST, start_y);
\r
2054 WriteCoord(MSG_BROADCAST, start_z);
\r
2055 zcurveparticles_effectno = effectno;
\r
2056 zcurveparticles_start = start;
\r
2059 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
\r
2060 WriteCoord(MSG_BROADCAST, end_x);
\r
2061 WriteCoord(MSG_BROADCAST, end_y);
\r
2062 WriteCoord(MSG_BROADCAST, end_z);
\r
2063 WriteCoord(MSG_BROADCAST, end_dz);
\r
2064 zcurveparticles_spd = spd;
\r
2067 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
\r
2070 vector vecxy, velxy;
\r
2072 vecxy = end - start;
\r
2077 if (vlen(velxy) < 0.000001 * fabs(vel_z))
\r
2079 endzcurveparticles();
\r
2080 trailparticles(world, effectno, start, end);
\r
2084 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
\r
2085 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
\r
2088 string GetGametype(); // g_world.qc
\r
2089 void write_recordmarker(entity pl, float tstart, float dt)
\r
2091 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
\r
2093 // also write a marker into demo files for demotc-race-record-extractor to find
\r
2096 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
\r
2097 " ", ftos(tstart), " ", ftos(dt), "\n"));
\r
2100 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter)
\r
2102 switch(self.owner.cvar_cl_gunalign)
\r
2113 if(allowcenter) // 2: allow center handedness
\r
2126 if(allowcenter) // 2: allow center handedness
\r
2142 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
\r
2147 if (cvar("g_shootfromeye"))
\r
2151 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
\r
2159 else if (cvar("g_shootfromcenter"))
\r
2163 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
\r
2171 else if ((s = cvar_string("g_shootfromfixedorigin")) != "")
\r
2181 else if (cvar("g_shootfromclient"))
\r
2183 vecs = shotorg_adjustfromclient(vecs, y_is_right, (cvar("g_shootfromclient") >= 2));
\r
2190 void attach_sameorigin(entity e, entity to, string tag)
\r
2192 vector org, t_forward, t_left, t_up, e_forward, e_up;
\r
2193 vector org0, ang0;
\r
2199 org = e.origin - gettaginfo(to, gettagindex(to, tag));
\r
2200 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
\r
2201 t_forward = v_forward * tagscale;
\r
2202 t_left = v_right * -tagscale;
\r
2203 t_up = v_up * tagscale;
\r
2205 e.origin_x = org * t_forward;
\r
2206 e.origin_y = org * t_left;
\r
2207 e.origin_z = org * t_up;
\r
2209 // current forward and up directions
\r
2210 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
\r
2211 e.angles = AnglesTransform_FromVAngles(e.angles);
\r
2213 e.angles = AnglesTransform_FromAngles(e.angles);
\r
2214 fixedmakevectors(e.angles);
\r
2216 // untransform forward, up!
\r
2217 e_forward_x = v_forward * t_forward;
\r
2218 e_forward_y = v_forward * t_left;
\r
2219 e_forward_z = v_forward * t_up;
\r
2220 e_up_x = v_up * t_forward;
\r
2221 e_up_y = v_up * t_left;
\r
2222 e_up_z = v_up * t_up;
\r
2224 e.angles = fixedvectoangles2(e_forward, e_up);
\r
2225 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
\r
2226 e.angles = AnglesTransform_ToVAngles(e.angles);
\r
2228 e.angles = AnglesTransform_ToAngles(e.angles);
\r
2230 setattachment(e, to, tag);
\r
2231 setorigin(e, e.origin);
\r
2234 void detach_sameorigin(entity e)
\r
2237 org = gettaginfo(e, 0);
\r
2238 e.angles = fixedvectoangles2(v_forward, v_up);
\r
2239 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
\r
2240 e.angles = AnglesTransform_ToVAngles(e.angles);
\r
2242 e.angles = AnglesTransform_ToAngles(e.angles);
\r
2243 setorigin(e, org);
\r
2244 setattachment(e, world, "");
\r
2245 setorigin(e, e.origin);
\r
2248 void follow_sameorigin(entity e, entity to)
\r
2250 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
\r
2251 e.aiment = to; // make the hole follow bmodel
\r
2252 e.punchangle = to.angles; // the original angles of bmodel
\r
2253 e.view_ofs = e.origin - to.origin; // relative origin
\r
2254 e.v_angle = e.angles - to.angles; // relative angles
\r
2257 void unfollow_sameorigin(entity e)
\r
2259 e.movetype = MOVETYPE_NONE;
\r
2262 entity gettaginfo_relative_ent;
\r
2263 vector gettaginfo_relative(entity e, float tag)
\r
2265 if (!gettaginfo_relative_ent)
\r
2267 gettaginfo_relative_ent = spawn();
\r
2268 gettaginfo_relative_ent.effects = EF_NODRAW;
\r
2270 gettaginfo_relative_ent.model = e.model;
\r
2271 gettaginfo_relative_ent.modelindex = e.modelindex;
\r
2272 gettaginfo_relative_ent.frame = e.frame;
\r
2273 return gettaginfo(gettaginfo_relative_ent, tag);
\r
2276 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
\r
2280 if (pl.soundentity.cnt & p)
\r
2282 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
\r
2283 pl.soundentity.cnt |= p;
\r
2286 void SoundEntity_StopSound(entity pl, float chan)
\r
2290 if (pl.soundentity.cnt & p)
\r
2292 stopsoundto(MSG_ALL, pl.soundentity, chan);
\r
2293 pl.soundentity.cnt &~= p;
\r
2297 void SoundEntity_Attach(entity pl)
\r
2299 pl.soundentity = spawn();
\r
2300 pl.soundentity.classname = "soundentity";
\r
2301 pl.soundentity.owner = pl;
\r
2302 setattachment(pl.soundentity, pl, "");
\r
2303 setmodel(pl.soundentity, "null");
\r
2306 void SoundEntity_Detach(entity pl)
\r
2309 for (i = 0; i <= 7; ++i)
\r
2310 SoundEntity_StopSound(pl, i);
\r
2314 float ParseCommandPlayerSlotTarget_firsttoken;
\r
2315 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
\r
2323 ParseCommandPlayerSlotTarget_firsttoken = -1;
\r
2327 if (substring(argv(idx), 0, 1) == "#")
\r
2329 s = substring(argv(idx), 1, -1);
\r
2337 ParseCommandPlayerSlotTarget_firsttoken = idx;
\r
2338 if (s == ftos(stof(s)))
\r
2340 e = edict_num(stof(s));
\r
2341 if (e.flags & FL_CLIENT)
\r
2347 // it must be a nick name
\r
2350 ParseCommandPlayerSlotTarget_firsttoken = idx;
\r
2353 FOR_EACH_CLIENT(head)
\r
2354 if (head.netname == s)
\r
2362 s = strdecolorize(s);
\r
2364 FOR_EACH_CLIENT(head)
\r
2365 if (strdecolorize(head.netname) == s)
\r
2380 float modeleffect_SendEntity(entity to, float sf)
\r
2383 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
\r
2386 if(self.velocity != '0 0 0')
\r
2388 if(self.angles != '0 0 0')
\r
2390 if(self.avelocity != '0 0 0')
\r
2393 WriteByte(MSG_ENTITY, f);
\r
2394 WriteShort(MSG_ENTITY, self.modelindex);
\r
2395 WriteByte(MSG_ENTITY, self.skin);
\r
2396 WriteByte(MSG_ENTITY, self.frame);
\r
2397 WriteCoord(MSG_ENTITY, self.origin_x);
\r
2398 WriteCoord(MSG_ENTITY, self.origin_y);
\r
2399 WriteCoord(MSG_ENTITY, self.origin_z);
\r
2402 WriteCoord(MSG_ENTITY, self.velocity_x);
\r
2403 WriteCoord(MSG_ENTITY, self.velocity_y);
\r
2404 WriteCoord(MSG_ENTITY, self.velocity_z);
\r
2408 WriteCoord(MSG_ENTITY, self.angles_x);
\r
2409 WriteCoord(MSG_ENTITY, self.angles_y);
\r
2410 WriteCoord(MSG_ENTITY, self.angles_z);
\r
2414 WriteCoord(MSG_ENTITY, self.avelocity_x);
\r
2415 WriteCoord(MSG_ENTITY, self.avelocity_y);
\r
2416 WriteCoord(MSG_ENTITY, self.avelocity_z);
\r
2418 WriteShort(MSG_ENTITY, self.scale * 256.0);
\r
2419 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
\r
2420 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
\r
2421 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
\r
2422 WriteByte(MSG_ENTITY, self.alpha * 255.0);
\r
2427 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
2432 e.classname = "modeleffect";
\r
2438 e.avelocity = angv;
\r
2440 e.teleport_time = t1;
\r
2444 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
\r
2448 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
\r
2451 sz = max(e.scale, e.scale2);
\r
2452 setsize(e, e.mins * sz, e.maxs * sz);
\r
2453 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
\r
2456 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
\r
2458 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
\r
2461 float randombit(float bits)
\r
2463 if not(bits & (bits-1)) // this ONLY holds for powers of two!
\r
2472 for(f = 1; f <= bits; f *= 2)
\r
2481 r = (r - 1) / (n - 1);
\r
2488 float randombits(float bits, float k, float error_return)
\r
2492 while(k > 0 && bits != r)
\r
2494 r += randombit(bits - r);
\r
2503 void randombit_test(float bits, float iter)
\r
2507 print(ftos(randombit(bits)), "\n");
\r
2512 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
\r
2514 if(halflifedist > 0)
\r
2515 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
\r
2516 else if(halflifedist < 0)
\r
2517 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
\r
2526 #define cvar_string_normal cvar_string_builtin
\r
2527 #define cvar_normal cvar_builtin
\r
2529 string cvar_string_normal(string n)
\r
2531 if not(cvar_type(n) & 1)
\r
2532 backtrace(strcat("Attempt to access undefined cvar: ", n));
\r
2533 return cvar_string_builtin(n);
\r
2536 float cvar_normal(string n)
\r
2538 return stof(cvar_string_normal(n));
\r
2541 #define cvar_set_normal cvar_set_builtin
\r
2543 void defer_think()
\r
2548 self = self.owner;
\r
2549 oself.think = SUB_Remove;
\r
2550 oself.nextthink = time;
\r
2556 Execute func() after time + fdelay.
\r
2557 self when func is executed = self when defer is called
\r
2559 void defer(float fdelay, void() func)
\r
2566 e.think = defer_think;
\r
2567 e.nextthink = time + fdelay;
\r