UNPKG

patchwork-mapconverter

Version:

Executable wrapper for https://github.com/ChiefOfGxBxL/WC3MapTranslator

190 lines (161 loc) 7.2 kB
import { HexBuffer } from '../HexBuffer' import { W3Buffer } from '../W3Buffer' import { rad2Deg, deg2Rad } from '../AngleConverter' import { type WarResult, type JsonResult } from '../CommonInterfaces' import { type Translator } from './Translator' import { SpecialDoodad, type Doodad } from '../data/Doodad' export class DoodadsTranslator implements Translator<[Doodad[], SpecialDoodad[]]> { private static instance: DoodadsTranslator private constructor() { } public static getInstance(): DoodadsTranslator { if (this.instance == null) { this.instance = new this() } return this.instance } public static jsonToWar(doodads: [Doodad[], SpecialDoodad[]]): WarResult { return this.getInstance().jsonToWar(doodads) } public static warToJson(buffer: Buffer): JsonResult<[Doodad[], SpecialDoodad[]]> { return this.getInstance().warToJson(buffer) } public jsonToWar(compositeJson: [Doodad[], SpecialDoodad[]]): WarResult { const doodadsJson = compositeJson[0]; const specialDoodadsJson = compositeJson[1]; const outBufferToWar = new HexBuffer() /* * Header */ outBufferToWar.addChars('W3do') // file id outBufferToWar.addInt(8) // file version outBufferToWar.addInt(11) // subversion 0x0B outBufferToWar.addInt(doodadsJson?.length || 0) // num of trees /* * Body */ doodadsJson?.forEach((tree) => { outBufferToWar.addChars(tree.type) outBufferToWar.addInt(tree.variation != null ? tree.variation : 0) // optional - default value 0 outBufferToWar.addFloat(tree.position[0]) outBufferToWar.addFloat(tree.position[1]) outBufferToWar.addFloat(tree.position[2]) // Angle // Doodads format is unique because it uses radians for angles, as opposed // to angles in any other file which use degrees. Hence conversion is needed. // war3map: Expects angle in RADIANS // JSON: Spec defines angle in DEGREES const radAngle = deg2Rad(tree.angle != null ? tree.angle : 0) outBufferToWar.addFloat(radAngle) // optional - default value 0 // Scale if (tree.scale == null) tree.scale = [1, 1, 1] outBufferToWar.addFloat(tree.scale[0] != null ? tree.scale[0] : 1) outBufferToWar.addFloat(tree.scale[1] != null ? tree.scale[1] : 1) outBufferToWar.addFloat(tree.scale[2] != null ? tree.scale[2] : 1) outBufferToWar.addChars(tree.skinId) if (tree.flags == null) tree.flags = { inUnplayableArea: false, notUsedInScript: true, fixedZ: false } // defaults if no flags are specified let treeFlag = 0 if (tree.flags.fixedZ) treeFlag |= 0x04 if (tree.flags.notUsedInScript) treeFlag |= 0x02 if (tree.flags.inUnplayableArea) treeFlag |= 0x01 outBufferToWar.addByte(treeFlag) outBufferToWar.addByte(tree.life != null ? tree.life : 100) outBufferToWar.addInt(tree.randomItemSetPtr) outBufferToWar.addInt(tree.droppedItemSets?.length || 0) tree?.droppedItemSets?.forEach(itemSet => { // Write the item set outBufferToWar.addInt(itemSet.items?.length || 0); itemSet.items?.forEach(item => { outBufferToWar.addChars(item.itemId) outBufferToWar.addInt(item.chance) }) }) outBufferToWar.addInt(tree.id) }) /* * Footer */ outBufferToWar.addInt(0) // special doodad format number, fixed at 0x00 outBufferToWar.addInt(specialDoodadsJson?.length || 0) // number of special doodads specialDoodadsJson?.forEach(specialDoodad => { outBufferToWar.addChars(specialDoodad.type) outBufferToWar.addInt(specialDoodad.position[0]) //x outBufferToWar.addInt(specialDoodad.position[1]) //y outBufferToWar.addInt(specialDoodad.position[2]) //z }) return { errors: [], buffer: outBufferToWar.getBuffer() } } public warToJson(buffer: Buffer): JsonResult<[Doodad[], SpecialDoodad[]]> { const result: Doodad[] = [] const outBufferToJSON = new W3Buffer(buffer) const fileId = outBufferToJSON.readChars(4) // W3do for doodad file const fileVersion = outBufferToJSON.readInt() // File version = 8 const subVersion = outBufferToJSON.readInt() // 0B 00 00 00 const numDoodads = outBufferToJSON.readInt() // # of doodads for (let i = 0; i < numDoodads; i++) { const doodad: Doodad = { type: '', variation: 0, position: [0, 0, 0], angle: -1, scale: [0, 0, 0], skinId: '', flags: { inUnplayableArea: false, notUsedInScript: true, fixedZ: false }, life: -1, randomItemSetPtr: 0, droppedItemSets: [], id: -1 } doodad.type = outBufferToJSON.readChars(4) doodad.variation = outBufferToJSON.readInt() doodad.position = [outBufferToJSON.readFloat(), outBufferToJSON.readFloat(), outBufferToJSON.readFloat()] // X Y Z coords // Angle // Doodads format is unique because it uses radians for angles, as opposed // to angles in any other file which use degrees. Hence conversion is needed. // war3map: Expects angle in RADIANS // JSON: Spec defines angle in DEGREES doodad.angle = rad2Deg(outBufferToJSON.readFloat()) doodad.scale = [outBufferToJSON.readFloat(), outBufferToJSON.readFloat(), outBufferToJSON.readFloat()] // X Y Z scaling doodad.skinId = outBufferToJSON.readChars(4) const flags = outBufferToJSON.readByte() doodad.flags = { fixedZ: !!(flags & 0x04), notUsedInScript: !!(flags & 0x02), inUnplayableArea: !!(flags & 0x01), } doodad.life = outBufferToJSON.readByte() // as a % doodad.randomItemSetPtr = outBufferToJSON.readInt() // points to an item set defined in the map (rather than custom one defined below) const numberOfItemSets = outBufferToJSON.readInt() // this should be 0 if randomItemSetPtr is >= 0 for (let j = 0; j < numberOfItemSets; j++) { // Read the item set const numberOfItems = outBufferToJSON.readInt() doodad.droppedItemSets.push({ items: [] }) for (let k = 0; k < numberOfItems; k++) { doodad.droppedItemSets[j].items.push({ itemId: outBufferToJSON.readChars(4), // Item ID chance: outBufferToJSON.readInt() // % chance to drop }); } } doodad.id = outBufferToJSON.readInt() result.push(doodad) } const resultSpecial: SpecialDoodad[] = [] outBufferToJSON.readInt() // special doodad format version set to '0' const numSpecialDoodads = outBufferToJSON.readInt() for (let i = 0; i < numSpecialDoodads; i++) { resultSpecial.push({ type: outBufferToJSON.readChars(4), // doodad ID position: [outBufferToJSON.readInt(), outBufferToJSON.readInt(), outBufferToJSON.readInt()] }) } return { errors: [], json: [result, resultSpecial] } } }