mirror of
https://github.com/tobspr/shapez.io.git
synced 2025-06-13 13:04:03 +00:00
Add building, HUD exclusion, building exclusion, and refactor
- [WIP] Add ConstantProducer building that combines ConstantSignal and ItemProducer functionality. Currently using temp assets. - Add pre-placement check to the zone - Use Rectangles for zone and boundary - Simplify zone drawing - Account for exclusion in savegame data - [WIP] Add puzzle play and edit buttons in puzzle mode menu
This commit is contained in:
parent
47abc24436
commit
dae1ad3687
BIN
res/ui/building_icons/constant_producer.png
Normal file
BIN
res/ui/building_icons/constant_producer.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.9 KiB |
BIN
res/ui/building_tutorials/constant_producer.png
Normal file
BIN
res/ui/building_tutorials/constant_producer.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 271 KiB |
BIN
res_raw/sprites/blueprints/constant_producer.png
Normal file
BIN
res_raw/sprites/blueprints/constant_producer.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.4 KiB |
BIN
res_raw/sprites/buildings/constant_producer.png
Normal file
BIN
res_raw/sprites/buildings/constant_producer.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.7 KiB |
@ -1,6 +1,6 @@
|
||||
$buildings: belt, cutter, miner, mixer, painter, rotater, balancer, stacker, trash, underground_belt, wire,
|
||||
constant_signal, logic_gate, lever, filter, wire_tunnel, display, virtual_processor, reader, storage,
|
||||
transistor, analyzer, comparator, item_producer;
|
||||
transistor, analyzer, comparator, item_producer, constant_producer;
|
||||
|
||||
@each $building in $buildings {
|
||||
[data-icon="building_icons/#{$building}.png"] {
|
||||
@ -13,7 +13,8 @@ $buildingsAndVariants: belt, balancer, underground_belt, underground_belt-tier2,
|
||||
cutter, cutter-quad, rotater, rotater-ccw, stacker, mixer, painter-double, painter-quad, trash, storage,
|
||||
reader, rotater-rotate180, display, constant_signal, wire, wire_tunnel, logic_gate-or, logic_gate-not,
|
||||
logic_gate-xor, analyzer, virtual_processor-rotater, virtual_processor-unstacker, item_producer,
|
||||
virtual_processor-stacker, virtual_processor-painter, wire-second, painter, painter-mirrored, comparator;
|
||||
constant_producer, virtual_processor-stacker, virtual_processor-painter, wire-second, painter,
|
||||
painter-mirrored, comparator;
|
||||
@each $building in $buildingsAndVariants {
|
||||
[data-icon="building_tutorials/#{$building}.png"] {
|
||||
/* @load-async */
|
||||
|
@ -242,6 +242,16 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.modeButtons {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
@include S(grid-column-gap, 10px);
|
||||
align-items: start;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.browserWarning {
|
||||
@include S(margin-bottom, 10px);
|
||||
background-color: $colorRedBright;
|
||||
@ -285,6 +295,18 @@
|
||||
@include S(margin-left, 15px);
|
||||
}
|
||||
|
||||
.playModeButton {
|
||||
@include IncreasedClickArea(0px);
|
||||
@include S(margin-top, 15px);
|
||||
@include S(margin-left, 15px);
|
||||
}
|
||||
|
||||
.editModeButton {
|
||||
@include IncreasedClickArea(0px);
|
||||
@include S(margin-top, 15px);
|
||||
@include S(margin-left, 15px);
|
||||
}
|
||||
|
||||
.savegames {
|
||||
@include S(max-height, 105px);
|
||||
overflow-y: auto;
|
||||
@ -452,7 +474,7 @@
|
||||
.buttons {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
grid-column-gap: 10px;
|
||||
@include S(grid-column-gap, 10px);
|
||||
align-items: start;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
35
src/js/game/buildings/constant_producer.js
Normal file
35
src/js/game/buildings/constant_producer.js
Normal file
@ -0,0 +1,35 @@
|
||||
/* typehints:start */
|
||||
import { Entity } from "../entity";
|
||||
/* typehints:end */
|
||||
|
||||
import { enumDirection, Vector } from "../../core/vector";
|
||||
import { ItemEjectorComponent } from "../components/item_ejector";
|
||||
import { enumItemProducerType, ItemProducerComponent } from "../components/item_producer";
|
||||
import { MetaBuilding } from "../meta_building";
|
||||
|
||||
export class MetaConstantProducerBuilding extends MetaBuilding {
|
||||
constructor() {
|
||||
super("constant_producer");
|
||||
}
|
||||
|
||||
getSilhouetteColor() {
|
||||
return "#bfd630";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the entity at the given location
|
||||
* @param {Entity} entity
|
||||
*/
|
||||
setupEntityComponents(entity) {
|
||||
entity.addComponent(
|
||||
new ItemEjectorComponent({
|
||||
slots: [{ pos: new Vector(0, 0), direction: enumDirection.top }],
|
||||
})
|
||||
);
|
||||
entity.addComponent(
|
||||
new ItemProducerComponent({
|
||||
type: enumItemProducerType.wireless,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
@ -39,6 +39,6 @@ export class MetaItemProducerBuilding extends MetaBuilding {
|
||||
],
|
||||
})
|
||||
);
|
||||
entity.addComponent(new ItemProducerComponent());
|
||||
entity.addComponent(new ItemProducerComponent({}));
|
||||
}
|
||||
}
|
||||
|
@ -711,7 +711,6 @@ export class Camera extends BasicSerializableObject {
|
||||
|
||||
this.didMoveSinceTouchStart = this.didMoveSinceTouchStart || delta.length() > 0;
|
||||
this.center = this.center.add(delta);
|
||||
this.clampPosition(this.center);
|
||||
|
||||
this.touchPostMoveVelocity = this.touchPostMoveVelocity
|
||||
.multiplyScalar(velocitySmoothing)
|
||||
@ -763,16 +762,15 @@ export class Camera extends BasicSerializableObject {
|
||||
* Clamps x, y position within set boundaries
|
||||
* @param {Vector} vector
|
||||
*/
|
||||
clampPosition(vector) {
|
||||
if (!this.root.gameMode.hasBoundaries()) {
|
||||
clampToBounds(vector) {
|
||||
if (!this.root.gameMode.hasBounds()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const width = this.root.gameMode.getBoundaryWidth();
|
||||
const height = this.root.gameMode.getBoundaryHeight();
|
||||
const bounds = this.root.gameMode.getBounds().allScaled(globalConfig.tileSize);
|
||||
|
||||
vector.x = clamp(vector.x, -width, width);
|
||||
vector.y = clamp(vector.y, -height, height);
|
||||
vector.x = clamp(vector.x, bounds.x, bounds.x + bounds.w);
|
||||
vector.y = clamp(vector.y, bounds.y, bounds.y + bounds.h);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -878,6 +876,7 @@ export class Camera extends BasicSerializableObject {
|
||||
// Panning
|
||||
this.currentPan = mixVector(this.currentPan, this.desiredPan, 0.06);
|
||||
this.center = this.center.add(this.currentPan.multiplyScalar((0.5 * dt) / this.zoomLevel));
|
||||
this.clampToBounds(this.center);
|
||||
}
|
||||
}
|
||||
|
||||
@ -943,7 +942,7 @@ export class Camera extends BasicSerializableObject {
|
||||
)
|
||||
);
|
||||
|
||||
this.clampPosition(this.center);
|
||||
this.clampToBounds(this.center);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1029,6 +1028,8 @@ export class Camera extends BasicSerializableObject {
|
||||
|
||||
this.center.x += moveAmount * forceX * movementSpeed;
|
||||
this.center.y += moveAmount * forceY * movementSpeed;
|
||||
|
||||
this.clampToBounds(this.center);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,22 @@
|
||||
import { Component } from "../component";
|
||||
|
||||
/** @enum {string} */
|
||||
export const enumItemProducerType = {
|
||||
wired: "wired",
|
||||
wireless: "wireless",
|
||||
};
|
||||
|
||||
export class ItemProducerComponent extends Component {
|
||||
static getId() {
|
||||
return "ItemProducer";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} options
|
||||
* @prop {type=} options.type
|
||||
*/
|
||||
constructor({ type = enumItemProducerType.wired }) {
|
||||
super();
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
/* typehints:start */
|
||||
import { GameRoot } from "./root";
|
||||
import { Rectangle } from "../core/rectangle";
|
||||
/* typehints:end */
|
||||
|
||||
import { gGameModeRegistry } from "../core/global_registries";
|
||||
@ -44,6 +45,8 @@ export class GameMode extends BasicSerializableObject {
|
||||
constructor(root) {
|
||||
super();
|
||||
this.root = root;
|
||||
this.hudParts = {};
|
||||
this.buildings = {};
|
||||
}
|
||||
|
||||
/** @returns {object} */
|
||||
@ -71,12 +74,12 @@ export class GameMode extends BasicSerializableObject {
|
||||
return this.constructor.getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name - Class name of HUD Part
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isHudPartHidden(name) {
|
||||
return false;
|
||||
setBuildings(buildings) {
|
||||
Object.assign(this.buildings, buildings);
|
||||
}
|
||||
|
||||
setHudParts(parts) {
|
||||
Object.assign(this.hudParts, parts);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -84,7 +87,15 @@ export class GameMode extends BasicSerializableObject {
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isHudPartExcluded(name) {
|
||||
return false;
|
||||
return this.hudParts[name] === false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name - Class name of building
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isBuildingExcluded(name) {
|
||||
return this.buildings[name] === false;
|
||||
}
|
||||
|
||||
/** @returns {boolean} */
|
||||
@ -92,11 +103,6 @@ export class GameMode extends BasicSerializableObject {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @returns {boolean} */
|
||||
hasHints() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @returns {boolean} */
|
||||
hasHub() {
|
||||
return true;
|
||||
@ -108,7 +114,7 @@ export class GameMode extends BasicSerializableObject {
|
||||
}
|
||||
|
||||
/** @returns {boolean} */
|
||||
hasBoundaries() {
|
||||
hasBounds() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -122,29 +128,24 @@ export class GameMode extends BasicSerializableObject {
|
||||
return 3.5;
|
||||
}
|
||||
|
||||
/** @returns {object} */
|
||||
/** @returns {Object<string, Array>} */
|
||||
getUpgrades() {
|
||||
return {};
|
||||
return {
|
||||
belt: [],
|
||||
miner: [],
|
||||
processors: [],
|
||||
painting: [],
|
||||
};
|
||||
}
|
||||
|
||||
/** @returns {number} */
|
||||
getZoneWidth() {
|
||||
return 0;
|
||||
/** @returns {?Rectangle} */
|
||||
getZone() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @returns {number} */
|
||||
getZoneHeight() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @returns {number} */
|
||||
getBoundaryWidth() {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
/** @returns {number} */
|
||||
getBoundaryHeight() {
|
||||
return Infinity;
|
||||
/** @returns {?Rectangle} */
|
||||
getBounds() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @returns {array} */
|
||||
|
@ -6,7 +6,6 @@ import { createLogger } from "../core/logging";
|
||||
import { BeltSystem } from "./systems/belt";
|
||||
import { ItemEjectorSystem } from "./systems/item_ejector";
|
||||
import { MapResourcesSystem } from "./systems/map_resources";
|
||||
import { MapZoneSystem } from "./systems/map_zone";
|
||||
import { MinerSystem } from "./systems/miner";
|
||||
import { ItemProcessorSystem } from "./systems/item_processor";
|
||||
import { UndergroundBeltSystem } from "./systems/underground_belt";
|
||||
@ -25,6 +24,7 @@ import { ItemProcessorOverlaysSystem } from "./systems/item_processor_overlays";
|
||||
import { BeltReaderSystem } from "./systems/belt_reader";
|
||||
import { FilterSystem } from "./systems/filter";
|
||||
import { ItemProducerSystem } from "./systems/item_producer";
|
||||
import { ZoneSystem } from "./systems/zone";
|
||||
|
||||
const logger = createLogger("game_system_manager");
|
||||
|
||||
@ -47,9 +47,6 @@ export class GameSystemManager {
|
||||
/** @type {MapResourcesSystem} */
|
||||
mapResources: null,
|
||||
|
||||
/** @type {MapZoneSystem} */
|
||||
mapZone: null,
|
||||
|
||||
/** @type {MinerSystem} */
|
||||
miner: null,
|
||||
|
||||
@ -104,6 +101,9 @@ export class GameSystemManager {
|
||||
/** @type {ItemProducerSystem} */
|
||||
itemProducer: null,
|
||||
|
||||
/** @type {ZoneSystem} */
|
||||
zone: null,
|
||||
|
||||
/* typehints:end */
|
||||
};
|
||||
this.systemUpdateOrder = [];
|
||||
@ -142,9 +142,9 @@ export class GameSystemManager {
|
||||
|
||||
add("itemEjector", ItemEjectorSystem);
|
||||
|
||||
if (this.root.gameMode.hasResources()) {
|
||||
add("mapResources", MapResourcesSystem);
|
||||
|
||||
add("mapZone", MapZoneSystem);
|
||||
}
|
||||
|
||||
add("hub", HubSystem);
|
||||
|
||||
@ -171,6 +171,10 @@ export class GameSystemManager {
|
||||
|
||||
add("itemProcessorOverlays", ItemProcessorOverlaysSystem);
|
||||
|
||||
if (this.root.gameMode.hasZone()) {
|
||||
add("zone", ZoneSystem);
|
||||
}
|
||||
|
||||
logger.log("📦 There are", this.systemUpdateOrder.length, "game systems");
|
||||
}
|
||||
|
||||
|
@ -129,10 +129,15 @@ export class GameHUD {
|
||||
this.parts.changesDebugger = new HUDChangesDebugger(this.root);
|
||||
}
|
||||
|
||||
if (this.root.gameMode.hasHints() && this.root.app.settings.getAllSettings().offerHints) {
|
||||
if (this.root.app.settings.getAllSettings().offerHints) {
|
||||
if (!this.root.gameMode.isHudPartExcluded(HUDPartTutorialHints.name)) {
|
||||
this.parts.tutorialHints = new HUDPartTutorialHints(this.root);
|
||||
}
|
||||
|
||||
if (!this.root.gameMode.isHudPartExcluded(HUDInteractiveTutorial.name)) {
|
||||
this.parts.interactiveTutorial = new HUDInteractiveTutorial(this.root);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.root.app.settings.getAllSettings().vignette) {
|
||||
this.parts.vignetteOverlay = new HUDVignetteOverlay(this.root);
|
||||
@ -177,7 +182,7 @@ export class GameHUD {
|
||||
for (let key in parts) {
|
||||
const Part = parts[key];
|
||||
|
||||
if (!Part || this.root.gameMode.isHudPartExcluded(Part)) {
|
||||
if (!Part || this.root.gameMode.isHudPartExcluded(Part.name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -23,8 +23,8 @@ export class HUDBaseToolbar extends BaseHUDPart {
|
||||
) {
|
||||
super(root);
|
||||
|
||||
this.primaryBuildings = primaryBuildings;
|
||||
this.secondaryBuildings = secondaryBuildings;
|
||||
this.primaryBuildings = this.filterBuildings(primaryBuildings);
|
||||
this.secondaryBuildings = this.filterBuildings(secondaryBuildings);
|
||||
this.visibilityCondition = visibilityCondition;
|
||||
this.htmlElementId = htmlElementId;
|
||||
this.layer = layer;
|
||||
@ -47,6 +47,24 @@ export class HUDBaseToolbar extends BaseHUDPart {
|
||||
this.element = makeDiv(parent, this.htmlElementId, ["ingame_buildingsToolbar"], "");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array<typeof MetaBuilding>} buildings
|
||||
* @returns {Array<typeof MetaBuilding>}
|
||||
*/
|
||||
filterBuildings(buildings) {
|
||||
const filtered = [];
|
||||
|
||||
for (let i = 0; i < buildings.length; i++) {
|
||||
if (this.root.gameMode.isBuildingExcluded(buildings[i].name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
filtered.push(buildings[i]);
|
||||
}
|
||||
|
||||
return filtered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all buildings
|
||||
* @returns {Array<typeof MetaBuilding>}
|
||||
|
@ -15,12 +15,13 @@ import { MetaUndergroundBeltBuilding } from "../../buildings/underground_belt";
|
||||
import { HUDBaseToolbar } from "./base_toolbar";
|
||||
import { MetaStorageBuilding } from "../../buildings/storage";
|
||||
import { MetaItemProducerBuilding } from "../../buildings/item_producer";
|
||||
import { queryParamOptions } from "../../../core/query_parameters";
|
||||
import { MetaConstantProducerBuilding } from "../../buildings/constant_producer";
|
||||
|
||||
export class HUDBuildingsToolbar extends HUDBaseToolbar {
|
||||
constructor(root) {
|
||||
super(root, {
|
||||
primaryBuildings: [
|
||||
MetaConstantProducerBuilding,
|
||||
MetaBeltBuilding,
|
||||
MetaBalancerBuilding,
|
||||
MetaUndergroundBeltBuilding,
|
||||
@ -31,7 +32,7 @@ export class HUDBuildingsToolbar extends HUDBaseToolbar {
|
||||
MetaMixerBuilding,
|
||||
MetaPainterBuilding,
|
||||
MetaTrashBuilding,
|
||||
...(queryParamOptions.sandboxMode || G_IS_DEV ? [MetaItemProducerBuilding] : []),
|
||||
MetaItemProducerBuilding,
|
||||
],
|
||||
secondaryBuildings: [
|
||||
MetaStorageBuilding,
|
||||
|
@ -153,10 +153,6 @@ export class HUDPinnedShapes extends BaseHUDPart {
|
||||
* Rerenders the whole component
|
||||
*/
|
||||
rerenderFull() {
|
||||
if (this.root.gameMode.isHudPartHidden(this.constructor.name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentGoal = this.root.hubGoals.currentGoal;
|
||||
const currentKey = currentGoal.definition.getHash();
|
||||
|
||||
|
@ -102,14 +102,12 @@ export class HUDWaypoints extends BaseHUDPart {
|
||||
|
||||
/** @type {Array<Waypoint>} */
|
||||
this.waypoints = [];
|
||||
if (this.root.gameMode.hasHub()) {
|
||||
this.waypoints.push({
|
||||
label: null,
|
||||
center: { x: 0, y: 0 },
|
||||
zoomLevel: 3,
|
||||
layer: gMetaBuildingRegistry.findByClass(MetaHubBuilding).getLayer(),
|
||||
});
|
||||
}
|
||||
|
||||
// Create a buffer we can use to measure text
|
||||
this.dummyBuffer = makeOffscreenBuffer(1, 1, {
|
||||
|
@ -49,6 +49,9 @@ export const KEYMAPPINGS = {
|
||||
},
|
||||
|
||||
buildings: {
|
||||
// Puzzle
|
||||
constant_producer: { keyCode: 192 }, // "`"
|
||||
|
||||
// Primary Toolbar
|
||||
belt: { keyCode: key("1") },
|
||||
balancer: { keyCode: key("2") },
|
||||
@ -262,6 +265,8 @@ export function getStringForKeyCode(code) {
|
||||
return ".";
|
||||
case 191:
|
||||
return "/";
|
||||
case 192:
|
||||
return "`";
|
||||
case 219:
|
||||
return "[";
|
||||
case 220:
|
||||
|
@ -42,7 +42,7 @@ export class MapChunkView extends MapChunk {
|
||||
drawBackgroundLayer(parameters) {
|
||||
const systems = this.root.systemMgr.systems;
|
||||
if (this.root.gameMode.hasZone()) {
|
||||
systems.mapZone.drawChunk(parameters, this);
|
||||
systems.zone.drawChunk(parameters, this);
|
||||
}
|
||||
|
||||
if (this.root.gameMode.hasResources()) {
|
||||
|
@ -5,6 +5,7 @@ import { MetaAnalyzerBuilding } from "./buildings/analyzer";
|
||||
import { enumBalancerVariants, MetaBalancerBuilding } from "./buildings/balancer";
|
||||
import { MetaBeltBuilding } from "./buildings/belt";
|
||||
import { MetaComparatorBuilding } from "./buildings/comparator";
|
||||
import { MetaConstantProducerBuilding } from "./buildings/constant_producer";
|
||||
import { MetaConstantSignalBuilding } from "./buildings/constant_signal";
|
||||
import { enumCutterVariants, MetaCutterBuilding } from "./buildings/cutter";
|
||||
import { MetaDisplayBuilding } from "./buildings/display";
|
||||
@ -59,6 +60,7 @@ export function initMetaBuildingRegistry() {
|
||||
gMetaBuildingRegistry.register(MetaAnalyzerBuilding);
|
||||
gMetaBuildingRegistry.register(MetaComparatorBuilding);
|
||||
gMetaBuildingRegistry.register(MetaItemProducerBuilding);
|
||||
gMetaBuildingRegistry.register(MetaConstantProducerBuilding);
|
||||
|
||||
// Belt
|
||||
registerBuildingVariant(1, MetaBeltBuilding, defaultBuildingVariant, 0);
|
||||
@ -165,6 +167,9 @@ export function initMetaBuildingRegistry() {
|
||||
// Item producer
|
||||
registerBuildingVariant(61, MetaItemProducerBuilding);
|
||||
|
||||
// Constant producer
|
||||
registerBuildingVariant(62, MetaConstantProducerBuilding);
|
||||
|
||||
// Propagate instances
|
||||
for (const key in gBuildingVariants) {
|
||||
gBuildingVariants[key].metaInstance = gMetaBuildingRegistry.findByClass(
|
||||
|
@ -2,19 +2,22 @@
|
||||
import { GameRoot } from "../root";
|
||||
/* typehints:end */
|
||||
|
||||
import { globalConfig } from "../../core/config";
|
||||
import { Rectangle } from "../../core/rectangle";
|
||||
import { types } from "../../savegame/serialization";
|
||||
import { HUDPinnedShapes } from "../hud/parts/pinned_shapes";
|
||||
import { enumGameModeTypes, GameMode } from "../game_mode";
|
||||
import { HUDInteractiveTutorial } from "../hud/parts/interactive_tutorial";
|
||||
import { HUDPinnedShapes } from "../hud/parts/pinned_shapes";
|
||||
import { HUDPartTutorialHints } from "../hud/parts/tutorial_hints";
|
||||
import { HUDWaypoints } from "../hud/parts/waypoints";
|
||||
|
||||
export class PuzzleGameMode extends GameMode {
|
||||
static getType() {
|
||||
return enumGameModeTypes.puzzle;
|
||||
}
|
||||
|
||||
/** @returns {object} */
|
||||
static getSchema() {
|
||||
return {
|
||||
hiddenHudParts: types.keyValueMap(types.bool),
|
||||
zoneHeight: types.uint,
|
||||
zoneWidth: types.uint,
|
||||
};
|
||||
@ -23,18 +26,24 @@ export class PuzzleGameMode extends GameMode {
|
||||
/** @param {GameRoot} root */
|
||||
constructor(root) {
|
||||
super(root);
|
||||
}
|
||||
|
||||
initialize() {
|
||||
const data = this.getSaveData();
|
||||
|
||||
this.type = this.getType();
|
||||
this.hiddenHudParts = data.hiddenHudParts || this.getDefaultHiddenHudParts();
|
||||
// this.excludedHudParts = data.hiddenHudParts || this.getDefaultHiddenHudParts();
|
||||
this.zoneHeight = data.zoneHeight || 3 * globalConfig.tileSize;
|
||||
this.zoneWidth = data.zoneWidth || 4 * globalConfig.tileSize;
|
||||
this.boundaryHeight = this.zoneHeight * 2;
|
||||
this.boundaryWidth = this.zoneWidth * 2;
|
||||
this.setHudParts({
|
||||
[HUDInteractiveTutorial.name]: false,
|
||||
[HUDPartTutorialHints.name]: false,
|
||||
[HUDPinnedShapes.name]: false,
|
||||
[HUDWaypoints.name]: false,
|
||||
});
|
||||
|
||||
this.setDimensions(data.zoneWidth, data.zoneHeight);
|
||||
}
|
||||
|
||||
setDimensions(w = 16, h = 9) {
|
||||
this.zoneWidth = w < 2 ? 2 : w;
|
||||
this.zoneHeight = h < 2 ? 2 : h;
|
||||
this.boundsHeight = this.zoneHeight < 8 ? 8 : this.zoneHeight;
|
||||
this.boundsWidth = this.zoneWidth < 8 ? 8 : this.zoneWidth;
|
||||
}
|
||||
|
||||
getSaveData() {
|
||||
@ -47,24 +56,52 @@ export class PuzzleGameMode extends GameMode {
|
||||
return save.gameMode.data;
|
||||
}
|
||||
|
||||
getDefaultHiddenHudParts() {
|
||||
return {
|
||||
[HUDPinnedShapes.name]: true,
|
||||
};
|
||||
createCenteredRectangle(width, height) {
|
||||
return new Rectangle(-Math.ceil(width / 2), -Math.ceil(height / 2), width, height);
|
||||
}
|
||||
|
||||
isHudPartHidden(name) {
|
||||
return this.hiddenHudParts[name];
|
||||
getBounds() {
|
||||
if (this.bounds) {
|
||||
return this.bounds;
|
||||
}
|
||||
|
||||
this.bounds = this.createCenteredRectangle(this.boundsWidth, this.boundsHeight);
|
||||
|
||||
return this.bounds;
|
||||
}
|
||||
|
||||
getZone() {
|
||||
if (this.zone) {
|
||||
return this.zone;
|
||||
}
|
||||
|
||||
this.zone = this.createCenteredRectangle(this.zoneWidth, this.zoneHeight);
|
||||
|
||||
return this.zone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides GameMode's implementation to treat buildings like a whitelist
|
||||
* instead of a blacklist by default.
|
||||
* @param {string} name - Class name of building
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isBuildingExcluded(name) {
|
||||
return this.buildings[name] !== true;
|
||||
}
|
||||
|
||||
isInBounds(x, y) {
|
||||
return this.bounds.containsPoint(x, y);
|
||||
}
|
||||
|
||||
isInZone(x, y) {
|
||||
return this.zone.containsPoint(x, y);
|
||||
}
|
||||
|
||||
hasZone() {
|
||||
return true;
|
||||
}
|
||||
|
||||
hasHints() {
|
||||
return false;
|
||||
}
|
||||
|
||||
hasHub() {
|
||||
return false;
|
||||
}
|
||||
@ -73,27 +110,11 @@ export class PuzzleGameMode extends GameMode {
|
||||
return false;
|
||||
}
|
||||
|
||||
hasBoundaries() {
|
||||
hasBounds() {
|
||||
return true;
|
||||
}
|
||||
|
||||
getMinimumZoom() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
getBoundaryWidth() {
|
||||
return this.boundaryWidth;
|
||||
}
|
||||
|
||||
getBoundaryHeight() {
|
||||
return this.boundaryHeight;
|
||||
}
|
||||
|
||||
getZoneWidth() {
|
||||
return this.zoneWidth;
|
||||
}
|
||||
|
||||
getZoneHeight() {
|
||||
return this.zoneHeight;
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,9 @@
|
||||
import { GameRoot } from "../root";
|
||||
/* typehints:end */
|
||||
|
||||
import { PuzzleGameMode } from "./puzzle";
|
||||
import { MetaConstantProducerBuilding } from "../buildings/constant_producer";
|
||||
import { enumGameModeIds } from "../game_mode";
|
||||
import { PuzzleGameMode } from "./puzzle";
|
||||
|
||||
export class PuzzleEditGameMode extends PuzzleGameMode {
|
||||
static getId() {
|
||||
@ -13,5 +14,9 @@ export class PuzzleEditGameMode extends PuzzleGameMode {
|
||||
/** @param {GameRoot} root */
|
||||
constructor(root) {
|
||||
super(root);
|
||||
|
||||
this.setBuildings({
|
||||
[MetaConstantProducerBuilding.name]: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,5 @@ export class PuzzlePlayGameMode extends PuzzleGameMode {
|
||||
/** @param {GameRoot} root */
|
||||
constructor(root) {
|
||||
super(root);
|
||||
this.initialize();
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,13 @@
|
||||
import { GameRoot } from "../root";
|
||||
/* typehints:end */
|
||||
|
||||
import { queryParamOptions } from "../../core/query_parameters";
|
||||
import { findNiceIntegerValue } from "../../core/utils";
|
||||
import { MetaConstantProducerBuilding } from "../buildings/constant_producer";
|
||||
import { MetaItemProducerBuilding } from "../buildings/item_producer";
|
||||
import { enumGameModeIds, enumGameModeTypes, GameMode } from "../game_mode";
|
||||
import { ShapeDefinition } from "../shape_definition";
|
||||
import { enumHubGoalRewards } from "../tutorial_goals";
|
||||
import { types } from "../../savegame/serialization";
|
||||
|
||||
/** @typedef {{
|
||||
* shape: string,
|
||||
@ -489,6 +491,11 @@ export class RegularGameMode extends GameMode {
|
||||
/** @param {GameRoot} root */
|
||||
constructor(root) {
|
||||
super(root);
|
||||
|
||||
this.setBuildings({
|
||||
[MetaConstantProducerBuilding.name]: false,
|
||||
[MetaItemProducerBuilding.name]: queryParamOptions.sandboxMode || G_IS_DEV,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,7 +1,12 @@
|
||||
import { ItemProducerComponent } from "../components/item_producer";
|
||||
/* typehints:start */
|
||||
import { GameRoot } from "../root";
|
||||
/* typehints:end */
|
||||
|
||||
import { enumItemProducerType, ItemProducerComponent } from "../components/item_producer";
|
||||
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||
|
||||
export class ItemProducerSystem extends GameSystemWithFilter {
|
||||
/** @param {GameRoot} root */
|
||||
constructor(root) {
|
||||
super(root, [ItemProducerComponent]);
|
||||
}
|
||||
@ -9,6 +14,8 @@ export class ItemProducerSystem extends GameSystemWithFilter {
|
||||
update() {
|
||||
for (let i = 0; i < this.allEntities.length; ++i) {
|
||||
const entity = this.allEntities[i];
|
||||
|
||||
if (entity.components.ItemProducer.type === enumItemProducerType.wired) {
|
||||
const pinsComp = entity.components.WiredPins;
|
||||
const pin = pinsComp.slots[0];
|
||||
const network = pin.linkedNetwork;
|
||||
@ -19,6 +26,9 @@ export class ItemProducerSystem extends GameSystemWithFilter {
|
||||
|
||||
const ejectorComp = entity.components.ItemEjector;
|
||||
ejectorComp.tryEject(0, network.currentValue);
|
||||
} else {
|
||||
// TODO: entity w/ wireless item producer (e.g. ConstantProducer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,60 +0,0 @@
|
||||
/* typehints:start */
|
||||
import { DrawParameters } from "../../core/draw_parameters";
|
||||
import { MapChunkView } from "../map_chunk_view";
|
||||
/* typehints:end */
|
||||
|
||||
import { globalConfig } from "../../core/config";
|
||||
import { drawSpriteClipped } from "../../core/draw_utils";
|
||||
import { GameSystem } from "../game_system";
|
||||
import { THEME } from "../theme";
|
||||
|
||||
export class MapZoneSystem extends GameSystem {
|
||||
/**
|
||||
* Draws the map resources
|
||||
* @param {DrawParameters} parameters
|
||||
* @param {MapChunkView} chunk
|
||||
*/
|
||||
drawChunk(parameters, chunk) {
|
||||
const width = this.root.gameMode.getZoneWidth();
|
||||
const height = this.root.gameMode.getZoneHeight();
|
||||
|
||||
const zoneChunkBackground = this.root.buffers.getForKey({
|
||||
key: "mapzonebg",
|
||||
subKey: chunk.renderKey,
|
||||
w: width,
|
||||
h: height,
|
||||
dpi: 1,
|
||||
redrawMethod: this.generateChunkBackground.bind(this, chunk),
|
||||
});
|
||||
|
||||
parameters.context.imageSmoothingEnabled = false;
|
||||
drawSpriteClipped({
|
||||
parameters,
|
||||
sprite: zoneChunkBackground,
|
||||
x: -width,
|
||||
y: -height,
|
||||
w: this.root.gameMode.getBoundaryWidth(),
|
||||
h: this.root.gameMode.getBoundaryHeight(),
|
||||
originalW: width,
|
||||
originalH: height,
|
||||
});
|
||||
parameters.context.imageSmoothingEnabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MapChunkView} chunk
|
||||
* @param {HTMLCanvasElement} canvas
|
||||
* @param {CanvasRenderingContext2D} context
|
||||
* @param {number} w
|
||||
* @param {number} h
|
||||
* @param {number} dpi
|
||||
*/
|
||||
generateChunkBackground(chunk, canvas, context, w, h, dpi) {
|
||||
context.clearRect(0, 0, w, h);
|
||||
|
||||
context.fillStyle = THEME.map.zone.background;
|
||||
context.strokeStyle = THEME.map.zone.border;
|
||||
context.fillRect(0, 0, w, h);
|
||||
context.strokeRect(0, 0, w, h);
|
||||
}
|
||||
}
|
53
src/js/game/systems/zone.js
Normal file
53
src/js/game/systems/zone.js
Normal file
@ -0,0 +1,53 @@
|
||||
/* typehints:start */
|
||||
import { DrawParameters } from "../../core/draw_parameters";
|
||||
import { MapChunkView } from "../map_chunk_view";
|
||||
import { GameRoot } from "../root";
|
||||
/* typehints:end */
|
||||
|
||||
import { globalConfig } from "../../core/config";
|
||||
import { STOP_PROPAGATION } from "../../core/signal";
|
||||
import { GameSystem } from "../game_system";
|
||||
import { THEME } from "../theme";
|
||||
|
||||
export class ZoneSystem extends GameSystem {
|
||||
/** @param {GameRoot} root */
|
||||
constructor(root) {
|
||||
super(root);
|
||||
|
||||
this.root.signals.prePlacementCheck.add(this.prePlacementCheck, this);
|
||||
}
|
||||
|
||||
prePlacementCheck(entity, tile = null) {
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
|
||||
if (!staticComp) {
|
||||
return;
|
||||
}
|
||||
|
||||
const zone = this.root.gameMode.getZone().expandedInAllDirections(-1);
|
||||
const transformed = staticComp.getTileSpaceBounds();
|
||||
|
||||
if (zone.containsRect(transformed)) {
|
||||
return STOP_PROPAGATION;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the zone
|
||||
* @param {DrawParameters} parameters
|
||||
* @param {MapChunkView} chunk
|
||||
*/
|
||||
drawChunk(parameters, chunk) {
|
||||
const zone = this.root.gameMode.getZone().allScaled(globalConfig.tileSize);
|
||||
|
||||
parameters.context.globalAlpha = 0.1;
|
||||
parameters.context.fillStyle = THEME.map.zone.background;
|
||||
parameters.context.fillRect(zone.x, zone.y, zone.w, zone.h);
|
||||
|
||||
parameters.context.globalAlpha = 0.9;
|
||||
parameters.context.strokeStyle = THEME.map.zone.border;
|
||||
parameters.context.strokeRect(zone.x, zone.y, zone.w, zone.h);
|
||||
|
||||
parameters.context.globalAlpha = 1;
|
||||
}
|
||||
}
|
@ -2,6 +2,8 @@ import { ExplainedResult } from "../core/explained_result";
|
||||
import { createLogger } from "../core/logging";
|
||||
import { gComponentRegistry } from "../core/global_registries";
|
||||
import { SerializerInternal } from "./serializer_internal";
|
||||
import { HUDPinnedShapes } from "../game/hud/parts/pinned_shapes";
|
||||
import { HUDWaypoints } from "../game/hud/parts/waypoints";
|
||||
|
||||
/**
|
||||
* @typedef {import("../game/component").Component} Component
|
||||
@ -36,10 +38,14 @@ export class SavegameSerializer {
|
||||
gameMode: root.gameMode.serialize(),
|
||||
entityMgr: root.entityMgr.serialize(),
|
||||
hubGoals: root.hubGoals.serialize(),
|
||||
pinnedShapes: root.hud.parts.pinnedShapes.serialize(),
|
||||
waypoints: root.hud.parts.waypoints.serialize(),
|
||||
entities: this.internal.serializeEntityArray(root.entityMgr.entities),
|
||||
beltPaths: root.systemMgr.systems.belt.serializePaths(),
|
||||
pinnedShapes: root.gameMode.isHudPartExcluded(HUDPinnedShapes.name)
|
||||
? null
|
||||
: root.hud.parts.pinnedShapes.serialize(),
|
||||
waypoints: root.gameMode.isHudPartExcluded(HUDWaypoints.name)
|
||||
? null
|
||||
: root.hud.parts.waypoints.serialize(),
|
||||
};
|
||||
|
||||
if (G_IS_DEV) {
|
||||
@ -133,11 +139,17 @@ export class SavegameSerializer {
|
||||
errorReason = errorReason || root.map.deserialize(savegame.map);
|
||||
errorReason = errorReason || root.gameMode.deserialize(savegame.gameMode);
|
||||
errorReason = errorReason || root.hubGoals.deserialize(savegame.hubGoals, root);
|
||||
errorReason = errorReason || root.hud.parts.pinnedShapes.deserialize(savegame.pinnedShapes);
|
||||
errorReason = errorReason || root.hud.parts.waypoints.deserialize(savegame.waypoints);
|
||||
errorReason = errorReason || this.internal.deserializeEntityArray(root, savegame.entities);
|
||||
errorReason = errorReason || root.systemMgr.systems.belt.deserializePaths(savegame.beltPaths);
|
||||
|
||||
if (!root.gameMode.isHudPartExcluded(HUDPinnedShapes.name)) {
|
||||
errorReason = errorReason || root.hud.parts.pinnedShapes.deserialize(savegame.pinnedShapes);
|
||||
}
|
||||
|
||||
if (!root.gameMode.isHudPartExcluded(HUDWaypoints.name)) {
|
||||
errorReason = errorReason || root.hud.parts.waypoints.deserialize(savegame.waypoints);
|
||||
}
|
||||
|
||||
// Check for errors
|
||||
if (errorReason) {
|
||||
return ExplainedResult.bad(errorReason);
|
||||
|
@ -333,10 +333,13 @@ export class MainMenuState extends GameState {
|
||||
const buttonContainer = this.htmlElement.querySelector(".mainContainer .buttons");
|
||||
removeAllChildren(buttonContainer);
|
||||
|
||||
const playButtonElement = makeButtonElement(["playButton", "styledButton"], T.mainMenu.play);
|
||||
const playButtonElement = makeButtonElement(["playModeButton", "styledButton"], T.puzzleMenu.play);
|
||||
const editButtonElement = makeButtonElement(["editModeButton", "styledButton"], T.puzzleMenu.edit);
|
||||
|
||||
buttonContainer.appendChild(playButtonElement);
|
||||
this.trackClicks(playButtonElement, this.onPuzzlePlayButtonClicked);
|
||||
buttonContainer.appendChild(editButtonElement);
|
||||
this.trackClicks(editButtonElement, this.onPuzzleEditButtonClicked);
|
||||
|
||||
const bottomButtonContainer = this.htmlElement.querySelector(".bottomContainer .buttons");
|
||||
removeAllChildren(bottomButtonContainer);
|
||||
@ -356,6 +359,15 @@ export class MainMenuState extends GameState {
|
||||
});
|
||||
}
|
||||
|
||||
onPuzzleEditButtonClicked() {
|
||||
const savegame = this.app.savegameMgr.createNewSavegame();
|
||||
|
||||
this.moveToState("InGameState", {
|
||||
gameModeId: enumGameModeIds.puzzleEdit,
|
||||
savegame,
|
||||
});
|
||||
}
|
||||
|
||||
onPuzzleModeButtonClicked() {
|
||||
this.renderPuzzleModeMenu();
|
||||
}
|
||||
|
@ -119,6 +119,10 @@ mainMenu:
|
||||
puzzleMode: Puzzle Mode
|
||||
back: Back
|
||||
|
||||
puzzleMenu:
|
||||
play: Play
|
||||
edit: Edit
|
||||
|
||||
dialogs:
|
||||
buttons:
|
||||
ok: OK
|
||||
@ -703,6 +707,11 @@ buildings:
|
||||
name: Item Producer
|
||||
description: Available in sandbox mode only, outputs the given signal from the wires layer on the regular layer.
|
||||
|
||||
constant_producer:
|
||||
default:
|
||||
name: &constant_producer Constant Producer
|
||||
description: Outputs a shape, color or boolean (1 or 0) as specified.
|
||||
|
||||
storyRewards:
|
||||
# Those are the rewards gained from completing the store
|
||||
reward_cutter_and_trash:
|
||||
@ -1130,6 +1139,7 @@ keybindings:
|
||||
analyzer: *analyzer
|
||||
comparator: *comparator
|
||||
item_producer: Item Producer (Sandbox)
|
||||
constant_producer: *constant_producer
|
||||
# ---
|
||||
|
||||
pipette: Pipette
|
||||
|
Loading…
Reference in New Issue
Block a user