diff --git a/artwork/buildings/buildings_2x2.psd b/artwork/buildings/buildings_2x2.psd index 027486dd..1bb23259 100644 Binary files a/artwork/buildings/buildings_2x2.psd and b/artwork/buildings/buildings_2x2.psd differ diff --git a/artwork/itch.io/background.jpg b/artwork/itch.io/background.jpg new file mode 100644 index 00000000..619876a2 Binary files /dev/null and b/artwork/itch.io/background.jpg differ diff --git a/artwork/itch.io/background.png b/artwork/itch.io/background.png new file mode 100644 index 00000000..674b7fab Binary files /dev/null and b/artwork/itch.io/background.png differ diff --git a/artwork/itch.io/banner.png b/artwork/itch.io/banner.png new file mode 100644 index 00000000..bff1dc6c Binary files /dev/null and b/artwork/itch.io/banner.png differ diff --git a/artwork/itch.io/banner.psd b/artwork/itch.io/banner.psd new file mode 100644 index 00000000..6fb9ef3c Binary files /dev/null and b/artwork/itch.io/banner.psd differ diff --git a/artwork/logo.png b/artwork/logo.png new file mode 100644 index 00000000..14141d58 Binary files /dev/null and b/artwork/logo.png differ diff --git a/artwork/steam/screenshots/9.png b/artwork/steam/screenshots/9.png index 9bf9534f..376ee4e4 100644 Binary files a/artwork/steam/screenshots/9.png and b/artwork/steam/screenshots/9.png differ diff --git a/res/ui/get_on_itch_io.svg b/res/ui/get_on_itch_io.svg new file mode 100644 index 00000000..f6dde21e --- /dev/null +++ b/res/ui/get_on_itch_io.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/res_raw/atlas.tps b/res_raw/atlas.tps index 0190d204..883a6932 100644 --- a/res_raw/atlas.tps +++ b/res_raw/atlas.tps @@ -375,6 +375,7 @@ sprites/blueprints/painter-double.png + sprites/blueprints/trash-storage.png sprites/buildings/painter-double.png pivotPoint @@ -475,6 +476,21 @@ scale9FromFile + sprites/misc/storage_overlay.png + + pivotPoint + 0.5,0.5 + spriteScale + 1 + scale9Enabled + + scale9Borders + 44,22,89,43 + scale9Paddings + 44,22,89,43 + scale9FromFile + + fileList diff --git a/src/css/main.scss b/src/css/main.scss index e4ee1b24..2d3f6857 100644 --- a/src/css/main.scss +++ b/src/css/main.scss @@ -71,8 +71,8 @@ ingame_HUD_BetaOverlay, ingame_HUD_UnlockNotification, ingame_HUD_Shop, ingame_HUD_Statistics, -ingame_HUD_ModalDialogs, -ingame_HUD_SettingsMenu; +ingame_HUD_SettingsMenu, +ingame_HUD_ModalDialogs; $zindex: 100; diff --git a/src/css/states/main_menu.scss b/src/css/states/main_menu.scss index 42f24c15..553fd960 100644 --- a/src/css/states/main_menu.scss +++ b/src/css/states/main_menu.scss @@ -109,7 +109,7 @@ width: 100%; @include S(height, 50px); - background: uiResource("get_on_steam.png") center center / contain no-repeat; + background: uiResource("get_on_itch_io.svg") center center / contain no-repeat; overflow: hidden; display: block; text-indent: -999em; diff --git a/src/js/core/config.js b/src/js/core/config.js index 31ced8c2..04c7b1a3 100644 --- a/src/js/core/config.js +++ b/src/js/core/config.js @@ -5,8 +5,9 @@ export const IS_DEBUG = (window.location.host.indexOf("localhost:") >= 0 || window.location.host.indexOf("192.168.0.") >= 0) && window.location.search.indexOf("nodebug") < 0; -// export const IS_DEMO = G_IS_PROD; -export const IS_DEMO = G_IS_RELEASE; +export const IS_DEMO = + (G_IS_RELEASE && !G_IS_STANDALONE) || + (typeof window !== "undefined" && window.location.search.indexOf("demo") >= 0); const smoothCanvas = true; @@ -14,7 +15,8 @@ export const THIRDPARTY_URLS = { discord: "https://discord.gg/HN7EVzV", github: "https://github.com/tobspr/shapez.io", - standaloneStorePage: "https://steam.shapez.io", + // standaloneStorePage: "https://steam.shapez.io", + standaloneStorePage: "https://tobspr.itch.io/shapez.io", }; export const globalConfig = { diff --git a/src/js/game/camera.js b/src/js/game/camera.js index 671f3b6e..99df0675 100644 --- a/src/js/game/camera.js +++ b/src/js/game/camera.js @@ -330,7 +330,7 @@ export class Camera extends BasicSerializableObject { * Binds the arrow keys */ bindKeys() { - const mapper = this.root.gameState.keyActionMapper; + const mapper = this.root.keyMapper; mapper.getBinding(KEYMAPPINGS.ingame.mapMoveUp).add(() => (this.keyboardForce.y = -1)); mapper.getBinding(KEYMAPPINGS.ingame.mapMoveDown).add(() => (this.keyboardForce.y = 1)); mapper.getBinding(KEYMAPPINGS.ingame.mapMoveRight).add(() => (this.keyboardForce.x = 1)); @@ -867,7 +867,7 @@ export class Camera extends BasicSerializableObject { let forceX = 0; let forceY = 0; - const actionMapper = this.root.gameState.keyActionMapper; + const actionMapper = this.root.keyMapper; if (actionMapper.getBinding(KEYMAPPINGS.ingame.mapMoveUp).currentlyDown) { forceY -= 1; } diff --git a/src/js/game/core.js b/src/js/game/core.js index 73346504..3d2c1f3d 100644 --- a/src/js/game/core.js +++ b/src/js/game/core.js @@ -75,6 +75,7 @@ export class GameCore { // Construct the root element, this is the data representation of the game this.root = new GameRoot(this.app); this.root.gameState = parentState; + this.root.keyMapper = parentState.keyActionMapper; this.root.savegame = savegame; this.root.gameWidth = this.app.screenWidth; this.root.gameHeight = this.app.screenHeight; @@ -86,7 +87,7 @@ export class GameCore { const root = this.root; // This isn't nice, but we need it right here - root.gameState.keyActionMapper = new KeyActionMapper(root, this.root.gameState.inputReciever); + root.keyMapper = new KeyActionMapper(root, this.root.gameState.inputReciever); // Needs to come first root.dynamicTickrate = new DynamicTickrate(root); diff --git a/src/js/game/hud/base_hud_part.js b/src/js/game/hud/base_hud_part.js index a700a6b3..032530fb 100644 --- a/src/js/game/hud/base_hud_part.js +++ b/src/js/game/hud/base_hud_part.js @@ -141,10 +141,7 @@ export class BaseHUDPart { * @param {KeyActionMapper} sourceMapper */ forwardGameSpeedKeybindings(sourceMapper) { - sourceMapper.forward(this.root.gameState.keyActionMapper, [ - "gamespeed_pause", - "gamespeed_fastforward", - ]); + sourceMapper.forward(this.root.keyMapper, ["gamespeed_pause", "gamespeed_fastforward"]); } /** @@ -153,7 +150,7 @@ export class BaseHUDPart { * @param {KeyActionMapper} sourceMapper */ forwardMapMovementKeybindings(sourceMapper) { - sourceMapper.forward(this.root.gameState.keyActionMapper, [ + sourceMapper.forward(this.root.keyMapper, [ "mapMoveUp", "mapMoveRight", "mapMoveDown", diff --git a/src/js/game/hud/hud.js b/src/js/game/hud/hud.js index cb591687..b66c85e3 100644 --- a/src/js/game/hud/hud.js +++ b/src/js/game/hud/hud.js @@ -97,7 +97,7 @@ export class GameHUD { } this.internalInitSignalConnections(); - this.root.gameState.keyActionMapper.getBinding(KEYMAPPINGS.ingame.toggleHud).add(this.toggleUi, this); + this.root.keyMapper.getBinding(KEYMAPPINGS.ingame.toggleHud).add(this.toggleUi, this); } /** diff --git a/src/js/game/hud/parts/building_placer.js b/src/js/game/hud/parts/building_placer.js index 8c83f17d..a581a0b3 100644 --- a/src/js/game/hud/parts/building_placer.js +++ b/src/js/game/hud/parts/building_placer.js @@ -30,7 +30,7 @@ export class HUDBuildingPlacer extends BaseHUDPart { /** @type {Entity} */ this.fakeEntity = null; - const keyActionMapper = this.root.gameState.keyActionMapper; + const keyActionMapper = this.root.keyMapper; keyActionMapper .getBinding(KEYMAPPINGS.placement.abortBuildingPlacement) .add(this.abortPlacement, this); @@ -284,9 +284,7 @@ export class HUDBuildingPlacer extends BaseHUDPart { this.buildingInfoElements.label.innerHTML = T.buildings[metaBuilding.id].name; this.buildingInfoElements.descText.innerHTML = T.buildings[metaBuilding.id].description; - const binding = this.root.gameState.keyActionMapper.getBinding( - KEYMAPPINGS.buildings[metaBuilding.getId()] - ); + const binding = this.root.keyMapper.getBinding(KEYMAPPINGS.buildings[metaBuilding.getId()]); this.buildingInfoElements.hotkey.innerHTML = T.ingame.buildingPlacement.hotkeyLabel.replace( "", "" + binding.getKeyCodeString() + "" @@ -327,7 +325,7 @@ export class HUDBuildingPlacer extends BaseHUDPart { T.ingame.buildingPlacement.cycleBuildingVariants.replace( "", "" + - this.root.gameState.keyActionMapper + this.root.keyMapper .getBinding(KEYMAPPINGS.placement.cycleBuildingVariants) .getKeyCodeString() + "" diff --git a/src/js/game/hud/parts/buildings_toolbar.js b/src/js/game/hud/parts/buildings_toolbar.js index f7a21a1b..86c99a01 100644 --- a/src/js/game/hud/parts/buildings_toolbar.js +++ b/src/js/game/hud/parts/buildings_toolbar.js @@ -60,7 +60,7 @@ export class HUDBuildingsToolbar extends BaseHUDPart { } initialize() { - const actionMapper = this.root.gameState.keyActionMapper; + const actionMapper = this.root.keyMapper; const items = makeDiv(this.element, null, ["buildings"]); @@ -143,6 +143,15 @@ export class HUDBuildingsToolbar extends BaseHUDPart { return; } + // Allow clicking an item again to deselect it + for (const buildingId in this.buildingHandles) { + const handle = this.buildingHandles[buildingId]; + if (handle.selected && handle.metaBuilding === metaBuilding) { + metaBuilding = null; + break; + } + } + this.root.soundProxy.playUiClick(); this.sigBuildingSelected.dispatch(metaBuilding); this.onSelectedPlacementBuildingChanged(metaBuilding); diff --git a/src/js/game/hud/parts/debug_info.js b/src/js/game/hud/parts/debug_info.js index 745fbbd5..59a1d98e 100644 --- a/src/js/game/hud/parts/debug_info.js +++ b/src/js/game/hud/parts/debug_info.js @@ -1,6 +1,7 @@ import { BaseHUDPart } from "../base_hud_part"; import { makeDiv, round3Digits, round2Digits } from "../../../core/utils"; import { Math_round } from "../../../core/builtins"; +import { DynamicDomAttach } from "../dynamic_dom_attach"; export class HUDDebugInfo extends BaseHUDPart { createElements(parent) { @@ -13,6 +14,11 @@ export class HUDDebugInfo extends BaseHUDPart { initialize() { this.lastTick = 0; + + this.visible = false; + this.domAttach = new DynamicDomAttach(this.root, this.element); + + // this.root.keyMapper } update() { diff --git a/src/js/game/hud/parts/game_menu.js b/src/js/game/hud/parts/game_menu.js index 86ad2564..b71adff4 100644 --- a/src/js/game/hud/parts/game_menu.js +++ b/src/js/game/hud/parts/game_menu.js @@ -47,7 +47,7 @@ export class HUDGameMenu extends BaseHUDPart { this.trackClicks(button, handler); if (keybinding) { - const binding = this.root.gameState.keyActionMapper.getBinding(keybinding); + const binding = this.root.keyMapper.getBinding(keybinding); binding.add(handler); binding.appendLabelToElement(button); } diff --git a/src/js/game/hud/parts/keybinding_overlay.js b/src/js/game/hud/parts/keybinding_overlay.js index a1a941dc..6f6040b6 100644 --- a/src/js/game/hud/parts/keybinding_overlay.js +++ b/src/js/game/hud/parts/keybinding_overlay.js @@ -20,7 +20,7 @@ export class HUDKeybindingOverlay extends BaseHUDPart { } createElements(parent) { - const mapper = this.root.gameState.keyActionMapper; + const mapper = this.root.keyMapper; const getKeycode = id => { return getStringForKeyCode(mapper.getBinding(id).keyCode); diff --git a/src/js/game/hud/parts/mass_selector.js b/src/js/game/hud/parts/mass_selector.js index 3dca5678..ddcf9117 100644 --- a/src/js/game/hud/parts/mass_selector.js +++ b/src/js/game/hud/parts/mass_selector.js @@ -16,12 +16,10 @@ const logger = createLogger("hud/mass_selector"); export class HUDMassSelector extends BaseHUDPart { createElements(parent) { - const removalKeybinding = this.root.gameState.keyActionMapper + const removalKeybinding = this.root.keyMapper .getBinding(KEYMAPPINGS.massSelect.confirmMassDelete) .getKeyCodeString(); - const abortKeybinding = this.root.gameState.keyActionMapper - .getBinding(KEYMAPPINGS.general.back) - .getKeyCodeString(); + const abortKeybinding = this.root.keyMapper.getBinding(KEYMAPPINGS.general.back).getKeyCodeString(); this.element = makeDiv( parent, @@ -46,8 +44,8 @@ export class HUDMassSelector extends BaseHUDPart { this.root.camera.movePreHandler.add(this.onMouseMove, this); this.root.camera.upPostHandler.add(this.onMouseUp, this); - this.root.gameState.keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.onBack, this); - this.root.gameState.keyActionMapper + this.root.keyMapper.getBinding(KEYMAPPINGS.general.back).add(this.onBack, this); + this.root.keyMapper .getBinding(KEYMAPPINGS.massSelect.confirmMassDelete) .add(this.confirmDelete, this); diff --git a/src/js/game/hud/parts/settings_menu.js b/src/js/game/hud/parts/settings_menu.js index 4acc04b1..28ceb9ff 100644 --- a/src/js/game/hud/parts/settings_menu.js +++ b/src/js/game/hud/parts/settings_menu.js @@ -7,6 +7,7 @@ import { T } from "../../../translations"; import { StaticMapEntityComponent } from "../../components/static_map_entity"; import { ItemProcessorComponent } from "../../components/item_processor"; import { BeltComponent } from "../../components/belt"; +import { IS_DEMO } from "../../../core/config"; export class HUDSettingsMenu extends BaseHUDPart { createElements(parent) { @@ -56,7 +57,16 @@ export class HUDSettingsMenu extends BaseHUDPart { } returnToMenu() { - this.root.gameState.goBackToMenu(); + if (IS_DEMO) { + const { cancel, deleteGame } = this.root.hud.parts.dialogs.showWarning( + T.dialogs.leaveNotPossibleInDemo.title, + T.dialogs.leaveNotPossibleInDemo.desc, + ["cancel:good", "deleteGame:bad"] + ); + deleteGame.add(() => this.root.gameState.goBackToMenu()); + } else { + this.root.gameState.goBackToMenu(); + } } goToSettings() { @@ -72,7 +82,7 @@ export class HUDSettingsMenu extends BaseHUDPart { } initialize() { - this.root.gameState.keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.show, this); + this.root.keyMapper.getBinding(KEYMAPPINGS.general.back).add(this.show, this); this.domAttach = new DynamicDomAttach(this.root, this.background, { attachClass: "visible", diff --git a/src/js/game/key_action_mapper.js b/src/js/game/key_action_mapper.js index 6aff0e88..eab0f408 100644 --- a/src/js/game/key_action_mapper.js +++ b/src/js/game/key_action_mapper.js @@ -31,6 +31,7 @@ export const KEYMAPPINGS = { menuOpenStats: { keyCode: key("G") }, toggleHud: { keyCode: 113 }, // F2 + toggleFPSInfo: { keyCode: 112 }, // F1 }, buildings: { @@ -362,7 +363,7 @@ export class KeyActionMapper { for (const key in this.keybindings) { /** @type {Keybinding} */ const binding = this.keybindings[key]; - if (binding.keyCode === keyCode /* && binding.shift === shift && binding.alt === alt */) { + if (binding.keyCode === keyCode && !binding.currentlyDown) { binding.currentlyDown = true; /** @type {Signal} */ diff --git a/src/js/game/map_chunk.js b/src/js/game/map_chunk.js index f51f63a0..bffaf9e8 100644 --- a/src/js/game/map_chunk.js +++ b/src/js/game/map_chunk.js @@ -163,17 +163,17 @@ export class MapChunk { [enumSubShape.windmill]: Math_round(6 + clamp(distanceToOriginInChunks / 2, 0, 20)), }; - if (distanceToOriginInChunks < 4) { + if (distanceToOriginInChunks < 7) { // Initial chunks can not spawn the good stuff weights[enumSubShape.star] = 0; weights[enumSubShape.windmill] = 0; } - if (distanceToOriginInChunks < 7) { + if (distanceToOriginInChunks < 10) { // Initial chunk patches always have the same shape const subShape = this.internalGenerateRandomSubShape(rng, weights); subShapes = [subShape, subShape, subShape, subShape]; - } else if (distanceToOriginInChunks < 17) { + } else if (distanceToOriginInChunks < 15) { // Later patches can also have mixed ones const subShapeA = this.internalGenerateRandomSubShape(rng, weights); const subShapeB = this.internalGenerateRandomSubShape(rng, weights); @@ -269,22 +269,12 @@ export class MapChunk { return true; } if (this.x === -1 && this.y === 0) { - const definition = this.root.shapeDefinitionMgr.getDefinitionFromSimpleShapes([ - enumSubShape.circle, - enumSubShape.circle, - enumSubShape.circle, - enumSubShape.circle, - ]); + const definition = this.root.shapeDefinitionMgr.getShapeFromShortKey("CuCuCuCu"); this.internalGeneratePatch(rng, 2, new ShapeItem(definition), globalConfig.mapChunkSize - 9, 7); return true; } if (this.x === 0 && this.y === -1) { - const definition = this.root.shapeDefinitionMgr.getDefinitionFromSimpleShapes([ - enumSubShape.rect, - enumSubShape.rect, - enumSubShape.rect, - enumSubShape.rect, - ]); + const definition = this.root.shapeDefinitionMgr.getShapeFromShortKey("RuRuRuRu"); this.internalGeneratePatch(rng, 2, new ShapeItem(definition), 5, globalConfig.mapChunkSize - 7); return true; } @@ -294,6 +284,12 @@ export class MapChunk { return true; } + if (this.x === 5 && this.y === -2) { + const definition = this.root.shapeDefinitionMgr.getShapeFromShortKey("SuSuSuSu"); + this.internalGeneratePatch(rng, 2, new ShapeItem(definition), 5, globalConfig.mapChunkSize - 7); + return true; + } + return false; } diff --git a/src/js/game/root.js b/src/js/game/root.js index f73f9a9d..0c4e6792 100644 --- a/src/js/game/root.js +++ b/src/js/game/root.js @@ -27,6 +27,7 @@ import { Entity } from "./entity"; import { ShapeDefinition } from "./shape_definition"; import { BaseItem } from "./base_item"; import { DynamicTickrate } from "./dynamic_tickrate"; +import { KeyActionMapper } from "./key_action_mapper"; /* typehints:end */ const logger = createLogger("game/root"); @@ -50,6 +51,9 @@ export class GameRoot { /** @type {InGameState} */ this.gameState = null; + /** @type {KeyActionMapper} */ + this.keyMapper = null; + // Store game dimensions this.gameWidth = 500; this.gameHeight = 500; diff --git a/src/js/states/main_menu.js b/src/js/states/main_menu.js index cb93156b..48c42041 100644 --- a/src/js/states/main_menu.js +++ b/src/js/states/main_menu.js @@ -194,8 +194,7 @@ export class MainMenuState extends GameState { onSteamLinkClicked(event) { this.app.analytics.trackUiClick("main_menu_steam_link"); - alert("The steam version will launch very soon! (Planned date: Begin of June 2020)"); - // window.open("https://steam.shapez.io"); + window.open(THIRDPARTY_URLS.standaloneStorePage); event.preventDefault(); return false; } diff --git a/translations/base-en.yaml b/translations/base-en.yaml index 37f111b1..d8e5373a 100644 --- a/translations/base-en.yaml +++ b/translations/base-en.yaml @@ -59,7 +59,7 @@ demoBanners: # This is the "advertisement" shown in the main menu and other various places title: This is a demo version intro: >- - Get shapez.io on steam to: + Get the shapez.io standalone to: advantages: - Save and resume your games. - No advertisements. @@ -84,6 +84,7 @@ dialogs: restart: Restart reset: Reset getStandalone: Get Standalone + deleteGame: Delete Progress importSavegameError: title: Import Error @@ -134,6 +135,10 @@ dialogs: saveNotPossibleInDemo: desc: Your game has been saved, but restoring it is only possible in the standalone version. Consider to get the standalone for the full experience! + leaveNotPossibleInDemo: + title: Saving not possible + desc: You can not save in the demo. Your game will be lost. Are you sure? + ingame: # This is shown in the top left corner and displays useful keybindings in # every situation