mirror of
https://github.com/tobspr/shapez.io.git
synced 2026-03-02 03:39:21 +00:00
Proper belt underlays for splitters
This commit is contained in:
@@ -1,33 +1,56 @@
|
||||
import { Component } from "../component";
|
||||
import { types } from "../../savegame/serialization";
|
||||
import { enumDirection, Vector } from "../../core/vector";
|
||||
|
||||
export class BeltUnderlaysComponent extends Component {
|
||||
static getId() {
|
||||
return "BeltUnderlays";
|
||||
}
|
||||
|
||||
duplicateWithoutContents() {
|
||||
const beltUnderlaysCopy = [];
|
||||
for (let i = 0; i < this.underlays.length; ++i) {
|
||||
const underlay = this.underlays[i];
|
||||
beltUnderlaysCopy.push({
|
||||
pos: underlay.pos.copy(),
|
||||
direction: underlay.direction,
|
||||
});
|
||||
}
|
||||
|
||||
return new BeltUnderlaysComponent({
|
||||
underlays: beltUnderlaysCopy,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} param0
|
||||
* @param {Array<{pos: Vector, direction: enumDirection}>=} param0.underlays Where to render belt underlays
|
||||
*/
|
||||
constructor({ underlays }) {
|
||||
super();
|
||||
this.underlays = underlays;
|
||||
}
|
||||
}
|
||||
import { enumDirection, Vector } from "../../core/vector";
|
||||
import { Component } from "../component";
|
||||
|
||||
/**
|
||||
* Store which type an underlay is, this is cached so we can easily
|
||||
* render it.
|
||||
*
|
||||
* Full: Render underlay at top and bottom of tile
|
||||
* Bottom Only: Only render underlay at the bottom half
|
||||
* Top Only:
|
||||
* @enum {string}
|
||||
*/
|
||||
export const enumClippedBeltUnderlayType = {
|
||||
full: "full",
|
||||
bottomOnly: "bottomOnly",
|
||||
topOnly: "topOnly",
|
||||
none: "none",
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* pos: Vector,
|
||||
* direction: enumDirection,
|
||||
* cachedType?: enumClippedBeltUnderlayType
|
||||
* }} BeltUnderlayTile
|
||||
*/
|
||||
|
||||
export class BeltUnderlaysComponent extends Component {
|
||||
static getId() {
|
||||
return "BeltUnderlays";
|
||||
}
|
||||
|
||||
duplicateWithoutContents() {
|
||||
const beltUnderlaysCopy = [];
|
||||
for (let i = 0; i < this.underlays.length; ++i) {
|
||||
const underlay = this.underlays[i];
|
||||
beltUnderlaysCopy.push({
|
||||
pos: underlay.pos.copy(),
|
||||
direction: underlay.direction,
|
||||
});
|
||||
}
|
||||
|
||||
return new BeltUnderlaysComponent({
|
||||
underlays: beltUnderlaysCopy,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} param0
|
||||
* @param {Array<BeltUnderlayTile>=} param0.underlays Where to render belt underlays
|
||||
*/
|
||||
constructor({ underlays = [] }) {
|
||||
super();
|
||||
this.underlays = underlays;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,161 +1,159 @@
|
||||
import { enumDirection, enumDirectionToVector, Vector } from "../../core/vector";
|
||||
import { types } from "../../savegame/serialization";
|
||||
import { BaseItem } from "../base_item";
|
||||
import { BeltPath } from "../belt_path";
|
||||
import { Component } from "../component";
|
||||
import { Entity } from "../entity";
|
||||
import { typeItemSingleton } from "../item_resolver";
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* pos: Vector,
|
||||
* direction: enumDirection,
|
||||
* item: BaseItem,
|
||||
* progress: number?,
|
||||
* cachedDestSlot?: import("./item_acceptor").ItemAcceptorLocatedSlot,
|
||||
* cachedBeltPath?: BeltPath,
|
||||
* cachedTargetEntity?: Entity
|
||||
* }} ItemEjectorSlot
|
||||
*/
|
||||
|
||||
export class ItemEjectorComponent extends Component {
|
||||
static getId() {
|
||||
return "ItemEjector";
|
||||
}
|
||||
|
||||
static getSchema() {
|
||||
// The cachedDestSlot, cachedTargetEntity fields are not serialized.
|
||||
return {
|
||||
slots: types.array(
|
||||
types.structured({
|
||||
item: types.nullable(typeItemSingleton),
|
||||
progress: types.float,
|
||||
})
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
duplicateWithoutContents() {
|
||||
const slotsCopy = [];
|
||||
for (let i = 0; i < this.slots.length; ++i) {
|
||||
const slot = this.slots[i];
|
||||
slotsCopy.push({
|
||||
pos: slot.pos.copy(),
|
||||
direction: slot.direction,
|
||||
});
|
||||
}
|
||||
|
||||
return new ItemEjectorComponent({
|
||||
slots: slotsCopy,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {object} param0
|
||||
* @param {Array<{pos: Vector, direction: enumDirection }>=} param0.slots The slots to eject on
|
||||
*/
|
||||
constructor({ slots = [] }) {
|
||||
super();
|
||||
|
||||
this.setSlots(slots);
|
||||
|
||||
/**
|
||||
* Whether this ejector slot is enabled
|
||||
*/
|
||||
this.enabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array<{pos: Vector, direction: enumDirection }>} slots The slots to eject on
|
||||
*/
|
||||
setSlots(slots) {
|
||||
/** @type {Array<ItemEjectorSlot>} */
|
||||
this.slots = [];
|
||||
for (let i = 0; i < slots.length; ++i) {
|
||||
const slot = slots[i];
|
||||
this.slots.push({
|
||||
pos: slot.pos,
|
||||
direction: slot.direction,
|
||||
item: null,
|
||||
progress: 0,
|
||||
cachedDestSlot: null,
|
||||
cachedTargetEntity: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns where this slot ejects to
|
||||
* @param {ItemEjectorSlot} slot
|
||||
* @returns {Vector}
|
||||
*/
|
||||
getSlotTargetLocalTile(slot) {
|
||||
const directionVector = enumDirectionToVector[slot.direction];
|
||||
return slot.pos.add(directionVector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether any slot ejects to the given local tile
|
||||
* @param {Vector} tile
|
||||
*/
|
||||
anySlotEjectsToLocalTile(tile) {
|
||||
for (let i = 0; i < this.slots.length; ++i) {
|
||||
if (this.getSlotTargetLocalTile(this.slots[i]).equals(tile)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if we can eject on a given slot
|
||||
* @param {number} slotIndex
|
||||
* @returns {boolean}
|
||||
*/
|
||||
canEjectOnSlot(slotIndex) {
|
||||
assert(slotIndex >= 0 && slotIndex < this.slots.length, "Invalid ejector slot: " + slotIndex);
|
||||
return !this.slots[slotIndex].item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first free slot on this ejector or null if there is none
|
||||
* @returns {number?}
|
||||
*/
|
||||
getFirstFreeSlot() {
|
||||
for (let i = 0; i < this.slots.length; ++i) {
|
||||
if (this.canEjectOnSlot(i)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to eject a given item
|
||||
* @param {number} slotIndex
|
||||
* @param {BaseItem} item
|
||||
* @returns {boolean}
|
||||
*/
|
||||
tryEject(slotIndex, item) {
|
||||
if (!this.canEjectOnSlot(slotIndex)) {
|
||||
return false;
|
||||
}
|
||||
this.slots[slotIndex].item = item;
|
||||
this.slots[slotIndex].progress = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the given slot and returns the item it had
|
||||
* @param {number} slotIndex
|
||||
* @returns {BaseItem|null}
|
||||
*/
|
||||
takeSlotItem(slotIndex) {
|
||||
const slot = this.slots[slotIndex];
|
||||
const item = slot.item;
|
||||
slot.item = null;
|
||||
slot.progress = 0.0;
|
||||
return item;
|
||||
}
|
||||
}
|
||||
import { enumDirection, enumDirectionToVector, Vector } from "../../core/vector";
|
||||
import { types } from "../../savegame/serialization";
|
||||
import { BaseItem } from "../base_item";
|
||||
import { BeltPath } from "../belt_path";
|
||||
import { Component } from "../component";
|
||||
import { Entity } from "../entity";
|
||||
import { typeItemSingleton } from "../item_resolver";
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* pos: Vector,
|
||||
* direction: enumDirection,
|
||||
* item: BaseItem,
|
||||
* progress: number?,
|
||||
* cachedDestSlot?: import("./item_acceptor").ItemAcceptorLocatedSlot,
|
||||
* cachedBeltPath?: BeltPath,
|
||||
* cachedTargetEntity?: Entity
|
||||
* }} ItemEjectorSlot
|
||||
*/
|
||||
|
||||
export class ItemEjectorComponent extends Component {
|
||||
static getId() {
|
||||
return "ItemEjector";
|
||||
}
|
||||
|
||||
static getSchema() {
|
||||
// The cachedDestSlot, cachedTargetEntity fields are not serialized.
|
||||
return {
|
||||
slots: types.array(
|
||||
types.structured({
|
||||
item: types.nullable(typeItemSingleton),
|
||||
progress: types.float,
|
||||
})
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
duplicateWithoutContents() {
|
||||
const slotsCopy = [];
|
||||
for (let i = 0; i < this.slots.length; ++i) {
|
||||
const slot = this.slots[i];
|
||||
slotsCopy.push({
|
||||
pos: slot.pos.copy(),
|
||||
direction: slot.direction,
|
||||
});
|
||||
}
|
||||
|
||||
return new ItemEjectorComponent({
|
||||
slots: slotsCopy,
|
||||
renderFloatingItems: this.renderFloatingItems,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {object} param0
|
||||
* @param {Array<{pos: Vector, direction: enumDirection }>=} param0.slots The slots to eject on
|
||||
* @param {boolean=} param0.renderFloatingItems Whether to render items even if they are not connected
|
||||
*/
|
||||
constructor({ slots = [], renderFloatingItems = true }) {
|
||||
super();
|
||||
|
||||
this.setSlots(slots);
|
||||
this.renderFloatingItems = renderFloatingItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array<{pos: Vector, direction: enumDirection }>} slots The slots to eject on
|
||||
*/
|
||||
setSlots(slots) {
|
||||
/** @type {Array<ItemEjectorSlot>} */
|
||||
this.slots = [];
|
||||
for (let i = 0; i < slots.length; ++i) {
|
||||
const slot = slots[i];
|
||||
this.slots.push({
|
||||
pos: slot.pos,
|
||||
direction: slot.direction,
|
||||
item: null,
|
||||
progress: 0,
|
||||
cachedDestSlot: null,
|
||||
cachedTargetEntity: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns where this slot ejects to
|
||||
* @param {ItemEjectorSlot} slot
|
||||
* @returns {Vector}
|
||||
*/
|
||||
getSlotTargetLocalTile(slot) {
|
||||
const directionVector = enumDirectionToVector[slot.direction];
|
||||
return slot.pos.add(directionVector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether any slot ejects to the given local tile
|
||||
* @param {Vector} tile
|
||||
*/
|
||||
anySlotEjectsToLocalTile(tile) {
|
||||
for (let i = 0; i < this.slots.length; ++i) {
|
||||
if (this.getSlotTargetLocalTile(this.slots[i]).equals(tile)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if we can eject on a given slot
|
||||
* @param {number} slotIndex
|
||||
* @returns {boolean}
|
||||
*/
|
||||
canEjectOnSlot(slotIndex) {
|
||||
assert(slotIndex >= 0 && slotIndex < this.slots.length, "Invalid ejector slot: " + slotIndex);
|
||||
return !this.slots[slotIndex].item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first free slot on this ejector or null if there is none
|
||||
* @returns {number?}
|
||||
*/
|
||||
getFirstFreeSlot() {
|
||||
for (let i = 0; i < this.slots.length; ++i) {
|
||||
if (this.canEjectOnSlot(i)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to eject a given item
|
||||
* @param {number} slotIndex
|
||||
* @param {BaseItem} item
|
||||
* @returns {boolean}
|
||||
*/
|
||||
tryEject(slotIndex, item) {
|
||||
if (!this.canEjectOnSlot(slotIndex)) {
|
||||
return false;
|
||||
}
|
||||
this.slots[slotIndex].item = item;
|
||||
this.slots[slotIndex].progress = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the given slot and returns the item it had
|
||||
* @param {number} slotIndex
|
||||
* @returns {BaseItem|null}
|
||||
*/
|
||||
takeSlotItem(slotIndex) {
|
||||
const slot = this.slots[slotIndex];
|
||||
const item = slot.item;
|
||||
slot.item = null;
|
||||
slot.progress = 0.0;
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user