set g_spawn_furthest 0.5 "this amount of the spawns shall be far away from any players"
set g_spawn_useallspawns 0 "use all spawns, e.g. also team spawns in non-teamplay, and all spawns, even enemy spawns, in teamplay"
+set g_spawn_near_teammate 0 "if set, players prefer spawns near a team mate"
+set g_spawn_near_teammate_distance 640 "max distance to consider a spawn to be near a team mate"
// respawn delay
set g_respawn_delay 2 "number of seconds you have to wait before you can respawn again"
set g_respawn_waves 0 "respawn in waves (every n seconds), intended to decrease overwhelming base attacks"
float time;
float frametime;
-float player_localentnum; //the entnum
+float player_localentnum; //the entnum of the VIEW entity
float player_localnum; //the playernum
float maxclients; //a constant filled in by the engine. gah, portability eh?
float i;
+#ifdef COMPAT_XON050_ENGINE
+ // old engine lacks implementation of player_localnum
+ player_localnum = player_localentnum - 1;
+#endif
+
binddb = db_create();
tempdb = db_create();
ClientProgsDB = db_load("client.db");
if(f & 2)
{
newspectatee_status = ReadByte();
- if(newspectatee_status == player_localentnum)
+ if(newspectatee_status == player_localnum + 1)
newspectatee_status = -1; // observing
}
else
prev_health = -1;
}
spectatee_status = newspectatee_status;
+
+ // non-COMPAT_XON050_ENGINE: we could get rid of spectatee_status, and derive it from player_localentnum and player_localnum
}
void Ent_Nagger()
strunzone(grecordholder[pos-1]);
grecordholder[pos-1] = strzone(ReadString());
grecordtime[pos-1] = ReadInt24_t();
- if(grecordholder[pos-1] == GetPlayerName(player_localentnum -1))
+ if(grecordholder[pos-1] == GetPlayerName(player_localnum))
race_myrank = pos;
break;
case RACE_NET_SERVER_STATUS:
vector v;
vector vf_size, vf_min;
float a;
+
hud = getstati(STAT_HUD);
if(checkextension("DP_CSQC_MINFPS_QUALITY"))
WaypointSprite_Load();
+#ifdef COMPAT_XON050_ENGINE
if(spectatee_status)
myteam = GetPlayerColor(spectatee_status - 1);
else
+#endif
myteam = GetPlayerColor(player_localentnum - 1);
ticrate = getstatf(STAT_MOVEVARS_TICRATE) * getstatf(STAT_MOVEVARS_TIMESCALE);
+++ /dev/null
-#ifdef CTF_EXAMPLE
-
-// NOTE: This file contains lots of debugging stuff
-// it is not really used... can be seen as another sample...
-
-string STR_PLAYER = "player";
-#define FOR_EACH_PLAYER(v) for(v = world; (v = find(v, classname, STR_PLAYER)) != world; )
-
-string ctf_temp_1;
-float order_page;
-
-void menu_close()
-{
- menu_visible = false;
- menu_show = menu_show_error;
- menu_action = menu_sub_null;
-
- /*if(ctf_temp_1)
- strunzone(ctf_temp_1);*/
-
- localcmd("\nin_bindmap 0 0;");
-}
-
-void order_menu_render()
-{
- vector ps, po;
- float i, p, n;
- string frags, color;
- ps = '0 200 0';
- po = '0 8 0';
-
- color = getplayerkeyvalue(player_localentnum-1, "topcolor");
-
- if(getstati(STAT_CTF_STATE) == CTF_STATE_COMMANDER) {
- drawstring(ps, _("----- Order Menu -----"), '8 8 0', '1 1 0', 1, 0); ps += po;
- drawstring(ps, sprintf(_("Order: %s"), ctf_temp_1), '8 8 0', '1 1 0', 1, 0); ps += po;
- drawcolorcodedstring(ps, _("1) ^3previous page"), '8 8 0', 1, 0); ps += po;
- drawcolorcodedstring(ps, _("2) ^3next page"), '8 8 0', 1, 0); ps += po;
- for((n = 2), (p = i = 0); i < maxclients && n > 0; ++i) {
- frags = getplayerkeyvalue(i, "frags");
- if(!frags || (i+1) == player_localentnum)
- continue;
- if(frags == "-666" || getplayerkeyvalue(i, "topcolor") != color) // FIXME use GetPlayerTeam
- continue;
- ++p;
- if(p > (8*order_page))
- {
- // only render current page
- ++n;
- if(n == 10)
- n = 0;
- drawcolorcodedstring(ps, strcat(ftos(n), ") ", GetPlayerName(i), " : ", ftos(getstatf(STAT_CTF_STATE))), '8 8 0', 1, 0); ps += po;
- }
- }
- drawstring(ps, _("ESC) Exit Menu"), '8 8 0', '1 1 0', 1, 0); ps += po;
- } else {
- menu_close();
- }
-}
-
-float order_menu_action(float bInputType, float nPrimary, float nSecondary)
-{
- string arg;
- float p, i, n, chose;
- string frags, color;
- if(bInputType != 0) // key down wanted
- return FALSE;
-
- arg = chr2str(nSecondary);
- chose = stof(chr2str(nPrimary));
- //str2chr
- if(arg == "1") {
- color = getplayerkeyvalue(player_localentnum-1, "topcolor");
- ++order_page;
- for(p = i = 0; i < maxclients; ++i) {
- frags = getplayerkeyvalue(i, "frags");
- if(!frags || (i+1) == player_localentnum)
- continue;
- if(frags == "-666" || getplayerkeyvalue(i, "topcolor") != color) // FIXME use GetPlayerTeam
- continue;
- ++p;
- }
- if(p <= (8*order_page)) // no ppl on page
- order_page = 0;
- } else if(arg == "2") {
- color = getplayerkeyvalue(player_localentnum-1, "topcolor");
- --order_page;
- if(order_page < 0) {
- for(p = i = 0; i < maxclients; ++i) {
- frags = getplayerkeyvalue(i, "frags");
- if(!frags || (i+1) == player_localentnum)
- continue;
- if(frags == "-666" || getplayerkeyvalue(i, "topcolor") != color) // FIXME use GetPlayerTeam
- continue;
- ++p;
- }
- order_page = floor(p/8);
- }
- } else if(chose >= 3 && chose <= 9 || arg == "0") { // the 10 needs extra checking, assuming that stof(astring) returns 0
- if(chose == 0)
- chose = 10;
- n = 2;
- color = getplayerkeyvalue(player_localentnum-1, "topcolor");
- for(p = i = 0; i < maxclients && n > 0; ++i) {
- frags = getplayerkeyvalue(i, "frags");
- if(!frags || (i+1) == player_localentnum)
- continue;
- if(frags == "-666" || getplayerkeyvalue(i, "topcolor") != color) // FIXME use GetPlayerTeam
- continue;
- ++p;
- if(p > (8*order_page))
- {
- // only render current page
- ++n;
- if(n == chose) {
- n = 0;
- break;
- }
- }
- }
- if(n == 0) {
- //print(sprintf(_("Issuing order to: %d\n"), i+1));
- //print(strcat("cmd order #", ftos(i+1), " ", ctf_temp_1, ";\n"));
- localcmd(strcat("\ncmd order #", ftos(i+1), " ", ctf_temp_1, ";"));
- } else {
- print(sprintf(_("Couldn't find player %d\n"), chose));
- }
- return TRUE;
- } else if(nSecondary == K_ESCAPE) {
- strunzone(ctf_temp_1);
- menu_close();
- } else {
- //print(sprintf(_("Menu action %s does not exist.\n"), arg));
- return FALSE;
- }
- return TRUE;
-}
-
-void order_menu_show()
-{
- order_page = 0;
- menu_show = order_menu_render;
- menu_action = order_menu_action;
-}
-
-
-void ctf_menu_render()
-{
- vector ps, po;
- ps = '0 200 0';
- po = '0 8 0';
-
- if(getstati(STAT_CTF_STATE) == CTF_STATE_COMMANDER) {
- drawstring(ps, _("----- Command Menu -----"), '8 8 0', '1 1 0', 1, 0); ps += po;
- drawstring(ps, _("Issue orders:"), '8 8 0', '1 1 0', 1, 0); ps += po;
- drawstring(ps, _(" 1) Attack"), '8 8 0', '1 1 0', 1, 0);
- drawstring(ps + '80 0 0', " \x0F", '8 8 0', '1 1 1', 1, 0); ps += po;
- drawstring(ps, strcat(_(" 2) Defend"), " \x0E"), '8 8 0', '1 1 0', 1, 0); ps += po;
- ps += po;
- drawstring(ps, _("3) Resign from command."), '8 8 0', '1 1 0', 1, 0); ps += po;
- drawstring(ps, _("ESC) Exit Menu"), '8 8 0', '1 1 0', 1, 0); ps += po;
- } else {
- menu_close();
- }
-}
-
-float ctf_menu_action(float bInputType, float nPrimary, float nSecondary)
-{
- string arg;
- if(bInputType != 0) // key down wanted
- return FALSE;
-
- arg = chr2str(nSecondary);
-
- if(arg == "1") {
- ctf_temp_1 = strzone("attack");
- order_menu_show();
- } else if(arg == "2") {
- ctf_temp_1 = strzone("defend");
- order_menu_show();
- } else if(arg == "3") {
- localcmd("\ncmd order resign;");
- menu_close();
- } else if(nSecondary == K_ESCAPE) {
- menu_close();
- } else {
- //print(strcat("Menu action ", arg, " does not exist.\n"));
- return FALSE;
- }
- return TRUE;
-}
-
-void ctf_menu_show()
-{
- if(getstati(STAT_CTF_STATE) < 0)
- return;
- menu_show = ctf_menu_render;
- menu_action = ctf_menu_action;
- menu_visible = TRUE;
- //menu_default_binds();
-}
-
-void ctf_view()
-{
- float stat;
- stat = getstati(STAT_CTF_STATE);
- if(stat == CTF_STATE_ATTACK) {
- drawpic('0 0 0', "gfx/ctf_ic_atk.tga", '64 64 0', '1 1 1', 1, 0);
- } else if(stat == CTF_STATE_DEFEND) {
- drawpic('0 0 0', "gfx/ctf_ic_def.tga", '64 64 0', '1 1 1', 1, 0);
- } else if(stat == CTF_STATE_COMMANDER) {
- drawstring('0 0 0', _("You're commander!"), '8 8 0', '1 1 1', 1, 0);
- } else if(stat < 0) {
- } else {
- drawstring('0 0 0', _("Awaiting orders..."), '8 8 0', '1 1 1', 0.5, 0);
- }
-}
-
-#endif
void HUD_Weapons(void)
{
// declarations
- float weapons_stat = getstati(STAT_WEAPONS);
+ float weapons_stat = getstati(STAT_WEAPONS);
float i, f, a, j, factor;
- float screen_ar, center_x, center_y;
- float weapon_count, weapon_id, weapon_alpha;
- float row, column, rows, columns;
+ float screen_ar, center_x, center_y;
+ float weapon_count, weapon_id, weapon_alpha;
+ float row, column, rows, columns;
float aspect = autocvar_hud_panel_weapons_aspect;
-
+
float show_accuracy, panel_weapon_accuracy;
-
+
float timeout = autocvar_hud_panel_weapons_timeout;
float timein_effect_length = (autocvar_hud_panel_weapons_timeout_effect ? 0.375 : 0);
float timeout_effect_length = (autocvar_hud_panel_weapons_timeout_effect ? 0.75 : 0);
-
+
float ammo_type, ammo_full, ammo_alpha;
float barsize_x, barsize_y, baroffset_x, baroffset_y;
-
+
float when = autocvar_hud_panel_weapons_complainbubble_time;
float fadetime = autocvar_hud_panel_weapons_complainbubble_fadetime;
-
+
vector weapon_pos, weapon_size;
local noref vector old_panel_size; // fteqcc sucks
vector color, ammo_color;
-
+
// check to see if we want to continue
- if(hud != HUD_NORMAL) { return; }
-
+ if(hud != HUD_NORMAL) { return; }
+
if(!autocvar__hud_configure)
+ {
if((!autocvar_hud_panel_weapons) || (spectatee_status == -1))
return;
else if(timeout && time >= weapontime + timeout + timeout_effect_length)
weaponprevtime = time;
return;
}
+ }
else
hud_configure_active_panel = HUD_PANEL_WEAPONS;
// update generic hud functions
HUD_Panel_UpdateCvars(weapons);
HUD_Panel_ApplyFadeAlpha();
-
+
// calculate fading effect to weapon images for when the panel is idle
if(autocvar_hud_panel_weapons_fade)
{
weaponorder_cmp_str = string_null;
}
-
+
// determine which weapons are going to be shown
if (autocvar_hud_panel_weapons_onlyowned)
{
if (weapons_stat == 0) // create some fake weapons anyway
for(i = 0; i <= WEP_LAST-WEP_FIRST; i += floor((WEP_LAST-WEP_FIRST)/5))
weapons_stat |= power2of(i);
-
+
if(menu_enabled != 2)
HUD_Panel_DrawBg(1); // also draw the bg of the entire panel
}
-
+
// do we own this weapon?
for(i = 0; i <= WEP_LAST-WEP_FIRST; ++i)
if(weapons_stat & weaponorder[i].weapons)
++weapon_count;
-
+
// add it anyway if weaponcomplain is shown
if((!autocvar__hud_configure)
&& (autocvar_hud_panel_weapons_complainbubble
&& time - complain_weapon_time < when + fadetime))
++weapon_count;
-
+
// might as well commit suicide now, no reason to live ;)
if (weapon_count == 0) { return; }
-
+
// reduce size of the panel
if (panel_size_y > panel_size_x)
{
}
}
- // draw the background, then change the virtual size of it to better fit other items inside
+ // draw the background, then change the virtual size of it to better fit other items inside
HUD_Panel_DrawBg(1);
if(panel_bg_padding)
{
panel_pos += '1 1 0' * panel_bg_padding;
- panel_size -= '1 1 0' * panel_bg_padding;
+ panel_size -= '2 2 0' * panel_bg_padding;
}
// after the sizing and animations are done, update the other values
rows = bound(1, floor((sqrt(4 * aspect * rows * weapon_count + rows * rows) + rows + 0.5) / 2), weapon_count);
columns = ceil(weapon_count/rows);
weapon_size = eX * panel_size_x*(1/columns) + eY * panel_size_y*(1/rows);
-
+
// calculate position/size for visual bar displaying ammount of ammo status
if (autocvar_hud_panel_weapons_ammo)
{
// retrieve information about the current weapon to be drawn
self = weaponorder[i];
weapon_id = self.impulse;
-
+
// skip if this weapon doesn't exist
if (!self || self.impulse < 0) { continue; }
-
+
// skip this weapon if we don't own it (and onlyowned is enabled)-- or if weapons_complainbubble is showing for this weapon
if (autocvar_hud_panel_weapons_onlyowned
&& !((weapons_stat & self.weapons)
&& time - complain_weapon_time < when + fadetime
&& autocvar_hud_panel_weapons_complainbubble)))
continue;
-
+
// figure out the drawing position of weapon
weapon_pos = (panel_pos
+ eX * column * weapon_size_x
case 1: // weapon number
drawstring(weapon_pos, ftos(weapon_id), '1 1 0' * 0.5 * weapon_size_y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
break;
-
+
case 2: // bind
drawstring(weapon_pos, getcommandkey(ftos(weapon_id), strcat("impulse ", ftos(weapon_id))), '1 1 0' * 0.5 * weapon_size_y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
break;
-
+
case 3: // weapon name
drawstring(weapon_pos, self.netname, '1 1 0' * 0.5 * weapon_size_y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
break;
-
+
default: // nothing
break;
}
-
+
// draw ammo status bar
if(autocvar_hud_panel_weapons_ammo && self.weapon != WEP_TUBA && self.weapon != WEP_LASER && self.weapon != WEP_PORTO)
{
{
drawpic_aspect_skin(weapon_pos, strcat("weapon", self.netname), weapon_size, '0 0 0', panel_fg_alpha * 0.5, DRAWFLAG_NORMAL);
}
-
+
// draw the complain message
if(time - complain_weapon_time < when + fadetime && self.weapon == complain_weapon && autocvar_hud_panel_weapons_complainbubble)
{
string sign;
vector distribution_color;
entity tm, pl, me;
+#ifdef COMPAT_XON050_ENGINE
me = (spectatee_status > 0) ? playerslots[spectatee_status - 1] : playerslots[player_localentnum - 1];
+#else
+ me = playerslots[player_localentnum - 1];
+#endif
if((scores_flags[ps_primary] & SFL_TIME) && !teamplay) { // race/cts record display on HUD
string timer, distrtimer;
{
mod_active = 1; // race should never hide the mod icons panel
entity me;
- me = playerslots[player_localentnum - 1];
+ me = playerslots[player_localnum];
float t, score;
float f; // yet another function has this
score = me.(scores[ps_primary]);
drawcolorcodedstring_aspect(namepos, s, '1 0.2 0' * squareSize, panel_fg_alpha * a, DRAWFLAG_NORMAL);
drawstring_aspect(rankpos, rankname, '1 0.15 0' * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
} else if(race_status == 2) {
- if(race_status_name == GetPlayerName(player_localentnum -1) || !race_myrank || race_myrank < rank)
+ if(race_status_name == GetPlayerName(player_localnum) || !race_myrank || race_myrank < rank)
drawpic_aspect_skin(medalPos + '0.1 0 0' * squareSize, "race_newrankgreen", '1 1 0' * 0.8 * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
else
drawpic_aspect_skin(medalPos + '0.1 0 0' * squareSize, "race_newrankyellow", '1 1 0' * 0.8 * squareSize, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
if(spectatee_status == -1)
s = _("^1Observing");
else
+#ifdef COMPAT_XON050_ENGINE
s = sprintf(_("^1Spectating: ^7%s"), GetPlayerName(spectatee_status - 1));
+#else
+ s = sprintf(_("^1Spectating: ^7%s"), GetPlayerName(player_localentnum - 1));
+#endif
drawInfoMessage(s)
if(spectatee_status == -1)
else if(gametype == GAME_LMS)
{
entity sk;
- sk = playerslots[player_localentnum - 1];
+ sk = playerslots[player_localnum];
if(sk.(scores[ps_primary]) >= 666)
s = _("^1Match has already begun");
else if(sk.(scores[ps_primary]) > 0)
drawfill(eY * i * hud_configure_realGridSize_y, eY + eX * vid_conwidth, '0.5 0.5 0.5', autocvar_hud_configure_grid_alpha, DRAWFLAG_NORMAL);
}
+#ifdef COMPAT_XON050_ENGINE
current_player = (spectatee_status > 0) ? spectatee_status : player_localentnum;
+#else
+ current_player = player_localentnum;
+#endif
// draw the dock
if(autocvar_hud_dock != "" && autocvar_hud_dock != "0")
var void() menu_show;
var float(float bInputType, float nPrimary, float nSecondary) menu_action;
-// --------------------------------------------------------------------------
-// CTF
-
-#ifdef CTF_EXAMPLE
-void() ctf_menu_show;
-string ctf_temp_1;
-#endif
-
// --------------------------------------------------------------------------
// Onslaught
miscfunctions.qc
teamplay.qc
-ctf.qc
-
teamradar.qc
hud_config.qc
hud.qc
Movetype_Physics_MatchServer(autocvar_cl_projectiles_sloppy);
if(!(self.move_flags & FL_ONGROUND))
if(self.velocity != '0 0 0')
- self.angles = vectoangles(self.velocity);
+ self.move_angles = self.angles = vectoangles(self.velocity);
}
else
{
{
if(pl.team != tm.team)
continue;
- HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localentnum - 1), i);
+ HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localnum), i);
pos_y += 1.25 * hud_fontsize_y;
++i;
}
{
if(pl.team == COLOR_SPECTATOR)
continue;
- HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localentnum - 1), i);
+ HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localnum), i);
pos_y += 1.25 * hud_fontsize_y;
++i;
}
continue;
n = grecordholder[i];
p = race_PlaceName(i+1);
- if(grecordholder[i] == GetPlayerName(player_localentnum - 1))
+ if(grecordholder[i] == GetPlayerName(player_localnum))
drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize_y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
else if(!mod(i, 2) && scoreboard_highlight)
drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize_y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
if(pl.team != COLOR_SPECTATOR)
continue;
pos_y += 1.25 * hud_fontsize_y;
- HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localentnum - 1), specs);
+ HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localnum), specs);
++specs;
}
if(!autocvar_hud_shownames)
return;
+#ifdef COMPAT_XON050_ENGINE
if((ent.sv_entnum == player_localentnum) || (ent.sv_entnum == spectatee_status)) // ent is me or person i'm spectating
+#else
+ if(ent.sv_entnum == player_localentnum) // ent is me or person i'm spectating
+#endif
if not (autocvar_hud_shownames_self && autocvar_chase_active)
return;
return;
string spriteimage = self.netname;
- float t = (GetPlayerColor(player_localentnum - 1) + 1);
+ float t = (GetPlayerColor(player_localnum) + 1);
float a = self.alpha * autocvar_hud_panel_fg_alpha;
vector rgb = spritelookupcolor(spriteimage, self.teamradar_color);
InterpolateOrigin_Do();
- t = GetPlayerColor(player_localentnum - 1) + 1;
+ t = GetPlayerColor(player_localnum) + 1;
spriteimage = "";
#define SERVERFLAG_ALLOW_FULLBRIGHT 1
#define SERVERFLAG_TEAMPLAY 2
#define SERVERFLAG_PLAYERSTATS 4
+
+// spawnpoint prios
+#define SPAWN_PRIO_NEAR_TEAMMATE_FOUND 200
+#define SPAWN_PRIO_NEAR_TEAMMATE_SAMETEAM 100
+#define SPAWN_PRIO_RACE_PREVIOUS_SPAWN 50
+#define SPAWN_PRIO_GOOD_DISTANCE 10
self = oldself;
}
+vector target_objective_spawn_evalfunc(entity player, entity spot, vector current)
+{
+ if(self.health < 0 || self.health >= ASSAULT_VALUE_INACTIVE)
+ return '-1 0 0';
+ return current;
+}
+
void spawnfunc_target_objective() {
if(!g_assault)
{
self.use = assault_objective_use;
assault_objective_reset();
self.reset = assault_objective_reset;
+ self.spawn_evalfunc = target_objective_spawn_evalfunc;
}
#define autocvar_slowmo cvar("slowmo")
float autocvar_snd_soundradius;
float autocvar_spawn_debug;
-float autocvar_spawn_debugview;
float autocvar_speedmeter;
float autocvar_sv_accelerate;
var float autocvar_sv_accuracy_data_share = 1;
// Returns:
// _x: prio (-1 if unusable)
// _y: weight
-vector Spawn_Score(entity spot, entity playerlist, float teamcheck, float anypoint)
+vector Spawn_Score(entity spot, float mindist, float teamcheck)
{
float shortest, thisdist;
float prio;
return '-1 0 0';
}
+ shortest = vlen(world.maxs - world.mins);
+ FOR_EACH_PLAYER(player) if (player != self)
+ {
+ thisdist = vlen(player.origin - spot.origin);
+ if (thisdist < shortest)
+ shortest = thisdist;
+ }
+ if(shortest < mindist)
+ prio += SPAWN_PRIO_GOOD_DISTANCE;
+
+ spawn_score = prio * '1 0 0' + shortest * '0 1 0';
+ spawn_spot = spot;
+
// filter out spots for assault
if(spot.target != "") {
entity ent;
- float good, found;
+ float found;
found = 0;
- good = 0;
for(ent = world; (ent = find(ent, targetname, spot.target)); )
{
++found;
- if(ent.classname == "target_objective")
+ if(ent.spawn_evalfunc)
{
- if(ent.health < 0 || ent.health >= ASSAULT_VALUE_INACTIVE)
- continue;
- }
- else if(ent.classname == "trigger_race_checkpoint")
- {
- if(!anypoint) // spectators may spawn everywhere
- {
- if(g_race_qualifying)
- {
- // spawn at first
- if(ent.race_checkpoint != 0)
- continue;
- if(spot.race_place != race_lowest_place_spawn)
- continue;
- }
- else
- {
- if(ent.race_checkpoint != self.race_respawn_checkpoint)
- continue;
- // try reusing the previous spawn
- if(ent == self.race_respawn_spotref || spot == self.race_respawn_spotref)
- prio += 1;
- if(ent.race_checkpoint == 0)
- {
- float pl;
- pl = self.race_place;
- if(pl > race_highest_place_spawn)
- pl = 0;
- if(pl == 0 && !self.race_started)
- pl = race_highest_place_spawn; // use last place if he has not even touched finish yet
- if(spot.race_place != pl)
- continue;
- }
- }
- }
+ entity oldself = self;
+ self = ent;
+ spawn_score = ent.spawn_evalfunc(oldself, spot, spawn_score);
+ self = oldself;
+ if(spawn_score_x < 0)
+ return spawn_score;
}
- ++good;
}
if(!found)
dprint("WARNING: spawnpoint at ", vtos(spot.origin), " could not find its target ", spot.target, "\n");
return '-1 0 0';
}
-
- if(good < found) // at least one was bad
- return '-1 0 0';
}
- player = playerlist;
- shortest = vlen(world.maxs - world.mins);
- for(player = playerlist; player; player = player.chain)
- if (player != self)
- {
- thisdist = vlen(player.origin - spot.origin);
- if (thisdist < shortest)
- shortest = thisdist;
- }
- return prio * '1 0 0' + shortest * '0 1 0';
+ MUTATOR_CALLHOOK(Spawn_Score);
+ return spawn_score;
+}
+
+void Spawn_ScoreAll(entity firstspot, float mindist, float teamcheck)
+{
+ entity spot;
+ for(spot = firstspot; spot; spot = spot.chain)
+ spot.spawnpoint_score = Spawn_Score(spot, mindist, teamcheck);
}
-float spawn_allbad;
-float spawn_allgood;
-entity Spawn_FilterOutBadSpots(entity firstspot, entity playerlist, float mindist, float teamcheck, float anypoint)
+entity Spawn_FilterOutBadSpots(entity firstspot, float mindist, float teamcheck)
{
entity spot, spotlist, spotlistend;
- spawn_allgood = TRUE;
- spawn_allbad = TRUE;
spotlist = world;
spotlistend = world;
+ Spawn_ScoreAll(firstspot, mindist, teamcheck);
+
for(spot = firstspot; spot; spot = spot.chain)
{
- spot.spawnpoint_score = Spawn_Score(spot, playerlist, teamcheck, anypoint);
-
- if(autocvar_spawn_debugview)
- {
- setmodel(spot, "models/runematch/rune.mdl");
- if(spot.spawnpoint_score_y < mindist)
- {
- spot.colormod = '1 0 0';
- spot.scale = 1;
- }
- else
- {
- spot.colormod = '0 1 0';
- spot.scale = spot.spawnpoint_score_y / mindist;
- }
- }
-
if(spot.spawnpoint_score_x >= 0) // spawning allowed here
{
- if(spot.spawnpoint_score_y < mindist)
- {
- // too short distance
- spawn_allgood = FALSE;
- }
- else
- {
- // perfect
- spawn_allbad = FALSE;
-
- if(spotlistend)
- spotlistend.chain = spot;
- spotlistend = spot;
- if(!spotlist)
- spotlist = spot;
-
- /*
- if(teamcheck >= 0)
- if(spot.team != teamcheck)
- error("invalid spawn added");
-
- print("added ", etos(spot), "\n");
- */
- }
+ if(spotlistend)
+ spotlistend.chain = spot;
+ spotlistend = spot;
+ if(!spotlist)
+ spotlist = spot;
}
}
if(spotlistend)
spotlistend.chain = world;
- /*
- entity e;
- if(teamcheck >= 0)
- for(e = spotlist; e; e = e.chain)
- {
- print("seen ", etos(e), "\n");
- if(e.team != teamcheck)
- error("invalid spawn found");
- }
- */
-
return spotlist;
}
entity SelectSpawnPoint (float anypoint)
{
float teamcheck;
- entity firstspot_new;
- entity spot, firstspot, playerlist;
+ entity spot, firstspot;
spot = find (world, classname, "testplayerstart");
if (spot)
// if we get here, we either require team spawns but have none, or we require non-team spawns and have none; use any spawn then
- // get the list of players
- playerlist = findchain(classname, "player");
// get the entire list of spots
firstspot = findchain(classname, "info_player_deathmatch");
// filter out the bad ones
}
else
{
- firstspot_new = Spawn_FilterOutBadSpots(firstspot, playerlist, 100, teamcheck, anypoint);
- if(!firstspot_new)
- firstspot_new = Spawn_FilterOutBadSpots(firstspot, playerlist, -1, teamcheck, anypoint);
- firstspot = firstspot_new;
+ float mindist;
+ if (arena_roundbased && !g_ca)
+ mindist = 800;
+ else
+ mindist = 100;
+ firstspot = Spawn_FilterOutBadSpots(firstspot, mindist, teamcheck);
// there is 50/50 chance of choosing a random spot or the furthest spot
// (this means that roughly every other spawn will be furthest, so you
// usually won't get fragged at spawn twice in a row)
- if (arena_roundbased && !g_ca)
- {
- firstspot_new = Spawn_FilterOutBadSpots(firstspot, playerlist, 800, teamcheck, anypoint);
- if(firstspot_new)
- firstspot = firstspot_new;
- spot = Spawn_WeightedPoint(firstspot, 1, 1, 1);
- }
- else if (random() > autocvar_g_spawn_furthest)
+ if (random() > autocvar_g_spawn_furthest)
spot = Spawn_WeightedPoint(firstspot, 1, 1, 1);
else
spot = Spawn_WeightedPoint(firstspot, 1, 5000, 5); // chooses a far far away spawnpoint
}
- if(autocvar_spawn_debugview)
- {
- print("spot mindistance: ", vtos(spot.spawnpoint_score), "\n");
-
- entity e;
- if(teamcheck >= 0)
- for(e = firstspot; e; e = e.chain)
- if(e.team != teamcheck)
- error("invalid spawn found");
- }
-
if (!spot)
{
if(autocvar_spawn_debug)
race_PostSpawn(spot);
- if(autocvar_spawn_debug)
- {
- sprint(self, strcat("spawnpoint origin: ", vtos(spot.origin), "\n"));
- remove(spot); // usefull for checking if there are spawnpoints, that let drop through the floor
- }
-
//stuffcmd(self, "chase_active 0");
//stuffcmd(self, "set viewsize $tmpviewsize \n");
activator = world;
self = oldself;
+ spawn_spot = spot;
MUTATOR_CALLHOOK(PlayerSpawn);
+ if(autocvar_spawn_debug)
+ {
+ sprint(self, strcat("spawnpoint origin: ", vtos(spot.origin), "\n"));
+ remove(spot); // usefull for checking if there are spawnpoints, that let drop through the floor
+ }
+
self.switchweapon = w_getbestweapon(self);
self.cnt = -1; // W_LastWeapon will not complain
self.weapon = 0;
.float misc_bulletcounter; // replaces uzi & hlac bullet counter.
void PlayerUseKey();
+
+typedef vector(entity player, entity spot, vector current) spawn_evalfunc_t;
+.spawn_evalfunc_t spawn_evalfunc;
MUTATOR_ADD(mutator_rocketflying);
if(cvar("g_vampire"))
MUTATOR_ADD(mutator_vampire);
+ if(cvar("g_spawn_near_teammate"))
+ MUTATOR_ADD(mutator_spawn_near_teammate);
if(cvar("sv_allow_fullbright"))
serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
// called when a player becomes observer, after shared setup
MUTATOR_HOOKABLE(PlayerSpawn);
+ entity spawn_spot; // spot that was used, or world
// called when a player spawns as player, after shared setup, before his weapon is chosen (so items may be changed in here)
MUTATOR_HOOKABLE(ClientDisconnect);
return 0;
}
*/
+
+MUTATOR_HOOKABLE(Spawn_Score);
+ // called when a spawnpoint is being evaluated
+ // return 1 to make the spawnpoint unusable
+ // INPUT
+ entity self; // player wanting to spawn
+ entity spawn_spot; // spot to be evaluated
+ // IN+OUT
+ vector spawn_score; // _x is priority, _y is "distance"
--- /dev/null
+float autocvar_g_spawn_near_teammate_distance;
+.entity msnt_lookat;
+
+MUTATOR_HOOKFUNCTION(msnt_Spawn_Score)
+{
+ entity p;
+
+ spawn_spot.msnt_lookat = world;
+
+ if(!teamplay)
+ return 0;
+
+ RandomSelection_Init();
+ FOR_EACH_PLAYER(p) if(p != self) if(p.team == self.team) if(!p.deadflag)
+ {
+ float l = vlen(spawn_spot.origin - p.origin);
+ if(l > autocvar_g_spawn_near_teammate_distance)
+ continue;
+ if(l < 48)
+ continue;
+ if(!checkpvs(spawn_spot.origin, p))
+ continue;
+ RandomSelection_Add(p, 0, string_null, 1, 1);
+ }
+
+ if(RandomSelection_chosen_ent)
+ {
+ spawn_spot.msnt_lookat = RandomSelection_chosen_ent;
+ spawn_score_x += SPAWN_PRIO_NEAR_TEAMMATE_FOUND;
+ }
+ else if(self.team == spawn_spot.team)
+ spawn_score_x += SPAWN_PRIO_NEAR_TEAMMATE_SAMETEAM; // prefer same team, if we can't find a spawn near teammate
+
+ return 0;
+}
+
+MUTATOR_HOOKFUNCTION(msnt_PlayerSpawn)
+{
+ if(spawn_spot.msnt_lookat)
+ {
+ self.angles = vectoangles(spawn_spot.msnt_lookat.origin - self.origin);
+ self.angles_x = -self.angles_x;
+ /*
+ sprint(self, "You should be looking at ", spawn_spot.msnt_lookat.netname, "^7.\n");
+ sprint(self, "distance: ", vtos(spawn_spot.msnt_lookat.origin - self.origin), "\n");
+ sprint(self, "angles: ", vtos(self.angles), "\n");
+ */
+ }
+
+ return 0;
+}
+
+MUTATOR_DEFINITION(mutator_spawn_near_teammate)
+{
+ MUTATOR_HOOK(Spawn_Score, msnt_Spawn_Score, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerSpawn, msnt_PlayerSpawn, CBC_ORDER_ANY);
+
+ return 0;
+}
MUTATOR_DECLARATION(mutator_dodging);
MUTATOR_DECLARATION(mutator_rocketflying);
MUTATOR_DECLARATION(mutator_vampire);
+
+MUTATOR_DECLARATION(mutator_spawn_near_teammate);
mutators/mutator_dodging.qc
mutators/mutator_rocketflying.qc
mutators/mutator_vampire.qc
+mutators/mutator_spawn_near_teammate.qc
../warpzonelib/anglestransform.qc
../warpzonelib/mathlib.qc
// race only (middle of the race)
g_race_qualifying = 0;
self.race_place = 0;
- if(!Spawn_FilterOutBadSpots(findchain(classname, "info_player_deathmatch"), world, 0, FALSE, FALSE))
+ if(!Spawn_FilterOutBadSpots(findchain(classname, "info_player_deathmatch"), 0, FALSE))
error(strcat("Checkpoint ", ftos(i), " misses a spawnpoint with race_place==", ftos(self.race_place), " (used for respawning in race) - bailing out"));
if(i == 0)
// qualifying only
g_race_qualifying = 1;
self.race_place = race_lowest_place_spawn;
- if(!Spawn_FilterOutBadSpots(findchain(classname, "info_player_deathmatch"), world, 0, FALSE, FALSE))
+ if(!Spawn_FilterOutBadSpots(findchain(classname, "info_player_deathmatch"), 0, FALSE))
error(strcat("Checkpoint ", ftos(i), " misses a spawnpoint with race_place==", ftos(self.race_place), " (used for qualifying) - bailing out"));
// race only (initial spawn)
for(p = 1; p <= race_highest_place_spawn; ++p)
{
self.race_place = p;
- if(!Spawn_FilterOutBadSpots(findchain(classname, "info_player_deathmatch"), world, 0, FALSE, FALSE))
+ if(!Spawn_FilterOutBadSpots(findchain(classname, "info_player_deathmatch"), 0, FALSE))
error(strcat("Checkpoint ", ftos(i), " misses a spawnpoint with race_place==", ftos(self.race_place), " (used for initially spawning in race) - bailing out"));
}
}
self.race_checkpoint = race_NextCheckpoint(0);
g_race_qualifying = 1;
self.race_place = race_lowest_place_spawn;
- if(!Spawn_FilterOutBadSpots(findchain(classname, "info_player_deathmatch"), world, 0, FALSE, FALSE))
+ if(!Spawn_FilterOutBadSpots(findchain(classname, "info_player_deathmatch"), 0, FALSE))
error(strcat("Checkpoint ", ftos(i), " misses a spawnpoint with race_place==", ftos(self.race_place), " (used for qualifying) - bailing out"));
}
else
self = oldself;
}
+vector trigger_race_checkpoint_spawn_evalfunc(entity player, entity spot, vector current)
+{
+ if(g_race_qualifying)
+ {
+ // spawn at first
+ if(self.race_checkpoint != 0)
+ return '-1 0 0';
+ if(spot.race_place != race_lowest_place_spawn)
+ return '-1 0 0';
+ }
+ else
+ {
+ if(self.race_checkpoint != player.race_respawn_checkpoint)
+ return '-1 0 0';
+ // try reusing the previous spawn
+ if(self == player.race_respawn_spotref || spot == player.race_respawn_spotref)
+ current_x += SPAWN_PRIO_RACE_PREVIOUS_SPAWN;
+ if(self.race_checkpoint == 0)
+ {
+ float pl;
+ pl = player.race_place;
+ if(pl > race_highest_place_spawn)
+ pl = 0;
+ if(pl == 0 && !player.race_started)
+ pl = race_highest_place_spawn; // use last place if he has not even touched finish yet
+ if(spot.race_place != pl)
+ return '-1 0 0';
+ }
+ }
+ return current;
+}
+
void spawnfunc_trigger_race_checkpoint()
{
vector o;
}
self.sprite.waypointsprite_visible_for_player = race_waypointsprite_visible_for_player;
+ self.spawn_evalfunc = trigger_race_checkpoint_spawn_evalfunc;
InitializeEntity(self, trigger_race_checkpoint_verify, INITPRIO_FINDTARGET);
}
RuneMatchGivePoints();
bot_serverframe();
- if(autocvar_spawn_debugview)
- {
- RandomSelection_Init();
- for(self = world; (self = find(self, classname, "player")); )
- RandomSelection_Add(self, 0, string_null, 1, 0);
- self = RandomSelection_chosen_ent;
- SelectSpawnPoint(0);
- }
-
FOR_EACH_PLAYER(self)
self.porto_forbidden = max(0, self.porto_forbidden - 1);
}