diff --git a/README.md b/README.md index 94e3b3f3..f5c9ec84 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@ # shapez.io shapez.io Logo -shapez.io Screenshot This is the source code for shapez.io, an open source base building game inspired by Factorio. - Your goal is to produce shapes by cutting, rotating, merging and painting parts of shapes. -## Playing - -You can already play it [here](https://shapez.io). +- [Trello Board & Roadmap](https://trello.com/b/ISQncpJP/shapezio) +- [Free web version](https://shapez.io) +- [itch.io Page](https://tobspr.itch.io/shapezio) +- [Steam Page](https://steam.shapez.io) +- [Official Discord](https://discord.com/invite/HN7EVzV) <- _Highly recommended to join!_ ## Building @@ -46,3 +46,5 @@ This project is based on ES5. Some ES2015 features are used but most of them are For most assets I use Adobe Photoshop, you can find them in `assets/`. You will need a Texture Packer license in order to regenerate the atlas. If you don't have one but want to contribute assets, let me know and I might compile it for you. I'm currently switching to an open source solution but I can't give an estimate when thats done. + +shapez.io Screenshot diff --git a/gulp/.itch.toml b/gulp/.itch.toml new file mode 100644 index 00000000..2556ac9c --- /dev/null +++ b/gulp/.itch.toml @@ -0,0 +1,9 @@ +[[actions]] +name = "play" +path = "shapezio.exe" +platform = "windows" + +[[actions]] +name = "play" +path = "play.sh" +platform = "linux" diff --git a/gulp/standalone.js b/gulp/standalone.js index 34001e07..6c5995bd 100644 --- a/gulp/standalone.js +++ b/gulp/standalone.js @@ -121,6 +121,8 @@ function gulptasksStandalone($, gulp, buildFolder) { * @param {boolean=} isRelease */ function packageStandalone(platform, arch, cb, isRelease = false) { + const tomlFile = fs.readFileSync(path.join(__dirname, ".itch.toml")); + packager({ dir: tempDestBuildDir, appCopyright: "Tobias Springer", @@ -150,17 +152,25 @@ function gulptasksStandalone($, gulp, buildFolder) { fs.readFileSync(path.join(__dirname, "..", "LICENSE")) ); - const playablePath = appPath + "_playable"; - fse.copySync(appPath, playablePath); - fs.writeFileSync(path.join(playablePath, "steam_appid.txt"), "1134480"); - fs.writeFileSync( - path.join(playablePath, "play.bat"), - "start shapezio --dev --disable-direct-composition --in-process-gpu\r\n" - ); - fs.writeFileSync( - path.join(playablePath, "play_local.bat"), - "start shapezio --local --dev --disable-direct-composition --in-process-gpu\r\n" - ); + fs.writeFileSync(path.join(appPath, ".itch.toml"), tomlFile); + + if (platform === "linux" || platform === "darwin") { + fs.writeFileSync(path.join(appPath, "play.sh"), "#!/usr/bin/env bash\n./shapezio\n"); + fs.chmodSync(path.join(appPath, "play.sh"), 0o775); + } else if (platform === "win32") { + // Optional: Create a playable copy. Shouldn't be required + // const playablePath = appPath + "_playable"; + // fse.copySync(appPath, playablePath); + // fs.writeFileSync(path.join(playablePath, "steam_appid.txt"), "1134480"); + // fs.writeFileSync( + // path.join(playablePath, "play.bat"), + // "start shapezio --dev --disable-direct-composition --in-process-gpu\r\n" + // ); + // fs.writeFileSync( + // path.join(playablePath, "play_local.bat"), + // "start shapezio --local --dev --disable-direct-composition --in-process-gpu\r\n" + // ); + } }); cb(); @@ -182,10 +192,10 @@ function gulptasksStandalone($, gulp, buildFolder) { "standalone.package.prod", $.sequence("standalone.prepare", [ "standalone.package.prod.win64", - // "standalone.package.prod.linux64", + "standalone.package.prod.linux64", + "standalone.package.prod.darwin64", // "standalone.package.prod.win32", // "standalone.package.prod.linux32", - // "standalone.package.prod.darwin64" ]) ); } diff --git a/package.json b/package.json index 64bc8ac1..8774e693 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "tslint": "cd src/js && tsc", "lint": "npx eslint src/js", "prettier-all": "prettier --write src/**/*.* && prettier --write gulp/**/*.*", - "publishOnItch": "butler push tmp_standalone_files/shapez.io-standalone-win32-x64 tobspr/shapezio:windows --userversion-file version", + "publishOnItchWindows": "butler push tmp_standalone_files/shapez.io-standalone-win32-x64 tobspr/shapezio:windows --userversion-file version", + "publishOnItchLinux": "butler push tmp_standalone_files/shapez.io-standalone-linux-x64 tobspr/shapezio:linux-experimental --userversion-file version", "publishOnSteam": "cd gulp/steampipe && ./upload.bat", "publishStandalone": "yarn publishOnItch && yarn publishOnSteam", "publishWeb": "cd gulp && yarn main.deploy.prod", diff --git a/src/css/ingame_hud/buildings_toolbar.scss b/src/css/ingame_hud/buildings_toolbar.scss index 740e1f58..13da9f99 100644 --- a/src/css/ingame_hud/buildings_toolbar.scss +++ b/src/css/ingame_hud/buildings_toolbar.scss @@ -11,7 +11,11 @@ border-bottom-width: 0; transition: transform 0.12s ease-in-out; - background: rgba(mix(#ddd, $colorBlueBright, 80%), 0.69); + background: rgba(mix(#ddd, $colorBlueBright, 90%), 0.75); + + @include DarkThemeOverride { + background: #222428; + } &:not(.visible) { transform: translateX(-50%) translateY(#{D(100px)}); diff --git a/src/css/ingame_hud/keybindings_overlay.scss b/src/css/ingame_hud/keybindings_overlay.scss index 1737e7b1..5a238f81 100644 --- a/src/css/ingame_hud/keybindings_overlay.scss +++ b/src/css/ingame_hud/keybindings_overlay.scss @@ -6,8 +6,12 @@ display: flex; flex-direction: column; align-items: flex-start; - color: #fff; - text-shadow: #{D(1px)} #{D(1px)} 0 rgba(0, 10, 20, 0.1); + color: #333438; + // text-shadow: #{D(1px)} #{D(1px)} 0 rgba(0, 10, 20, 0.1); + + @include DarkThemeOverride { + color: #fff; + } > .binding { display: inline-grid; @@ -42,10 +46,13 @@ } label { - color: $accentColorDark; + color: #333438; @include SuperSmallText; text-transform: uppercase; - color: #fff; + // color: #fff; + @include DarkThemeOverride { + color: #fff; + } @include S(margin-left, 5px); } diff --git a/src/css/ingame_hud/pinned_shapes.scss b/src/css/ingame_hud/pinned_shapes.scss index afedd6a5..1c944e35 100644 --- a/src/css/ingame_hud/pinned_shapes.scss +++ b/src/css/ingame_hud/pinned_shapes.scss @@ -16,8 +16,8 @@ grid-template-columns: auto 1fr; grid-template-rows: 1fr 1fr; @include S(margin-bottom, 4px); - color: #fff; - text-shadow: #{D(1px)} #{D(1px)} 0 rgba(0, 10, 20, 0.2); + color: #333438; + // text-shadow: #{D(1px)} #{D(1px)} 0 rgba(0, 10, 20, 0.2); &.unpinable { > canvas { @@ -59,7 +59,7 @@ > .goalLabel { @include S(font-size, 7px); - opacity: 0.5; + opacity: 0.9; align-self: start; justify-self: start; font-weight: normal; @@ -81,7 +81,6 @@ display: inline-block; @include S(width, 8px); @include S(height, 8px); - opacity: 0.8; @include S(top, 4px); @include S(left, -7px); background: uiResource("icons/current_goal_marker.png") center center / contain no-repeat; diff --git a/src/css/ingame_hud/waypoints.scss b/src/css/ingame_hud/waypoints.scss index fbb430fd..6517bbcf 100644 --- a/src/css/ingame_hud/waypoints.scss +++ b/src/css/ingame_hud/waypoints.scss @@ -38,13 +38,13 @@ @include SuperSmallText; pointer-events: all; cursor: pointer; - color: #000; + color: #333438; @include S(padding-left, 11px); display: grid; grid-template-columns: 1fr auto; align-items: center; background: uiResource("icons/waypoint.png") left 50% / #{D(8px)} no-repeat; - opacity: 0.5; + opacity: 0.7; @include S(margin-bottom, 1px); font-weight: bold; &:hover { diff --git a/src/css/states/preload.scss b/src/css/states/preload.scss index 68a268e1..9bd23358 100644 --- a/src/css/states/preload.scss +++ b/src/css/states/preload.scss @@ -36,6 +36,9 @@ @include S(padding, 1px, 2px); @include S(margin-right, 3px); } + a { + color: $colorBlueBright; + } } } diff --git a/src/html/index.html b/src/html/index.html index b1d89377..243455ea 100644 --- a/src/html/index.html +++ b/src/html/index.html @@ -40,6 +40,9 @@ + + + diff --git a/src/js/changelog.js b/src/js/changelog.js index 4d8844c8..0559801f 100644 --- a/src/js/changelog.js +++ b/src/js/changelog.js @@ -1,11 +1,25 @@ export const CHANGELOG = [ { - version: "1.1.2", - date: "unreleased", + version: "1.1.3", + date: "01.06.2020", entries: [ + "Added setting to configure zoom / mouse wheel / touchpad sensitivity", + "Fix belts being too slow when copied via blueprint (by Dimava)", + "Allow binding mouse buttons to actions (by Dimava)", + "Increase readability of certain HUD elements", + ], + }, + { + version: "1.1.2", + date: "30.05.2020", + entries: [ + "The official trailer is now ready! Check it out here!", + "The steam page is now live!", + "Experimental linux builds are now available! Please give me feedback on them in the discord", "Allow hovering pinned shapes to enlarge them", + "Allow deselecting blueprints with right click and 'Q'", "Move default key for deleting from 'X' to 'DEL'", - "Show confirmation when deleting > 100 buildings", + "Show confirmation when deleting more than 100 buildings", "Reintroduce 'SPACE' keybinding to center on map", "Improved keybinding hints", "Fixed some keybindings showing as 'undefined'", diff --git a/src/js/core/click_detector.js b/src/js/core/click_detector.js index 557c1f28..1e332aa2 100644 --- a/src/js/core/click_detector.js +++ b/src/js/core/click_detector.js @@ -311,7 +311,7 @@ export class ClickDetector { const position = /** @type {typeof ClickDetector} */ (this.constructor).extractPointerPosition(event); if (event instanceof MouseEvent) { - const isRightClick = event.which == 3; + const isRightClick = event.button === 2; if (isRightClick) { // Ignore right clicks this.rightClick.dispatch(position, event); @@ -384,7 +384,7 @@ export class ClickDetector { } if (event instanceof MouseEvent) { - const isRightClick = event.which == 3; + const isRightClick = event.button === 2; if (isRightClick) { return; } diff --git a/src/js/core/config.js b/src/js/core/config.js index 5b802de3..c84dee3a 100644 --- a/src/js/core/config.js +++ b/src/js/core/config.js @@ -83,7 +83,7 @@ export const globalConfig = { debug: { /* dev:start */ - fastGameEnter: true, + // fastGameEnter: true, // noArtificialDelays: true, // disableSavegameWrite: true, // showEntityBounds: true, diff --git a/src/js/core/input_distributor.js b/src/js/core/input_distributor.js index 4bd476b4..03ad8e0c 100644 --- a/src/js/core/input_distributor.js +++ b/src/js/core/input_distributor.js @@ -141,8 +141,13 @@ export class InputDistributor { bindToEvents() { window.addEventListener("popstate", this.handleBackButton.bind(this), false); document.addEventListener("backbutton", this.handleBackButton.bind(this), false); - window.addEventListener("keydown", this.handleKeydown.bind(this)); - window.addEventListener("keyup", this.handleKeyup.bind(this)); + + window.addEventListener("keydown", this.handleKeyMouseDown.bind(this)); + window.addEventListener("keyup", this.handleKeyMouseUp.bind(this)); + + window.addEventListener("mousedown", this.handleKeyMouseDown.bind(this)); + window.addEventListener("mouseup", this.handleKeyMouseUp.bind(this)); + window.addEventListener("blur", this.handleBlur.bind(this)); } @@ -182,25 +187,28 @@ export class InputDistributor { } /** - * @param {KeyboardEvent} event + * @param {KeyboardEvent | MouseEvent} event */ - handleKeydown(event) { + handleKeyMouseDown(event) { + const keyCode = event instanceof MouseEvent ? event.button + 1 : event.keyCode; if ( - event.keyCode === 9 || // TAB - event.keyCode === 16 || // SHIFT - event.keyCode === 17 || // CTRL - event.keyCode === 18 || // ALT - (event.keyCode >= 112 && event.keyCode < 122) // F1 - F10 + keyCode === 4 || // MB4 + keyCode === 5 || // MB5 + keyCode === 9 || // TAB + keyCode === 16 || // SHIFT + keyCode === 17 || // CTRL + keyCode === 18 || // ALT + (keyCode >= 112 && keyCode < 122) // F1 - F10 ) { event.preventDefault(); } - const isInitial = !this.keysDown.has(event.keyCode); - this.keysDown.add(event.keyCode); + const isInitial = !this.keysDown.has(keyCode); + this.keysDown.add(keyCode); if ( this.forwardToReceiver("keydown", { - keyCode: event.keyCode, + keyCode: keyCode, shift: event.shiftKey, alt: event.altKey, initial: isInitial, @@ -210,8 +218,7 @@ export class InputDistributor { return; } - const code = event.keyCode; - if (code === 27) { + if (keyCode === 27) { // Escape key event.preventDefault(); event.stopPropagation(); @@ -220,13 +227,14 @@ export class InputDistributor { } /** - * @param {KeyboardEvent} event + * @param {KeyboardEvent | MouseEvent} event */ - handleKeyup(event) { - this.keysDown.delete(event.keyCode); + handleKeyMouseUp(event) { + const keyCode = event instanceof MouseEvent ? event.button + 1 : event.keyCode; + this.keysDown.delete(keyCode); this.forwardToReceiver("keyup", { - keyCode: event.keyCode, + keyCode: keyCode, shift: event.shiftKey, alt: event.altKey, }); diff --git a/src/js/core/utils.js b/src/js/core/utils.js index f756a651..e50b71c8 100644 --- a/src/js/core/utils.js +++ b/src/js/core/utils.js @@ -24,9 +24,7 @@ export const BOTTOM = new Vector(0, 1); export const LEFT = new Vector(-1, 0); export const ALL_DIRECTIONS = [TOP, RIGHT, BOTTOM, LEFT]; -export const thousand = 1000; -export const million = 1000 * 1000; -export const billion = 1000 * 1000 * 1000; +const bigNumberSuffixTranslationKeys = ["thousands", "millions", "billions", "trillions"]; /** * Returns the build id @@ -435,21 +433,20 @@ export function formatBigNumber(num, divider = ".") { if (num < 1000) { return sign + "" + num; + } else { + let leadingDigits = num; + let suffix = ""; + for (let suffixIndex = 0; suffixIndex < bigNumberSuffixTranslationKeys.length; ++suffixIndex) { + leadingDigits = leadingDigits / 1000; + suffix = T.global.suffix[bigNumberSuffixTranslationKeys[suffixIndex]]; + if (leadingDigits < 1000) { + break; + } + } + const leadingDigitsRounded = round1Digit(leadingDigits); + const leadingDigitsNoTrailingDecimal = leadingDigitsRounded.toString().replace(".0", ""); + return sign + leadingDigitsNoTrailingDecimal + suffix; } - if (num > 10000) { - return Math_floor(num / 1000.0) + "k"; - } - - let rest = num; - let out = ""; - - while (rest >= 1000) { - out = (rest % 1000).toString().padStart(3, "0") + (out !== "" ? divider : "") + out; - rest = Math_floor(rest / 1000); - } - - out = rest + divider + out; - return sign + out; } /** @@ -731,14 +728,14 @@ export function checkTimerExpired(now, lastTick, tickRate) { Client A computes the timer and checks T > lastTick + interval. He computes 30 >= 29.90 + 0.1 <=> 30 >= 30.0000 <=> True <=> Tick performed - + However, this is what it looks on client B: - + 33 >= 32.90 + 0.1 <=> 33 >= 32.999999999999998 <=> False <=> No tick performed! - + This means that Client B will only tick at the *next* frame, which means it from now is out of sync by one tick, which means the game will resync further or later and be not able to recover, - since it will run into the same issue over and over. + since it will run into the same issue over and over. */ // The next tick, in our example it would be 30.0000 / 32.99999999998. In order to fix it, we quantize diff --git a/src/js/game/camera.js b/src/js/game/camera.js index 96bf5cd0..1a389cad 100644 --- a/src/js/game/camera.js +++ b/src/js/game/camera.js @@ -440,11 +440,11 @@ export class Camera extends BasicSerializableObject { } this.touchPostMoveVelocity = new Vector(0, 0); - if (event.which === 1) { + if (event.button === 0) { this.combinedSingleTouchStartHandler(event.clientX, event.clientY); - } else if (event.which === 2) { + } else if (event.button === 1) { this.downPreHandler.dispatch(new Vector(event.clientX, event.clientY), enumMouseButton.middle); - } else if (event.which === 3) { + } else if (event.button === 2) { this.downPreHandler.dispatch(new Vector(event.clientX, event.clientY), enumMouseButton.right); } return false; @@ -464,7 +464,7 @@ export class Camera extends BasicSerializableObject { return; } - if (event.which === 1) { + if (event.button === 0) { this.combinedSingleTouchMoveHandler(event.clientX, event.clientY); } @@ -503,7 +503,7 @@ export class Camera extends BasicSerializableObject { // event.stopPropagation(); } - const delta = Math.sign(event.deltaY) * -0.15; + const delta = Math.sign(event.deltaY) * -0.15 * this.root.app.settings.getScrollWheelSensitivity(); assert(Number.isFinite(delta), "Got invalid delta in mouse wheel event: " + event.deltaY); assert(Number.isFinite(this.zoomLevel), "Got invalid zoom level *before* wheel: " + this.zoomLevel); this.zoomLevel *= 1 + delta; diff --git a/src/js/game/components/item_ejector.js b/src/js/game/components/item_ejector.js index d5881a7d..5a40870b 100644 --- a/src/js/game/components/item_ejector.js +++ b/src/js/game/components/item_ejector.js @@ -44,7 +44,7 @@ export class ItemEjectorComponent extends Component { return new ItemEjectorComponent({ slots: slotsCopy, - instantEject: false, + instantEject: this.instantEject, }); } diff --git a/src/js/game/hud/hud.js b/src/js/game/hud/hud.js index b2472a68..e1f1dbbf 100644 --- a/src/js/game/hud/hud.js +++ b/src/js/game/hud/hud.js @@ -48,8 +48,8 @@ export class GameHUD { this.parts = { processingOverlay: new HUDProcessingOverlay(this.root), buildingsToolbar: new HUDBuildingsToolbar(this.root), - buildingPlacer: new HUDBuildingPlacer(this.root), blueprintPlacer: new HUDBlueprintPlacer(this.root), + buildingPlacer: new HUDBuildingPlacer(this.root), unlockNotification: new HUDUnlockNotification(this.root), gameMenu: new HUDGameMenu(this.root), massSelector: new HUDMassSelector(this.root), diff --git a/src/js/game/hud/parts/blueprint_placer.js b/src/js/game/hud/parts/blueprint_placer.js index b3b91a66..173d1809 100644 --- a/src/js/game/hud/parts/blueprint_placer.js +++ b/src/js/game/hud/parts/blueprint_placer.js @@ -1,4 +1,4 @@ -import { DrawParameters } from "../../../core/draw_parameters"; +//www.youtube.com/watch?v=KyorY1uIqiQimport { DrawParameters } from "../../../core/draw_parameters"; import { STOP_PROPAGATION } from "../../../core/signal"; import { TrackedState } from "../../../core/tracked_state"; import { Vector } from "../../../core/vector"; @@ -36,6 +36,9 @@ export class HUDBlueprintPlacer extends BaseHUDPart { .getBinding(KEYMAPPINGS.placement.abortBuildingPlacement) .add(this.abortPlacement, this); keyActionMapper.getBinding(KEYMAPPINGS.placement.rotateWhilePlacing).add(this.rotateBlueprint, this); + keyActionMapper + .getBinding(KEYMAPPINGS.placement.abortBuildingPlacement) + .add(this.abortPlacement, this); this.root.camera.downPreHandler.add(this.onMouseDown, this); this.root.camera.movePreHandler.add(this.onMouseMove, this); diff --git a/src/js/game/key_action_mapper.js b/src/js/game/key_action_mapper.js index 2ffbb03c..9de75731 100644 --- a/src/js/game/key_action_mapper.js +++ b/src/js/game/key_action_mapper.js @@ -89,6 +89,16 @@ for (const categoryId in KEYMAPPINGS) { */ export function getStringForKeyCode(code) { switch (code) { + case 1: + return "LMB"; + case 2: + return "MMB"; + case 3: + return "RMB"; + case 4: + return "MB4"; + case 5: + return "MB5"; case 8: return "⌫"; case 9: diff --git a/src/js/platform/browser/google_analytics.js b/src/js/platform/browser/google_analytics.js index 55de95cc..3c54fbbd 100644 --- a/src/js/platform/browser/google_analytics.js +++ b/src/js/platform/browser/google_analytics.js @@ -66,11 +66,6 @@ export class GoogleAnalyticsImpl extends AnalyticsInterface { } trackUiClick(elementName) { - // Only track a fraction of clicks to not annoy google analytics - if (Math_random() < 0.9) { - return; - } - const stateKey = this.app.stateMgr.getCurrentState().key; const fullSelector = stateKey + ">" + elementName; diff --git a/src/js/profile/application_settings.js b/src/js/profile/application_settings.js index aa5048ff..37576466 100644 --- a/src/js/profile/application_settings.js +++ b/src/js/profile/application_settings.js @@ -8,6 +8,7 @@ import { createLogger } from "../core/logging"; import { ExplainedResult } from "../core/explained_result"; import { THEMES, THEME, applyGameTheme } from "../game/theme"; import { IS_DEMO } from "../core/config"; +import { T } from "../translations"; const logger = createLogger("application_settings"); @@ -18,27 +19,45 @@ 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", + }, +]; + +export const scrollWheelSensitivities = [ + { + id: "super_slow", + scale: 0.25, + }, + { + id: "slow", + scale: 0.5, + }, + { + id: "regular", + scale: 1, + }, + { + id: "fast", + scale: 2, + }, + { + id: "super_fast", + scale: 4, }, ]; @@ -47,7 +66,7 @@ export const allApplicationSettings = [ new EnumSetting("uiScale", { options: uiScales.sort((a, b) => a.size - b.size), valueGetter: scale => scale.id, - textGetter: scale => scale.label, + textGetter: scale => T.settings.labels.uiScale.scales[scale.id], category: categoryApp, restartRequired: false, changeCb: @@ -56,6 +75,7 @@ export const allApplicationSettings = [ */ (app, id) => app.updateAfterUiScaleChanged(), }), + new BoolSetting( "fullscreen", categoryApp, @@ -86,6 +106,18 @@ export const allApplicationSettings = [ */ (app, value) => app.sound.setMusicMuted(value) ), + new EnumSetting("scrollWheelSensitivity", { + options: scrollWheelSensitivities.sort((a, b) => a.scale - b.scale), + valueGetter: scale => scale.id, + textGetter: scale => T.settings.labels.scrollWheelSensitivity.sensitivity[scale.id], + category: categoryApp, + restartRequired: false, + changeCb: + /** + * @param {Application} app + */ + (app, id) => app.updateAfterUiScaleChanged(), + }), // GAME new EnumSetting("theme", { @@ -133,6 +165,7 @@ class SettingsStorage { this.musicMuted = false; this.theme = "light"; this.refreshRate = "60"; + this.scrollWheelSensitivity = "regular"; this.alwaysMultiplace = false; this.abortPlacementOnDeletion = true; @@ -209,6 +242,17 @@ export class ApplicationSettings extends ReadWriteProxy { return 1; } + getScrollWheelSensitivity() { + const id = this.getAllSettings().scrollWheelSensitivity; + for (let i = 0; i < scrollWheelSensitivities.length; ++i) { + if (scrollWheelSensitivities[i].id === id) { + return scrollWheelSensitivities[i].scale; + } + } + logger.error("Unknown scroll wheel sensitivity id:", id); + return 1; + } + getIsFullScreen() { return this.getAllSettings().fullscreen; } @@ -295,7 +339,7 @@ export class ApplicationSettings extends ReadWriteProxy { } getCurrentVersion() { - return 8; + return 9; } /** @param {{settings: SettingsStorage, version: number}} data */ @@ -318,10 +362,15 @@ export class ApplicationSettings extends ReadWriteProxy { } if (data.version < 8) { - data.settings.abortPlacementOnDeletion = true; + data.settings.scrollWheelSensitivity = "regular"; data.version = 8; } + if (data.version < 9) { + data.settings.abortPlacementOnDeletion = true; + data.version = 9; + } + return ExplainedResult.good(); } } diff --git a/src/js/savegame/savegame.js b/src/js/savegame/savegame.js index a78200f9..7d59a056 100644 --- a/src/js/savegame/savegame.js +++ b/src/js/savegame/savegame.js @@ -10,8 +10,9 @@ import { BaseSavegameInterface } from "./savegame_interface"; import { createLogger } from "../core/logging"; import { globalConfig } from "../core/config"; import { SavegameInterface_V1000 } from "./schemas/1000"; -import { getSavegameInterface } from "./savegame_interface_registry"; +import { getSavegameInterface, savegameInterfaces } from "./savegame_interface_registry"; import { SavegameInterface_V1001 } from "./schemas/1001"; +import { SavegameInterface_V1002 } from "./schemas/1002"; const logger = createLogger("savegame"); @@ -30,6 +31,11 @@ export class Savegame extends ReadWriteProxy { /** @type {import("./savegame_typedefs").SavegameData} */ this.currentData = this.getDefaultData(); + + assert( + savegameInterfaces[Savegame.getCurrentVersion()], + "Savegame interface not defined: " + Savegame.getCurrentVersion() + ); } //////// RW Proxy Impl ////////// @@ -38,14 +44,14 @@ export class Savegame extends ReadWriteProxy { * @returns {number} */ static getCurrentVersion() { - return 1001; + return 1002; } /** * @returns {typeof BaseSavegameInterface} */ static getReaderClass() { - return SavegameInterface_V1001; + return savegameInterfaces[Savegame.getCurrentVersion()]; } /** @@ -82,6 +88,11 @@ export class Savegame extends ReadWriteProxy { data.version = 1001; } + if (data.version === 1001) { + SavegameInterface_V1002.migrate1001to1002(data); + data.version = 1002; + } + return ExplainedResult.good(); } diff --git a/src/js/savegame/savegame_interface_registry.js b/src/js/savegame/savegame_interface_registry.js index 2560b23e..7c6db250 100644 --- a/src/js/savegame/savegame_interface_registry.js +++ b/src/js/savegame/savegame_interface_registry.js @@ -2,11 +2,13 @@ import { BaseSavegameInterface } from "./savegame_interface"; import { SavegameInterface_V1000 } from "./schemas/1000"; import { createLogger } from "../core/logging"; import { SavegameInterface_V1001 } from "./schemas/1001"; +import { SavegameInterface_V1002 } from "./schemas/1002"; /** @type {Object.} */ -const interfaces = { +export const savegameInterfaces = { 1000: SavegameInterface_V1000, 1001: SavegameInterface_V1001, + 1002: SavegameInterface_V1002, }; const logger = createLogger("savegame_interface_registry"); @@ -27,7 +29,7 @@ export function getSavegameInterface(savegame) { return null; } - const interfaceClass = interfaces[version]; + const interfaceClass = savegameInterfaces[version]; if (!interfaceClass) { logger.warn("Version", version, "has no implemented interface!"); return null; diff --git a/src/js/savegame/schemas/1002.js b/src/js/savegame/schemas/1002.js new file mode 100644 index 00000000..92dadfc1 --- /dev/null +++ b/src/js/savegame/schemas/1002.js @@ -0,0 +1,37 @@ +import { createLogger } from "../../core/logging.js"; +import { T } from "../../translations.js"; +import { SavegameInterface_V1001 } from "./1001.js"; + +const schema = require("./1002.json"); +const logger = createLogger("savegame_interface/1002"); + +export class SavegameInterface_V1002 extends SavegameInterface_V1001 { + getVersion() { + return 1002; + } + + getSchemaUncached() { + return schema; + } + + /** + * @param {import("../savegame_typedefs.js").SavegameData} data + */ + static migrate1001to1002(data) { + logger.log("Migrating 1001 to 1002"); + const dump = data.dump; + if (!dump) { + return true; + } + + const entities = dump.entities; + for (let i = 0; i < entities.length; ++i) { + const entity = entities[i]; + const beltComp = entity.components.Belt; + const ejectorComp = entity.components.ItemEjector; + if (beltComp && ejectorComp) { + ejectorComp.instantEject = true; + } + } + } +} diff --git a/src/js/savegame/schemas/1002.json b/src/js/savegame/schemas/1002.json new file mode 100644 index 00000000..6682f615 --- /dev/null +++ b/src/js/savegame/schemas/1002.json @@ -0,0 +1,5 @@ +{ + "type": "object", + "required": [], + "additionalProperties": true +} diff --git a/src/js/states/keybindings.js b/src/js/states/keybindings.js index 0f7fcf9e..bc2b4a18 100644 --- a/src/js/states/keybindings.js +++ b/src/js/states/keybindings.js @@ -105,6 +105,10 @@ export class KeybindingsState extends TextualGameState { event.preventDefault(); } + if (event.target && event.target.tagName === "BUTTON" && keyCode === 1) { + return; + } + if ( // Enter keyCode === 13 || @@ -122,8 +126,8 @@ export class KeybindingsState extends TextualGameState { }); dialog.inputReciever.backButton.add(() => {}); - this.dialogs.internalShowDialog(dialog); + this.app.sound.playUiSound(SOUNDS.dialogOk); } diff --git a/translations/base-en.yaml b/translations/base-en.yaml index c2a39348..7602b64e 100644 --- a/translations/base-en.yaml +++ b/translations/base-en.yaml @@ -26,6 +26,13 @@ global: # How big numbers are rendered, e.g. "10,000" thousandsDivider: "," + # The suffix for large numbers, e.g. 1.3k, 400.2M, etc. + suffix: + thousands: k + millions: M + billions: B + trillions: T + # Shown for infinitely big numbers infinite: inf @@ -127,7 +134,7 @@ dialogs: editKeybinding: title: Change Keybinding - desc: Press the key you want to assign, or escape to cancel. + desc: Press the key or mouse button you want to assign, or escape to cancel. resetKeybindingsConfirmation: title: Reset keybindings @@ -511,6 +518,23 @@ settings: title: Interface scale description: >- Changes the size of the user interface. The interface will still scale based on your device resolution, but this setting controls the amount of scale. + scales: + super_small: Super small + small: Small + regular: Regular + large: Large + huge: Huge + + scrollWheelSensitivity: + title: Zoom sensitivity + description: >- + Changes how sensitive the zoom is (Either mouse wheel or trackpad). + sensitivity: + super_slow: Super slow + slow: Slow + regular: Regular + fast: Fast + super_fast: Super fast fullscreen: title: Fullscreen diff --git a/version b/version index 8428158d..9c1218c2 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.1.2 \ No newline at end of file +1.1.3 \ No newline at end of file