1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2026-03-02 03:39:21 +00:00

Initial waypoint support

This commit is contained in:
tobspr
2020-05-28 19:40:48 +02:00
parent 37685d64bd
commit ec021780d8
30 changed files with 598 additions and 314 deletions

View File

@@ -250,7 +250,7 @@ input[type="email"] {
opacity: 0.4;
}
transition: background-color 0.4s ease-in-out !important;
transition: background-color 0.1s ease-in-out !important;
@include TextShadow3D(#fff);
@include BoxShadow3D(lighten($mainBgColor, 30));

View File

@@ -129,6 +129,16 @@
height: unset;
margin: 1px 0;
}
input {
background: #eee;
color: #333438;
width: 100%;
&.errored {
background-color: rgb(250, 206, 206);
}
}
}
> .buttons {

View File

@@ -6,6 +6,8 @@
display: flex;
flex-direction: column;
align-items: flex-start;
color: #fff;
text-shadow: #{D(1px)} #{D(1px)} 0 rgba(0, 10, 20, 0.1);
> .binding {
display: inline-grid;
@@ -20,7 +22,7 @@
@include S(height, 10px);
width: 1px;
@include S(margin, 0, 3px);
background-color: #888;
background-color: #fff;
transform: rotate(10deg);
// @include S(margin, 0, 3px);
}
@@ -43,9 +45,7 @@
color: $accentColorDark;
@include SuperSmallText;
text-transform: uppercase;
// font-weight: bold;
color: #fff;
text-shadow: #{D(1px)} #{D(1px)} 0 rgba(0, 10, 20, 0.1);
@include S(margin-left, 5px);
}

View File

@@ -0,0 +1,27 @@
#ingame_HUD_Waypoints {
position: absolute;
@include S(right, 10px);
@include S(bottom, 100px);
display: flex;
flex-direction: column;
@include PlainText;
@include S(width, 150px);
background: rgba(0, 10, 20, 0.5);
@include S(padding, 5px);
color: #eee;
.desc {
@include SuperSmallText;
color: #babcbf;
.keybinding {
position: relative;
}
strong {
color: #fff;
}
}
}

View File

@@ -48,6 +48,7 @@
@import "ingame_hud/tutorial_hints";
@import "ingame_hud/watermark";
@import "ingame_hud/blueprint_placer";
@import "ingame_hud/waypoints";
// prettier-ignore
$elements:
@@ -70,6 +71,7 @@ ingame_HUD_EntityDebugger,
ingame_HUD_TutorialHints,
ingame_HUD_buildings_toolbar,
ingame_HUD_BlueprintPlacer,
ingame_HUD_Waypoints,
ingame_HUD_Watermark,
// Overlays

View File

@@ -3,7 +3,7 @@ export const CHANGELOG = [
version: "1.1.0",
date: "unreleased",
entries: [
"BLUEPRINTS! They are unlocked at level 12",
"BLUEPRINTS! They are unlocked at level 12 and cost a special shape to build.",
"Savegame levels are now shown in the main menu. For existing games, save them again to make the level show up.",
"Allow holding SHIFT to rotate counter clockwise",
"Added confirmation when deleting more than 500 buildings at a time",

View File

@@ -83,7 +83,7 @@ export const globalConfig = {
debug: {
/* dev:start */
fastGameEnter: true,
// fastGameEnter: true,
// noArtificialDelays: true,
// disableSavegameWrite: true,
// showEntityBounds: true,

View File

@@ -385,6 +385,8 @@ export class DialogWithForm extends Dialog {
});
this.confirmButtonId = confirmButton.split(":")[0];
this.formElements = formElements;
this.enterHandler = "ok";
}
internalButtonHandler(id, ...payload) {

View File

@@ -86,7 +86,6 @@ export class AtlasSprite extends BaseSprite {
if (G_IS_DEV) {
assert(context instanceof CanvasRenderingContext2D, "Not a valid context");
}
console.warn("drawing sprite regulary");
const link = this.linksByResolution[ORIGINAL_SCALE];

View File

@@ -147,6 +147,14 @@ export class Camera extends BasicSerializableObject {
this.currentlyMoving = false;
}
/**
* Sets a desired zoom level
* @param {number} zoom
*/
setDesiredZoom(zoom) {
this.desiredZoom = zoom;
}
/**
* Returns if this camera is currently moving by a non-user interaction
*/
@@ -337,15 +345,15 @@ export class Camera extends BasicSerializableObject {
*/
bindKeys() {
const mapper = this.root.keyMapper;
mapper.getBinding(KEYMAPPINGS.ingame.mapMoveUp).add(() => (this.keyboardForce.y = -1));
mapper
.getBinding(KEYMAPPINGS.ingame.mapMoveUp)
.add(() => console.log("move up") || (this.keyboardForce.y = -1));
mapper.getBinding(KEYMAPPINGS.ingame.mapMoveDown).add(() => (this.keyboardForce.y = 1));
mapper.getBinding(KEYMAPPINGS.ingame.mapMoveRight).add(() => (this.keyboardForce.x = 1));
mapper.getBinding(KEYMAPPINGS.ingame.mapMoveLeft).add(() => (this.keyboardForce.x = -1));
mapper.getBinding(KEYMAPPINGS.ingame.mapZoomIn).add(() => (this.desiredZoom = this.zoomLevel * 1.2));
mapper.getBinding(KEYMAPPINGS.ingame.mapZoomOut).add(() => (this.desiredZoom = this.zoomLevel * 0.8));
mapper.getBinding(KEYMAPPINGS.ingame.centerMap).add(() => this.centerOnMap());
}
centerOnMap() {

View File

@@ -7,7 +7,6 @@ import { DrawParameters } from "../../core/draw_parameters";
import { HUDProcessingOverlay } from "./parts/processing_overlay";
import { HUDBuildingsToolbar } from "./parts/buildings_toolbar";
import { HUDBuildingPlacer } from "./parts/building_placer";
import { HUDBetaOverlay } from "./parts/beta_overlay";
import { HUDBlueprintPlacer } from "./parts/blueprint_placer";
import { HUDKeybindingOverlay } from "./parts/keybinding_overlay";
import { HUDUnlockNotification } from "./parts/unlock_notification";
@@ -28,6 +27,7 @@ import { KEYMAPPINGS } from "../key_action_mapper";
import { HUDWatermark } from "./parts/watermark";
import { HUDModalDialogs } from "./parts/modal_dialogs";
import { HUDPartTutorialHints } from "./parts/tutorial_hints";
import { HUDWaypoints } from "./parts/waypoints";
export class GameHUD {
/**
@@ -43,31 +43,23 @@ export class GameHUD {
initialize() {
this.parts = {
processingOverlay: new HUDProcessingOverlay(this.root),
buildingsToolbar: new HUDBuildingsToolbar(this.root),
buildingPlacer: new HUDBuildingPlacer(this.root),
blueprintPlacer: new HUDBlueprintPlacer(this.root),
unlockNotification: new HUDUnlockNotification(this.root),
gameMenu: new HUDGameMenu(this.root),
massSelector: new HUDMassSelector(this.root),
shop: new HUDShop(this.root),
statistics: new HUDStatistics(this.root),
waypoints: new HUDWaypoints(this.root),
vignetteOverlay: new HUDVignetteOverlay(this.root),
// Must always exist
pinnedShapes: new HUDPinnedShapes(this.root),
notifications: new HUDNotifications(this.root),
settingsMenu: new HUDSettingsMenu(this.root),
// betaOverlay: new HUDBetaOverlay(this.root),
debugInfo: new HUDDebugInfo(this.root),
dialogs: new HUDModalDialogs(this.root),
};
@@ -189,7 +181,7 @@ export class GameHUD {
* @param {DrawParameters} parameters
*/
draw(parameters) {
const partsOrder = ["massSelector", "buildingPlacer", "blueprintPlacer"];
const partsOrder = ["waypoints", "massSelector", "buildingPlacer", "blueprintPlacer"];
for (let i = 0; i < partsOrder.length; ++i) {
if (this.parts[partsOrder[i]]) {

View File

@@ -81,8 +81,10 @@ export class HUDBlueprintPlacer extends BaseHUDPart {
*/
onMouseDown(pos, button) {
if (button === enumMouseButton.right) {
this.abortPlacement();
return STOP_PROPAGATION;
if (this.currentBlueprint.get()) {
this.abortPlacement();
return STOP_PROPAGATION;
}
}
const blueprint = this.currentBlueprint.get();

View File

@@ -24,8 +24,8 @@ export class HUDKeybindingOverlay extends BaseHUDPart {
[],
`
<div class="binding">
<code class="keybinding">${getKeycode(KEYMAPPINGS.ingame.centerMap)}</code>
<label>${T.ingame.keybindingsOverlay.centerMap}</label>
<code class="keybinding">${getKeycode(KEYMAPPINGS.ingame.createMarker)}</code>
<label>${T.ingame.keybindingsOverlay.createMarker}</label>
</div>
<div class="binding">

View File

@@ -107,6 +107,11 @@ export class HUDUnlockNotification extends BaseHUDPart {
requestClose() {
this.root.app.adProvider.showVideoAd().then(() => {
this.close();
if (!this.root.app.settings.getAllSettings().offerHints) {
return;
}
if (this.root.hubGoals.level === 3) {
const { showUpgrades } = this.root.hud.parts.dialogs.showInfo(
T.dialogs.upgradesIntroduction.title,

View File

@@ -0,0 +1,208 @@
import { BaseHUDPart } from "../base_hud_part";
import { makeDiv, arrayDelete, arrayDeleteValue, lerp } from "../../../core/utils";
import { Vector } from "../../../core/vector";
import { DrawParameters } from "../../../core/draw_parameters";
import { Loader } from "../../../core/loader";
import { T } from "../../../translations";
import { Rectangle } from "../../../core/rectangle";
import { makeOffscreenBuffer } from "../../../core/buffer_utils";
import { enumMouseButton } from "../../camera";
import { STOP_PROPAGATION } from "../../../core/signal";
import { KEYMAPPINGS } from "../../key_action_mapper";
import { DynamicDomAttach } from "../dynamic_dom_attach";
import { IS_DEMO, globalConfig } from "../../../core/config";
import { DialogWithForm } from "../../../core/modal_dialog_elements";
import { FormElementInput } from "../../../core/modal_dialog_forms";
import { Math_max } from "../../../core/builtins";
import { enumNotificationType } from "./notifications";
export class HUDWaypoints extends BaseHUDPart {
createElements(parent) {
if (this.root.app.settings.getAllSettings().offerHints) {
this.element = makeDiv(
parent,
"ingame_HUD_Waypoints",
[],
`
<strong class='title'>${T.ingame.waypoints.waypoints}</strong>
<span class='desc'>${T.ingame.waypoints.description.replace(
"<keybinding>",
`<code class='keybinding'>${this.root.keyMapper
.getBinding(KEYMAPPINGS.ingame.createMarker)
.getKeyCodeString()}</code>`
)}</span>
`
);
}
this.waypointSprite = Loader.getSprite("sprites/misc/waypoint.png");
}
initialize() {
this.waypoints = [
{
label: T.ingame.waypoints.hub,
center: new Vector(0, 0),
zoomLevel: 3,
deletable: false,
},
];
this.dummyBuffer = makeOffscreenBuffer(1, 1, {
reusable: false,
label: "waypoints-measure-canvas",
})[1];
this.root.camera.downPreHandler.add(this.onMouseDown, this);
this.domAttach = new DynamicDomAttach(this.root, this.element);
this.root.keyMapper.getBinding(KEYMAPPINGS.ingame.createMarker).add(this.requestCreateMarker, this);
this.currentMarkerOpacity = 1;
}
/**
*
* @param {Vector=} worldPos Override the world pos, otherwise it is the camera position
*/
requestCreateMarker(worldPos = null) {
if (IS_DEMO) {
this.root.hud.parts.dialogs.showFeatureRestrictionInfo(T.demo.features.creatingMarkers);
return;
}
const markerNameInput = new FormElementInput({
id: "markerName",
label: null,
placeholder: "",
validator: val => val.length > 0 && val.length < 15,
});
const dialog = new DialogWithForm({
app: this.root.app,
title: T.dialogs.createMarker.title,
desc: T.dialogs.createMarker.desc,
formElements: [markerNameInput],
});
this.root.hud.parts.dialogs.internalShowDialog(dialog);
dialog.buttonSignals.ok.add(() => {
this.waypoints.push({
label: markerNameInput.getValue(),
center: (worldPos || this.root.camera.center).copy(),
zoomLevel: Math_max(this.root.camera.zoomLevel, globalConfig.mapChunkOverviewMinZoom + 0.05),
deletable: true,
});
this.root.hud.signals.notification.dispatch(
T.ingame.waypoints.creationSuccessNotification,
enumNotificationType.success
);
});
}
update() {
this.domAttach.update(this.root.camera.getIsMapOverlayActive());
}
findCurrentIntersectedWaypoint() {
const mousePos = this.root.app.mousePosition;
if (!mousePos) {
return;
}
if (!this.root.camera.getIsMapOverlayActive()) {
return;
}
const scale = this.root.app.getEffectiveUiScale();
this.dummyBuffer.font = "bold " + 12 * scale + "px GameFont";
for (let i = 0; i < this.waypoints.length; ++i) {
const waypoint = this.waypoints[i];
const screenPos = this.root.camera.worldToScreen(waypoint.center);
const intersectionRect = new Rectangle(
screenPos.x - 7 * scale,
screenPos.y - 12 * scale,
15 * scale + this.dummyBuffer.measureText(waypoint.label).width,
15 * scale
);
if (intersectionRect.containsPoint(mousePos.x, mousePos.y)) {
return waypoint;
}
}
}
/**
*
* @param {Vector} pos
* @param {enumMouseButton} button
*/
onMouseDown(pos, button) {
const waypoint = this.findCurrentIntersectedWaypoint();
if (waypoint) {
if (button === enumMouseButton.left) {
this.root.soundProxy.playUiClick();
this.root.camera.setDesiredCenter(waypoint.center);
this.root.camera.setDesiredZoom(waypoint.zoomLevel);
} else if (button === enumMouseButton.right) {
if (waypoint.deletable) {
this.root.soundProxy.playUiClick();
arrayDeleteValue(this.waypoints, waypoint);
} else {
this.root.soundProxy.playUiError();
}
}
return STOP_PROPAGATION;
} else {
// Allow right click to create a marker
if (button === enumMouseButton.right) {
const worldPos = this.root.camera.screenToWorld(pos);
this.requestCreateMarker(worldPos);
return STOP_PROPAGATION;
}
}
}
/**
*
* @param {DrawParameters} parameters
*/
draw(parameters) {
const desiredOpacity = this.root.camera.getIsMapOverlayActive() ? 1 : 0;
this.currentMarkerOpacity = lerp(this.currentMarkerOpacity, desiredOpacity, 0.08);
if (this.currentMarkerOpacity < 0.01) {
return;
}
const selected = this.findCurrentIntersectedWaypoint();
const scale = (1 / this.root.camera.zoomLevel) * this.root.app.getEffectiveUiScale();
for (let i = 0; i < this.waypoints.length; ++i) {
const waypoint = this.waypoints[i];
const pos = waypoint.center;
parameters.context.globalAlpha = this.currentMarkerOpacity * (selected === waypoint ? 1 : 0.7);
parameters.context.fillStyle = "#000";
parameters.context.textAlign = "left";
parameters.context.textBaseline = "middle";
const yOffset = -5 * scale;
parameters.context.font = "bold " + 12 * scale + "px GameFont";
parameters.context.fillText(waypoint.label, pos.x + 6 * scale, pos.y + 0.5 * scale + yOffset);
parameters.context.textBaseline = "alphabetic";
parameters.context.textAlign = "left";
this.waypointSprite.drawCentered(parameters.context, pos.x, pos.y + yOffset, 10 * scale);
}
parameters.context.globalAlpha = 1;
}
}

View File

@@ -25,8 +25,6 @@ export const KEYMAPPINGS = {
mapMoveDown: { keyCode: key("S") },
mapMoveLeft: { keyCode: key("A") },
centerMap: { keyCode: 32 },
menuOpenShop: { keyCode: key("F") },
menuOpenStats: { keyCode: key("G") },
@@ -35,6 +33,8 @@ export const KEYMAPPINGS = {
mapZoomIn: { keyCode: 187, repeated: true }, // "+"
mapZoomOut: { keyCode: 189, repeated: true }, // "-"
createMarker: { keyCode: key("M") },
},
buildings: {
@@ -222,14 +222,16 @@ export function getStringForKeyCode(code) {
export class Keybinding {
/**
*
* @param {KeyActionMapper} keyMapper
* @param {Application} app
* @param {object} param0
* @param {number} param0.keyCode
* @param {boolean=} param0.builtin
* @param {boolean=} param0.repeated
*/
constructor(app, { keyCode, builtin = false, repeated = false }) {
constructor(keyMapper, app, { keyCode, builtin = false, repeated = false }) {
assert(keyCode && Number.isInteger(keyCode), "Invalid key code: " + keyCode);
this.keyMapper = keyMapper;
this.app = app;
this.keyCode = keyCode;
this.builtin = builtin;
@@ -243,7 +245,12 @@ export class Keybinding {
* Returns whether this binding is currently pressed
*/
isCurrentlyPressed() {
return this.app.inputMgr.keysDown.has(this.keyCode);
// Check if the key is down
if (this.app.inputMgr.keysDown.has(this.keyCode)) {
// Check if it is the top reciever
const reciever = this.keyMapper.inputReceiver;
return this.app.inputMgr.getTopReciever() === reciever;
}
}
/**
@@ -293,6 +300,8 @@ export class KeyActionMapper {
*/
constructor(root, inputReciever) {
this.root = root;
this.inputReceiver = inputReciever;
inputReciever.keydown.add(this.handleKeydown, this);
inputReciever.keyup.add(this.handleKeyup, this);
@@ -308,7 +317,7 @@ export class KeyActionMapper {
payload.keyCode = overrides[key];
}
this.keybindings[key] = new Keybinding(this.root.app, payload);
this.keybindings[key] = new Keybinding(this, this.root.app, payload);
}
}

View File

@@ -37,14 +37,6 @@ export class GameTime extends BasicSerializableObject {
// Store how much time we have in bucket
this.logicTimeBudget = 0;
if (G_IS_DEV) {
window.addEventListener("keydown", ev => {
if (ev.key === "p") {
this.requestSpeedToggle();
}
});
}
}
static getId() {
@@ -199,23 +191,6 @@ export class GameTime extends BasicSerializableObject {
return this.speed.getId() === PausedGameSpeed.getId();
}
requestSpeedToggle() {
logger.warn("Request speed toggle");
switch (this.speed.getId()) {
case PausedGameSpeed.getId():
this.setSpeed(new RegularGameSpeed(this.root));
break;
case RegularGameSpeed.getId():
this.setSpeed(new PausedGameSpeed(this.root));
break;
case FastForwardGameSpeed.getId():
this.setSpeed(new RegularGameSpeed(this.root));
break;
}
}
getSpeed() {
return this.speed;
}