From 46e0c1494abe85ec61fbfc5d8ef88bb0c6721de7 Mon Sep 17 00:00:00 2001 From: tobspr Date: Thu, 29 Apr 2021 22:31:06 +0200 Subject: [PATCH] Puzzle mode, part 2 --- gulp/bundle-loader.js | 7 +- res/ui/building_icons/constant_producer.png | Bin 3494 -> 3674 bytes res/ui/building_icons/goal_acceptor.png | Bin 3590 -> 3633 bytes res/ui/languages/he.svg | 42 ++++++++++ res_raw/sprites/buildings/goal_acceptor.png | Bin 16989 -> 15770 bytes src/css/ingame_hud/mode_menu_back.scss | 7 +- src/css/ingame_hud/mode_menu_next.scss | 2 +- src/css/ingame_hud/puzzle_dlc_logo.scss | 4 +- .../ingame_hud/puzzle_editor_controls.scss | 26 +++++++ src/css/main.scss | 3 + src/css/resources.scss | 2 +- src/css/states/puzzle_menu.scss | 5 +- src/js/core/animation_frame.js | 7 +- src/js/core/error_handler.js | 4 +- src/js/game/camera.js | 9 ++- src/js/game/components/goal_acceptor.js | 2 - src/js/game/core.js | 4 + src/js/game/game_mode.js | 65 ++++++---------- src/js/game/game_system_manager.js | 2 +- src/js/game/hub_goals.js | 21 +++++ src/js/game/hud/hud.js | 2 + src/js/game/hud/parts/base_toolbar.js | 2 +- src/js/game/hud/parts/mode_menu_back.js | 4 +- src/js/game/hud/parts/mode_settings.js | 10 ++- .../game/hud/parts/puzzle_editor_controls.js | 21 +++++ src/js/game/hud/parts/wires_overlay.js | 3 + src/js/game/map_chunk_view.js | 2 +- src/js/game/modes/puzzle.js | 62 ++++----------- src/js/game/modes/puzzle_edit.js | 47 ++++++++---- src/js/game/modes/regular.js | 10 +-- src/js/game/systems/goal_acceptor.js | 15 +--- src/js/game/systems/zone.js | 72 +++++++++++++----- src/js/game/themes/dark.json | 6 +- src/js/game/themes/light.json | 4 +- src/js/languages.js | 8 ++ src/js/states/ingame.js | 8 +- src/js/states/puzzle_menu.js | 5 ++ translations/base-en.yaml | 4 + translations/{base-he.yml => base-he.yaml} | 13 ++-- 39 files changed, 332 insertions(+), 178 deletions(-) create mode 100644 res/ui/languages/he.svg create mode 100644 src/css/ingame_hud/puzzle_editor_controls.scss create mode 100644 src/js/game/hud/parts/puzzle_editor_controls.js rename translations/{base-he.yml => base-he.yaml} (97%) diff --git a/gulp/bundle-loader.js b/gulp/bundle-loader.js index d8bb8c24..16db26fa 100644 --- a/gulp/bundle-loader.js +++ b/gulp/bundle-loader.js @@ -54,8 +54,11 @@ document.documentElement.appendChild(element); } - window.addEventListener("error", errorHandler); - window.addEventListener("unhandledrejection", errorHandler); + + if (window.location.host.indexOf("localhost") < 0) { + window.addEventListener("error", errorHandler); + window.addEventListener("unhandledrejection", errorHandler); + } function makeJsTag(src, integrity) { var script = document.createElement("script"); diff --git a/res/ui/building_icons/constant_producer.png b/res/ui/building_icons/constant_producer.png index 887a3ae636aa980ae6232278434fb6db43821528..f7ac8afa45730c2ee24193a97f52469ea637218d 100644 GIT binary patch delta 2840 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 delta 2755 zcmb_eX*iUP0)1x;Cd3f3#UL5$$1seDF{VO`HA_l-)+}iw`#W}#otZw7B}>^g&7>|F zuGDB z#^cTKI;Lj&cwH>UOk3}?Sxz)m=D#Ad|0#M6^*oOCHgNYs>ti*%v~TB1=JuaHM}h)Pzaiq*zvhMvZ2h|QIgGPegUaeVMl99$tjpMvQzj%c)Of9_1n4o$@%#N=`&JMSa7fP-?*j(u@usS)i9_qaTb{DKXj_7Xj>DE29 z*F`!0Q!{@~91eluff&bW|I830`t5A5_&L})^0}2w5vHN~yi@n{$f;ncK*NLQMyxEs zm*lxeuYn*+fwx8YEjWjVnIvobMp$-erERF?3=z(Jr0y=6fKlV{oO?-z0~9%t@yWL0 zXe*;>E~75C`7f{ohcs`A+Up-2G}X}1sO!J2Nq#agAn>tA3H4|kwZExCBKt2(48DUF z=-TdEuOWg%egxODAy=g9w51HsN4HM;lIz3W;SwqnpN8aaQD@SUiT;}N6HQrwa*F;7 zDmCK9Cx*3ScgrnB$mMfVpO;z=SsY0nn&XDTrQmo#9wg%6$e-$wsu;bZ9vk*H)C+xm zIl{vr*w}Anq3mXpqe`WNB-BkjaJ%z(ZGAn)m!I;27LtSh?iFZzPD})4HsE>tO-vqm z)Yk+33TcchhfwPI97O%9(XjXP?9Q?Nj~_4mg80S99qvuHxDRE;xx71@(bG?X9GUef z$_oR)7z7RpL;T0_{~sNQXK^cppXgwO6VfJ>=-f6XXA^bAyc`{-HpwK^X{&W$rR^GPfcUzOTR`rVpd+X8{;x*ftyS?53uQoJ- z{@>6h$6q$$SeI6xu&gEZL-@kna@xm)h{IuDUu?d*kQjbj7n~O<2kVC!7F4Zq}D7E<7TaBdTkOUUb5UIJ8QB8%Mryf zxf!39OY4wvu9NL4X})}du-J}{#wR|If*a`kH`Segh(Y@ue{Cvwnk{(KghUS| zBu0cYqn)^p-|zS$wRW~9J;uXen#ccB%~Q5;>4gRPNA5v`9UBjW>tEurW7Wr6=bkqdsL=FR#s{XciAb9f2?kW4^maV+w+q#8t&^5 zF8}dkeyJO~q{*_BT=>p+DRUs~Gws?f=~fNcQ`s}l#zAXBMi?W}Q)Mbumr+h6jY;|% zEv?F-k)Q-My1pGaDrNJ^z4hCQm6^wZGhE_>Qcx=$i(u$_oe3_k9@x zOO><@!O?k^oJ5(lzAc_8Ra|ELASd5cO-(ZYTvu;L$9NL@`EFErxMejCs@&ThHXSN; z!2}Lb%>c6*2{%R!u;eF;3+vAoD|YA#p1n+6#IV(}U`e-R~|& zUA41z7j(;GY-1xh!)a+L2iVfY?`k#CNU>XUnJ*Cz_%}J6<4_>U+oo4)SzO?Z`FGZae3T(tfz_PRM!i zt;G3ur_u&{nO+~n(9jS{wx}GlA>9etO@SGb#j%wl5PNn&$SFqpX+`ygcguR>e3ay2ab#W>~t0H zzG`kj!Ro8`E#iqR_x`6X`APMaL`NMB0kiw!D(&iu34K-N$<^5+JU;93SxFhLE4tic zKUAj&n@z1LJbQa_-dS%qe=j>DIg34AwzUW0P3f`Q9l4a>eE0LM5|(vSl0qDBr(ZR~ zZ4>o<&*`r_Gt*|%)TR=E zpx51oGA;PkoVd!u6-S3^4qugR9UT!PEJGfyW<|;>VvrNlKx(okbeqMs#_a zsu|AWvEu~YKdE$P$UDRma)4p3nxj7-7-O`JH;#?u`|{;&^EjuSkJt?Eqtm0yC-_(= z_^q?O6Oyoc`ubGaj7HfK8Bwp2U1@czhMA%-6_JOx`4pe0O`ubeTDj+4x9h*jeO)yO zbg>1SYv7h%Fbw#w*JJr2r?X2yuk9b|8Ma|zFe}y|{W_l(i*Mgt9p?p!I8f$g)UrJG z;CV#^_6eptY4aE@*glcS0=_$>>TMh-GfhcyCpj9;@1d6%uGFnZCf&(o=( zSON-I-= + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res_raw/sprites/buildings/goal_acceptor.png b/res_raw/sprites/buildings/goal_acceptor.png index 9e5f08085b0cdead217f4f9d16ac29b988f75926..2ebf879a242cfe1688eebb896124e862f8dc3897 100644 GIT binary patch literal 15770 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 delta 16355 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 .button { @include PlainText; - @include IncreasedClickArea(0px); pointer-events: all; cursor: pointer; position: relative; @@ -20,6 +19,8 @@ transition-property: opacity, transform; text-transform: uppercase; @include PlainText; + @include S(width, 30px); + @include S(height, 30px); opacity: 1; &:hover { @@ -30,11 +31,9 @@ transform: scale(0.95) !important; } - @include S(padding-left, 25px); - & { /* @load-async */ - background: uiResource("icons/state_back_button.png") left center / D(15px) no-repeat; + background: uiResource("icons/state_back_button.png") center center / D(15px) no-repeat; } } } diff --git a/src/css/ingame_hud/mode_menu_next.scss b/src/css/ingame_hud/mode_menu_next.scss index 7c0cfb4b..2deb4965 100644 --- a/src/css/ingame_hud/mode_menu_next.scss +++ b/src/css/ingame_hud/mode_menu_next.scss @@ -1,6 +1,6 @@ #ingame_HUD_ModeMenuNext { position: absolute; - @include S(top, 10px); + @include S(top, 15px); @include S(right, 10px); display: flex; diff --git a/src/css/ingame_hud/puzzle_dlc_logo.scss b/src/css/ingame_hud/puzzle_dlc_logo.scss index 921e853c..fc3b4c76 100644 --- a/src/css/ingame_hud/puzzle_dlc_logo.scss +++ b/src/css/ingame_hud/puzzle_dlc_logo.scss @@ -2,8 +2,8 @@ position: absolute; @include S(width, 150px); @include S(height, 40px); - @include S(bottom, 10px); - @include S(right, 15px); + @include S(left, 50px); + @include S(top, 10px); & { /* @load-async */ diff --git a/src/css/ingame_hud/puzzle_editor_controls.scss b/src/css/ingame_hud/puzzle_editor_controls.scss new file mode 100644 index 00000000..eb402aa2 --- /dev/null +++ b/src/css/ingame_hud/puzzle_editor_controls.scss @@ -0,0 +1,26 @@ +#ingame_HUD_PuzzleEditorControls { + position: absolute; + + @include S(top, 70px); + @include S(left, 10px); + + display: flex; + flex-direction: column; + @include SuperDuperSmallText; + @include S(width, 200px); + + > span { + @include S(margin-bottom, 10px); + } +} + +#ingame_HUD_PuzzleEditorTitle { + position: absolute; + + @include S(top, 23px); + left: 50%; + transform: translateX(-50%); + text-transform: uppercase; + @include Heading; + text-align: center; +} diff --git a/src/css/main.scss b/src/css/main.scss index 89ebbb10..d703663c 100644 --- a/src/css/main.scss +++ b/src/css/main.scss @@ -61,6 +61,7 @@ @import "ingame_hud/mode_menu"; @import "ingame_hud/mode_settings"; @import "ingame_hud/puzzle_dlc_logo"; +@import "ingame_hud/puzzle_editor_controls"; // prettier-ignore $elements: @@ -80,6 +81,8 @@ ingame_HUD_GameMenu, ingame_HUD_KeybindingOverlay, ingame_HUD_ModeMenuBack, ingame_HUD_ModeMenuNext, +ingame_HUD_PuzzleEditorControls, +ingame_HUD_PuzzleEditorTitle, ingame_HUD_ModeMenu, ingame_HUD_ModeSettings, ingame_HUD_Notifications, diff --git a/src/css/resources.scss b/src/css/resources.scss index 769829f6..158db23d 100644 --- a/src/css/resources.scss +++ b/src/css/resources.scss @@ -68,7 +68,7 @@ $icons: notification_saved, notification_success, notification_upgrade; } $languages: en, de, cs, da, et, es-419, fr, it, pt-BR, sv, tr, el, ru, uk, zh-TW, zh-CN, nb, mt-MT, ar, nl, vi, - th, hu, pl, ja, kor, no, pt-PT, fi, ro; + th, hu, pl, ja, kor, no, pt-PT, fi, ro, he; @each $language in $languages { [data-languageicon="#{$language}"] { diff --git a/src/css/states/puzzle_menu.scss b/src/css/states/puzzle_menu.scss index e3c139a8..75541d8d 100644 --- a/src/css/states/puzzle_menu.scss +++ b/src/css/states/puzzle_menu.scss @@ -124,16 +124,17 @@ @include SuperSmallText; grid-column: 2 / 3; grid-row: 3 / 4; - color: $accentColorDark; + color: #444; align-self: end; justify-self: end; font-weight: bold; @include S(padding-right, 12px); + opacity: 0.89; & { /* @load-async */ background: uiResource("icons/puzzle_upvotes.png") calc(100% - #{D(2px)}) #{D( - 3.3px + 3.5px )} / #{D(8px)} #{D(8px)} no-repeat; } } diff --git a/src/js/core/animation_frame.js b/src/js/core/animation_frame.js index eeefb4b0..6aa629a5 100644 --- a/src/js/core/animation_frame.js +++ b/src/js/core/animation_frame.js @@ -51,9 +51,12 @@ export class AnimationFrame { dt = resetDtMs; } - this.frameEmitted.dispatch(dt); + try { + this.frameEmitted.dispatch(dt); + } catch (ex) { + console.error(ex); + } this.lastTime = time; - window.requestAnimationFrame(this.boundMethod); } } diff --git a/src/js/core/error_handler.js b/src/js/core/error_handler.js index 686e4e4e..c149ba76 100644 --- a/src/js/core/error_handler.js +++ b/src/js/core/error_handler.js @@ -123,4 +123,6 @@ function catchErrors(message, source, lineno, colno, error) { return true; } -window.onerror = catchErrors; +if (!G_IS_DEV) { + window.onerror = catchErrors; +} diff --git a/src/js/game/camera.js b/src/js/game/camera.js index 68968d6b..d59f1059 100644 --- a/src/js/game/camera.js +++ b/src/js/game/camera.js @@ -763,13 +763,14 @@ export class Camera extends BasicSerializableObject { * Clamps the center within set boundaries */ clampToBounds() { - if (!this.root.gameMode.hasBounds()) { + const bounds = this.root.gameMode.getCameraBounds(); + if (!bounds) { return; } - const bounds = this.root.gameMode.getBounds().allScaled(globalConfig.tileSize); - this.center.x = clamp(this.center.x, bounds.x, bounds.x + bounds.w); - this.center.y = clamp(this.center.y, bounds.y, bounds.y + bounds.h); + const tileScaleBounds = this.root.gameMode.getCameraBounds().allScaled(globalConfig.tileSize); + this.center.x = clamp(this.center.x, tileScaleBounds.x, tileScaleBounds.x + tileScaleBounds.w); + this.center.y = clamp(this.center.y, tileScaleBounds.y, tileScaleBounds.y + tileScaleBounds.h); } /** diff --git a/src/js/game/components/goal_acceptor.js b/src/js/game/components/goal_acceptor.js index 72c157d7..869dd3f6 100644 --- a/src/js/game/components/goal_acceptor.js +++ b/src/js/game/components/goal_acceptor.js @@ -14,9 +14,7 @@ export class GoalAcceptorComponent extends Component { constructor({ item = null, rate = null }) { super(); this.item = item; - this.rate = rate; this.achieved = false; - this.achievedOnce = false; } } diff --git a/src/js/game/core.js b/src/js/game/core.js index d9d73cae..3333d1da 100644 --- a/src/js/game/core.js +++ b/src/js/game/core.js @@ -83,6 +83,8 @@ export class GameCore { * @param {Savegame} savegame */ initializeRoot(parentState, savegame, gameModeId) { + logger.log("initializing root"); + // Construct the root element, this is the data representation of the game this.root = new GameRoot(this.app); this.root.gameState = parentState; @@ -157,6 +159,8 @@ export class GameCore { } }); } + + logger.log("root initialized"); } /** diff --git a/src/js/game/game_mode.js b/src/js/game/game_mode.js index 31189306..b9d830d3 100644 --- a/src/js/game/game_mode.js +++ b/src/js/game/game_mode.js @@ -5,6 +5,8 @@ import { Rectangle } from "../core/rectangle"; import { gGameModeRegistry } from "../core/global_registries"; import { types, BasicSerializableObject } from "../savegame/serialization"; +import { MetaBuilding } from "./meta_building"; +import { MetaItemProducerBuilding } from "./buildings/item_producer"; /** @enum {string} */ export const enumGameModeIds = { @@ -45,8 +47,10 @@ export class GameMode extends BasicSerializableObject { constructor(root) { super(); this.root = root; - this.hudParts = {}; - this.buildings = {}; + this.hiddenHurtParts = {}; + + /** @type {typeof MetaBuilding[]} */ + this.hiddenBuildings = [MetaItemProducerBuilding]; } /** @returns {object} */ @@ -74,33 +78,30 @@ export class GameMode extends BasicSerializableObject { return this.constructor.getType(); } - setBuildings(buildings) { - Object.assign(this.buildings, buildings); - } - - setHudParts(parts) { - Object.assign(this.hudParts, parts); - } - /** * @param {string} name - Class name of HUD Part * @returns {boolean} */ isHudPartExcluded(name) { - return this.hudParts[name] === false; + return this.hiddenHurtParts[name] === false; } /** - * @param {string} name - Class name of building + * @param {typeof MetaBuilding} building - Class name of building * @returns {boolean} */ - isBuildingExcluded(name) { - return this.buildings[name] === false; + isBuildingExcluded(building) { + return this.hiddenBuildings.indexOf(building) >= 0; } - /** @returns {boolean} */ - hasZone() { - return false; + /** @returns {undefined|Rectangle[]} */ + getBuildableZones() { + return; + } + + /** @returns {Rectangle|undefined} */ + getCameraBounds() { + return; } /** @returns {boolean} */ @@ -113,21 +114,6 @@ export class GameMode extends BasicSerializableObject { return true; } - /** @returns {boolean} */ - hasBounds() { - return false; - } - - /** @returns {boolean} */ - isZoneRestricted() { - return false; - } - - /** @returns {boolean} */ - isBoundaryRestricted() { - return false; - } - /** @returns {number} */ getMinimumZoom() { return 0.1; @@ -148,9 +134,8 @@ export class GameMode extends BasicSerializableObject { }; } - /** @returns {?Rectangle} */ - getZone() { - return null; + throughputDoesNotMatter() { + return false; } /** @@ -162,11 +147,6 @@ export class GameMode extends BasicSerializableObject { return; } - /** @returns {?Rectangle} */ - getBounds() { - return null; - } - /** @returns {array} */ getLevelDefinitions() { return []; @@ -187,6 +167,11 @@ export class GameMode extends BasicSerializableObject { return true; } + /** @returns {boolean} */ + getSupportsWires() { + return true; + } + /** @returns {string} */ getBlueprintShapeKey() { return "CbCbCbRb:CwCwCwCw"; diff --git a/src/js/game/game_system_manager.js b/src/js/game/game_system_manager.js index 9220acaa..08609f89 100644 --- a/src/js/game/game_system_manager.js +++ b/src/js/game/game_system_manager.js @@ -183,7 +183,7 @@ export class GameSystemManager { add("goalAcceptor", GoalAcceptorSystem); - if (this.root.gameMode.hasZone()) { + if (this.root.gameMode.getBuildableZones()) { add("zone", ZoneSystem); } diff --git a/src/js/game/hub_goals.js b/src/js/game/hub_goals.js index b3536a3c..fee1bd79 100644 --- a/src/js/game/hub_goals.js +++ b/src/js/game/hub_goals.js @@ -184,6 +184,10 @@ export class HubGoals extends BasicSerializableObject { * @param {string} upgradeId */ getUpgradeLevel(upgradeId) { + if (this.root.gameMode.throughputDoesNotMatter()) { + return 10; + } + return this.upgradeLevels[upgradeId] || 0; } @@ -195,6 +199,10 @@ export class HubGoals extends BasicSerializableObject { if (G_IS_DEV && globalConfig.debug.allBuildingsUnlocked) { return true; } + if (this.root.gameMode.getLevelDefinitions().length < 1) { + // no story, so always unlocked + return true; + } return !!this.gainedRewards[reward]; } @@ -472,6 +480,9 @@ export class HubGoals extends BasicSerializableObject { * @returns {number} items / sec */ getBeltBaseSpeed() { + if (this.root.gameMode.throughputDoesNotMatter()) { + return globalConfig.beltSpeedItemsPerSecond * 5; + } return globalConfig.beltSpeedItemsPerSecond * this.upgradeImprovements.belt; } @@ -480,6 +491,9 @@ export class HubGoals extends BasicSerializableObject { * @returns {number} items / sec */ getUndergroundBeltBaseSpeed() { + if (this.root.gameMode.throughputDoesNotMatter()) { + return globalConfig.beltSpeedItemsPerSecond * 5; + } return globalConfig.beltSpeedItemsPerSecond * this.upgradeImprovements.belt; } @@ -488,6 +502,9 @@ export class HubGoals extends BasicSerializableObject { * @returns {number} items / sec */ getMinerBaseSpeed() { + if (this.root.gameMode.throughputDoesNotMatter()) { + return globalConfig.minerSpeedItemsPerSecond * 5; + } return globalConfig.minerSpeedItemsPerSecond * this.upgradeImprovements.miner; } @@ -497,6 +514,10 @@ export class HubGoals extends BasicSerializableObject { * @returns {number} items / sec */ getProcessorBaseSpeed(processorType) { + if (this.root.gameMode.throughputDoesNotMatter()) { + return 10; + } + switch (processorType) { case enumItemProcessorTypes.trash: case enumItemProcessorTypes.hub: diff --git a/src/js/game/hud/hud.js b/src/js/game/hud/hud.js index 8f4257b9..7d0646f4 100644 --- a/src/js/game/hud/hud.js +++ b/src/js/game/hud/hud.js @@ -32,6 +32,7 @@ import { HUDModeSettings } from "./parts/mode_settings"; import { enumNotificationType, HUDNotifications } from "./parts/notifications"; import { HUDPinnedShapes } from "./parts/pinned_shapes"; import { HUDPuzzleDLCLogo } from "./parts/puzzle_dlc_logo"; +import { HUDPuzzleEditorControls } from "./parts/puzzle_editor_controls"; import { HUDSandboxController } from "./parts/sandbox_controller"; import { HUDScreenshotExporter } from "./parts/screenshot_exporter"; import { HUDSettingsMenu } from "./parts/settings_menu"; @@ -95,6 +96,7 @@ export class GameHUD { modeMenu: HUDModeMenu, modeSettings: HUDModeSettings, puzzleDlcLogo: HUDPuzzleDLCLogo, + puzzleEditorControls: HUDPuzzleEditorControls, // Must always exist pinnedShapes: HUDPinnedShapes, diff --git a/src/js/game/hud/parts/base_toolbar.js b/src/js/game/hud/parts/base_toolbar.js index 01e9fafa..9df362f3 100644 --- a/src/js/game/hud/parts/base_toolbar.js +++ b/src/js/game/hud/parts/base_toolbar.js @@ -55,7 +55,7 @@ export class HUDBaseToolbar extends BaseHUDPart { const filtered = []; for (let i = 0; i < buildings.length; i++) { - if (this.root.gameMode.isBuildingExcluded(buildings[i].name)) { + if (this.root.gameMode.isBuildingExcluded(buildings[i])) { continue; } diff --git a/src/js/game/hud/parts/mode_menu_back.js b/src/js/game/hud/parts/mode_menu_back.js index 9eae74cc..ebe8b1e6 100644 --- a/src/js/game/hud/parts/mode_menu_back.js +++ b/src/js/game/hud/parts/mode_menu_back.js @@ -1,6 +1,5 @@ -import { BaseHUDPart } from "../base_hud_part"; import { makeDiv } from "../../../core/utils"; -import { T } from "../../../translations"; +import { BaseHUDPart } from "../base_hud_part"; export class HUDModeMenuBack extends BaseHUDPart { createElements(parent) { @@ -9,7 +8,6 @@ export class HUDModeMenuBack extends BaseHUDPart { this.element = makeDiv(parent, "ingame_HUD_ModeMenuBack"); this.button = document.createElement("button"); this.button.classList.add("button"); - this.button.textContent = T.ingame.modeMenu[key].back.title; this.element.appendChild(this.button); this.trackClicks(this.button, this.back); diff --git a/src/js/game/hud/parts/mode_settings.js b/src/js/game/hud/parts/mode_settings.js index a1dd220a..48e4defe 100644 --- a/src/js/game/hud/parts/mode_settings.js +++ b/src/js/game/hud/parts/mode_settings.js @@ -8,7 +8,8 @@ export class HUDModeSettings extends BaseHUDPart { const bind = (selector, handler) => this.trackClicks(this.element.querySelector(selector), handler); - if (this.root.gameMode.hasZone()) { + // @fixme + if (this.root.gameMode.getBuildableZones()) { this.zone = makeDiv( this.element, null, @@ -52,7 +53,12 @@ export class HUDModeSettings extends BaseHUDPart { } updateZoneValues() { - const zone = this.root.gameMode.getZone(); + const zones = this.root.gameMode.getBuildableZones(); + if (!zones || zones.length === 0) { + return; + } + + const zone = zones[0]; this.element.querySelector(".zoneWidth > .value").textContent = String(zone.w); this.element.querySelector(".zoneHeight > .value").textContent = String(zone.h); } diff --git a/src/js/game/hud/parts/puzzle_editor_controls.js b/src/js/game/hud/parts/puzzle_editor_controls.js new file mode 100644 index 00000000..77d609b7 --- /dev/null +++ b/src/js/game/hud/parts/puzzle_editor_controls.js @@ -0,0 +1,21 @@ +import { makeDiv } from "../../../core/utils"; +import { BaseHUDPart } from "../base_hud_part"; + +export class HUDPuzzleEditorControls extends BaseHUDPart { + createElements(parent) { + this.element = makeDiv(parent, "ingame_HUD_PuzzleEditorControls"); + + this.element.innerHTML = ` + + 1. Build constant producers to generate resources. + 2. Build goal acceptors the capture shapes. + 3. Produce your desired shape(s) within the puzzle area and deliver it to the goal acceptors, which will capture it. + 4. Once you are done, press 'Playtest' to validate your puzzle. + `; + + this.titleElement = makeDiv(parent, "ingame_HUD_PuzzleEditorTitle"); + this.titleElement.innerText = "Puzzle Editor"; + } + + initialize() {} +} diff --git a/src/js/game/hud/parts/wires_overlay.js b/src/js/game/hud/parts/wires_overlay.js index 2fd3092c..328d6689 100644 --- a/src/js/game/hud/parts/wires_overlay.js +++ b/src/js/game/hud/parts/wires_overlay.js @@ -28,6 +28,9 @@ export class HUDWiresOverlay extends BaseHUDPart { * Switches between layers */ switchLayers() { + if (!this.root.gameMode.getSupportsWires()) { + return; + } if (this.root.currentLayer === "regular") { if ( this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_wires_painter_and_levers) || diff --git a/src/js/game/map_chunk_view.js b/src/js/game/map_chunk_view.js index 06ff7337..59bff340 100644 --- a/src/js/game/map_chunk_view.js +++ b/src/js/game/map_chunk_view.js @@ -41,7 +41,7 @@ export class MapChunkView extends MapChunk { */ drawBackgroundLayer(parameters) { const systems = this.root.systemMgr.systems; - if (this.root.gameMode.hasZone()) { + if (systems.zone) { systems.zone.drawChunk(parameters, this); } diff --git a/src/js/game/modes/puzzle.js b/src/js/game/modes/puzzle.js index 31cfcb3d..15b0c868 100644 --- a/src/js/game/modes/puzzle.js +++ b/src/js/game/modes/puzzle.js @@ -32,7 +32,7 @@ export class PuzzleGameMode extends GameMode { const data = this.getSaveData(); - this.setHudParts({ + this.hiddenHurtParts = { [HUDGameMenu.name]: false, [HUDMassSelector.name]: false, [HUDInteractiveTutorial.name]: false, @@ -40,7 +40,7 @@ export class PuzzleGameMode extends GameMode { [HUDPartTutorialHints.name]: false, [HUDPinnedShapes.name]: false, [HUDWaypoints.name]: false, - }); + }; this.setDimensions(data.zoneWidth, data.zoneHeight); } @@ -48,17 +48,13 @@ export class PuzzleGameMode extends GameMode { setDimensions(w = 16, h = 9) { this.zoneWidth = w < 2 ? 2 : w; this.zoneHeight = h < 2 ? 2 : h; - this.boundsHeight = this.zoneHeight < 8 ? 8 : this.zoneHeight; - this.boundsWidth = this.zoneWidth < 8 ? 8 : this.zoneWidth; } getSaveData() { const save = this.root.savegame.getCurrentDump(); - if (!save) { return {}; } - return save.gameMode.data; } @@ -66,46 +62,12 @@ export class PuzzleGameMode extends GameMode { return new Rectangle(-Math.ceil(width / 2), -Math.ceil(height / 2), width, height); } - getBounds() { - if (this.bounds) { - return this.bounds; - } - - this.bounds = this.createCenteredRectangle(this.boundsWidth, this.boundsHeight); - - return this.bounds; + getCameraBounds() { + return this.createCenteredRectangle(this.zoneWidth + 20, this.zoneHeight + 20); } - getZone() { - if (this.zone) { - return this.zone; - } - - this.zone = this.createCenteredRectangle(this.zoneWidth, this.zoneHeight); - - return this.zone; - } - - /** - * Overrides GameMode's implementation to treat buildings like a whitelist - * instead of a blacklist by default. - * @param {string} name - Class name of building - * @returns {boolean} - */ - isBuildingExcluded(name) { - return this.buildings[name] !== true; - } - - isInBounds(x, y) { - return this.bounds.containsPoint(x, y); - } - - isInZone(x, y) { - return this.zone.containsPoint(x, y); - } - - hasZone() { - return true; + getBuildableZones() { + return [this.createCenteredRectangle(this.zoneWidth, this.zoneHeight)]; } hasHub() { @@ -116,10 +78,6 @@ export class PuzzleGameMode extends GameMode { return false; } - hasBounds() { - return true; - } - getMinimumZoom() { return 1; } @@ -132,6 +90,14 @@ export class PuzzleGameMode extends GameMode { return false; } + throughputDoesNotMatter() { + return true; + } + + getSupportsWires() { + return false; + } + /** @returns {boolean} */ getIsFreeplayAvailable() { return true; diff --git a/src/js/game/modes/puzzle_edit.js b/src/js/game/modes/puzzle_edit.js index f927b001..680778aa 100644 --- a/src/js/game/modes/puzzle_edit.js +++ b/src/js/game/modes/puzzle_edit.js @@ -2,12 +2,23 @@ import { GameRoot } from "../root"; /* typehints:end */ -// import { MetaBeltBuilding } from "../buildings/belt"; -import { MetaConstantProducerBuilding } from "../buildings/constant_producer"; -import { MetaGoalAcceptorBuilding } from "../buildings/goal_acceptor"; -// import { MetaItemProducerBuilding } from "../buildings/item_producer"; import { enumGameModeIds } from "../game_mode"; import { PuzzleGameMode } from "./puzzle"; +import { MetaStorageBuilding } from "../buildings/storage"; +import { MetaReaderBuilding } from "../buildings/reader"; +import { MetaFilterBuilding } from "../buildings/filter"; +import { MetaDisplayBuilding } from "../buildings/display"; +import { MetaLeverBuilding } from "../buildings/lever"; +import { MetaItemProducerBuilding } from "../buildings/item_producer"; +import { MetaMinerBuilding } from "../buildings/miner"; +import { MetaWireBuilding } from "../buildings/wire"; +import { MetaWireTunnelBuilding } from "../buildings/wire_tunnel"; +import { MetaConstantSignalBuilding } from "../buildings/constant_signal"; +import { MetaLogicGateBuilding } from "../buildings/logic_gate"; +import { MetaVirtualProcessorBuilding } from "../buildings/virtual_processor"; +import { MetaAnalyzerBuilding } from "../buildings/analyzer"; +import { MetaComparatorBuilding } from "../buildings/comparator"; +import { MetaTransistorBuilding } from "../buildings/transistor"; export class PuzzleEditGameMode extends PuzzleGameMode { static getId() { @@ -24,18 +35,24 @@ export class PuzzleEditGameMode extends PuzzleGameMode { this.playtest = false; - this.setBuildings({ - [MetaConstantProducerBuilding.name]: true, - [MetaGoalAcceptorBuilding.name]: true, - }); - } + this.hiddenBuildings = [ + MetaStorageBuilding, + MetaReaderBuilding, + MetaFilterBuilding, + MetaDisplayBuilding, + MetaLeverBuilding, + MetaItemProducerBuilding, + MetaMinerBuilding, - isZoneRestricted() { - return !this.playtest; - } - - isBoundaryRestricted() { - return this.playtest; + MetaWireBuilding, + MetaWireTunnelBuilding, + MetaConstantSignalBuilding, + MetaLogicGateBuilding, + MetaVirtualProcessorBuilding, + MetaAnalyzerBuilding, + MetaComparatorBuilding, + MetaTransistorBuilding, + ]; } expandZone(w = 0, h = 0) { diff --git a/src/js/game/modes/regular.js b/src/js/game/modes/regular.js index 4f8e9ec2..1e84a115 100644 --- a/src/js/game/modes/regular.js +++ b/src/js/game/modes/regular.js @@ -518,19 +518,15 @@ export class RegularGameMode extends GameMode { constructor(root) { super(root); - this.setHudParts({ + this.hiddenHurtParts = { [HUDModeMenuBack.name]: false, [HUDModeMenuNext.name]: false, [HUDModeMenu.name]: false, [HUDModeSettings.name]: false, [HUDPuzzleDLCLogo.name]: false, - }); + }; - this.setBuildings({ - [MetaConstantProducerBuilding.name]: false, - [MetaGoalAcceptorBuilding.name]: false, - [MetaItemProducerBuilding.name]: queryParamOptions.sandboxMode || G_IS_DEV, - }); + this.hiddenBuildings = [MetaConstantProducerBuilding, MetaGoalAcceptorBuilding]; } /** diff --git a/src/js/game/systems/goal_acceptor.js b/src/js/game/systems/goal_acceptor.js index e24eb80b..6fcb479e 100644 --- a/src/js/game/systems/goal_acceptor.js +++ b/src/js/game/systems/goal_acceptor.js @@ -49,25 +49,17 @@ export class GoalAcceptorSystem extends GameSystemWithFilter { const itemInput = new FormElementInput({ id: "goalItemInput", - label: fillInLinkIntoTranslation(T.dialogs.editSignal.descShortKey, THIRDPARTY_URLS.shapeViewer), + label: fillInLinkIntoTranslation(T.dialogs.editGoalAcceptor.desc, THIRDPARTY_URLS.shapeViewer), placeholder: "CuCuCuCu", defaultValue: "CuCuCuCu", validator: val => this.parseItem(val), }); - const rateInput = new FormElementInput({ - id: "goalRateInput", - label: "Rate:", - placeholder: "0", - defaultValue: "0", - validator: val => !isNaN(Number(val)), - }); - const dialog = new DialogWithForm({ app: this.root.app, - title: "Set Goal", + title: T.dialogs.editGoalAcceptor.title, desc: "", - formElements: [itemInput, rateInput], + formElements: [itemInput], buttons: ["cancel:bad:escape", "ok:good:enter"], closeButton: false, }); @@ -79,7 +71,6 @@ export class GoalAcceptorSystem extends GameSystemWithFilter { } goalComp.item = this.parseItem(itemInput.getValue()); - goalComp.rate = this.parseRate(rateInput.getValue()); }; dialog.buttonSignals.ok.add(closeHandler); diff --git a/src/js/game/systems/zone.js b/src/js/game/systems/zone.js index 3dd68804..7e977438 100644 --- a/src/js/game/systems/zone.js +++ b/src/js/game/systems/zone.js @@ -13,8 +13,12 @@ export class ZoneSystem extends GameSystem { /** @param {GameRoot} root */ constructor(root) { super(root); - + this.drawn = false; this.root.signals.prePlacementCheck.add(this.prePlacementCheck, this); + + this.root.signals.gameFrameStarted.add(() => { + this.drawn = false; + }); } prePlacementCheck(entity, tile = null) { @@ -25,18 +29,24 @@ export class ZoneSystem extends GameSystem { } const mode = this.root.gameMode; - const zone = mode.getZone().expandedInAllDirections(-1); + + const zones = mode.getBuildableZones(); + if (!zones) { + return; + } + const transformed = staticComp.getTileSpaceBounds(); - if (zone.containsRect(transformed)) { - if (mode.isZoneRestricted()) { - return STOP_PROPAGATION; - } - } else { - if (mode.isBoundaryRestricted()) { - return STOP_PROPAGATION; + let withinAnyZone = false; + for (const zone of zones) { + if (zone.expandedInAllDirections(-1).containsRect(transformed)) { + withinAnyZone = true; } } + + if (!withinAnyZone) { + return STOP_PROPAGATION; + } } /** @@ -45,18 +55,46 @@ export class ZoneSystem extends GameSystem { * @param {MapChunkView} chunk */ drawChunk(parameters, chunk) { + if (this.drawn) { + // oof + return; + } + this.drawn = true; + const mode = this.root.gameMode; - const zone = mode.getZone().allScaled(globalConfig.tileSize); + + const zones = mode.getBuildableZones(); + if (!zones) { + return; + } + + const zone = zones[0].allScaled(globalConfig.tileSize); const context = parameters.context; - context.globalAlpha = 0.1; - context.fillStyle = THEME.map.zone.background; - context.fillRect(zone.x, zone.y, zone.w, zone.h); - - context.globalAlpha = 1; - context.strokeStyle = THEME.map.zone.border; context.lineWidth = 2; - context.strokeRect(zone.x, zone.y, zone.w, zone.h); + context.strokeStyle = THEME.map.zone.borderSolid; + context.beginPath(); + context.rect(zone.x, zone.y, zone.w, zone.h); + + context.stroke(); + + const outer = zone; + const padding = 40 * globalConfig.tileSize; + context.fillStyle = THEME.map.zone.outerColor; + context.fillRect(outer.x + outer.w, outer.y, padding, outer.h); + context.fillRect(outer.x - padding, outer.y, padding, outer.h); + context.fillRect( + outer.x - padding - globalConfig.tileSize, + outer.y - padding, + 2 * padding + zone.w + 2 * globalConfig.tileSize, + padding + ); + context.fillRect( + outer.x - padding - globalConfig.tileSize, + outer.y + outer.h, + 2 * padding + zone.w + 2 * globalConfig.tileSize, + padding + ); context.globalAlpha = 1; } diff --git a/src/js/game/themes/dark.json b/src/js/game/themes/dark.json index 02ff6ae3..010f9251 100644 --- a/src/js/game/themes/dark.json +++ b/src/js/game/themes/dark.json @@ -50,8 +50,10 @@ }, "zone": { - "background": "#3e3f47", - "border": "#667964" + "background": "#fff", + "border": "rgba(23, 192, 255, 0.1)", + "borderSolid": "rgba(23, 192, 255, 0.7)", + "outerColor": "rgba(240, 240, 255, 0.5)" } }, diff --git a/src/js/game/themes/light.json b/src/js/game/themes/light.json index 4aa367fd..c39e19f0 100644 --- a/src/js/game/themes/light.json +++ b/src/js/game/themes/light.json @@ -52,7 +52,9 @@ "zone": { "background": "#fff", - "border": "#cbffc4" + "border": "rgba(23, 192, 255, 0.1)", + "borderSolid": "rgba(23, 192, 255, 0.7)", + "outerColor": "rgba(240, 240, 255, 0.5)" } }, diff --git a/src/js/languages.js b/src/js/languages.js index 6899ef09..4dfb15d4 100644 --- a/src/js/languages.js +++ b/src/js/languages.js @@ -184,4 +184,12 @@ export const LANGUAGES = { code: "uk", region: "", }, + + "he": { + // hebrew + name: "עברית", + data: require("./built-temp/base-he.json"), + code: "he", + region: "", + }, }; diff --git a/src/js/states/ingame.js b/src/js/states/ingame.js index 32ef3edc..a872b6f6 100644 --- a/src/js/states/ingame.js +++ b/src/js/states/ingame.js @@ -370,7 +370,13 @@ export class InGameState extends GameState { // Remove unneded default element document.body.querySelector(".modalDialogParent").remove(); - this.asyncChannel.watch(waitNextFrame()).then(() => this.stage3CreateCore()); + this.asyncChannel + .watch(waitNextFrame()) + .then(() => this.stage3CreateCore()) + .catch(ex => { + logger.error(ex); + throw ex; + }); } /** diff --git a/src/js/states/puzzle_menu.js b/src/js/states/puzzle_menu.js index d6465627..8b6c8fcb 100644 --- a/src/js/states/puzzle_menu.js +++ b/src/js/states/puzzle_menu.js @@ -1,3 +1,4 @@ +import { globalConfig } from "../core/config"; import { TextualGameState } from "../core/textual_game_state"; import { formatBigNumberFull } from "../core/utils"; import { enumGameModeIds } from "../game/game_mode"; @@ -206,6 +207,10 @@ export class PuzzleMenuState extends TextualGameState { } this.trackClicks(this.htmlElement.querySelector("button.createPuzzle"), this.createNewPuzzle); + + if (G_IS_DEV && globalConfig.debug.testPuzzleMode) { + this.createNewPuzzle(); + } } createNewPuzzle() { diff --git a/translations/base-en.yaml b/translations/base-en.yaml index 29c610a7..f02b8335 100644 --- a/translations/base-en.yaml +++ b/translations/base-en.yaml @@ -248,6 +248,10 @@ dialogs: Choose a pre-defined item: descShortKey: ... or enter the short key of a shape (Which you can generate here) + editGoalAcceptor: + title: Set Goal + desc: Enter the short key of a shape (Which you can generate here). The goal will count as completed once 1 item /s is delivered. + markerDemoLimit: desc: You can only create two custom markers in the demo. Get the standalone for unlimited markers! diff --git a/translations/base-he.yml b/translations/base-he.yaml similarity index 97% rename from translations/base-he.yml rename to translations/base-he.yaml index d0e80e99..40b458c7 100644 --- a/translations/base-he.yml +++ b/translations/base-he.yaml @@ -7,13 +7,13 @@ steamPage: intro: >- אתה אוהב משחקי אוטומציה? אתה במקום הנכון! - + shapez.io הוא משחק שלווה שבו אתה בונה מפעל בשביל ליצור צורות גאומטריות אוטומטית. ככל שמתקדמים השלבים, הצורות נהיות יותר ויותר מסובכות, ואתה צריך להפתח על המפה האין סופית. - + ואם זה לא היה מספיק, אתה צריך ליצור יותר ויותר צורות בשביל לספק את הדרישה - הדבר היחיד שיכול לעזור זה להגדיל את המפעל! בזמן שבהתחלה אתה רק צריך לערוך צורות, בהמשך אתה צריך לצבוע אותם בעזרת צבעים שאתה מערבב. - + קניית המשחק בsteam תתן לך גישה למשחק המלא, אבל אתה יכול לשחק משחק דמו בhttps://shapez.io/ ולהחליט אחר כך. - + what_others_say: What people say about shapez.io nothernlion_comment: >- @@ -85,7 +85,8 @@ mainMenu: openSourceHint: המשחק הזה הוא עם קוד פתוח! discordLink: שרת הדסקורד הרשמי helpTranslate: תעזור לתרגם! - madeBy: יוצר המשחק: + madeBy: >- + יוצר המשחק: # This is shown when using firefox and other browsers which are not supported. browserWarning: >- @@ -1205,4 +1206,4 @@ tips: - בשביל לנקות מסוע, חתוך את האיזור ואז תדביק באותו מקום. - לחץ F4 בשביל להציג את הFPS ואת הTickRate. - לחץ F4 פעמיים בשביל להציג את המשבצת שהעכבר והמצלמה בהם. - - אתה יכול ללחוץ על צורה מוצמדת בצד שמאל בשביל לבטל את ההצמדה. \ No newline at end of file + - אתה יכול ללחוץ על צורה מוצמדת בצד שמאל בשביל לבטל את ההצמדה.