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_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
\r
600 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete);
\r
601 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
\r
602 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
\r
603 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
\r
604 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
\r
605 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
\r
606 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
\r
607 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
\r
608 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
\r
609 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
\r
610 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
\r
611 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
\r
612 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
\r
613 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
\r
614 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
\r
615 GetCvars_handleFloat(s, f, cvar_cl_hitsound, "cl_hitsound");
\r
616 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
\r
617 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
\r
618 GetCvars_handleFloat(s, f, cvar_chase_active, "chase_active");
\r
619 GetCvars_handleFloat(s, f, cvar_cl_vore_stomachmodel, "cl_vore_stomachmodel");
\r
620 GetCvars_handleFloat(s, f, cvar_cl_vore_gulletmodel, "cl_vore_gulletmodel");
\r
621 GetCvars_handleFloat(s, f, cvar_cl_vore_autodigest, "cl_vore_autodigest");
\r
622 GetCvars_handleFloat(s, f, cvar_cl_dodging_timeout, "cl_dodging_timeout");
\r
624 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
\r
625 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
\r
627 #ifdef ALLOW_FORCEMODELS
\r
628 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
\r
629 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromvoretournament, "cl_forceplayermodelsfromvoretournament");
\r
631 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
\r
633 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
\r
636 if (s == "cl_weaponpriority")
\r
637 self.switchweapon = w_getbestweapon(self);
\r
641 float fexists(string f)
\r
644 fh = fopen(f, FILE_READ);
\r
651 void backtrace(string msg)
\r
654 dev = cvar("developer");
\r
655 war = cvar("prvm_backtraceforwarnings");
\r
656 cvar_set("developer", "1");
\r
657 cvar_set("prvm_backtraceforwarnings", "1");
\r
659 print("--- CUT HERE ---\nWARNING: ");
\r
662 remove(world); // isn't there any better way to cause a backtrace?
\r
663 print("\n--- CUT UNTIL HERE ---\n");
\r
664 cvar_set("developer", ftos(dev));
\r
665 cvar_set("prvm_backtraceforwarnings", ftos(war));
\r
668 string Team_ColorCode(float teamid)
\r
670 if (teamid == COLOR_TEAM1)
\r
672 else if (teamid == COLOR_TEAM2)
\r
674 else if (teamid == COLOR_TEAM3)
\r
676 else if (teamid == COLOR_TEAM4)
\r
682 string Team_ColorName(float t)
\r
684 // fixme: Search for team entities and get their .netname's!
\r
685 if (t == COLOR_TEAM1)
\r
687 if (t == COLOR_TEAM2)
\r
689 if (t == COLOR_TEAM3)
\r
691 if (t == COLOR_TEAM4)
\r
696 string Team_ColorNameLowerCase(float t)
\r
698 // fixme: Search for team entities and get their .netname's!
\r
699 if (t == COLOR_TEAM1)
\r
701 if (t == COLOR_TEAM2)
\r
703 if (t == COLOR_TEAM3)
\r
705 if (t == COLOR_TEAM4)
\r
710 float ColourToNumber(string team_colour)
\r
712 if (team_colour == "red")
\r
713 return COLOR_TEAM1;
\r
715 if (team_colour == "blue")
\r
716 return COLOR_TEAM2;
\r
718 if (team_colour == "yellow")
\r
719 return COLOR_TEAM3;
\r
721 if (team_colour == "pink")
\r
722 return COLOR_TEAM4;
\r
724 if (team_colour == "auto")
\r
730 float NumberToTeamNumber(float number)
\r
733 return COLOR_TEAM1;
\r
736 return COLOR_TEAM2;
\r
739 return COLOR_TEAM3;
\r
742 return COLOR_TEAM4;
\r
747 #define CENTERPRIO_POINT 1
\r
748 #define CENTERPRIO_SPAM 2
\r
749 #define CENTERPRIO_VOTE 4
\r
750 #define CENTERPRIO_NORMAL 5
\r
751 #define CENTERPRIO_SHIELDING 7
\r
752 #define CENTERPRIO_MAPVOTE 9
\r
753 #define CENTERPRIO_IDLEKICK 50
\r
754 #define CENTERPRIO_ADMIN 99
\r
755 .float centerprint_priority;
\r
756 .float centerprint_expires;
\r
757 void centerprint_atprio(entity e, float prio, string s)
\r
759 if (intermission_running)
\r
760 if (prio < CENTERPRIO_MAPVOTE)
\r
762 if (time > e.centerprint_expires)
\r
763 e.centerprint_priority = 0;
\r
764 if (prio >= e.centerprint_priority)
\r
766 e.centerprint_priority = prio;
\r
767 if (timeoutStatus == 2)
\r
768 e.centerprint_expires = time + (e.cvar_scr_centertime * TIMEOUT_SLOWMO_VALUE);
\r
770 e.centerprint_expires = time + e.cvar_scr_centertime;
\r
771 centerprint_builtin(e, s);
\r
774 void centerprint_expire(entity e, float prio)
\r
776 if (prio == e.centerprint_priority)
\r
778 e.centerprint_priority = 0;
\r
779 centerprint_builtin(e, "");
\r
782 void centerprint(entity e, string s)
\r
784 centerprint_atprio(e, CENTERPRIO_NORMAL, s);
\r
787 // decolorizes and team colors the player name when needed
\r
788 string playername(entity p)
\r
791 if (teams_matter && !intermission_running && p.classname == "player")
\r
793 t = Team_ColorCode(p.team);
\r
794 return strcat(t, strdecolorize(p.netname));
\r
800 vector randompos(vector m1, vector m2)
\r
804 v_x = m2_x * random() + m1_x;
\r
805 v_y = m2_y * random() + m1_y;
\r
806 v_z = m2_z * random() + m1_z;
\r
810 float g_pickup_fuel;
\r
811 float g_pickup_fuel_jetpack;
\r
812 float g_pickup_fuel_max;
\r
813 float g_pickup_armorsmall;
\r
814 float g_pickup_armorsmall_max;
\r
815 float g_pickup_armormedium;
\r
816 float g_pickup_armormedium_max;
\r
817 float g_pickup_armorbig;
\r
818 float g_pickup_armorbig_max;
\r
819 float g_pickup_armorlarge;
\r
820 float g_pickup_armorlarge_max;
\r
821 float g_pickup_healthsmall;
\r
822 float g_pickup_healthsmall_max;
\r
823 float g_pickup_healthsmall_consumable;
\r
824 float g_pickup_healthmedium;
\r
825 float g_pickup_healthmedium_max;
\r
826 float g_pickup_healthmedium_consumable;
\r
827 float g_pickup_healthlarge;
\r
828 float g_pickup_healthlarge_max;
\r
829 float g_pickup_healthlarge_consumable;
\r
830 float g_pickup_healthmega;
\r
831 float g_pickup_healthmega_max;
\r
832 float g_pickup_healthmega_consumable;
\r
833 float g_weaponspeedfactor;
\r
834 float g_weaponratefactor;
\r
835 float g_weapondamagefactor;
\r
836 float g_weaponforcefactor;
\r
837 float g_weaponspreadfactor;
\r
839 float start_weapons;
\r
841 float start_ammo_fuel;
\r
842 float start_health;
\r
843 float start_armorvalue;
\r
844 float warmup_start_weapons;
\r
845 float warmup_start_ammo_fuel;
\r
846 float warmup_start_health;
\r
847 float warmup_start_armorvalue;
\r
848 float g_weapon_stay;
\r
849 float g_ghost_items;
\r
851 entity get_weaponinfo(float w);
\r
853 float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
\r
855 var float i = weaponinfo.weapon;
\r
860 var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
\r
862 if (t < 0) // "default" weapon selection
\r
864 if(g_rpg) // no start weapons in RPG by default
\r
867 t = (i == WEP_GRABBER);
\r
873 void readplayerstartcvars()
\r
878 // initialize starting values for players
\r
881 start_health = cvar("g_balance_health_start");
\r
882 start_armorvalue = cvar("g_balance_armor_start");
\r
886 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
\r
887 start_health = cvar("g_lms_start_health");
\r
888 start_armorvalue = cvar("g_lms_start_armor");
\r
890 else if (cvar("g_use_ammunition"))
\r
892 start_ammo_fuel = cvar("g_start_ammo_fuel");
\r
896 start_ammo_fuel = cvar("g_pickup_fuel_max");
\r
897 start_items |= IT_UNLIMITED_AMMO;
\r
900 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
\r
902 e = get_weaponinfo(i);
\r
903 if(want_weapon("g_start_weapon_", e, FALSE))
\r
905 start_weapons |= e.weapons;
\r
906 weapon_action(e.weapon, WR_PRECACHE);
\r
912 warmup_start_ammo_fuel = start_ammo_fuel;
\r
913 warmup_start_health = start_health;
\r
914 warmup_start_armorvalue = start_armorvalue;
\r
915 warmup_start_weapons = start_weapons;
\r
917 if (cvar("g_use_ammunition"))
\r
919 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
\r
921 warmup_start_health = cvar("g_warmup_start_health");
\r
922 warmup_start_armorvalue = cvar("g_warmup_start_armor");
\r
923 warmup_start_weapons = 0;
\r
924 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
\r
926 e = get_weaponinfo(i);
\r
927 if(want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns")))
\r
929 warmup_start_weapons |= e.weapons;
\r
930 weapon_action(e.weapon, WR_PRECACHE);
\r
937 start_items |= IT_FUEL_REGEN;
\r
938 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
\r
939 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
\r
943 start_items |= IT_JETPACK;
\r
945 if (g_weapon_stay == 2)
\r
947 if (!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
\r
948 if (!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
\r
951 start_ammo_fuel = max(0, start_ammo_fuel);
\r
953 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
\r
957 float g_bugrigs_planar_movement;
\r
958 float g_bugrigs_planar_movement_car_jumping;
\r
959 float g_bugrigs_reverse_spinning;
\r
960 float g_bugrigs_reverse_speeding;
\r
961 float g_bugrigs_reverse_stopping;
\r
962 float g_bugrigs_air_steering;
\r
963 float g_bugrigs_angle_smoothing;
\r
964 float g_bugrigs_friction_floor;
\r
965 float g_bugrigs_friction_brake;
\r
966 float g_bugrigs_friction_air;
\r
967 float g_bugrigs_accel;
\r
968 float g_bugrigs_speed_ref;
\r
969 float g_bugrigs_speed_pow;
\r
970 float g_bugrigs_steer;
\r
972 float g_touchexplode;
\r
973 float g_touchexplode_radius;
\r
974 float g_touchexplode_damage;
\r
975 float g_touchexplode_edgedamage;
\r
976 float g_touchexplode_force;
\r
978 float sv_autotaunt;
\r
981 float sv_pitch_min;
\r
982 float sv_pitch_max;
\r
983 float sv_pitch_fixyaw;
\r
985 float sv_accuracy_data_share;
\r
987 void readlevelcvars(void)
\r
989 g_bugrigs = cvar("g_bugrigs");
\r
990 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
\r
991 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
\r
992 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
\r
993 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
\r
994 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
\r
995 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
\r
996 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
\r
997 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
\r
998 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
\r
999 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
\r
1000 g_bugrigs_accel = cvar("g_bugrigs_accel");
\r
1001 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
\r
1002 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
\r
1003 g_bugrigs_steer = cvar("g_bugrigs_steer");
\r
1005 g_touchexplode = cvar("g_touchexplode");
\r
1006 g_touchexplode_radius = cvar("g_touchexplode_radius");
\r
1007 g_touchexplode_damage = cvar("g_touchexplode_damage");
\r
1008 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
\r
1009 g_touchexplode_force = cvar("g_touchexplode_force");
\r
1011 #ifdef ALLOW_FORCEMODELS
\r
1012 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
\r
1015 sv_clones = cvar("sv_clones");
\r
1016 sv_gentle = cvar("sv_gentle");
\r
1017 sv_foginterval = cvar("sv_foginterval");
\r
1018 g_cloaked = cvar("g_cloaked");
\r
1019 g_jump_grunt = cvar("g_jump_grunt");
\r
1020 g_footsteps = cvar("g_footsteps");
\r
1021 g_jetpack = cvar("g_jetpack");
\r
1022 g_midair = cvar("g_midair");
\r
1023 g_norecoil = cvar("g_norecoil");
\r
1024 g_vampire = cvar("g_vampire");
\r
1025 g_bloodloss = cvar("g_bloodloss");
\r
1026 sv_maxidle = cvar("sv_maxidle");
\r
1027 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
\r
1028 sv_pogostick = cvar("sv_pogostick");
\r
1029 sv_doublejump = cvar("sv_doublejump");
\r
1030 g_ctf_reverse = cvar("g_ctf_reverse");
\r
1031 sv_autotaunt = cvar("sv_autotaunt");
\r
1032 sv_taunt = cvar("sv_taunt");
\r
1034 inWarmupStage = cvar("g_warmup");
\r
1035 g_warmup_limit = cvar("g_warmup_limit");
\r
1036 g_warmup_allguns = cvar("g_warmup_allguns");
\r
1037 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
\r
1039 if ((g_race && g_race_qualifying == 2) || g_arena || g_assault || g_rpg || cvar("g_campaign"))
\r
1040 inWarmupStage = 0; // these modes cannot work together, sorry
\r
1042 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
\r
1043 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
\r
1044 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
\r
1045 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
\r
1046 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
\r
1047 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
\r
1048 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
\r
1049 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
\r
1050 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
\r
1051 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
\r
1052 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
\r
1053 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
\r
1055 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
\r
1056 g_weaponratefactor = cvar("g_weaponratefactor");
\r
1057 g_weapondamagefactor = cvar("g_weapondamagefactor");
\r
1058 g_weaponforcefactor = cvar("g_weaponforcefactor");
\r
1059 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
\r
1061 g_pickup_fuel = cvar("g_pickup_fuel");
\r
1062 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
\r
1063 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
\r
1064 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
\r
1065 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
\r
1066 g_pickup_armormedium = cvar("g_pickup_armormedium");
\r
1067 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
\r
1068 g_pickup_armorbig = cvar("g_pickup_armorbig");
\r
1069 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
\r
1070 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
\r
1071 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
\r
1072 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
\r
1073 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
\r
1074 g_pickup_healthsmall_consumable = cvar("g_pickup_healthsmall_consumable");
\r
1075 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
\r
1076 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
\r
1077 g_pickup_healthmedium_consumable = cvar("g_pickup_healthmedium_consumable");
\r
1078 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
\r
1079 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
\r
1080 g_pickup_healthlarge_consumable = cvar("g_pickup_healthlarge_consumable");
\r
1081 g_pickup_healthmega = cvar("g_pickup_healthmega");
\r
1082 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
\r
1083 g_pickup_healthmega_consumable = cvar("g_pickup_healthmega_consumable");
\r
1085 g_weapon_stay = cvar("g_weapon_stay");
\r
1087 if (!g_weapon_stay && (cvar("deathmatch") == 2))
\r
1088 g_weapon_stay = 1;
\r
1090 g_ghost_items = cvar("g_ghost_items");
\r
1092 if(g_ghost_items >= 1)
\r
1093 g_ghost_items = 0.25; // default alpha value
\r
1095 if not(inWarmupStage && !g_ca && !g_rpg)
\r
1096 game_starttime = cvar("g_start_delay");
\r
1098 sv_pitch_min = cvar("sv_pitch_min");
\r
1099 sv_pitch_max = cvar("sv_pitch_max");
\r
1100 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
\r
1102 sv_accuracy_data_share = boolean(cvar("sv_accuracy_data_share"));
\r
1104 readplayerstartcvars();
\r
1108 // TODO sound pack system
\r
1111 string precache_sound_builtin (string s) = #19;
\r
1112 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
\r
1113 string precache_sound(string s)
\r
1115 return precache_sound_builtin(strcat(soundpack, s));
\r
1117 void play2(entity e, string filename)
\r
1119 stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
\r
1121 void sound(entity e, float chan, string samp, float vol, float atten)
\r
1123 sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
\r
1127 // Sound functions
\r
1128 string precache_sound (string s) = #19;
\r
1129 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
\r
1130 float precache_sound_index (string s) = #19;
\r
1132 #define SND_VOLUME 1
\r
1133 #define SND_ATTENUATION 2
\r
1134 #define SND_LARGEENTITY 8
\r
1135 #define SND_LARGESOUND 16
\r
1136 #define SND_SPEEDUSHORT4000 32
\r
1138 float sound_allowed(float dest, entity e)
\r
1140 // sounds from world may always pass
\r
1143 if (e.classname == "body")
\r
1145 if (e.owner && e.owner != e)
\r
1150 // sounds to self may always pass
\r
1151 if (dest == MSG_ONE)
\r
1152 if (e == msg_entity)
\r
1154 // sounds by players can be removed
\r
1155 if (cvar("bot_sound_monopoly"))
\r
1156 if (clienttype(e) == CLIENTTYPE_REAL)
\r
1158 // anything else may pass
\r
1162 void sound(entity e, float chan, string samp, float vol, float atten)
\r
1164 if (!sound_allowed(MSG_BROADCAST, e))
\r
1166 sound_builtin(e, chan, samp, vol, atten);
\r
1168 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten, float spd)
\r
1172 if (!sound_allowed(dest, e))
\r
1175 entno = num_for_edict(e);
\r
1176 idx = precache_sound_index(samp);
\r
1181 atten = floor(atten * 64);
\r
1182 vol = floor(vol * 255);
\r
1185 sflags |= SND_VOLUME;
\r
1187 sflags |= SND_ATTENUATION;
\r
1189 sflags |= SND_SPEEDUSHORT4000;
\r
1190 if (entno >= 8192)
\r
1191 sflags |= SND_LARGEENTITY;
\r
1193 sflags |= SND_LARGESOUND;
\r
1195 WriteByte(dest, SVC_SOUND);
\r
1196 WriteByte(dest, sflags);
\r
1197 if (sflags & SND_VOLUME)
\r
1198 WriteByte(dest, vol);
\r
1199 if (sflags & SND_ATTENUATION)
\r
1200 WriteByte(dest, atten);
\r
1201 if(sflags & SND_SPEEDUSHORT4000)
\r
1202 WriteShort(dest, spd * 4000);
\r
1203 if (sflags & SND_LARGEENTITY)
\r
1205 WriteShort(dest, entno);
\r
1206 WriteByte(dest, chan);
\r
1210 WriteShort(dest, entno * 8 + chan);
\r
1212 if (sflags & SND_LARGESOUND)
\r
1213 WriteShort(dest, idx);
\r
1215 WriteByte(dest, idx);
\r
1217 WriteCoord(dest, o_x);
\r
1218 WriteCoord(dest, o_y);
\r
1219 WriteCoord(dest, o_z);
\r
1221 void soundto(float dest, entity e, float chan, string samp, float vol, float atten, float spd)
\r
1225 if (!sound_allowed(dest, e))
\r
1228 o = e.origin + 0.5 * (e.mins + e.maxs);
\r
1229 soundtoat(dest, e, o, chan, samp, vol, atten, spd);
\r
1231 void soundat(entity e, vector o, float chan, string samp, float vol, float atten, float spd)
\r
1233 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten, spd);
\r
1235 void stopsoundto(float dest, entity e, float chan)
\r
1239 if (!sound_allowed(dest, e))
\r
1242 entno = num_for_edict(e);
\r
1244 if (entno >= 8192)
\r
1246 float idx, sflags;
\r
1247 idx = precache_sound_index("misc/null.wav");
\r
1248 sflags = SND_LARGEENTITY;
\r
1250 sflags |= SND_LARGESOUND;
\r
1251 WriteByte(dest, SVC_SOUND);
\r
1252 WriteByte(dest, sflags);
\r
1253 WriteShort(dest, entno);
\r
1254 WriteByte(dest, chan);
\r
1255 if (sflags & SND_LARGESOUND)
\r
1256 WriteShort(dest, idx);
\r
1258 WriteByte(dest, idx);
\r
1259 WriteCoord(dest, e.origin_x);
\r
1260 WriteCoord(dest, e.origin_y);
\r
1261 WriteCoord(dest, e.origin_z);
\r
1265 WriteByte(dest, SVC_STOPSOUND);
\r
1266 WriteShort(dest, entno * 8 + chan);
\r
1269 void stopsound(entity e, float chan)
\r
1271 if (!sound_allowed(MSG_BROADCAST, e))
\r
1274 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
\r
1275 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
\r
1278 void play2(entity e, string filename)
\r
1280 //stuffcmd(e, strcat("play2 ", filename, "\n"));
\r
1281 if (clienttype(e) == CLIENTTYPE_REAL)
\r
1284 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE, 0);
\r
1288 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
\r
1290 float spamsound(entity e, float chan, string samp, float vol, float atten)
\r
1292 if (!sound_allowed(MSG_BROADCAST, e))
\r
1295 if (time > e.spamtime)
\r
1297 e.spamtime = time;
\r
1298 sound(e, chan, samp, vol, atten);
\r
1304 void play2team(float t, string filename)
\r
1306 local entity head;
\r
1308 if (cvar("bot_sound_monopoly"))
\r
1311 FOR_EACH_REALPLAYER(head)
\r
1313 if (head.team == t)
\r
1314 play2(head, filename);
\r
1318 void play2all(string samp)
\r
1320 if (cvar("bot_sound_monopoly"))
\r
1323 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
\r
1326 void PrecachePlayerSounds(string f);
\r
1327 void precache_playermodel(string m)
\r
1329 float globhandle, i, n;
\r
1332 precache_model(m);
\r
1334 globhandle = search_begin(strcat(m, "_*.sounds"), TRUE, FALSE);
\r
1335 if (globhandle < 0)
\r
1337 n = search_getsize(globhandle);
\r
1338 for (i = 0; i < n; ++i)
\r
1340 //print(search_getfilename(globhandle, i), "\n");
\r
1341 f = search_getfilename(globhandle, i);
\r
1342 PrecachePlayerSounds(f);
\r
1344 search_end(globhandle);
\r
1346 void precache_all_playermodels(string pattern)
\r
1348 float globhandle, i, n;
\r
1351 globhandle = search_begin(pattern, TRUE, FALSE);
\r
1352 if (globhandle < 0)
\r
1354 n = search_getsize(globhandle);
\r
1355 for (i = 0; i < n; ++i)
\r
1357 //print(search_getfilename(globhandle, i), "\n");
\r
1358 f = search_getfilename(globhandle, i);
\r
1359 precache_playermodel(f);
\r
1361 search_end(globhandle);
\r
1368 // gamemode related things
\r
1369 precache_model ("models/misc/chatbubble.spr");
\r
1371 // used by the waypoint editor
\r
1372 precache_model ("models/rune.mdl");
\r
1374 #ifdef TTURRETS_ENABLED
\r
1375 if (cvar("g_turrets"))
\r
1376 turrets_precash();
\r
1379 // Precache all player models if desired
\r
1380 if (cvar("sv_precacheplayermodels"))
\r
1382 PrecachePlayerSounds("sound/player/default.sounds");
\r
1383 precache_all_playermodels("models/player/*.zym");
\r
1384 precache_all_playermodels("models/player/*.dpm");
\r
1385 precache_all_playermodels("models/player/*.md3");
\r
1386 precache_all_playermodels("models/player/*.psk");
\r
1387 precache_all_playermodels("models/player/*.iqm");
\r
1390 if (cvar("sv_defaultcharacter"))
\r
1393 s = cvar_string("sv_defaultplayermodel_red");
\r
1396 precache_model(s);
\r
1397 PrecachePlayerSounds(strcat(s, ".sounds"));
\r
1399 s = cvar_string("sv_defaultplayermodel_blue");
\r
1402 precache_model(s);
\r
1403 PrecachePlayerSounds(strcat(s, ".sounds"));
\r
1405 s = cvar_string("sv_defaultplayermodel_yellow");
\r
1408 precache_model(s);
\r
1409 PrecachePlayerSounds(strcat(s, ".sounds"));
\r
1411 s = cvar_string("sv_defaultplayermodel_pink");
\r
1414 precache_model(s);
\r
1415 PrecachePlayerSounds(strcat(s, ".sounds"));
\r
1417 s = cvar_string("sv_defaultplayermodel");
\r
1420 precache_model(s);
\r
1421 PrecachePlayerSounds(strcat(s, ".sounds"));
\r
1427 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
\r
1428 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
\r
1429 if(cvar("g_healthsize"))
\r
1430 precache_sound("misc/macro_footstep.wav");
\r
1433 // gore and miscellaneous sounds
\r
1434 //precache_sound ("misc/h2ohit.wav");
\r
1435 precache_model ("models/grabber.md3");
\r
1436 precache_sound ("misc/armorimpact.wav");
\r
1437 precache_sound ("misc/bodyimpact1.wav");
\r
1438 precache_sound ("misc/bodyimpact2.wav");
\r
1439 precache_sound ("misc/gib.wav");
\r
1440 precache_sound ("misc/gib_splat01.wav");
\r
1441 precache_sound ("misc/gib_splat02.wav");
\r
1442 precache_sound ("misc/gib_splat03.wav");
\r
1443 precache_sound ("misc/gib_splat04.wav");
\r
1444 precache_sound ("misc/hit.wav");
\r
1445 precache_sound ("misc/typehit.wav");
\r
1446 precache_sound ("misc/unavailable.wav");
\r
1447 precache_sound ("misc/forbidden.wav");
\r
1448 precache_sound ("misc/beep.wav");
\r
1449 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
\r
1450 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
\r
1451 if(cvar("g_healthsize"))
\r
1452 precache_sound("misc/macro_hitground.wav");
\r
1453 precache_sound ("misc/null.wav");
\r
1454 precache_sound ("misc/spawn.wav");
\r
1455 precache_sound ("misc/talk.wav");
\r
1456 precache_sound ("misc/teleport.wav");
\r
1457 precache_sound ("misc/poweroff.wav");
\r
1458 precache_sound ("player/lava.wav");
\r
1459 precache_sound ("player/slime.wav");
\r
1460 precache_sound ("player/digest.wav");
\r
1461 precache_sound ("misc/health_regen.wav");
\r
1462 precache_sound ("misc/armor_regen.wav");
\r
1463 precache_sound ("misc/power_fail.wav");
\r
1466 precache_sound ("misc/jetpack_fly.wav");
\r
1468 precache_model ("models/sprites/0.spr32");
\r
1469 precache_model ("models/sprites/1.spr32");
\r
1470 precache_model ("models/sprites/2.spr32");
\r
1471 precache_model ("models/sprites/3.spr32");
\r
1472 precache_model ("models/sprites/4.spr32");
\r
1473 precache_model ("models/sprites/5.spr32");
\r
1474 precache_model ("models/sprites/6.spr32");
\r
1475 precache_model ("models/sprites/7.spr32");
\r
1476 precache_model ("models/sprites/8.spr32");
\r
1477 precache_model ("models/sprites/9.spr32");
\r
1478 precache_model ("models/sprites/10.spr32");
\r
1480 // common weapon precaches
\r
1481 precache_sound ("weapons/weapon_switch.wav");
\r
1482 precache_sound ("weapons/weaponpickup.wav");
\r
1484 // precache display digits
\r
1485 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
\r
1489 e = get_weaponinfo(i);
\r
1490 for(w = 1; fexists(strcat("models/weapons/v_", e.netname, "_digit1-", ftos(w) , ".md3")); w++)
\r
1491 precache_model (strcat("models/weapons/v_", e.netname, "_digit1-", ftos(w) , ".md3"));
\r
1492 for(w = 1; fexists(strcat("models/weapons/v_", e.netname, "_digit2-", ftos(w) , ".md3")); w++)
\r
1493 precache_model (strcat("models/weapons/v_", e.netname, "_digit2-", ftos(w) , ".md3"));
\r
1496 for(i = 0; i < 8; i += 1)
\r
1497 precache_sound (strcat("weapons/hit", ftos(i), ".wav"));
\r
1499 if (cvar("sv_precacheweapons"))
\r
1501 //precache weapon models/sounds
\r
1504 while (wep <= WEP_LAST)
\r
1506 weapon_action(wep, WR_PRECACHE);
\r
1511 precache_model("models/elaser.mdl");
\r
1512 precache_model("models/laser.mdl");
\r
1513 precache_model("models/ebomb.mdl");
\r
1516 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
\r
1518 if (!self.noise && self.music) // quake 3 uses the music field
\r
1519 self.noise = self.music;
\r
1521 // plays music for the level if there is any
\r
1524 precache_sound (self.noise);
\r
1525 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
\r
1530 // sorry, but using \ in macros breaks line numbers
\r
1531 #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
1532 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
\r
1533 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
\r
1535 // WARNING: this kills the trace globals
\r
1536 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
\r
1537 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
\r
1539 #define INITPRIO_FIRST 0
\r
1540 #define INITPRIO_GAMETYPE 0
\r
1541 #define INITPRIO_GAMETYPE_FALLBACK 1
\r
1542 #define INITPRIO_CVARS 5
\r
1543 #define INITPRIO_FINDTARGET 10
\r
1544 #define INITPRIO_DROPTOFLOOR 20
\r
1545 #define INITPRIO_SETLOCATION 90
\r
1546 #define INITPRIO_LINKDOORS 91
\r
1547 #define INITPRIO_LAST 99
\r
1549 .void(void) initialize_entity;
\r
1550 .float initialize_entity_order;
\r
1551 .entity initialize_entity_next;
\r
1552 entity initialize_entity_first;
\r
1554 void make_safe_for_remove(entity e)
\r
1556 if (e.initialize_entity)
\r
1559 for (ent = initialize_entity_first; ent; )
\r
1561 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
\r
1563 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
\r
1564 // skip it in linked list
\r
1567 prev.initialize_entity_next = ent.initialize_entity_next;
\r
1568 ent = prev.initialize_entity_next;
\r
1572 initialize_entity_first = ent.initialize_entity_next;
\r
1573 ent = initialize_entity_first;
\r
1579 ent = ent.initialize_entity_next;
\r
1585 void objerror(string s)
\r
1587 make_safe_for_remove(self);
\r
1588 objerror_builtin(s);
\r
1591 void remove_unsafely(entity e)
\r
1593 remove_builtin(e);
\r
1596 void remove_safely(entity e)
\r
1598 make_safe_for_remove(e);
\r
1599 remove_builtin(e);
\r
1602 void InitializeEntity(entity e, void(void) func, float order)
\r
1606 if (!e || e.initialize_entity)
\r
1608 // make a proxy initializer entity
\r
1612 e.classname = "initialize_entity";
\r
1616 e.initialize_entity = func;
\r
1617 e.initialize_entity_order = order;
\r
1619 cur = initialize_entity_first;
\r
1622 if (!cur || cur.initialize_entity_order > order)
\r
1624 // insert between prev and cur
\r
1626 prev.initialize_entity_next = e;
\r
1628 initialize_entity_first = e;
\r
1629 e.initialize_entity_next = cur;
\r
1633 cur = cur.initialize_entity_next;
\r
1636 void InitializeEntitiesRun()
\r
1638 entity startoflist;
\r
1639 startoflist = initialize_entity_first;
\r
1640 initialize_entity_first = world;
\r
1641 for (self = startoflist; self; )
\r
1644 var void(void) func;
\r
1645 e = self.initialize_entity_next;
\r
1646 func = self.initialize_entity;
\r
1647 self.initialize_entity_order = 0;
\r
1648 self.initialize_entity = func_null;
\r
1649 self.initialize_entity_next = world;
\r
1650 if (self.classname == "initialize_entity")
\r
1653 e_old = self.enemy;
\r
1654 remove_builtin(self);
\r
1657 //dprint("Delayed initialization: ", self.classname, "\n");
\r
1663 .float uncustomizeentityforclient_set;
\r
1664 .void(void) uncustomizeentityforclient;
\r
1665 void(void) SUB_Nullpointer = #0;
\r
1666 void UncustomizeEntitiesRun()
\r
1670 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
\r
1671 self.uncustomizeentityforclient();
\r
1674 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
\r
1676 e.customizeentityforclient = customizer;
\r
1677 e.uncustomizeentityforclient = uncustomizer;
\r
1678 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
\r
1681 .float nottargeted;
\r
1682 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
\r
1684 void() SUB_Remove;
\r
1685 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
\r
1689 if (e.classname == "")
\r
1690 e.classname = "net_linked";
\r
1692 if (e.model == "" || self.modelindex == 0)
\r
1696 setmodel(e, "null");
\r
1697 setsize(e, mi, ma);
\r
1700 e.SendEntity = sendfunc;
\r
1701 e.SendFlags = 0xFFFFFF;
\r
1704 e.effects |= EF_NODEPTHTEST;
\r
1708 e.nextthink = time + dt;
\r
1709 e.think = SUB_Remove;
\r
1713 void adaptor_think2touch()
\r
1722 void adaptor_think2use()
\r
1727 activator = world;
\r
1734 // deferred dropping
\r
1735 void DropToFloor_Handler()
\r
1737 droptofloor_builtin();
\r
1738 self.dropped_origin = self.origin;
\r
1741 void droptofloor()
\r
1743 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
\r
1748 float trace_hits_box_a0, trace_hits_box_a1;
\r
1750 float trace_hits_box_1d(float end, float thmi, float thma)
\r
1754 // just check if x is in range
\r
1762 // do the trace with respect to x
\r
1763 // 0 -> end has to stay in thmi -> thma
\r
1764 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
\r
1765 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
\r
1766 if (trace_hits_box_a0 > trace_hits_box_a1)
\r
1772 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
\r
1777 // now it is a trace from 0 to end
\r
1779 trace_hits_box_a0 = 0;
\r
1780 trace_hits_box_a1 = 1;
\r
1782 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
\r
1784 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
\r
1786 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
\r
1792 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
\r
1794 return trace_hits_box(start, end, thmi - ma, thma - mi);
\r
1797 float SUB_NoImpactCheck()
\r
1799 // zero hitcontents = this is not the real impact, but either the
\r
1800 // mirror-impact of something hitting the projectile instead of the
\r
1801 // projectile hitting the something, or a touchareagrid one. Neither of
\r
1802 // these stop the projectile from moving, so...
\r
1803 if(trace_dphitcontents == 0)
\r
1805 dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
\r
1808 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
\r
1810 if (other == world && self.size != '0 0 0')
\r
1813 tic = self.velocity * sys_frametime;
\r
1814 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
\r
1815 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
\r
1816 if (trace_fraction >= 1)
\r
1818 dprint("Odd... did not hit...?\n");
\r
1820 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
\r
1822 dprint("Detected and prevented the sky-grapple bug.\n");
\r
1830 #define SUB_OwnerCheck() (other && (other == self.owner))
\r
1832 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
\r
1834 if(SUB_OwnerCheck())
\r
1836 if(SUB_NoImpactCheck())
\r
1841 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
\r
1842 UpdateCSQCProjectileNextFrame(self);
\r
1845 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
\r
1847 float MAX_IPBAN_URIS = 16;
\r
1849 float URI_GET_DISCARD = 0;
\r
1850 float URI_GET_IPBAN = 1;
\r
1851 float URI_GET_IPBAN_END = 16;
\r
1853 void URI_Get_Callback(float id, float status, string data)
\r
1855 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
\r
1857 dprint("\nEnd of data.\n");
\r
1859 if (id == URI_GET_DISCARD)
\r
1863 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
\r
1865 // online ban list
\r
1866 OnlineBanList_URI_Get_Callback(id, status, data);
\r
1870 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
\r
1874 void print_to(entity e, string s)
\r
1877 sprint(e, strcat(s, "\n"));
\r
1882 string getrecords(float page) // 50 records per page
\r
1896 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
\r
1898 if (MapInfo_Get_ByID(i))
\r
1900 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
\r
1903 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
\r
1904 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
\r
1912 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
\r
1914 if (MapInfo_Get_ByID(i))
\r
1916 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "time")));
\r
1919 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "netname"));
\r
1920 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
\r
1928 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
\r
1930 if (MapInfo_Get_ByID(i))
\r
1932 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "time")));
\r
1935 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "netname"));
\r
1936 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
\r
1942 MapInfo_ClearTemps();
\r
1944 if (s == "" && page == 0)
\r
1945 return "No records are available on this server.\n";
\r
1950 string getrankings()
\r
1961 map = GetMapname();
\r
1963 for (i = 1; i <= RANKINGS_CNT; ++i)
\r
1965 t = race_GetTime(i);
\r
1968 n = race_GetName(i);
\r
1969 p = race_PlaceName(i);
\r
1970 s = strcat(s, strpad(8, p), " ", strpad(-8, TIME_ENCODED_TOSTRING(t)), " ", n, "\n");
\r
1973 MapInfo_ClearTemps();
\r
1976 return strcat("No records are available for the map: ", map, "\n");
\r
1978 return strcat("Records for ", map, ":\n", s);
\r
1981 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
\r
1984 vector start, org, delta, end, enddown, mstart;
\r
1986 m = e.dphitcontentsmask;
\r
1987 e.dphitcontentsmask = goodcontents | badcontents;
\r
1990 delta = world.maxs - world.mins;
\r
1992 for (i = 0; i < attempts; ++i)
\r
1994 start_x = org_x + random() * delta_x;
\r
1995 start_y = org_y + random() * delta_y;
\r
1996 start_z = org_z + random() * delta_z;
\r
1998 // rule 1: start inside world bounds, and outside
\r
1999 // solid, and don't start from somewhere where you can
\r
2000 // fall down to evil
\r
2001 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
\r
2002 if (trace_fraction >= 1)
\r
2004 if (trace_startsolid)
\r
2006 if (trace_dphitcontents & badcontents)
\r
2008 if (trace_dphitq3surfaceflags & badsurfaceflags)
\r
2011 // rule 2: if we are too high, lower the point
\r
2012 if (trace_fraction * delta_z > maxaboveground)
\r
2013 start = trace_endpos + '0 0 1' * maxaboveground;
\r
2014 enddown = trace_endpos;
\r
2016 // rule 3: make sure we aren't outside the map. This only works
\r
2017 // for somewhat well formed maps. A good rule of thumb is that
\r
2018 // the map should have a convex outside hull.
\r
2019 // these can be traceLINES as we already verified the starting box
\r
2020 mstart = start + 0.5 * (e.mins + e.maxs);
\r
2021 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
\r
2022 if (trace_fraction >= 1)
\r
2024 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
\r
2025 if (trace_fraction >= 1)
\r
2027 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
\r
2028 if (trace_fraction >= 1)
\r
2030 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
\r
2031 if (trace_fraction >= 1)
\r
2033 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
\r
2034 if (trace_fraction >= 1)
\r
2037 // find a random vector to "look at"
\r
2038 end_x = org_x + random() * delta_x;
\r
2039 end_y = org_y + random() * delta_y;
\r
2040 end_z = org_z + random() * delta_z;
\r
2041 end = start + normalize(end - start) * vlen(delta);
\r
2043 // rule 4: start TO end must not be too short
\r
2044 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
\r
2045 if (trace_startsolid)
\r
2047 if (trace_fraction < minviewdistance / vlen(delta))
\r
2050 // rule 5: don't want to look at sky
\r
2051 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
\r
2054 // rule 6: we must not end up in trigger_hurt
\r
2055 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
\r
2057 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
\r
2064 e.dphitcontentsmask = m;
\r
2068 setorigin(e, start);
\r
2069 e.angles = vectoangles(end - start);
\r
2070 dprint("Needed ", ftos(i + 1), " attempts\n");
\r
2077 float zcurveparticles_effectno;
\r
2078 vector zcurveparticles_start;
\r
2079 float zcurveparticles_spd;
\r
2081 void endzcurveparticles()
\r
2083 if(zcurveparticles_effectno)
\r
2086 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
\r
2088 zcurveparticles_effectno = 0;
\r
2091 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
\r
2093 spd = bound(0, floor(spd / 16), 32767);
\r
2094 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
\r
2096 endzcurveparticles();
\r
2097 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
\r
2098 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
\r
2099 WriteShort(MSG_BROADCAST, effectno);
\r
2100 WriteCoord(MSG_BROADCAST, start_x);
\r
2101 WriteCoord(MSG_BROADCAST, start_y);
\r
2102 WriteCoord(MSG_BROADCAST, start_z);
\r
2103 zcurveparticles_effectno = effectno;
\r
2104 zcurveparticles_start = start;
\r
2107 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
\r
2108 WriteCoord(MSG_BROADCAST, end_x);
\r
2109 WriteCoord(MSG_BROADCAST, end_y);
\r
2110 WriteCoord(MSG_BROADCAST, end_z);
\r
2111 WriteCoord(MSG_BROADCAST, end_dz);
\r
2112 zcurveparticles_spd = spd;
\r
2115 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
\r
2118 vector vecxy, velxy;
\r
2120 vecxy = end - start;
\r
2125 if (vlen(velxy) < 0.000001 * fabs(vel_z))
\r
2127 endzcurveparticles();
\r
2128 trailparticles(world, effectno, start, end);
\r
2132 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
\r
2133 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
\r
2136 string GetGametype(); // g_world.qc
\r
2137 void write_recordmarker(entity pl, float tstart, float dt)
\r
2139 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
\r
2141 // also write a marker into demo files for demotc-race-record-extractor to find
\r
2144 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
\r
2145 " ", ftos(tstart), " ", ftos(dt), "\n"));
\r
2148 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter)
\r
2150 switch(self.owner.cvar_cl_gunalign)
\r
2161 if(allowcenter) // 2: allow center handedness
\r
2174 if(allowcenter) // 2: allow center handedness
\r
2190 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
\r
2195 if (cvar("g_shootfromeye"))
\r
2199 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
\r
2207 else if (cvar("g_shootfromcenter"))
\r
2211 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
\r
2219 else if ((s = cvar_string("g_shootfromfixedorigin")) != "")
\r
2229 else if (cvar("g_shootfromclient"))
\r
2231 vecs = shotorg_adjustfromclient(vecs, y_is_right, (cvar("g_shootfromclient") >= 2));
\r
2238 void attach_sameorigin(entity e, entity to, string tag)
\r
2240 vector org, t_forward, t_left, t_up, e_forward, e_up;
\r
2241 vector org0, ang0;
\r
2247 org = e.origin - gettaginfo(to, gettagindex(to, tag));
\r
2248 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
\r
2249 t_forward = v_forward * tagscale;
\r
2250 t_left = v_right * -tagscale;
\r
2251 t_up = v_up * tagscale;
\r
2253 e.origin_x = org * t_forward;
\r
2254 e.origin_y = org * t_left;
\r
2255 e.origin_z = org * t_up;
\r
2257 // current forward and up directions
\r
2258 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
\r
2259 e.angles = AnglesTransform_FromVAngles(e.angles);
\r
2261 e.angles = AnglesTransform_FromAngles(e.angles);
\r
2262 fixedmakevectors(e.angles);
\r
2264 // untransform forward, up!
\r
2265 e_forward_x = v_forward * t_forward;
\r
2266 e_forward_y = v_forward * t_left;
\r
2267 e_forward_z = v_forward * t_up;
\r
2268 e_up_x = v_up * t_forward;
\r
2269 e_up_y = v_up * t_left;
\r
2270 e_up_z = v_up * t_up;
\r
2272 e.angles = fixedvectoangles2(e_forward, e_up);
\r
2273 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
\r
2274 e.angles = AnglesTransform_ToVAngles(e.angles);
\r
2276 e.angles = AnglesTransform_ToAngles(e.angles);
\r
2278 setattachment(e, to, tag);
\r
2279 setorigin(e, e.origin);
\r
2282 void detach_sameorigin(entity e)
\r
2285 org = gettaginfo(e, 0);
\r
2286 e.angles = fixedvectoangles2(v_forward, v_up);
\r
2287 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
\r
2288 e.angles = AnglesTransform_ToVAngles(e.angles);
\r
2290 e.angles = AnglesTransform_ToAngles(e.angles);
\r
2291 setorigin(e, org);
\r
2292 setattachment(e, world, "");
\r
2293 setorigin(e, e.origin);
\r
2296 void follow_sameorigin(entity e, entity to)
\r
2298 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
\r
2299 e.aiment = to; // make the hole follow bmodel
\r
2300 e.punchangle = to.angles; // the original angles of bmodel
\r
2301 e.view_ofs = e.origin - to.origin; // relative origin
\r
2302 e.v_angle = e.angles - to.angles; // relative angles
\r
2305 void unfollow_sameorigin(entity e)
\r
2307 e.movetype = MOVETYPE_NONE;
\r
2310 entity gettaginfo_relative_ent;
\r
2311 vector gettaginfo_relative(entity e, float tag)
\r
2313 if (!gettaginfo_relative_ent)
\r
2315 gettaginfo_relative_ent = spawn();
\r
2316 gettaginfo_relative_ent.effects = EF_NODRAW;
\r
2318 gettaginfo_relative_ent.model = e.model;
\r
2319 gettaginfo_relative_ent.modelindex = e.modelindex;
\r
2320 gettaginfo_relative_ent.frame = e.frame;
\r
2321 return gettaginfo(gettaginfo_relative_ent, tag);
\r
2324 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
\r
2328 if (pl.soundentity.cnt & p)
\r
2330 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn, 0);
\r
2331 pl.soundentity.cnt |= p;
\r
2334 void SoundEntity_StopSound(entity pl, float chan)
\r
2338 if (pl.soundentity.cnt & p)
\r
2340 stopsoundto(MSG_ALL, pl.soundentity, chan);
\r
2341 pl.soundentity.cnt &~= p;
\r
2345 void SoundEntity_Attach(entity pl)
\r
2347 pl.soundentity = spawn();
\r
2348 pl.soundentity.classname = "soundentity";
\r
2349 pl.soundentity.owner = pl;
\r
2350 setattachment(pl.soundentity, pl, "");
\r
2351 setmodel(pl.soundentity, "null");
\r
2354 void SoundEntity_Detach(entity pl)
\r
2357 for (i = 0; i <= 7; ++i)
\r
2358 SoundEntity_StopSound(pl, i);
\r
2362 float ParseCommandPlayerSlotTarget_firsttoken;
\r
2363 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
\r
2371 ParseCommandPlayerSlotTarget_firsttoken = -1;
\r
2375 if (substring(argv(idx), 0, 1) == "#")
\r
2377 s = substring(argv(idx), 1, -1);
\r
2385 ParseCommandPlayerSlotTarget_firsttoken = idx;
\r
2386 if (s == ftos(stof(s)))
\r
2388 e = edict_num(stof(s));
\r
2389 if (e.flags & FL_CLIENT)
\r
2395 // it must be a nick name
\r
2398 ParseCommandPlayerSlotTarget_firsttoken = idx;
\r
2401 FOR_EACH_CLIENT(head)
\r
2402 if (head.netname == s)
\r
2410 s = strdecolorize(s);
\r
2412 FOR_EACH_CLIENT(head)
\r
2413 if (strdecolorize(head.netname) == s)
\r
2428 float modeleffect_SendEntity(entity to, float sf)
\r
2431 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
\r
2434 if(self.velocity != '0 0 0')
\r
2436 if(self.angles != '0 0 0')
\r
2438 if(self.avelocity != '0 0 0')
\r
2441 WriteByte(MSG_ENTITY, f);
\r
2442 WriteShort(MSG_ENTITY, self.modelindex);
\r
2443 WriteByte(MSG_ENTITY, self.skin);
\r
2444 WriteByte(MSG_ENTITY, self.frame);
\r
2445 WriteCoord(MSG_ENTITY, self.origin_x);
\r
2446 WriteCoord(MSG_ENTITY, self.origin_y);
\r
2447 WriteCoord(MSG_ENTITY, self.origin_z);
\r
2450 WriteCoord(MSG_ENTITY, self.velocity_x);
\r
2451 WriteCoord(MSG_ENTITY, self.velocity_y);
\r
2452 WriteCoord(MSG_ENTITY, self.velocity_z);
\r
2456 WriteCoord(MSG_ENTITY, self.angles_x);
\r
2457 WriteCoord(MSG_ENTITY, self.angles_y);
\r
2458 WriteCoord(MSG_ENTITY, self.angles_z);
\r
2462 WriteCoord(MSG_ENTITY, self.avelocity_x);
\r
2463 WriteCoord(MSG_ENTITY, self.avelocity_y);
\r
2464 WriteCoord(MSG_ENTITY, self.avelocity_z);
\r
2466 WriteShort(MSG_ENTITY, self.scale * 256.0);
\r
2467 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
\r
2468 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
\r
2469 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
\r
2470 WriteByte(MSG_ENTITY, self.alpha * 255.0);
\r
2475 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
2480 e.classname = "modeleffect";
\r
2486 e.avelocity = angv;
\r
2488 e.teleport_time = t1;
\r
2492 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
\r
2496 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
\r
2499 sz = max(e.scale, e.scale2);
\r
2500 setsize(e, e.mins * sz, e.maxs * sz);
\r
2501 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
\r
2504 float portrait_SendEntity(entity to, float sf)
\r
2506 if(to != self.enemy)
\r
2509 WriteByte(MSG_ENTITY, ENT_CLIENT_PORTRAIT);
\r
2511 WriteString(MSG_ENTITY, self.owner.playermodel);
\r
2512 WriteByte(MSG_ENTITY, stof(self.owner.playerskin));
\r
2513 WriteString(MSG_ENTITY, self.owner.netname);
\r
2518 void portrait(entity pl, entity targ)
\r
2522 e.classname = "portrait";
\r
2526 Net_LinkEntity(e, FALSE, 0, portrait_SendEntity);
\r
2529 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
\r
2531 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
\r
2534 float randombit(float bits)
\r
2536 if not(bits & (bits-1)) // this ONLY holds for powers of two!
\r
2545 for(f = 1; f <= bits; f *= 2)
\r
2554 r = (r - 1) / (n - 1);
\r
2561 float randombits(float bits, float k, float error_return)
\r
2565 while(k > 0 && bits != r)
\r
2567 r += randombit(bits - r);
\r
2576 void randombit_test(float bits, float iter)
\r
2580 print(ftos(randombit(bits)), "\n");
\r
2585 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
\r
2587 if(halflifedist > 0)
\r
2588 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
\r
2589 else if(halflifedist < 0)
\r
2590 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
\r
2599 #define cvar_string_normal cvar_string_builtin
\r
2600 #define cvar_normal cvar_builtin
\r
2602 string cvar_string_normal(string n)
\r
2604 if not(cvar_type(n) & 1)
\r
2605 backtrace(strcat("Attempt to access undefined cvar: ", n));
\r
2606 return cvar_string_builtin(n);
\r
2609 float cvar_normal(string n)
\r
2611 return stof(cvar_string_normal(n));
\r
2614 #define cvar_set_normal cvar_set_builtin
\r
2616 void defer_think()
\r
2621 self = self.owner;
\r
2622 oself.think = SUB_Remove;
\r
2623 oself.nextthink = time;
\r
2629 Execute func() after time + fdelay.
\r
2630 self when func is executed = self when defer is called
\r
2632 void defer(float fdelay, void() func)
\r
2639 e.think = defer_think;
\r
2640 e.nextthink = time + fdelay;
\r
2643 // returns 1 if player is at minimum size and 0 if player is at normal size
\r
2644 float playersize_micro(entity e)
\r
2646 if(!cvar("g_healthsize"))
\r
2648 return bound(0, (e.health / cvar("g_healthsize_center") - 1) / (cvar("g_healthsize_min") / cvar("g_healthsize_center") - 1), 1);
\r
2650 // returns 0 if player is at normal size and 1 if player is at maximum size
\r
2651 float playersize_macro(entity e)
\r
2653 if(!cvar("g_healthsize"))
\r
2655 return 1 - bound(0, (e.health / cvar("g_healthsize_max") - 1) / (cvar("g_healthsize_center") / cvar("g_healthsize_max") - 1), 1);
\r
2658 // returns 1 if the player is close to a wall
\r
2659 float check_close_to_wall(float threshold) {
\r
2660 //TODO: This check should be moved somehow for this to be a common utility
\r
2661 if (!cvar("sv_dodging_wall_dodging"))
\r
2664 vector trace_start;
\r
2667 trace_start = self.origin;
\r
2669 trace_end = self.origin + (1000*v_right);
\r
2670 tracebox(trace_start, self.mins, self.maxs, trace_end, TRUE, self);
\r
2671 if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold)
\r
2674 trace_end = self.origin - (1000*v_right);
\r
2675 tracebox(trace_start, self.mins, self.maxs, trace_end, TRUE, self);
\r
2676 if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold)
\r
2679 trace_end = self.origin + (1000*v_forward);
\r
2680 tracebox(trace_start, self.mins, self.maxs, trace_end, TRUE, self);
\r
2681 if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold)
\r
2684 trace_end = self.origin - (1000*v_forward);
\r
2685 tracebox(trace_start, self.mins, self.maxs, trace_end, TRUE, self);
\r
2686 if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold)
\r
2692 float check_close_to_ground(float threshold) {
\r
2693 if (self.flags & FL_ONGROUND)
\r