1
0
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:
Greg Considine 2021-03-24 16:43:59 -04:00
parent 212ea67125
commit 19c91faf8d
16 changed files with 421 additions and 8 deletions

View 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);
}
}
}

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

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

View 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);
}
}
}
}

View File

@ -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,

View File

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

View File

@ -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) {

View 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() {}
}

View 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();
}
}

View 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() {}
}

View 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);
}
}

View File

@ -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,

View File

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

View File

@ -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,

View File

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

View File

@ -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: