Changeset View
Changeset View
Standalone View
Standalone View
binaries/data/mods/mod/gui/modmod/modmod.js
Show First 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
var g_ModsCompatibility = []; | var g_ModsCompatibility = []; | ||||
/** | /** | ||||
* Name of the mods installed by the ModInstaller. | * Name of the mods installed by the ModInstaller. | ||||
*/ | */ | ||||
var g_InstalledMods; | var g_InstalledMods; | ||||
var g_HasFailedMods; | |||||
var g_FakeMod = { | |||||
"name": translate("This mod does not exist"), | |||||
Silier: hmm, somehow i made this irrelevant, one can ask for failed mods directly, however in case of… | |||||
"version": "", | |||||
"label": "", | |||||
"url": "", | |||||
"description": "", | |||||
"dependencies": [] | |||||
}; | |||||
var g_ColorNoModSelected = "255 255 100"; | var g_ColorNoModSelected = "255 255 100"; | ||||
var g_ColorDependenciesMet = "100 255 100"; | var g_ColorDependenciesMet = "100 255 100"; | ||||
var g_ColorDependenciesNotMet = "255 100 100"; | var g_ColorDependenciesNotMet = "255 100 100"; | ||||
function init(data, hotloadData) | function init(data, hotloadData) | ||||
{ | { | ||||
g_InstalledMods = data && data.installedMods || hotloadData && hotloadData.installedMods || []; | g_InstalledMods = data && data.installedMods || hotloadData && hotloadData.installedMods || []; | ||||
g_HasFailedMods = Engine.HasFailedMods(); | |||||
initMods(); | initMods(); | ||||
initGUIButtons(data); | initGUIButtons(data); | ||||
if (g_HasFailedMods) | |||||
Engine.PushGuiPage("page_incompatible_mods.xml", {}); | |||||
} | } | ||||
function initMods() | function initMods() | ||||
{ | { | ||||
loadMods(); | loadMods(); | ||||
loadEnabledMods(); | loadEnabledMods(); | ||||
recomputeCompatibility(); | recomputeCompatibility(); | ||||
validateMods(); | validateMods(); | ||||
initGUIFilters(); | initGUIFilters(); | ||||
} | } | ||||
function getHotloadData() | function getHotloadData() | ||||
{ | { | ||||
return { "installedMods": g_InstalledMods }; | return { "installedMods": g_InstalledMods }; | ||||
} | } | ||||
function loadMods() | function loadMods() | ||||
{ | { | ||||
g_Mods = Engine.GetAvailableMods(); | g_Mods = Engine.GetAvailableMods(); | ||||
deepfreeze(g_Mods); | deepfreeze(g_Mods); | ||||
} | } | ||||
/** | |||||
* Return fake mod for mods which do not exist | |||||
*/ | |||||
function getMod(folder) | |||||
{ | |||||
return !!g_Mods[folder] ? g_Mods[folder] : g_FakeMod; | |||||
} | |||||
function loadEnabledMods() | function loadEnabledMods() | ||||
{ | { | ||||
g_ModsEnabled = Engine.ConfigDB_GetValue("user", "mod.enabledmods").split(/\s+/).filter(folder => !!g_Mods[folder]); | if (g_HasFailedMods) | ||||
g_ModsEnabled = Engine.GetFailedMods().filter(folder => folder != "mod"); | |||||
else | |||||
g_ModsEnabled = Engine.GetEnabledMods().filter(folder => !!g_Mods[folder]); | |||||
g_ModsDisabled = Object.keys(g_Mods).filter(folder => g_ModsEnabled.indexOf(folder) == -1); | g_ModsDisabled = Object.keys(g_Mods).filter(folder => g_ModsEnabled.indexOf(folder) == -1); | ||||
g_ModsEnabledFiltered = g_ModsEnabled; | g_ModsEnabledFiltered = g_ModsEnabled; | ||||
g_ModsDisabledFiltered = g_ModsDisabled; | g_ModsDisabledFiltered = g_ModsDisabled; | ||||
} | } | ||||
function validateMods() | function validateMods() | ||||
{ | { | ||||
for (let folder in g_Mods) | for (let folder in g_Mods) | ||||
validateMod(folder, g_Mods[folder], true); | validateMod(folder, g_Mods[folder], true); | ||||
} | } | ||||
function initGUIFilters() | function initGUIFilters() | ||||
{ | { | ||||
Engine.GetGUIObjectByName("negateFilter").checked = false; | Engine.GetGUIObjectByName("negateFilter").checked = false; | ||||
Engine.GetGUIObjectByName("modCompatibleFilter").checked = true; | Engine.GetGUIObjectByName("modCompatibleFilter").checked = true; | ||||
displayModLists(); | displayModLists(); | ||||
} | } | ||||
function initGUIButtons(data) | function initGUIButtons(data) | ||||
{ | { | ||||
// Either get back to the previous page or quit if there is no previous page | // Either get back to the previous page or quit if there is no previous page | ||||
let cancelButton = !data || data.cancelbutton; | let hasPreviousPage = !data || data.cancelbutton; | ||||
Done Inline Actionsrename to something general Silier: rename to something general | |||||
Engine.GetGUIObjectByName("cancelButton").hidden = !cancelButton; | Engine.GetGUIObjectByName("cancelButton").hidden = !hasPreviousPage; | ||||
Engine.GetGUIObjectByName("quitButton").hidden = cancelButton; | Engine.GetGUIObjectByName("quitButton").hidden = hasPreviousPage; | ||||
Engine.GetGUIObjectByName("startModsButton").hidden = !hasPreviousPage; | |||||
Engine.GetGUIObjectByName("startButton").hidden = hasPreviousPage; | |||||
Engine.GetGUIObjectByName("toggleModButton").caption = translateWithContext("mod activation", "Enable"); | Engine.GetGUIObjectByName("toggleModButton").caption = translateWithContext("mod activation", "Enable"); | ||||
} | } | ||||
function saveMods() | function saveMods() | ||||
{ | { | ||||
sortEnabledMods(); | sortEnabledMods(); | ||||
Engine.ConfigDB_CreateValue("user", "mod.enabledmods", ["mod"].concat(g_ModsEnabled).join(" ")); | Engine.ConfigDB_CreateValue("user", "mod.enabledmods", ["mod"].concat(g_ModsEnabled).join(" ")); | ||||
Engine.ConfigDB_WriteFile("user", "config/user.cfg"); | Engine.ConfigDB_WriteFile("user", "config/user.cfg"); | ||||
} | } | ||||
function startMods() | function startMods() | ||||
{ | { | ||||
sortEnabledMods(); | sortEnabledMods(); | ||||
Engine.SetMods(["mod"].concat(g_ModsEnabled)); | if (!Engine.SetModsAndRestartEngine(["mod"].concat(g_ModsEnabled))) | ||||
Engine.RestartEngine(); | Engine.GetGUIObjectByName("message").caption = coloredText(translate('Dependencies not met'), g_ColorDependenciesNotMet); | ||||
} | } | ||||
function displayModLists() | function displayModLists() | ||||
{ | { | ||||
Done Inline Actionsset mod in c++ as we require it to be able to show at least mod page and we hide it from available mods anyway so no one can mess up this line Silier: set mod in c++ as we require it to be able to show at least mod page and we hide it from… | |||||
g_ModsEnabledFiltered = displayModList("modsEnabledList", g_ModsEnabled, true); | g_ModsEnabledFiltered = displayModList("modsEnabledList", g_ModsEnabled, true); | ||||
g_ModsDisabledFiltered = displayModList("modsDisabledList", g_ModsDisabled, false); | g_ModsDisabledFiltered = displayModList("modsDisabledList", g_ModsDisabled, false); | ||||
} | } | ||||
function displayModList(listObjectName, folders, enabled) | function displayModList(listObjectName, folders, enabled) | ||||
{ | { | ||||
let listObject = Engine.GetGUIObjectByName(listObjectName); | let listObject = Engine.GetGUIObjectByName(listObjectName); | ||||
if (listObjectName == "modsDisabledList") | if (listObjectName == "modsDisabledList") | ||||
{ | { | ||||
let sortFolder = folder => String(g_Mods[folder][listObject.selected_column] || folder); | let sortFolder = folder => String(getMod(folder)[listObject.selected_column] || folder); | ||||
folders.sort((folder1, folder2) => | folders.sort((folder1, folder2) => | ||||
listObject.selected_column_order * | listObject.selected_column_order * | ||||
sortFolder(folder1).localeCompare(sortFolder(folder2))); | sortFolder(folder1).localeCompare(sortFolder(folder2))); | ||||
} | } | ||||
folders = folders.filter(filterMod); | folders = folders.filter(filterMod); | ||||
if (!enabled && Engine.GetGUIObjectByName("modCompatibleFilter").checked) | if (!enabled && Engine.GetGUIObjectByName("modCompatibleFilter").checked) | ||||
folders = folders.filter(folder => g_ModsCompatibility[folder]); | folders = folders.filter(folder => g_ModsCompatibility[folder]); | ||||
let selected = listObject.selected !== -1 ? listObject.list_name[listObject.selected] : null; | let selected = listObject.selected !== -1 ? listObject.list_name[listObject.selected] : null; | ||||
listObject.list_name = folders.map(folder => colorMod(folder, g_Mods[folder].name, enabled)); | listObject.list_name = folders.map(folder => colorMod(folder, getMod(folder).name, enabled)); | ||||
listObject.list_folder = folders.map(folder => colorMod(folder, folder, enabled)); | listObject.list_folder = folders.map(folder => colorMod(folder, folder, enabled)); | ||||
listObject.list_label = folders.map(folder => colorMod(folder, g_Mods[folder].label, enabled)); | listObject.list_label = folders.map(folder => colorMod(folder, getMod(folder).label, enabled)); | ||||
listObject.list_url = folders.map(folder => colorMod(folder, g_Mods[folder].url || "", enabled)); | listObject.list_url = folders.map(folder => colorMod(folder, getMod(folder).url || "", enabled)); | ||||
listObject.list_version = folders.map(folder => colorMod(folder, g_Mods[folder].version, enabled)); | listObject.list_version = folders.map(folder => colorMod(folder, getMod(folder).version, enabled)); | ||||
listObject.list_dependencies = folders.map(folder => colorMod(folder, g_Mods[folder].dependencies.join(" "), enabled)); | listObject.list_dependencies = folders.map(folder => colorMod(folder, getMod(folder).dependencies.join(" "), enabled)); | ||||
listObject.list = folders; | listObject.list = folders; | ||||
listObject.selected = selected ? listObject.list_name.indexOf(selected) : -1; | listObject.selected = selected ? listObject.list_name.indexOf(selected) : -1; | ||||
return folders; | return folders; | ||||
} | } | ||||
function getModColor(folder, enabled) | function getModColor(folder, enabled) | ||||
{ | { | ||||
if (!g_ModsCompatibility[folder]) | if (!g_ModsCompatibility[folder]) | ||||
return enabled ? g_ColorDependenciesNotMet : "gray"; | return enabled ? g_ColorDependenciesNotMet : "gray"; | ||||
if (g_InstalledMods.indexOf(g_Mods[folder].name) != -1) | if (g_InstalledMods.indexOf(getMod(folder).name) != -1) | ||||
return "green"; | return "green"; | ||||
return false; | return false; | ||||
} | } | ||||
function colorMod(folder, text, enabled) | function colorMod(folder, text, enabled) | ||||
{ | { | ||||
let color = getModColor(folder, enabled); | let color = getModColor(folder, enabled); | ||||
return color ? coloredText(text, color) : text; | return color ? coloredText(text, color) : text; | ||||
Show All 15 Lines | function enableMod() | ||||
g_ModsEnabled.push(g_ModsDisabledFiltered.splice(pos, 1)[0]); | g_ModsEnabled.push(g_ModsDisabledFiltered.splice(pos, 1)[0]); | ||||
reloadDisabledMods(); | reloadDisabledMods(); | ||||
recomputeCompatibility(); | recomputeCompatibility(); | ||||
if (pos >= g_ModsDisabledFiltered.length) | if (pos >= g_ModsDisabledFiltered.length) | ||||
--pos; | --pos; | ||||
displayModLists(); | displayModLists(); | ||||
Engine.GetGUIObjectByName("message").caption = ""; | |||||
modsDisabledList.selected = pos; | modsDisabledList.selected = pos; | ||||
} | } | ||||
function disableMod() | function disableMod() | ||||
{ | { | ||||
let modsEnabledList = Engine.GetGUIObjectByName("modsEnabledList"); | let modsEnabledList = Engine.GetGUIObjectByName("modsEnabledList"); | ||||
let pos = modsEnabledList.selected; | let pos = modsEnabledList.selected; | ||||
if (pos == -1) | if (pos == -1) | ||||
return; | return; | ||||
// Find true position of disabled mod and remove it | // Find true position of disabled mod and remove it | ||||
let disabledMod = g_ModsEnabledFiltered[pos]; | let disabledMod = g_ModsEnabledFiltered[pos]; | ||||
for (let i = 0; i < g_ModsEnabled.length; ++i) | for (let i = 0; i < g_ModsEnabled.length; ++i) | ||||
if (g_ModsEnabled[i] == disabledMod) | if (g_ModsEnabled[i] == disabledMod) | ||||
{ | { | ||||
g_ModsEnabled.splice(i, 1); | g_ModsEnabled.splice(i, 1); | ||||
break; | break; | ||||
} | } | ||||
if (!!g_Mods[disabledMod]) | |||||
g_ModsDisabled.push(disabledMod); | g_ModsDisabled.push(disabledMod); | ||||
// Remove mods that required the removed mod and cascade | // Remove mods that required the removed mod and cascade | ||||
// Sort them, so we know which ones can depend on the removed mod | // Sort them, so we know which ones can depend on the removed mod | ||||
// TODO: Find position where the removed mod would have fit (for now assume idx 0) | // TODO: Find position where the removed mod would have fit (for now assume idx 0) | ||||
sortEnabledMods(); | sortEnabledMods(); | ||||
for (let i = 0; i < g_ModsEnabled.length; ++i) | for (let i = 0; i < g_ModsEnabled.length; ++i) | ||||
if (!areDependenciesMet(g_ModsEnabled[i], true)) | if (!areDependenciesMet(g_ModsEnabled[i], true)) | ||||
{ | { | ||||
g_ModsDisabled.push(g_ModsEnabled.splice(i, 1)[0]); | g_ModsDisabled.push(g_ModsEnabled.splice(i, 1)[0]); | ||||
--i; | --i; | ||||
} | } | ||||
recomputeCompatibility(true); | recomputeCompatibility(true); | ||||
displayModLists(); | displayModLists(); | ||||
Engine.GetGUIObjectByName("message").caption = ""; | |||||
modsEnabledList.selected = Math.min(pos, g_ModsEnabledFiltered.length - 1); | modsEnabledList.selected = Math.min(pos, g_ModsEnabledFiltered.length - 1); | ||||
} | } | ||||
function filterMod(folder) | function filterMod(folder) | ||||
{ | { | ||||
let mod = g_Mods[folder]; | let mod = getMod(folder); | ||||
let negateFilter = Engine.GetGUIObjectByName("negateFilter").checked; | let negateFilter = Engine.GetGUIObjectByName("negateFilter").checked; | ||||
let searchText = Engine.GetGUIObjectByName("modGenericFilter").caption; | let searchText = Engine.GetGUIObjectByName("modGenericFilter").caption; | ||||
if (searchText && | if (searchText && | ||||
folder.indexOf(searchText) == -1 && | folder.indexOf(searchText) == -1 && | ||||
mod.name.indexOf(searchText) == -1 && | mod.name.indexOf(searchText) == -1 && | ||||
mod.label.indexOf(searchText) == -1 && | mod.label.indexOf(searchText) == -1 && | ||||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
function areDependenciesMet(folder, disabledAction = false) | function areDependenciesMet(folder, disabledAction = false) | ||||
{ | { | ||||
// If we disabled mod it will not change satus of incompatible mods | // If we disabled mod it will not change satus of incompatible mods | ||||
if (disabledAction && !g_ModsCompatibility[folder]) | if (disabledAction && !g_ModsCompatibility[folder]) | ||||
return g_ModsCompatibility[folder]; | return g_ModsCompatibility[folder]; | ||||
for (let dependency of g_Mods[folder].dependencies) | if (!g_Mods[folder]) | ||||
return false; | |||||
for (let dependency of getMod(folder).dependencies) | |||||
{ | { | ||||
if (!isDependencyMet(dependency)) | if (!isDependencyMet(dependency)) | ||||
return false; | return false; | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
function recomputeCompatibility(disabledAction = false) | function recomputeCompatibility(disabledAction = false) | ||||
{ | { | ||||
for (let mod in g_Mods) | for (let mod in g_Mods) | ||||
g_ModsCompatibility[mod] = areDependenciesMet(mod, disabledAction); | g_ModsCompatibility[mod] = areDependenciesMet(mod, disabledAction); | ||||
} | } | ||||
/** | /** | ||||
* @param dependency is a mod name or a mod version comparison. | * @param dependency is a mod name or a mod version comparison. | ||||
*/ | */ | ||||
function isDependencyMet(dependency) | function isDependencyMet(dependency) | ||||
{ | { | ||||
let operator = dependency.match(g_RegExpComparisonOperator); | let operator = dependency.match(g_RegExpComparisonOperator); | ||||
let [name, version] = operator ? dependency.split(operator[0]) : [dependency, undefined]; | let [name, version] = operator ? dependency.split(operator[0]) : [dependency, undefined]; | ||||
return g_ModsEnabled.some(folder => | return g_ModsEnabled.some(folder => | ||||
g_Mods[folder].name == name && | getMod(folder).name == name && | ||||
(!operator || versionSatisfied(g_Mods[folder].version, operator[0], version))); | (!operator || versionSatisfied(getMod(folder).version, operator[0], version))); | ||||
} | } | ||||
/** | /** | ||||
* Compares the given versions using the given operator. | * Compares the given versions using the given operator. | ||||
* '-' or '_' is ignored. Only numbers are supported. | * '-' or '_' is ignored. Only numbers are supported. | ||||
* @note "5.3" < "5.3.0" | * @note "5.3" < "5.3.0" | ||||
*/ | */ | ||||
function versionSatisfied(version1, operator, version2) | function versionSatisfied(version1, operator, version2) | ||||
Show All 27 Lines | function versionSatisfied(version1, operator, version2) | ||||
return gt; | return gt; | ||||
} | } | ||||
function sortEnabledMods() | function sortEnabledMods() | ||||
{ | { | ||||
let dependencies = {}; | let dependencies = {}; | ||||
for (let folder of g_ModsEnabled) | for (let folder of g_ModsEnabled) | ||||
dependencies[folder] = g_Mods[folder].dependencies.map(d => d.split(g_RegExpComparisonOperator)[0]); | dependencies[folder] = getMod(folder).dependencies.map(d => d.split(g_RegExpComparisonOperator)[0]); | ||||
g_ModsEnabled.sort((folder1, folder2) => | g_ModsEnabled.sort((folder1, folder2) => | ||||
dependencies[folder1].indexOf(g_Mods[folder2].name) != -1 ? 1 : | dependencies[folder1].indexOf(getMod(folder2).name) != -1 ? 1 : | ||||
dependencies[folder2].indexOf(g_Mods[folder1].name) != -1 ? -1 : 0); | dependencies[folder2].indexOf(getMod(folder1).name) != -1 ? -1 : 0); | ||||
g_ModsEnabledFiltered = displayModList("modsEnabledList", g_ModsEnabled, true); | g_ModsEnabledFiltered = displayModList("modsEnabledList", g_ModsEnabled, true); | ||||
} | } | ||||
function selectedMod(listObjectName) | function selectedMod(listObjectName) | ||||
{ | { | ||||
let listObject = Engine.GetGUIObjectByName(listObjectName); | let listObject = Engine.GetGUIObjectByName(listObjectName); | ||||
let isPickedDisabledList = listObjectName == "modsDisabledList"; | let isPickedDisabledList = listObjectName == "modsDisabledList"; | ||||
Show All 13 Lines | toggleModButton.caption = isPickedDisabledList ? | ||||
translateWithContext("mod activation", "Enable") : | translateWithContext("mod activation", "Enable") : | ||||
translateWithContext("mod activation", "Disable"); | translateWithContext("mod activation", "Disable"); | ||||
toggleModButton.enabled = isPickedDisabledList ? isModSelected && g_ModsCompatibility[listObject.list[listObject.selected]] : isModSelected; | toggleModButton.enabled = isPickedDisabledList ? isModSelected && g_ModsCompatibility[listObject.list[listObject.selected]] : isModSelected; | ||||
Engine.GetGUIObjectByName("enabledModUp").enabled = isModSelected && listObjectName == "modsEnabledList" && !areFilters(); | Engine.GetGUIObjectByName("enabledModUp").enabled = isModSelected && listObjectName == "modsEnabledList" && !areFilters(); | ||||
Engine.GetGUIObjectByName("enabledModDown").enabled = isModSelected && listObjectName == "modsEnabledList" && !areFilters(); | Engine.GetGUIObjectByName("enabledModDown").enabled = isModSelected && listObjectName == "modsEnabledList" && !areFilters(); | ||||
Engine.GetGUIObjectByName("globalModDescription").caption = | Engine.GetGUIObjectByName("globalModDescription").caption = | ||||
listObject.list[listObject.selected] ? | listObject.list[listObject.selected] ? | ||||
g_Mods[listObject.list[listObject.selected]].description : | getMod(listObject.list[listObject.selected]).description : | ||||
'[color="' + g_ColorNoModSelected + '"]' + translate("No mod has been selected.") + '[/color]'; | '[color="' + g_ColorNoModSelected + '"]' + translate("No mod has been selected.") + '[/color]'; | ||||
if (!g_ModsEnabled.length) | |||||
{ | |||||
if (!Engine.GetGUIObjectByName("startButton").hidden) | |||||
Engine.GetGUIObjectByName("message").caption = coloredText(translate('Enable at least 0ad mod and save configuration'), g_ColorDependenciesNotMet); | |||||
else | |||||
Engine.GetGUIObjectByName("message").caption = coloredText(translate('Enable at least 0ad mod'), g_ColorDependenciesNotMet); | |||||
} | |||||
if (!Engine.GetGUIObjectByName("startButton").hidden) | |||||
Engine.GetGUIObjectByName("startButton").enabled = g_ModsEnabled.length > 0; | |||||
if (!Engine.GetGUIObjectByName("startModsButton").hidden) | |||||
Engine.GetGUIObjectByName("startModsButton").enabled = g_ModsEnabled.length > 0; | |||||
} | } | ||||
/** | /** | ||||
* @returns {string} The url of the currently selected mod. | * @returns {string} The url of the currently selected mod. | ||||
*/ | */ | ||||
function getSelectedModUrl() | function getSelectedModUrl() | ||||
{ | { | ||||
let modsEnabledList = Engine.GetGUIObjectByName("modsEnabledList"); | let modsEnabledList = Engine.GetGUIObjectByName("modsEnabledList"); | ||||
let modsDisabledList = Engine.GetGUIObjectByName("modsDisabledList"); | let modsDisabledList = Engine.GetGUIObjectByName("modsDisabledList"); | ||||
let list = modsEnabledList.selected == -1 ? modsDisabledList : modsEnabledList; | let list = modsEnabledList.selected == -1 ? modsDisabledList : modsEnabledList; | ||||
let folder = list.list[list.selected]; | let folder = list.list[list.selected]; | ||||
return folder && g_Mods[folder] && g_Mods[folder].url || undefined; | return folder && getMod(folder) && getMod(folder).url || undefined; | ||||
} | } | ||||
function visitModWebsite() | function visitModWebsite() | ||||
{ | { | ||||
let url = getSelectedModUrl(); | let url = getSelectedModUrl(); | ||||
if (!url) | if (!url) | ||||
return; | return; | ||||
if (!url.startsWith("http://") && !url.startsWith("https://")) | if (!url.startsWith("http://") && !url.startsWith("https://")) | ||||
url = "http://" + url; | url = "http://" + url; | ||||
openURL(url); | openURL(url); | ||||
} | } |
Wildfire Games · Phabricator
hmm, somehow i made this irrelevant, one can ask for failed mods directly, however in case of not visual starts it would be good to know which one exactly caused it, so irrelevant just in js