1
0
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:
Tobias Springer
2020-05-09 16:45:23 +02:00
commit 93c6ea683d
304 changed files with 56031 additions and 0 deletions

View File

@@ -0,0 +1,191 @@
/* typehints:start */
import { Application } from "../application";
/* typehints:end */
import { ReadWriteProxy } from "../core/read_write_proxy";
import { BoolSetting, EnumSetting, BaseSetting } from "./setting_types";
import { createLogger } from "../core/logging";
import { ExplainedResult } from "../core/explained_result";
const logger = createLogger("application_settings");
const categoryGame = "game";
const categoryApp = "app";
export const uiScales = [
{
id: "super_small",
size: 0.6,
label: "Super small",
},
{
id: "small",
size: 0.8,
label: "Small",
},
{
id: "regular",
size: 1,
label: "Regular",
},
{
id: "large",
size: 1.2,
label: "Large",
},
{
id: "huge",
size: 1.4,
label: "Huge",
},
];
/** @type {Array<BaseSetting>} */
export const allApplicationSettings = [
new EnumSetting("uiScale", {
options: uiScales.sort((a, b) => a.size - b.size),
valueGetter: scale => scale.id,
textGetter: scale => scale.label,
category: categoryApp,
restartRequired: false,
changeCb:
/**
* @param {Application} app
*/
(app, id) => app.updateAfterUiScaleChanged(),
}),
new BoolSetting(
"fullscreen",
categoryApp,
/**
* @param {Application} app
*/
(app, value) => {
if (app.platformWrapper.getSupportsFullscreen()) {
app.platformWrapper.setFullscreen(value);
}
},
G_IS_STANDALONE
),
// GAME
];
export function getApplicationSettingById(id) {
return allApplicationSettings.find(setting => setting.id === id);
}
class SettingsStorage {
constructor() {
this.uiScale = "regular";
this.fullscreen = G_IS_STANDALONE;
}
}
export class ApplicationSettings extends ReadWriteProxy {
constructor(app) {
super(app, "app_settings.bin");
}
initialize() {
// Read and directly write latest data back
return this.readAsync().then(() => this.writeAsync());
}
save() {
return this.writeAsync();
}
// Getters
/**
* @returns {SettingsStorage}
*/
getAllSettings() {
return this.getCurrentData().settings;
}
/**
* @param {string} key
*/
getSetting(key) {
assert(this.getAllSettings().hasOwnProperty(key), "Setting not known: " + key);
return this.getAllSettings()[key];
}
getInterfaceScaleId() {
if (!this.currentData) {
// Not initialized yet
return "regular";
}
return this.getAllSettings().uiScale;
}
getInterfaceScaleValue() {
const id = this.getInterfaceScaleId();
for (let i = 0; i < uiScales.length; ++i) {
if (uiScales[i].id === id) {
return uiScales[i].size;
}
}
logger.error("Unknown ui scale id:", id);
return 1;
}
getIsFullScreen() {
return this.getAllSettings().fullscreen;
}
// Setters
/**
* @param {string} key
* @param {string|boolean} value
*/
updateSetting(key, value) {
assert(this.getAllSettings().hasOwnProperty(key), "Setting not known: " + key);
this.getAllSettings()[key] = value;
return this.writeAsync();
}
// RW Proxy impl
verify(data) {
if (!data.settings) {
return ExplainedResult.bad("missing key 'settings'");
}
if (typeof data.settings !== "object") {
return ExplainedResult.bad("Bad settings object");
}
const settings = data.settings;
for (let i = 0; i < allApplicationSettings.length; ++i) {
const setting = allApplicationSettings[i];
const storedValue = settings[setting.id];
if (!setting.validate(storedValue)) {
return ExplainedResult.bad("Bad setting value for " + setting.id + ": " + storedValue);
}
}
return ExplainedResult.good();
}
getDefaultData() {
return {
version: this.getCurrentVersion(),
settings: new SettingsStorage(),
};
}
getCurrentVersion() {
return 1;
}
migrate(data) {
// Simply reset
if (data.version < 1) {
data.settings = new SettingsStorage();
data.version = 1;
}
return ExplainedResult.good();
}
}

View File

@@ -0,0 +1,208 @@
/* typehints:start */
import { Application } from "../application";
/* typehints:end */
import { createLogger } from "../core/logging";
const logger = createLogger("setting_types");
export class BaseSetting {
/**
*
* @param {string} id
* @param {string} categoryId
* @param {function(Application, any):void} changeCb
* @param {boolean} enabled
*/
constructor(id, categoryId, changeCb, enabled) {
this.id = id;
this.categoryId = categoryId;
this.changeCb = changeCb;
this.enabled = enabled;
/** @type {Application} */
this.app = null;
this.element = null;
this.dialogs = null;
}
/**
* @param {Application} app
* @param {Element} element
* @param {HUDModalDialogs} dialogs
*/
bind(app, element, dialogs) {
this.app = app;
this.element = element;
this.dialogs = dialogs;
}
getHtml() {
abstract;
return "";
}
syncValueToElement() {
abstract;
}
modify() {
abstract;
}
showRestartRequiredDialog() {
const { restart } = this.dialogs.showInfo(
"Restart required",
"You need to restart the game to apply the settings.",
this.app.platformWrapper.getSupportsRestart() ? ["later:grey", "restart:misc"] : ["ok:good"]
);
if (restart) {
restart.add(() => this.app.platformWrapper.performRestart());
}
}
/**
* @param {any} value
* @returns {boolean}
*/
validate(value) {
abstract;
return false;
}
}
export class EnumSetting extends BaseSetting {
constructor(
id,
{
options,
valueGetter,
textGetter,
descGetter = null,
category,
restartRequired = true,
iconPrefix = null,
changeCb = null,
magicValue = null,
enabled = true,
}
) {
super(id, category, changeCb, enabled);
this.options = options;
this.valueGetter = valueGetter;
this.textGetter = textGetter;
this.descGetter = descGetter || (() => null);
this.restartRequired = restartRequired;
this.iconPrefix = iconPrefix;
this.magicValue = magicValue;
}
getHtml() {
return `
<div class="setting cardbox ${this.enabled ? "enabled" : "disabled"}">
<div class="row">
<label>TODO: SETTING TITLE</label>
<div class="value enum" data-setting="${this.id}"></div>
</div>
<div class="desc">
TODO: SETTING DESC
</div>
</div>`;
}
validate(value) {
if (value === this.magicValue) {
return true;
}
const availableValues = this.options.map(option => this.valueGetter(option));
if (availableValues.indexOf(value) < 0) {
logger.error(
"Value '" + value + "' is not contained in available values:",
availableValues,
"of",
this.id
);
return false;
}
return true;
}
syncValueToElement() {
const value = this.app.settings.getSetting(this.id);
let displayText = "???";
const matchedInstance = this.options.find(data => this.valueGetter(data) === value);
if (matchedInstance) {
displayText = this.textGetter(matchedInstance);
} else {
logger.warn("Setting value", value, "not found for", this.id, "!");
}
this.element.innerText = displayText;
}
modify() {
const { optionSelected } = this.dialogs.showOptionChooser("TODO: SETTING TITLE", {
active: this.app.settings.getSetting(this.id),
options: this.options.map(option => ({
value: this.valueGetter(option),
text: this.textGetter(option),
desc: this.descGetter(option),
iconPrefix: this.iconPrefix,
})),
});
optionSelected.add(value => {
this.app.settings.updateSetting(this.id, value);
this.syncValueToElement();
if (this.restartRequired) {
this.showRestartRequiredDialog();
}
if (this.changeCb) {
this.changeCb(this.app, value);
}
}, this);
}
}
export class BoolSetting extends BaseSetting {
constructor(id, category, changeCb = null, enabled = true) {
super(id, category, changeCb, enabled);
}
getHtml() {
return `
<div class="setting cardbox ${this.enabled ? "enabled" : "disabled"}">
<div class="row">
<label>TODO: SETTING TITLE</label>
<div class="value checkbox checked" data-setting="${this.id}">
<span class="knob"></span>
</div>
</div>
<div class="desc">
TODO: SETTING DESC
</div>
</div>`;
}
syncValueToElement() {
const value = this.app.settings.getSetting(this.id);
this.element.classList.toggle("checked", value);
}
modify() {
const newValue = !this.app.settings.getSetting(this.id);
this.app.settings.updateSetting(this.id, newValue);
this.syncValueToElement();
if (this.changeCb) {
this.changeCb(this.app, newValue);
}
}
validate(value) {
return typeof value === "boolean";
}
}