mirror of
https://github.com/tobspr/shapez.io.git
synced 2026-03-02 03:39:21 +00:00
Initial commit
This commit is contained in:
443
src/js/states/ingame.js
Normal file
443
src/js/states/ingame.js
Normal file
@@ -0,0 +1,443 @@
|
||||
import { APPLICATION_ERROR_OCCURED } from "../core/error_handler";
|
||||
import { GameState } from "../core/game_state";
|
||||
import { logSection, createLogger } from "../core/logging";
|
||||
import { waitNextFrame } from "../core/utils";
|
||||
import { globalConfig } from "../core/config";
|
||||
import { GameLoadingOverlay } from "../game/game_loading_overlay";
|
||||
import { KeyActionMapper } from "../game/key_action_mapper";
|
||||
import { Savegame } from "../savegame/savegame";
|
||||
import { GameCore } from "../game/core";
|
||||
import { MUSIC } from "../platform/sound";
|
||||
|
||||
const logger = createLogger("state/ingame");
|
||||
|
||||
// Different sub-states
|
||||
const stages = {
|
||||
s3_createCore: "🌈 3: Create core",
|
||||
s4_A_initEmptyGame: "🌈 4/A: Init empty game",
|
||||
s4_B_resumeGame: "🌈 4/B: Resume game",
|
||||
|
||||
s5_firstUpdate: "🌈 5: First game update",
|
||||
s6_postLoadHook: "🌈 6: Post load hook",
|
||||
s7_warmup: "🌈 7: Warmup",
|
||||
|
||||
s10_gameRunning: "🌈 10: Game finally running",
|
||||
|
||||
leaving: "🌈 Saving, then leaving the game",
|
||||
destroyed: "🌈 DESTROYED: Core is empty and waits for state leave",
|
||||
initFailed: "🌈 ERROR: Initialization failed!",
|
||||
};
|
||||
|
||||
export const gameCreationAction = {
|
||||
new: "new-game",
|
||||
resume: "resume-game",
|
||||
};
|
||||
|
||||
// Typehints
|
||||
export class GameCreationPayload {
|
||||
constructor() {
|
||||
/** @type {boolean|undefined} */
|
||||
this.fastEnter;
|
||||
|
||||
/** @type {Savegame} */
|
||||
this.savegame;
|
||||
}
|
||||
}
|
||||
|
||||
export class InGameState extends GameState {
|
||||
constructor() {
|
||||
super("InGameState");
|
||||
|
||||
/** @type {GameCreationPayload} */
|
||||
this.creationPayload = null;
|
||||
|
||||
// Stores current stage
|
||||
this.stage = "";
|
||||
|
||||
/** @type {GameCore} */
|
||||
this.core = null;
|
||||
|
||||
/** @type {KeyActionMapper} */
|
||||
this.keyActionMapper = null;
|
||||
|
||||
/** @type {GameLoadingOverlay} */
|
||||
this.loadingOverlay = null;
|
||||
|
||||
/** @type {Savegame} */
|
||||
this.savegame;
|
||||
|
||||
this.boundInputFilter = this.filterInput.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches the game into another sub-state
|
||||
* @param {string} stage
|
||||
*/
|
||||
switchStage(stage) {
|
||||
assert(stage, "Got empty stage");
|
||||
if (stage !== this.stage) {
|
||||
this.stage = stage;
|
||||
logger.log(this.stage);
|
||||
return true;
|
||||
} else {
|
||||
// log(this, "Re entering", stage);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// GameState implementation
|
||||
getInnerHTML() {
|
||||
return "";
|
||||
}
|
||||
|
||||
getThemeMusic() {
|
||||
// set later
|
||||
return MUSIC.gameBg;
|
||||
}
|
||||
|
||||
onBeforeExit() {
|
||||
logger.log("Saving before quitting");
|
||||
return this.doSave(true, true).then(() => {
|
||||
logger.log(this, "Successfully saved");
|
||||
// this.stageDestroyed();
|
||||
});
|
||||
}
|
||||
|
||||
onAppPause() {
|
||||
if (this.stage === stages.s10_gameRunning) {
|
||||
logger.log("Saving because app got paused");
|
||||
this.doSave(true, true);
|
||||
}
|
||||
}
|
||||
|
||||
getHasFadeIn() {
|
||||
return false;
|
||||
}
|
||||
|
||||
getPauseOnFocusLost() {
|
||||
return !this.isMultiplayer();
|
||||
}
|
||||
|
||||
getHasUnloadConfirmation() {
|
||||
return true;
|
||||
}
|
||||
|
||||
onLeave() {
|
||||
if (this.core) {
|
||||
this.stageDestroyed();
|
||||
}
|
||||
this.app.inputMgr.dismountFilter(this.boundInputFilter);
|
||||
}
|
||||
|
||||
onResized(w, h) {
|
||||
super.onResized(w, h);
|
||||
if (this.stage === stages.s10_gameRunning) {
|
||||
this.core.resize(w, h);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- End of GameState implementation
|
||||
|
||||
/**
|
||||
* Goes back to the menu state
|
||||
*/
|
||||
goBackToMenu() {
|
||||
this.saveThenGoToState("MainMenuState");
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves to a state outside of the game
|
||||
* @param {string} stateId
|
||||
* @param {any=} payload
|
||||
*/
|
||||
saveThenGoToState(stateId, payload) {
|
||||
if (this.stage === stages.leaving || this.stage === stages.destroyed) {
|
||||
logger.warn(
|
||||
"Tried to leave game twice or during destroy:",
|
||||
this.stage,
|
||||
"(attempted to move to",
|
||||
stateId,
|
||||
")"
|
||||
);
|
||||
return;
|
||||
}
|
||||
this.stageLeavingGame();
|
||||
this.doSave(false, true).then(() => {
|
||||
this.stageDestroyed();
|
||||
this.moveToState(stateId, payload);
|
||||
});
|
||||
}
|
||||
|
||||
onBackButton() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the game somehow failed to initialize. Resets everything to basic state and
|
||||
* then goes to the main menu, showing the error
|
||||
* @param {string} err
|
||||
*/
|
||||
onInitializationFailure(err) {
|
||||
if (this.switchStage(stages.initFailed)) {
|
||||
logger.error("Init failure:", err);
|
||||
this.stageDestroyed();
|
||||
this.moveToState("MainMenuState", { loadError: err });
|
||||
}
|
||||
}
|
||||
|
||||
// STAGES
|
||||
|
||||
/**
|
||||
* Creates the game core instance, and thus the root
|
||||
*/
|
||||
stage3CreateCore() {
|
||||
if (this.switchStage(stages.s3_createCore)) {
|
||||
logger.log("Creating new game core");
|
||||
this.core = new GameCore(this.app);
|
||||
|
||||
this.core.initializeRoot(this, this.savegame);
|
||||
|
||||
if (this.savegame.hasGameDump()) {
|
||||
this.stage4bResumeGame();
|
||||
} else {
|
||||
this.stage4aInitEmptyGame();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new empty game
|
||||
*/
|
||||
stage4aInitEmptyGame() {
|
||||
if (this.switchStage(stages.s4_A_initEmptyGame)) {
|
||||
this.core.initNewGame();
|
||||
this.stage5FirstUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes an existing game
|
||||
*/
|
||||
stage4bResumeGame() {
|
||||
if (this.switchStage(stages.s4_B_resumeGame)) {
|
||||
if (!this.core.initExistingGame()) {
|
||||
this.onInitializationFailure("Savegame is corrupt and can not be restored.");
|
||||
return;
|
||||
}
|
||||
this.stage5FirstUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the first game update on the game which initializes most caches
|
||||
*/
|
||||
stage5FirstUpdate() {
|
||||
if (this.switchStage(stages.s5_firstUpdate)) {
|
||||
this.core.root.logicInitialized = true;
|
||||
this.core.updateLogic();
|
||||
this.stage6PostLoadHook();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the post load hook, this means that we have loaded the game, and all systems
|
||||
* can operate and start to work now.
|
||||
*/
|
||||
stage6PostLoadHook() {
|
||||
if (this.switchStage(stages.s6_postLoadHook)) {
|
||||
logger.log("Post load hook");
|
||||
this.core.postLoadHook();
|
||||
this.stage7Warmup();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This makes the game idle and draw for a while, because we run most code this way
|
||||
* the V8 engine can already start to optimize it. Also this makes sure the resources
|
||||
* are in the VRAM and we have a smooth experience once we start.
|
||||
*/
|
||||
stage7Warmup() {
|
||||
if (this.switchStage(stages.s7_warmup)) {
|
||||
if (G_IS_DEV && globalConfig.debug.noArtificialDelays) {
|
||||
this.warmupTimeSeconds = 0.05;
|
||||
} else {
|
||||
if (this.creationPayload.fastEnter) {
|
||||
this.warmupTimeSeconds = globalConfig.warmupTimeSecondsFast;
|
||||
} else {
|
||||
this.warmupTimeSeconds = globalConfig.warmupTimeSecondsRegular;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The final stage where this game is running and updating regulary.
|
||||
*/
|
||||
stage10GameRunning() {
|
||||
if (this.switchStage(stages.s10_gameRunning)) {
|
||||
this.core.root.signals.readyToRender.dispatch();
|
||||
|
||||
logSection("GAME STARTED", "#26a69a");
|
||||
|
||||
// Initial resize, might have changed during loading (this is possible)
|
||||
this.core.resize(this.app.screenWidth, this.app.screenHeight);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This stage destroys the whole game, used to cleanup
|
||||
*/
|
||||
stageDestroyed() {
|
||||
if (this.switchStage(stages.destroyed)) {
|
||||
// Cleanup all api calls
|
||||
this.cancelAllAsyncOperations();
|
||||
|
||||
if (this.syncer) {
|
||||
this.syncer.cancelSync();
|
||||
this.syncer = null;
|
||||
}
|
||||
|
||||
// Cleanup core
|
||||
if (this.core) {
|
||||
this.core.destruct();
|
||||
this.core = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When leaving the game
|
||||
*/
|
||||
stageLeavingGame() {
|
||||
if (this.switchStage(stages.leaving)) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
// END STAGES
|
||||
|
||||
/**
|
||||
* Filters the input (keybindings)
|
||||
*/
|
||||
filterInput() {
|
||||
return this.stage === stages.s10_gameRunning;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GameCreationPayload} payload
|
||||
*/
|
||||
onEnter(payload) {
|
||||
this.app.inputMgr.installFilter(this.boundInputFilter);
|
||||
|
||||
this.creationPayload = payload;
|
||||
this.savegame = payload.savegame;
|
||||
|
||||
this.loadingOverlay = new GameLoadingOverlay(this.app, this.getDivElement());
|
||||
this.loadingOverlay.showBasic();
|
||||
|
||||
// Remove unneded default element
|
||||
document.body.querySelector(".modalDialogParent").remove();
|
||||
|
||||
this.asyncChannel.watch(waitNextFrame()).then(() => this.stage3CreateCore());
|
||||
}
|
||||
|
||||
/**
|
||||
* Render callback
|
||||
* @param {number} dt
|
||||
*/
|
||||
onRender(dt) {
|
||||
if (APPLICATION_ERROR_OCCURED) {
|
||||
// Application somehow crashed, do not do anything
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.stage === stages.s7_warmup) {
|
||||
this.core.draw();
|
||||
this.warmupTimeSeconds -= dt / 1000.0;
|
||||
if (this.warmupTimeSeconds < 0) {
|
||||
logger.log("Warmup completed");
|
||||
this.stage10GameRunning();
|
||||
}
|
||||
}
|
||||
|
||||
// // Check if we can show an ad
|
||||
// // DISABLED
|
||||
// if (this.stage === stages.s10_gameRunning && !this.core.root.hud.parts.processingOverlay.hasTasks()) {
|
||||
// if (this.app.isRenderable() && this.app.adProvider.getCanShowVideoAd()) {
|
||||
// this.saveThenGoToState("WatchAdState", {
|
||||
// nextStateId: "RunningGameState",
|
||||
// nextStatePayload: this.creationPayload,
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
if (this.stage === stages.s10_gameRunning) {
|
||||
this.core.tick(dt);
|
||||
}
|
||||
|
||||
// If the stage is still active (This might not be the case if tick() moved us to game over)
|
||||
if (this.stage === stages.s10_gameRunning) {
|
||||
// Only draw if page visible
|
||||
if (this.app.pageVisible) {
|
||||
this.core.draw();
|
||||
}
|
||||
|
||||
this.loadingOverlay.removeIfAttached();
|
||||
} else {
|
||||
if (!this.loadingOverlay.isAttached()) {
|
||||
this.loadingOverlay.showBasic();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onBackgroundTick(dt) {
|
||||
this.onRender(dt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the game
|
||||
* @param {boolean=} syncWithServer
|
||||
* @param {boolean} force
|
||||
*/
|
||||
|
||||
doSave(syncWithServer = true, force = false) {
|
||||
// TODO
|
||||
return;
|
||||
|
||||
if (!this.savegame || !this.savegame.isSaveable()) {
|
||||
// Can not save in multiplayer
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if (APPLICATION_ERROR_OCCURED) {
|
||||
logger.warn("skipping save because application crashed");
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if (
|
||||
this.stage !== stages.s10_gameRunning &&
|
||||
this.stage !== stages.s7_warmup &&
|
||||
this.stage !== stages.leaving
|
||||
) {
|
||||
logger.warn("Skipping save because game is not ready");
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// First update the game data
|
||||
|
||||
logger.log("Starting to save game ...");
|
||||
this.savegame.updateData(this.core.root);
|
||||
|
||||
let savePromise = this.savegame.writeSavegameAndMetadata();
|
||||
|
||||
if (syncWithServer) {
|
||||
// Sync in parallel
|
||||
// @ts-ignore
|
||||
savePromise = savePromise.then(() => this.syncer.sync(this.core, this.savegame, force));
|
||||
}
|
||||
|
||||
return savePromise.catch(err => {
|
||||
logger.warn("Failed to save:", err);
|
||||
});
|
||||
}
|
||||
}
|
||||
52
src/js/states/main_menu.js
Normal file
52
src/js/states/main_menu.js
Normal file
@@ -0,0 +1,52 @@
|
||||
import { GameState } from "../core/game_state";
|
||||
import { cachebust } from "../core/cachebust";
|
||||
import { globalConfig } from "../core/config";
|
||||
|
||||
export class MainMenuState extends GameState {
|
||||
constructor() {
|
||||
super("MainMenuState");
|
||||
}
|
||||
|
||||
getInnerHTML() {
|
||||
return `
|
||||
<div class="logo">
|
||||
<img src="${cachebust("res/logo.png")}" alt="shapez.io Logo">
|
||||
</div>
|
||||
|
||||
|
||||
<div class="mainContainer">
|
||||
<button class="playButton styledButton">Play</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
onBackButton() {
|
||||
this.app.platformWrapper.exitApp();
|
||||
}
|
||||
|
||||
onEnter(payload) {
|
||||
if (payload.loadError) {
|
||||
alert("Error while loading game: " + payload.loadError);
|
||||
}
|
||||
|
||||
const qs = this.htmlElement.querySelector.bind(this.htmlElement);
|
||||
this.trackClicks(qs(".mainContainer .playButton"), this.onPlayButtonClicked);
|
||||
|
||||
if (G_IS_DEV && globalConfig.debug.fastGameEnter) {
|
||||
this.onPlayButtonClicked();
|
||||
}
|
||||
}
|
||||
|
||||
onPlayButtonClicked() {
|
||||
const savegame = this.app.savegameMgr.createNewSavegame();
|
||||
|
||||
this.app.analytics.trackUiClick("startgame");
|
||||
this.moveToState("InGameState", {
|
||||
savegame,
|
||||
});
|
||||
}
|
||||
|
||||
onLeave() {
|
||||
// this.dialogs.cleanup();
|
||||
}
|
||||
}
|
||||
238
src/js/states/preload.js
Normal file
238
src/js/states/preload.js
Normal file
@@ -0,0 +1,238 @@
|
||||
import { GameState } from "../core/game_state";
|
||||
import { createLogger } from "../core/logging";
|
||||
import { findNiceValue, waitNextFrame } from "../core/utils";
|
||||
import { cachebust } from "../core/cachebust";
|
||||
import { PlatformWrapperImplBrowser } from "../platform/browser/wrapper";
|
||||
|
||||
const logger = createLogger("state/preload");
|
||||
|
||||
export class PreloadState extends GameState {
|
||||
constructor() {
|
||||
super("PreloadState");
|
||||
}
|
||||
|
||||
getInnerHTML() {
|
||||
return `
|
||||
<div class="loadingImage"></div>
|
||||
<div class="loadingStatus">
|
||||
<span class="desc">Booting</span>
|
||||
<span class="bar">
|
||||
<span class="inner" style="width: 0%"></span>
|
||||
<span class="status">0%</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
getThemeMusic() {
|
||||
return null;
|
||||
}
|
||||
|
||||
getHasFadeIn() {
|
||||
return false;
|
||||
}
|
||||
|
||||
onEnter(payload) {
|
||||
this.htmlElement.classList.add("prefab_LoadingState");
|
||||
|
||||
const elementsToRemove = ["#loadingPreload", "#fontPreload"];
|
||||
for (let i = 0; i < elementsToRemove.length; ++i) {
|
||||
const elem = document.querySelector(elementsToRemove[i]);
|
||||
if (elem) {
|
||||
elem.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// this.dialogs = new HUDModalDialogs(null, this.app);
|
||||
// const dialogsElement = document.body.querySelector(".modalDialogParent");
|
||||
// this.dialogs.initializeToElement(dialogsElement);
|
||||
|
||||
this.statusText = this.htmlElement.querySelector(".loadingStatus > .desc");
|
||||
this.statusBar = this.htmlElement.querySelector(".loadingStatus > .bar > .inner");
|
||||
this.statusBarText = this.htmlElement.querySelector(".loadingStatus > .bar > .status");
|
||||
this.currentStatus = "booting";
|
||||
this.currentIndex = 0;
|
||||
|
||||
this.startLoading();
|
||||
}
|
||||
|
||||
onLeave() {
|
||||
// this.dialogs.cleanup();
|
||||
}
|
||||
|
||||
startLoading() {
|
||||
this.setStatus("Booting")
|
||||
|
||||
.then(() => this.setStatus("Creating platform wrapper"))
|
||||
.then(() => this.app.platformWrapper.initialize())
|
||||
|
||||
.then(() => this.setStatus("Initializing libraries"))
|
||||
.then(() => this.app.analytics.initialize())
|
||||
.then(() => this.app.gameAnalytics.initialize())
|
||||
|
||||
.then(() => this.setStatus("Initializing local storage"))
|
||||
.then(() => {
|
||||
const wrapper = this.app.platformWrapper;
|
||||
if (wrapper instanceof PlatformWrapperImplBrowser) {
|
||||
try {
|
||||
window.localStorage.setItem("local_storage_test", "1");
|
||||
window.localStorage.removeItem("local_storage_test");
|
||||
} catch (ex) {
|
||||
logger.error("Failed to read/write local storage:", ex);
|
||||
return new Promise(() => {
|
||||
alert(`Your brower does not support thirdparty cookies or you have disabled it in your security settings.\n\n
|
||||
In Chrome this setting is called "Block third-party cookies and site data".\n\n
|
||||
Please allow third party cookies and then reload the page.`);
|
||||
// Never return
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
.then(() => this.setStatus("Creating storage"))
|
||||
.then(() => {
|
||||
return this.app.storage.initialize();
|
||||
})
|
||||
|
||||
.then(() => this.setStatus("Initializing settings"))
|
||||
.then(() => {
|
||||
return this.app.settings.initialize();
|
||||
})
|
||||
.then(() => {
|
||||
// Make sure the app pickups the right size
|
||||
this.app.updateAfterUiScaleChanged();
|
||||
})
|
||||
|
||||
.then(() => {
|
||||
// Initialize fullscreen
|
||||
if (this.app.platformWrapper.getSupportsFullscreen()) {
|
||||
this.app.platformWrapper.setFullscreen(this.app.settings.getIsFullScreen());
|
||||
}
|
||||
})
|
||||
|
||||
.then(() => this.setStatus("Initializing sounds"))
|
||||
.then(() => {
|
||||
// Notice: We don't await the sounds loading itself
|
||||
return this.app.sound.initialize();
|
||||
})
|
||||
|
||||
.then(() => {
|
||||
this.app.backgroundResourceLoader.startLoading();
|
||||
})
|
||||
|
||||
.then(() => this.setStatus("Initializing savegame"))
|
||||
.then(() => {
|
||||
return this.app.savegameMgr.initialize().catch(err => {
|
||||
logger.error("Failed to initialize savegames:", err);
|
||||
return new Promise(resolve => {
|
||||
// const { ok } = this.dialogs.showWarning(
|
||||
// T.preload.savegame_corrupt_dialog.title,
|
||||
// T.preload.savegame_corrupt_dialog.content,
|
||||
// ["ok:good"]
|
||||
// );
|
||||
// ok.add(resolve);
|
||||
alert("Your savegames failed to load. They might not show up. Sorry!");
|
||||
});
|
||||
});
|
||||
})
|
||||
|
||||
.then(() => this.setStatus("Downloading resources"))
|
||||
.then(() => {
|
||||
return this.app.backgroundResourceLoader.getPromiseForBareGame();
|
||||
})
|
||||
|
||||
.then(() => this.setStatus("Launching"))
|
||||
.then(
|
||||
() => {
|
||||
this.moveToState("MainMenuState");
|
||||
},
|
||||
err => {
|
||||
this.showFailMessage(err);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
setStatus(text) {
|
||||
logger.log("✅ " + text);
|
||||
this.currentIndex += 1;
|
||||
this.currentStatus = text;
|
||||
this.statusText.innerText = text;
|
||||
|
||||
const numSteps = 10; // FIXME
|
||||
|
||||
const percentage = (this.currentIndex / numSteps) * 100.0;
|
||||
this.statusBar.style.width = percentage + "%";
|
||||
this.statusBarText.innerText = findNiceValue(percentage) + "%";
|
||||
|
||||
if (G_IS_DEV) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return waitNextFrame();
|
||||
}
|
||||
|
||||
showFailMessage(text) {
|
||||
logger.error("App init failed:", text);
|
||||
|
||||
const email = "bugs@shapez.io";
|
||||
|
||||
const subElement = document.createElement("div");
|
||||
subElement.classList.add("failureBox");
|
||||
|
||||
subElement.innerHTML = `
|
||||
<div class="logo">
|
||||
<img src="${cachebust("res/logo.png")}" alt="Shapez.io Logo">
|
||||
</div>
|
||||
<div class="failureInner">
|
||||
<div class="errorHeader">
|
||||
Failed to initialize application!
|
||||
</div>
|
||||
<div class="errorMessage">
|
||||
${this.currentStatus} failed:<br/>
|
||||
${text}
|
||||
</div>
|
||||
|
||||
<div class="supportHelp">
|
||||
Please send me an email with steps to reproduce and what you did before this happened:
|
||||
<br /><a class="email" href="mailto:${email}?subject=App%20does%20not%20launch">${email}</a>
|
||||
</div>
|
||||
|
||||
<div class="lower">
|
||||
<button class="resetApp styledButton">Reset App</button>
|
||||
<i>Build ${G_BUILD_VERSION} @ ${G_BUILD_COMMIT_HASH}</i>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
this.htmlElement.classList.add("failure");
|
||||
this.htmlElement.appendChild(subElement);
|
||||
|
||||
const resetBtn = subElement.querySelector("button.resetApp");
|
||||
this.trackClicks(resetBtn, this.showResetConfirm);
|
||||
}
|
||||
|
||||
showResetConfirm() {
|
||||
if (confirm("Are you sure you want to reset the app? This will delete all your savegames")) {
|
||||
this.resetApp();
|
||||
}
|
||||
// const signals = this.dialogs.showWarning(T.preload.reset_app_warning.title, T.preload.reset_app_warning.desc, [
|
||||
// "delete:bad:timeout",
|
||||
// "cancel:good",
|
||||
// ]);
|
||||
// signals.delete.add(this.resetApp, this);
|
||||
}
|
||||
|
||||
resetApp() {
|
||||
this.app.settings
|
||||
.resetEverythingAsync()
|
||||
.then(() => {
|
||||
this.app.savegameMgr.resetEverythingAsync();
|
||||
})
|
||||
.then(() => {
|
||||
this.app.settings.resetEverythingAsync();
|
||||
})
|
||||
.then(() => {
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user