mirror of
https://github.com/teoxoy/factorio-blueprint-editor.git
synced 2025-03-27 21:39:03 +02:00
parent
200093f68f
commit
455cd36034
@ -11,9 +11,10 @@ import { BlueprintContainer, EditorMode, GridPattern } from './containers/Bluepr
|
||||
import { UIContainer } from './UI/UIContainer'
|
||||
import { Dialog } from './UI/controls/Dialog'
|
||||
import { initActions, registerAction } from './actions'
|
||||
import { IllegalFlipError } from './containers/PaintContainer'
|
||||
|
||||
export class Editor {
|
||||
public async init(canvas: HTMLCanvasElement): Promise<void> {
|
||||
public async init(canvas: HTMLCanvasElement, showError: (msg: string) => void): Promise<void> {
|
||||
await Promise.all([
|
||||
fetch('__STATIC_URL__/data.json')
|
||||
.then(res => res.text())
|
||||
@ -61,7 +62,7 @@ export class Editor {
|
||||
G.UI.showDebuggingLayer = G.debug
|
||||
|
||||
initActions(canvas)
|
||||
this.registerActions()
|
||||
this.registerActions(showError)
|
||||
}
|
||||
|
||||
public get moveSpeed(): number {
|
||||
@ -118,6 +119,16 @@ export class Editor {
|
||||
return G.BPC.getPicture()
|
||||
}
|
||||
|
||||
public haveBlueprint(): boolean {
|
||||
return !G.bp.isEmpty();
|
||||
}
|
||||
|
||||
public async appendBlueprint(bp: Blueprint): Promise<void> {
|
||||
const result = bp.entities.valuesArray().map(e => new Entity(e.rawEntity, G.BPC.bp));
|
||||
|
||||
G.BPC.spawnPaintContainer(result, 0)
|
||||
}
|
||||
|
||||
public async loadBlueprint(bp: Blueprint): Promise<void> {
|
||||
const last = G.BPC
|
||||
const i = G.app.stage.getChildIndex(last)
|
||||
@ -131,7 +142,7 @@ export class Editor {
|
||||
last.destroy()
|
||||
}
|
||||
|
||||
private registerActions(): void {
|
||||
private registerActions(showError: (msg: string) => void): void {
|
||||
registerAction('moveUp', 'w')
|
||||
registerAction('moveLeft', 'a')
|
||||
registerAction('moveDown', 's')
|
||||
@ -166,12 +177,29 @@ export class Editor {
|
||||
if (G.BPC.mode === EditorMode.EDIT) {
|
||||
G.BPC.hoverContainer.entity.rotate(false, true)
|
||||
} else if (G.BPC.mode === EditorMode.PAINT) {
|
||||
const copies = G.BPC.paintContainer.rotatedEntities(ccw)
|
||||
if (copies === undefined) {
|
||||
G.BPC.paintContainer.rotate(ccw)
|
||||
} else {
|
||||
if (G.BPC.paintContainer.canFlipOrRotateByCopying()) {
|
||||
const copies = G.BPC.paintContainer.rotatedEntities(ccw)
|
||||
G.BPC.paintContainer.destroy()
|
||||
G.BPC.spawnPaintContainer(copies, 0)
|
||||
} else {
|
||||
G.BPC.paintContainer.rotate(ccw)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function doFlip(vertical: boolean): void {
|
||||
if (G.BPC.mode === EditorMode.PAINT
|
||||
&& G.BPC.paintContainer.canFlipOrRotateByCopying()) {
|
||||
try {
|
||||
const copies = G.BPC.paintContainer.flippedEntities(vertical)
|
||||
G.BPC.paintContainer.destroy()
|
||||
G.BPC.spawnPaintContainer(copies, 0)
|
||||
} catch (e) {
|
||||
if (e instanceof IllegalFlipError) {
|
||||
showError(e.message)
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -184,6 +212,14 @@ export class Editor {
|
||||
press: () => doRotate(true)
|
||||
})
|
||||
|
||||
registerAction('flipHorizontal', 'shift+f').bind({
|
||||
press: () => doFlip(false)
|
||||
})
|
||||
|
||||
registerAction('flipVertical', 'shift+g').bind({
|
||||
press: () => doFlip(true)
|
||||
})
|
||||
|
||||
registerAction('pipette', 'q').bind({
|
||||
press: () => {
|
||||
if (G.BPC.mode === EditorMode.EDIT) {
|
||||
|
@ -88,6 +88,16 @@ export class PaintBlueprintContainer extends PaintContainer {
|
||||
return undefined
|
||||
}
|
||||
|
||||
public logDataForComparison(): void {
|
||||
const withOutNums = [...this.entities.keys()].map(e => ({...e.rawEntity, entity_number: undefined}))
|
||||
withOutNums.sort((a, b) => Math.sign(b.position.y - a.position.y) || Math.sign(b.position.x - a.position.x))
|
||||
console.log(withOutNums)
|
||||
}
|
||||
|
||||
public canFlipOrRotateByCopying(): boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
public rotatedEntities(ccw?: boolean): Entity[] {
|
||||
if (!this.visible) return undefined
|
||||
const result = []
|
||||
@ -97,6 +107,14 @@ export class PaintBlueprintContainer extends PaintContainer {
|
||||
return result
|
||||
}
|
||||
|
||||
public flippedEntities(vertical: boolean): Entity[] {
|
||||
const result = []
|
||||
for (const [e] of this.entities) {
|
||||
result.push(e.getFlippedCopy(vertical))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
public moveAtCursor(): void {
|
||||
if (!this.visible) return
|
||||
|
@ -4,6 +4,13 @@ import { Entity } from '../core/Entity'
|
||||
import F from '../UI/controls/functions'
|
||||
import { BlueprintContainer } from './BlueprintContainer'
|
||||
|
||||
export class IllegalFlipError {
|
||||
public message: string
|
||||
public constructor(message: string) {
|
||||
this.message = message
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class PaintContainer extends PIXI.Container {
|
||||
protected readonly bpc: BlueprintContainer
|
||||
private readonly icon: PIXI.DisplayObject
|
||||
@ -91,12 +98,21 @@ export abstract class PaintContainer extends PIXI.Container {
|
||||
// override
|
||||
public abstract getItemName(): string
|
||||
|
||||
// override
|
||||
// override
|
||||
public abstract rotate(ccw?: boolean): void
|
||||
|
||||
// override
|
||||
// override
|
||||
public abstract flip(vertical: boolean): void
|
||||
|
||||
// override
|
||||
public abstract canFlipOrRotateByCopying(): boolean
|
||||
|
||||
// override
|
||||
public abstract rotatedEntities(ccw?: boolean): Entity[]
|
||||
|
||||
// override
|
||||
public abstract flippedEntities(vertical: boolean): Entity[]
|
||||
|
||||
// override
|
||||
protected abstract redraw(): void
|
||||
|
||||
|
@ -130,8 +130,8 @@ export class PaintEntityContainer extends PaintContainer {
|
||||
this.moveAtCursor()
|
||||
}
|
||||
|
||||
public rotatedEntities(): Entity[] {
|
||||
return undefined
|
||||
public canFlipOrRotateByCopying(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected redraw(): void {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { EventEmitter } from 'eventemitter3'
|
||||
import util from '../common/util'
|
||||
import { IllegalFlipError } from '../containers/PaintContainer'
|
||||
import FD, { Entity as FD_Entity } from './factorioData'
|
||||
import { Blueprint } from './Blueprint'
|
||||
import { getBeltWireConnectionIndex } from './spriteDataBuilder'
|
||||
@ -78,6 +79,11 @@ export class Entity extends EventEmitter {
|
||||
public get position(): IPoint {
|
||||
return this.m_rawEntity.position
|
||||
}
|
||||
|
||||
public get rawEntity(): BPS.IEntity {
|
||||
return this.m_rawEntity;
|
||||
}
|
||||
|
||||
public set position(position: IPoint) {
|
||||
if (util.areObjectsEquivalent(this.m_rawEntity.position, position)) return
|
||||
|
||||
@ -530,8 +536,82 @@ export class Entity extends EventEmitter {
|
||||
const position = ccw
|
||||
? {x: this.m_rawEntity.position.y, y: -this.m_rawEntity.position.x}
|
||||
: {x: -this.m_rawEntity.position.y, y: this.m_rawEntity.position.x}
|
||||
const direction = this.rotateDir(ccw)
|
||||
return new Entity({...this.m_rawEntity, position, direction}, this.m_BP);
|
||||
const direction = this.constrainDirection((this.direction + (ccw ? 6 : 2)) % 8)
|
||||
const updatedRawEntity = {...this.m_rawEntity, position, direction}
|
||||
if (direction === 0) delete updatedRawEntity.direction
|
||||
|
||||
return new Entity(updatedRawEntity, this.m_BP);
|
||||
}
|
||||
|
||||
private constrainDirection(direction: number): number {
|
||||
const pr = this.entityData.possible_rotations
|
||||
let canRotate = pr !== undefined
|
||||
|
||||
if (this.type === 'assembling_machine')
|
||||
canRotate = this.assemblerCraftsWithFluid
|
||||
if (canRotate) {
|
||||
if (!pr.includes(direction)) {
|
||||
if (direction === 4 && pr.includes(0)) {
|
||||
return 0
|
||||
} else if (direction === 6 && pr.includes(2)) {
|
||||
return 2
|
||||
} else {
|
||||
return this.direction
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
return direction
|
||||
}
|
||||
|
||||
private changePriority(priority?: 'left' | 'right'): 'left' | 'right' | undefined {
|
||||
if (priority === 'left')
|
||||
return 'right'
|
||||
else if (priority === 'right')
|
||||
return 'left'
|
||||
return priority
|
||||
}
|
||||
|
||||
public getFlippedCopy(vertical: boolean): Entity {
|
||||
// Curved Rail thing is (2, 4, 6, 0) down left, (7, 1, 3, 5)
|
||||
// Vert: 2-7, 6-3, 4-5, 0-1 Normal: 0-4
|
||||
// Horz: 2-3, 4-1, 6-7, 0-5 Normal: 2-6
|
||||
// Straight rail: 1, 2, 7, 0, 5, 2, 3, 0
|
||||
// Vert: 1-3, 2-2, 7-5, 0-0
|
||||
// Horz: 1-7, 3-5
|
||||
const translation_map: {[key: string]: {[vert: string]: number[]}} = {
|
||||
'curved_rail': {true: [5, 4, 3, 2, 1, 0, 7, 6], false: [1, 0, 7, 6, 5, 4, 3, 2] },
|
||||
'straight_rail': {true: [0, 3, 2, 1, 4, 7, 6, 5], false: [0, 7, 2, 5, 4, 3, 6, 1]},
|
||||
'default': {true: [4, 1, 2, 3, 0, 5, 6, 7], false: [0, 1, 6, 3, 4, 5, 2, 7]}
|
||||
}
|
||||
|
||||
const non_flip_entities = ['chemical_plant', 'oil_refinery', 'train_stop', 'rail_chain_signal', 'rail_signal']
|
||||
|
||||
if (non_flip_entities.includes(this.name))
|
||||
throw new IllegalFlipError(`${this.name } cannot be flipped`);
|
||||
|
||||
const translation = this.name in translation_map ? translation_map[this.name] : translation_map.default
|
||||
const direction = this.name === 'storage_tank'
|
||||
? (2 - this.direction)
|
||||
: this.constrainDirection(translation[String(vertical)][this.direction])
|
||||
|
||||
let input_priority = this.m_rawEntity.input_priority
|
||||
let output_priority = this.m_rawEntity.output_priority
|
||||
|
||||
if ((vertical && (direction === 2 || direction === 4))
|
||||
|| (!vertical && (direction === 0 || direction === 6))) {
|
||||
input_priority = this.changePriority(input_priority)
|
||||
output_priority = this.changePriority(output_priority)
|
||||
}
|
||||
|
||||
const position = vertical
|
||||
? {x: this.m_rawEntity.position.x, y: -this.m_rawEntity.position.y}
|
||||
: {x: -this.m_rawEntity.position.x, y: this.m_rawEntity.position.y}
|
||||
const updatedRawEntity = {...this.m_rawEntity, direction, position, input_priority, output_priority}
|
||||
if (direction === 0) delete updatedRawEntity.direction
|
||||
|
||||
return new Entity(updatedRawEntity, this.m_BP);
|
||||
}
|
||||
|
||||
private rotateDir(ccw: boolean): number {
|
||||
|
File diff suppressed because one or more lines are too long
@ -377,9 +377,7 @@ do
|
||||
'arithmetic-combinator',
|
||||
'decider-combinator',
|
||||
'constant-combinator',
|
||||
'gun-turret',
|
||||
'artillery-turret',
|
||||
'laser-turret',
|
||||
'flamethrower-turret',
|
||||
'offshore-pump',
|
||||
'pump'
|
||||
@ -399,7 +397,6 @@ do
|
||||
elseif list_includes({
|
||||
'storage-tank',
|
||||
'gate',
|
||||
'straight-rail'
|
||||
}, entity.name) or list_includes({
|
||||
'generator'
|
||||
}, entity.type) then
|
||||
@ -407,11 +404,18 @@ do
|
||||
entity.possible_rotations = { 0, 2 }
|
||||
|
||||
elseif list_includes({
|
||||
'curved-rail',
|
||||
'rail-signal',
|
||||
'rail-chain-signal'
|
||||
}, entity.name) then
|
||||
|
||||
entity.possible_rotations = { 0, 1, 2, 3, 4, 5, 6, 7 }
|
||||
|
||||
elseif list_includes({
|
||||
'straight-rail'
|
||||
}, entity.name) then
|
||||
|
||||
entity.possible_rotations = { 0, 1, 2, 3, 5, 7 }
|
||||
end
|
||||
|
||||
-- modify fast_replaceable_group
|
||||
|
@ -91,7 +91,7 @@ for (const p of params) {
|
||||
let changeBookForIndexSelector: (bpOrBook: Book | Blueprint) => void
|
||||
|
||||
editor
|
||||
.init(CANVAS)
|
||||
.init(CANVAS, (text: string) => createToast({text, type: 'error', timeout: 3000}))
|
||||
.then(() => {
|
||||
if (localStorage.getItem('quickbarItemNames')) {
|
||||
const quickbarItems = JSON.parse(localStorage.getItem('quickbarItemNames'))
|
||||
@ -193,6 +193,17 @@ function registerActions(): void {
|
||||
press: () => loadBp(new Blueprint()),
|
||||
})
|
||||
|
||||
EDITOR.registerAction('appendBlueprint', 'shift+modifier+v').bind({
|
||||
press: () => {
|
||||
navigator.clipboard.readText()
|
||||
.then(getBlueprintOrBookFromSource)
|
||||
.then(bp => editor.appendBlueprint(bp instanceof Book ? bp.selectBlueprint(0) : bp))
|
||||
.catch(error => {
|
||||
createBPImportError(error)
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
EDITOR.registerAction('generateOilOutpost', 'g').bind({
|
||||
press: () => {
|
||||
const errorMessage = bp.generatePipes()
|
||||
|
Loading…
x
Reference in New Issue
Block a user