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 // you need an init method to set them somewhere
\r
185 // default values simply become constant
\r
186 const vector PL_VIEW_OFS;
\r
187 const vector PL_MIN;
\r
188 const vector PL_MAX;
\r
189 const vector PL_CROUCH_VIEW_OFS;
\r
190 const vector PL_CROUCH_MIN;
\r
191 const vector PL_CROUCH_MAX;
\r
193 float spawnpoint_nag;
\r
194 void relocate_spawnpoint()
\r
196 // this code wouldn't work anyways, they're constants by having default assignment in a header.
\r
198 PL_VIEW_OFS = stov(cvar_string("sv_player_viewoffset"));
\r
199 PL_MIN = stov(cvar_string("sv_player_mins"));
\r
200 PL_MAX = stov(cvar_string("sv_player_maxs"));
\r
201 PL_CROUCH_VIEW_OFS = stov(cvar_string("sv_player_crouch_viewoffset"));
\r
202 PL_CROUCH_MIN = stov(cvar_string("sv_player_crouch_mins"));
\r
203 PL_CROUCH_MAX = stov(cvar_string("sv_player_crouch_maxs"));
\r
206 // nudge off the floor
\r
207 setorigin(self, self.origin + '0 0 1');
\r
209 tracebox(self.origin, PL_MIN, PL_MAX, self.origin, TRUE, self);
\r
210 if (trace_startsolid)
\r
214 self.mins = PL_MIN;
\r
215 self.maxs = PL_MAX;
\r
216 if (!move_out_of_solid(self))
\r
217 objerror("could not get out of solid at all!");
\r
218 print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1'));
\r
219 print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x));
\r
220 print(" ", ftos(self.origin_y - o_y));
\r
221 print(" ", ftos(self.origin_z - o_z), "'\n");
\r
222 if (cvar("g_spawnpoints_auto_move_out_of_solid"))
\r
224 if (!spawnpoint_nag)
\r
225 print("\{1}^1NOTE: this map needs FIXING (it contains spawnpoints in solid, see server log)\n");
\r
226 spawnpoint_nag = 1;
\r
230 setorigin(self, o);
\r
231 self.mins = self.maxs = '0 0 0';
\r
232 objerror("player spawn point in solid, mapper sucks!\n");
\r
237 if (cvar("g_spawnpoints_autodrop"))
\r
239 setsize(self, PL_MIN, PL_MAX);
\r
243 self.use = spawnpoint_use;
\r
244 self.team_saved = self.team;
\r
248 if (have_team_spawns != 0)
\r
250 have_team_spawns = 1;
\r
252 if (cvar("r_showbboxes"))
\r
254 // show where spawnpoints point at too
\r
255 makevectors(self.angles);
\r
258 e.classname = "info_player_foo";
\r
259 setorigin(e, self.origin + v_forward * 24);
\r
260 setsize(e, '-8 -8 -8', '8 8 8');
\r
261 e.solid = SOLID_TRIGGER;
\r
265 #define strstr strstrofs
\r
267 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
\r
268 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
\r
269 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
\r
270 // BE CONSTANT OR strzoneD!
\r
271 float strstr(string haystack, string needle, float offset)
\r
275 len = strlen(needle);
\r
276 endpos = strlen(haystack) - len;
\r
277 while(offset <= endpos)
\r
279 found = substring(haystack, offset, len);
\r
280 if(found == needle)
\r
282 offset = offset + 1;
\r
288 float NUM_NEAREST_ENTITIES = 4;
\r
289 entity nearest_entity[NUM_NEAREST_ENTITIES];
\r
290 float nearest_length[NUM_NEAREST_ENTITIES];
\r
291 entity findnearest(vector point, .string field, string value, vector axismod)
\r
302 localhead = find(world, field, value);
\r
305 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
\r
306 dist = localhead.oldorigin;
\r
308 dist = localhead.origin;
\r
309 dist = dist - point;
\r
310 dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
\r
313 for (i = 0; i < num_nearest; ++i)
\r
315 if (len < nearest_length[i])
\r
319 // now i tells us where to insert at
\r
320 // INSERTION SORT! YOU'VE SEEN IT! RUN!
\r
321 if (i < NUM_NEAREST_ENTITIES)
\r
323 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
\r
325 nearest_length[j + 1] = nearest_length[j];
\r
326 nearest_entity[j + 1] = nearest_entity[j];
\r
328 nearest_length[i] = len;
\r
329 nearest_entity[i] = localhead;
\r
330 if (num_nearest < NUM_NEAREST_ENTITIES)
\r
331 num_nearest = num_nearest + 1;
\r
334 localhead = find(localhead, field, value);
\r
337 // now use the first one from our list that we can see
\r
338 for (i = 0; i < num_nearest; ++i)
\r
340 traceline(point, nearest_entity[i].origin, TRUE, world);
\r
341 if (trace_fraction == 1)
\r
345 dprint("Nearest point (");
\r
346 dprint(nearest_entity[0].netname);
\r
347 dprint(") is not visible, using a visible one.\n");
\r
349 return nearest_entity[i];
\r
353 if (num_nearest == 0)
\r
356 dprint("Not seeing any location point, using nearest as fallback.\n");
\r
358 dprint("Candidates were: ");
\r
359 for(j = 0; j < num_nearest; ++j)
\r
363 dprint(nearest_entity[j].netname);
\r
368 return nearest_entity[0];
\r
371 void spawnfunc_target_location()
\r
373 self.classname = "target_location";
\r
374 // location name in netname
\r
375 // eventually support: count, teamgame selectors, line of sight?
\r
378 void spawnfunc_info_location()
\r
380 self.classname = "target_location";
\r
381 self.message = self.netname;
\r
384 string NearestLocation(vector p)
\r
389 loc = findnearest(p, classname, "target_location", '1 1 1');
\r
396 loc = findnearest(p, target, "###item###", '1 1 4');
\r
403 string formatmessage(string msg)
\r
410 string replacement;
\r
414 WarpZone_crosshair_trace(self);
\r
415 cursor = trace_endpos;
\r
416 cursor_ent = trace_ent;
\r
420 break; // too many replacements
\r
423 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
\r
424 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
\r
437 replacement = substring(msg, p, 2);
\r
438 escape = substring(msg, p + 1, 1);
\r
442 else if (escape == "\\")
\r
443 replacement = "\\";
\r
444 else if (escape == "n")
\r
445 replacement = "\n";
\r
446 else if (escape == "a")
\r
447 replacement = ftos(floor(self.armorvalue));
\r
448 else if (escape == "h")
\r
449 replacement = ftos(floor(self.health));
\r
450 else if (escape == "l")
\r
451 replacement = NearestLocation(self.origin);
\r
452 else if (escape == "y")
\r
453 replacement = NearestLocation(cursor);
\r
454 else if (escape == "d")
\r
455 replacement = NearestLocation(self.death_origin);
\r
456 else if (escape == "w") {
\r
460 wep = self.switchweapon;
\r
463 replacement = W_Name(wep);
\r
464 } else if (escape == "W") {
\r
465 replacement = "batteries"; // ;)
\r
466 } else if (escape == "x") {
\r
467 replacement = cursor_ent.netname;
\r
468 if (!replacement || !cursor_ent)
\r
469 replacement = "nothing";
\r
470 } else if (escape == "p") {
\r
471 if (self.last_selected_player)
\r
472 replacement = self.last_selected_player.netname;
\r
474 replacement = "(nobody)";
\r
475 } else if (escape == "s")
\r
476 replacement = ftos(vlen(self.velocity - self.velocity_z * '0 0 1'));
\r
477 else if (escape == "S")
\r
478 replacement = ftos(vlen(self.velocity));
\r
479 else if (escape == "v") {
\r
480 float weapon_number;
\r
481 local entity stats;
\r
483 if(self.classname == "spectator")
\r
484 stats = self.enemy;
\r
488 weapon_number = stats.weapon;
\r
490 if (!weapon_number)
\r
491 weapon_number = stats.switchweapon;
\r
493 if (!weapon_number)
\r
494 weapon_number = stats.cnt;
\r
496 if(stats.cvar_cl_accuracy_data_share && stats.stats_fired[weapon_number - 1])
\r
497 replacement = ftos(bound(0, floor(100 * stats.stats_hit[weapon_number - 1] / stats.stats_fired[weapon_number - 1]), 100));
\r
499 replacement = "~"; // or something to indicate NULL, not available
\r
502 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
\r
503 p = p + strlen(replacement);
\r
508 float boolean(float value) { // if value is 0 return FALSE (0), otherwise return TRUE (1)
\r
509 return (value == 0) ? FALSE : TRUE;
\r
517 0: sends the request
\r
518 >0: receives a cvar from name=argv(f) value=argv(f+1)
\r
520 void GetCvars_handleString(string thisname, float f, .string field, string name)
\r
525 strunzone(self.field);
\r
526 self.field = string_null;
\r
530 if (thisname == name)
\r
533 strunzone(self.field);
\r
534 self.field = strzone(argv(f + 1));
\r
538 stuffcmd(self, strcat("sendcvar ", name, "\n"));
\r
540 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
\r
542 GetCvars_handleString(thisname, f, field, name);
\r
543 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
\r
544 if (thisname == name)
\r
547 s = func(strcat1(self.field));
\r
548 if (s != self.field)
\r
550 strunzone(self.field);
\r
551 self.field = strzone(s);
\r
555 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
\r
562 if (thisname == name)
\r
563 self.field = stof(argv(f + 1));
\r
566 stuffcmd(self, strcat("sendcvar ", name, "\n"));
\r
568 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
\r
575 if (thisname == name)
\r
579 self.field = stof(argv(f + 1));
\r
588 stuffcmd(self, strcat("sendcvar ", name, "\n"));
\r
591 string W_FixWeaponOrder_ForceComplete(string s);
\r
592 string W_FixWeaponOrder_AllowIncomplete(string s);
\r
593 float w_getbestweapon(entity e);
\r
594 void GetCvars(float f)
\r
598 s = strcat1(argv(f));
\r
599 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
\r
600 GetCvars_handleFloat(s, f, cvar_scr_centertime, "scr_centertime");
\r
601 GetCvars_handleFloat(s, f, cvar_cl_shownames, "cl_shownames");
\r
602 GetCvars_handleString(s, f, cvar_g_voretournamentversion, "g_voretournamentversion");
\r
603 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
\r
604 GetCvars_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
\r
605 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete);
\r
606 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
\r
607 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
\r
608 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
\r
609 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
\r
610 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
\r
611 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
\r
612 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
\r
613 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
\r
614 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
\r
615 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
\r
616 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
\r
617 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
\r
618 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
\r
619 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
\r
620 GetCvars_handleFloat(s, f, cvar_cl_hitsound, "cl_hitsound");
\r
621 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
\r
622 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
\r
623 GetCvars_handleFloat(s, f, cvar_chase_active, "chase_active");
\r
624 GetCvars_handleFloat(s, f, cvar_cl_vore_stomachmodel, "cl_vore_stomachmodel");
\r
625 GetCvars_handleFloat(s, f, cvar_cl_vore_gulletmodel, "cl_vore_gulletmodel");
\r
626 GetCvars_handleFloat(s, f, cvar_cl_vore_autodigest, "cl_vore_autodigest");
\r
627 GetCvars_handleFloat(s, f, cvar_cl_dodging_timeout, "cl_dodging_timeout");
\r
629 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
\r
630 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
\r
632 #ifdef ALLOW_FORCEMODELS
\r
633 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
\r
634 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromvoretournament, "cl_forceplayermodelsfromvoretournament");
\r
636 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
\r
638 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
\r
641 if (s == "cl_weaponpriority")
\r
642 self.switchweapon = w_getbestweapon(self);
\r
646 float fexists(string f)
\r
649 fh = fopen(f, FILE_READ);
\r
656 void backtrace(string msg)
\r
659 dev = cvar("developer");
\r
660 war = cvar("prvm_backtraceforwarnings");
\r
661 cvar_set("developer", "1");
\r
662 cvar_set("prvm_backtraceforwarnings", "1");
\r
664 print("--- CUT HERE ---\nWARNING: ");
\r
667 remove(world); // isn't there any better way to cause a backtrace?
\r
668 print("\n--- CUT UNTIL HERE ---\n");
\r
669 cvar_set("developer", ftos(dev));
\r
670 cvar_set("prvm_backtraceforwarnings", ftos(war));
\r
673 string Team_ColorCode(float teamid)
\r
675 if (teamid == COLOR_TEAM1)
\r
677 else if (teamid == COLOR_TEAM2)
\r
679 else if (teamid == COLOR_TEAM3)
\r
681 else if (teamid == COLOR_TEAM4)
\r
687 string Team_ColorName(float t)
\r
689 // fixme: Search for team entities and get their .netname's!
\r
690 if (t == COLOR_TEAM1)
\r
692 if (t == COLOR_TEAM2)
\r
694 if (t == COLOR_TEAM3)
\r
696 if (t == COLOR_TEAM4)
\r
701 string Team_ColorNameLowerCase(float t)
\r
703 // fixme: Search for team entities and get their .netname's!
\r
704 if (t == COLOR_TEAM1)
\r
706 if (t == COLOR_TEAM2)
\r
708 if (t == COLOR_TEAM3)
\r
710 if (t == COLOR_TEAM4)
\r
715 float ColourToNumber(string team_colour)
\r
717 if (team_colour == "red")
\r
718 return COLOR_TEAM1;
\r
720 if (team_colour == "blue")
\r
721 return COLOR_TEAM2;
\r
723 if (team_colour == "yellow")
\r
724 return COLOR_TEAM3;
\r
726 if (team_colour == "pink")
\r
727 return COLOR_TEAM4;
\r
729 if (team_colour == "auto")
\r
735 float NumberToTeamNumber(float number)
\r
738 return COLOR_TEAM1;
\r
741 return COLOR_TEAM2;
\r
744 return COLOR_TEAM3;
\r
747 return COLOR_TEAM4;
\r
752 #define CENTERPRIO_POINT 1
\r
753 #define CENTERPRIO_SPAM 2
\r
754 #define CENTERPRIO_VOTE 4
\r
755 #define CENTERPRIO_NORMAL 5
\r
756 #define CENTERPRIO_SHIELDING 7
\r
757 #define CENTERPRIO_MAPVOTE 9
\r
758 #define CENTERPRIO_IDLEKICK 50
\r
759 #define CENTERPRIO_ADMIN 99
\r
760 .float centerprint_priority;
\r
761 .float centerprint_expires;
\r
762 void centerprint_atprio(entity e, float prio, string s)
\r
764 if (intermission_running)
\r
765 if (prio < CENTERPRIO_MAPVOTE)
\r
767 if (time > e.centerprint_expires)
\r
768 e.centerprint_priority = 0;
\r
769 if (prio >= e.centerprint_priority)
\r
771 e.centerprint_priority = prio;
\r
772 if (timeoutStatus == 2)
\r
773 e.centerprint_expires = time + (e.cvar_scr_centertime * TIMEOUT_SLOWMO_VALUE);
\r
775 e.centerprint_expires = time + e.cvar_scr_centertime;
\r
776 centerprint_builtin(e, s);
\r
779 void centerprint_expire(entity e, float prio)
\r
781 if (prio == e.centerprint_priority)
\r
783 e.centerprint_priority = 0;
\r
784 centerprint_builtin(e, "");
\r
787 void centerprint(entity e, string s)
\r
789 centerprint_atprio(e, CENTERPRIO_NORMAL, s);
\r
792 // decolorizes and team colors the player name when needed
\r
793 string playername(entity p)
\r
796 if (teams_matter && !intermission_running && p.classname == "player")
\r
798 t = Team_ColorCode(p.team);
\r
799 return strcat(t, strdecolorize(p.netname));
\r
805 vector randompos(vector m1, vector m2)
\r
809 v_x = m2_x * random() + m1_x;
\r
810 v_y = m2_y * random() + m1_y;
\r
811 v_z = m2_z * random() + m1_z;
\r
815 float g_pickup_fuel;
\r
816 float g_pickup_fuel_jetpack;
\r
817 float g_pickup_fuel_max;
\r
818 float g_pickup_armorsmall;
\r
819 float g_pickup_armorsmall_max;
\r
820 float g_pickup_armormedium;
\r
821 float g_pickup_armormedium_max;
\r
822 float g_pickup_armorbig;
\r
823 float g_pickup_armorbig_max;
\r
824 float g_pickup_armorlarge;
\r
825 float g_pickup_armorlarge_max;
\r
826 float g_pickup_healthsmall;
\r
827 float g_pickup_healthsmall_max;
\r
828 float g_pickup_healthsmall_consumable;
\r
829 float g_pickup_healthmedium;
\r
830 float g_pickup_healthmedium_max;
\r
831 float g_pickup_healthmedium_consumable;
\r
832 float g_pickup_healthlarge;
\r
833 float g_pickup_healthlarge_max;
\r
834 float g_pickup_healthlarge_consumable;
\r
835 float g_pickup_healthmega;
\r
836 float g_pickup_healthmega_max;
\r
837 float g_pickup_healthmega_consumable;
\r
838 float g_weaponspeedfactor;
\r
839 float g_weaponratefactor;
\r
840 float g_weapondamagefactor;
\r
841 float g_weaponforcefactor;
\r
842 float g_weaponspreadfactor;
\r
844 float start_weapons;
\r
846 float start_ammo_fuel;
\r
847 float start_health;
\r
848 float start_armorvalue;
\r
849 float warmup_start_weapons;
\r
850 float warmup_start_ammo_fuel;
\r
851 float warmup_start_health;
\r
852 float warmup_start_armorvalue;
\r
853 float g_weapon_stay;
\r
854 float g_ghost_items;
\r
856 entity get_weaponinfo(float w);
\r
858 float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
\r
860 var float i = weaponinfo.weapon;
\r
865 var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
\r
867 if (t < 0) // "default" weapon selection
\r
869 if(g_rpg) // no start weapons in RPG by default
\r
872 t = (i == WEP_GRABBER);
\r
878 void readplayerstartcvars()
\r
883 // initialize starting values for players
\r
886 start_health = cvar("g_balance_health_start");
\r
887 start_armorvalue = cvar("g_balance_armor_start");
\r
891 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
\r
892 start_health = cvar("g_lms_start_health");
\r
893 start_armorvalue = cvar("g_lms_start_armor");
\r
895 else if (cvar("g_use_ammunition"))
\r
897 start_ammo_fuel = cvar("g_start_ammo_fuel");
\r
901 start_ammo_fuel = cvar("g_pickup_fuel_max");
\r
902 start_items |= IT_UNLIMITED_AMMO;
\r
905 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
\r
907 e = get_weaponinfo(i);
\r
908 if(want_weapon("g_start_weapon_", e, FALSE))
\r
910 start_weapons |= e.weapons;
\r
911 weapon_action(e.weapon, WR_PRECACHE);
\r
917 warmup_start_ammo_fuel = start_ammo_fuel;
\r
918 warmup_start_health = start_health;
\r
919 warmup_start_armorvalue = start_armorvalue;
\r
920 warmup_start_weapons = start_weapons;
\r
922 if (cvar("g_use_ammunition"))
\r
924 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
\r
926 warmup_start_health = cvar("g_warmup_start_health");
\r
927 warmup_start_armorvalue = cvar("g_warmup_start_armor");
\r
928 warmup_start_weapons = 0;
\r
929 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
\r
931 e = get_weaponinfo(i);
\r
932 if(want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns")))
\r
934 warmup_start_weapons |= e.weapons;
\r
935 weapon_action(e.weapon, WR_PRECACHE);
\r
942 start_items |= IT_FUEL_REGEN;
\r
943 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
\r
944 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
\r
948 start_items |= IT_JETPACK;
\r
950 if (g_weapon_stay == 2)
\r
952 if (!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
\r
953 if (!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
\r
956 start_ammo_fuel = max(0, start_ammo_fuel);
\r
958 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
\r
962 float g_bugrigs_planar_movement;
\r
963 float g_bugrigs_planar_movement_car_jumping;
\r
964 float g_bugrigs_reverse_spinning;
\r
965 float g_bugrigs_reverse_speeding;
\r
966 float g_bugrigs_reverse_stopping;
\r
967 float g_bugrigs_air_steering;
\r
968 float g_bugrigs_angle_smoothing;
\r
969 float g_bugrigs_friction_floor;
\r
970 float g_bugrigs_friction_brake;
\r
971 float g_bugrigs_friction_air;
\r
972 float g_bugrigs_accel;
\r
973 float g_bugrigs_speed_ref;
\r
974 float g_bugrigs_speed_pow;
\r
975 float g_bugrigs_steer;
\r
977 float g_touchexplode;
\r
978 float g_touchexplode_radius;
\r
979 float g_touchexplode_damage;
\r
980 float g_touchexplode_edgedamage;
\r
981 float g_touchexplode_force;
\r
983 float sv_autotaunt;
\r
986 float sv_pitch_min;
\r
987 float sv_pitch_max;
\r
988 float sv_pitch_fixyaw;
\r
990 float sv_accuracy_data_share;
\r
992 void readlevelcvars(void)
\r
994 g_bugrigs = cvar("g_bugrigs");
\r
995 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
\r
996 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
\r
997 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
\r
998 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
\r
999 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
\r
1000 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
\r
1001 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
\r
1002 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
\r
1003 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
\r
1004 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
\r
1005 g_bugrigs_accel = cvar("g_bugrigs_accel");
\r
1006 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
\r
1007 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
\r
1008 g_bugrigs_steer = cvar("g_bugrigs_steer");
\r
1010 g_touchexplode = cvar("g_touchexplode");
\r
1011 g_touchexplode_radius = cvar("g_touchexplode_radius");
\r
1012 g_touchexplode_damage = cvar("g_touchexplode_damage");
\r
1013 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
\r
1014 g_touchexplode_force = cvar("g_touchexplode_force");
\r
1016 #ifdef ALLOW_FORCEMODELS
\r
1017 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
\r
1020 sv_clones = cvar("sv_clones");
\r
1021 sv_gentle = cvar("sv_gentle");
\r
1022 sv_foginterval = cvar("sv_foginterval");
\r
1023 g_cloaked = cvar("g_cloaked");
\r
1024 g_jump_grunt = cvar("g_jump_grunt");
\r
1025 g_footsteps = cvar("g_footsteps");
\r
1026 g_jetpack = cvar("g_jetpack");
\r
1027 g_midair = cvar("g_midair");
\r
1028 g_norecoil = cvar("g_norecoil");
\r
1029 g_vampire = cvar("g_vampire");
\r
1030 g_bloodloss = cvar("g_bloodloss");
\r
1031 sv_maxidle = cvar("sv_maxidle");
\r
1032 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
\r
1033 sv_pogostick = cvar("sv_pogostick");
\r
1034 sv_doublejump = cvar("sv_doublejump");
\r
1035 g_ctf_reverse = cvar("g_ctf_reverse");
\r
1036 sv_autotaunt = cvar("sv_autotaunt");
\r
1037 sv_taunt = cvar("sv_taunt");
\r
1039 inWarmupStage = cvar("g_warmup");
\r
1040 g_warmup_limit = cvar("g_warmup_limit");
\r
1041 g_warmup_allguns = cvar("g_warmup_allguns");
\r
1042 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
\r
1044 if ((g_race && g_race_qualifying == 2) || g_arena || g_assault || g_rpg || cvar("g_campaign"))
\r
1045 inWarmupStage = 0; // these modes cannot work together, sorry
\r
1047 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
\r
1048 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
\r
1049 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
\r
1050 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
\r
1051 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
\r
1052 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
\r
1053 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
\r
1054 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
\r
1055 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
\r
1056 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
\r
1057 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
\r
1058 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
\r
1060 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
\r
1061 g_weaponratefactor = cvar("g_weaponratefactor");
\r
1062 g_weapondamagefactor = cvar("g_weapondamagefactor");
\r
1063 g_weaponforcefactor = cvar("g_weaponforcefactor");
\r
1064 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
\r
1066 g_pickup_fuel = cvar("g_pickup_fuel");
\r
1067 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
\r
1068 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
\r
1069 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
\r
1070 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
\r
1071 g_pickup_armormedium = cvar("g_pickup_armormedium");
\r
1072 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
\r
1073 g_pickup_armorbig = cvar("g_pickup_armorbig");
\r
1074 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
\r
1075 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
\r
1076 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
\r
1077 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
\r
1078 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
\r
1079 g_pickup_healthsmall_consumable = cvar("g_pickup_healthsmall_consumable");
\r
1080 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
\r
1081 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
\r
1082 g_pickup_healthmedium_consumable = cvar("g_pickup_healthmedium_consumable");
\r
1083 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
\r
1084 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
\r
1085 g_pickup_healthlarge_consumable = cvar("g_pickup_healthlarge_consumable");
\r
1086 g_pickup_healthmega = cvar("g_pickup_healthmega");
\r
1087 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
\r
1088 g_pickup_healthmega_consumable = cvar("g_pickup_healthmega_consumable");
\r
1090 g_weapon_stay = cvar("g_weapon_stay");
\r
1092 if (!g_weapon_stay && (cvar("deathmatch") == 2))
\r
1093 g_weapon_stay = 1;
\r
1095 g_ghost_items = cvar("g_ghost_items");
\r
1097 if(g_ghost_items >= 1)
\r
1098 g_ghost_items = 0.25; // default alpha value
\r
1100 if not(inWarmupStage && !g_ca && !g_rpg)
\r
1101 game_starttime = cvar("g_start_delay");
\r
1103 sv_pitch_min = cvar("sv_pitch_min");
\r
1104 sv_pitch_max = cvar("sv_pitch_max");
\r
1105 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
\r
1107 sv_accuracy_data_share = boolean(cvar("sv_accuracy_data_share"));
\r
1109 readplayerstartcvars();
\r
1113 // TODO sound pack system
\r
1116 string precache_sound_builtin (string s) = #19;
\r
1117 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
\r
1118 string precache_sound(string s)
\r
1120 return precache_sound_builtin(strcat(soundpack, s));
\r
1122 void play2(entity e, string filename)
\r
1124 stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
\r
1126 void sound(entity e, float chan, string samp, float vol, float atten)
\r
1128 sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
\r
1132 // Sound functions
\r
1133 string precache_sound (string s) = #19;
\r
1134 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
\r
1135 float precache_sound_index (string s) = #19;
\r
1137 #define SND_VOLUME 1
\r
1138 #define SND_ATTENUATION 2
\r
1139 #define SND_LARGEENTITY 8
\r
1140 #define SND_LARGESOUND 16
\r
1141 #define SND_SPEEDUSHORT4000 32
\r
1143 float sound_allowed(float dest, entity e)
\r
1145 // sounds from world may always pass
\r
1148 if (e.classname == "body")
\r
1150 if (e.owner && e.owner != e)
\r
1155 // sounds to self may always pass
\r
1156 if (dest == MSG_ONE)
\r
1157 if (e == msg_entity)
\r
1159 // sounds by players can be removed
\r
1160 if (cvar("bot_sound_monopoly"))
\r
1161 if (clienttype(e) == CLIENTTYPE_REAL)
\r
1163 // anything else may pass
\r
1167 void sound(entity e, float chan, string samp, float vol, float atten)
\r
1169 if (!sound_allowed(MSG_BROADCAST, e))
\r
1171 sound_builtin(e, chan, samp, vol, atten);
\r
1173 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten, float spd)
\r
1177 if (!sound_allowed(dest, e))
\r
1180 entno = num_for_edict(e);
\r
1181 idx = precache_sound_index(samp);
\r
1186 atten = floor(atten * 64);
\r
1187 vol = floor(vol * 255);
\r
1190 sflags |= SND_VOLUME;
\r
1192 sflags |= SND_ATTENUATION;
\r
1194 sflags |= SND_SPEEDUSHORT4000;
\r
1195 if (entno >= 8192)
\r
1196 sflags |= SND_LARGEENTITY;
\r
1198 sflags |= SND_LARGESOUND;
\r
1200 WriteByte(dest, SVC_SOUND);
\r
1201 WriteByte(dest, sflags);
\r
1202 if (sflags & SND_VOLUME)
\r
1203 WriteByte(dest, vol);
\r
1204 if (sflags & SND_ATTENUATION)
\r
1205 WriteByte(dest, atten);
\r
1206 if(sflags & SND_SPEEDUSHORT4000)
\r
1207 WriteShort(dest, spd * 4000);
\r
1208 if (sflags & SND_LARGEENTITY)
\r
1210 WriteShort(dest, entno);
\r
1211 WriteByte(dest, chan);
\r
1215 WriteShort(dest, entno * 8 + chan);
\r
1217 if (sflags & SND_LARGESOUND)
\r
1218 WriteShort(dest, idx);
\r
1220 WriteByte(dest, idx);
\r
1222 WriteCoord(dest, o_x);
\r
1223 WriteCoord(dest, o_y);
\r
1224 WriteCoord(dest, o_z);
\r
1226 void soundto(float dest, entity e, float chan, string samp, float vol, float atten, float spd)
\r
1230 if (!sound_allowed(dest, e))
\r
1233 o = e.origin + 0.5 * (e.mins + e.maxs);
\r
1234 soundtoat(dest, e, o, chan, samp, vol, atten, spd);
\r
1236 void soundat(entity e, vector o, float chan, string samp, float vol, float atten, float spd)
\r
1238 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten, spd);
\r
1240 void stopsoundto(float dest, entity e, float chan)
\r
1244 if (!sound_allowed(dest, e))
\r
1247 entno = num_for_edict(e);
\r
1249 if (entno >= 8192)
\r
1251 float idx, sflags;
\r
1252 idx = precache_sound_index("misc/null.wav");
\r
1253 sflags = SND_LARGEENTITY;
\r
1255 sflags |= SND_LARGESOUND;
\r
1256 WriteByte(dest, SVC_SOUND);
\r
1257 WriteByte(dest, sflags);
\r
1258 WriteShort(dest, entno);
\r
1259 WriteByte(dest, chan);
\r
1260 if (sflags & SND_LARGESOUND)
\r
1261 WriteShort(dest, idx);
\r
1263 WriteByte(dest, idx);
\r
1264 WriteCoord(dest, e.origin_x);
\r
1265 WriteCoord(dest, e.origin_y);
\r
1266 WriteCoord(dest, e.origin_z);
\r
1270 WriteByte(dest, SVC_STOPSOUND);
\r
1271 WriteShort(dest, entno * 8 + chan);
\r
1274 void stopsound(entity e, float chan)
\r
1276 if (!sound_allowed(MSG_BROADCAST, e))
\r
1279 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
\r
1280 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
\r
1283 void play2(entity e, string filename)
\r
1285 //stuffcmd(e, strcat("play2 ", filename, "\n"));
\r
1286 if (clienttype(e) == CLIENTTYPE_REAL)
\r
1289 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE, 0);
\r
1293 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
\r
1295 float spamsound(entity e, float chan, string samp, float vol, float atten)
\r
1297 if (!sound_allowed(MSG_BROADCAST, e))
\r
1300 if (time > e.spamtime)
\r
1302 e.spamtime = time;
\r
1303 sound(e, chan, samp, vol, atten);
\r
1309 void play2team(float t, string filename)
\r
1311 local entity head;
\r
1313 if (cvar("bot_sound_monopoly"))
\r
1316 FOR_EACH_REALPLAYER(head)
\r
1318 if (head.team == t)
\r
1319 play2(head, filename);
\r
1323 void play2all(string samp)
\r
1325 if (cvar("bot_sound_monopoly"))
\r
1328 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
\r
1331 void PrecachePlayerSounds(string f);
\r
1332 void precache_playermodel(string m)
\r
1334 float globhandle, i, n;
\r
1337 precache_model(m);
\r
1339 globhandle = search_begin(strcat(m, "_*.sounds"), TRUE, FALSE);
\r
1340 if (globhandle < 0)
\r
1342 n = search_getsize(globhandle);
\r
1343 for (i = 0; i < n; ++i)
\r
1345 //print(search_getfilename(globhandle, i), "\n");
\r
1346 f = search_getfilename(globhandle, i);
\r
1347 PrecachePlayerSounds(f);
\r
1349 search_end(globhandle);
\r
1351 void precache_all_playermodels(string pattern)
\r
1353 float globhandle, i, n;
\r
1356 globhandle = search_begin(pattern, TRUE, FALSE);
\r
1357 if (globhandle < 0)
\r
1359 n = search_getsize(globhandle);
\r
1360 for (i = 0; i < n; ++i)
\r
1362 //print(search_getfilename(globhandle, i), "\n");
\r
1363 f = search_getfilename(globhandle, i);
\r
1364 precache_playermodel(f);
\r
1366 search_end(globhandle);
\r
1373 // gamemode related things
\r
1374 precache_model ("models/misc/chatbubble.spr");
\r
1376 // used by the waypoint editor
\r
1377 precache_model ("models/rune.mdl");
\r
1379 #ifdef TTURRETS_ENABLED
\r
1380 if (cvar("g_turrets"))
\r
1381 turrets_precash();
\r
1384 // Precache all player models if desired
\r
1385 if (cvar("sv_precacheplayermodels"))
\r
1387 PrecachePlayerSounds("sound/player/default.sounds");
\r
1388 precache_all_playermodels("models/player/*.zym");
\r
1389 precache_all_playermodels("models/player/*.dpm");
\r
1390 precache_all_playermodels("models/player/*.md3");
\r
1391 precache_all_playermodels("models/player/*.psk");
\r
1392 precache_all_playermodels("models/player/*.iqm");
\r
1395 if (cvar("sv_defaultcharacter"))
\r
1398 s = cvar_string("sv_defaultplayermodel_red");
\r
1401 precache_model(s);
\r
1402 PrecachePlayerSounds(strcat(s, ".sounds"));
\r
1404 s = cvar_string("sv_defaultplayermodel_blue");
\r
1407 precache_model(s);
\r
1408 PrecachePlayerSounds(strcat(s, ".sounds"));
\r
1410 s = cvar_string("sv_defaultplayermodel_yellow");
\r
1413 precache_model(s);
\r
1414 PrecachePlayerSounds(strcat(s, ".sounds"));
\r
1416 s = cvar_string("sv_defaultplayermodel_pink");
\r
1419 precache_model(s);
\r
1420 PrecachePlayerSounds(strcat(s, ".sounds"));
\r
1422 s = cvar_string("sv_defaultplayermodel");
\r
1425 precache_model(s);
\r
1426 PrecachePlayerSounds(strcat(s, ".sounds"));
\r
1432 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
\r
1433 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
\r
1434 if(cvar("g_healthsize"))
\r
1435 precache_sound("misc/macro_footstep.wav");
\r
1438 // gore and miscellaneous sounds
\r
1439 //precache_sound ("misc/h2ohit.wav");
\r
1440 precache_model ("models/grabber.md3");
\r
1441 precache_sound ("misc/armorimpact.wav");
\r
1442 precache_sound ("misc/bodyimpact1.wav");
\r
1443 precache_sound ("misc/bodyimpact2.wav");
\r
1444 precache_sound ("misc/gib.wav");
\r
1445 precache_sound ("misc/gib_splat01.wav");
\r
1446 precache_sound ("misc/gib_splat02.wav");
\r
1447 precache_sound ("misc/gib_splat03.wav");
\r
1448 precache_sound ("misc/gib_splat04.wav");
\r
1449 precache_sound ("misc/hit.wav");
\r
1450 precache_sound ("misc/typehit.wav");
\r
1451 precache_sound ("misc/unavailable.wav");
\r
1452 precache_sound ("misc/forbidden.wav");
\r
1453 precache_sound ("misc/beep.wav");
\r
1454 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
\r
1455 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
\r
1456 if(cvar("g_healthsize"))
\r
1457 precache_sound("misc/macro_hitground.wav");
\r
1458 precache_sound ("misc/null.wav");
\r
1459 precache_sound ("misc/spawn.wav");
\r
1460 precache_sound ("misc/talk.wav");
\r
1461 precache_sound ("misc/teleport.wav");
\r
1462 precache_sound ("misc/poweroff.wav");
\r
1463 precache_sound ("player/lava.wav");
\r
1464 precache_sound ("player/slime.wav");
\r
1465 precache_sound ("player/digest.wav");
\r
1466 precache_sound ("misc/health_regen.wav");
\r
1467 precache_sound ("misc/armor_regen.wav");
\r
1468 precache_sound ("misc/power_fail.wav");
\r
1471 precache_sound ("misc/jetpack_fly.wav");
\r
1473 precache_model ("models/sprites/0.spr32");
\r
1474 precache_model ("models/sprites/1.spr32");
\r
1475 precache_model ("models/sprites/2.spr32");
\r
1476 precache_model ("models/sprites/3.spr32");
\r
1477 precache_model ("models/sprites/4.spr32");
\r
1478 precache_model ("models/sprites/5.spr32");
\r
1479 precache_model ("models/sprites/6.spr32");
\r
1480 precache_model ("models/sprites/7.spr32");
\r
1481 precache_model ("models/sprites/8.spr32");
\r
1482 precache_model ("models/sprites/9.spr32");
\r
1483 precache_model ("models/sprites/10.spr32");
\r
1485 // common weapon precaches
\r
1486 precache_sound ("weapons/weapon_switch.wav");
\r
1487 precache_sound ("weapons/weaponpickup.wav");
\r
1489 // precache display digits
\r
1490 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
\r
1494 e = get_weaponinfo(i);
\r
1495 for(w = 1; fexists(strcat("models/weapons/v_", e.netname, "_digit1-", ftos(w) , ".md3")); w++)
\r
1496 precache_model (strcat("models/weapons/v_", e.netname, "_digit1-", ftos(w) , ".md3"));
\r
1497 for(w = 1; fexists(strcat("models/weapons/v_", e.netname, "_digit2-", ftos(w) , ".md3")); w++)
\r
1498 precache_model (strcat("models/weapons/v_", e.netname, "_digit2-", ftos(w) , ".md3"));
\r
1501 for(i = 0; i < 8; i += 1)
\r
1502 precache_sound (strcat("weapons/hit", ftos(i), ".wav"));
\r
1504 if (cvar("sv_precacheweapons"))
\r
1506 //precache weapon models/sounds
\r
1509 while (wep <= WEP_LAST)
\r
1511 weapon_action(wep, WR_PRECACHE);
\r
1516 precache_model("models/elaser.mdl");
\r
1517 precache_model("models/laser.mdl");
\r
1518 precache_model("models/ebomb.mdl");
\r
1521 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
\r
1523 if (!self.noise && self.music) // quake 3 uses the music field
\r
1524 self.noise = self.music;
\r
1526 // plays music for the level if there is any
\r
1529 precache_sound (self.noise);
\r
1530 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
\r
1535 // sorry, but using \ in macros breaks line numbers
\r
1536 #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
1537 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
\r
1538 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
\r
1540 // WARNING: this kills the trace globals
\r
1541 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
\r
1542 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
\r
1544 #define INITPRIO_FIRST 0
\r
1545 #define INITPRIO_GAMETYPE 0
\r
1546 #define INITPRIO_GAMETYPE_FALLBACK 1
\r
1547 #define INITPRIO_CVARS 5
\r
1548 #define INITPRIO_FINDTARGET 10
\r
1549 #define INITPRIO_DROPTOFLOOR 20
\r
1550 #define INITPRIO_SETLOCATION 90
\r
1551 #define INITPRIO_LINKDOORS 91
\r
1552 #define INITPRIO_LAST 99
\r
1554 .void(void) initialize_entity;
\r
1555 .float initialize_entity_order;
\r
1556 .entity initialize_entity_next;
\r
1557 entity initialize_entity_first;
\r
1559 void make_safe_for_remove(entity e)
\r
1561 if (e.initialize_entity)
\r
1564 for (ent = initialize_entity_first; ent; )
\r
1566 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
\r
1568 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
\r
1569 // skip it in linked list
\r
1572 prev.initialize_entity_next = ent.initialize_entity_next;
\r
1573 ent = prev.initialize_entity_next;
\r
1577 initialize_entity_first = ent.initialize_entity_next;
\r
1578 ent = initialize_entity_first;
\r
1584 ent = ent.initialize_entity_next;
\r
1590 void objerror(string s)
\r
1592 make_safe_for_remove(self);
\r
1593 objerror_builtin(s);
\r
1596 void remove_unsafely(entity e)
\r
1598 remove_builtin(e);
\r
1601 void remove_safely(entity e)
\r
1603 make_safe_for_remove(e);
\r
1604 remove_builtin(e);
\r
1607 void InitializeEntity(entity e, void(void) func, float order)
\r
1611 if (!e || e.initialize_entity)
\r
1613 // make a proxy initializer entity
\r
1617 e.classname = "initialize_entity";
\r
1621 e.initialize_entity = func;
\r
1622 e.initialize_entity_order = order;
\r
1624 cur = initialize_entity_first;
\r
1627 if (!cur || cur.initialize_entity_order > order)
\r
1629 // insert between prev and cur
\r
1631 prev.initialize_entity_next = e;
\r
1633 initialize_entity_first = e;
\r
1634 e.initialize_entity_next = cur;
\r
1638 cur = cur.initialize_entity_next;
\r
1641 void InitializeEntitiesRun()
\r
1643 entity startoflist;
\r
1644 startoflist = initialize_entity_first;
\r
1645 initialize_entity_first = world;
\r
1646 for (self = startoflist; self; )
\r
1649 var void(void) func;
\r
1650 e = self.initialize_entity_next;
\r
1651 func = self.initialize_entity;
\r
1652 self.initialize_entity_order = 0;
\r
1653 self.initialize_entity = func_null;
\r
1654 self.initialize_entity_next = world;
\r
1655 if (self.classname == "initialize_entity")
\r
1658 e_old = self.enemy;
\r
1659 remove_builtin(self);
\r
1662 //dprint("Delayed initialization: ", self.classname, "\n");
\r
1668 .float uncustomizeentityforclient_set;
\r
1669 .void(void) uncustomizeentityforclient;
\r
1670 void(void) SUB_Nullpointer = #0;
\r
1671 void UncustomizeEntitiesRun()
\r
1675 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
\r
1676 self.uncustomizeentityforclient();
\r
1679 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
\r
1681 e.customizeentityforclient = customizer;
\r
1682 e.uncustomizeentityforclient = uncustomizer;
\r
1683 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
\r
1686 .float nottargeted;
\r
1687 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
\r
1689 void() SUB_Remove;
\r
1690 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
\r
1694 if (e.classname == "")
\r
1695 e.classname = "net_linked";
\r
1697 if (e.model == "" || self.modelindex == 0)
\r
1701 setmodel(e, "null");
\r
1702 setsize(e, mi, ma);
\r
1705 e.SendEntity = sendfunc;
\r
1706 e.SendFlags = 0xFFFFFF;
\r
1709 e.effects |= EF_NODEPTHTEST;
\r
1713 e.nextthink = time + dt;
\r
1714 e.think = SUB_Remove;
\r
1718 void adaptor_think2touch()
\r
1727 void adaptor_think2use()
\r
1732 activator = world;
\r
1739 // deferred dropping
\r
1740 void DropToFloor_Handler()
\r
1742 droptofloor_builtin();
\r
1743 self.dropped_origin = self.origin;
\r
1746 void droptofloor()
\r
1748 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
\r
1753 float trace_hits_box_a0, trace_hits_box_a1;
\r
1755 float trace_hits_box_1d(float end, float thmi, float thma)
\r
1759 // just check if x is in range
\r
1767 // do the trace with respect to x
\r
1768 // 0 -> end has to stay in thmi -> thma
\r
1769 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
\r
1770 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
\r
1771 if (trace_hits_box_a0 > trace_hits_box_a1)
\r
1777 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
\r
1782 // now it is a trace from 0 to end
\r
1784 trace_hits_box_a0 = 0;
\r
1785 trace_hits_box_a1 = 1;
\r
1787 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
\r
1789 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
\r
1791 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
\r
1797 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
\r
1799 return trace_hits_box(start, end, thmi - ma, thma - mi);
\r
1802 float SUB_NoImpactCheck()
\r
1804 // zero hitcontents = this is not the real impact, but either the
\r
1805 // mirror-impact of something hitting the projectile instead of the
\r
1806 // projectile hitting the something, or a touchareagrid one. Neither of
\r
1807 // these stop the projectile from moving, so...
\r
1808 if(trace_dphitcontents == 0)
\r
1810 dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
\r
1813 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
\r
1815 if (other == world && self.size != '0 0 0')
\r
1818 tic = self.velocity * sys_frametime;
\r
1819 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
\r
1820 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
\r
1821 if (trace_fraction >= 1)
\r
1823 dprint("Odd... did not hit...?\n");
\r
1825 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
\r
1827 dprint("Detected and prevented the sky-grapple bug.\n");
\r
1835 #define SUB_OwnerCheck() (other && (other == self.owner))
\r
1837 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
\r
1839 if(SUB_OwnerCheck())
\r
1841 if(SUB_NoImpactCheck())
\r
1846 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
\r
1847 UpdateCSQCProjectileNextFrame(self);
\r
1850 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
\r
1852 float MAX_IPBAN_URIS = 16;
\r
1854 float URI_GET_DISCARD = 0;
\r
1855 float URI_GET_IPBAN = 1;
\r
1856 float URI_GET_IPBAN_END = 16;
\r
1858 void URI_Get_Callback(float id, float status, string data)
\r
1860 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
\r
1862 dprint("\nEnd of data.\n");
\r
1864 if (id == URI_GET_DISCARD)
\r
1868 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
\r
1870 // online ban list
\r
1871 OnlineBanList_URI_Get_Callback(id, status, data);
\r
1875 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
\r
1879 void print_to(entity e, string s)
\r
1882 sprint(e, strcat(s, "\n"));
\r
1887 string getrecords(float page) // 50 records per page
\r
1901 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
\r
1903 if (MapInfo_Get_ByID(i))
\r
1905 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
\r
1908 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
\r
1909 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
\r
1917 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
\r
1919 if (MapInfo_Get_ByID(i))
\r
1921 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "time")));
\r
1924 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "netname"));
\r
1925 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
\r
1933 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
\r
1935 if (MapInfo_Get_ByID(i))
\r
1937 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "time")));
\r
1940 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "netname"));
\r
1941 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
\r
1947 MapInfo_ClearTemps();
\r
1949 if (s == "" && page == 0)
\r
1950 return "No records are available on this server.\n";
\r
1955 string getrankings()
\r
1966 map = GetMapname();
\r
1968 for (i = 1; i <= RANKINGS_CNT; ++i)
\r
1970 t = race_GetTime(i);
\r
1973 n = race_GetName(i);
\r
1974 p = race_PlaceName(i);
\r
1975 s = strcat(s, strpad(8, p), " ", strpad(-8, TIME_ENCODED_TOSTRING(t)), " ", n, "\n");
\r
1978 MapInfo_ClearTemps();
\r
1981 return strcat("No records are available for the map: ", map, "\n");
\r
1983 return strcat("Records for ", map, ":\n", s);
\r
1986 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
\r
1989 vector start, org, delta, end, enddown, mstart;
\r
1991 m = e.dphitcontentsmask;
\r
1992 e.dphitcontentsmask = goodcontents | badcontents;
\r
1995 delta = world.maxs - world.mins;
\r
1997 for (i = 0; i < attempts; ++i)
\r
1999 start_x = org_x + random() * delta_x;
\r
2000 start_y = org_y + random() * delta_y;
\r
2001 start_z = org_z + random() * delta_z;
\r
2003 // rule 1: start inside world bounds, and outside
\r
2004 // solid, and don't start from somewhere where you can
\r
2005 // fall down to evil
\r
2006 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
\r
2007 if (trace_fraction >= 1)
\r
2009 if (trace_startsolid)
\r
2011 if (trace_dphitcontents & badcontents)
\r
2013 if (trace_dphitq3surfaceflags & badsurfaceflags)
\r
2016 // rule 2: if we are too high, lower the point
\r
2017 if (trace_fraction * delta_z > maxaboveground)
\r
2018 start = trace_endpos + '0 0 1' * maxaboveground;
\r
2019 enddown = trace_endpos;
\r
2021 // rule 3: make sure we aren't outside the map. This only works
\r
2022 // for somewhat well formed maps. A good rule of thumb is that
\r
2023 // the map should have a convex outside hull.
\r
2024 // these can be traceLINES as we already verified the starting box
\r
2025 mstart = start + 0.5 * (e.mins + e.maxs);
\r
2026 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
\r
2027 if (trace_fraction >= 1)
\r
2029 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
\r
2030 if (trace_fraction >= 1)
\r
2032 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
\r
2033 if (trace_fraction >= 1)
\r
2035 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
\r
2036 if (trace_fraction >= 1)
\r
2038 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
\r
2039 if (trace_fraction >= 1)
\r
2042 // find a random vector to "look at"
\r
2043 end_x = org_x + random() * delta_x;
\r
2044 end_y = org_y + random() * delta_y;
\r
2045 end_z = org_z + random() * delta_z;
\r
2046 end = start + normalize(end - start) * vlen(delta);
\r
2048 // rule 4: start TO end must not be too short
\r
2049 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
\r
2050 if (trace_startsolid)
\r
2052 if (trace_fraction < minviewdistance / vlen(delta))
\r
2055 // rule 5: don't want to look at sky
\r
2056 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
\r
2059 // rule 6: we must not end up in trigger_hurt
\r
2060 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
\r
2062 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
\r
2069 e.dphitcontentsmask = m;
\r
2073 setorigin(e, start);
\r
2074 e.angles = vectoangles(end - start);
\r
2075 dprint("Needed ", ftos(i + 1), " attempts\n");
\r
2082 float zcurveparticles_effectno;
\r
2083 vector zcurveparticles_start;
\r
2084 float zcurveparticles_spd;
\r
2086 void endzcurveparticles()
\r
2088 if(zcurveparticles_effectno)
\r
2091 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
\r
2093 zcurveparticles_effectno = 0;
\r
2096 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
\r
2098 spd = bound(0, floor(spd / 16), 32767);
\r
2099 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
\r
2101 endzcurveparticles();
\r
2102 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
\r
2103 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
\r
2104 WriteShort(MSG_BROADCAST, effectno);
\r
2105 WriteCoord(MSG_BROADCAST, start_x);
\r
2106 WriteCoord(MSG_BROADCAST, start_y);
\r
2107 WriteCoord(MSG_BROADCAST, start_z);
\r
2108 zcurveparticles_effectno = effectno;
\r
2109 zcurveparticles_start = start;
\r
2112 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
\r
2113 WriteCoord(MSG_BROADCAST, end_x);
\r
2114 WriteCoord(MSG_BROADCAST, end_y);
\r
2115 WriteCoord(MSG_BROADCAST, end_z);
\r
2116 WriteCoord(MSG_BROADCAST, end_dz);
\r
2117 zcurveparticles_spd = spd;
\r
2120 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
\r
2123 vector vecxy, velxy;
\r
2125 vecxy = end - start;
\r
2130 if (vlen(velxy) < 0.000001 * fabs(vel_z))
\r
2132 endzcurveparticles();
\r
2133 trailparticles(world, effectno, start, end);
\r
2137 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
\r
2138 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
\r
2141 string GetGametype(); // g_world.qc
\r
2142 void write_recordmarker(entity pl, float tstart, float dt)
\r
2144 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
\r
2146 // also write a marker into demo files for demotc-race-record-extractor to find
\r
2149 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
\r
2150 " ", ftos(tstart), " ", ftos(dt), "\n"));
\r
2153 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter)
\r
2155 switch(self.owner.cvar_cl_gunalign)
\r
2166 if(allowcenter) // 2: allow center handedness
\r
2179 if(allowcenter) // 2: allow center handedness
\r
2195 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
\r
2200 if (cvar("g_shootfromeye"))
\r
2204 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
\r
2212 else if (cvar("g_shootfromcenter"))
\r
2216 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
\r
2224 else if ((s = cvar_string("g_shootfromfixedorigin")) != "")
\r
2234 else if (cvar("g_shootfromclient"))
\r
2236 vecs = shotorg_adjustfromclient(vecs, y_is_right, (cvar("g_shootfromclient") >= 2));
\r
2243 void attach_sameorigin(entity e, entity to, string tag)
\r
2245 vector org, t_forward, t_left, t_up, e_forward, e_up;
\r
2246 vector org0, ang0;
\r
2252 org = e.origin - gettaginfo(to, gettagindex(to, tag));
\r
2253 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
\r
2254 t_forward = v_forward * tagscale;
\r
2255 t_left = v_right * -tagscale;
\r
2256 t_up = v_up * tagscale;
\r
2258 e.origin_x = org * t_forward;
\r
2259 e.origin_y = org * t_left;
\r
2260 e.origin_z = org * t_up;
\r
2262 // current forward and up directions
\r
2263 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
\r
2264 e.angles = AnglesTransform_FromVAngles(e.angles);
\r
2266 e.angles = AnglesTransform_FromAngles(e.angles);
\r
2267 fixedmakevectors(e.angles);
\r
2269 // untransform forward, up!
\r
2270 e_forward_x = v_forward * t_forward;
\r
2271 e_forward_y = v_forward * t_left;
\r
2272 e_forward_z = v_forward * t_up;
\r
2273 e_up_x = v_up * t_forward;
\r
2274 e_up_y = v_up * t_left;
\r
2275 e_up_z = v_up * t_up;
\r
2277 e.angles = fixedvectoangles2(e_forward, e_up);
\r
2278 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
\r
2279 e.angles = AnglesTransform_ToVAngles(e.angles);
\r
2281 e.angles = AnglesTransform_ToAngles(e.angles);
\r
2283 setattachment(e, to, tag);
\r
2284 setorigin(e, e.origin);
\r
2287 void detach_sameorigin(entity e)
\r
2290 org = gettaginfo(e, 0);
\r
2291 e.angles = fixedvectoangles2(v_forward, v_up);
\r
2292 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
\r
2293 e.angles = AnglesTransform_ToVAngles(e.angles);
\r
2295 e.angles = AnglesTransform_ToAngles(e.angles);
\r
2296 setorigin(e, org);
\r
2297 setattachment(e, world, "");
\r
2298 setorigin(e, e.origin);
\r
2301 void follow_sameorigin(entity e, entity to)
\r
2303 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
\r
2304 e.aiment = to; // make the hole follow bmodel
\r
2305 e.punchangle = to.angles; // the original angles of bmodel
\r
2306 e.view_ofs = e.origin - to.origin; // relative origin
\r
2307 e.v_angle = e.angles - to.angles; // relative angles
\r
2310 void unfollow_sameorigin(entity e)
\r
2312 e.movetype = MOVETYPE_NONE;
\r
2315 entity gettaginfo_relative_ent;
\r
2316 vector gettaginfo_relative(entity e, float tag)
\r
2318 if (!gettaginfo_relative_ent)
\r
2320 gettaginfo_relative_ent = spawn();
\r
2321 gettaginfo_relative_ent.effects = EF_NODRAW;
\r
2323 gettaginfo_relative_ent.model = e.model;
\r
2324 gettaginfo_relative_ent.modelindex = e.modelindex;
\r
2325 gettaginfo_relative_ent.frame = e.frame;
\r
2326 return gettaginfo(gettaginfo_relative_ent, tag);
\r
2329 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
\r
2333 if (pl.soundentity.cnt & p)
\r
2335 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn, 0);
\r
2336 pl.soundentity.cnt |= p;
\r
2339 void SoundEntity_StopSound(entity pl, float chan)
\r
2343 if (pl.soundentity.cnt & p)
\r
2345 stopsoundto(MSG_ALL, pl.soundentity, chan);
\r
2346 pl.soundentity.cnt &~= p;
\r
2350 void SoundEntity_Attach(entity pl)
\r
2352 pl.soundentity = spawn();
\r
2353 pl.soundentity.classname = "soundentity";
\r
2354 pl.soundentity.owner = pl;
\r
2355 setattachment(pl.soundentity, pl, "");
\r
2356 setmodel(pl.soundentity, "null");
\r
2359 void SoundEntity_Detach(entity pl)
\r
2362 for (i = 0; i <= 7; ++i)
\r
2363 SoundEntity_StopSound(pl, i);
\r
2367 float ParseCommandPlayerSlotTarget_firsttoken;
\r
2368 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
\r
2376 ParseCommandPlayerSlotTarget_firsttoken = -1;
\r
2380 if (substring(argv(idx), 0, 1) == "#")
\r
2382 s = substring(argv(idx), 1, -1);
\r
2390 ParseCommandPlayerSlotTarget_firsttoken = idx;
\r
2391 if (s == ftos(stof(s)))
\r
2393 e = edict_num(stof(s));
\r
2394 if (e.flags & FL_CLIENT)
\r
2400 // it must be a nick name
\r
2403 ParseCommandPlayerSlotTarget_firsttoken = idx;
\r
2406 FOR_EACH_CLIENT(head)
\r
2407 if (head.netname == s)
\r
2415 s = strdecolorize(s);
\r
2417 FOR_EACH_CLIENT(head)
\r
2418 if (strdecolorize(head.netname) == s)
\r
2433 float modeleffect_SendEntity(entity to, float sf)
\r
2436 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
\r
2439 if(self.velocity != '0 0 0')
\r
2441 if(self.angles != '0 0 0')
\r
2443 if(self.avelocity != '0 0 0')
\r
2446 WriteByte(MSG_ENTITY, f);
\r
2447 WriteShort(MSG_ENTITY, self.modelindex);
\r
2448 WriteByte(MSG_ENTITY, self.skin);
\r
2449 WriteByte(MSG_ENTITY, self.frame);
\r
2450 WriteCoord(MSG_ENTITY, self.origin_x);
\r
2451 WriteCoord(MSG_ENTITY, self.origin_y);
\r
2452 WriteCoord(MSG_ENTITY, self.origin_z);
\r
2455 WriteCoord(MSG_ENTITY, self.velocity_x);
\r
2456 WriteCoord(MSG_ENTITY, self.velocity_y);
\r
2457 WriteCoord(MSG_ENTITY, self.velocity_z);
\r
2461 WriteCoord(MSG_ENTITY, self.angles_x);
\r
2462 WriteCoord(MSG_ENTITY, self.angles_y);
\r
2463 WriteCoord(MSG_ENTITY, self.angles_z);
\r
2467 WriteCoord(MSG_ENTITY, self.avelocity_x);
\r
2468 WriteCoord(MSG_ENTITY, self.avelocity_y);
\r
2469 WriteCoord(MSG_ENTITY, self.avelocity_z);
\r
2471 WriteShort(MSG_ENTITY, self.scale * 256.0);
\r
2472 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
\r
2473 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
\r
2474 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
\r
2475 WriteByte(MSG_ENTITY, self.alpha * 255.0);
\r
2480 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
2485 e.classname = "modeleffect";
\r
2491 e.avelocity = angv;
\r
2493 e.teleport_time = t1;
\r
2497 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
\r
2501 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
\r
2504 sz = max(e.scale, e.scale2);
\r
2505 setsize(e, e.mins * sz, e.maxs * sz);
\r
2506 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
\r
2509 float portrait_SendEntity(entity to, float sf)
\r
2511 if(to != self.enemy)
\r
2514 WriteByte(MSG_ENTITY, ENT_CLIENT_PORTRAIT);
\r
2516 WriteString(MSG_ENTITY, self.owner.playermodel);
\r
2517 WriteByte(MSG_ENTITY, stof(self.owner.playerskin));
\r
2518 WriteString(MSG_ENTITY, self.owner.netname);
\r
2523 void portrait(entity pl, entity targ)
\r
2527 e.classname = "portrait";
\r
2531 Net_LinkEntity(e, FALSE, 0, portrait_SendEntity);
\r
2534 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
\r
2536 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
\r
2539 float randombit(float bits)
\r
2541 if not(bits & (bits-1)) // this ONLY holds for powers of two!
\r
2550 for(f = 1; f <= bits; f *= 2)
\r
2559 r = (r - 1) / (n - 1);
\r
2566 float randombits(float bits, float k, float error_return)
\r
2570 while(k > 0 && bits != r)
\r
2572 r += randombit(bits - r);
\r
2581 void randombit_test(float bits, float iter)
\r
2585 print(ftos(randombit(bits)), "\n");
\r
2590 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
\r
2592 if(halflifedist > 0)
\r
2593 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
\r
2594 else if(halflifedist < 0)
\r
2595 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
\r
2604 #define cvar_string_normal cvar_string_builtin
\r
2605 #define cvar_normal cvar_builtin
\r
2607 string cvar_string_normal(string n)
\r
2609 if not(cvar_type(n) & 1)
\r
2610 backtrace(strcat("Attempt to access undefined cvar: ", n));
\r
2611 return cvar_string_builtin(n);
\r
2614 float cvar_normal(string n)
\r
2616 return stof(cvar_string_normal(n));
\r
2619 #define cvar_set_normal cvar_set_builtin
\r
2621 void defer_think()
\r
2626 self = self.owner;
\r
2627 oself.think = SUB_Remove;
\r
2628 oself.nextthink = time;
\r
2634 Execute func() after time + fdelay.
\r
2635 self when func is executed = self when defer is called
\r
2637 void defer(float fdelay, void() func)
\r
2644 e.think = defer_think;
\r
2645 e.nextthink = time + fdelay;
\r
2648 // returns 1 if player is at minimum size and 0 if player is at normal size
\r
2649 float playersize_micro(entity e)
\r
2651 if(!cvar("g_healthsize"))
\r
2653 return bound(0, (e.health / cvar("g_healthsize_center") - 1) / (cvar("g_healthsize_min") / cvar("g_healthsize_center") - 1), 1);
\r
2655 // returns 0 if player is at normal size and 1 if player is at maximum size
\r
2656 float playersize_macro(entity e)
\r
2658 if(!cvar("g_healthsize"))
\r
2660 return 1 - bound(0, (e.health / cvar("g_healthsize_max") - 1) / (cvar("g_healthsize_center") / cvar("g_healthsize_max") - 1), 1);
\r
2663 // returns 1 if the player is close to a wall
\r
2664 float check_close_to_wall(float threshold) {
\r
2665 //TODO: This check should be moved somehow for this to be a common utility
\r
2666 if (!cvar("sv_dodging_wall_dodging"))
\r
2669 vector trace_start;
\r
2672 trace_start = self.origin;
\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_right);
\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
2689 trace_end = self.origin - (1000*v_forward);
\r
2690 tracebox(trace_start, self.mins, self.maxs, trace_end, TRUE, self);
\r
2691 if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold)
\r
2697 float check_close_to_ground(float threshold) {
\r
2698 if (self.flags & FL_ONGROUND)
\r