mirror of
https://github.com/tobspr/shapez.io.git
synced 2025-06-13 13:04:03 +00:00
Waypoints - Step 1
This commit is contained in:
parent
5bda1ee8a5
commit
7390fa350a
95
src/css/ingame_hud/waypoints.scss
Normal file
95
src/css/ingame_hud/waypoints.scss
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
#ingame_HUD_Waypoints {
|
||||||
|
.content {
|
||||||
|
@include S(width, 500px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizardWrap {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr auto;
|
||||||
|
@include S(column-gap, 5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
@include S(margin-top, 10px);
|
||||||
|
@include S(height, 350px);
|
||||||
|
overflow-y: scroll;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
@include S(padding-right, 4px);
|
||||||
|
|
||||||
|
> .noWaypoints {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
@include PlainText;
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
> div {
|
||||||
|
background: #f4f4f4;
|
||||||
|
@include S(border-radius, $globalBorderRadius);
|
||||||
|
@include S(margin-bottom, 4px);
|
||||||
|
display: grid;
|
||||||
|
|
||||||
|
@include DarkThemeOverride {
|
||||||
|
background: #222428;
|
||||||
|
color: #efefef;
|
||||||
|
}
|
||||||
|
|
||||||
|
grid-template-columns: 1fr auto;
|
||||||
|
@include S(padding, 5px);
|
||||||
|
@include S(padding-left, 10px);
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
@include Text;
|
||||||
|
|
||||||
|
@include S(padding-top, 10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.position {
|
||||||
|
@include PlainText;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
@include S(margin, 4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.removeButton {
|
||||||
|
background: #df3f3d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.teleportButton {
|
||||||
|
background: #804db1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialogInner {
|
||||||
|
&[data-displaymode="detailed"] .content.hasEntries {
|
||||||
|
> div {
|
||||||
|
@include S(padding, 10px);
|
||||||
|
@include S(height, 40px);
|
||||||
|
grid-template-columns: auto 1fr auto;
|
||||||
|
@include S(grid-column-gap, 15px);
|
||||||
|
|
||||||
|
.counter {
|
||||||
|
grid-column: 3 / 4;
|
||||||
|
grid-row: 1 / 2;
|
||||||
|
@include Heading;
|
||||||
|
align-self: center;
|
||||||
|
text-align: right;
|
||||||
|
color: #55595a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -39,6 +39,7 @@
|
|||||||
@import "ingame_hud/vignette_overlay";
|
@import "ingame_hud/vignette_overlay";
|
||||||
@import "ingame_hud/statistics";
|
@import "ingame_hud/statistics";
|
||||||
@import "ingame_hud/pinned_shapes";
|
@import "ingame_hud/pinned_shapes";
|
||||||
|
@import "ingame_hud/waypoints";
|
||||||
@import "ingame_hud/notifications";
|
@import "ingame_hud/notifications";
|
||||||
@import "ingame_hud/settings_menu";
|
@import "ingame_hud/settings_menu";
|
||||||
@import "ingame_hud/debug_info";
|
@import "ingame_hud/debug_info";
|
||||||
@ -71,6 +72,7 @@ ingame_HUD_BetaOverlay,
|
|||||||
ingame_HUD_UnlockNotification,
|
ingame_HUD_UnlockNotification,
|
||||||
ingame_HUD_Shop,
|
ingame_HUD_Shop,
|
||||||
ingame_HUD_Statistics,
|
ingame_HUD_Statistics,
|
||||||
|
ingame_HUD_Waypoints,
|
||||||
ingame_HUD_SettingsMenu,
|
ingame_HUD_SettingsMenu,
|
||||||
ingame_HUD_ModalDialogs;
|
ingame_HUD_ModalDialogs;
|
||||||
|
|
||||||
|
@ -56,6 +56,9 @@ export class Camera extends BasicSerializableObject {
|
|||||||
/** @type {Vector} */
|
/** @type {Vector} */
|
||||||
this.center = new Vector(0, 0);
|
this.center = new Vector(0, 0);
|
||||||
|
|
||||||
|
/** @type {{ name: string, pos: Vector }[]} */
|
||||||
|
this.waypoints = [];
|
||||||
|
|
||||||
// Input handling
|
// Input handling
|
||||||
this.currentlyMoving = false;
|
this.currentlyMoving = false;
|
||||||
this.lastMovingPosition = null;
|
this.lastMovingPosition = null;
|
||||||
@ -117,6 +120,12 @@ export class Camera extends BasicSerializableObject {
|
|||||||
return {
|
return {
|
||||||
zoomLevel: types.float,
|
zoomLevel: types.float,
|
||||||
center: types.vector,
|
center: types.vector,
|
||||||
|
waypoints: types.array(
|
||||||
|
types.structured({
|
||||||
|
name: types.string,
|
||||||
|
pos: types.vector,
|
||||||
|
})
|
||||||
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ import { HUDVignetteOverlay } from "./parts/vignette_overlay";
|
|||||||
import { HUDStatistics } from "./parts/statistics";
|
import { HUDStatistics } from "./parts/statistics";
|
||||||
import { MetaBuilding } from "../meta_building";
|
import { MetaBuilding } from "../meta_building";
|
||||||
import { HUDPinnedShapes } from "./parts/pinned_shapes";
|
import { HUDPinnedShapes } from "./parts/pinned_shapes";
|
||||||
|
import { HUDWaypoints } from "./parts/waypoints";
|
||||||
import { ShapeDefinition } from "../shape_definition";
|
import { ShapeDefinition } from "../shape_definition";
|
||||||
import { HUDNotifications, enumNotificationType } from "./parts/notifications";
|
import { HUDNotifications, enumNotificationType } from "./parts/notifications";
|
||||||
import { HUDSettingsMenu } from "./parts/settings_menu";
|
import { HUDSettingsMenu } from "./parts/settings_menu";
|
||||||
@ -26,6 +27,7 @@ import { HUDEntityDebugger } from "./parts/entity_debugger";
|
|||||||
import { KEYMAPPINGS } from "../key_action_mapper";
|
import { KEYMAPPINGS } from "../key_action_mapper";
|
||||||
import { HUDWatermark } from "./parts/watermark";
|
import { HUDWatermark } from "./parts/watermark";
|
||||||
import { HUDModalDialogs } from "./parts/modal_dialogs";
|
import { HUDModalDialogs } from "./parts/modal_dialogs";
|
||||||
|
import { Vector } from "../../core/vector";
|
||||||
|
|
||||||
export class GameHUD {
|
export class GameHUD {
|
||||||
/**
|
/**
|
||||||
@ -53,6 +55,7 @@ export class GameHUD {
|
|||||||
|
|
||||||
shop: new HUDShop(this.root),
|
shop: new HUDShop(this.root),
|
||||||
statistics: new HUDStatistics(this.root),
|
statistics: new HUDStatistics(this.root),
|
||||||
|
waypoints: new HUDWaypoints(this.root),
|
||||||
|
|
||||||
vignetteOverlay: new HUDVignetteOverlay(this.root),
|
vignetteOverlay: new HUDVignetteOverlay(this.root),
|
||||||
|
|
||||||
|
@ -29,6 +29,12 @@ export class HUDGameMenu extends BaseHUDPart {
|
|||||||
handler: () => this.root.hud.parts.statistics.show(),
|
handler: () => this.root.hud.parts.statistics.show(),
|
||||||
keybinding: KEYMAPPINGS.ingame.menuOpenStats,
|
keybinding: KEYMAPPINGS.ingame.menuOpenStats,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "waypoints",
|
||||||
|
label: "Waypoints",
|
||||||
|
handler: () => this.root.hud.parts.waypoints.show(),
|
||||||
|
keybinding: KEYMAPPINGS.ingame.menuOpenWaypoints,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
/** @type {Array<{
|
/** @type {Array<{
|
||||||
|
215
src/js/game/hud/parts/waypoints.js
Normal file
215
src/js/game/hud/parts/waypoints.js
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
import { BaseHUDPart } from "../base_hud_part";
|
||||||
|
import { DrawParameters } from "../../../core/draw_parameters";
|
||||||
|
import { makeDiv, removeAllChildren, makeButton } from "../../../core/utils";
|
||||||
|
import { T } from "../../../translations";
|
||||||
|
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||||
|
import { InputReceiver } from "../../../core/input_receiver";
|
||||||
|
import { KeyActionMapper, KEYMAPPINGS } from "../../key_action_mapper";
|
||||||
|
import { createLogger } from "../../../core/logging";
|
||||||
|
|
||||||
|
const logger = createLogger("waypoints");
|
||||||
|
|
||||||
|
export class HUDWaypoints extends BaseHUDPart {
|
||||||
|
createElements(parent) {
|
||||||
|
this.background = makeDiv(parent, "ingame_HUD_Waypoints", ["ingameDialog"]);
|
||||||
|
|
||||||
|
// DIALOG Inner / Wrapper
|
||||||
|
this.dialogInner = makeDiv(this.background, null, ["dialogInner"]);
|
||||||
|
this.title = makeDiv(this.dialogInner, null, ["title"], T.ingame.waypoints.title);
|
||||||
|
this.closeButton = makeDiv(this.title, null, ["closeButton"]);
|
||||||
|
this.trackClicks(this.closeButton, this.close);
|
||||||
|
|
||||||
|
this.wizardWrap = makeDiv(this.dialogInner, null, ["wizardWrap"]);
|
||||||
|
|
||||||
|
// FIXME: Make use of built-in methods
|
||||||
|
this.nameInput = document.createElement("input");
|
||||||
|
this.nameInput.classList.add("findOrCreate");
|
||||||
|
this.nameInput.placeholder = T.ingame.waypoints.findOrCreate;
|
||||||
|
|
||||||
|
this.nameInput.addEventListener("focus", () => {
|
||||||
|
this.root.app.inputMgr.makeSureAttachedAndOnTop(this.textInputReciever);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.nameInput.addEventListener("blur", () => {
|
||||||
|
this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.nameInput.addEventListener("keyup", ev => {
|
||||||
|
if (ev.keyCode == 13) {
|
||||||
|
ev.preventDefault();
|
||||||
|
if (!this.newWaypoint()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.nameInput.blur();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.rerenderFull();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.wizardWrap.appendChild(this.nameInput);
|
||||||
|
|
||||||
|
this.newButton = makeButton(this.wizardWrap, ["newButton"], T.ingame.waypoints.buttonNew);
|
||||||
|
this.trackClicks(this.newButton, this.newWaypoint);
|
||||||
|
|
||||||
|
this.contentDiv = makeDiv(this.dialogInner, null, ["content"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
this.domAttach = new DynamicDomAttach(this.root, this.background, {
|
||||||
|
attachClass: "visible",
|
||||||
|
});
|
||||||
|
|
||||||
|
this.textInputReciever = new InputReceiver("waypoints_text");
|
||||||
|
|
||||||
|
this.inputReciever = new InputReceiver("waypoints");
|
||||||
|
this.keyActionMapper = new KeyActionMapper(this.root, this.inputReciever);
|
||||||
|
|
||||||
|
this.keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.close, this);
|
||||||
|
this.keyActionMapper.getBinding(KEYMAPPINGS.ingame.menuOpenWaypoints).add(this.close, this);
|
||||||
|
|
||||||
|
this.close();
|
||||||
|
this.rerenderFull();
|
||||||
|
}
|
||||||
|
|
||||||
|
getNextWaypointName() {
|
||||||
|
const inputName = this.nameInput.value.trim().substr(0, 32);
|
||||||
|
if (inputName !== "") return inputName;
|
||||||
|
|
||||||
|
let counter = 0;
|
||||||
|
let autoName = "The BEST name for a WAYPOINT!";
|
||||||
|
|
||||||
|
do {
|
||||||
|
counter++;
|
||||||
|
autoName = T.ingame.waypoints.defaultName.replace("<num>", counter.toString());
|
||||||
|
} while (this.waypoints.find(w => w.name == autoName));
|
||||||
|
|
||||||
|
return autoName;
|
||||||
|
}
|
||||||
|
|
||||||
|
newWaypoint() {
|
||||||
|
const vector = this.root.camera.center.round();
|
||||||
|
if (this.waypoints.find(w => w.pos.distance(vector) < 2)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.waypoints.push({
|
||||||
|
name: this.getNextWaypointName(),
|
||||||
|
pos: vector,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.nameInput.value = "";
|
||||||
|
this.rerenderFull();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
document.body.classList.remove("ingameDialogOpen");
|
||||||
|
}
|
||||||
|
|
||||||
|
show() {
|
||||||
|
this.visible = true;
|
||||||
|
document.body.classList.add("ingameDialogOpen");
|
||||||
|
this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
|
||||||
|
this.rerenderFull();
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.nameInput.value = "";
|
||||||
|
|
||||||
|
this.visible = false;
|
||||||
|
document.body.classList.remove("ingameDialogOpen");
|
||||||
|
this.root.app.inputMgr.makeSureDetached(this.inputReciever);
|
||||||
|
this.root.app.inputMgr.makeSureDetached(this.textInputReciever);
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
this.domAttach.update(this.visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} index
|
||||||
|
*/
|
||||||
|
removeWaypoint(index) {
|
||||||
|
if (this.waypoints[index] === undefined) {
|
||||||
|
logger.warn("Attempt to remove nonexisting waypoint", index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.waypoints.splice(index, 1);
|
||||||
|
this.rerenderFull();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} index
|
||||||
|
*/
|
||||||
|
waypointTeleport(index) {
|
||||||
|
if (this.waypoints[index] === undefined) {
|
||||||
|
logger.warn("Attempt to teleport to nonexisting waypoint", index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.close();
|
||||||
|
this.root.camera.setDesiredCenter(this.waypoints[index].pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
rerenderFull() {
|
||||||
|
this.waypoints = this.root.camera.waypoints;
|
||||||
|
removeAllChildren(this.contentDiv);
|
||||||
|
|
||||||
|
if (this.waypoints.length == 0) {
|
||||||
|
return (this.contentDiv.innerHTML = `
|
||||||
|
<strong class="noWaypoints">
|
||||||
|
${T.ingame.waypoints.noWaypoints.replace("<buttonNew>", T.ingame.waypoints.buttonNew)}
|
||||||
|
</strong>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.waypoints.forEach(waypoint => {
|
||||||
|
const term = this.nameInput.value.replaceAll(" ", "");
|
||||||
|
if (term !== "") {
|
||||||
|
const simpleName = waypoint.name.toLowerCase().replaceAll(" ", "");
|
||||||
|
if (!simpleName.includes(term.toLowerCase())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const tilePos = waypoint.pos.toTileSpace();
|
||||||
|
|
||||||
|
const wpContainer = makeDiv(this.contentDiv, null, ["waypoint"]);
|
||||||
|
const positionStr = T.ingame.waypoints.position
|
||||||
|
.replace("<xpos>", tilePos.x)
|
||||||
|
.replace("<ypos>", tilePos.y);
|
||||||
|
|
||||||
|
const index = this.waypoints.indexOf(waypoint);
|
||||||
|
|
||||||
|
// Waypoint name
|
||||||
|
makeDiv(wpContainer, null, ["title"], waypoint.name);
|
||||||
|
|
||||||
|
// Remove button
|
||||||
|
const buttonRemove = makeButton(wpContainer, ["removeButton"], T.ingame.waypoints.buttonRemove);
|
||||||
|
this.trackClicks(buttonRemove, this.removeWaypoint.bind(this, index));
|
||||||
|
|
||||||
|
// Waypoint coords
|
||||||
|
makeDiv(wpContainer, null, ["position"], positionStr);
|
||||||
|
|
||||||
|
// Teleport button
|
||||||
|
const buttonTeleport = makeButton(
|
||||||
|
wpContainer,
|
||||||
|
["teleportButton"],
|
||||||
|
T.ingame.waypoints.buttonTeleport
|
||||||
|
);
|
||||||
|
this.trackClicks(buttonTeleport, this.waypointTeleport.bind(this, index));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {DrawParameters} parameters
|
||||||
|
*/
|
||||||
|
drawOverlays(parameters) {
|
||||||
|
// TODO: Draw tile overlays on existing waypoints
|
||||||
|
}
|
||||||
|
}
|
@ -29,6 +29,7 @@ export const KEYMAPPINGS = {
|
|||||||
|
|
||||||
menuOpenShop: { keyCode: key("F") },
|
menuOpenShop: { keyCode: key("F") },
|
||||||
menuOpenStats: { keyCode: key("G") },
|
menuOpenStats: { keyCode: key("G") },
|
||||||
|
menuOpenWaypoints: { keyCode: key("H") },
|
||||||
|
|
||||||
toggleHud: { keyCode: 113 }, // F2
|
toggleHud: { keyCode: 113 }, // F2
|
||||||
toggleFPSInfo: { keyCode: 115 }, // F1
|
toggleFPSInfo: { keyCode: 115 }, // F1
|
||||||
|
@ -239,6 +239,21 @@ ingame:
|
|||||||
# Displays the shapes per minute, e.g. '523 / m'
|
# Displays the shapes per minute, e.g. '523 / m'
|
||||||
shapesPerMinute: <shapes> / m
|
shapesPerMinute: <shapes> / m
|
||||||
|
|
||||||
|
# The "Waypoints" window
|
||||||
|
waypoints:
|
||||||
|
title: Waypoints
|
||||||
|
position: "X: <xpos>; Y: <ypos>"
|
||||||
|
|
||||||
|
findOrCreate: Find or create...
|
||||||
|
buttonNew: Add Waypoint
|
||||||
|
buttonTeleport: Teleport
|
||||||
|
buttonRemove: Remove
|
||||||
|
|
||||||
|
# When a new waypoint is created, this name is used.
|
||||||
|
defaultName: Waypoint <num>
|
||||||
|
noWaypoints: >-
|
||||||
|
There are no waypoints. Click "<buttonNew>" to create a new waypoint.
|
||||||
|
|
||||||
# Settings menu, when you press "ESC"
|
# Settings menu, when you press "ESC"
|
||||||
settingsMenu:
|
settingsMenu:
|
||||||
playtime: Playtime
|
playtime: Playtime
|
||||||
|
Loading…
Reference in New Issue
Block a user