Refactor belt underlay feature into seperate component

This commit is contained in:
tobspr 2020-08-10 20:26:47 +02:00
parent f91e677f2e
commit b3b8da04a1
9 changed files with 148 additions and 105 deletions

View File

@ -1,14 +1,14 @@
import { globalConfig } from "../../core/config";
import { enumDirection, Vector } from "../../core/vector";
import { ItemAcceptorComponent } from "../components/item_acceptor";
import { ItemEjectorComponent } from "../components/item_ejector";
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
import { Entity } from "../entity";
import { MetaBuilding, defaultBuildingVariant } from "../meta_building";
import { GameRoot, enumLayer } from "../root";
import { GameRoot } from "../root";
import { enumHubGoalRewards } from "../tutorial_goals";
import { T } from "../../translations";
import { formatItemsPerSecond } from "../../core/utils";
import { BeltUnderlaysComponent } from "../components/belt_underlays";
/** @enum {string} */
export const enumSplitterVariants = { compact: "compact", compactInverse: "compact-inverse" };
@ -88,6 +88,8 @@ export class MetaSplitterBuilding extends MetaBuilding {
slots: [], // set later
})
);
entity.addComponent(new BeltUnderlaysComponent({ underlays: [] }));
}
/**
@ -115,9 +117,9 @@ export class MetaSplitterBuilding extends MetaBuilding {
{ pos: new Vector(1, 0), direction: enumDirection.top },
]);
entity.components.ItemAcceptor.beltUnderlays = [
{ pos: new Vector(0, 0), direction: enumDirection.top, layer: enumLayer.regular },
{ pos: new Vector(1, 0), direction: enumDirection.top, layer: enumLayer.regular },
entity.components.BeltUnderlays.underlays = [
{ pos: new Vector(0, 0), direction: enumDirection.top },
{ pos: new Vector(1, 0), direction: enumDirection.top },
];
break;
@ -143,8 +145,8 @@ export class MetaSplitterBuilding extends MetaBuilding {
{ pos: new Vector(0, 0), direction: enumDirection.top },
]);
entity.components.ItemAcceptor.beltUnderlays = [
{ pos: new Vector(0, 0), direction: enumDirection.top, layer: enumLayer.regular },
entity.components.BeltUnderlays.underlays = [
{ pos: new Vector(0, 0), direction: enumDirection.top },
];
break;

View File

@ -13,6 +13,7 @@ import { StorageComponent } from "./components/storage";
import { EnergyGeneratorComponent } from "./components/energy_generator";
import { WiredPinsComponent } from "./components/wired_pins";
import { EnergyConsumerComponent } from "./components/energy_consumer";
import { BeltUnderlaysComponent } from "./components/belt_underlays";
export function initComponentRegistry() {
gComponentRegistry.register(StaticMapEntityComponent);
@ -29,6 +30,7 @@ export function initComponentRegistry() {
gComponentRegistry.register(EnergyGeneratorComponent);
gComponentRegistry.register(WiredPinsComponent);
gComponentRegistry.register(EnergyConsumerComponent);
gComponentRegistry.register(BeltUnderlaysComponent);
// IMPORTANT ^^^^^ UPDATE ENTITY COMPONENT STORAGE AFTERWARDS

View File

@ -0,0 +1,44 @@
import { Component } from "../component";
import { types } from "../../savegame/serialization";
import { enumDirection, Vector } from "../../core/vector";
export class BeltUnderlaysComponent extends Component {
static getId() {
return "BeltUnderlays";
}
static getSchema() {
return {
underlays: types.array(
types.structured({
pos: types.vector,
direction: types.enum(enumDirection),
})
),
};
}
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;
}
}

View File

@ -39,16 +39,6 @@ export class ItemAcceptorComponent extends Component {
directions: types.array(types.enum(enumDirection)),
filter: types.nullable(types.enum(enumItemType)),
// TODO: MIGRATE
layer: types.enum(enumLayer),
})
),
animated: types.bool,
beltUnderlays: types.array(
types.structured({
pos: types.vector,
direction: types.enum(enumDirection),
// TODO: MIGRATE
layer: types.enum(enumLayer),
})
@ -68,20 +58,8 @@ export class ItemAcceptorComponent extends Component {
});
}
const beltUnderlaysCopy = [];
for (let i = 0; i < this.beltUnderlays.length; ++i) {
const underlay = this.beltUnderlays[i];
beltUnderlaysCopy.push({
pos: underlay.pos.copy(),
direction: underlay.direction,
layer: underlay.layer,
});
}
return new ItemAcceptorComponent({
slots: slotsCopy,
beltUnderlays: beltUnderlaysCopy,
animated: this.animated,
});
}
@ -89,23 +67,16 @@ export class ItemAcceptorComponent extends Component {
*
* @param {object} param0
* @param {Array<ItemAcceptorSlotConfig>} param0.slots The slots from which we accept items
* @param {boolean=} param0.animated Whether to animate item consumption
* @param {Array<{pos: Vector, direction: enumDirection, layer: enumLayer}>=} param0.beltUnderlays Where to render belt underlays
*/
constructor({ slots = [], beltUnderlays = [], animated = true }) {
constructor({ slots = [] }) {
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);
}
@ -164,14 +135,12 @@ export class ItemAcceptorComponent extends Component {
* @param {BaseItem} item
*/
onItemAccepted(slotIndex, direction, item) {
if (this.animated) {
this.itemConsumptionAnimations.push({
item,
slotIndex,
direction,
animProgress: 0.0,
});
}
this.itemConsumptionAnimations.push({
item,
slotIndex,
direction,
animProgress: 0.0,
});
}
/**

View File

@ -398,7 +398,7 @@ export class GameCore {
if (!this.root.camera.getIsMapOverlayActive()) {
// Underlays for splitters / balancers
systems.itemAcceptor.drawUnderlays(params, enumLayer.regular);
systems.beltUnderlays.drawUnderlays(params, enumLayer.regular);
// Belt items
systems.belt.drawLayerBeltItems(params, enumLayer.regular);

View File

@ -13,6 +13,7 @@ import { StorageComponent } from "./components/storage";
import { EnergyGeneratorComponent } from "./components/energy_generator";
import { WiredPinsComponent } from "./components/wired_pins";
import { EnergyConsumerComponent } from "./components/energy_consumer";
import { BeltUnderlaysComponent } from "./components/belt_underlays";
/* typehints:end */
/**
@ -65,6 +66,9 @@ export class EntityComponentStorage {
/** @type {EnergyConsumerComponent} */
this.EnergyConsumer;
/** @type {BeltUnderlaysComponent} */
this.BeltUnderlays;
/* typehints:end */
}
}

View File

@ -16,6 +16,7 @@ import { StorageSystem } from "./systems/storage";
import { EnergyGeneratorSystem } from "./systems/energy_generator";
import { WiredPinsSystem } from "./systems/wired_pins";
import { EnergyConsumerSystem } from "./systems/energy_consumer";
import { BeltUnderlaysSystem } from "./systems/belt_underlays";
const logger = createLogger("game_system_manager");
@ -68,6 +69,9 @@ export class GameSystemManager {
/** @type {EnergyConsumerSystem} */
energyConsumer: null,
/** @type {BeltUnderlaysSystem} */
beltUnderlays: null,
/* typehints:end */
};
this.systemUpdateOrder = [];
@ -110,6 +114,8 @@ export class GameSystemManager {
add("energyConsumer", EnergyConsumerSystem);
add("beltUnderlays", BeltUnderlaysSystem);
// IMPORTANT: Must be after belt system since belt system can change the
// orientation of an entity after it is placed -> the item acceptor cache
// then would be invalid

View File

@ -0,0 +1,75 @@
import { GameSystemWithFilter } from "../game_system_with_filter";
import { BeltUnderlaysComponent } from "../components/belt_underlays";
import { BELT_ANIM_COUNT } from "./belt";
import { Loader } from "../../core/loader";
import { enumLayer } from "../root";
import { Entity } from "../entity";
import { enumDirectionToAngle } from "../../core/vector";
import { globalConfig } from "../../core/config";
import { drawRotatedSprite } from "../../core/draw_utils";
export class BeltUnderlaysSystem extends GameSystemWithFilter {
constructor(root) {
super(root, [BeltUnderlaysComponent]);
this.underlayBeltSprites = [];
for (let i = 0; i < BELT_ANIM_COUNT; ++i) {
this.underlayBeltSprites.push(Loader.getSprite("sprites/belt/forward_" + i + ".png"));
}
}
/**
* Draws the acceptor underlays
* @param {import("../../core/draw_utils").DrawParameters} parameters
* @param {enumLayer} layer
*/
drawUnderlays(parameters, layer) {
this.forEachMatchingEntityOnScreen(parameters, this.drawEntityUnderlays.bind(this, layer));
}
/**
* @param {enumLayer} layer
* @param {import("../../core/draw_utils").DrawParameters} parameters
* @param {Entity} entity
*/
drawEntityUnderlays(layer, parameters, entity) {
const staticComp = entity.components.StaticMapEntity;
const underlayComp = entity.components.BeltUnderlays;
if (entity.layer !== layer) {
// Not our layer
return;
}
if (!staticComp.shouldBeDrawn(parameters)) {
return;
}
// Limit speed to avoid belts going backwards
const speedMultiplier = Math.min(this.root.hubGoals.getBeltBaseSpeed(layer), 10);
const underlays = underlayComp.underlays;
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.realtimeNow() * speedMultiplier * BELT_ANIM_COUNT * 126) / 42) *
globalConfig.beltItemSpacingByLayer[layer]
);
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

@ -13,12 +13,6 @@ import { enumLayer } from "../root";
export class ItemAcceptorSystem extends GameSystemWithFilter {
constructor(root) {
super(root, [ItemAcceptorComponent]);
this.underlayBeltSprites = [];
for (let i = 0; i < BELT_ANIM_COUNT; ++i) {
this.underlayBeltSprites.push(Loader.getSprite("sprites/belt/forward_" + i + ".png"));
}
}
update() {
@ -59,15 +53,6 @@ export class ItemAcceptorSystem extends GameSystemWithFilter {
this.forEachMatchingEntityOnScreen(parameters, this.drawEntityRegularLayer.bind(this, layer));
}
/**
* Draws the acceptor underlays
* @param {DrawParameters} parameters
* @param {enumLayer} layer
*/
drawUnderlays(parameters, layer) {
this.forEachMatchingEntityOnScreen(parameters, this.drawEntityUnderlays.bind(this, layer));
}
/**
* @param {enumLayer} layer
* @param {DrawParameters} parameters
@ -105,48 +90,4 @@ export class ItemAcceptorSystem extends GameSystemWithFilter {
);
}
}
/**
* @param {enumLayer} layer
* @param {DrawParameters} parameters
* @param {Entity} entity
*/
drawEntityUnderlays(layer, parameters, entity) {
const staticComp = entity.components.StaticMapEntity;
const acceptorComp = entity.components.ItemAcceptor;
if (!staticComp.shouldBeDrawn(parameters)) {
return;
}
// Limit speed to avoid belts going backwards
const speedMultiplier = Math.min(this.root.hubGoals.getBeltBaseSpeed(layer), 10);
const underlays = acceptorComp.beltUnderlays;
for (let i = 0; i < underlays.length; ++i) {
const { pos, direction, layer: underlayLayer } = underlays[i];
if (underlayLayer !== layer) {
// Not our layer
continue;
}
const transformedPos = staticComp.localTileToWorld(pos);
const angle = enumDirectionToAngle[staticComp.localDirectionToWorld(direction)];
// SYNC with systems/belt.js:drawSingleEntity!
const animationIndex = Math.floor(
((this.root.time.realtimeNow() * speedMultiplier * BELT_ANIM_COUNT * 126) / 42) *
globalConfig.beltItemSpacingByLayer[layer]
);
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,
});
}
}
}