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

Initial take on wires

This commit is contained in:
tobspr
2020-06-24 22:23:10 +02:00
parent 97ef46bd52
commit 6677ff0a44
40 changed files with 1204 additions and 518 deletions

View File

@@ -36,6 +36,7 @@ import { HUDInteractiveTutorial } from "./parts/interactive_tutorial";
import { HUDScreenshotExporter } from "./parts/screenshot_exporter";
import { HUDColorBlindHelper } from "./parts/color_blind_helper";
import { HUDShapeViewer } from "./parts/shape_viewer";
import { HUDWiresOverlay } from "./parts/wires_overlay";
export class GameHUD {
/**
@@ -70,6 +71,7 @@ export class GameHUD {
dialogs: new HUDModalDialogs(this.root),
screenshotExporter: new HUDScreenshotExporter(this.root),
shapeViewer: new HUDShapeViewer(this.root),
wiresOverlay: new HUDWiresOverlay(this.root),
};
this.signals = {

View File

@@ -0,0 +1,172 @@
import { gMetaBuildingRegistry } from "../../../core/global_registries";
import { Signal } from "../../../core/signal";
import { TrackedState } from "../../../core/tracked_state";
import { makeDiv } from "../../../core/utils";
import { KEYMAPPINGS } from "../../key_action_mapper";
import { MetaBuilding } from "../../meta_building";
import { BaseHUDPart } from "../base_hud_part";
import { GameRoot } from "../../root";
export class HUDBaseToolbar extends BaseHUDPart {
/**
* @param {GameRoot} root
* @param {Array<typeof MetaBuilding>} supportedBuildings
* @param {function} visibilityCondition
*/
constructor(root, supportedBuildings, visibilityCondition) {
super(root);
this.supportedBuildings = supportedBuildings;
this.visibilityCondition = visibilityCondition;
/** @type {Object.<string, {
* metaBuilding: MetaBuilding,
* unlocked: boolean,
* selected: boolean,
* element: HTMLElement,
* index: number
* }>} */
this.buildingHandles = {};
this.sigBuildingSelected = new Signal();
this.trackedIsVisisible = new TrackedState(this.onVisibilityChanged, this);
}
/**
* Called when the visibility of the toolbar changed
* @param {boolean} visible
*/
onVisibilityChanged(visible) {
this.element.classList.toggle("visible", visible);
}
/**
* Should create all require elements
* @param {HTMLElement} parent
*/
createElements(parent) {
this.element = makeDiv(parent, "ingame_HUD_buildings_toolbar", ["ingame_buildingsToolbar"], "");
}
initialize() {
const actionMapper = this.root.keyMapper;
const items = makeDiv(this.element, null, ["buildings"]);
for (let i = 0; i < this.supportedBuildings.length; ++i) {
const metaBuilding = gMetaBuildingRegistry.findByClass(this.supportedBuildings[i]);
const binding = actionMapper.getBinding(KEYMAPPINGS.buildings[metaBuilding.getId()]);
const itemContainer = makeDiv(items, null, ["building"]);
itemContainer.setAttribute("data-icon", "building_icons/" + metaBuilding.getId() + ".png");
binding.add(() => this.selectBuildingForPlacement(metaBuilding));
this.trackClicks(itemContainer, () => this.selectBuildingForPlacement(metaBuilding), {
clickSound: null,
});
this.buildingHandles[metaBuilding.id] = {
metaBuilding,
element: itemContainer,
unlocked: false,
selected: false,
index: i,
};
}
this.root.hud.signals.selectedPlacementBuildingChanged.add(
this.onSelectedPlacementBuildingChanged,
this
);
this.lastSelectedIndex = 0;
actionMapper.getBinding(KEYMAPPINGS.placement.cycleBuildings).add(this.cycleBuildings, this);
}
/**
* Updates the toolbar
*/
update() {
this.trackedIsVisisible.set(this.visibilityCondition());
if (!this.trackedIsVisisible.get()) {
// Currently not active
} else {
for (const buildingId in this.buildingHandles) {
const handle = this.buildingHandles[buildingId];
const newStatus = handle.metaBuilding.getIsUnlocked(this.root);
if (handle.unlocked !== newStatus) {
handle.unlocked = newStatus;
handle.element.classList.toggle("unlocked", newStatus);
}
}
}
}
/**
* Cycles through all buildings
*/
cycleBuildings() {
let newIndex = this.lastSelectedIndex;
for (let i = 0; i < this.supportedBuildings.length; ++i, ++newIndex) {
newIndex %= this.supportedBuildings.length;
const metaBuilding = gMetaBuildingRegistry.findByClass(this.supportedBuildings[newIndex]);
const handle = this.buildingHandles[metaBuilding.id];
if (!handle.selected && handle.unlocked) {
break;
}
}
const metaBuildingClass = this.supportedBuildings[newIndex];
const metaBuilding = gMetaBuildingRegistry.findByClass(metaBuildingClass);
this.selectBuildingForPlacement(metaBuilding);
}
/**
* Called when the selected building got changed
* @param {MetaBuilding} metaBuilding
*/
onSelectedPlacementBuildingChanged(metaBuilding) {
for (const buildingId in this.buildingHandles) {
const handle = this.buildingHandles[buildingId];
const newStatus = handle.metaBuilding === metaBuilding;
if (handle.selected !== newStatus) {
handle.selected = newStatus;
handle.element.classList.toggle("selected", newStatus);
}
if (handle.selected) {
this.lastSelectedIndex = handle.index;
}
}
this.element.classList.toggle("buildingSelected", !!metaBuilding);
}
/**
* @param {MetaBuilding} metaBuilding
*/
selectBuildingForPlacement(metaBuilding) {
if (!this.visibilityCondition()) {
// Not active
return;
}
if (!metaBuilding.getIsUnlocked(this.root)) {
this.root.soundProxy.playUiError();
return STOP_PROPAGATION;
}
// Allow clicking an item again to deselect it
for (const buildingId in this.buildingHandles) {
const handle = this.buildingHandles[buildingId];
if (handle.selected && handle.metaBuilding === metaBuilding) {
metaBuilding = null;
break;
}
}
this.root.soundProxy.playUiClick();
this.sigBuildingSelected.dispatch(metaBuilding);
this.onSelectedPlacementBuildingChanged(metaBuilding);
}
}

View File

@@ -13,6 +13,7 @@ import { BaseHUDPart } from "../base_hud_part";
import { SOUNDS } from "../../../platform/sound";
import { MetaMinerBuilding, enumMinerVariants } from "../../buildings/miner";
import { enumHubGoalRewards } from "../../tutorial_goals";
import { enumEditMode } from "../../root";
/**
* Contains all logic for the building placer - this doesn't include the rendering
@@ -115,6 +116,7 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart {
this.root.hud.signals.pasteBlueprintRequested.add(this.abortPlacement, this);
this.root.signals.storyGoalCompleted.add(() => this.signals.variantChanged.dispatch());
this.root.signals.upgradePurchased.add(() => this.signals.variantChanged.dispatch());
this.root.signals.editModeChanged.add(this.onEditModeChanged, this);
// MOUSE BINDINGS
this.root.camera.downPreHandler.add(this.onMouseDown, this);
@@ -122,6 +124,20 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart {
this.root.camera.upPostHandler.add(this.onMouseUp, this);
}
/**
* Called when the edit mode got changed
* @param {enumEditMode} editMode
*/
onEditModeChanged(editMode) {
const metaBuilding = this.currentMetaBuilding.get();
if (metaBuilding) {
if (metaBuilding.getEditLayer() !== editMode) {
// This layer doesn't fit the edit mode anymore
this.currentMetaBuilding.set(null);
}
}
}
/**
* Returns the current base rotation for the current meta-building.
* @returns {number}

View File

@@ -1,9 +1,6 @@
import { gMetaBuildingRegistry } from "../../../core/global_registries";
import { Signal } from "../../../core/signal";
import { TrackedState } from "../../../core/tracked_state";
import { makeDiv } from "../../../core/utils";
import { MetaBeltBaseBuilding } from "../../buildings/belt_base";
import { MetaCutterBuilding } from "../../buildings/cutter";
import { MetaEnergyGenerator } from "../../buildings/energy_generator";
import { MetaMinerBuilding } from "../../buildings/miner";
import { MetaMixerBuilding } from "../../buildings/mixer";
import { MetaPainterBuilding } from "../../buildings/painter";
@@ -12,9 +9,8 @@ import { MetaSplitterBuilding } from "../../buildings/splitter";
import { MetaStackerBuilding } from "../../buildings/stacker";
import { MetaTrashBuilding } from "../../buildings/trash";
import { MetaUndergroundBeltBuilding } from "../../buildings/underground_belt";
import { MetaBuilding } from "../../meta_building";
import { BaseHUDPart } from "../base_hud_part";
import { KEYMAPPINGS } from "../../key_action_mapper";
import { enumEditMode } from "../../root";
import { HUDBaseToolbar } from "./base_toolbar";
const toolbarBuildings = [
MetaBeltBaseBuilding,
@@ -27,146 +23,15 @@ const toolbarBuildings = [
MetaMixerBuilding,
MetaPainterBuilding,
MetaTrashBuilding,
MetaEnergyGenerator,
];
export class HUDBuildingsToolbar extends BaseHUDPart {
export class HUDBuildingsToolbar extends HUDBaseToolbar {
constructor(root) {
super(root);
/** @type {Object.<string, {
* metaBuilding: MetaBuilding,
* unlocked: boolean,
* selected: boolean,
* element: HTMLElement,
* index: number
* }>} */
this.buildingHandles = {};
this.sigBuildingSelected = new Signal();
this.trackedIsVisisible = new TrackedState(this.onVisibilityChanged, this);
}
onVisibilityChanged(visible) {
this.element.classList.toggle("visible", visible);
}
/**
* Should create all require elements
* @param {HTMLElement} parent
*/
createElements(parent) {
this.element = makeDiv(parent, "ingame_HUD_buildings_toolbar", [], "");
}
initialize() {
const actionMapper = this.root.keyMapper;
const items = makeDiv(this.element, null, ["buildings"]);
for (let i = 0; i < toolbarBuildings.length; ++i) {
const metaBuilding = gMetaBuildingRegistry.findByClass(toolbarBuildings[i]);
const binding = actionMapper.getBinding(KEYMAPPINGS.buildings[metaBuilding.getId()]);
const itemContainer = makeDiv(items, null, ["building"]);
itemContainer.setAttribute("data-icon", "building_icons/" + metaBuilding.getId() + ".png");
binding.add(() => this.selectBuildingForPlacement(metaBuilding));
this.trackClicks(itemContainer, () => this.selectBuildingForPlacement(metaBuilding), {
clickSound: null,
});
this.buildingHandles[metaBuilding.id] = {
metaBuilding,
element: itemContainer,
unlocked: false,
selected: false,
index: i,
};
}
this.root.hud.signals.selectedPlacementBuildingChanged.add(
this.onSelectedPlacementBuildingChanged,
this
super(
root,
toolbarBuildings,
() => !this.root.camera.getIsMapOverlayActive() && this.root.editMode === enumEditMode.regular
);
this.lastSelectedIndex = 0;
actionMapper.getBinding(KEYMAPPINGS.placement.cycleBuildings).add(this.cycleBuildings, this);
}
update() {
this.trackedIsVisisible.set(!this.root.camera.getIsMapOverlayActive());
for (const buildingId in this.buildingHandles) {
const handle = this.buildingHandles[buildingId];
const newStatus = handle.metaBuilding.getIsUnlocked(this.root);
if (handle.unlocked !== newStatus) {
handle.unlocked = newStatus;
handle.element.classList.toggle("unlocked", newStatus);
}
}
}
cycleBuildings() {
let newIndex = this.lastSelectedIndex;
for (let i = 0; i < toolbarBuildings.length; ++i, ++newIndex) {
newIndex %= toolbarBuildings.length;
const metaBuilding = gMetaBuildingRegistry.findByClass(toolbarBuildings[newIndex]);
const handle = this.buildingHandles[metaBuilding.id];
if (!handle.selected && handle.unlocked) {
break;
}
}
const metaBuildingClass = toolbarBuildings[newIndex];
const metaBuilding = gMetaBuildingRegistry.findByClass(metaBuildingClass);
this.selectBuildingForPlacement(metaBuilding);
}
/**
* @param {MetaBuilding} metaBuilding
*/
onSelectedPlacementBuildingChanged(metaBuilding) {
for (const buildingId in this.buildingHandles) {
const handle = this.buildingHandles[buildingId];
const newStatus = handle.metaBuilding === metaBuilding;
if (handle.selected !== newStatus) {
handle.selected = newStatus;
handle.element.classList.toggle("selected", newStatus);
}
if (handle.selected) {
this.lastSelectedIndex = handle.index;
}
}
this.element.classList.toggle("buildingSelected", !!metaBuilding);
}
/**
* @param {MetaBuilding} metaBuilding
*/
selectBuildingForPlacement(metaBuilding) {
if (!metaBuilding.getIsUnlocked(this.root)) {
this.root.soundProxy.playUiError();
return;
}
if (this.root.camera.getIsMapOverlayActive()) {
this.root.soundProxy.playUiError();
return;
}
// Allow clicking an item again to deselect it
for (const buildingId in this.buildingHandles) {
const handle = this.buildingHandles[buildingId];
if (handle.selected && handle.metaBuilding === metaBuilding) {
metaBuilding = null;
break;
}
}
this.root.soundProxy.playUiClick();
this.sigBuildingSelected.dispatch(metaBuilding);
this.onSelectedPlacementBuildingChanged(metaBuilding);
}
}

View File

@@ -254,6 +254,13 @@ export class HUDKeybindingOverlay extends BaseHUDPart {
keys: [k.massSelect.massSelectCopy],
condition: () => this.anythingSelectedOnMap,
},
{
// Switch layers
label: T.ingame.keybindingsOverlay.switchLayers,
keys: [k.ingame.switchLayers],
condition: () => true,
},
];
if (!this.root.app.settings.getAllSettings().alwaysMultiplace) {

View File

@@ -0,0 +1,83 @@
import { makeOffscreenBuffer } from "../../../core/buffer_utils";
import { globalConfig } from "../../../core/config";
import { DrawParameters } from "../../../core/draw_parameters";
import { KEYMAPPINGS } from "../../key_action_mapper";
import { enumEditMode } from "../../root";
import { THEME } from "../../theme";
import { BaseHUDPart } from "../base_hud_part";
const wiresBackgroundDpi = 3;
export class HUDWiresOverlay extends BaseHUDPart {
createElements(parent) {}
initialize() {
// Probably not the best location, but the one which makes most sense
this.root.keyMapper.getBinding(KEYMAPPINGS.ingame.switchLayers).add(this.switchLayers, this);
this.generateTilePattern();
}
/**
* Switches between layers
*/
switchLayers() {
if (this.root.editMode === enumEditMode.regular) {
this.root.editMode = enumEditMode.wires;
} else {
this.root.editMode = enumEditMode.regular;
}
this.root.signals.editModeChanged.dispatch(this.root.editMode);
}
/**
* Generates the background pattern for the wires overlay
*/
generateTilePattern() {
const dims = globalConfig.tileSize * wiresBackgroundDpi;
const [canvas, context] = makeOffscreenBuffer(dims, dims, {
smooth: false,
reusable: false,
label: "wires-tile-pattern",
});
context.scale(wiresBackgroundDpi, wiresBackgroundDpi);
context.fillStyle = THEME.map.wires.overlay;
context.fillRect(0, 0, globalConfig.tileSize, globalConfig.tileSize);
const lineWidth = 1;
context.fillRect(0, 0, globalConfig.tileSize, lineWidth);
context.fillRect(0, lineWidth, lineWidth, globalConfig.tileSize);
this.tilePatternCanvas = canvas;
}
/**
*
* @param {DrawParameters} parameters
*/
draw(parameters) {
if (this.root.editMode !== enumEditMode.wires) {
return;
}
if (!this.cachedPatternBackground) {
this.cachedPatternBackground = parameters.context.createPattern(this.tilePatternCanvas, "repeat");
}
const bounds = parameters.visibleRect;
const scaleFactor = 1 / wiresBackgroundDpi;
parameters.context.scale(scaleFactor, scaleFactor);
parameters.context.fillStyle = this.cachedPatternBackground;
parameters.context.fillRect(
bounds.x / scaleFactor,
bounds.y / scaleFactor,
bounds.w / scaleFactor,
bounds.h / scaleFactor
);
parameters.context.scale(1 / scaleFactor, 1 / scaleFactor);
}
}