1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2024-10-27 20:34:29 +00:00

Multiple improvements

This commit is contained in:
tobspr 2020-05-17 10:07:20 +02:00
parent ce8640195a
commit 13c6fc7598
26 changed files with 360 additions and 208 deletions

View File

@ -1,5 +1,11 @@
/* eslint-disable */
const nodeVersion = process.versions.node.split(".")[0];
if (nodeVersion !== "10") {
console.error("This cli requires exactly Node 10. You are using node " + nodeVersion);
process.exit(1);
}
require("colors");
const gulp = require("gulp");

View File

@ -304,6 +304,24 @@
<key>scale9FromFile</key>
<false/>
</struct>
<key type="filename">sprites/blueprints/cutter-quad.png</key>
<key type="filename">sprites/blueprints/painter-quad.png</key>
<key type="filename">sprites/buildings/cutter-quad.png</key>
<key type="filename">sprites/buildings/painter-quad.png</key>
<struct type="IndividualSpriteSettings">
<key>pivotPoint</key>
<point_f>0.5,0.5</point_f>
<key>spriteScale</key>
<double>1</double>
<key>scale9Enabled</key>
<false/>
<key>scale9Borders</key>
<rect>192,48,384,96</rect>
<key>scale9Paddings</key>
<rect>192,48,384,96</rect>
<key>scale9FromFile</key>
<false/>
</struct>
<key type="filename">sprites/blueprints/cutter.png</key>
<key type="filename">sprites/blueprints/mixer.png</key>
<key type="filename">sprites/blueprints/painter.png</key>
@ -323,12 +341,22 @@
<key>scale9FromFile</key>
<false/>
</struct>
<key type="filename">sprites/blueprints/miner-chainable.png</key>
<key type="filename">sprites/blueprints/miner.png</key>
<key type="filename">sprites/blueprints/rotater-ccw.png</key>
<key type="filename">sprites/blueprints/rotater.png</key>
<key type="filename">sprites/blueprints/splitter-compact.png</key>
<key type="filename">sprites/blueprints/trash.png</key>
<key type="filename">sprites/blueprints/underground_belt_entry-tier2.png</key>
<key type="filename">sprites/blueprints/underground_belt_entry.png</key>
<key type="filename">sprites/blueprints/underground_belt_exit-tier2.png</key>
<key type="filename">sprites/blueprints/underground_belt_exit.png</key>
<key type="filename">sprites/buildings/miner-chainable.png</key>
<key type="filename">sprites/buildings/rotater-ccw.png</key>
<key type="filename">sprites/buildings/splitter-compact.png</key>
<key type="filename">sprites/buildings/underground_belt_entry-tier2.png</key>
<key type="filename">sprites/buildings/underground_belt_entry.png</key>
<key type="filename">sprites/buildings/underground_belt_exit-tier2.png</key>
<key type="filename">sprites/buildings/underground_belt_exit.png</key>
<struct type="IndividualSpriteSettings">
<key>pivotPoint</key>
@ -344,6 +372,22 @@
<key>scale9FromFile</key>
<false/>
</struct>
<key type="filename">sprites/blueprints/painter-double.png</key>
<key type="filename">sprites/buildings/painter-double.png</key>
<struct type="IndividualSpriteSettings">
<key>pivotPoint</key>
<point_f>0.5,0.5</point_f>
<key>spriteScale</key>
<double>1</double>
<key>scale9Enabled</key>
<false/>
<key>scale9Borders</key>
<rect>96,96,192,192</rect>
<key>scale9Paddings</key>
<rect>96,96,192,192</rect>
<key>scale9FromFile</key>
<false/>
</struct>
<key type="filename">sprites/buildings/cutter.png</key>
<key type="filename">sprites/buildings/mixer.png</key>
<key type="filename">sprites/buildings/painter.png</key>

View File

@ -3,20 +3,21 @@
@include S(top, 50px);
left: 50%;
transform: translateX(-50%);
background: rgba(#f77, 0.8);
@include S(border-radius, 4px);
@include S(padding, 9px);
@include PlainText;
background: rgba(lighten(#f77, 5), 0.95);
@include S(border-radius, 2px);
@include S(padding, 6px, 10px);
@include SuperSmallText;
color: #fff;
display: flex;
align-items: center;
// color: #f77;
.keybinding {
vertical-align: middle;
@include S(margin, 0, 4px);
position: relative;
top: unset;
left: unset;
right: unset;
bottom: unset;
@include S(margin-top, -2px);
}
}

View File

@ -54,8 +54,8 @@ ingame_HUD_PinnedShapes,
ingame_HUD_buildings_toolbar,
ingame_HUD_GameMenu,
ingame_HUD_KeybindingOverlay,
ingame_HUD_MassSelector,
ingame_HUD_Notifications,
ingame_HUD_MassSelector,
// Overlays
ingame_HUD_BetaOverlay,

View File

@ -38,7 +38,7 @@ export const globalConfig = {
// Belt speeds
// NOTICE: Update webpack.production.config too!
beltSpeedItemsPerSecond: 5,
beltSpeedItemsPerSecond: 1,
itemSpacingOnBelts: 0.63,
minerSpeedItemsPerSecond: 0, // COMPUTED

View File

@ -93,6 +93,7 @@ export class MetaBeltBaseBuilding extends MetaBuilding {
directions: [enumDirection.bottom],
},
],
animated: false,
})
);

View File

@ -44,6 +44,10 @@ export class MetaHubBuilding extends MetaBuilding {
processorType: enumItemProcessorTypes.hub,
})
);
// We render the sprite ourself
entity.components.StaticMapEntity.spriteKey = null;
entity.addComponent(new UnremovableComponent());
entity.addComponent(
new ItemAcceptorComponent({

View File

@ -112,7 +112,7 @@ export class MetaSplitterBuilding extends MetaBuilding {
{ pos: new Vector(1, 0), direction: enumDirection.top },
]);
entity.components.ItemProcessor.beltUnderlays = [
entity.components.ItemAcceptor.beltUnderlays = [
{ pos: new Vector(0, 0), direction: enumDirection.top },
{ pos: new Vector(1, 0), direction: enumDirection.top },
];
@ -135,7 +135,7 @@ export class MetaSplitterBuilding extends MetaBuilding {
{ pos: new Vector(0, 0), direction: enumDirection.top },
]);
entity.components.ItemProcessor.beltUnderlays = [
entity.components.ItemAcceptor.beltUnderlays = [
{ pos: new Vector(0, 0), direction: enumDirection.top },
];

View File

@ -29,6 +29,15 @@ const velocityFade = 0.98;
const velocityStrength = 0.4;
const velocityMax = 20;
/**
* @enum {string}
*/
export const enumMouseButton = {
left: "left",
middle: "middle",
right: "right",
};
export class Camera extends BasicSerializableObject {
constructor(root) {
super();
@ -81,10 +90,10 @@ export class Camera extends BasicSerializableObject {
this.touchPostMoveVelocity = new Vector(0, 0);
// Handlers
this.downPreHandler = new Signal(/* pos */);
this.movePreHandler = new Signal(/* pos */);
this.pinchPreHandler = new Signal(/* pos */);
this.upPostHandler = new Signal(/* pos */);
this.downPreHandler = /** @type {TypedSignal<[Vector, enumMouseButton]>} */ (new Signal());
this.movePreHandler = /** @type {TypedSignal<[Vector]>} */ (new Signal());
// this.pinchPreHandler = /** @type {TypedSignal<[Vector]>} */ (new Signal());
this.upPostHandler = /** @type {TypedSignal<[Vector]>} */ (new Signal());
this.internalInitEvents();
this.clampZoomLevel();
@ -411,6 +420,10 @@ export class Camera extends BasicSerializableObject {
this.touchPostMoveVelocity = new Vector(0, 0);
if (event.which === 1) {
this.combinedSingleTouchStartHandler(event.clientX, event.clientY);
} else if (event.which === 2) {
this.downPreHandler.dispatch(new Vector(event.clientX, event.clientY), enumMouseButton.middle);
} else if (event.which === 3) {
this.downPreHandler.dispatch(new Vector(event.clientX, event.clientY), enumMouseButton.right);
}
return false;
}
@ -496,10 +509,10 @@ export class Camera extends BasicSerializableObject {
const touch = event.touches[0];
this.combinedSingleTouchStartHandler(touch.clientX, touch.clientY);
} else if (event.touches.length === 2) {
if (this.pinchPreHandler.dispatch() === STOP_PROPAGATION) {
// Something prevented pinching
return false;
}
// if (this.pinchPreHandler.dispatch() === STOP_PROPAGATION) {
// // Something prevented pinching
// return false;
// }
const touch1 = event.touches[0];
const touch2 = event.touches[1];
@ -620,7 +633,7 @@ export class Camera extends BasicSerializableObject {
*/
combinedSingleTouchStartHandler(x, y) {
const pos = new Vector(x, y);
if (this.downPreHandler.dispatch(pos) === STOP_PROPAGATION) {
if (this.downPreHandler.dispatch(pos, enumMouseButton.left) === STOP_PROPAGATION) {
// Somebody else captured it
return;
}

View File

@ -45,7 +45,7 @@ export const enumColorsToHexCode = {
[enumColors.purple]: "#dd66ff",
// blue + green
[enumColors.cyan]: "#87fff5",
[enumColors.cyan]: "#00fcff",
// blue + green + red
[enumColors.white]: "#ffffff",

View File

@ -59,9 +59,8 @@ export class BeltComponent extends Component {
/**
* Returns if the belt can currently accept an item from the given direction
* @param {enumDirection} direction
*/
canAcceptNewItem(direction) {
canAcceptNewItem() {
const firstItem = this.sortedItems[0];
if (!firstItem) {
return true;
@ -73,9 +72,8 @@ export class BeltComponent extends Component {
/**
* Pushes a new item to the belt
* @param {BaseItem} item
* @param {enumDirection} direction
*/
takeNewItem(item, direction) {
takeNewItem(item) {
this.sortedItems.unshift([0, item]);
}

View File

@ -1,5 +1,5 @@
import { Component } from "../component";
import { Vector, enumDirection, enumDirectionToAngle, enumInvertedDirections } from "../../core/vector";
import { Vector, enumDirection, enumInvertedDirections } from "../../core/vector";
import { BaseItem } from "../base_item";
import { ShapeItem } from "../items/shape_item";
import { ColorItem } from "../items/color_item";
@ -34,6 +34,23 @@ export class ItemAcceptorComponent extends Component {
filter: types.nullable(types.enum(enumItemAcceptorItemFilter)),
})
),
animated: types.bool,
beltUnderlays: types.array(
types.structured({
pos: types.vector,
direction: types.enum(enumDirection),
})
),
// We don't actually need to store the animations
// itemConsumptionAnimations: types.array(
// types.structured({
// item: types.obj(gItemRegistry),
// slotIndex: types.uint,
// animProgress: types.float,
// direction: types.enum(enumDirection),
// })
// ),
};
}
@ -41,10 +58,23 @@ export class ItemAcceptorComponent extends Component {
*
* @param {object} param0
* @param {Array<{pos: Vector, directions: enumDirection[], filter?: enumItemAcceptorItemFilter}>} param0.slots The slots from which we accept items
* @param {boolean=} param0.animated Whether to animate item consumption
* @param {Array<{pos: Vector, direction: enumDirection}>=} param0.beltUnderlays Where to render belt underlays
*/
constructor({ slots = [] }) {
constructor({ slots = [], beltUnderlays = [], animated = true }) {
super();
this.animated = animated;
/**
* Fixes belt animations
* @type {Array<{ item: BaseItem, slotIndex: number, animProgress: number, direction: enumDirection}>}
*/
this.itemConsumptionAnimations = [];
/* Which belt underlays to render */
this.beltUnderlays = beltUnderlays;
this.setSlots(slots);
}
@ -86,6 +116,23 @@ export class ItemAcceptorComponent extends Component {
}
}
/**
* Called when an item has been accepted so that
* @param {number} slotIndex
* @param {enumDirection} direction
* @param {BaseItem} item
*/
onItemAccepted(slotIndex, direction, item) {
if (this.animated) {
this.itemConsumptionAnimations.push({
item,
slotIndex,
direction,
animProgress: 0.0,
});
}
}
/**
* Tries to find a slot which accepts the current item
* @param {Vector} targetLocalTile
@ -106,12 +153,6 @@ export class ItemAcceptorComponent extends Component {
for (let slotIndex = 0; slotIndex < this.slots.length; ++slotIndex) {
const slot = this.slots[slotIndex];
// const acceptorLocalPosition = targetStaticComp.applyRotationToVector(
// slot.pos
// );
// const acceptorGlobalPosition = acceptorLocalPosition.add(targetStaticComp.origin);
// Make sure the acceptor slot is on the right position
if (!slot.pos.equals(targetLocalTile)) {
continue;
@ -130,7 +171,6 @@ export class ItemAcceptorComponent extends Component {
}
}
// && this.canAcceptItem(slotIndex, ejectingItem)
return null;
}
}

View File

@ -30,12 +30,7 @@ export class ItemProcessorComponent extends Component {
nextOutputSlot: types.uint,
type: types.enum(enumItemProcessorTypes),
inputsPerCharge: types.uint,
beltUnderlays: types.array(
types.structured({
pos: types.vector,
direction: types.enum(enumDirection),
})
),
inputSlots: types.array(
types.structured({
item: types.obj(gItemRegistry),
@ -50,14 +45,6 @@ export class ItemProcessorComponent extends Component {
})
),
secondsUntilEject: types.float,
itemConsumptionAnimations: types.array(
types.structured({
item: types.obj(gItemRegistry),
slotIndex: types.uint,
animProgress: types.float,
direction: types.enum(enumDirection),
})
),
};
}
@ -66,14 +53,9 @@ export class ItemProcessorComponent extends Component {
* @param {object} param0
* @param {enumItemProcessorTypes=} param0.processorType Which type of processor this is
* @param {number=} param0.inputsPerCharge How many items this machine needs until it can start working
* @param {Array<{pos: Vector, direction: enumDirection}>=} param0.beltUnderlays Where to render belt underlays
*
*/
constructor({
processorType = enumItemProcessorTypes.splitter,
inputsPerCharge = 1,
beltUnderlays = [],
}) {
constructor({ processorType = enumItemProcessorTypes.splitter, inputsPerCharge = 1 }) {
super();
// Which slot to emit next, this is only a preference and if it can't emit
@ -87,9 +69,6 @@ export class ItemProcessorComponent extends Component {
// How many inputs we need for one charge
this.inputsPerCharge = inputsPerCharge;
// Which belt underlays to render
this.beltUnderlays = beltUnderlays;
/**
* Our current inputs
* @type {Array<{ item: BaseItem, sourceSlot: number }>}
@ -108,19 +87,14 @@ export class ItemProcessorComponent extends Component {
* How long it takes until we are done with the current items
*/
this.secondsUntilEject = 0;
/**
* Fixes belt animations
* @type {Array<{ item: BaseItem, slotIndex: number, animProgress: number, direction: enumDirection}>}
*/
this.itemConsumptionAnimations = [];
}
/**
* Tries to take the item
* @param {BaseItem} item
* @param {number} sourceSlot
*/
tryTakeItem(item, sourceSlot, sourceDirection) {
tryTakeItem(item, sourceSlot) {
// Check that we only take one item per slot
for (let i = 0; i < this.inputSlots.length; ++i) {
const slot = this.inputSlots[i];
@ -130,12 +104,6 @@ export class ItemProcessorComponent extends Component {
}
this.inputSlots.push({ item, sourceSlot });
this.itemConsumptionAnimations.push({
item,
slotIndex: sourceSlot,
direction: sourceDirection,
animProgress: 0.0,
});
return true;
}
}

View File

@ -34,6 +34,9 @@ export class UndergroundBeltComponent extends Component {
this.mode = mode;
this.tier = tier;
/** @type {Array<{ item: BaseItem, progress: number }>} */
this.consumptionAnimations = [];
/**
* Used on both receiver and sender.
* Reciever: Used to store the next item to transfer, and to block input while doing this

View File

@ -398,10 +398,10 @@ export class GameCore {
// systems.mapResources.draw(params);
if (!this.root.camera.getIsMapOverlayActive()) {
systems.itemProcessor.drawUnderlays(params);
systems.itemAcceptor.drawUnderlays(params);
systems.belt.draw(params);
systems.itemEjector.draw(params);
systems.itemProcessor.draw(params);
systems.itemAcceptor.draw(params);
}
root.map.drawForeground(params);

View File

@ -11,6 +11,7 @@ import { ItemProcessorSystem } from "./systems/item_processor";
import { UndergroundBeltSystem } from "./systems/underground_belt";
import { HubSystem } from "./systems/hub";
import { StaticMapEntitySystem } from "./systems/static_map_entity";
import { ItemAcceptorSystem } from "./systems/item_acceptor";
const logger = createLogger("game_system_manager");
@ -48,6 +49,9 @@ export class GameSystemManager {
/** @type {StaticMapEntitySystem} */
staticMapEntities: null,
/** @type {ItemAcceptorSystem} */
itemAcceptor: null,
/* typehints:end */
};
this.systemUpdateOrder = [];
@ -82,6 +86,8 @@ export class GameSystemManager {
add("staticMapEntities", StaticMapEntitySystem);
add("itemAcceptor", ItemAcceptorSystem);
logger.log("📦 There are", this.systemUpdateOrder.length, "game systems");
}

View File

@ -17,6 +17,7 @@ import { Math_abs, Math_radians, Math_degrees } from "../../../core/builtins";
import { Loader } from "../../../core/loader";
import { drawRotatedSprite } from "../../../core/draw_utils";
import { Entity } from "../../entity";
import { enumMouseButton } from "../../camera";
export class HUDBuildingPlacer extends BaseHUDPart {
initialize() {
@ -45,6 +46,11 @@ export class HUDBuildingPlacer extends BaseHUDPart {
this.variantsAttach = new DynamicDomAttach(this.root, this.variantsElement, {});
/**
* Whether we are currently drag-deleting
*/
this.currentlyDeleting = false;
/**
* Stores which variants for each building we prefer, this is based on what
* the user last selected
@ -87,14 +93,17 @@ export class HUDBuildingPlacer extends BaseHUDPart {
/**
* mouse down pre handler
* @param {Vector} pos
* @param {enumMouseButton} button
*/
onMouseDown(pos) {
onMouseDown(pos, button) {
if (this.root.camera.getIsMapOverlayActive()) {
return;
}
if (this.currentMetaBuilding.get()) {
// Placement
if (button === enumMouseButton.left && this.currentMetaBuilding.get()) {
this.currentlyDragging = true;
this.currentlyDeleting = false;
this.lastDragTile = this.root.camera.screenToWorld(pos).toTileSpace();
// Place initial building
@ -102,6 +111,15 @@ export class HUDBuildingPlacer extends BaseHUDPart {
return STOP_PROPAGATION;
}
// Deletion
if (button === enumMouseButton.right && !this.currentMetaBuilding.get()) {
this.currentlyDragging = true;
this.currentlyDeleting = true;
this.lastDragTile = this.root.camera.screenToWorld(pos).toTileSpace();
this.currentMetaBuilding.set(null);
return STOP_PROPAGATION;
}
}
/**
@ -114,7 +132,7 @@ export class HUDBuildingPlacer extends BaseHUDPart {
}
const metaBuilding = this.currentMetaBuilding.get();
if (metaBuilding && this.lastDragTile) {
if ((metaBuilding || this.currentlyDeleting) && this.lastDragTile) {
const oldPos = this.lastDragTile;
const newPos = this.root.camera.screenToWorld(pos).toTileSpace();
@ -126,6 +144,7 @@ export class HUDBuildingPlacer extends BaseHUDPart {
if (!oldPos.equals(newPos)) {
if (
metaBuilding &&
metaBuilding.getRotateAutomaticallyWhilePlacing(this.currentVariant.get()) &&
!this.root.app.inputMgr.ctrlIsDown
) {
@ -152,8 +171,15 @@ export class HUDBuildingPlacer extends BaseHUDPart {
var sy = y0 < y1 ? 1 : -1;
var err = dx - dy;
while (this.currentMetaBuilding.get()) {
this.tryPlaceCurrentBuildingAt(new Vector(x0, y0));
while (this.currentlyDeleting || this.currentMetaBuilding.get()) {
if (this.currentlyDeleting) {
const contents = this.root.map.getTileContentXY(x0, y0);
if (contents && !contents.queuedForDestroy && !contents.destroyed) {
this.root.logic.tryDeleteBuilding(contents);
}
} else {
this.tryPlaceCurrentBuildingAt(new Vector(x0, y0));
}
if (x0 === x1 && y0 === y1) break;
var e2 = 2 * err;
if (e2 > -dy) {
@ -186,6 +212,7 @@ export class HUDBuildingPlacer extends BaseHUDPart {
*/
abortDragging() {
this.currentlyDragging = true;
this.currentlyDeleting = false;
this.lastDragTile = null;
}
@ -201,6 +228,7 @@ export class HUDBuildingPlacer extends BaseHUDPart {
* @param {MetaBuilding} metaBuilding
*/
onSelectedMetaBuildingChanged(metaBuilding) {
this.abortDragging();
this.root.hud.signals.selectedPlacementBuildingChanged.dispatch(metaBuilding);
if (metaBuilding) {
this.buildingInfoElements.label.innerHTML = metaBuilding.getName();
@ -215,7 +243,8 @@ export class HUDBuildingPlacer extends BaseHUDPart {
this.currentVariant.set(variant);
this.fakeEntity = new Entity(null);
metaBuilding.setupEntityComponents(this.fakeEntity, null, variant);
metaBuilding.setupEntityComponents(this.fakeEntity, null);
this.fakeEntity.addComponent(
new StaticMapEntityComponent({
origin: new Vector(0, 0),
@ -223,13 +252,13 @@ export class HUDBuildingPlacer extends BaseHUDPart {
tileSize: metaBuilding.getDimensions(this.currentVariant.get()).copy(),
})
);
metaBuilding.updateVariants(this.fakeEntity, 0, this.currentVariant.get());
this.buildingInfoElements.tutorialImage.setAttribute(
"data-icon",
"building_tutorials/" + metaBuilding.getId() + ".png"
);
} else {
this.currentlyDragging = false;
this.fakeEntity = null;
}

View File

@ -33,7 +33,13 @@ export class HUDBuildingsToolbar extends BaseHUDPart {
constructor(root) {
super(root);
/** @type {Object.<string, { metaBuilding: MetaBuilding, unlocked: boolean, selected: boolean, element: HTMLElement}>} */
/** @type {Object.<string, {
* metaBuilding: MetaBuilding,
* unlocked: boolean,
* selected: boolean,
* element: HTMLElement,
* index: number
* }>} */
this.buildingHandles = {};
this.sigBuildingSelected = new Signal();
@ -76,6 +82,7 @@ export class HUDBuildingsToolbar extends BaseHUDPart {
element: itemContainer,
unlocked: false,
selected: false,
index: i,
};
}
@ -83,6 +90,9 @@ export class HUDBuildingsToolbar extends BaseHUDPart {
this.onSelectedPlacementBuildingChanged,
this
);
this.lastSelectedIndex = 0;
actionMapper.getBinding("cycle_buildings").add(this.cycleBuildings, this);
}
update() {
@ -98,6 +108,13 @@ export class HUDBuildingsToolbar extends BaseHUDPart {
}
}
cycleBuildings() {
const newIndex = (this.lastSelectedIndex + 1) % toolbarBuildings.length;
const metaBuildingClass = toolbarBuildings[newIndex];
const metaBuilding = gMetaBuildingRegistry.findByClass(metaBuildingClass);
this.selectBuildingForPlacement(metaBuilding);
}
/**
* @param {MetaBuilding} metaBuilding
*/
@ -109,6 +126,9 @@ export class HUDBuildingsToolbar extends BaseHUDPart {
handle.selected = newStatus;
handle.element.classList.toggle("selected", newStatus);
}
if (handle.selected) {
this.lastSelectedIndex = handle.index;
}
}
this.element.classList.toggle("buildingSelected", !!metaBuilding);

View File

@ -8,6 +8,7 @@ import { globalConfig } from "../../../core/config";
import { makeDiv } from "../../../core/utils";
import { DynamicDomAttach } from "../dynamic_dom_attach";
import { createLogger } from "../../../core/logging";
import { enumMouseButton } from "../../camera";
const logger = createLogger("hud/mass_selector");
@ -82,12 +83,17 @@ export class HUDMassSelector extends BaseHUDPart {
/**
* mouse down pre handler
* @param {Vector} pos
* @param {enumMouseButton} mouseButton
*/
onMouseDown(pos) {
onMouseDown(pos, mouseButton) {
if (!this.root.app.inputMgr.ctrlIsDown) {
return;
}
if (mouseButton !== enumMouseButton.left) {
return;
}
if (!this.root.app.inputMgr.shiftIsDown) {
// Start new selection
this.entityUidsMarkedForDeletion = new Set();

View File

@ -50,6 +50,8 @@ export const defaultKeybindings = {
rotate_while_placing: { keyCode: key("R") },
cycle_variants: { keyCode: key("T") },
cycle_buildings: { keyCode: 9 },
},
};

View File

@ -4,10 +4,13 @@ import { DrawParameters } from "../../core/draw_parameters";
import { Entity } from "../entity";
import { formatBigNumber } from "../../core/utils";
import { enumHubGoalRewardToString } from "../tutorial_goals";
import { Loader } from "../../core/loader";
export class HubSystem extends GameSystemWithFilter {
constructor(root) {
super(root, [HubComponent]);
this.hubSprite = Loader.getSprite("sprites/buildings/hub.png");
}
draw(parameters) {
@ -40,6 +43,9 @@ export class HubSystem extends GameSystemWithFilter {
const pos = staticComp.getTileSpaceBounds().getCenter().toWorldSpace();
// Background
staticComp.drawSpriteOnFullEntityBounds(parameters, this.hubSprite, 2.2);
const definition = this.root.hubGoals.currentGoal.definition;
definition.draw(pos.x - 25, pos.y - 10, parameters, 40);

View File

@ -0,0 +1,114 @@
import { GameSystemWithFilter } from "../game_system_with_filter";
import { globalConfig } from "../../core/config";
import { DrawParameters } from "../../core/draw_parameters";
import { Entity } from "../entity";
import { enumDirectionToVector, enumDirectionToAngle } from "../../core/vector";
import { ItemAcceptorComponent } from "../components/item_acceptor";
import { Loader } from "../../core/loader";
import { drawRotatedSprite } from "../../core/draw_utils";
import { Math_radians } from "../../core/builtins";
export class ItemAcceptorSystem extends GameSystemWithFilter {
constructor(root) {
super(root, [ItemAcceptorComponent]);
this.underlayBeltSprites = [
Loader.getSprite("sprites/belt/forward_0.png"),
Loader.getSprite("sprites/belt/forward_1.png"),
Loader.getSprite("sprites/belt/forward_2.png"),
Loader.getSprite("sprites/belt/forward_3.png"),
Loader.getSprite("sprites/belt/forward_4.png"),
Loader.getSprite("sprites/belt/forward_5.png"),
];
}
update() {
for (let i = 0; i < this.allEntities.length; ++i) {
const entity = this.allEntities[i];
const aceptorComp = entity.components.ItemAcceptor;
// Process item consumption animations to avoid items popping from the belts
for (let animIndex = 0; animIndex < aceptorComp.itemConsumptionAnimations.length; ++animIndex) {
const anim = aceptorComp.itemConsumptionAnimations[animIndex];
anim.animProgress +=
globalConfig.physicsDeltaSeconds * this.root.hubGoals.getBeltBaseSpeed() * 2;
if (anim.animProgress > 1) {
aceptorComp.itemConsumptionAnimations.splice(animIndex, 1);
animIndex -= 1;
}
}
}
}
draw(parameters) {
this.forEachMatchingEntityOnScreen(parameters, this.drawEntity.bind(this));
}
drawUnderlays(parameters) {
this.forEachMatchingEntityOnScreen(parameters, this.drawEntityUnderlays.bind(this));
}
/**
* @param {DrawParameters} parameters
* @param {Entity} entity
*/
drawEntity(parameters, entity) {
const staticComp = entity.components.StaticMapEntity;
const acceptorComp = entity.components.ItemAcceptor;
for (let animIndex = 0; animIndex < acceptorComp.itemConsumptionAnimations.length; ++animIndex) {
const { item, slotIndex, animProgress, direction } = acceptorComp.itemConsumptionAnimations[
animIndex
];
const slotData = acceptorComp.slots[slotIndex];
const slotWorldPos = staticComp.applyRotationToVector(slotData.pos).add(staticComp.origin);
const fadeOutDirection = enumDirectionToVector[staticComp.localDirectionToWorld(direction)];
const finalTile = slotWorldPos.subScalars(
fadeOutDirection.x * (animProgress / 2 - 0.5),
fadeOutDirection.y * (animProgress / 2 - 0.5)
);
item.draw(
(finalTile.x + 0.5) * globalConfig.tileSize,
(finalTile.y + 0.5) * globalConfig.tileSize,
parameters
);
}
}
/**
* @param {DrawParameters} parameters
* @param {Entity} entity
*/
drawEntityUnderlays(parameters, entity) {
const staticComp = entity.components.StaticMapEntity;
const acceptorComp = entity.components.ItemAcceptor;
const underlays = acceptorComp.beltUnderlays;
for (let i = 0; i < underlays.length; ++i) {
const { pos, direction } = underlays[i];
const transformedPos = staticComp.localTileToWorld(pos);
const angle = enumDirectionToAngle[staticComp.localDirectionToWorld(direction)];
// SYNC with systems/belt.js:drawSingleEntity!
const animationIndex = Math.floor(
(this.root.time.now() *
this.root.hubGoals.getBeltBaseSpeed() *
this.underlayBeltSprites.length *
126) /
42
);
drawRotatedSprite({
parameters,
sprite: this.underlayBeltSprites[animationIndex % this.underlayBeltSprites.length],
x: (transformedPos.x + 0.5) * globalConfig.tileSize,
y: (transformedPos.y + 0.5) * globalConfig.tileSize,
angle: Math_radians(angle),
size: globalConfig.tileSize,
});
}
}
}

View File

@ -72,14 +72,12 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
continue;
}
if (
this.tryPassOverItem(
ejectingItem,
targetEntity,
if (this.tryPassOverItem(ejectingItem, targetEntity, matchingSlot.index)) {
targetAcceptorComp.onItemAccepted(
matchingSlot.index,
matchingSlot.acceptedDirection
)
) {
matchingSlot.acceptedDirection,
ejectingItem
);
ejectorSlot.item = null;
continue;
}
@ -92,9 +90,8 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
* @param {BaseItem} item
* @param {Entity} receiver
* @param {number} slotIndex
* @param {string} localDirection
*/
tryPassOverItem(item, receiver, slotIndex, localDirection) {
tryPassOverItem(item, receiver, slotIndex) {
// Try figuring out how what to do with the item
// TODO: Kinda hacky. How to solve this properly? Don't want to go through inheritance hell.
// Also its just a few cases (hope it stays like this .. :x).
@ -102,8 +99,8 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
const beltComp = receiver.components.Belt;
if (beltComp) {
// Ayy, its a belt!
if (beltComp.canAcceptNewItem(localDirection)) {
beltComp.takeNewItem(item, localDirection);
if (beltComp.canAcceptNewItem()) {
beltComp.takeNewItem(item);
return true;
}
}
@ -111,7 +108,7 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
const itemProcessorComp = receiver.components.ItemProcessor;
if (itemProcessorComp) {
// Its an item processor ..
if (itemProcessorComp.tryTakeItem(item, slotIndex, localDirection)) {
if (itemProcessorComp.tryTakeItem(item, slotIndex)) {
return true;
}
}

View File

@ -1,37 +1,16 @@
import { Math_max } from "../../core/builtins";
import { globalConfig } from "../../core/config";
import { DrawParameters } from "../../core/draw_parameters";
import { Loader } from "../../core/loader";
import { BaseItem } from "../base_item";
import { enumColorMixingResults } from "../colors";
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
import { Entity } from "../entity";
import { GameSystemWithFilter } from "../game_system_with_filter";
import { ItemProcessorComponent, enumItemProcessorTypes } from "../components/item_processor";
import { Math_max, Math_radians } from "../../core/builtins";
import { BaseItem } from "../base_item";
import { ShapeItem } from "../items/shape_item";
import { enumDirectionToVector, enumDirection, enumDirectionToAngle } from "../../core/vector";
import { ColorItem } from "../items/color_item";
import { enumColorMixingResults } from "../colors";
import { drawRotatedSprite } from "../../core/draw_utils";
import { ShapeItem } from "../items/shape_item";
export class ItemProcessorSystem extends GameSystemWithFilter {
constructor(root) {
super(root, [ItemProcessorComponent]);
this.underlayBeltSprites = [
Loader.getSprite("sprites/belt/forward_0.png"),
Loader.getSprite("sprites/belt/forward_1.png"),
Loader.getSprite("sprites/belt/forward_2.png"),
Loader.getSprite("sprites/belt/forward_3.png"),
Loader.getSprite("sprites/belt/forward_4.png"),
Loader.getSprite("sprites/belt/forward_5.png"),
];
}
draw(parameters) {
this.forEachMatchingEntityOnScreen(parameters, this.drawEntity.bind(this));
}
drawUnderlays(parameters) {
this.forEachMatchingEntityOnScreen(parameters, this.drawEntityUnderlays.bind(this));
}
update() {
@ -47,17 +26,6 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
processorComp.secondsUntilEject - globalConfig.physicsDeltaSeconds
);
// Also, process item consumption animations to avoid items popping from the belts
for (let animIndex = 0; animIndex < processorComp.itemConsumptionAnimations.length; ++animIndex) {
const anim = processorComp.itemConsumptionAnimations[animIndex];
anim.animProgress +=
globalConfig.physicsDeltaSeconds * this.root.hubGoals.getBeltBaseSpeed() * 2;
if (anim.animProgress > 1) {
processorComp.itemConsumptionAnimations.splice(animIndex, 1);
animIndex -= 1;
}
}
// Check if we have any finished items we can eject
if (
processorComp.secondsUntilEject === 0 && // it was processed in time
@ -363,68 +331,4 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
processorComp.itemsToEject = outItems;
}
/**
* @param {DrawParameters} parameters
* @param {Entity} entity
*/
drawEntity(parameters, entity) {
const staticComp = entity.components.StaticMapEntity;
const processorComp = entity.components.ItemProcessor;
const acceptorComp = entity.components.ItemAcceptor;
for (let animIndex = 0; animIndex < processorComp.itemConsumptionAnimations.length; ++animIndex) {
const { item, slotIndex, animProgress, direction } = processorComp.itemConsumptionAnimations[
animIndex
];
const slotData = acceptorComp.slots[slotIndex];
const slotWorldPos = staticComp.applyRotationToVector(slotData.pos).add(staticComp.origin);
const fadeOutDirection = enumDirectionToVector[staticComp.localDirectionToWorld(direction)];
const finalTile = slotWorldPos.subScalars(
fadeOutDirection.x * (animProgress / 2 - 0.5),
fadeOutDirection.y * (animProgress / 2 - 0.5)
);
item.draw(
(finalTile.x + 0.5) * globalConfig.tileSize,
(finalTile.y + 0.5) * globalConfig.tileSize,
parameters
);
}
}
/**
* @param {DrawParameters} parameters
* @param {Entity} entity
*/
drawEntityUnderlays(parameters, entity) {
const staticComp = entity.components.StaticMapEntity;
const processorComp = entity.components.ItemProcessor;
const underlays = processorComp.beltUnderlays;
for (let i = 0; i < underlays.length; ++i) {
const { pos, direction } = underlays[i];
const transformedPos = staticComp.localTileToWorld(pos);
const angle = enumDirectionToAngle[staticComp.localDirectionToWorld(direction)];
// SYNC with systems/belt.js:drawSingleEntity!
const animationIndex = Math.floor(
(this.root.time.now() *
this.root.hubGoals.getBeltBaseSpeed() *
this.underlayBeltSprites.length *
126) /
42
);
drawRotatedSprite({
parameters,
sprite: this.underlayBeltSprites[animationIndex % this.underlayBeltSprites.length],
x: (transformedPos.x + 0.5) * globalConfig.tileSize,
y: (transformedPos.y + 0.5) * globalConfig.tileSize,
angle: Math_radians(angle),
size: globalConfig.tileSize,
});
}
}
}

View File

@ -26,7 +26,6 @@ export class StaticMapEntitySystem extends GameSystem {
return;
}
const drawEntitiesOutside = parameters.zoomLevel < globalConfig.mapChunkPrerenderMinZoom;
const drawOutlinesOnly = parameters.zoomLevel < globalConfig.mapChunkOverviewMinZoom;
const contents = chunk.contents;
@ -54,17 +53,8 @@ export class StaticMapEntitySystem extends GameSystem {
} else {
const spriteKey = staticComp.spriteKey;
if (spriteKey) {
// Check if origin is contained to avoid drawing entities multiple times
if (
drawEntitiesOutside ||
(staticComp.origin.x >= chunk.tileX &&
staticComp.origin.x < chunk.tileX + globalConfig.mapChunkSize &&
staticComp.origin.y >= chunk.tileY &&
staticComp.origin.y < chunk.tileY + globalConfig.mapChunkSize)
) {
const sprite = Loader.getSprite(spriteKey);
staticComp.drawSpriteOnFullEntityBounds(parameters, sprite, 2, false);
}
const sprite = Loader.getSprite(spriteKey);
staticComp.drawSpriteOnFullEntityBounds(parameters, sprite, 2, false);
}
}
}

6
src/js/globals.d.ts vendored
View File

@ -199,10 +199,10 @@ declare class TypedTrackedState<T> {
declare const STOP_PROPAGATION = "stop_propagation";
declare interface TypedSignal<T extends Array<any>> {
add(receiver: (...args: T) => typeof STOP_PROPAGATION | void, scope?: object);
remove(receiver: (...args: T) => typeof STOP_PROPAGATION | void);
add(receiver: (...args: T) => /* STOP_PROPAGATION */ string | void, scope?: object);
remove(receiver: (...args: T) => /* STOP_PROPAGATION */ string | void);
dispatch(...args: T): typeof STOP_PROPAGATION | void;
dispatch(...args: T): /* STOP_PROPAGATION */ string | void;
removeAll();
}