Index: ps/trunk/binaries/data/mods/public/gui/page_replaymenu.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/page_replaymenu.xml (revision 19649)
+++ ps/trunk/binaries/data/mods/public/gui/page_replaymenu.xml (revision 19650)
@@ -1,12 +1,13 @@
common/modern/setup.xmlcommon/modern/styles.xmlcommon/modern/sprites.xmlcommon/setup.xmlcommon/sprites.xmlcommon/styles.xml
+ replaymenu/sprites.xmlreplaymenu/replay_menu.xml
Index: ps/trunk/binaries/data/mods/public/gui/replaymenu/replay_menu.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/replaymenu/replay_menu.js (revision 19649)
+++ ps/trunk/binaries/data/mods/public/gui/replaymenu/replay_menu.js (revision 19650)
@@ -1,351 +1,353 @@
/**
* Used for checking replay compatibility.
*/
const g_EngineInfo = Engine.GetEngineInfo();
/**
* Needed for formatPlayerInfo to show the player civs in the details.
*/
const g_CivData = loadCivData();
/**
* Used for creating the mapsize filter.
*/
const g_MapSizes = prepareForDropdown(g_Settings && g_Settings.MapSizes);
/**
* All replays found in the directory.
*/
var g_Replays = [];
/**
* List of replays after applying the display filter.
*/
var g_ReplaysFiltered = [];
/**
* Array of unique usernames of all replays. Used for autocompleting usernames.
*/
var g_Playernames = [];
/**
* Sorted list of unique maptitles. Used by mapfilter.
*/
var g_MapNames = [];
/**
* Sorted list of the victory conditions occuring in the replays
*/
var g_VictoryConditions = [];
/**
* Directory name of the currently selected replay. Used to restore the selection after changing filters.
*/
var g_SelectedReplayDirectory = "";
/**
* Skip duplicate expensive GUI updates before init is complete.
*/
var g_ReplaysLoaded = false;
/**
* Initializes globals, loads replays and displays the list.
*/
function init(data)
{
if (!g_Settings)
{
Engine.SwitchGuiPage("page_pregame.xml");
return;
}
loadReplays(data && data.replaySelectionData);
if (!g_Replays)
{
Engine.SwitchGuiPage("page_pregame.xml");
return;
}
initHotkeyTooltips();
displayReplayList();
}
/**
* Store the list of replays loaded in C++ in g_Replays.
* Check timestamp and compatibility and extract g_Playernames, g_MapNames, g_VictoryConditions.
* Restore selected filters and item.
*/
function loadReplays(replaySelectionData)
{
g_Replays = Engine.GetReplays();
if (!g_Replays)
return;
g_Playernames = [];
for (let replay of g_Replays)
{
let nonAIPlayers = 0;
// Check replay for compatibility
replay.isCompatible = isReplayCompatible(replay);
sanitizeGameAttributes(replay.attribs);
// Extract map names
if (g_MapNames.indexOf(replay.attribs.settings.Name) == -1 && replay.attribs.settings.Name != "")
g_MapNames.push(replay.attribs.settings.Name);
// Extract victory conditions
if (replay.attribs.settings.GameType && g_VictoryConditions.indexOf(replay.attribs.settings.GameType) == -1)
g_VictoryConditions.push(replay.attribs.settings.GameType);
// Extract playernames
for (let playerData of replay.attribs.settings.PlayerData)
{
if (!playerData || playerData.AI)
continue;
// Remove rating from nick
let playername = playerData.Name;
let ratingStart = playername.indexOf(" (");
if (ratingStart != -1)
playername = playername.substr(0, ratingStart);
if (g_Playernames.indexOf(playername) == -1)
g_Playernames.push(playername);
++nonAIPlayers;
}
replay.isMultiplayer = nonAIPlayers > 1;
replay.isRated = nonAIPlayers == 2 &&
replay.attribs.settings.PlayerData.length == 2 &&
replay.attribs.settings.RatingEnabled;
}
g_MapNames.sort();
g_VictoryConditions.sort();
// Reload filters (since they depend on g_Replays and its derivatives)
initFilters(replaySelectionData && replaySelectionData.filters);
// Restore user selection
if (replaySelectionData)
{
if (replaySelectionData.directory)
g_SelectedReplayDirectory = replaySelectionData.directory;
let replaySelection = Engine.GetGUIObjectByName("replaySelection");
if (replaySelectionData.column)
replaySelection.selected_column = replaySelectionData.column;
if (replaySelectionData.columnOrder)
replaySelection.selected_column_order = replaySelectionData.columnOrder;
}
g_ReplaysLoaded = true;
}
/**
* We may encounter malformed replays.
*/
function sanitizeGameAttributes(attribs)
{
if (!attribs.settings)
attribs.settings = {};
if (!attribs.settings.Size)
attribs.settings.Size = -1;
if (!attribs.settings.Name)
attribs.settings.Name = "";
if (!attribs.settings.PlayerData)
attribs.settings.PlayerData = [];
if (!attribs.settings.PopulationCap)
attribs.settings.PopulationCap = 300;
if (!attribs.settings.mapType)
attribs.settings.mapType = "skirmish";
if (!attribs.settings.GameType)
attribs.settings.GameType = "conquest";
// Remove gaia
if (attribs.settings.PlayerData.length && attribs.settings.PlayerData[0] == null)
attribs.settings.PlayerData.shift();
attribs.settings.PlayerData.forEach((pData, index) => {
if (!pData.Name)
pData.Name = "";
});
}
function initHotkeyTooltips()
{
Engine.GetGUIObjectByName("playersFilter").tooltip =
translate("Filter replays by typing one or more, partial or complete playernames.") +
" " + colorizeAutocompleteHotkey();
Engine.GetGUIObjectByName("deleteReplayButton").tooltip = deleteTooltip();
}
/**
* Filter g_Replays, fill the GUI list with that data and show the description of the current replay.
*/
function displayReplayList()
{
if (!g_ReplaysLoaded)
return;
// Remember previously selected replay
var replaySelection = Engine.GetGUIObjectByName("replaySelection");
if (replaySelection.selected != -1)
g_SelectedReplayDirectory = g_ReplaysFiltered[replaySelection.selected].directory;
filterReplays();
var list = g_ReplaysFiltered.map(replay => {
let works = replay.isCompatible;
return {
"directories": replay.directory,
"months": compatibilityColor(getReplayDateTime(replay), works),
"popCaps": compatibilityColor(translatePopulationCapacity(replay.attribs.settings.PopulationCap), works),
"mapNames": compatibilityColor(getReplayMapName(replay), works),
"mapSizes": compatibilityColor(translateMapSize(replay.attribs.settings.Size), works),
"durations": compatibilityColor(getReplayDuration(replay), works),
"playerNames": compatibilityColor(getReplayPlayernames(replay), works)
};
});
if (list.length)
list = prepareForDropdown(list);
// Push to GUI
replaySelection.selected = -1;
replaySelection.list_months = list.months || [];
replaySelection.list_players = list.playerNames || [];
replaySelection.list_mapName = list.mapNames || [];
replaySelection.list_mapSize = list.mapSizes || [];
replaySelection.list_popCapacity = list.popCaps || [];
replaySelection.list_duration = list.durations || [];
// Change these last, otherwise crash
replaySelection.list = list.directories || [];
replaySelection.list_data = list.directories || [];
replaySelection.selected = replaySelection.list.findIndex(directory => directory == g_SelectedReplayDirectory);
displayReplayDetails();
}
/**
* Shows preview image, description and player text in the right panel.
*/
function displayReplayDetails()
{
let selected = Engine.GetGUIObjectByName("replaySelection").selected;
let replaySelected = selected > -1;
Engine.GetGUIObjectByName("replayInfo").hidden = !replaySelected;
Engine.GetGUIObjectByName("replayInfoEmpty").hidden = replaySelected;
Engine.GetGUIObjectByName("startReplayButton").enabled = replaySelected;
Engine.GetGUIObjectByName("deleteReplayButton").enabled = replaySelected;
+ Engine.GetGUIObjectByName("replayFilename").hidden = !replaySelected;
Engine.GetGUIObjectByName("summaryButton").hidden = true;
if (!replaySelected)
return;
let replay = g_ReplaysFiltered[selected];
Engine.GetGUIObjectByName("sgMapName").caption = translate(replay.attribs.settings.Name);
Engine.GetGUIObjectByName("sgMapSize").caption = translateMapSize(replay.attribs.settings.Size);
Engine.GetGUIObjectByName("sgMapType").caption = translateMapType(replay.attribs.settings.mapType);
Engine.GetGUIObjectByName("sgVictory").caption = translateVictoryCondition(replay.attribs.settings.GameType);
Engine.GetGUIObjectByName("sgNbPlayers").caption = sprintf(translate("Players: %(numberOfPlayers)s"),
{ "numberOfPlayers": replay.attribs.settings.PlayerData.length });
+ Engine.GetGUIObjectByName("replayFilename").caption = escapeText(Engine.GetReplayDirectoryName(replay.directory));
let metadata = Engine.GetReplayMetadata(replay.directory);
Engine.GetGUIObjectByName("sgPlayersNames").caption =
formatPlayerInfo(
replay.attribs.settings.PlayerData,
Engine.GetGUIObjectByName("showSpoiler").checked &&
metadata &&
metadata.playerStates &&
metadata.playerStates.map(pState => pState.state)
);
let mapData = getMapDescriptionAndPreview(replay.attribs.settings.mapType, replay.attribs.map);
Engine.GetGUIObjectByName("sgMapDescription").caption = mapData.description;
Engine.GetGUIObjectByName("summaryButton").hidden = !Engine.HasReplayMetadata(replay.directory);
setMapPreviewImage("sgMapPreview", mapData.preview);
}
/**
* Returns a human-readable version of the replay date.
*/
function getReplayDateTime(replay)
{
return Engine.FormatMillisecondsIntoDateStringLocal(replay.attribs.timestamp * 1000, translate("yyyy-MM-dd HH:mm"));
}
/**
* Returns a human-readable list of the playernames of that replay.
*
* @returns {string}
*/
function getReplayPlayernames(replay)
{
return replay.attribs.settings.PlayerData.map(pData => pData.Name).join(", ");
}
/**
* Returns the name of the map of the given replay.
*
* @returns {string}
*/
function getReplayMapName(replay)
{
return translate(replay.attribs.settings.Name);
}
/**
* Returns the month of the given replay in the format "yyyy-MM".
*
* @returns {string}
*/
function getReplayMonth(replay)
{
return Engine.FormatMillisecondsIntoDateStringLocal(replay.attribs.timestamp * 1000, translate("yyyy-MM"));
}
/**
* Returns a human-readable version of the time when the replay started.
*
* @returns {string}
*/
function getReplayDuration(replay)
{
return timeToString(replay.duration * 1000);
}
/**
* True if we can start the given replay with the currently loaded mods.
*/
function isReplayCompatible(replay)
{
return replayHasSameEngineVersion(replay) && hasSameMods(replay.attribs, g_EngineInfo);
}
/**
* True if we can start the given replay with the currently loaded mods.
*/
function replayHasSameEngineVersion(replay)
{
return replay.attribs.engine_version && replay.attribs.engine_version == g_EngineInfo.engine_version;
}
Index: ps/trunk/binaries/data/mods/public/gui/replaymenu/replay_menu.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/replaymenu/replay_menu.xml (revision 19649)
+++ ps/trunk/binaries/data/mods/public/gui/replaymenu/replay_menu.xml (revision 19650)
@@ -1,263 +1,266 @@
Index: ps/trunk/binaries/data/mods/public/gui/replaymenu/sprites.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/replaymenu/sprites.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/replaymenu/sprites.xml (revision 19650)
@@ -0,0 +1,9 @@
+
+
+
+
+
+
Property changes on: ps/trunk/binaries/data/mods/public/gui/replaymenu/sprites.xml
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/source/ps/scripting/JSInterface_VisualReplay.cpp
===================================================================
--- ps/trunk/source/ps/scripting/JSInterface_VisualReplay.cpp (revision 19649)
+++ ps/trunk/source/ps/scripting/JSInterface_VisualReplay.cpp (revision 19650)
@@ -1,64 +1,70 @@
-/* Copyright (C) 2016 Wildfire Games.
+/* Copyright (C) 2017 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#include "precompiled.h"
#include "ps/scripting/JSInterface_VisualReplay.h"
#include "ps/CStr.h"
#include "ps/Profile.h"
#include "ps/VisualReplay.h"
void JSI_VisualReplay::StartVisualReplay(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), const CStrW& directory)
{
VisualReplay::StartVisualReplay(directory);
}
bool JSI_VisualReplay::DeleteReplay(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), const CStrW& replayFile)
{
return VisualReplay::DeleteReplay(replayFile);
}
JS::Value JSI_VisualReplay::GetReplays(ScriptInterface::CxPrivate* pCxPrivate)
{
return VisualReplay::GetReplays(*(pCxPrivate->pScriptInterface));
}
JS::Value JSI_VisualReplay::GetReplayAttributes(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& directoryName)
{
return VisualReplay::GetReplayAttributes(pCxPrivate, directoryName);
}
bool JSI_VisualReplay::HasReplayMetadata(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), const CStrW& directoryName)
{
return VisualReplay::HasReplayMetadata(directoryName);
}
JS::Value JSI_VisualReplay::GetReplayMetadata(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& directoryName)
{
return VisualReplay::GetReplayMetadata(pCxPrivate, directoryName);
}
+CStrW JSI_VisualReplay::GetReplayDirectoryName(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), const CStrW& directoryName)
+{
+ return OsPath(VisualReplay::GetDirectoryName() / directoryName).string();
+}
+
void JSI_VisualReplay::RegisterScriptFunctions(ScriptInterface& scriptInterface)
{
scriptInterface.RegisterFunction("GetReplays");
scriptInterface.RegisterFunction("DeleteReplay");
scriptInterface.RegisterFunction("StartVisualReplay");
scriptInterface.RegisterFunction("GetReplayAttributes");
scriptInterface.RegisterFunction("GetReplayMetadata");
scriptInterface.RegisterFunction("HasReplayMetadata");
+ scriptInterface.RegisterFunction("GetReplayDirectoryName");
}
Index: ps/trunk/source/ps/scripting/JSInterface_VisualReplay.h
===================================================================
--- ps/trunk/source/ps/scripting/JSInterface_VisualReplay.h (revision 19649)
+++ ps/trunk/source/ps/scripting/JSInterface_VisualReplay.h (revision 19650)
@@ -1,35 +1,36 @@
-/* Copyright (C) 2016 Wildfire Games.
+/* Copyright (C) 2017 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#ifndef INCLUDED_JSI_VISUALREPLAY
#define INCLUDED_JSI_VISUALREPLAY
#include "ps/VisualReplay.h"
#include "scriptinterface/ScriptInterface.h"
namespace JSI_VisualReplay
{
void StartVisualReplay(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& directory);
bool DeleteReplay(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& replayFile);
JS::Value GetReplays(ScriptInterface::CxPrivate* pCxPrivate);
JS::Value GetReplayAttributes(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& directoryName);
bool HasReplayMetadata(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& directoryName);
JS::Value GetReplayMetadata(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& directoryName);
void RegisterScriptFunctions(ScriptInterface& scriptInterface);
+ CStrW GetReplayDirectoryName(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& directoryName);
}
#endif