mirror of
https://github.com/tobspr/shapez.io.git
synced 2025-06-13 13:04:03 +00:00
Add ingame puzzle mode UI elements
- Add minimal menus in puzzle mode for back, next navigation - Add lower menu for changing zone dimenensions
This commit is contained in:
parent
212ea67125
commit
19c91faf8d
54
src/css/ingame_hud/mode_menu.scss
Normal file
54
src/css/ingame_hud/mode_menu.scss
Normal file
@ -0,0 +1,54 @@
|
||||
#ingame_HUD_ModeMenu {
|
||||
position: absolute;
|
||||
@include S(bottom, 10px);
|
||||
@include S(left, 10px);
|
||||
display: flex;
|
||||
|
||||
backdrop-filter: blur(D(1px));
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
backdrop-filter: blur(D(1px));
|
||||
padding: D(3px);
|
||||
|
||||
> button,
|
||||
> .button {
|
||||
@include PlainText;
|
||||
@include IncreasedClickArea(0px);
|
||||
background: green;
|
||||
@include S(width, 30px);
|
||||
@include S(height, 30px);
|
||||
|
||||
pointer-events: all;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: all 0.12s ease-in-out;
|
||||
transition-property: opacity, transform;
|
||||
|
||||
display: inline-flex;
|
||||
background: center center / 70% no-repeat;
|
||||
grid-row: 1;
|
||||
|
||||
&.pressed {
|
||||
transform: scale(0.9) !important;
|
||||
}
|
||||
|
||||
opacity: 0.7;
|
||||
&:hover {
|
||||
opacity: 0.9 !important;
|
||||
}
|
||||
|
||||
@include DarkThemeInvert;
|
||||
|
||||
&.settings {
|
||||
& {
|
||||
/* @load-async */
|
||||
background-image: uiResource("icons/settings_menu_settings.png");
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
opacity: 0.9;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
}
|
35
src/css/ingame_hud/mode_menu_back.scss
Normal file
35
src/css/ingame_hud/mode_menu_back.scss
Normal file
@ -0,0 +1,35 @@
|
||||
#ingame_HUD_ModeMenuBack {
|
||||
position: absolute;
|
||||
@include S(top, 10px);
|
||||
@include S(left, 10px);
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
backdrop-filter: blur(D(1px));
|
||||
padding: D(3px);
|
||||
|
||||
> .button {
|
||||
@include PlainText;
|
||||
@include IncreasedClickArea(0px);
|
||||
pointer-events: all;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
color: #333438;
|
||||
transition: all 0.12s ease-in-out;
|
||||
transition-property: opacity, transform;
|
||||
|
||||
opacity: 0.8;
|
||||
&:hover {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
&.pressed {
|
||||
transform: scale(0.9) !important;
|
||||
}
|
||||
|
||||
@include DarkThemeOverride {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
42
src/css/ingame_hud/mode_menu_next.scss
Normal file
42
src/css/ingame_hud/mode_menu_next.scss
Normal file
@ -0,0 +1,42 @@
|
||||
#ingame_HUD_ModeMenuNext {
|
||||
position: absolute;
|
||||
@include S(top, 10px);
|
||||
@include S(right, 10px);
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
backdrop-filter: blur(D(1px));
|
||||
padding: D(3px);
|
||||
|
||||
> .button {
|
||||
@include ButtonText;
|
||||
@include IncreasedClickArea(0px);
|
||||
pointer-events: all;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
color: #333438;
|
||||
transition: all 0.12s ease-in-out;
|
||||
transition-property: opacity, transform;
|
||||
|
||||
opacity: 0.8;
|
||||
&:hover {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
&.pressed {
|
||||
transform: scale(0.9) !important;
|
||||
}
|
||||
|
||||
@include DarkThemeOverride {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
> .content {
|
||||
@include SuperSmallText;
|
||||
@include S(font-size, 7px);
|
||||
@include S(width, 150px);
|
||||
text-align: right;
|
||||
}
|
||||
}
|
47
src/css/ingame_hud/mode_settings.scss
Normal file
47
src/css/ingame_hud/mode_settings.scss
Normal file
@ -0,0 +1,47 @@
|
||||
#ingame_HUD_ModeSettings {
|
||||
position: absolute;
|
||||
background: $ingameHudBg;
|
||||
@include S(padding, 10px);
|
||||
@include S(bottom, 50px);
|
||||
@include S(left, 15px);
|
||||
|
||||
@include SuperSmallText;
|
||||
color: #eee;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
> .section {
|
||||
> label {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.plusMinus {
|
||||
@include S(margin-top, 5px);
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto auto auto;
|
||||
align-items: center;
|
||||
@include S(grid-gap, 5px);
|
||||
|
||||
label {
|
||||
@include S(margin-right, 10px);
|
||||
}
|
||||
|
||||
button {
|
||||
@include PlainText;
|
||||
@include S(padding, 0);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@include S(width, 15px);
|
||||
@include S(height, 15px);
|
||||
@include IncreasedClickArea(0px);
|
||||
}
|
||||
|
||||
|
||||
.value {
|
||||
text-align: center;
|
||||
@include S(min-width, 15px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -55,6 +55,10 @@
|
||||
@import "ingame_hud/sandbox_controller";
|
||||
@import "ingame_hud/standalone_advantages";
|
||||
@import "ingame_hud/cat_memes";
|
||||
@import "ingame_hud/mode_menu_back";
|
||||
@import "ingame_hud/mode_menu_next";
|
||||
@import "ingame_hud/mode_menu";
|
||||
@import "ingame_hud/mode_settings";
|
||||
|
||||
// prettier-ignore
|
||||
$elements:
|
||||
@ -71,6 +75,10 @@ ingame_HUD_PlacerVariants,
|
||||
ingame_HUD_PinnedShapes,
|
||||
ingame_HUD_GameMenu,
|
||||
ingame_HUD_KeybindingOverlay,
|
||||
ingame_HUD_ModeMenuBack,
|
||||
ingame_HUD_ModeMenuNext,
|
||||
ingame_HUD_ModeMenu,
|
||||
ingame_HUD_ModeSettings,
|
||||
ingame_HUD_Notifications,
|
||||
ingame_HUD_DebugInfo,
|
||||
ingame_HUD_EntityDebugger,
|
||||
@ -113,6 +121,8 @@ body.uiHidden {
|
||||
#ingame_HUD_PlacementHints,
|
||||
#ingame_HUD_GameMenu,
|
||||
#ingame_HUD_PinnedShapes,
|
||||
#ingame_HUD_ModeMenuBack,
|
||||
#ingame_HUD_ModeMenuNext,
|
||||
#ingame_HUD_Notifications,
|
||||
#ingame_HUD_TutorialHints,
|
||||
#ingame_HUD_Waypoints,
|
||||
|
@ -118,6 +118,16 @@ export class GameMode extends BasicSerializableObject {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @returns {boolean} */
|
||||
isZoneRestricted() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @returns {boolean} */
|
||||
isBoundaryRestricted() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @returns {number} */
|
||||
getMinimumZoom() {
|
||||
return 0.1;
|
||||
@ -143,6 +153,15 @@ export class GameMode extends BasicSerializableObject {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} w
|
||||
* @param {number} h
|
||||
*/
|
||||
expandZone(w = 0, h = 0) {
|
||||
abstract;
|
||||
return;
|
||||
}
|
||||
|
||||
/** @returns {?Rectangle} */
|
||||
getBounds() {
|
||||
return null;
|
||||
|
@ -49,6 +49,10 @@ import { HUDStandaloneAdvantages } from "./parts/standalone_advantages";
|
||||
import { HUDCatMemes } from "./parts/cat_memes";
|
||||
import { HUDTutorialVideoOffer } from "./parts/tutorial_video_offer";
|
||||
import { HUDConstantSignalEdit } from "./parts/constant_signal_edit";
|
||||
import { HUDModeMenuBack } from "./parts/mode_menu_back";
|
||||
import { HUDModeMenuNext } from "./parts/mode_menu_next";
|
||||
import { HUDModeMenu } from "./parts/mode_menu";
|
||||
import { HUDModeSettings } from "./parts/mode_settings";
|
||||
|
||||
export class GameHUD {
|
||||
/**
|
||||
@ -88,6 +92,10 @@ export class GameHUD {
|
||||
wireInfo: HUDWireInfo,
|
||||
leverToggle: HUDLeverToggle,
|
||||
constantSignalEdit: HUDConstantSignalEdit,
|
||||
modeMenuBack: HUDModeMenuBack,
|
||||
modeMenuNext: HUDModeMenuNext,
|
||||
modeMenu: HUDModeMenu,
|
||||
modeSettings: HUDModeSettings,
|
||||
|
||||
// Must always exist
|
||||
pinnedShapes: HUDPinnedShapes,
|
||||
@ -112,7 +120,9 @@ export class GameHUD {
|
||||
});
|
||||
|
||||
if (!IS_MOBILE) {
|
||||
this.parts.keybindingOverlay = new HUDKeybindingOverlay(this.root);
|
||||
if (!this.root.gameMode.isHudPartExcluded(HUDKeybindingOverlay.name)) {
|
||||
this.parts.keybindingOverlay = new HUDKeybindingOverlay(this.root);
|
||||
}
|
||||
}
|
||||
|
||||
if (G_IS_DEV && globalConfig.debug.enableEntityInspector) {
|
||||
|
17
src/js/game/hud/parts/mode_menu.js
Normal file
17
src/js/game/hud/parts/mode_menu.js
Normal file
@ -0,0 +1,17 @@
|
||||
import { BaseHUDPart } from "../base_hud_part";
|
||||
import { makeDiv } from "../../../core/utils";
|
||||
|
||||
export class HUDModeMenu extends BaseHUDPart {
|
||||
createElements(parent) {
|
||||
this.element = makeDiv(parent, "ingame_HUD_ModeMenu");
|
||||
|
||||
this.settingsButton = makeDiv(this.element, null, ["button", "settings"]);
|
||||
this.trackClicks(this.settingsButton, this.openSettings);
|
||||
}
|
||||
|
||||
openSettings() {
|
||||
this.root.hud.parts.modeSettings.toggle();
|
||||
}
|
||||
|
||||
initialize() {}
|
||||
}
|
23
src/js/game/hud/parts/mode_menu_back.js
Normal file
23
src/js/game/hud/parts/mode_menu_back.js
Normal file
@ -0,0 +1,23 @@
|
||||
import { BaseHUDPart } from "../base_hud_part";
|
||||
import { makeDiv } from "../../../core/utils";
|
||||
import { T } from "../../../translations";
|
||||
|
||||
export class HUDModeMenuBack extends BaseHUDPart {
|
||||
createElements(parent) {
|
||||
const key = this.root.gameMode.getId();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
initialize() {}
|
||||
|
||||
back() {
|
||||
this.root.gameState.goBackToMenu();
|
||||
}
|
||||
}
|
23
src/js/game/hud/parts/mode_menu_next.js
Normal file
23
src/js/game/hud/parts/mode_menu_next.js
Normal file
@ -0,0 +1,23 @@
|
||||
import { BaseHUDPart } from "../base_hud_part";
|
||||
import { makeDiv } from "../../../core/utils";
|
||||
import { T } from "../../../translations";
|
||||
|
||||
export class HUDModeMenuNext extends BaseHUDPart {
|
||||
createElements(parent) {
|
||||
const key = this.root.gameMode.getId();
|
||||
|
||||
this.element = makeDiv(parent, "ingame_HUD_ModeMenuNext");
|
||||
this.button = document.createElement("button");
|
||||
this.button.classList.add("button");
|
||||
this.button.textContent = T.ingame.modeMenu[key].next.title + " ➡ ";
|
||||
this.element.appendChild(this.button);
|
||||
|
||||
this.content = makeDiv(this.element, null, ["content"], T.ingame.modeMenu[key].next.desc);
|
||||
|
||||
this.trackClicks(this.button, this.next);
|
||||
}
|
||||
|
||||
initialize() {}
|
||||
|
||||
next() {}
|
||||
}
|
67
src/js/game/hud/parts/mode_settings.js
Normal file
67
src/js/game/hud/parts/mode_settings.js
Normal file
@ -0,0 +1,67 @@
|
||||
import { makeDiv } from "../../../core/utils";
|
||||
import { BaseHUDPart } from "../base_hud_part";
|
||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||
|
||||
export class HUDModeSettings extends BaseHUDPart {
|
||||
createElements(parent) {
|
||||
this.element = makeDiv(parent, "ingame_HUD_ModeSettings");
|
||||
|
||||
const bind = (selector, handler) => this.trackClicks(this.element.querySelector(selector), handler);
|
||||
|
||||
if (this.root.gameMode.hasZone()) {
|
||||
this.zone = makeDiv(
|
||||
this.element,
|
||||
null,
|
||||
["section", "zone"],
|
||||
`
|
||||
<label>Zone</label>
|
||||
|
||||
<div class="buttons">
|
||||
<div class="zoneWidth plusMinus">
|
||||
<label>Width</label>
|
||||
<button class="styledButton minus">-</button>
|
||||
<span class="value"></span>
|
||||
<button class="styledButton plus">+</button>
|
||||
</div>
|
||||
|
||||
<div class="zoneHeight plusMinus">
|
||||
<label>Height</label>
|
||||
<button class="styledButton minus">-</button>
|
||||
<span class="value"></span>
|
||||
<button class="styledButton plus">+</button>
|
||||
</div>
|
||||
</div>`
|
||||
);
|
||||
|
||||
bind(".zoneWidth .minus", () => this.modifyZone(-1, 0));
|
||||
bind(".zoneWidth .plus", () => this.modifyZone(1, 0));
|
||||
bind(".zoneHeight .minus", () => this.modifyZone(0, -1));
|
||||
bind(".zoneHeight .plus", () => this.modifyZone(0, 1));
|
||||
}
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.visible = false;
|
||||
this.domAttach = new DynamicDomAttach(this.root, this.element);
|
||||
this.updateZoneValues();
|
||||
}
|
||||
|
||||
modifyZone(width, height) {
|
||||
this.root.gameMode.expandZone(width, height);
|
||||
this.updateZoneValues();
|
||||
}
|
||||
|
||||
updateZoneValues() {
|
||||
const zone = this.root.gameMode.getZone();
|
||||
this.element.querySelector(".zoneWidth > .value").textContent = String(zone.w);
|
||||
this.element.querySelector(".zoneHeight > .value").textContent = String(zone.h);
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this.visible = !this.visible;
|
||||
}
|
||||
|
||||
update() {
|
||||
this.domAttach.update(this.visible);
|
||||
}
|
||||
}
|
@ -5,9 +5,11 @@ import { GameRoot } from "../root";
|
||||
import { Rectangle } from "../../core/rectangle";
|
||||
import { types } from "../../savegame/serialization";
|
||||
import { enumGameModeTypes, GameMode } from "../game_mode";
|
||||
import { HUDGameMenu } from "../hud/parts/game_menu";
|
||||
import { HUDInteractiveTutorial } from "../hud/parts/interactive_tutorial";
|
||||
import { HUDPinnedShapes } from "../hud/parts/pinned_shapes";
|
||||
import { HUDKeybindingOverlay } from "../hud/parts/keybinding_overlay";
|
||||
import { HUDPartTutorialHints } from "../hud/parts/tutorial_hints";
|
||||
import { HUDPinnedShapes } from "../hud/parts/pinned_shapes";
|
||||
import { HUDWaypoints } from "../hud/parts/waypoints";
|
||||
|
||||
export class PuzzleGameMode extends GameMode {
|
||||
@ -30,7 +32,9 @@ export class PuzzleGameMode extends GameMode {
|
||||
const data = this.getSaveData();
|
||||
|
||||
this.setHudParts({
|
||||
[HUDGameMenu.name]: false,
|
||||
[HUDInteractiveTutorial.name]: false,
|
||||
[HUDKeybindingOverlay.name]: false,
|
||||
[HUDPartTutorialHints.name]: false,
|
||||
[HUDPinnedShapes.name]: false,
|
||||
[HUDWaypoints.name]: false,
|
||||
|
@ -14,15 +14,39 @@ export class PuzzleEditGameMode extends PuzzleGameMode {
|
||||
return enumGameModeIds.puzzleEdit;
|
||||
}
|
||||
|
||||
static getSchema() {
|
||||
return {};
|
||||
}
|
||||
|
||||
/** @param {GameRoot} root */
|
||||
constructor(root) {
|
||||
super(root);
|
||||
|
||||
this.playtest = false;
|
||||
|
||||
this.setBuildings({
|
||||
[MetaConstantProducerBuilding.name]: true,
|
||||
// [MetaBeltBuilding.name]: true,
|
||||
[MetaGoalAcceptorBuilding.name]: true,
|
||||
// [MetaItemProducerBuilding.name]: true,
|
||||
});
|
||||
}
|
||||
|
||||
isZoneRestricted() {
|
||||
return !this.playtest;
|
||||
}
|
||||
|
||||
isBoundaryRestricted() {
|
||||
return this.playtest;
|
||||
}
|
||||
|
||||
expandZone(w = 0, h = 0) {
|
||||
if (this.zoneWidth + w > 0) {
|
||||
this.zoneWidth += w;
|
||||
}
|
||||
|
||||
if (this.zoneHeight + h > 0) {
|
||||
this.zoneHeight += h;
|
||||
}
|
||||
|
||||
this.zone = this.createCenteredRectangle(this.zoneWidth, this.zoneHeight);
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,10 @@ import { findNiceIntegerValue } from "../../core/utils";
|
||||
import { MetaConstantProducerBuilding } from "../buildings/constant_producer";
|
||||
import { MetaGoalAcceptorBuilding } from "../buildings/goal_acceptor";
|
||||
import { MetaItemProducerBuilding } from "../buildings/item_producer";
|
||||
import { HUDModeMenuBack } from "../hud/parts/mode_menu_back";
|
||||
import { HUDModeMenuNext } from "../hud/parts/mode_menu_next";
|
||||
import { HUDModeMenu } from "../hud/parts/mode_menu";
|
||||
import { HUDModeSettings } from "../hud/parts/mode_settings";
|
||||
import { enumGameModeIds, enumGameModeTypes, GameMode } from "../game_mode";
|
||||
import { ShapeDefinition } from "../shape_definition";
|
||||
import { enumHubGoalRewards } from "../tutorial_goals";
|
||||
@ -493,6 +497,13 @@ export class RegularGameMode extends GameMode {
|
||||
constructor(root) {
|
||||
super(root);
|
||||
|
||||
this.setHudParts({
|
||||
[HUDModeMenuBack.name]: false,
|
||||
[HUDModeMenuNext.name]: false,
|
||||
[HUDModeMenu.name]: false,
|
||||
[HUDModeSettings.name]: false,
|
||||
});
|
||||
|
||||
this.setBuildings({
|
||||
[MetaConstantProducerBuilding.name]: false,
|
||||
[MetaGoalAcceptorBuilding.name]: false,
|
||||
|
@ -24,11 +24,18 @@ export class ZoneSystem extends GameSystem {
|
||||
return;
|
||||
}
|
||||
|
||||
const zone = this.root.gameMode.getZone().expandedInAllDirections(-1);
|
||||
const mode = this.root.gameMode;
|
||||
const zone = mode.getZone().expandedInAllDirections(-1);
|
||||
const transformed = staticComp.getTileSpaceBounds();
|
||||
|
||||
if (zone.containsRect(transformed)) {
|
||||
return STOP_PROPAGATION;
|
||||
if (mode.isZoneRestricted()) {
|
||||
return STOP_PROPAGATION;
|
||||
}
|
||||
} else {
|
||||
if (mode.isBoundaryRestricted()) {
|
||||
return STOP_PROPAGATION;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,15 +45,17 @@ export class ZoneSystem extends GameSystem {
|
||||
* @param {MapChunkView} chunk
|
||||
*/
|
||||
drawChunk(parameters, chunk) {
|
||||
const zone = this.root.gameMode.getZone().allScaled(globalConfig.tileSize);
|
||||
const mode = this.root.gameMode;
|
||||
const zone = mode.getZone().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 = 0.9;
|
||||
context.globalAlpha = 1;
|
||||
context.strokeStyle = THEME.map.zone.border;
|
||||
context.lineWidth = 2;
|
||||
context.strokeRect(zone.x, zone.y, zone.w, zone.h);
|
||||
|
||||
context.globalAlpha = 1;
|
||||
|
@ -483,6 +483,24 @@ ingame:
|
||||
title: Support me
|
||||
desc: I develop the game in my spare time!
|
||||
|
||||
modeMenu:
|
||||
puzzleEditMode:
|
||||
back:
|
||||
title: Main Menu
|
||||
next:
|
||||
title: Playtest
|
||||
desc: You will have to complete the puzzle before being able to publish it
|
||||
puzzleEditTestMode:
|
||||
back:
|
||||
title: Edit
|
||||
next:
|
||||
title: Publish
|
||||
puzzlePlayMode:
|
||||
back:
|
||||
title: Puzzle Menu
|
||||
next:
|
||||
title: Next
|
||||
|
||||
# All shop upgrades
|
||||
shopUpgrades:
|
||||
belt:
|
||||
|
Loading…
Reference in New Issue
Block a user