seta g_maplist_votable_abstain 0 "when 1, you can abstain from your vote"
seta g_maplist_votable_screenshot_dir "maps levelshots" "where to look for map screenshots"
+set sv_vote_gametype 0 "show a vote screen for gametypes before map vote screen"
+set sv_vote_gametype_keeptwotime 10 "show only 2 options for this amount of time during gametype vote screen"
+set sv_vote_gametype_options "dm ctf ca lms tdm ft"
+set sv_vote_gametype_timeout 20
+set sv_vote_gametype_default_current 1 "Keep the current gametype if no one votes"
+
set g_chat_flood_spl 3 "normal chat: seconds between lines to not count as flooding"
set g_chat_flood_lmax 2 "normal chat: maximum number of lines per chat message at once"
set g_chat_flood_burst 2 "normal chat: allow bursts of so many chat lines"
alias sv_hook_gameend
+// =====================
+// gametype vote hooks
+// =====================
+// these are called when the mode is switched via gametype vote screen, earlier than gamestart hooks (useful for enabling per-gamemode mutators)
+alias sv_vote_gametype_hook_all
+alias sv_vote_gametype_hook_as
+alias sv_vote_gametype_hook_ca
+alias sv_vote_gametype_hook_ctf
+alias sv_vote_gametype_hook_cts
+alias sv_vote_gametype_hook_dm
+alias sv_vote_gametype_hook_dom
+alias sv_vote_gametype_hook_ft
+alias sv_vote_gametype_hook_inv
+alias sv_vote_gametype_hook_ka
+alias sv_vote_gametype_hook_kh
+alias sv_vote_gametype_hook_lms
+alias sv_vote_gametype_hook_nb
+alias sv_vote_gametype_hook_ons
+alias sv_vote_gametype_hook_rc
+alias sv_vote_gametype_hook_tdm
+
+
// ===========
// leadlimit
// ===========
string mv_pk3[MAPVOTE_COUNT];
float mv_preview[MAPVOTE_COUNT];
float mv_votes[MAPVOTE_COUNT];
+float mv_avail[MAPVOTE_COUNT];
+float mv_avail_start[MAPVOTE_COUNT];
entity mv_pk3list;
float mv_abstain;
float mv_ownvote;
float mv_detail;
float mv_timeout;
-float mv_maps_mask;
float mv_top2_time;
float mv_top2_alpha;
vector mv_mousepos;
float mv_selection;
+float mv_columns;
+
+float gametypevote;
+string mapvote_choosenmap;
+vector gtv_text_size;
+vector gtv_text_size_small;
string MapVote_FormatMapItem(float id, string map, float count, float maxwidth, vector fontsize)
{
{
if(count == 1)
post = _(" (1 vote)");
- else if(count >= 0)
+ else if(count >= 0 && mv_avail[id] == GTV_AVAILABLE)
post = sprintf(_(" (%d votes)"), count);
else
post = "";
return strcat(pre, map, post);
}
-vector MapVote_RGB(float id, float count)
+string GameTypeVote_DescriptionByID(float id)
+{
+ return MapInfo_Type_Description(MapInfo_Type_FromString(mv_maps[id]));
+}
+
+vector MapVote_RGB(float id)
{
- if(count < 0)
+ if(mv_avail[id] != GTV_AVAILABLE)
return '1 1 1';
if(id == mv_ownvote)
return '0 1 0';
return '1 1 1';
}
+void GameTypeVote_DrawGameTypeItem(vector pos, float maxh, float tsize, string gtype, string pic, float count, float id)
+{
+ float alpha;
+ float desc_padding = gtv_text_size_x * 3;
+ float rect_margin = hud_fontsize_y / 2;
+ vector rect_pos = pos - '0.5 0.5 0' * rect_margin;
+ vector rect_size = '1 1 0';
+ rect_size_x = tsize + rect_margin;
+ rect_size_y = maxh + rect_margin;
+ vector rgb = MapVote_RGB(id);
+ vector offset = pos;
+ float nlines = 0;
+
+ if(mv_avail_start[id] != GTV_AVAILABLE)
+ alpha = 0.2;
+ else if ( mv_avail[id] != GTV_AVAILABLE && mv_top2_alpha)
+ alpha = mv_top2_alpha;
+ else
+ alpha = 1;
+
+ if(id == mv_selection && mv_avail[id] == GTV_AVAILABLE)
+ {
+ drawfill(rect_pos, rect_size, '1 1 1', 0.1, DRAWFLAG_NORMAL);
+ }
+ if(id == mv_ownvote)
+ {
+ drawfill(rect_pos, rect_size, rgb, 0.1*alpha, DRAWFLAG_NORMAL);
+ drawborderlines(autocvar_scoreboard_border_thickness, rect_pos, rect_size, rgb, alpha, DRAWFLAG_NORMAL);
+ }
+
+ entity title;
+ title = spawn();
+ title.message = MapVote_FormatMapItem(id, MapInfo_Type_ToText(MapInfo_Type_FromString(gtype)),
+ count, tsize, gtv_text_size);
+ title.origin = pos-offset;
+
+ pos_y += gtv_text_size_small_y;
+ pos_y += gtv_text_size_y/2;
+
+ maxh -= gtv_text_size_y;
+
+ entity picent = spawn();
+ picent.origin = pos-offset;
+ picent.maxs = '1 1 0 ' * min(maxh, desc_padding) * 0.8;
+
+ pos_x += desc_padding;
+ tsize -= desc_padding;
+
+ string thelabel = GameTypeVote_DescriptionByID(id), ts;
+ entity last = title;
+ entity next = world;
+ if( thelabel != "")
+ {
+ float i,n = tokenizebyseparator(thelabel, "\n");
+ for(i = 0; i < n && maxh > (nlines+1)*gtv_text_size_small_y; ++i)
+ {
+ getWrappedLine_remaining = argv(i);
+ while(getWrappedLine_remaining && maxh > (nlines+1)*gtv_text_size_small_y)
+ {
+ ts = getWrappedLine(tsize, gtv_text_size_small, stringwidth_colors);
+ if (ts != "")
+ {
+ next = spawn();
+ next.message = ts;
+ next.origin = pos-offset;
+ last.chain = next;
+ last = next;
+ pos_y += gtv_text_size_small_y;
+ nlines++;
+ }
+ }
+ }
+ }
+
+ maxh -= max(nlines*gtv_text_size_small_y,picent.maxs_y);
+ if ( maxh > 0 )
+ offset_y += maxh/2;
+ drawstring(title.origin+offset, title.message, gtv_text_size, rgb, alpha, DRAWFLAG_NORMAL);
+
+ if(pic != "")
+ drawpic(picent.origin+offset, pic, picent.maxs, '1 1 1', alpha, DRAWFLAG_NORMAL);
+
+ for ( last = title.chain; last ; )
+ {
+ drawstring(last.origin+offset, last.message, gtv_text_size_small, '1 1 1', alpha, DRAWFLAG_NORMAL);
+ next = last;
+ last = last.chain;
+ remove(next);
+ }
+
+ remove(picent);
+ remove(title);
+}
+
void MapVote_DrawMapItem(vector pos, float isize, float tsize, string map, string pic, float count, float id)
{
vector img_size = '0 0 0';
isize -= hud_fontsize_y; // respect the text when calculating the image size
- rgb = MapVote_RGB(id, count);
+ rgb = MapVote_RGB(id);
img_size_y = isize;
img_size_x = isize / 0.75; // 4:3 x can be stretched easily, height is defined in isize
text_size = stringwidth(label, false, hud_fontsize);
float theAlpha;
- if (count < 0 && mv_top2_alpha)
+ if (mv_avail[id] != GTV_AVAILABLE && mv_top2_alpha)
theAlpha = mv_top2_alpha;
else
theAlpha = 1;
else
drawborderlines(autocvar_scoreboard_border_thickness, pos, img_size, '0 0 0', theAlpha, DRAWFLAG_NORMAL);
- if(id == mv_selection && count >= 0)
+ if(id == mv_selection && mv_avail[id] == GTV_AVAILABLE)
drawfill(pos, img_size, '1 1 1', 0.1, DRAWFLAG_NORMAL);
}
float text_size;
string label;
- rgb = MapVote_RGB(id, count);
+ rgb = MapVote_RGB(id);
pos_y = pos_y + hud_fontsize_y;
vector pos;
float isize;
float center;
- float columns, rows;
+ float rows;
float tsize;
vector dist = '0 0 0';
pos_z = 0;
draw_beginBoldFont();
- map = _("Vote for a map");
+ map = ((gametypevote) ? _("Decide the gametype") : _("Vote for a map"));
pos_x = center - stringwidth(map, false, '12 0 0');
drawstring(pos, map, '24 24 0', '1 1 1', 1, DRAWFLAG_NORMAL);
pos_y += 26;
+ if(gametypevote && mapvote_choosenmap != "" )
+ {
+ pos_x = center - stringwidth(mapvote_choosenmap, false, hud_fontsize);
+ drawstring(pos, mapvote_choosenmap, hud_fontsize*2, '1 1 1', 1, DRAWFLAG_NORMAL);
+ pos_y += hud_fontsize_y*2;
+ }
+
i = ceil(max(0, mv_timeout - time));
map = sprintf(_("%d seconds left"), i);
pos_x = center - stringwidth(map, false, '8 0 0');
if(mv_abstain)
mv_num_maps -= 1;
- if(mv_num_maps > 3)
- {
- columns = 3;
- } else {
- columns = mv_num_maps;
- }
- rows = ceil(mv_num_maps / columns);
+ rows = ceil(mv_num_maps / mv_columns);
- dist_x = (xmax - xmin) / columns;
+ dist_x = (xmax - xmin) / mv_columns;
dist_y = (ymax - pos_y) / rows;
- tsize = dist_x - 10;
- isize = min(dist_y - 10, 0.75 * tsize);
- mv_selection = MapVote_Selection(pos, dist, rows, columns);
+ if ( gametypevote )
+ {
+ tsize = dist_x - hud_fontsize_y;
+ isize = dist_y;
+ float maxheight = (ymax - pos_y) / 3;
+ if ( isize > maxheight )
+ {
+ pos_x += (isize - maxheight)/2;
+ isize = maxheight;
+ }
+ else
+ dist_y += hud_fontsize_y;
+ pos_x = ( vid_conwidth - dist_x * mv_columns ) / 2;
+ }
+ else
+ {
+ tsize = dist_x - 10;
+ isize = min(dist_y - 10, 0.75 * tsize);
+ }
+
+ mv_selection = MapVote_Selection(pos, dist, rows, mv_columns);
- pos_x += (xmax - xmin) / (2 * columns);
+ if ( !gametypevote )
+ pos_x += dist_x / 2;
pos_y += (dist_y - isize) / 2;
ymax -= isize;
if (mv_top2_time)
mv_top2_alpha = max(0.2, 1 - (time - mv_top2_time)*(time - mv_top2_time));
+ void (vector, float, float, string, string, float, float) DrawItem;
+
+ if(gametypevote)
+ DrawItem = GameTypeVote_DrawGameTypeItem;
+ else
+ DrawItem = MapVote_DrawMapItem;
+
for(i = 0; i < mv_num_maps; ++i)
{
tmp = mv_votes[i]; // FTEQCC bug: too many array accesses in the function call screw it up
map = mv_maps[i];
if(mv_preview[i])
- MapVote_DrawMapItem(pos + MapVote_GridVec(dist, i, columns), isize, tsize, map, mv_pics[i], tmp, i);
+ DrawItem(pos + MapVote_GridVec(dist, i, mv_columns), isize, tsize, map, mv_pics[i], tmp, i);
else
- MapVote_DrawMapItem(pos + MapVote_GridVec(dist, i, columns), isize, tsize, map, "", tmp, i);
+ DrawItem(pos + MapVote_GridVec(dist, i, mv_columns), isize, tsize, map, "", tmp, i);
}
if(mv_abstain)
MapVote_CheckPK3(pic, pk3, id);
}
+void MapVote_ReadMask()
+{
+ float i;
+ if ( mv_num_maps < 24 )
+ {
+ float mask, power;
+ if(mv_num_maps < 8)
+ mask = ReadByte();
+ else if(mv_num_maps < 16)
+ mask = ReadShort();
+ else
+ mask = ReadLong();
+
+ for(i = 0, power = 1; i < mv_num_maps; ++i, power *= 2)
+ mv_avail[i] = (mask & power) ? GTV_AVAILABLE : GTV_FORBIDDEN;
+ }
+ else
+ {
+ for(i = 0; i < mv_num_maps; ++i )
+ mv_avail[i] = ReadByte();
+ }
+}
+
#define NUM_SSDIRS 4
string ssdirs[NUM_SSDIRS];
float n_ssdirs;
void MapVote_Init()
{
- float i, j, power;
+ float i, j;
string map, pk3, s;
precache_sound ("misc/invshot.wav");
mv_ownvote = -1;
mv_timeout = ReadCoord();
- if(mv_num_maps <= 8)
- mv_maps_mask = ReadByte();
- else
- mv_maps_mask = ReadShort();
+ gametypevote = ReadByte();
+
+ float mv_real_num_maps = mv_num_maps - mv_abstain;
+
+ if(gametypevote)
+ {
+ // read map name in case we have nextmap set
+ mapvote_choosenmap = strzone(ReadString());
+
+ gtv_text_size = hud_fontsize*1.4;
+ gtv_text_size_small = hud_fontsize*1.1;
+
+ if (mv_real_num_maps > 8 )
+ mv_columns = 3;
+ else
+ mv_columns = min(2, mv_real_num_maps);
+ }
+ else
+ {
+ if (mv_real_num_maps > 16)
+ mv_columns = 5;
+ else if (mv_real_num_maps > 9)
+ mv_columns = 4;
+ else if(mv_real_num_maps > 3)
+ mv_columns = 3;
+ else
+ mv_columns = mv_real_num_maps;
+ }
+
+ MapVote_ReadMask();
+ for(i = 0; i < mv_num_maps; ++i )
+ mv_avail_start[i] = mv_avail[i];
// Assume mv_pk3list is world, there should only be 1 mapvote per round
mv_pk3list = world; // I'm still paranoid!
- for(i = 0, power = 1; i < mv_num_maps; ++i, power *= 2)
+ for(i = 0; i < mv_num_maps; ++i)
{
mv_votes[i] = 0;
- if(mv_maps_mask & power)
- {
- map = strzone(ReadString());
- pk3 = strzone(ReadString());
- j = bound(0, ReadByte(), n_ssdirs - 1);
-
- mv_maps[i] = map;
- mv_pk3[i] = pk3;
- map = strzone(strcat(ssdirs[j], "/", map));
- mv_pics[i] = map;
+ map = strzone(ReadString());
+ pk3 = strzone(ReadString());
+ j = bound(0, ReadByte(), n_ssdirs - 1);
- mv_preview[i] = false;
+ mv_maps[i] = map;
+ mv_pk3[i] = pk3;
+ mv_avail[i] = ReadByte();
- MapVote_CheckPic(map, pk3, i);
+ if(gametypevote)
+ {
+ //map = strzone(strcat("gfx/menu/default/gametype_", map));
+ map = strzone(sprintf("gfx/menu/%s/gametype_%s", autocvar_menu_skin, map));
+ mv_pics[i] = map;
+ mv_preview[i] = PreviewExists(map);
}
else
{
- mv_maps[i] = strzone("if-you-see-this-the-code-is-broken");
- mv_pk3[i] = strzone("if-you-see-this-the-code-is-broken");
- mv_pics[i] = strzone("if-you-see-this-the-code-is-broken");
+ map = strzone(strcat(ssdirs[j], "/", map));
+ mv_pics[i] = map;
mv_preview[i] = false;
+ MapVote_CheckPic(map, pk3, i);
}
}
n_ssdirs = 0;
}
+void MapVote_SendChoice(float index)
+{
+ localcmd(strcat("\nimpulse ", ftos(index+1), "\n"));
+}
+
+float MapVote_MoveLeft(float pos)
+{
+ float imp;
+ if ( pos < 0 )
+ imp = mv_num_maps - 1;
+ else
+ imp = pos < 1 ? mv_num_maps - 1 : pos - 1;
+ if ( mv_avail[imp] != GTV_AVAILABLE && imp != mv_ownvote )
+ imp = MapVote_MoveLeft(imp);
+ return imp;
+}
+float MapVote_MoveRight(float pos)
+{
+ float imp;
+ if ( pos < 0 )
+ imp = 0;
+ else
+ imp = pos >= mv_num_maps - 1 ? 0 : pos + 1;
+ if ( mv_avail[imp] != GTV_AVAILABLE && imp != mv_ownvote )
+ imp = MapVote_MoveRight(imp);
+ return imp;
+}
+float MapVote_MoveUp(float pos)
+{
+ float imp;
+ if ( pos < 0 )
+ imp = mv_num_maps - 1;
+ else
+ {
+ imp = pos - mv_columns;
+ if ( imp < 0 )
+ {
+ imp = floor(mv_num_maps/mv_columns)*mv_columns + pos % mv_columns;
+ if ( imp >= mv_num_maps )
+ imp -= mv_columns;
+ }
+ }
+ if ( mv_avail[imp] != GTV_AVAILABLE && imp != mv_ownvote )
+ imp = MapVote_MoveUp(imp);
+ return imp;
+}
+float MapVote_MoveDown(float pos)
+{
+ float imp;
+ if ( pos < 0 )
+ imp = 0;
+ else
+ {
+ imp = pos + mv_columns;
+ if ( imp >= mv_num_maps )
+ imp = imp % mv_columns;
+ }
+ if ( mv_avail[imp] != GTV_AVAILABLE && imp != mv_ownvote )
+ imp = MapVote_MoveDown(imp);
+ return imp;
+}
+
float MapVote_InputEvent(float bInputType, float nPrimary, float nSecondary)
{
float imp;
case K_KP_8: localcmd("\nimpulse 8\n"); return true;
case K_KP_9: localcmd("\nimpulse 9\n"); return true;
case K_KP_0: localcmd("\nimpulse 10\n"); return true;
+
+ case K_RIGHTARROW:
+ MapVote_SendChoice(MapVote_MoveRight(mv_ownvote));
+ return true;
+ case K_LEFTARROW:
+ MapVote_SendChoice(MapVote_MoveLeft(mv_ownvote));
+ return true;
+ case K_DOWNARROW:
+ MapVote_SendChoice(MapVote_MoveDown(mv_ownvote));
+ return true;
+ case K_UPARROW:
+ MapVote_SendChoice(MapVote_MoveUp(mv_ownvote));
+ return true;
}
if (nPrimary == K_MOUSE1)
void MapVote_UpdateMask()
{
- float i, power;
- float oldmask;
-
- oldmask = mv_maps_mask;
- if(mv_num_maps <= 8)
- mv_maps_mask = ReadByte();
- else
- mv_maps_mask = ReadShort();
-
- if((oldmask & mv_maps_mask) != oldmask)
- if((oldmask & mv_maps_mask) == mv_maps_mask)
- sound(world, CH_INFO, "misc_invshot.wav", VOL_BASE, ATTEN_NONE);
-
- // remove votes that no longer apply
- for(i = 0, power = 1; i < mv_num_maps; ++i, power *= 2)
- if (!(mv_maps_mask & power))
- mv_votes[i] = -1;
-
+ MapVote_ReadMask();
mv_top2_time = time;
}
void MapVote_UpdateVotes()
{
- float i, power;
- for(i = 0, power = 1; i < mv_num_maps; ++i, power *= 2)
+ float i;
+ for(i = 0; i < mv_num_maps; ++i)
{
- if(mv_maps_mask & power)
+ if(mv_avail[i] == GTV_AVAILABLE)
{
if(mv_detail)
mv_votes[i] = ReadByte();
// moved that here so the client knows the max.
// # of maps, I'll use arrays for them :P
-#define MAPVOTE_COUNT 10
+#define MAPVOTE_COUNT 30
/**
* Lower scores are better (e.g. suicides)
#define URI_GET_UPDATENOTIFICATION 33
#define URI_GET_URLLIB 128
#define URI_GET_URLLIB_END 191
+
+// gametype votes
+#define GTV_AVAILABLE 0
+// for later use in per-map gametype filtering
+#define GTV_FORBIDDEN 2
return 0;
}
+string MapInfo_Type_Description(float t)
+{
+ entity e;
+ for(e = MapInfo_Type_first; e; e = e.enemy)
+ if(t == e.items)
+ return e.gametype_description;
+ return "";
+}
+
string MapInfo_Type_ToString(float t)
{
entity e;
{
if(!(MapInfo_Map_supportedGametypes & pGametypeToSet))
{
- error("Can't select the requested game type. This should never happen as the caller should prevent it!\n");
+ //error("Can't select the requested game type. This should never happen as the caller should prevent it!\n");
+ return 0;
//_MapInfo_Map_ApplyGametypeEx("", pGametypeToSet, MAPINFO_TYPE_DEATHMATCH);
//return;
}
localcmd(strcat("\nchangelevel ", s, "\n"));
}
-string MapInfo_ListAllowedMaps(float pRequiredFlags, float pForbiddenFlags)
+string MapInfo_ListAllowedMaps(float type, float pRequiredFlags, float pForbiddenFlags)
{
string out;
float i;
// to make absolutely sure:
MapInfo_Enumerate();
- MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), pRequiredFlags, pForbiddenFlags, 0);
+ MapInfo_FilterGametype(type, MapInfo_CurrentFeatures(), pRequiredFlags, pForbiddenFlags, 0);
out = "";
for(i = 0; i < MapInfo_count; ++i)
.string mdl; // game type short name
.string message; // human readable name
.string model2; // game type defaults
+.string gametype_description; // game type description
-#define REGISTER_GAMETYPE(hname,sname,g_name,NAME,defaults) \
+#define REGISTER_GAMETYPE(hname,sname,g_name,NAME,defaults,gdescription) \
var float MAPINFO_TYPE_##NAME; \
var entity MapInfo_Type##g_name; \
void RegisterGametypes_##g_name() \
MapInfo_Type##g_name.mdl = #sname; \
MapInfo_Type##g_name.message = hname; \
MapInfo_Type##g_name.model2 = defaults; \
+ MapInfo_Type##g_name.gametype_description = gdescription; \
if(!MapInfo_Type_first) \
MapInfo_Type_first = MapInfo_Type##g_name; \
if(MapInfo_Type_last) \
#define IS_GAMETYPE(NAME) \
(MapInfo_LoadedGametype == MAPINFO_TYPE_##NAME)
-REGISTER_GAMETYPE(_("Deathmatch"),dm,g_dm,DEATHMATCH,"timelimit=20 pointlimit=30 leadlimit=0");
+REGISTER_GAMETYPE(_("Deathmatch"),dm,g_dm,DEATHMATCH,"timelimit=20 pointlimit=30 leadlimit=0",_("Kill all enemies"));
#define g_dm IS_GAMETYPE(DEATHMATCH)
-REGISTER_GAMETYPE(_("Last Man Standing"),lms,g_lms,LMS,"timelimit=20 lives=9 leadlimit=0");
+REGISTER_GAMETYPE(_("Last Man Standing"),lms,g_lms,LMS,"timelimit=20 lives=9 leadlimit=0",_("Survive and kill until the enemies have no lives left"));
#define g_lms IS_GAMETYPE(LMS)
-REGISTER_GAMETYPE(_("Race"),rc,g_race,RACE,"timelimit=20 qualifying_timelimit=5 laplimit=7 teamlaplimit=15 leadlimit=0");
+REGISTER_GAMETYPE(_("Race"),rc,g_race,RACE,"timelimit=20 qualifying_timelimit=5 laplimit=7 teamlaplimit=15 leadlimit=0",_("Race against other players to the finish line"));
#define g_race IS_GAMETYPE(RACE)
-REGISTER_GAMETYPE(_("Race CTS"),cts,g_cts,CTS,"timelimit=20 skill=-1");
+REGISTER_GAMETYPE(_("Race CTS"),cts,g_cts,CTS,"timelimit=20 skill=-1",_("Race for fastest time"));
#define g_cts IS_GAMETYPE(CTS)
-REGISTER_GAMETYPE(_("Team Deathmatch"),tdm,g_tdm,TEAM_DEATHMATCH,"timelimit=20 pointlimit=50 teams=2 leadlimit=0");
+REGISTER_GAMETYPE(_("Team Deathmatch"),tdm,g_tdm,TEAM_DEATHMATCH,"timelimit=20 pointlimit=50 teams=2 leadlimit=0",_("Kill all enemy teammates"));
#define g_tdm IS_GAMETYPE(TEAM_DEATHMATCH)
-REGISTER_GAMETYPE(_("Capture the Flag"),ctf,g_ctf,CTF,"timelimit=20 caplimit=10 leadlimit=0");
+REGISTER_GAMETYPE(_("Capture the Flag"),ctf,g_ctf,CTF,"timelimit=20 caplimit=10 leadlimit=0",_("Find and bring the enemy flag to your base to capture it"));
#define g_ctf IS_GAMETYPE(CTF)
-REGISTER_GAMETYPE(_("Clan Arena"),ca,g_ca,CA,"timelimit=20 pointlimit=10 leadlimit=0");
+REGISTER_GAMETYPE(_("Clan Arena"),ca,g_ca,CA,"timelimit=20 pointlimit=10 leadlimit=0",_("Kill all enemy teammates to win the round"));
#define g_ca IS_GAMETYPE(CA)
-REGISTER_GAMETYPE(_("Domination"),dom,g_domination,DOMINATION,"timelimit=20 pointlimit=200 teams=2 leadlimit=0");
+REGISTER_GAMETYPE(_("Domination"),dom,g_domination,DOMINATION,"timelimit=20 pointlimit=200 teams=2 leadlimit=0",_("Capture all the control points to win"));
#define g_domination IS_GAMETYPE(DOMINATION)
-REGISTER_GAMETYPE(_("Key Hunt"),kh,g_keyhunt,KEYHUNT,"timelimit=20 pointlimit=1000 teams=3 leadlimit=0");
+REGISTER_GAMETYPE(_("Key Hunt"),kh,g_keyhunt,KEYHUNT,"timelimit=20 pointlimit=1000 teams=3 leadlimit=0",_("Gather all the keys to win the round"));
#define g_keyhunt IS_GAMETYPE(KEYHUNT)
-REGISTER_GAMETYPE(_("Assault"),as,g_assault,ASSAULT,"timelimit=20");
+REGISTER_GAMETYPE(_("Assault"),as,g_assault,ASSAULT,"timelimit=20",_("Destroy obstacles to find and destroy the enemy power core before time runs out"));
#define g_assault IS_GAMETYPE(ASSAULT)
-REGISTER_GAMETYPE(_("Onslaught"),ons,g_onslaught,ONSLAUGHT,"timelimit=20");
+REGISTER_GAMETYPE(_("Onslaught"),ons,g_onslaught,ONSLAUGHT,"timelimit=20",_("Capture control points to reach and destroy the enemy generator"));
#define g_onslaught IS_GAMETYPE(ONSLAUGHT)
-REGISTER_GAMETYPE(_("Nexball"),nb,g_nexball,NEXBALL,"timelimit=20 pointlimit=5 leadlimit=0");
+REGISTER_GAMETYPE(_("Nexball"),nb,g_nexball,NEXBALL,"timelimit=20 pointlimit=5 leadlimit=0",_("XonSports"));
#define g_nexball IS_GAMETYPE(NEXBALL)
-REGISTER_GAMETYPE(_("Freeze Tag"),ft,g_freezetag,FREEZETAG,"timelimit=20 pointlimit=10 teams=2 leadlimit=0");
+REGISTER_GAMETYPE(_("Freeze Tag"),ft,g_freezetag,FREEZETAG,"timelimit=20 pointlimit=10 teams=2 leadlimit=0",_("Kill enemies to freeze them, stand next to teammates to revive them"));
#define g_freezetag IS_GAMETYPE(FREEZETAG)
-REGISTER_GAMETYPE(_("Keepaway"),ka,g_keepaway,KEEPAWAY,"timelimit=20 pointlimit=30");
+REGISTER_GAMETYPE(_("Keepaway"),ka,g_keepaway,KEEPAWAY,"timelimit=20 pointlimit=30",_("Hold the ball to get points for kills"));
#define g_keepaway IS_GAMETYPE(KEEPAWAY)
-REGISTER_GAMETYPE(_("Invasion"),inv,g_invasion,INVASION,"pointlimit=5");
+REGISTER_GAMETYPE(_("Invasion"),inv,g_invasion,INVASION,"pointlimit=5",_("Survive against waves of monsters"));
#define g_invasion IS_GAMETYPE(INVASION)
const float MAPINFO_FEATURE_WEAPONS = 1; // not defined for minstagib-only maps
void MapInfo_LoadMap(string s, float reinit);
// list all maps for the current game type
-string MapInfo_ListAllowedMaps(float pFlagsRequired, float pFlagsForbidden);
+string MapInfo_ListAllowedMaps(float type, float pFlagsRequired, float pFlagsForbidden);
// list all allowed maps (for any game type)
string MapInfo_ListAllAllowedMaps(float pFlagsRequired, float pFlagsForbidden);
// gets a gametype from a string
string _MapInfo_GetDefaultEx(float t);
float MapInfo_Type_FromString(string t);
+string MapInfo_Type_Description(float t);
string MapInfo_Type_ToString(float t);
string MapInfo_Type_ToText(float t);
void MapInfo_SwitchGameType(float t);
float autocvar_sv_vote_call;
float autocvar_sv_vote_change;
string autocvar_sv_vote_commands;
+float autocvar_sv_vote_gametype;
+float autocvar_sv_vote_gametype_timeout;
+string autocvar_sv_vote_gametype_options;
+float autocvar_sv_vote_gametype_keeptwotime;
+float autocvar_sv_vote_gametype_default_current;
float autocvar_sv_vote_limit;
float autocvar_sv_vote_majority_factor;
float autocvar_sv_vote_majority_factor_of_voted;
string redirection_target;
float world_initialized;
-string GetMapname();
string GetGametype();
void GotoNextMap(float reinit);
void ShuffleMaplist();
return MapInfo_Type_ToString(MapInfo_LoadedGametype);
}
-string getmapname_stored;
string GetMapname()
{
return mapname;
return TRUE;
}
if(autocvar_nextmap != "")
- if(MapInfo_CheckMap(autocvar_nextmap))
+ {
+ string m;
+ m = GameTypeVote_MapInfo_FixName(autocvar_nextmap);
+ cvar_set("nextmap",m);
+
+ if(!m || gametypevote)
+ return FALSE;
+ if(autocvar_sv_vote_gametype)
{
- Map_Goto_SetStr(autocvar_nextmap);
+ Map_Goto_SetStr(m);
+ return FALSE;
+ }
+
+ if(MapInfo_CheckMap(m))
+ {
+ Map_Goto_SetStr(m);
Map_Goto(reinit);
alreadychangedlevel = TRUE;
return TRUE;
}
+ }
if(!reinit && autocvar_lastlevel)
{
cvar_settemp_restore();
============
*/
.float autoscreenshot;
-void() MapVote_Start;
-void() MapVote_Think;
-float mapvote_initialized;
void IntermissionThink()
{
FixIntermissionClient(self);
SetDefaultAlpha();
- /*
- MapVote_Think should now do that part
- if (intermission_running)
- if (time >= intermission_exittime + 60)
- {
- if(!DoNextMapOverride())
- GotoNextMap();
- return;
- }
- */
-
if (gameover) // someone else quit the game already
{
if(player_count == 0) // Nobody there? Then let's go to the next map
}
}
-float mapvote_nextthink;
-float mapvote_initialized;
-float mapvote_keeptwotime;
-float mapvote_timeout;
-string mapvote_message;
-#define MAPVOTE_SCREENSHOT_DIRS_COUNT 4
-string mapvote_screenshot_dirs[MAPVOTE_SCREENSHOT_DIRS_COUNT];
-float mapvote_screenshot_dirs_count;
-
-float mapvote_count;
-float mapvote_count_real;
-string mapvote_maps[MAPVOTE_COUNT];
-float mapvote_maps_screenshot_dir[MAPVOTE_COUNT];
-string mapvote_maps_pakfile[MAPVOTE_COUNT];
-float mapvote_maps_suggested[MAPVOTE_COUNT];
-string mapvote_suggestions[MAPVOTE_COUNT];
-float mapvote_suggestion_ptr;
-float mapvote_voters;
-float mapvote_selections[MAPVOTE_COUNT];
-float mapvote_run;
-float mapvote_detail;
-float mapvote_abstain;
-.float mapvote;
-
-void MapVote_ClearAllVotes()
-{
- FOR_EACH_CLIENT(other)
- other.mapvote = 0;
-}
-
-string MapVote_Suggest(string m)
+string GotoMap(string m)
{
- float i;
- if(m == "")
- return "That's not how to use this command.";
- if(!autocvar_g_maplist_votable_suggestions)
- return "Suggestions are not accepted on this server.";
- if(mapvote_initialized)
- return "Can't suggest - voting is already in progress!";
- m = MapInfo_FixName(m);
+ m = GameTypeVote_MapInfo_FixName(m);
if (!m)
return "The map you suggested is not available on this server.";
- if(!autocvar_g_maplist_votable_suggestions_override_mostrecent)
- if(Map_IsRecent(m))
- return "This server does not allow for recent maps to be played again. Please be patient for some rounds.";
-
+ if (!autocvar_sv_vote_gametype)
if(!MapInfo_CheckMap(m))
return "The map you suggested does not support the current game mode.";
- for(i = 0; i < mapvote_suggestion_ptr; ++i)
- if(mapvote_suggestions[i] == m)
- return "This map was already suggested.";
- if(mapvote_suggestion_ptr >= MAPVOTE_COUNT)
- {
- i = floor(random() * mapvote_suggestion_ptr);
- }
- else
- {
- i = mapvote_suggestion_ptr;
- mapvote_suggestion_ptr += 1;
- }
- if(mapvote_suggestions[i] != "")
- strunzone(mapvote_suggestions[i]);
- mapvote_suggestions[i] = strzone(m);
- if(autocvar_sv_eventlog)
- GameLogEcho(strcat(":vote:suggested:", m, ":", ftos(self.playerid)));
- return strcat("Suggestion of ", m, " accepted.");
-}
-
-void MapVote_AddVotable(string nextMap, float isSuggestion)
-{
- float j, i, o;
- string pakfile, mapfile;
-
- if(nextMap == "")
- return;
- for(j = 0; j < mapvote_count; ++j)
- if(mapvote_maps[j] == nextMap)
- return;
- // suggestions might be no longer valid/allowed after gametype switch!
- if(isSuggestion)
- if(!MapInfo_CheckMap(nextMap))
- return;
- mapvote_maps[mapvote_count] = strzone(nextMap);
- mapvote_maps_suggested[mapvote_count] = isSuggestion;
-
- pakfile = string_null;
- for(i = 0; i < mapvote_screenshot_dirs_count; ++i)
- {
- mapfile = strcat(mapvote_screenshot_dirs[i], "/", mapvote_maps[i]);
- pakfile = whichpack(strcat(mapfile, ".tga"));
- if(pakfile == "")
- pakfile = whichpack(strcat(mapfile, ".jpg"));
- if(pakfile == "")
- pakfile = whichpack(strcat(mapfile, ".png"));
- if(pakfile != "")
- break;
- }
- if(i >= mapvote_screenshot_dirs_count)
- i = 0; // FIXME maybe network this error case, as that means there is no mapshot on the server?
- for(o = strstr(pakfile, "/", 0)+1; o > 0; o = strstr(pakfile, "/", 0)+1)
- pakfile = substring(pakfile, o, -1);
-
- mapvote_maps_screenshot_dir[mapvote_count] = i;
- mapvote_maps_pakfile[mapvote_count] = strzone(pakfile);
-
- mapvote_count += 1;
-}
-
-void MapVote_Spawn();
-void MapVote_Init()
-{
- float i;
- float nmax, smax;
-
- MapVote_ClearAllVotes();
-
- mapvote_count = 0;
- mapvote_detail = !autocvar_g_maplist_votable_nodetail;
- mapvote_abstain = autocvar_g_maplist_votable_abstain;
-
- if(mapvote_abstain)
- nmax = min(MAPVOTE_COUNT - 1, autocvar_g_maplist_votable);
- else
- nmax = min(MAPVOTE_COUNT, autocvar_g_maplist_votable);
- smax = min3(nmax, autocvar_g_maplist_votable_suggestions, mapvote_suggestion_ptr);
-
- // we need this for AddVotable, as that cycles through the screenshot dirs
- mapvote_screenshot_dirs_count = tokenize_console(autocvar_g_maplist_votable_screenshot_dir);
- if(mapvote_screenshot_dirs_count == 0)
- mapvote_screenshot_dirs_count = tokenize_console("maps levelshots");
- mapvote_screenshot_dirs_count = min(mapvote_screenshot_dirs_count, MAPVOTE_SCREENSHOT_DIRS_COUNT);
- for(i = 0; i < mapvote_screenshot_dirs_count; ++i)
- mapvote_screenshot_dirs[i] = strzone(argv(i));
-
- if(mapvote_suggestion_ptr)
- for(i = 0; i < 100 && mapvote_count < smax; ++i)
- MapVote_AddVotable(mapvote_suggestions[floor(random() * mapvote_suggestion_ptr)], TRUE);
-
- for(i = 0; i < 100 && mapvote_count < nmax; ++i)
- MapVote_AddVotable(GetNextMap(), FALSE);
-
- if(mapvote_count == 0)
- {
- bprint( "Maplist contains no single playable map! Resetting it to default map list.\n" );
- cvar_set("g_maplist", MapInfo_ListAllAllowedMaps(MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags()));
- if(autocvar_g_maplist_shuffle)
- ShuffleMaplist();
- localcmd("\nmenu_cmd sync\n");
- for(i = 0; i < 100 && mapvote_count < nmax; ++i)
- MapVote_AddVotable(GetNextMap(), FALSE);
- }
-
- mapvote_count_real = mapvote_count;
- if(mapvote_abstain)
- MapVote_AddVotable("don't care", 0);
-
- //dprint("mapvote count is ", ftos(mapvote_count), "\n");
-
- mapvote_keeptwotime = time + autocvar_g_maplist_votable_keeptwotime;
- mapvote_timeout = time + autocvar_g_maplist_votable_timeout;
- if(mapvote_count_real < 3 || mapvote_keeptwotime <= time)
- mapvote_keeptwotime = 0;
- mapvote_message = "Choose a map and press its key!";
-
- MapVote_Spawn();
-}
-
-void MapVote_SendPicture(float id)
-{
- msg_entity = self;
- WriteByte(MSG_ONE, SVC_TEMPENTITY);
- WriteByte(MSG_ONE, TE_CSQC_PICTURE);
- WriteByte(MSG_ONE, id);
- WritePicture(MSG_ONE, strcat(mapvote_screenshot_dirs[mapvote_maps_screenshot_dir[id]], "/", mapvote_maps[id]), 3072);
-}
-
-float MapVote_GetMapMask()
-{
- float mask, i, power;
- mask = 0;
- for(i = 0, power = 1; i < mapvote_count; ++i, power *= 2)
- if(mapvote_maps[i] != "")
- mask |= power;
- return mask;
-}
-
-entity mapvote_ent;
-float MapVote_SendEntity(entity to, float sf)
-{
- float i;
-
- if(sf & 1)
- sf &= ~2; // if we send 1, we don't need to also send 2
-
- WriteByte(MSG_ENTITY, ENT_CLIENT_MAPVOTE);
- WriteByte(MSG_ENTITY, sf);
-
- if(sf & 1)
- {
- // flag 1 == initialization
- for(i = 0; i < mapvote_screenshot_dirs_count; ++i)
- WriteString(MSG_ENTITY, mapvote_screenshot_dirs[i]);
- WriteString(MSG_ENTITY, "");
- WriteByte(MSG_ENTITY, mapvote_count);
- WriteByte(MSG_ENTITY, mapvote_abstain);
- WriteByte(MSG_ENTITY, mapvote_detail);
- WriteCoord(MSG_ENTITY, mapvote_timeout);
- if(mapvote_count <= 8)
- WriteByte(MSG_ENTITY, MapVote_GetMapMask());
- else
- WriteShort(MSG_ENTITY, MapVote_GetMapMask());
- for(i = 0; i < mapvote_count; ++i)
- if(mapvote_maps[i] != "")
- {
- if(mapvote_abstain && i == mapvote_count - 1)
- {
- WriteString(MSG_ENTITY, ""); // abstain needs no text
- WriteString(MSG_ENTITY, ""); // abstain needs no pack
- WriteByte(MSG_ENTITY, 0); // abstain needs no screenshot dir
- }
- else
- {
- WriteString(MSG_ENTITY, mapvote_maps[i]);
- WriteString(MSG_ENTITY, mapvote_maps_pakfile[i]);
- WriteByte(MSG_ENTITY, mapvote_maps_screenshot_dir[i]);
- }
- }
- }
-
- if(sf & 2)
- {
- // flag 2 == update of mask
- if(mapvote_count <= 8)
- WriteByte(MSG_ENTITY, MapVote_GetMapMask());
- else
- WriteShort(MSG_ENTITY, MapVote_GetMapMask());
- }
-
- if(sf & 4)
- {
- if(mapvote_detail)
- for(i = 0; i < mapvote_count; ++i)
- if(mapvote_maps[i] != "")
- WriteByte(MSG_ENTITY, mapvote_selections[i]);
-
- WriteByte(MSG_ENTITY, to.mapvote);
- }
-
- return TRUE;
-}
-
-void MapVote_Spawn()
-{
- Net_LinkEntity(mapvote_ent = spawn(), FALSE, 0, MapVote_SendEntity);
-}
-
-void MapVote_TouchMask()
-{
- mapvote_ent.SendFlags |= 2;
-}
-
-void MapVote_TouchVotes(entity voter)
-{
- mapvote_ent.SendFlags |= 4;
-}
-
-float MapVote_Finished(float mappos)
-{
- string result;
- float i;
- float didntvote;
-
- if(autocvar_sv_eventlog)
- {
- result = strcat(":vote:finished:", mapvote_maps[mappos]);
- result = strcat(result, ":", ftos(mapvote_selections[mappos]), "::");
- didntvote = mapvote_voters;
- for(i = 0; i < mapvote_count; ++i)
- if(mapvote_maps[i] != "")
- {
- didntvote -= mapvote_selections[i];
- if(i != mappos)
- {
- result = strcat(result, ":", mapvote_maps[i]);
- result = strcat(result, ":", ftos(mapvote_selections[i]));
- }
- }
- result = strcat(result, ":didn't vote:", ftos(didntvote));
-
- GameLogEcho(result);
- if(mapvote_maps_suggested[mappos])
- GameLogEcho(strcat(":vote:suggestion_accepted:", mapvote_maps[mappos]));
- }
-
- FOR_EACH_REALCLIENT(other)
- FixClientCvars(other);
-
- Map_Goto_SetStr(mapvote_maps[mappos]);
- Map_Goto(0);
- alreadychangedlevel = TRUE;
- return TRUE;
-}
-void MapVote_CheckRules_1()
-{
- float i;
-
- for(i = 0; i < mapvote_count; ++i) if(mapvote_maps[i] != "")
- {
- //dprint("Map ", ftos(i), ": "); dprint(mapvote_maps[i], "\n");
- mapvote_selections[i] = 0;
- }
-
- mapvote_voters = 0;
- FOR_EACH_REALCLIENT(other)
- {
- ++mapvote_voters;
- if(other.mapvote)
- {
- i = other.mapvote - 1;
- //dprint("Player ", other.netname, " vote = ", ftos(other.mapvote - 1), "\n");
- mapvote_selections[i] = mapvote_selections[i] + 1;
- }
- }
-}
-
-float MapVote_CheckRules_2()
-{
- float i;
- float firstPlace, secondPlace;
- float firstPlaceVotes, secondPlaceVotes;
- float mapvote_voters_real;
- string result;
-
- if(mapvote_count_real == 1)
- return MapVote_Finished(0);
-
- mapvote_voters_real = mapvote_voters;
- if(mapvote_abstain)
- mapvote_voters_real -= mapvote_selections[mapvote_count - 1];
-
- RandomSelection_Init();
- for(i = 0; i < mapvote_count_real; ++i) if(mapvote_maps[i] != "")
- RandomSelection_Add(world, i, string_null, 1, mapvote_selections[i]);
- firstPlace = RandomSelection_chosen_float;
- firstPlaceVotes = RandomSelection_best_priority;
- //dprint("First place: ", ftos(firstPlace), "\n");
- //dprint("First place votes: ", ftos(firstPlaceVotes), "\n");
-
- RandomSelection_Init();
- for(i = 0; i < mapvote_count_real; ++i) if(mapvote_maps[i] != "")
- if(i != firstPlace)
- RandomSelection_Add(world, i, string_null, 1, mapvote_selections[i]);
- secondPlace = RandomSelection_chosen_float;
- secondPlaceVotes = RandomSelection_best_priority;
- //dprint("Second place: ", ftos(secondPlace), "\n");
- //dprint("Second place votes: ", ftos(secondPlaceVotes), "\n");
-
- if(firstPlace == -1)
- error("No first place in map vote... WTF?");
-
- if(secondPlace == -1 || time > mapvote_timeout || (mapvote_voters_real - firstPlaceVotes) < firstPlaceVotes)
- return MapVote_Finished(firstPlace);
-
- if(mapvote_keeptwotime)
- if(time > mapvote_keeptwotime || (mapvote_voters_real - firstPlaceVotes - secondPlaceVotes) < secondPlaceVotes)
- {
- float didntvote;
- MapVote_TouchMask();
- mapvote_message = "Now decide between the TOP TWO!";
- mapvote_keeptwotime = 0;
- result = strcat(":vote:keeptwo:", mapvote_maps[firstPlace]);
- result = strcat(result, ":", ftos(firstPlaceVotes));
- result = strcat(result, ":", mapvote_maps[secondPlace]);
- result = strcat(result, ":", ftos(secondPlaceVotes), "::");
- didntvote = mapvote_voters;
- for(i = 0; i < mapvote_count; ++i)
- if(mapvote_maps[i] != "")
- {
- didntvote -= mapvote_selections[i];
- if(i != firstPlace)
- if(i != secondPlace)
- {
- result = strcat(result, ":", mapvote_maps[i]);
- result = strcat(result, ":", ftos(mapvote_selections[i]));
- if(i < mapvote_count_real)
- {
- strunzone(mapvote_maps[i]);
- mapvote_maps[i] = "";
- strunzone(mapvote_maps_pakfile[i]);
- mapvote_maps_pakfile[i] = "";
- }
- }
- }
- result = strcat(result, ":didn't vote:", ftos(didntvote));
- if(autocvar_sv_eventlog)
- GameLogEcho(result);
- }
-
- return FALSE;
-}
-void MapVote_Tick()
-{
- float keeptwo;
- float totalvotes;
-
- keeptwo = mapvote_keeptwotime;
- MapVote_CheckRules_1(); // count
- if(MapVote_CheckRules_2()) // decide
- return;
-
- totalvotes = 0;
- FOR_EACH_REALCLIENT(other)
- {
- // hide scoreboard again
- if(other.health != 2342)
- {
- other.health = 2342;
- other.impulse = 0;
- if(IS_REAL_CLIENT(other))
- {
- msg_entity = other;
- WriteByte(MSG_ONE, SVC_FINALE);
- WriteString(MSG_ONE, "");
- }
- }
-
- // clear possibly invalid votes
- if(mapvote_maps[other.mapvote - 1] == "")
- other.mapvote = 0;
- // use impulses as new vote
- if(other.impulse >= 1 && other.impulse <= mapvote_count)
- if(mapvote_maps[other.impulse - 1] != "")
- {
- other.mapvote = other.impulse;
- MapVote_TouchVotes(other);
- }
- other.impulse = 0;
-
- if(other.mapvote)
- ++totalvotes;
- }
-
- MapVote_CheckRules_1(); // just count
-}
-void MapVote_Start()
-{
- if(mapvote_run)
- return;
-
- // wait for stats to be sent first
- if(!playerstats_waitforme)
- return;
-
- MapInfo_Enumerate();
- if(MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 1))
- mapvote_run = TRUE;
-}
-void MapVote_Think()
-{
- if(!mapvote_run)
- return;
-
- if(alreadychangedlevel)
- return;
-
- if(time < mapvote_nextthink)
- return;
- //dprint("tick\n");
-
- mapvote_nextthink = time + 0.5;
-
- if(!mapvote_initialized)
- {
- if(autocvar_rescan_pending == 1)
- {
- cvar_set("rescan_pending", "2");
- localcmd("fs_rescan\nrescan_pending 3\n");
- return;
- }
- else if(autocvar_rescan_pending == 2)
- {
- return;
- }
- else if(autocvar_rescan_pending == 3)
- {
- // now build missing mapinfo files
- if(!MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 1))
- return;
-
- // we're done, start the timer
- cvar_set("rescan_pending", "0");
- }
-
- mapvote_initialized = TRUE;
- if(DoNextMapOverride(0))
- return;
- if(!autocvar_g_maplist_votable || player_count <= 0)
- {
- GotoNextMap(0);
- return;
- }
- MapVote_Init();
- }
-
- MapVote_Tick();
-}
-
-string GotoMap(string m)
-{
- if(!MapInfo_CheckMap(m))
- return "The map you chose is not available on this server.";
cvar_set("nextmap", m);
cvar_set("timelimit", "-1");
if(mapvote_initialized || alreadychangedlevel)
--- /dev/null
+float GameTypeVote_AvailabilityStatus(string gtname)
+{
+ float type = MapInfo_Type_FromString(gtname);
+ if( type == 0 )
+ return GTV_FORBIDDEN;
+
+ if ( autocvar_nextmap != "" )
+ if ( !MapInfo_Get_ByName(autocvar_nextmap, FALSE, type) )
+ return GTV_FORBIDDEN;
+
+ return GTV_AVAILABLE;
+}
+
+float GameTypeVote_GetMask()
+{
+ float n, j, gametype_mask;
+ n = tokenizebyseparator(autocvar_sv_vote_gametype_options, " ");
+ n = min(MAPVOTE_COUNT, n);
+ gametype_mask = 0;
+ for(j = 0; j < n; ++j)
+ gametype_mask |= MapInfo_Type_FromString(argv(j));
+ return gametype_mask;
+}
+
+string GameTypeVote_MapInfo_FixName(string m)
+{
+ if ( autocvar_sv_vote_gametype )
+ {
+ MapInfo_Enumerate();
+ MapInfo_FilterGametype(GameTypeVote_GetMask(), 0, MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
+ }
+ return MapInfo_FixName(m);
+}
+
+void MapVote_ClearAllVotes()
+{
+ FOR_EACH_CLIENT(other)
+ other.mapvote = 0;
+}
+
+void MapVote_UnzoneStrings()
+{
+ float j;
+ for(j = 0; j < mapvote_count; ++j)
+ {
+ if ( mapvote_maps[j] )
+ {
+ strunzone(mapvote_maps[j]);
+ mapvote_maps[j] = string_null;
+ }
+ if ( mapvote_maps_pakfile[j] )
+ {
+ strunzone(mapvote_maps_pakfile[j]);
+ mapvote_maps_pakfile[j] = string_null;
+ }
+ }
+}
+
+string MapVote_Suggest(string m)
+{
+ float i;
+ if(m == "")
+ return "That's not how to use this command.";
+ if(!autocvar_g_maplist_votable_suggestions)
+ return "Suggestions are not accepted on this server.";
+ if(mapvote_initialized)
+ if(!gametypevote)
+ return "Can't suggest - voting is already in progress!";
+ m = GameTypeVote_MapInfo_FixName(m);
+ if (!m)
+ return "The map you suggested is not available on this server.";
+ if(!autocvar_g_maplist_votable_suggestions_override_mostrecent)
+ if(Map_IsRecent(m))
+ return "This server does not allow for recent maps to be played again. Please be patient for some rounds.";
+
+ if (!autocvar_sv_vote_gametype)
+ if(!MapInfo_CheckMap(m))
+ return "The map you suggested does not support the current game mode.";
+ for(i = 0; i < mapvote_suggestion_ptr; ++i)
+ if(mapvote_suggestions[i] == m)
+ return "This map was already suggested.";
+ if(mapvote_suggestion_ptr >= MAPVOTE_COUNT)
+ {
+ i = floor(random() * mapvote_suggestion_ptr);
+ }
+ else
+ {
+ i = mapvote_suggestion_ptr;
+ mapvote_suggestion_ptr += 1;
+ }
+ if(mapvote_suggestions[i] != "")
+ strunzone(mapvote_suggestions[i]);
+ mapvote_suggestions[i] = strzone(m);
+ if(autocvar_sv_eventlog)
+ GameLogEcho(strcat(":vote:suggested:", m, ":", ftos(self.playerid)));
+ return strcat("Suggestion of ", m, " accepted.");
+}
+
+void MapVote_AddVotable(string nextMap, float isSuggestion)
+{
+ float j, i, o;
+ string pakfile, mapfile;
+
+ if(nextMap == "")
+ return;
+ for(j = 0; j < mapvote_count; ++j)
+ if(mapvote_maps[j] == nextMap)
+ return;
+ // suggestions might be no longer valid/allowed after gametype switch!
+ if(isSuggestion)
+ if(!MapInfo_CheckMap(nextMap))
+ return;
+ mapvote_maps[mapvote_count] = strzone(nextMap);
+ mapvote_maps_suggested[mapvote_count] = isSuggestion;
+
+ pakfile = string_null;
+ for(i = 0; i < mapvote_screenshot_dirs_count; ++i)
+ {
+ mapfile = strcat(mapvote_screenshot_dirs[i], "/", mapvote_maps[i]);
+ pakfile = whichpack(strcat(mapfile, ".tga"));
+ if(pakfile == "")
+ pakfile = whichpack(strcat(mapfile, ".jpg"));
+ if(pakfile == "")
+ pakfile = whichpack(strcat(mapfile, ".png"));
+ if(pakfile != "")
+ break;
+ }
+ if(i >= mapvote_screenshot_dirs_count)
+ i = 0; // FIXME maybe network this error case, as that means there is no mapshot on the server?
+ for(o = strstr(pakfile, "/", 0)+1; o > 0; o = strstr(pakfile, "/", 0)+1)
+ pakfile = substring(pakfile, o, -1);
+
+ mapvote_maps_screenshot_dir[mapvote_count] = i;
+ mapvote_maps_pakfile[mapvote_count] = strzone(pakfile);
+ mapvote_maps_availability[mapvote_count] = GTV_AVAILABLE;
+
+ mapvote_count += 1;
+}
+
+void MapVote_Init()
+{
+ float i;
+ float nmax, smax;
+
+ MapVote_ClearAllVotes();
+ MapVote_UnzoneStrings();
+
+ mapvote_count = 0;
+ mapvote_detail = !autocvar_g_maplist_votable_nodetail;
+ mapvote_abstain = autocvar_g_maplist_votable_abstain;
+
+ if(mapvote_abstain)
+ nmax = min(MAPVOTE_COUNT - 1, autocvar_g_maplist_votable);
+ else
+ nmax = min(MAPVOTE_COUNT, autocvar_g_maplist_votable);
+ smax = min3(nmax, autocvar_g_maplist_votable_suggestions, mapvote_suggestion_ptr);
+
+ // we need this for AddVotable, as that cycles through the screenshot dirs
+ mapvote_screenshot_dirs_count = tokenize_console(autocvar_g_maplist_votable_screenshot_dir);
+ if(mapvote_screenshot_dirs_count == 0)
+ mapvote_screenshot_dirs_count = tokenize_console("maps levelshots");
+ mapvote_screenshot_dirs_count = min(mapvote_screenshot_dirs_count, MAPVOTE_SCREENSHOT_DIRS_COUNT);
+ for(i = 0; i < mapvote_screenshot_dirs_count; ++i)
+ mapvote_screenshot_dirs[i] = strzone(argv(i));
+
+ if(mapvote_suggestion_ptr)
+ for(i = 0; i < 100 && mapvote_count < smax; ++i)
+ MapVote_AddVotable(mapvote_suggestions[floor(random() * mapvote_suggestion_ptr)], TRUE);
+
+ for(i = 0; i < 100 && mapvote_count < nmax; ++i)
+ MapVote_AddVotable(GetNextMap(), FALSE);
+
+ if(mapvote_count == 0)
+ {
+ bprint( "Maplist contains no single playable map! Resetting it to default map list.\n" );
+ cvar_set("g_maplist", MapInfo_ListAllowedMaps(MapInfo_CurrentGametype(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags()));
+ if(autocvar_g_maplist_shuffle)
+ ShuffleMaplist();
+ localcmd("\nmenu_cmd sync\n");
+ for(i = 0; i < 100 && mapvote_count < nmax; ++i)
+ MapVote_AddVotable(GetNextMap(), FALSE);
+ }
+
+ mapvote_count_real = mapvote_count;
+ if(mapvote_abstain)
+ MapVote_AddVotable("don't care", 0);
+
+ //dprint("mapvote count is ", ftos(mapvote_count), "\n");
+
+ mapvote_keeptwotime = time + autocvar_g_maplist_votable_keeptwotime;
+ mapvote_timeout = time + autocvar_g_maplist_votable_timeout;
+ if(mapvote_count_real < 3 || mapvote_keeptwotime <= time)
+ mapvote_keeptwotime = 0;
+ mapvote_message = "Choose a map and press its key!";
+
+ MapVote_Spawn();
+}
+
+void MapVote_SendPicture(float id)
+{
+ msg_entity = self;
+ WriteByte(MSG_ONE, SVC_TEMPENTITY);
+ WriteByte(MSG_ONE, TE_CSQC_PICTURE);
+ WriteByte(MSG_ONE, id);
+ WritePicture(MSG_ONE, strcat(mapvote_screenshot_dirs[mapvote_maps_screenshot_dir[id]], "/", mapvote_maps[id]), 3072);
+}
+
+
+void MapVote_WriteMask()
+{
+ float i;
+ if ( mapvote_count < 24 )
+ {
+ float mask,power;
+ mask = 0;
+ for(i = 0, power = 1; i < mapvote_count; ++i, power *= 2)
+ if(mapvote_maps_availability[i] == GTV_AVAILABLE )
+ mask |= power;
+
+ if(mapvote_count < 8)
+ WriteByte(MSG_ENTITY, mask);
+ else if (mapvote_count < 16)
+ WriteShort(MSG_ENTITY,mask);
+ else
+ WriteLong(MSG_ENTITY, mask);
+ }
+ else
+ {
+ for ( i = 0; i < mapvote_count; ++i )
+ WriteByte(MSG_ENTITY, mapvote_maps_availability[i]);
+ }
+}
+
+float MapVote_SendEntity(entity to, float sf)
+{
+ float i;
+
+ if(sf & 1)
+ sf &= ~2; // if we send 1, we don't need to also send 2
+
+ WriteByte(MSG_ENTITY, ENT_CLIENT_MAPVOTE);
+ WriteByte(MSG_ENTITY, sf);
+
+ if(sf & 1)
+ {
+ // flag 1 == initialization
+ for(i = 0; i < mapvote_screenshot_dirs_count; ++i)
+ WriteString(MSG_ENTITY, mapvote_screenshot_dirs[i]);
+ WriteString(MSG_ENTITY, "");
+ WriteByte(MSG_ENTITY, mapvote_count);
+ WriteByte(MSG_ENTITY, mapvote_abstain);
+ WriteByte(MSG_ENTITY, mapvote_detail);
+ WriteCoord(MSG_ENTITY, mapvote_timeout);
+
+ WriteByte(MSG_ENTITY, gametypevote);
+
+ if(gametypevote)
+ WriteString(MSG_ENTITY, autocvar_nextmap);
+
+ MapVote_WriteMask();
+
+ for(i = 0; i < mapvote_count; ++i)
+ {
+ if(mapvote_abstain && i == mapvote_count - 1)
+ {
+ WriteString(MSG_ENTITY, ""); // abstain needs no text
+ WriteString(MSG_ENTITY, ""); // abstain needs no pack
+ WriteByte(MSG_ENTITY, 0); // abstain needs no screenshot dir
+ WriteByte(MSG_ENTITY, GTV_AVAILABLE);
+ }
+ else
+ {
+ WriteString(MSG_ENTITY, mapvote_maps[i]);
+ WriteString(MSG_ENTITY, mapvote_maps_pakfile[i]);
+ WriteByte(MSG_ENTITY, mapvote_maps_screenshot_dir[i]);
+ WriteByte(MSG_ENTITY, mapvote_maps_availability[i]);
+ }
+ }
+ }
+
+ if(sf & 2)
+ {
+ // flag 2 == update of mask
+ MapVote_WriteMask();
+ }
+
+ if(sf & 4)
+ {
+ if(mapvote_detail)
+ for(i = 0; i < mapvote_count; ++i)
+ if ( mapvote_maps_availability[i] == GTV_AVAILABLE )
+ WriteByte(MSG_ENTITY, mapvote_selections[i]);
+
+ WriteByte(MSG_ENTITY, to.mapvote);
+ }
+
+ return TRUE;
+}
+
+void MapVote_Spawn()
+{
+ Net_LinkEntity(mapvote_ent = spawn(), FALSE, 0, MapVote_SendEntity);
+}
+
+void MapVote_TouchMask()
+{
+ mapvote_ent.SendFlags |= 2;
+}
+
+void MapVote_TouchVotes(entity voter)
+{
+ mapvote_ent.SendFlags |= 4;
+}
+
+float MapVote_Finished(float mappos)
+{
+ if(alreadychangedlevel)
+ return FALSE;
+
+ string result;
+ float i;
+ float didntvote;
+
+ if(autocvar_sv_eventlog)
+ {
+ result = strcat(":vote:finished:", mapvote_maps[mappos]);
+ result = strcat(result, ":", ftos(mapvote_selections[mappos]), "::");
+ didntvote = mapvote_voters;
+ for(i = 0; i < mapvote_count; ++i)
+ if(mapvote_maps_availability[i] == GTV_AVAILABLE )
+ {
+ didntvote -= mapvote_selections[i];
+ if(i != mappos)
+ {
+ result = strcat(result, ":", mapvote_maps[i]);
+ result = strcat(result, ":", ftos(mapvote_selections[i]));
+ }
+ }
+ result = strcat(result, ":didn't vote:", ftos(didntvote));
+
+ GameLogEcho(result);
+ if(mapvote_maps_suggested[mappos])
+ GameLogEcho(strcat(":vote:suggestion_accepted:", mapvote_maps[mappos]));
+ }
+
+ FOR_EACH_REALCLIENT(other)
+ FixClientCvars(other);
+
+ if(gametypevote)
+ {
+ if ( GameTypeVote_Finished(mappos) )
+ {
+ gametypevote = FALSE;
+ if(autocvar_nextmap != "")
+ {
+ Map_Goto_SetStr(autocvar_nextmap);
+ Map_Goto(0);
+ alreadychangedlevel = TRUE;
+ return TRUE;
+ }
+ else
+ MapVote_Init();
+ }
+ return FALSE;
+ }
+
+ Map_Goto_SetStr(mapvote_maps[mappos]);
+ Map_Goto(0);
+ alreadychangedlevel = TRUE;
+
+ return TRUE;
+}
+
+void MapVote_CheckRules_1()
+{
+ float i;
+
+ for(i = 0; i < mapvote_count; ++i)
+ if( mapvote_maps_availability[i] == GTV_AVAILABLE )
+ {
+ //dprint("Map ", ftos(i), ": "); dprint(mapvote_maps[i], "\n");
+ mapvote_selections[i] = 0;
+ }
+
+ mapvote_voters = 0;
+ FOR_EACH_REALCLIENT(other)
+ {
+ ++mapvote_voters;
+ if(other.mapvote)
+ {
+ i = other.mapvote - 1;
+ //dprint("Player ", other.netname, " vote = ", ftos(other.mapvote - 1), "\n");
+ mapvote_selections[i] = mapvote_selections[i] + 1;
+ }
+ }
+}
+
+float MapVote_CheckRules_2()
+{
+ float i;
+ float firstPlace, secondPlace, currentPlace;
+ float firstPlaceVotes, secondPlaceVotes, currentVotes;
+ float mapvote_voters_real;
+ string result;
+
+ if(mapvote_count_real == 1)
+ return MapVote_Finished(0);
+
+ mapvote_voters_real = mapvote_voters;
+ if(mapvote_abstain)
+ mapvote_voters_real -= mapvote_selections[mapvote_count - 1];
+
+ RandomSelection_Init();
+ currentPlace = 0;
+ currentVotes = -1;
+ for(i = 0; i < mapvote_count_real; ++i)
+ if ( mapvote_maps_availability[i] == GTV_AVAILABLE )
+ {
+ RandomSelection_Add(world, i, string_null, 1, mapvote_selections[i]);
+ if ( gametypevote && mapvote_maps[i] == MapInfo_Type_ToString(MapInfo_CurrentGametype()) )
+ {
+ currentVotes = mapvote_selections[i];
+ currentPlace = i;
+ }
+ }
+ firstPlaceVotes = RandomSelection_best_priority;
+ if ( autocvar_sv_vote_gametype_default_current && currentVotes == firstPlaceVotes )
+ firstPlace = currentPlace;
+ else
+ firstPlace = RandomSelection_chosen_float;
+
+ //dprint("First place: ", ftos(firstPlace), "\n");
+ //dprint("First place votes: ", ftos(firstPlaceVotes), "\n");
+
+ RandomSelection_Init();
+ for(i = 0; i < mapvote_count_real; ++i)
+ if(i != firstPlace)
+ if ( mapvote_maps_availability[i] == GTV_AVAILABLE )
+ RandomSelection_Add(world, i, string_null, 1, mapvote_selections[i]);
+ secondPlace = RandomSelection_chosen_float;
+ secondPlaceVotes = RandomSelection_best_priority;
+ //dprint("Second place: ", ftos(secondPlace), "\n");
+ //dprint("Second place votes: ", ftos(secondPlaceVotes), "\n");
+
+ if(firstPlace == -1)
+ error("No first place in map vote... WTF?");
+
+ if(secondPlace == -1 || time > mapvote_timeout || (mapvote_voters_real - firstPlaceVotes) < firstPlaceVotes)
+ return MapVote_Finished(firstPlace);
+
+ if(mapvote_keeptwotime)
+ if(time > mapvote_keeptwotime || (mapvote_voters_real - firstPlaceVotes - secondPlaceVotes) < secondPlaceVotes)
+ {
+ float didntvote;
+ MapVote_TouchMask();
+ mapvote_message = "Now decide between the TOP TWO!";
+ mapvote_keeptwotime = 0;
+ result = strcat(":vote:keeptwo:", mapvote_maps[firstPlace]);
+ result = strcat(result, ":", ftos(firstPlaceVotes));
+ result = strcat(result, ":", mapvote_maps[secondPlace]);
+ result = strcat(result, ":", ftos(secondPlaceVotes), "::");
+ didntvote = mapvote_voters;
+ for(i = 0; i < mapvote_count; ++i)
+ {
+ didntvote -= mapvote_selections[i];
+ if(i != firstPlace)
+ if(i != secondPlace)
+ {
+ result = strcat(result, ":", mapvote_maps[i]);
+ result = strcat(result, ":", ftos(mapvote_selections[i]));
+ if(i < mapvote_count_real)
+ {
+ mapvote_maps_availability[i] = GTV_FORBIDDEN;
+ }
+ }
+ }
+ result = strcat(result, ":didn't vote:", ftos(didntvote));
+ if(autocvar_sv_eventlog)
+ GameLogEcho(result);
+ }
+
+ return FALSE;
+}
+
+void MapVote_Tick()
+{
+ float keeptwo;
+ float totalvotes;
+
+ keeptwo = mapvote_keeptwotime;
+ MapVote_CheckRules_1(); // count
+ if(MapVote_CheckRules_2()) // decide
+ return;
+
+ totalvotes = 0;
+ FOR_EACH_REALCLIENT(other)
+ {
+ // hide scoreboard again
+ if(other.health != 2342)
+ {
+ other.health = 2342;
+ other.impulse = 0;
+ if(IS_REAL_CLIENT(other))
+ {
+ msg_entity = other;
+ WriteByte(MSG_ONE, SVC_FINALE);
+ WriteString(MSG_ONE, "");
+ }
+ }
+
+ // clear possibly invalid votes
+ if ( mapvote_maps_availability[other.mapvote-1] != GTV_AVAILABLE )
+ other.mapvote = 0;
+ // use impulses as new vote
+ if(other.impulse >= 1 && other.impulse <= mapvote_count)
+ if( mapvote_maps_availability[other.impulse - 1] == GTV_AVAILABLE )
+ {
+ other.mapvote = other.impulse;
+ MapVote_TouchVotes(other);
+ }
+ other.impulse = 0;
+
+ if(other.mapvote)
+ ++totalvotes;
+ }
+
+ MapVote_CheckRules_1(); // just count
+}
+
+void MapVote_Start()
+{
+ if(mapvote_run)
+ return;
+
+ // wait for stats to be sent first
+ if(!playerstats_waitforme)
+ return;
+
+ MapInfo_Enumerate();
+ if(MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 1))
+ mapvote_run = TRUE;
+}
+
+void MapVote_Think()
+{
+ if(!mapvote_run)
+ return;
+
+ if(alreadychangedlevel)
+ return;
+
+ if(time < mapvote_nextthink)
+ return;
+ //dprint("tick\n");
+
+ mapvote_nextthink = time + 0.5;
+
+ if(!mapvote_initialized)
+ {
+ if(autocvar_rescan_pending == 1)
+ {
+ cvar_set("rescan_pending", "2");
+ localcmd("fs_rescan\nrescan_pending 3\n");
+ return;
+ }
+ else if(autocvar_rescan_pending == 2)
+ {
+ return;
+ }
+ else if(autocvar_rescan_pending == 3)
+ {
+ // now build missing mapinfo files
+ if(!MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 1))
+ return;
+
+ // we're done, start the timer
+ cvar_set("rescan_pending", "0");
+ }
+
+ mapvote_initialized = TRUE;
+ if(DoNextMapOverride(0))
+ return;
+ if(!autocvar_g_maplist_votable || player_count <= 0)
+ {
+ GotoNextMap(0);
+ return;
+ }
+
+ if(autocvar_sv_vote_gametype) { GameTypeVote_Start(); }
+ else if(autocvar_nextmap == "") { MapVote_Init(); }
+ }
+
+ MapVote_Tick();
+}
+
+float GameTypeVote_SetGametype(float type)
+{
+ if (MapInfo_CurrentGametype() == type)
+ return TRUE;
+
+ float tsave = MapInfo_CurrentGametype();
+
+ MapInfo_SwitchGameType(type);
+
+ MapInfo_Enumerate();
+ MapInfo_FilterGametype(type, MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
+ if(MapInfo_count > 0)
+ {
+ // update lsmaps in case the gametype changed, this way people can easily list maps for it
+ if(lsmaps_reply != "") { strunzone(lsmaps_reply); }
+ lsmaps_reply = strzone(getlsmaps());
+ bprint("Game type successfully switched to ", MapInfo_Type_ToString(type), "\n");
+ }
+ else
+ {
+ bprint("Cannot use this game type: no map for it found\n");
+ MapInfo_SwitchGameType(tsave);
+ MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
+ return FALSE;
+ }
+
+ //localcmd("gametype ", MapInfo_Type_ToString(type), "\n");
+
+ cvar_set("g_maplist", MapInfo_ListAllowedMaps(type, MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags()) );
+ if(autocvar_g_maplist_shuffle)
+ ShuffleMaplist();
+
+ return TRUE;
+}
+
+float gametypevote_finished;
+float GameTypeVote_Finished(float pos)
+{
+ if(!gametypevote || gametypevote_finished)
+ return FALSE;
+
+ if ( !GameTypeVote_SetGametype(MapInfo_Type_FromString(mapvote_maps[pos])) )
+ {
+ dprint("Selected gametype is not supported by any map");
+ }
+
+ localcmd("sv_vote_gametype_hook_all\n");
+ localcmd("sv_vote_gametype_hook_", mapvote_maps[pos], "\n");
+
+ gametypevote_finished = TRUE;
+
+ return TRUE;
+}
+
+float GameTypeVote_AddVotable(string nextMode)
+{
+ float j;
+ if ( nextMode == "" || MapInfo_Type_FromString(nextMode) == 0 )
+ return FALSE;
+ for(j = 0; j < mapvote_count; ++j)
+ if(mapvote_maps[j] == nextMode)
+ return FALSE;
+
+ mapvote_maps[mapvote_count] = strzone(nextMode);
+ mapvote_maps_suggested[mapvote_count] = FALSE;
+
+ mapvote_maps_screenshot_dir[mapvote_count] = 0;
+ mapvote_maps_pakfile[mapvote_count] = strzone("");
+ mapvote_maps_availability[mapvote_count] = GameTypeVote_AvailabilityStatus(nextMode);
+
+ mapvote_count += 1;
+
+ return TRUE;
+
+}
+
+float GameTypeVote_Start()
+{
+ float j;
+ MapVote_ClearAllVotes();
+ MapVote_UnzoneStrings();
+
+ mapvote_count = 0;
+ mapvote_timeout = time + autocvar_sv_vote_gametype_timeout;
+ mapvote_abstain = 0;
+ mapvote_detail = !autocvar_g_maplist_votable_nodetail;
+
+ float n = tokenizebyseparator(autocvar_sv_vote_gametype_options, " ");
+ n = min(MAPVOTE_COUNT, n);
+
+ float really_available, which_available;
+ really_available = 0;
+ which_available = -1;
+ for(j = 0; j < n; ++j)
+ {
+ if ( GameTypeVote_AddVotable(argv(j)) )
+ if ( mapvote_maps_availability[j] == GTV_AVAILABLE )
+ {
+ really_available++;
+ which_available = j;
+ }
+ }
+
+ mapvote_count_real = mapvote_count;
+
+ gametypevote = 1;
+
+ if ( really_available == 0 )
+ {
+ if ( mapvote_count > 0 )
+ strunzone(mapvote_maps[0]);
+ mapvote_maps[0] = strzone(MapInfo_Type_ToString(MapInfo_CurrentGametype()));
+ //GameTypeVote_Finished(0);
+ MapVote_Finished(0);
+ return FALSE;
+ }
+ if ( really_available == 1 )
+ {
+ //GameTypeVote_Finished(which_available);
+ MapVote_Finished(which_available);
+ return FALSE;
+ }
+
+ mapvote_count_real = mapvote_count;
+
+ mapvote_keeptwotime = time + autocvar_sv_vote_gametype_keeptwotime;
+ if(mapvote_count_real < 3 || mapvote_keeptwotime <= time)
+ mapvote_keeptwotime = 0;
+
+ MapVote_Spawn();
+
+ return TRUE;
+}
--- /dev/null
+// definitions for functions used outside mapvoting.qc
+void MapVote_Start();
+void MapVote_Spawn();
+void MapVote_Think();
+float GameTypeVote_Start();
+float GameTypeVote_Finished(float pos);
+string GameTypeVote_MapInfo_FixName(string m);
+
+// definitions
+float gametypevote;
+string getmapname_stored;
+float mapvote_initialized;
+
+float mapvote_nextthink;
+float mapvote_initialized;
+float mapvote_keeptwotime;
+float mapvote_timeout;
+string mapvote_message;
+#define MAPVOTE_SCREENSHOT_DIRS_COUNT 4
+string mapvote_screenshot_dirs[MAPVOTE_SCREENSHOT_DIRS_COUNT];
+float mapvote_screenshot_dirs_count;
+
+float mapvote_count;
+float mapvote_count_real;
+string mapvote_maps[MAPVOTE_COUNT];
+float mapvote_maps_screenshot_dir[MAPVOTE_COUNT];
+string mapvote_maps_pakfile[MAPVOTE_COUNT];
+float mapvote_maps_suggested[MAPVOTE_COUNT];
+string mapvote_suggestions[MAPVOTE_COUNT];
+float mapvote_suggestion_ptr;
+float mapvote_voters;
+float mapvote_selections[MAPVOTE_COUNT];
+float mapvote_maps_availability[MAPVOTE_COUNT];
+float mapvote_run;
+float mapvote_detail;
+float mapvote_abstain;
+.float mapvote;
+
+entity mapvote_ent;
spawnpoints.qh
+mapvoting.qh
+
ipban.qh
race.qh
g_world.qc
g_casings.qc
+mapvoting.qc
+
t_jumppads.qc
t_teleporters.qc