1
0
mirror of https://github.com/teoxoy/factorio-blueprint-editor.git synced 2025-03-27 21:39:03 +02:00

Blueprint tools (rotate, flip) for #9, and additive past e (#237)

This commit is contained in:
Ken Vogel 2022-02-26 23:40:48 -06:00 committed by Teodor Tanasoaia
parent 200093f68f
commit 455cd36034
8 changed files with 183 additions and 18 deletions

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -130,8 +130,8 @@ export class PaintEntityContainer extends PaintContainer {
this.moveAtCursor()
}
public rotatedEntities(): Entity[] {
return undefined
public canFlipOrRotateByCopying(): boolean {
return false;
}
protected redraw(): void {

View File

@ -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

View File

@ -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

View File

@ -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()