mirror of
https://github.com/tobspr/shapez.io.git
synced 2025-12-15 19:21:49 +00:00
Store mods in savegame and show warning when it differs
This commit is contained in:
parent
509c01f642
commit
41841651db
@ -7,6 +7,10 @@ const METADATA = {
|
|||||||
id: "base",
|
id: "base",
|
||||||
description: "The most basic mod",
|
description: "The most basic mod",
|
||||||
minimumGameVersion: ">=1.5.0",
|
minimumGameVersion: ">=1.5.0",
|
||||||
|
|
||||||
|
// You can specify this parameter if savegames will still work
|
||||||
|
// after your mod has been uninstalled
|
||||||
|
doesNotAffectSavegame: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
class Mod extends shapez.Mod {
|
class Mod extends shapez.Mod {
|
||||||
|
|||||||
@ -7,6 +7,10 @@ const METADATA = {
|
|||||||
id: "custom-css",
|
id: "custom-css",
|
||||||
description: "Shows how to add custom css",
|
description: "Shows how to add custom css",
|
||||||
minimumGameVersion: ">=1.5.0",
|
minimumGameVersion: ">=1.5.0",
|
||||||
|
|
||||||
|
// You can specify this parameter if savegames will still work
|
||||||
|
// after your mod has been uninstalled
|
||||||
|
doesNotAffectSavegame: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
class Mod extends shapez.Mod {
|
class Mod extends shapez.Mod {
|
||||||
|
|||||||
@ -7,6 +7,10 @@ const METADATA = {
|
|||||||
id: "base",
|
id: "base",
|
||||||
description: "Displays an indicator on every item processing building when its working",
|
description: "Displays an indicator on every item processing building when its working",
|
||||||
minimumGameVersion: ">=1.5.0",
|
minimumGameVersion: ">=1.5.0",
|
||||||
|
|
||||||
|
// You can specify this parameter if savegames will still work
|
||||||
|
// after your mod has been uninstalled
|
||||||
|
doesNotAffectSavegame: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
class ItemProcessorStatusGameSystem extends shapez.GameSystem {
|
class ItemProcessorStatusGameSystem extends shapez.GameSystem {
|
||||||
|
|||||||
@ -7,6 +7,10 @@ const METADATA = {
|
|||||||
id: "base",
|
id: "base",
|
||||||
description: "Shows how to add a new keybinding",
|
description: "Shows how to add a new keybinding",
|
||||||
minimumGameVersion: ">=1.5.0",
|
minimumGameVersion: ">=1.5.0",
|
||||||
|
|
||||||
|
// You can specify this parameter if savegames will still work
|
||||||
|
// after your mod has been uninstalled
|
||||||
|
doesNotAffectSavegame: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
class Mod extends shapez.Mod {
|
class Mod extends shapez.Mod {
|
||||||
|
|||||||
@ -7,6 +7,10 @@ const METADATA = {
|
|||||||
id: "custom-theme",
|
id: "custom-theme",
|
||||||
description: "Shows how to add a custom game theme",
|
description: "Shows how to add a custom game theme",
|
||||||
minimumGameVersion: ">=1.5.0",
|
minimumGameVersion: ">=1.5.0",
|
||||||
|
|
||||||
|
// You can specify this parameter if savegames will still work
|
||||||
|
// after your mod has been uninstalled
|
||||||
|
doesNotAffectSavegame: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
class Mod extends shapez.Mod {
|
class Mod extends shapez.Mod {
|
||||||
|
|||||||
@ -7,6 +7,10 @@ const METADATA = {
|
|||||||
id: "modify-theme",
|
id: "modify-theme",
|
||||||
description: "Shows how to modify builtin themes",
|
description: "Shows how to modify builtin themes",
|
||||||
minimumGameVersion: ">=1.5.0",
|
minimumGameVersion: ">=1.5.0",
|
||||||
|
|
||||||
|
// You can specify this parameter if savegames will still work
|
||||||
|
// after your mod has been uninstalled
|
||||||
|
doesNotAffectSavegame: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
class Mod extends shapez.Mod {
|
class Mod extends shapez.Mod {
|
||||||
|
|||||||
@ -7,6 +7,10 @@ const METADATA = {
|
|||||||
id: "modify-ui",
|
id: "modify-ui",
|
||||||
description: "Shows how to modify a builtin game state, in this case the main menu",
|
description: "Shows how to modify a builtin game state, in this case the main menu",
|
||||||
minimumGameVersion: ">=1.5.0",
|
minimumGameVersion: ">=1.5.0",
|
||||||
|
|
||||||
|
// You can specify this parameter if savegames will still work
|
||||||
|
// after your mod has been uninstalled
|
||||||
|
doesNotAffectSavegame: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
class Mod extends shapez.Mod {
|
class Mod extends shapez.Mod {
|
||||||
|
|||||||
@ -7,6 +7,10 @@ const METADATA = {
|
|||||||
id: "replace-builtin-sprites",
|
id: "replace-builtin-sprites",
|
||||||
description: "Shows how to replace builtin sprites",
|
description: "Shows how to replace builtin sprites",
|
||||||
minimumGameVersion: ">=1.5.0",
|
minimumGameVersion: ">=1.5.0",
|
||||||
|
|
||||||
|
// You can specify this parameter if savegames will still work
|
||||||
|
// after your mod has been uninstalled
|
||||||
|
doesNotAffectSavegame: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
class Mod extends shapez.Mod {
|
class Mod extends shapez.Mod {
|
||||||
|
|||||||
@ -7,6 +7,10 @@ const METADATA = {
|
|||||||
id: "translations",
|
id: "translations",
|
||||||
description: "Shows how to add and modify translations",
|
description: "Shows how to add and modify translations",
|
||||||
minimumGameVersion: ">=1.5.0",
|
minimumGameVersion: ">=1.5.0",
|
||||||
|
|
||||||
|
// You can specify this parameter if savegames will still work
|
||||||
|
// after your mod has been uninstalled
|
||||||
|
doesNotAffectSavegame: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
class Mod extends shapez.Mod {
|
class Mod extends shapez.Mod {
|
||||||
|
|||||||
@ -169,6 +169,10 @@
|
|||||||
margin: 1px 0;
|
margin: 1px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
@include S(margin-top, 10px);
|
||||||
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
background: #eee;
|
background: #eee;
|
||||||
color: #333438;
|
color: #333438;
|
||||||
@ -214,6 +218,33 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dialogModsMod {
|
||||||
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
@include S(padding, 5px);
|
||||||
|
@include S(margin, 10px, 0);
|
||||||
|
@include S(border-radius, $globalBorderRadius);
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr D(100px);
|
||||||
|
|
||||||
|
@include DarkThemeOverride {
|
||||||
|
background: rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
grid-column: 2 / 3;
|
||||||
|
grid-row: 1 / 3;
|
||||||
|
align-self: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version {
|
||||||
|
@include SuperSmallText;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> .buttons {
|
> .buttons {
|
||||||
|
|||||||
@ -205,7 +205,7 @@ export class GameSystemManager {
|
|||||||
addBefore("end");
|
addBefore("end");
|
||||||
|
|
||||||
for (const key in MODS_ADDITIONAL_SYSTEMS) {
|
for (const key in MODS_ADDITIONAL_SYSTEMS) {
|
||||||
if (!this.systems[key]) {
|
if (!this.systems[key] && key !== "end") {
|
||||||
logger.error("Mod system not attached due to invalid 'before': ", key);
|
logger.error("Mod system not attached due to invalid 'before': ", key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,7 +24,8 @@ const LOG = createLogger("mods");
|
|||||||
* description: string;
|
* description: string;
|
||||||
* id: string;
|
* id: string;
|
||||||
* minimumGameVersion?: string;
|
* minimumGameVersion?: string;
|
||||||
* settings: []
|
* settings: [];
|
||||||
|
* doesNotAffectSavegame?: boolean
|
||||||
* }} ModMetadata
|
* }} ModMetadata
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -58,6 +59,51 @@ export class ModLoader {
|
|||||||
return this.mods.length > 0;
|
return this.mods.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns {import("../savegame/savegame_typedefs").SavegameStoredMods}
|
||||||
|
*/
|
||||||
|
getModsListForSavegame() {
|
||||||
|
return this.mods
|
||||||
|
.filter(mod => !mod.metadata.doesNotAffectSavegame)
|
||||||
|
.map(mod => ({
|
||||||
|
id: mod.metadata.id,
|
||||||
|
version: mod.metadata.version,
|
||||||
|
website: mod.metadata.website,
|
||||||
|
name: mod.metadata.name,
|
||||||
|
author: mod.metadata.author,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import("../savegame/savegame_typedefs").SavegameStoredMods} originalMods
|
||||||
|
*/
|
||||||
|
computeModDifference(originalMods) {
|
||||||
|
/**
|
||||||
|
* @type {import("../savegame/savegame_typedefs").SavegameStoredMods}
|
||||||
|
*/
|
||||||
|
let missing = [];
|
||||||
|
|
||||||
|
const current = this.getModsListForSavegame();
|
||||||
|
|
||||||
|
originalMods.forEach(mod => {
|
||||||
|
for (let i = 0; i < current.length; ++i) {
|
||||||
|
const currentMod = current[i];
|
||||||
|
if (currentMod.id === mod.id && currentMod.version === mod.version) {
|
||||||
|
current.splice(i, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
missing.push(mod);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
missing,
|
||||||
|
extra: current,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
exposeExports() {
|
exposeExports() {
|
||||||
if (G_IS_DEV || G_IS_STANDALONE) {
|
if (G_IS_DEV || G_IS_STANDALONE) {
|
||||||
let exports = {};
|
let exports = {};
|
||||||
|
|||||||
@ -14,6 +14,8 @@ import { SavegameInterface_V1006 } from "./schemas/1006";
|
|||||||
import { SavegameInterface_V1007 } from "./schemas/1007";
|
import { SavegameInterface_V1007 } from "./schemas/1007";
|
||||||
import { SavegameInterface_V1008 } from "./schemas/1008";
|
import { SavegameInterface_V1008 } from "./schemas/1008";
|
||||||
import { SavegameInterface_V1009 } from "./schemas/1009";
|
import { SavegameInterface_V1009 } from "./schemas/1009";
|
||||||
|
import { MODS } from "../mods/modloader";
|
||||||
|
import { SavegameInterface_V1010 } from "./schemas/1010";
|
||||||
|
|
||||||
const logger = createLogger("savegame");
|
const logger = createLogger("savegame");
|
||||||
|
|
||||||
@ -54,7 +56,7 @@ export class Savegame extends ReadWriteProxy {
|
|||||||
* @returns {number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
static getCurrentVersion() {
|
static getCurrentVersion() {
|
||||||
return 1009;
|
return 1010;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -103,6 +105,7 @@ export class Savegame extends ReadWriteProxy {
|
|||||||
usedInverseRotater: false,
|
usedInverseRotater: false,
|
||||||
},
|
},
|
||||||
lastUpdate: Date.now(),
|
lastUpdate: Date.now(),
|
||||||
|
mods: MODS.getModsListForSavegame(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,6 +162,10 @@ export class Savegame extends ReadWriteProxy {
|
|||||||
SavegameInterface_V1009.migrate1008to1009(data);
|
SavegameInterface_V1009.migrate1008to1009(data);
|
||||||
data.version = 1009;
|
data.version = 1009;
|
||||||
}
|
}
|
||||||
|
if (data.version === 1009) {
|
||||||
|
SavegameInterface_V1010.migrate1009to1010(data);
|
||||||
|
data.version = 1010;
|
||||||
|
}
|
||||||
|
|
||||||
return ExplainedResult.good();
|
return ExplainedResult.good();
|
||||||
}
|
}
|
||||||
@ -269,6 +276,7 @@ export class Savegame extends ReadWriteProxy {
|
|||||||
shadowData.dump = dump;
|
shadowData.dump = dump;
|
||||||
shadowData.lastUpdate = new Date().getTime();
|
shadowData.lastUpdate = new Date().getTime();
|
||||||
shadowData.version = this.getCurrentVersion();
|
shadowData.version = this.getCurrentVersion();
|
||||||
|
shadowData.mods = MODS.getModsListForSavegame();
|
||||||
|
|
||||||
const reader = this.getDumpReaderForExternalData(shadowData);
|
const reader = this.getDumpReaderForExternalData(shadowData);
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import { SavegameInterface_V1006 } from "./schemas/1006";
|
|||||||
import { SavegameInterface_V1007 } from "./schemas/1007";
|
import { SavegameInterface_V1007 } from "./schemas/1007";
|
||||||
import { SavegameInterface_V1008 } from "./schemas/1008";
|
import { SavegameInterface_V1008 } from "./schemas/1008";
|
||||||
import { SavegameInterface_V1009 } from "./schemas/1009";
|
import { SavegameInterface_V1009 } from "./schemas/1009";
|
||||||
|
import { SavegameInterface_V1010 } from "./schemas/1010";
|
||||||
|
|
||||||
/** @type {Object.<number, typeof BaseSavegameInterface>} */
|
/** @type {Object.<number, typeof BaseSavegameInterface>} */
|
||||||
export const savegameInterfaces = {
|
export const savegameInterfaces = {
|
||||||
@ -23,6 +24,7 @@ export const savegameInterfaces = {
|
|||||||
1007: SavegameInterface_V1007,
|
1007: SavegameInterface_V1007,
|
||||||
1008: SavegameInterface_V1008,
|
1008: SavegameInterface_V1008,
|
||||||
1009: SavegameInterface_V1009,
|
1009: SavegameInterface_V1009,
|
||||||
|
1010: SavegameInterface_V1010,
|
||||||
};
|
};
|
||||||
|
|
||||||
const logger = createLogger("savegame_interface_registry");
|
const logger = createLogger("savegame_interface_registry");
|
||||||
|
|||||||
@ -2,6 +2,14 @@
|
|||||||
* @typedef {import("../game/entity").Entity} Entity
|
* @typedef {import("../game/entity").Entity} Entity
|
||||||
*
|
*
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
|
* id: string;
|
||||||
|
* version: string;
|
||||||
|
* website: string;
|
||||||
|
* name: string;
|
||||||
|
* author: string;
|
||||||
|
* }[]} SavegameStoredMods
|
||||||
|
*
|
||||||
|
* @typedef {{
|
||||||
* failedMam: boolean,
|
* failedMam: boolean,
|
||||||
* trashedCount: number,
|
* trashedCount: number,
|
||||||
* usedInverseRotater: boolean
|
* usedInverseRotater: boolean
|
||||||
@ -25,6 +33,7 @@
|
|||||||
* dump: SerializedGame,
|
* dump: SerializedGame,
|
||||||
* stats: SavegameStats,
|
* stats: SavegameStats,
|
||||||
* lastUpdate: number,
|
* lastUpdate: number,
|
||||||
|
* mods: SavegameStoredMods
|
||||||
* }} SavegameData
|
* }} SavegameData
|
||||||
*
|
*
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
|
|||||||
24
src/js/savegame/schemas/1010.js
Normal file
24
src/js/savegame/schemas/1010.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { createLogger } from "../../core/logging.js";
|
||||||
|
import { SavegameInterface_V1009 } from "./1009.js";
|
||||||
|
|
||||||
|
const schema = require("./1010.json");
|
||||||
|
const logger = createLogger("savegame_interface/1010");
|
||||||
|
|
||||||
|
export class SavegameInterface_V1010 extends SavegameInterface_V1009 {
|
||||||
|
getVersion() {
|
||||||
|
return 1010;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSchemaUncached() {
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import("../savegame_typedefs.js").SavegameData} data
|
||||||
|
*/
|
||||||
|
static migrate1009to1010(data) {
|
||||||
|
logger.log("Migrating 1009 to 1010");
|
||||||
|
|
||||||
|
data.mods = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
5
src/js/savegame/schemas/1010.json
Normal file
5
src/js/savegame/schemas/1010.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [],
|
||||||
|
"additionalProperties": true
|
||||||
|
}
|
||||||
@ -20,6 +20,7 @@ import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs";
|
|||||||
import { MODS } from "../mods/modloader";
|
import { MODS } from "../mods/modloader";
|
||||||
import { PlatformWrapperImplBrowser } from "../platform/browser/wrapper";
|
import { PlatformWrapperImplBrowser } from "../platform/browser/wrapper";
|
||||||
import { PlatformWrapperImplElectron } from "../platform/electron/wrapper";
|
import { PlatformWrapperImplElectron } from "../platform/electron/wrapper";
|
||||||
|
import { Savegame } from "../savegame/savegame";
|
||||||
import { T } from "../translations";
|
import { T } from "../translations";
|
||||||
|
|
||||||
const trim = require("trim");
|
const trim = require("trim");
|
||||||
@ -615,11 +616,13 @@ export class MainMenuState extends GameState {
|
|||||||
const savegame = this.app.savegameMgr.getSavegameById(game.internalId);
|
const savegame = this.app.savegameMgr.getSavegameById(game.internalId);
|
||||||
savegame
|
savegame
|
||||||
.readAsync()
|
.readAsync()
|
||||||
|
.then(() => this.checkForModDifferences(savegame))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.moveToState("InGameState", {
|
this.moveToState("InGameState", {
|
||||||
savegame,
|
savegame,
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
this.dialogs.showWarning(
|
this.dialogs.showWarning(
|
||||||
T.dialogs.gameLoadFailure.title,
|
T.dialogs.gameLoadFailure.title,
|
||||||
@ -629,6 +632,57 @@ export class MainMenuState extends GameState {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Savegame} savegame
|
||||||
|
*/
|
||||||
|
checkForModDifferences(savegame) {
|
||||||
|
const difference = MODS.computeModDifference(savegame.currentData.mods);
|
||||||
|
|
||||||
|
if (difference.missing.length === 0 && difference.extra.length === 0) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
let dialogHtml = T.dialogs.modsDifference.desc;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import("../savegame/savegame_typedefs").SavegameStoredMods[0]} mod
|
||||||
|
*/
|
||||||
|
function formatMod(mod) {
|
||||||
|
return `
|
||||||
|
<div class="dialogModsMod">
|
||||||
|
<div class="name">${mod.name}</div>
|
||||||
|
<div class="version">${T.mods.version} ${mod.version}</div>
|
||||||
|
<button class="website styledButton" onclick="window.open('${mod.website.replace(
|
||||||
|
/"'/,
|
||||||
|
""
|
||||||
|
)}')">${T.mods.modWebsite}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (difference.missing.length > 0) {
|
||||||
|
dialogHtml += "<h3>" + T.dialogs.modsDifference.missingMods + "</h3>";
|
||||||
|
dialogHtml += difference.missing.map(formatMod).join("<br>");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (difference.extra.length > 0) {
|
||||||
|
dialogHtml += "<h3>" + T.dialogs.modsDifference.newMods + "</h3>";
|
||||||
|
dialogHtml += difference.extra.map(formatMod).join("<br>");
|
||||||
|
}
|
||||||
|
|
||||||
|
const signals = this.dialogs.showWarning(T.dialogs.modsDifference.title, dialogHtml, [
|
||||||
|
"cancel:good",
|
||||||
|
"continue:bad",
|
||||||
|
]);
|
||||||
|
|
||||||
|
return new Promise(resolve => {
|
||||||
|
signals.continue.add(resolve);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {SavegameMetadata} game
|
* @param {SavegameMetadata} game
|
||||||
*/
|
*/
|
||||||
@ -754,6 +808,7 @@ export class MainMenuState extends GameState {
|
|||||||
savegame
|
savegame
|
||||||
.readAsync()
|
.readAsync()
|
||||||
.then(() => this.app.adProvider.showVideoAd())
|
.then(() => this.app.adProvider.showVideoAd())
|
||||||
|
.then(() => this.checkForModDifferences(savegame))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.moveToState("InGameState", {
|
this.moveToState("InGameState", {
|
||||||
savegame,
|
savegame,
|
||||||
|
|||||||
@ -74,7 +74,7 @@ export class ModsState extends TextualGameState {
|
|||||||
<div class="mainInfo">
|
<div class="mainInfo">
|
||||||
<span class="name">${mod.metadata.name}</span>
|
<span class="name">${mod.metadata.name}</span>
|
||||||
<span class="description">${mod.metadata.description}</span>
|
<span class="description">${mod.metadata.description}</span>
|
||||||
<a class="website" href="${mod.metadata.website}" target="_blank">Website</a>
|
<a class="website" href="${mod.metadata.website}" target="_blank">${T.mods.modWebsite}</a>
|
||||||
</div>
|
</div>
|
||||||
<span class="version"><strong>${T.mods.version}</strong>${mod.metadata.version}</span>
|
<span class="version"><strong>${T.mods.version}</strong>${mod.metadata.version}</span>
|
||||||
<span class="author"><strong>${T.mods.author}</strong>${mod.metadata.author}</span>
|
<span class="author"><strong>${T.mods.author}</strong>${mod.metadata.author}</span>
|
||||||
|
|||||||
@ -423,6 +423,14 @@ dialogs:
|
|||||||
desc: >-
|
desc: >-
|
||||||
Are you sure you want to delete '<title>'? This can not be undone!
|
Are you sure you want to delete '<title>'? This can not be undone!
|
||||||
|
|
||||||
|
modsDifference:
|
||||||
|
title: Mod Warning
|
||||||
|
desc: >-
|
||||||
|
The currently installed mods differ from the mods the savegame was created with.
|
||||||
|
This might cause the savegame to break or not load at all. Are you sure you want to continue?
|
||||||
|
missingMods: Missing Mods
|
||||||
|
newMods: Newly installed Mods
|
||||||
|
|
||||||
ingame:
|
ingame:
|
||||||
# This is shown in the top left corner and displays useful keybindings in
|
# This is shown in the top left corner and displays useful keybindings in
|
||||||
# every situation
|
# every situation
|
||||||
@ -1096,6 +1104,7 @@ mods:
|
|||||||
|
|
||||||
author: Author
|
author: Author
|
||||||
version: Version
|
version: Version
|
||||||
|
modWebsite: Website
|
||||||
openFolder: Open Mods Folder
|
openFolder: Open Mods Folder
|
||||||
folderOnlyStandalone: Opening the mod folder is only possible when running the standalone.
|
folderOnlyStandalone: Opening the mod folder is only possible when running the standalone.
|
||||||
browseMods: Browse Mods
|
browseMods: Browse Mods
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user