1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2025-06-13 13:04:03 +00:00

Integrate with addition of new setting in master.

This commit is contained in:
hexagonhexagon 2020-06-01 14:01:32 -04:00
commit 6f7d0a4444
29 changed files with 301 additions and 104 deletions

View File

@ -1,15 +1,15 @@
# shapez.io # shapez.io
<img src="https://i.imgur.com/Y5Z2iqQ.png" alt="shapez.io Logo"> <img src="https://i.imgur.com/Y5Z2iqQ.png" alt="shapez.io Logo">
<img src="https://i.imgur.com/W25Fkl0.png" alt="shapez.io Screenshot">
This is the source code for shapez.io, an open source base building game inspired by Factorio. 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. Your goal is to produce shapes by cutting, rotating, merging and painting parts of shapes.
## Playing - [Trello Board & Roadmap](https://trello.com/b/ISQncpJP/shapezio)
- [Free web version](https://shapez.io)
You can already play it [here](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 ## 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/`. For most assets I use Adobe Photoshop, you can find them in `assets/`.
You will need a <a href="https://www.codeandweb.com/texturepacker" target="_blank">Texture Packer</a> 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. You will need a <a href="https://www.codeandweb.com/texturepacker" target="_blank">Texture Packer</a> 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.
<img src="https://i.imgur.com/W25Fkl0.png" alt="shapez.io Screenshot">

9
gulp/.itch.toml Normal file
View File

@ -0,0 +1,9 @@
[[actions]]
name = "play"
path = "shapezio.exe"
platform = "windows"
[[actions]]
name = "play"
path = "play.sh"
platform = "linux"

View File

@ -121,6 +121,8 @@ function gulptasksStandalone($, gulp, buildFolder) {
* @param {boolean=} isRelease * @param {boolean=} isRelease
*/ */
function packageStandalone(platform, arch, cb, isRelease = false) { function packageStandalone(platform, arch, cb, isRelease = false) {
const tomlFile = fs.readFileSync(path.join(__dirname, ".itch.toml"));
packager({ packager({
dir: tempDestBuildDir, dir: tempDestBuildDir,
appCopyright: "Tobias Springer", appCopyright: "Tobias Springer",
@ -150,17 +152,25 @@ function gulptasksStandalone($, gulp, buildFolder) {
fs.readFileSync(path.join(__dirname, "..", "LICENSE")) fs.readFileSync(path.join(__dirname, "..", "LICENSE"))
); );
const playablePath = appPath + "_playable"; fs.writeFileSync(path.join(appPath, ".itch.toml"), tomlFile);
fse.copySync(appPath, playablePath);
fs.writeFileSync(path.join(playablePath, "steam_appid.txt"), "1134480"); if (platform === "linux" || platform === "darwin") {
fs.writeFileSync( fs.writeFileSync(path.join(appPath, "play.sh"), "#!/usr/bin/env bash\n./shapezio\n");
path.join(playablePath, "play.bat"), fs.chmodSync(path.join(appPath, "play.sh"), 0o775);
"start shapezio --dev --disable-direct-composition --in-process-gpu\r\n" } else if (platform === "win32") {
); // Optional: Create a playable copy. Shouldn't be required
fs.writeFileSync( // const playablePath = appPath + "_playable";
path.join(playablePath, "play_local.bat"), // fse.copySync(appPath, playablePath);
"start shapezio --local --dev --disable-direct-composition --in-process-gpu\r\n" // 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(); cb();
@ -182,10 +192,10 @@ function gulptasksStandalone($, gulp, buildFolder) {
"standalone.package.prod", "standalone.package.prod",
$.sequence("standalone.prepare", [ $.sequence("standalone.prepare", [
"standalone.package.prod.win64", "standalone.package.prod.win64",
// "standalone.package.prod.linux64", "standalone.package.prod.linux64",
"standalone.package.prod.darwin64",
// "standalone.package.prod.win32", // "standalone.package.prod.win32",
// "standalone.package.prod.linux32", // "standalone.package.prod.linux32",
// "standalone.package.prod.darwin64"
]) ])
); );
} }

View File

@ -11,7 +11,8 @@
"tslint": "cd src/js && tsc", "tslint": "cd src/js && tsc",
"lint": "npx eslint src/js", "lint": "npx eslint src/js",
"prettier-all": "prettier --write src/**/*.* && prettier --write gulp/**/*.*", "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", "publishOnSteam": "cd gulp/steampipe && ./upload.bat",
"publishStandalone": "yarn publishOnItch && yarn publishOnSteam", "publishStandalone": "yarn publishOnItch && yarn publishOnSteam",
"publishWeb": "cd gulp && yarn main.deploy.prod", "publishWeb": "cd gulp && yarn main.deploy.prod",

View File

@ -11,7 +11,11 @@
border-bottom-width: 0; border-bottom-width: 0;
transition: transform 0.12s ease-in-out; 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) { &:not(.visible) {
transform: translateX(-50%) translateY(#{D(100px)}); transform: translateX(-50%) translateY(#{D(100px)});

View File

@ -6,8 +6,12 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: flex-start; align-items: flex-start;
color: #fff; color: #333438;
text-shadow: #{D(1px)} #{D(1px)} 0 rgba(0, 10, 20, 0.1); // text-shadow: #{D(1px)} #{D(1px)} 0 rgba(0, 10, 20, 0.1);
@include DarkThemeOverride {
color: #fff;
}
> .binding { > .binding {
display: inline-grid; display: inline-grid;
@ -42,10 +46,13 @@
} }
label { label {
color: $accentColorDark; color: #333438;
@include SuperSmallText; @include SuperSmallText;
text-transform: uppercase; text-transform: uppercase;
color: #fff; // color: #fff;
@include DarkThemeOverride {
color: #fff;
}
@include S(margin-left, 5px); @include S(margin-left, 5px);
} }

View File

@ -16,8 +16,8 @@
grid-template-columns: auto 1fr; grid-template-columns: auto 1fr;
grid-template-rows: 1fr 1fr; grid-template-rows: 1fr 1fr;
@include S(margin-bottom, 4px); @include S(margin-bottom, 4px);
color: #fff; color: #333438;
text-shadow: #{D(1px)} #{D(1px)} 0 rgba(0, 10, 20, 0.2); // text-shadow: #{D(1px)} #{D(1px)} 0 rgba(0, 10, 20, 0.2);
&.unpinable { &.unpinable {
> canvas { > canvas {
@ -59,7 +59,7 @@
> .goalLabel { > .goalLabel {
@include S(font-size, 7px); @include S(font-size, 7px);
opacity: 0.5; opacity: 0.9;
align-self: start; align-self: start;
justify-self: start; justify-self: start;
font-weight: normal; font-weight: normal;
@ -81,7 +81,6 @@
display: inline-block; display: inline-block;
@include S(width, 8px); @include S(width, 8px);
@include S(height, 8px); @include S(height, 8px);
opacity: 0.8;
@include S(top, 4px); @include S(top, 4px);
@include S(left, -7px); @include S(left, -7px);
background: uiResource("icons/current_goal_marker.png") center center / contain no-repeat; background: uiResource("icons/current_goal_marker.png") center center / contain no-repeat;

View File

@ -38,13 +38,13 @@
@include SuperSmallText; @include SuperSmallText;
pointer-events: all; pointer-events: all;
cursor: pointer; cursor: pointer;
color: #000; color: #333438;
@include S(padding-left, 11px); @include S(padding-left, 11px);
display: grid; display: grid;
grid-template-columns: 1fr auto; grid-template-columns: 1fr auto;
align-items: center; align-items: center;
background: uiResource("icons/waypoint.png") left 50% / #{D(8px)} no-repeat; background: uiResource("icons/waypoint.png") left 50% / #{D(8px)} no-repeat;
opacity: 0.5; opacity: 0.7;
@include S(margin-bottom, 1px); @include S(margin-bottom, 1px);
font-weight: bold; font-weight: bold;
&:hover { &:hover {

View File

@ -36,6 +36,9 @@
@include S(padding, 1px, 2px); @include S(padding, 1px, 2px);
@include S(margin-right, 3px); @include S(margin-right, 3px);
} }
a {
color: $colorBlueBright;
}
} }
} }

View File

@ -40,6 +40,9 @@
<meta http-equiv="Expires" content="0" /> <meta http-equiv="Expires" content="0" />
<link rel="shortcut icon" type="image/x-icon" href="favicon.ico" /> <link rel="shortcut icon" type="image/x-icon" href="favicon.ico" />
<link rel="canonical" href="https://shapez.io" /> <link rel="canonical" href="https://shapez.io" />
<!-- a/b testing -->
<script src="https://www.googleoptimize.com/optimize.js?id=OPT-M5NHCV7"></script>
</head> </head>
<body oncontextmenu="return false" style="background: #393747;"></body> <body oncontextmenu="return false" style="background: #393747;"></body>

View File

@ -1,11 +1,25 @@
export const CHANGELOG = [ export const CHANGELOG = [
{ {
version: "1.1.2", version: "1.1.3",
date: "unreleased", date: "01.06.2020",
entries: [ 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 <a href='https://www.youtube.com/watch?v=KyorY1uIqiQ' target='_blank'>here</a>!",
"The <a href='https://steam.shapez.io' target='_blank'>steam page</a> 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 hovering pinned shapes to enlarge them",
"Allow deselecting blueprints with right click and 'Q'",
"Move default key for deleting from 'X' to 'DEL'", "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", "Reintroduce 'SPACE' keybinding to center on map",
"Improved keybinding hints", "Improved keybinding hints",
"Fixed some keybindings showing as 'undefined'", "Fixed some keybindings showing as 'undefined'",

View File

@ -311,7 +311,7 @@ export class ClickDetector {
const position = /** @type {typeof ClickDetector} */ (this.constructor).extractPointerPosition(event); const position = /** @type {typeof ClickDetector} */ (this.constructor).extractPointerPosition(event);
if (event instanceof MouseEvent) { if (event instanceof MouseEvent) {
const isRightClick = event.which == 3; const isRightClick = event.button === 2;
if (isRightClick) { if (isRightClick) {
// Ignore right clicks // Ignore right clicks
this.rightClick.dispatch(position, event); this.rightClick.dispatch(position, event);
@ -384,7 +384,7 @@ export class ClickDetector {
} }
if (event instanceof MouseEvent) { if (event instanceof MouseEvent) {
const isRightClick = event.which == 3; const isRightClick = event.button === 2;
if (isRightClick) { if (isRightClick) {
return; return;
} }

View File

@ -83,7 +83,7 @@ export const globalConfig = {
debug: { debug: {
/* dev:start */ /* dev:start */
fastGameEnter: true, // fastGameEnter: true,
// noArtificialDelays: true, // noArtificialDelays: true,
// disableSavegameWrite: true, // disableSavegameWrite: true,
// showEntityBounds: true, // showEntityBounds: true,

View File

@ -141,8 +141,13 @@ export class InputDistributor {
bindToEvents() { bindToEvents() {
window.addEventListener("popstate", this.handleBackButton.bind(this), false); window.addEventListener("popstate", this.handleBackButton.bind(this), false);
document.addEventListener("backbutton", 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)); 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 ( if (
event.keyCode === 9 || // TAB keyCode === 4 || // MB4
event.keyCode === 16 || // SHIFT keyCode === 5 || // MB5
event.keyCode === 17 || // CTRL keyCode === 9 || // TAB
event.keyCode === 18 || // ALT keyCode === 16 || // SHIFT
(event.keyCode >= 112 && event.keyCode < 122) // F1 - F10 keyCode === 17 || // CTRL
keyCode === 18 || // ALT
(keyCode >= 112 && keyCode < 122) // F1 - F10
) { ) {
event.preventDefault(); event.preventDefault();
} }
const isInitial = !this.keysDown.has(event.keyCode); const isInitial = !this.keysDown.has(keyCode);
this.keysDown.add(event.keyCode); this.keysDown.add(keyCode);
if ( if (
this.forwardToReceiver("keydown", { this.forwardToReceiver("keydown", {
keyCode: event.keyCode, keyCode: keyCode,
shift: event.shiftKey, shift: event.shiftKey,
alt: event.altKey, alt: event.altKey,
initial: isInitial, initial: isInitial,
@ -210,8 +218,7 @@ export class InputDistributor {
return; return;
} }
const code = event.keyCode; if (keyCode === 27) {
if (code === 27) {
// Escape key // Escape key
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
@ -220,13 +227,14 @@ export class InputDistributor {
} }
/** /**
* @param {KeyboardEvent} event * @param {KeyboardEvent | MouseEvent} event
*/ */
handleKeyup(event) { handleKeyMouseUp(event) {
this.keysDown.delete(event.keyCode); const keyCode = event instanceof MouseEvent ? event.button + 1 : event.keyCode;
this.keysDown.delete(keyCode);
this.forwardToReceiver("keyup", { this.forwardToReceiver("keyup", {
keyCode: event.keyCode, keyCode: keyCode,
shift: event.shiftKey, shift: event.shiftKey,
alt: event.altKey, alt: event.altKey,
}); });

View File

@ -24,9 +24,7 @@ export const BOTTOM = new Vector(0, 1);
export const LEFT = new Vector(-1, 0); export const LEFT = new Vector(-1, 0);
export const ALL_DIRECTIONS = [TOP, RIGHT, BOTTOM, LEFT]; export const ALL_DIRECTIONS = [TOP, RIGHT, BOTTOM, LEFT];
export const thousand = 1000; const bigNumberSuffixTranslationKeys = ["thousands", "millions", "billions", "trillions"];
export const million = 1000 * 1000;
export const billion = 1000 * 1000 * 1000;
/** /**
* Returns the build id * Returns the build id
@ -435,21 +433,20 @@ export function formatBigNumber(num, divider = ".") {
if (num < 1000) { if (num < 1000) {
return sign + "" + num; 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;
} }
/** /**

View File

@ -440,11 +440,11 @@ export class Camera extends BasicSerializableObject {
} }
this.touchPostMoveVelocity = new Vector(0, 0); this.touchPostMoveVelocity = new Vector(0, 0);
if (event.which === 1) { if (event.button === 0) {
this.combinedSingleTouchStartHandler(event.clientX, event.clientY); 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); 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); this.downPreHandler.dispatch(new Vector(event.clientX, event.clientY), enumMouseButton.right);
} }
return false; return false;
@ -464,7 +464,7 @@ export class Camera extends BasicSerializableObject {
return; return;
} }
if (event.which === 1) { if (event.button === 0) {
this.combinedSingleTouchMoveHandler(event.clientX, event.clientY); this.combinedSingleTouchMoveHandler(event.clientX, event.clientY);
} }
@ -503,7 +503,7 @@ export class Camera extends BasicSerializableObject {
// event.stopPropagation(); // 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(delta), "Got invalid delta in mouse wheel event: " + event.deltaY);
assert(Number.isFinite(this.zoomLevel), "Got invalid zoom level *before* wheel: " + this.zoomLevel); assert(Number.isFinite(this.zoomLevel), "Got invalid zoom level *before* wheel: " + this.zoomLevel);
this.zoomLevel *= 1 + delta; this.zoomLevel *= 1 + delta;

View File

@ -44,7 +44,7 @@ export class ItemEjectorComponent extends Component {
return new ItemEjectorComponent({ return new ItemEjectorComponent({
slots: slotsCopy, slots: slotsCopy,
instantEject: false, instantEject: this.instantEject,
}); });
} }

View File

@ -48,8 +48,8 @@ export class GameHUD {
this.parts = { this.parts = {
processingOverlay: new HUDProcessingOverlay(this.root), processingOverlay: new HUDProcessingOverlay(this.root),
buildingsToolbar: new HUDBuildingsToolbar(this.root), buildingsToolbar: new HUDBuildingsToolbar(this.root),
buildingPlacer: new HUDBuildingPlacer(this.root),
blueprintPlacer: new HUDBlueprintPlacer(this.root), blueprintPlacer: new HUDBlueprintPlacer(this.root),
buildingPlacer: new HUDBuildingPlacer(this.root),
unlockNotification: new HUDUnlockNotification(this.root), unlockNotification: new HUDUnlockNotification(this.root),
gameMenu: new HUDGameMenu(this.root), gameMenu: new HUDGameMenu(this.root),
massSelector: new HUDMassSelector(this.root), massSelector: new HUDMassSelector(this.root),

View File

@ -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 { STOP_PROPAGATION } from "../../../core/signal";
import { TrackedState } from "../../../core/tracked_state"; import { TrackedState } from "../../../core/tracked_state";
import { Vector } from "../../../core/vector"; import { Vector } from "../../../core/vector";
@ -36,6 +36,9 @@ export class HUDBlueprintPlacer extends BaseHUDPart {
.getBinding(KEYMAPPINGS.placement.abortBuildingPlacement) .getBinding(KEYMAPPINGS.placement.abortBuildingPlacement)
.add(this.abortPlacement, this); .add(this.abortPlacement, this);
keyActionMapper.getBinding(KEYMAPPINGS.placement.rotateWhilePlacing).add(this.rotateBlueprint, 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.downPreHandler.add(this.onMouseDown, this);
this.root.camera.movePreHandler.add(this.onMouseMove, this); this.root.camera.movePreHandler.add(this.onMouseMove, this);

View File

@ -89,6 +89,16 @@ for (const categoryId in KEYMAPPINGS) {
*/ */
export function getStringForKeyCode(code) { export function getStringForKeyCode(code) {
switch (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: case 8:
return "⌫"; return "⌫";
case 9: case 9:

View File

@ -66,11 +66,6 @@ export class GoogleAnalyticsImpl extends AnalyticsInterface {
} }
trackUiClick(elementName) { 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 stateKey = this.app.stateMgr.getCurrentState().key;
const fullSelector = stateKey + ">" + elementName; const fullSelector = stateKey + ">" + elementName;

View File

@ -8,6 +8,7 @@ import { createLogger } from "../core/logging";
import { ExplainedResult } from "../core/explained_result"; import { ExplainedResult } from "../core/explained_result";
import { THEMES, THEME, applyGameTheme } from "../game/theme"; import { THEMES, THEME, applyGameTheme } from "../game/theme";
import { IS_DEMO } from "../core/config"; import { IS_DEMO } from "../core/config";
import { T } from "../translations";
const logger = createLogger("application_settings"); const logger = createLogger("application_settings");
@ -18,27 +19,45 @@ export const uiScales = [
{ {
id: "super_small", id: "super_small",
size: 0.6, size: 0.6,
label: "Super small",
}, },
{ {
id: "small", id: "small",
size: 0.8, size: 0.8,
label: "Small",
}, },
{ {
id: "regular", id: "regular",
size: 1, size: 1,
label: "Regular",
}, },
{ {
id: "large", id: "large",
size: 1.2, size: 1.2,
label: "Large",
}, },
{ {
id: "huge", id: "huge",
size: 1.4, 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", { new EnumSetting("uiScale", {
options: uiScales.sort((a, b) => a.size - b.size), options: uiScales.sort((a, b) => a.size - b.size),
valueGetter: scale => scale.id, valueGetter: scale => scale.id,
textGetter: scale => scale.label, textGetter: scale => T.settings.labels.uiScale.scales[scale.id],
category: categoryApp, category: categoryApp,
restartRequired: false, restartRequired: false,
changeCb: changeCb:
@ -56,6 +75,7 @@ export const allApplicationSettings = [
*/ */
(app, id) => app.updateAfterUiScaleChanged(), (app, id) => app.updateAfterUiScaleChanged(),
}), }),
new BoolSetting( new BoolSetting(
"fullscreen", "fullscreen",
categoryApp, categoryApp,
@ -86,6 +106,18 @@ export const allApplicationSettings = [
*/ */
(app, value) => app.sound.setMusicMuted(value) (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 // GAME
new EnumSetting("theme", { new EnumSetting("theme", {
@ -133,6 +165,7 @@ class SettingsStorage {
this.musicMuted = false; this.musicMuted = false;
this.theme = "light"; this.theme = "light";
this.refreshRate = "60"; this.refreshRate = "60";
this.scrollWheelSensitivity = "regular";
this.alwaysMultiplace = false; this.alwaysMultiplace = false;
this.abortPlacementOnDeletion = true; this.abortPlacementOnDeletion = true;
@ -209,6 +242,17 @@ export class ApplicationSettings extends ReadWriteProxy {
return 1; 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() { getIsFullScreen() {
return this.getAllSettings().fullscreen; return this.getAllSettings().fullscreen;
} }
@ -295,7 +339,7 @@ export class ApplicationSettings extends ReadWriteProxy {
} }
getCurrentVersion() { getCurrentVersion() {
return 8; return 9;
} }
/** @param {{settings: SettingsStorage, version: number}} data */ /** @param {{settings: SettingsStorage, version: number}} data */
@ -318,10 +362,15 @@ export class ApplicationSettings extends ReadWriteProxy {
} }
if (data.version < 8) { if (data.version < 8) {
data.settings.abortPlacementOnDeletion = true; data.settings.scrollWheelSensitivity = "regular";
data.version = 8; data.version = 8;
} }
if (data.version < 9) {
data.settings.abortPlacementOnDeletion = true;
data.version = 9;
}
return ExplainedResult.good(); return ExplainedResult.good();
} }
} }

View File

@ -10,8 +10,9 @@ import { BaseSavegameInterface } from "./savegame_interface";
import { createLogger } from "../core/logging"; import { createLogger } from "../core/logging";
import { globalConfig } from "../core/config"; import { globalConfig } from "../core/config";
import { SavegameInterface_V1000 } from "./schemas/1000"; 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_V1001 } from "./schemas/1001";
import { SavegameInterface_V1002 } from "./schemas/1002";
const logger = createLogger("savegame"); const logger = createLogger("savegame");
@ -30,6 +31,11 @@ export class Savegame extends ReadWriteProxy {
/** @type {import("./savegame_typedefs").SavegameData} */ /** @type {import("./savegame_typedefs").SavegameData} */
this.currentData = this.getDefaultData(); this.currentData = this.getDefaultData();
assert(
savegameInterfaces[Savegame.getCurrentVersion()],
"Savegame interface not defined: " + Savegame.getCurrentVersion()
);
} }
//////// RW Proxy Impl ////////// //////// RW Proxy Impl //////////
@ -38,14 +44,14 @@ export class Savegame extends ReadWriteProxy {
* @returns {number} * @returns {number}
*/ */
static getCurrentVersion() { static getCurrentVersion() {
return 1001; return 1002;
} }
/** /**
* @returns {typeof BaseSavegameInterface} * @returns {typeof BaseSavegameInterface}
*/ */
static getReaderClass() { static getReaderClass() {
return SavegameInterface_V1001; return savegameInterfaces[Savegame.getCurrentVersion()];
} }
/** /**
@ -82,6 +88,11 @@ export class Savegame extends ReadWriteProxy {
data.version = 1001; data.version = 1001;
} }
if (data.version === 1001) {
SavegameInterface_V1002.migrate1001to1002(data);
data.version = 1002;
}
return ExplainedResult.good(); return ExplainedResult.good();
} }

View File

@ -2,11 +2,13 @@ import { BaseSavegameInterface } from "./savegame_interface";
import { SavegameInterface_V1000 } from "./schemas/1000"; import { SavegameInterface_V1000 } from "./schemas/1000";
import { createLogger } from "../core/logging"; import { createLogger } from "../core/logging";
import { SavegameInterface_V1001 } from "./schemas/1001"; import { SavegameInterface_V1001 } from "./schemas/1001";
import { SavegameInterface_V1002 } from "./schemas/1002";
/** @type {Object.<number, typeof BaseSavegameInterface>} */ /** @type {Object.<number, typeof BaseSavegameInterface>} */
const interfaces = { export const savegameInterfaces = {
1000: SavegameInterface_V1000, 1000: SavegameInterface_V1000,
1001: SavegameInterface_V1001, 1001: SavegameInterface_V1001,
1002: SavegameInterface_V1002,
}; };
const logger = createLogger("savegame_interface_registry"); const logger = createLogger("savegame_interface_registry");
@ -27,7 +29,7 @@ export function getSavegameInterface(savegame) {
return null; return null;
} }
const interfaceClass = interfaces[version]; const interfaceClass = savegameInterfaces[version];
if (!interfaceClass) { if (!interfaceClass) {
logger.warn("Version", version, "has no implemented interface!"); logger.warn("Version", version, "has no implemented interface!");
return null; return null;

View File

@ -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;
}
}
}
}

View File

@ -0,0 +1,5 @@
{
"type": "object",
"required": [],
"additionalProperties": true
}

View File

@ -105,6 +105,10 @@ export class KeybindingsState extends TextualGameState {
event.preventDefault(); event.preventDefault();
} }
if (event.target && event.target.tagName === "BUTTON" && keyCode === 1) {
return;
}
if ( if (
// Enter // Enter
keyCode === 13 || keyCode === 13 ||
@ -122,8 +126,8 @@ export class KeybindingsState extends TextualGameState {
}); });
dialog.inputReciever.backButton.add(() => {}); dialog.inputReciever.backButton.add(() => {});
this.dialogs.internalShowDialog(dialog); this.dialogs.internalShowDialog(dialog);
this.app.sound.playUiSound(SOUNDS.dialogOk); this.app.sound.playUiSound(SOUNDS.dialogOk);
} }

View File

@ -26,6 +26,13 @@ global:
# How big numbers are rendered, e.g. "10,000" # How big numbers are rendered, e.g. "10,000"
thousandsDivider: "," 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 # Shown for infinitely big numbers
infinite: inf infinite: inf
@ -127,7 +134,7 @@ dialogs:
editKeybinding: editKeybinding:
title: Change Keybinding 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: resetKeybindingsConfirmation:
title: Reset keybindings title: Reset keybindings
@ -511,6 +518,23 @@ settings:
title: Interface scale title: Interface scale
description: >- 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. 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: fullscreen:
title: Fullscreen title: Fullscreen

View File

@ -1 +1 @@
1.1.2 1.1.3