diff --git a/src/data_formats/Vec3.ts b/src/data_formats/Vec3.ts new file mode 100644 index 00000000..b6d76f42 --- /dev/null +++ b/src/data_formats/Vec3.ts @@ -0,0 +1,22 @@ +export class Vec3 { + x: number; + y: number; + z: number; + + constructor(x: number, y: number, z: number) { + this.x = x; + this.y = y; + this.z = z; + } + + add(v: Vec3): Vec3 { + this.x += v.x; + this.y += v.y; + this.z += v.z; + return this; + } + + clone() { + return new Vec3(this.x, this.y, this.z); + } +} diff --git a/src/data_formats/parsing/geometry.ts b/src/data_formats/parsing/geometry.ts index 7266799c..88d416a7 100644 --- a/src/data_formats/parsing/geometry.ts +++ b/src/data_formats/parsing/geometry.ts @@ -1,23 +1,12 @@ -import { - BufferAttribute, - BufferGeometry, - DoubleSide, - Face3, - Geometry, - Mesh, - MeshBasicMaterial, - MeshLambertMaterial, - Object3D, - TriangleStripDrawMode, - Vector3 -} from 'three'; -import { Vec3, Section } from '../../domain'; import Logger from 'js-logger'; +import { BufferGeometry, DoubleSide, Face3, Float32BufferAttribute, Geometry, Mesh, MeshBasicMaterial, MeshLambertMaterial, Object3D, TriangleStripDrawMode, Uint16BufferAttribute, Vector3 } from 'three'; +import { Section } from '../../domain'; +import { Vec3 } from "../Vec3"; const logger = Logger.get('data_formats/parsing/geometry'); -export function parseCRel(arrayBuffer: ArrayBuffer): Object3D { - const dv = new DataView(arrayBuffer); +export function parse_c_rel(array_buffer: ArrayBuffer): Object3D { + const dv = new DataView(array_buffer); const object = new Object3D(); const materials = [ @@ -43,7 +32,7 @@ export function parseCRel(arrayBuffer: ArrayBuffer): Object3D { side: DoubleSide }) ]; - const wireframeMaterials = [ + const wireframe_materials = [ // Wall new MeshBasicMaterial({ color: 0x90D0E0, @@ -68,33 +57,33 @@ export function parseCRel(arrayBuffer: ArrayBuffer): Object3D { }) ]; - const mainBlockOffset = dv.getUint32(dv.byteLength - 16, true); - const mainOffsetTableOffset = dv.getUint32(mainBlockOffset, true); + const main_block_offset = dv.getUint32(dv.byteLength - 16, true); + const main_offset_table_offset = dv.getUint32(main_block_offset, true); for ( - let i = mainOffsetTableOffset; - i === mainOffsetTableOffset || dv.getUint32(i) !== 0; + let i = main_offset_table_offset; + i === main_offset_table_offset || dv.getUint32(i) !== 0; i += 24 ) { - const blockGeometry = new Geometry(); + const block_geometry = new Geometry(); - const blockTrailerOffset = dv.getUint32(i, true); - const vertexCount = dv.getUint32(blockTrailerOffset, true); - const vertexTableOffset = dv.getUint32(blockTrailerOffset + 4, true); - const vertexTableEnd = vertexTableOffset + 12 * vertexCount; - const triangleCount = dv.getUint32(blockTrailerOffset + 8, true); - const triangleTableOffset = dv.getUint32(blockTrailerOffset + 12, true); - const triangleTableEnd = triangleTableOffset + 36 * triangleCount; + const block_trailer_offset = dv.getUint32(i, true); + const vertex_count = dv.getUint32(block_trailer_offset, true); + const vertex_table_offset = dv.getUint32(block_trailer_offset + 4, true); + const vertex_table_end = vertex_table_offset + 12 * vertex_count; + const triangle_count = dv.getUint32(block_trailer_offset + 8, true); + const triangle_table_offset = dv.getUint32(block_trailer_offset + 12, true); + const triangle_table_end = triangle_table_offset + 36 * triangle_count; - for (let j = vertexTableOffset; j < vertexTableEnd; j += 12) { + for (let j = vertex_table_offset; j < vertex_table_end; j += 12) { const x = dv.getFloat32(j, true); const y = dv.getFloat32(j + 4, true); const z = dv.getFloat32(j + 8, true); - blockGeometry.vertices.push(new Vector3(x, y, z)); + block_geometry.vertices.push(new Vector3(x, y, z)); } - for (let j = triangleTableOffset; j < triangleTableEnd; j += 36) { + for (let j = triangle_table_offset; j < triangle_table_end; j += 36) { const v1 = dv.getUint16(j, true); const v2 = dv.getUint16(j + 2, true); const v3 = dv.getUint16(j + 4, true); @@ -104,70 +93,68 @@ export function parseCRel(arrayBuffer: ArrayBuffer): Object3D { dv.getFloat32(j + 12, true), dv.getFloat32(j + 16, true) ); - const isSectionTransition = flags & 0b1000000; - const isVegetation = flags & 0b10000; - const isGround = flags & 0b1; - const colorIndex = isSectionTransition ? 3 : (isVegetation ? 2 : (isGround ? 1 : 0)); + const is_section_transition = flags & 0b1000000; + const is_vegetation = flags & 0b10000; + const is_ground = flags & 0b1; + const color_index = is_section_transition ? 3 : (is_vegetation ? 2 : (is_ground ? 1 : 0)); - blockGeometry.faces.push(new Face3(v1, v2, v3, n, undefined, colorIndex)); + block_geometry.faces.push(new Face3(v1, v2, v3, n, undefined, color_index)); } - const mesh = new Mesh(blockGeometry, materials); + const mesh = new Mesh(block_geometry, materials); mesh.renderOrder = 1; object.add(mesh); - const wireframeMesh = new Mesh(blockGeometry, wireframeMaterials); - wireframeMesh.renderOrder = 2; - object.add(wireframeMesh); + const wireframe_mesh = new Mesh(block_geometry, wireframe_materials); + wireframe_mesh.renderOrder = 2; + object.add(wireframe_mesh); } return object; } -export function parseNRel( - arrayBuffer: ArrayBuffer -): { sections: Section[], object3d: Object3D } { - const dv = new DataView(arrayBuffer); +export function parse_n_rel( + array_buffer: ArrayBuffer +): { sections: Section[], object_3d: Object3D } { + const dv = new DataView(array_buffer); const sections = new Map(); const object = new Object3D(); - const mainBlockOffset = dv.getUint32(dv.byteLength - 16, true); - const sectionCount = dv.getUint32(mainBlockOffset + 8, true); - const sectionTableOffset = dv.getUint32(mainBlockOffset + 16, true); - // const textureNameOffset = dv.getUint32(mainBlockOffset + 20, true); + const main_block_offset = dv.getUint32(dv.byteLength - 16, true); + const section_count = dv.getUint32(main_block_offset + 8, true); + const section_table_offset = dv.getUint32(main_block_offset + 16, true); + // const texture_name_offset = dv.getUint32(main_block_offset + 20, true); for ( - let i = sectionTableOffset; - i < sectionTableOffset + sectionCount * 52; + let i = section_table_offset; + i < section_table_offset + section_count * 52; i += 52 ) { - const sectionId = dv.getInt32(i, true); - const sectionX = dv.getFloat32(i + 4, true); - const sectionY = dv.getFloat32(i + 8, true); - const sectionZ = dv.getFloat32(i + 12, true); - const sectionRotation = dv.getInt32(i + 20, true) / 0xFFFF * 2 * Math.PI; + const section_id = dv.getInt32(i, true); + const section_x = dv.getFloat32(i + 4, true); + const section_y = dv.getFloat32(i + 8, true); + const section_z = dv.getFloat32(i + 12, true); + const section_rotation = dv.getInt32(i + 20, true) / 0xFFFF * 2 * Math.PI; const section = new Section( - sectionId, - new Vec3(sectionX, sectionY, sectionZ), - sectionRotation + section_id, + new Vec3(section_x, section_y, section_z), + section_rotation ); - sections.set(sectionId, section); + sections.set(section_id, section); - const indexListsList = []; - const positionListsList = []; - const normalListsList = []; + const index_lists_list = []; + const position_lists_list = []; + const normal_lists_list = []; - const simpleGeometryOffsetTableOffset = dv.getUint32(i + 32, true); - // const complexGeometryOffsetTableOffset = dv.getUint32(i + 36, true); - const simpleGeometryOffsetCount = dv.getUint32(i + 40, true); - // const complexGeometryOffsetCount = dv.getUint32(i + 44, true); - - // logger.log(`section id: ${sectionId}, section rotation: ${sectionRotation}, simple vertices: ${simpleGeometryOffsetCount}, complex vertices: ${complexGeometryOffsetCount}`); + const simple_geometry_offset_table_offset = dv.getUint32(i + 32, true); + // const complex_geometry_offset_table_offset = dv.getUint32(i + 36, true); + const simple_geometry_offset_count = dv.getUint32(i + 40, true); + // const complex_geometry_offset_count = dv.getUint32(i + 44, true); for ( - let j = simpleGeometryOffsetTableOffset; - j < simpleGeometryOffsetTableOffset + simpleGeometryOffsetCount * 16; + let j = simple_geometry_offset_table_offset; + j < simple_geometry_offset_table_offset + simple_geometry_offset_count * 16; j += 16 ) { let offset = dv.getUint32(j, true); @@ -177,41 +164,39 @@ export function parseNRel( offset = dv.getUint32(offset, true); } - const geometryOffset = dv.getUint32(offset + 4, true); + const geometry_offset = dv.getUint32(offset + 4, true); - if (geometryOffset > 0) { - const vertexInfoTableOffset = dv.getUint32(geometryOffset + 4, true); - const vertexInfoCount = dv.getUint32(geometryOffset + 8, true); - const triangleStripTableOffset = dv.getUint32(geometryOffset + 12, true); - const triangleStripCount = dv.getUint32(geometryOffset + 16, true); - // const transparentObjectTableOffset = dv.getUint32(blockOffset + 20, true); - // const transparentObjectCount = dv.getUint32(blockOffset + 24, true); + if (geometry_offset > 0) { + const vertex_info_table_offset = dv.getUint32(geometry_offset + 4, true); + const vertex_info_count = dv.getUint32(geometry_offset + 8, true); + const triangle_strip_table_offset = dv.getUint32(geometry_offset + 12, true); + const triangle_strip_count = dv.getUint32(geometry_offset + 16, true); + // const transparent_object_table_offset = dv.getUint32(blockOffset + 20, true); + // const transparent_object_count = dv.getUint32(blockOffset + 24, true); - // logger.log(`block offset: ${blockOffset}, vertex info count: ${vertexInfoCount}, object table offset ${objectTableOffset}, object count: ${objectCount}, transparent object count: ${transparentObjectCount}`); - - const geomIndexLists = []; + const geom_index_lists = []; for ( - let k = triangleStripTableOffset; - k < triangleStripTableOffset + triangleStripCount * 20; + let k = triangle_strip_table_offset; + k < triangle_strip_table_offset + triangle_strip_count * 20; k += 20 ) { - // const flagAndTextureIdOffset = dv.getUint32(k, true); - // const dataType = dv.getUint32(k + 4, true); - const triangleStripIndexTableOffset = dv.getUint32(k + 8, true); - const triangleStripIndexCount = dv.getUint32(k + 12, true); + // const flag_and_texture_id_offset = dv.getUint32(k, true); + // const data_type = dv.getUint32(k + 4, true); + const triangle_strip_index_table_offset = dv.getUint32(k + 8, true); + const triangle_strip_index_count = dv.getUint32(k + 12, true); - const triangleStripIndices = []; + const triangle_strip_indices = []; for ( - let l = triangleStripIndexTableOffset; - l < triangleStripIndexTableOffset + triangleStripIndexCount * 2; + let l = triangle_strip_index_table_offset; + l < triangle_strip_index_table_offset + triangle_strip_index_count * 2; l += 2 ) { - triangleStripIndices.push(dv.getUint16(l, true)); + triangle_strip_indices.push(dv.getUint16(l, true)); } - geomIndexLists.push(triangleStripIndices); + geom_index_lists.push(triangle_strip_indices); // TODO: Read texture info. } @@ -219,66 +204,62 @@ export function parseNRel( // TODO: Do the previous for the transparent index table. // Assume vertexInfoCount == 1. TODO: Does that make sense? - if (vertexInfoCount > 1) { - logger.warn(`Vertex info count of ${vertexInfoCount} was larger than expected.`); + if (vertex_info_count > 1) { + logger.warn(`Vertex info count of ${vertex_info_count} was larger than expected.`); } - // const vertexType = dv.getUint32(vertexInfoTableOffset, true); - const vertexTableOffset = dv.getUint32(vertexInfoTableOffset + 4, true); - const vertexSize = dv.getUint32(vertexInfoTableOffset + 8, true); - const vertexCount = dv.getUint32(vertexInfoTableOffset + 12, true); + // const vertex_type = dv.getUint32(vertexInfoTableOffset, true); + const vertex_table_offset = dv.getUint32(vertex_info_table_offset + 4, true); + const vertex_size = dv.getUint32(vertex_info_table_offset + 8, true); + const vertex_count = dv.getUint32(vertex_info_table_offset + 12, true); - // logger.log(`vertex type: ${vertexType}, vertex size: ${vertexSize}, vertex count: ${vertexCount}`); - - const geomPositions = []; - const geomNormals = []; + const geom_positions = []; + const geom_normals = []; for ( - let k = vertexTableOffset; - k < vertexTableOffset + vertexCount * vertexSize; - k += vertexSize + let k = vertex_table_offset; + k < vertex_table_offset + vertex_count * vertex_size; + k += vertex_size ) { - let nX, nY, nZ; + let n_x, n_y, n_z; - switch (vertexSize) { + switch (vertex_size) { case 16: case 24: // TODO: are these values sensible? - nX = 0; - nY = 1; - nZ = 0; + n_x = 0; + n_y = 1; + n_z = 0; break; case 28: case 36: - nX = dv.getFloat32(k + 12, true); - nY = dv.getFloat32(k + 16, true); - nZ = dv.getFloat32(k + 20, true); + n_x = dv.getFloat32(k + 12, true); + n_y = dv.getFloat32(k + 16, true); + n_z = dv.getFloat32(k + 20, true); // TODO: color, texture coords. break; default: - logger.error(`Unexpected vertex size of ${vertexSize}.`); + logger.error(`Unexpected vertex size of ${vertex_size}.`); continue; } const x = dv.getFloat32(k, true); const y = dv.getFloat32(k + 4, true); const z = dv.getFloat32(k + 8, true); - const rotatedX = section.cos_y_axis_rotation * x + section.sin_y_axis_rotation * z; - const rotatedZ = -section.sin_y_axis_rotation * x + section.cos_y_axis_rotation * z; + const rotated_x = section.cos_y_axis_rotation * x + section.sin_y_axis_rotation * z; + const rotated_z = -section.sin_y_axis_rotation * x + section.cos_y_axis_rotation * z; - geomPositions.push(sectionX + rotatedX); - geomPositions.push(sectionY + y); - geomPositions.push(sectionZ + rotatedZ); - geomNormals.push(nX); - geomNormals.push(nY); - geomNormals.push(nZ); + geom_positions.push(section_x + rotated_x); + geom_positions.push(section_y + y); + geom_positions.push(section_z + rotated_z); + geom_normals.push(n_x); + geom_normals.push(n_y); + geom_normals.push(n_z); } - indexListsList.push(geomIndexLists); - positionListsList.push(geomPositions); - normalListsList.push(geomNormals); - } else { - // logger.error(`Block offset at ${offset + 4} was ${blockOffset}.`); + index_lists_list.push(geom_index_lists); + position_lists_list.push(geom_positions); + normal_lists_list.push(geom_normals); } } @@ -286,13 +267,13 @@ export function parseNRel( // return v[0] === w[0] && v[1] === w[1] && v[2] === w[2]; // } - for (let i = 0; i < positionListsList.length; ++i) { - const positions = positionListsList[i]; - const normals = normalListsList[i]; - const geomIndexLists = indexListsList[i]; + for (let i = 0; i < position_lists_list.length; ++i) { + const positions = position_lists_list[i]; + const normals = normal_lists_list[i]; + const geom_index_lists = index_lists_list[i]; // const indices = []; - geomIndexLists.forEach(objectIndices => { + geom_index_lists.forEach(object_indices => { // for (let j = 2; j < objectIndices.length; ++j) { // const a = objectIndices[j - 2]; // const b = objectIndices[j - 1]; @@ -318,11 +299,9 @@ export function parseNRel( // } const geometry = new BufferGeometry(); - geometry.addAttribute( - 'position', new BufferAttribute(new Float32Array(positions), 3)); - geometry.addAttribute( - 'normal', new BufferAttribute(new Float32Array(normals), 3)); - geometry.setIndex(new BufferAttribute(new Uint16Array(objectIndices), 1)); + geometry.addAttribute('position', new Float32BufferAttribute(positions, 3)); + geometry.addAttribute('normal', new Float32BufferAttribute(normals, 3)); + geometry.setIndex(new Uint16BufferAttribute(object_indices, 1)); const mesh = new Mesh( geometry, @@ -372,6 +351,6 @@ export function parseNRel( return { sections: [...sections.values()].sort((a, b) => a.id - b.id), - object3d: object + object_3d: object }; } diff --git a/src/data_formats/parsing/itempmt.ts b/src/data_formats/parsing/itempmt.ts index e3f0e75c..e2012f1b 100644 --- a/src/data_formats/parsing/itempmt.ts +++ b/src/data_formats/parsing/itempmt.ts @@ -1,7 +1,7 @@ import { BufferCursor } from "../BufferCursor"; export type ItemPmt = { - statBoosts: PmtStatBoost[], + stat_boosts: PmtStatBoost[], armors: PmtArmor[], shields: PmtShield[], units: PmtUnit[], @@ -10,63 +10,63 @@ export type ItemPmt = { } export type PmtStatBoost = { - stat1: number, - stat2: number, - amount1: number, - amount2: number, + stat_1: number, + stat_2: number, + amount_1: number, + amount_2: number, } export type PmtWeapon = { id: number, type: number, skin: number, - teamPoints: number, + team_points: number, class: number, - reserved1: number, - minAtp: number, - maxAtp: number, - reqAtp: number, - reqMst: number, - reqAta: number, + reserved_1: number, + min_atp: number, + max_atp: number, + req_atp: number, + req_mst: number, + req_ata: number, mst: number, - maxGrind: number, + max_grind: number, photon: number, special: number, ata: number, - statBoost: number, + stat_boost: number, projectile: number, - photonTrail1X: number, - photonTrail1Y: number, - photonTrail2X: number, - photonTrail2Y: number, - photonType: number, - unknown1: number[], - techBoost: number, - comboType: number, + photon_trail_1_x: number, + photon_trail_1_y: number, + photon_trail_2_x: number, + photon_trail_2_y: number, + photon_type: number, + unknown_1: number[], + tech_boost: number, + combo_type: number, } export type PmtArmor = { id: number, type: number, skin: number, - teamPoints: number, + team_points: number, dfp: number, evp: number, - blockParticle: number, - blockEffect: number, + block_particle: number, + block_effect: number, class: number, - reserved1: number, - requiredLevel: number, + reserved_1: number, + required_level: number, efr: number, eth: number, eic: number, edk: number, elt: number, - dfpRange: number, - evpRange: number, - statBoost: number, - techBoost: number, - unknown1: number, + dfp_range: number, + evp_range: number, + stat_boost: number, + tech_boost: number, + unknown_1: number, } export type PmtShield = PmtArmor @@ -75,10 +75,10 @@ export type PmtUnit = { id: number, type: number, skin: number, - teamPoints: number, + team_points: number, stat: number, - statAmount: number, - plusMinus: number, + stat_amount: number, + plus_minus: number, reserved: number[] } @@ -86,74 +86,74 @@ export type PmtTool = { id: number, type: number, skin: number, - teamPoints: number, + team_points: number, amount: number, tech: number, cost: number, - itemFlag: number, + item_flag: number, reserved: number[], } -export function parseItemPmt(cursor: BufferCursor): ItemPmt { +export function parse_item_pmt(cursor: BufferCursor): ItemPmt { cursor.seek_end(32); - const mainTableOffset = cursor.u32(); - const mainTableSize = cursor.u32(); - // const mainTableCount = cursor.u32(); // Should be 1. + const main_table_offset = cursor.u32(); + const main_table_size = cursor.u32(); + // const main_table_count = cursor.u32(); // Should be 1. - cursor.seek_start(mainTableOffset); + cursor.seek_start(main_table_offset); - const compactTableOffsets = cursor.u16_array(mainTableSize); - const tableOffsets: { offset: number, size: number }[] = []; - let expandedOffset: number = 0; + const compact_table_offsets = cursor.u16_array(main_table_size); + const table_offsets: { offset: number, size: number }[] = []; + let expanded_offset: number = 0; - for (const compactOffset of compactTableOffsets) { - expandedOffset = expandedOffset + 4 * compactOffset; - cursor.seek_start(expandedOffset - 4); + for (const compact_offset of compact_table_offsets) { + expanded_offset = expanded_offset + 4 * compact_offset; + cursor.seek_start(expanded_offset - 4); const size = cursor.u32(); const offset = cursor.u32(); - tableOffsets.push({ offset, size }); + table_offsets.push({ offset, size }); } - const itemPmt: ItemPmt = { + const item_pmt: ItemPmt = { // This size (65268) of this table seems wrong, so we pass in a hard-coded value. - statBoosts: parseStatBoosts(cursor, tableOffsets[305].offset, 52), - armors: parseArmors(cursor, tableOffsets[7].offset, tableOffsets[7].size), - shields: parseShields(cursor, tableOffsets[8].offset, tableOffsets[8].size), - units: parseUnits(cursor, tableOffsets[9].offset, tableOffsets[9].size), + stat_boosts: parse_stat_boosts(cursor, table_offsets[305].offset, 52), + armors: parse_armors(cursor, table_offsets[7].offset, table_offsets[7].size), + shields: parse_shields(cursor, table_offsets[8].offset, table_offsets[8].size), + units: parse_units(cursor, table_offsets[9].offset, table_offsets[9].size), tools: [], weapons: [], }; for (let i = 11; i <= 37; i++) { - itemPmt.tools.push(parseTools(cursor, tableOffsets[i].offset, tableOffsets[i].size)); + item_pmt.tools.push(parse_tools(cursor, table_offsets[i].offset, table_offsets[i].size)); } for (let i = 38; i <= 275; i++) { - itemPmt.weapons.push( - parseWeapons(cursor, tableOffsets[i].offset, tableOffsets[i].size) + item_pmt.weapons.push( + parse_weapons(cursor, table_offsets[i].offset, table_offsets[i].size) ); } - return itemPmt; + return item_pmt; } -function parseStatBoosts(cursor: BufferCursor, offset: number, size: number): PmtStatBoost[] { +function parse_stat_boosts(cursor: BufferCursor, offset: number, size: number): PmtStatBoost[] { cursor.seek_start(offset); - const statBoosts: PmtStatBoost[] = []; + const stat_boosts: PmtStatBoost[] = []; for (let i = 0; i < size; i++) { - statBoosts.push({ - stat1: cursor.u8(), - stat2: cursor.u8(), - amount1: cursor.i16(), - amount2: cursor.i16(), + stat_boosts.push({ + stat_1: cursor.u8(), + stat_2: cursor.u8(), + amount_1: cursor.i16(), + amount_2: cursor.i16(), }); } - return statBoosts; + return stat_boosts; } -function parseWeapons(cursor: BufferCursor, offset: number, size: number): PmtWeapon[] { +function parse_weapons(cursor: BufferCursor, offset: number, size: number): PmtWeapon[] { cursor.seek_start(offset); const weapons: PmtWeapon[] = []; @@ -162,36 +162,36 @@ function parseWeapons(cursor: BufferCursor, offset: number, size: number): PmtWe id: cursor.u32(), type: cursor.i16(), skin: cursor.i16(), - teamPoints: cursor.i32(), + team_points: cursor.i32(), class: cursor.u8(), - reserved1: cursor.u8(), - minAtp: cursor.i16(), - maxAtp: cursor.i16(), - reqAtp: cursor.i16(), - reqMst: cursor.i16(), - reqAta: cursor.i16(), + reserved_1: cursor.u8(), + min_atp: cursor.i16(), + max_atp: cursor.i16(), + req_atp: cursor.i16(), + req_mst: cursor.i16(), + req_ata: cursor.i16(), mst: cursor.i16(), - maxGrind: cursor.u8(), + max_grind: cursor.u8(), photon: cursor.i8(), special: cursor.u8(), ata: cursor.u8(), - statBoost: cursor.u8(), + stat_boost: cursor.u8(), projectile: cursor.u8(), - photonTrail1X: cursor.i8(), - photonTrail1Y: cursor.i8(), - photonTrail2X: cursor.i8(), - photonTrail2Y: cursor.i8(), - photonType: cursor.i8(), - unknown1: cursor.u8_array(5), - techBoost: cursor.u8(), - comboType: cursor.u8(), + photon_trail_1_x: cursor.i8(), + photon_trail_1_y: cursor.i8(), + photon_trail_2_x: cursor.i8(), + photon_trail_2_y: cursor.i8(), + photon_type: cursor.i8(), + unknown_1: cursor.u8_array(5), + tech_boost: cursor.u8(), + combo_type: cursor.u8(), }); } return weapons; } -function parseArmors(cursor: BufferCursor, offset: number, size: number): PmtArmor[] { +function parse_armors(cursor: BufferCursor, offset: number, size: number): PmtArmor[] { cursor.seek_start(offset); const armors: PmtArmor[] = []; @@ -200,35 +200,35 @@ function parseArmors(cursor: BufferCursor, offset: number, size: number): PmtArm id: cursor.u32(), type: cursor.i16(), skin: cursor.i16(), - teamPoints: cursor.i32(), + team_points: cursor.i32(), dfp: cursor.i16(), evp: cursor.i16(), - blockParticle: cursor.u8(), - blockEffect: cursor.u8(), + block_particle: cursor.u8(), + block_effect: cursor.u8(), class: cursor.u8(), - reserved1: cursor.u8(), - requiredLevel: cursor.u8(), + reserved_1: cursor.u8(), + required_level: cursor.u8(), efr: cursor.u8(), eth: cursor.u8(), eic: cursor.u8(), edk: cursor.u8(), elt: cursor.u8(), - dfpRange: cursor.u8(), - evpRange: cursor.u8(), - statBoost: cursor.u8(), - techBoost: cursor.u8(), - unknown1: cursor.i16(), + dfp_range: cursor.u8(), + evp_range: cursor.u8(), + stat_boost: cursor.u8(), + tech_boost: cursor.u8(), + unknown_1: cursor.i16(), }); } return armors; } -function parseShields(cursor: BufferCursor, offset: number, size: number): PmtShield[] { - return parseArmors(cursor, offset, size); +function parse_shields(cursor: BufferCursor, offset: number, size: number): PmtShield[] { + return parse_armors(cursor, offset, size); } -function parseUnits(cursor: BufferCursor, offset: number, size: number): PmtUnit[] { +function parse_units(cursor: BufferCursor, offset: number, size: number): PmtUnit[] { cursor.seek_start(offset); const units: PmtUnit[] = []; @@ -237,10 +237,10 @@ function parseUnits(cursor: BufferCursor, offset: number, size: number): PmtUnit id: cursor.u32(), type: cursor.i16(), skin: cursor.i16(), - teamPoints: cursor.i32(), + team_points: cursor.i32(), stat: cursor.i16(), - statAmount: cursor.i16(), - plusMinus: cursor.u8(), + stat_amount: cursor.i16(), + plus_minus: cursor.u8(), reserved: cursor.u8_array(3), }); } @@ -248,7 +248,7 @@ function parseUnits(cursor: BufferCursor, offset: number, size: number): PmtUnit return units; } -function parseTools(cursor: BufferCursor, offset: number, size: number): PmtTool[] { +function parse_tools(cursor: BufferCursor, offset: number, size: number): PmtTool[] { cursor.seek_start(offset); const tools: PmtTool[] = []; @@ -257,11 +257,11 @@ function parseTools(cursor: BufferCursor, offset: number, size: number): PmtTool id: cursor.u32(), type: cursor.i16(), skin: cursor.i16(), - teamPoints: cursor.i32(), + team_points: cursor.i32(), amount: cursor.i16(), tech: cursor.i16(), cost: cursor.i32(), - itemFlag: cursor.u8(), + item_flag: cursor.u8(), reserved: cursor.u8_array(3), }); } diff --git a/src/data_formats/parsing/ninja/index.ts b/src/data_formats/parsing/ninja/index.ts index 99b8f132..582b5a39 100644 --- a/src/data_formats/parsing/ninja/index.ts +++ b/src/data_formats/parsing/ninja/index.ts @@ -1,4 +1,4 @@ -import { Vec3 } from '../../../domain'; +import { Vec3 } from "../../Vec3"; import { BufferCursor } from '../../BufferCursor'; import { NjModel, parse_nj_model } from './nj'; import { parse_xj_model, XjModel } from './xj'; diff --git a/src/data_formats/parsing/ninja/motion.ts b/src/data_formats/parsing/ninja/motion.ts index ee93f5ce..83647199 100644 --- a/src/data_formats/parsing/ninja/motion.ts +++ b/src/data_formats/parsing/ninja/motion.ts @@ -1,5 +1,5 @@ import { BufferCursor } from '../../BufferCursor'; -import { Vec3 } from '../../../domain'; +import { Vec3 } from "../../Vec3"; const ANGLE_TO_RAD = 2 * Math.PI / 0xFFFF; diff --git a/src/data_formats/parsing/ninja/nj.ts b/src/data_formats/parsing/ninja/nj.ts index cce73ad3..87480b0e 100644 --- a/src/data_formats/parsing/ninja/nj.ts +++ b/src/data_formats/parsing/ninja/nj.ts @@ -1,6 +1,6 @@ import Logger from 'js-logger'; import { BufferCursor } from '../../BufferCursor'; -import { Vec3 } from '../../../domain'; +import { Vec3 } from "../../Vec3"; import { NinjaVertex } from '../ninja'; const logger = Logger.get('data_formats/parsing/ninja/nj'); diff --git a/src/data_formats/parsing/ninja/xj.ts b/src/data_formats/parsing/ninja/xj.ts index c9c86ea8..3765fed7 100644 --- a/src/data_formats/parsing/ninja/xj.ts +++ b/src/data_formats/parsing/ninja/xj.ts @@ -1,5 +1,5 @@ import { BufferCursor } from '../../BufferCursor'; -import { Vec3 } from '../../../domain'; +import { Vec3 } from "../../Vec3"; import { NinjaVertex } from '../ninja'; // TODO: diff --git a/src/data_formats/parsing/quest/bin.test.ts b/src/data_formats/parsing/quest/bin.test.ts index e973d38f..a321e754 100644 --- a/src/data_formats/parsing/quest/bin.test.ts +++ b/src/data_formats/parsing/quest/bin.test.ts @@ -1,23 +1,23 @@ import * as fs from 'fs'; import { BufferCursor } from '../../BufferCursor'; import * as prs from '../../compression/prs'; -import { parseBin, writeBin } from './bin'; +import { parse_bin, write_bin } from './bin'; /** * Parse a file, convert the resulting structure to BIN again and check whether the end result is equal to the original. */ -test('parseBin and writeBin', () => { - const origBuffer = fs.readFileSync('test/resources/quest118_e.bin').buffer; - const origBin = prs.decompress(new BufferCursor(origBuffer, true)); - const testBin = writeBin(parseBin(origBin)); - origBin.seek_start(0); +test('parse_bin and write_bin', () => { + const orig_buffer = fs.readFileSync('test/resources/quest118_e.bin').buffer; + const orig_bin = prs.decompress(new BufferCursor(orig_buffer, true)); + const test_bin = write_bin(parse_bin(orig_bin)); + orig_bin.seek_start(0); - expect(testBin.size).toBe(origBin.size); + expect(test_bin.size).toBe(orig_bin.size); let match = true; - while (origBin.bytes_left) { - if (testBin.u8() !== origBin.u8()) { + while (orig_bin.bytes_left) { + if (test_bin.u8() !== orig_bin.u8()) { match = false; break; } diff --git a/src/data_formats/parsing/quest/bin.ts b/src/data_formats/parsing/quest/bin.ts index 7b5c00cf..a9846ce4 100644 --- a/src/data_formats/parsing/quest/bin.ts +++ b/src/data_formats/parsing/quest/bin.ts @@ -4,59 +4,59 @@ import Logger from 'js-logger'; const logger = Logger.get('data_formats/parsing/quest/bin'); export interface BinFile { - questNumber: number; + quest_id: number; language: number; - questName: string; - shortDescription: string; - longDescription: string; - functionOffsets: number[]; + quest_name: string; + short_description: string; + long_description: string; + function_offsets: number[]; instructions: Instruction[]; data: BufferCursor; } -export function parseBin(cursor: BufferCursor, lenient: boolean = false): BinFile { - const objectCodeOffset = cursor.u32(); - const functionOffsetTableOffset = cursor.u32(); // Relative offsets +export function parse_bin(cursor: BufferCursor, lenient: boolean = false): BinFile { + const object_code_offset = cursor.u32(); + const function_offset_table_offset = cursor.u32(); // Relative offsets const size = cursor.u32(); cursor.seek(4); // Always seems to be 0xFFFFFFFF - const questNumber = cursor.u32(); + const quest_id = cursor.u32(); const language = cursor.u32(); - const questName = cursor.string_utf16(64, true, true); - const shortDescription = cursor.string_utf16(256, true, true); - const longDescription = cursor.string_utf16(576, true, true); + const quest_name = cursor.string_utf16(64, true, true); + const short_description = cursor.string_utf16(256, true, true); + const long_description = cursor.string_utf16(576, true, true); if (size !== cursor.size) { logger.warn(`Value ${size} in bin size field does not match actual size ${cursor.size}.`); } - const functionOffsetCount = Math.floor( - (cursor.size - functionOffsetTableOffset) / 4); + const function_offset_count = Math.floor( + (cursor.size - function_offset_table_offset) / 4); - cursor.seek_start(functionOffsetTableOffset); - const functionOffsets = []; + cursor.seek_start(function_offset_table_offset); + const function_offsets = []; - for (let i = 0; i < functionOffsetCount; ++i) { - functionOffsets.push(cursor.i32()); + for (let i = 0; i < function_offset_count; ++i) { + function_offsets.push(cursor.i32()); } - const instructions = parseObjectCode( - cursor.seek_start(objectCodeOffset).take(functionOffsetTableOffset - objectCodeOffset), + const instructions = parse_object_code( + cursor.seek_start(object_code_offset).take(function_offset_table_offset - object_code_offset), lenient ); return { - questNumber, + quest_id, language, - questName, - shortDescription, - longDescription, - functionOffsets, + quest_name, + short_description, + long_description, + function_offsets, instructions, data: cursor.seek_start(0).take(cursor.size) }; } -export function writeBin({ data }: { data: BufferCursor }): BufferCursor { +export function write_bin({ data }: { data: BufferCursor }): BufferCursor { return data.seek_start(0); } @@ -67,44 +67,44 @@ export interface Instruction { size: number; } -function parseObjectCode(cursor: BufferCursor, lenient: boolean): Instruction[] { +function parse_object_code(cursor: BufferCursor, lenient: boolean): Instruction[] { const instructions = []; try { while (cursor.bytes_left) { - const mainOpcode = cursor.u8(); + const main_opcode = cursor.u8(); let opcode; let opsize; let list; - switch (mainOpcode) { + switch (main_opcode) { case 0xF8: opcode = cursor.u8(); opsize = 2; - list = F8opcodeList; + list = f8_opcode_list; break; case 0xF9: opcode = cursor.u8(); opsize = 2; - list = F9opcodeList; + list = f9_opcode_list; break; default: - opcode = mainOpcode; + opcode = main_opcode; opsize = 1; - list = opcodeList; + list = opcode_list; break; } let [, mnemonic, mask] = list[opcode]; if (mask == null) { - let fullOpcode = mainOpcode; + let full_opcode = main_opcode; - if (mainOpcode === 0xF8 || mainOpcode === 0xF9) { - fullOpcode = (fullOpcode << 8) | opcode; + if (main_opcode === 0xF8 || main_opcode === 0xF9) { + full_opcode = (full_opcode << 8) | opcode; } - logger.warn(`Parameters unknown for opcode 0x${fullOpcode.toString(16).toUpperCase()}, assuming 0.`); + logger.warn(`Parameters unknown for opcode 0x${full_opcode.toString(16).toUpperCase()}, assuming 0.`); instructions.push({ opcode, @@ -114,7 +114,7 @@ function parseObjectCode(cursor: BufferCursor, lenient: boolean): Instruction[] }); } else { try { - const opargs = parseInstructionArguments(cursor, mask); + const opargs = parse_instruction_arguments(cursor, mask); instructions.push({ opcode, @@ -143,13 +143,13 @@ function parseObjectCode(cursor: BufferCursor, lenient: boolean): Instruction[] return instructions; } -function parseInstructionArguments( +function parse_instruction_arguments( cursor: BufferCursor, mask: string ): { args: any[], size: number } { - const oldPos = cursor.position; + const old_pos = cursor.position; const args = []; - let argsSize: number; + let args_size: number; outer: for (let i = 0; i < mask.length; ++i) { @@ -208,13 +208,13 @@ function parseInstructionArguments( // Variably sized data? case 'j': case 'J': - argsSize = 2 * cursor.u8(); - cursor.seek(argsSize); + args_size = 2 * cursor.u8(); + cursor.seek(args_size); break; case 't': case 'T': - argsSize = cursor.u8(); - cursor.seek(argsSize); + args_size = cursor.u8(); + cursor.seek(args_size); break; // Strings @@ -228,10 +228,10 @@ function parseInstructionArguments( } } - return { args, size: cursor.position - oldPos }; + return { args, size: cursor.position - old_pos }; } -const opcodeList: Array<[number, string, string | null]> = [ +const opcode_list: Array<[number, string, string | null]> = [ [0x00, 'nop', ''], [0x01, 'ret', ''], [0x02, 'sync', ''], @@ -512,7 +512,7 @@ const opcodeList: Array<[number, string, string | null]> = [ [0xFF, 'unknownFF', ''], ]; -const F8opcodeList: Array<[number, string, string | null]> = [ +const f8_opcode_list: Array<[number, string, string | null]> = [ [0x00, 'unknown', null], [0x01, 'set_chat_callback?', 'aRs'], [0x02, 'unknown', null], @@ -771,7 +771,7 @@ const F8opcodeList: Array<[number, string, string | null]> = [ [0xFF, 'unknown', null], ]; -const F9opcodeList: Array<[number, string, string | null]> = [ +const f9_opcode_list: Array<[number, string, string | null]> = [ [0x00, 'unknown', null], [0x01, 'dec2float', 'RR'], [0x02, 'float2dec', 'RR'], diff --git a/src/data_formats/parsing/quest/dat.test.ts b/src/data_formats/parsing/quest/dat.test.ts index da81456b..6fcb6066 100644 --- a/src/data_formats/parsing/quest/dat.test.ts +++ b/src/data_formats/parsing/quest/dat.test.ts @@ -1,23 +1,23 @@ import * as fs from 'fs'; import { BufferCursor } from '../../BufferCursor'; import * as prs from '../../compression/prs'; -import { parseDat, writeDat } from './dat'; +import { parse_dat, write_dat } from './dat'; /** * Parse a file, convert the resulting structure to DAT again and check whether the end result is equal to the original. */ -test('parseDat and writeDat', () => { - const origBuffer = fs.readFileSync('test/resources/quest118_e.dat').buffer; - const origDat = prs.decompress(new BufferCursor(origBuffer, true)); - const testDat = writeDat(parseDat(origDat)); - origDat.seek_start(0); +test('parse_dat and write_dat', () => { + const orig_buffer = fs.readFileSync('test/resources/quest118_e.dat').buffer; + const orig_dat = prs.decompress(new BufferCursor(orig_buffer, true)); + const test_dat = write_dat(parse_dat(orig_dat)); + orig_dat.seek_start(0); - expect(testDat.size).toBe(origDat.size); + expect(test_dat.size).toBe(orig_dat.size); let match = true; - while (origDat.bytes_left) { - if (testDat.u8() !== origDat.u8()) { + while (orig_dat.bytes_left) { + if (test_dat.u8() !== orig_dat.u8()) { match = false; break; } @@ -30,29 +30,29 @@ test('parseDat and writeDat', () => { * Parse a file, modify the resulting structure, convert it to DAT again and check whether the end result is equal to the original except for the bytes that should be changed. */ test('parse, modify and write DAT', () => { - const origBuffer = fs.readFileSync('./test/resources/quest118_e.dat').buffer; - const origDat = prs.decompress(new BufferCursor(origBuffer, true)); - const testParsed = parseDat(origDat); - origDat.seek_start(0); + const orig_buffer = fs.readFileSync('./test/resources/quest118_e.dat').buffer; + const orig_dat = prs.decompress(new BufferCursor(orig_buffer, true)); + const test_parsed = parse_dat(orig_dat); + orig_dat.seek_start(0); - testParsed.objs[9].position.x = 13; - testParsed.objs[9].position.y = 17; - testParsed.objs[9].position.z = 19; + test_parsed.objs[9].position.x = 13; + test_parsed.objs[9].position.y = 17; + test_parsed.objs[9].position.z = 19; - const testDat = writeDat(testParsed); + const test_dat = write_dat(test_parsed); - expect(testDat.size).toBe(origDat.size); + expect(test_dat.size).toBe(orig_dat.size); let match = true; - while (origDat.bytes_left) { - if (origDat.position === 16 + 9 * 68 + 16) { - origDat.seek(12); + while (orig_dat.bytes_left) { + if (orig_dat.position === 16 + 9 * 68 + 16) { + orig_dat.seek(12); - expect(testDat.f32()).toBe(13); - expect(testDat.f32()).toBe(17); - expect(testDat.f32()).toBe(19); - } else if (testDat.u8() !== origDat.u8()) { + expect(test_dat.f32()).toBe(13); + expect(test_dat.f32()).toBe(17); + expect(test_dat.f32()).toBe(19); + } else if (test_dat.u8() !== orig_dat.u8()) { match = false; break; } diff --git a/src/data_formats/parsing/quest/dat.ts b/src/data_formats/parsing/quest/dat.ts index 555aecab..3b9ee865 100644 --- a/src/data_formats/parsing/quest/dat.ts +++ b/src/data_formats/parsing/quest/dat.ts @@ -1,110 +1,110 @@ import { groupBy } from 'lodash'; import { BufferCursor } from '../../BufferCursor'; import Logger from 'js-logger'; +import { Vec3 } from '../../Vec3'; const logger = Logger.get('data_formats/parsing/quest/dat'); const OBJECT_SIZE = 68; const NPC_SIZE = 72; -export interface DatFile { - objs: DatObject[]; - npcs: DatNpc[]; - unknowns: DatUnknown[]; +export type DatFile = { + objs: DatObject[], + npcs: DatNpc[], + unknowns: DatUnknown[], } -interface DatEntity { - typeId: number; - sectionId: number; - position: { x: number, y: number, z: number }; - rotation: { x: number, y: number, z: number }; - areaId: number; - unknown: number[][]; +export type DatEntity = { + type_id: number, + section_id: number, + position: Vec3, + rotation: Vec3, + area_id: number, + unknown: number[][], } -export interface DatObject extends DatEntity { +export type DatObject = DatEntity + +export type DatNpc = DatEntity & { + flags: number, + skin: number, } -export interface DatNpc extends DatEntity { - flags: number; - skin: number; +export type DatUnknown = { + entity_type: number, + total_size: number, + area_id: number, + entities_size: number, + data: number[], } -export interface DatUnknown { - entityType: number; - totalSize: number; - areaId: number; - entitiesSize: number; - data: number[]; -} - -export function parseDat(cursor: BufferCursor): DatFile { +export function parse_dat(cursor: BufferCursor): DatFile { const objs: DatObject[] = []; const npcs: DatNpc[] = []; const unknowns: DatUnknown[] = []; while (cursor.bytes_left) { - const entityType = cursor.u32(); - const totalSize = cursor.u32(); - const areaId = cursor.u32(); - const entitiesSize = cursor.u32(); + const entity_type = cursor.u32(); + const total_size = cursor.u32(); + const area_id = cursor.u32(); + const entities_size = cursor.u32(); - if (entityType === 0) { + if (entity_type === 0) { break; } else { - if (entitiesSize !== totalSize - 16) { - throw Error(`Malformed DAT file. Expected an entities size of ${totalSize - 16}, got ${entitiesSize}.`); + if (entities_size !== total_size - 16) { + throw Error(`Malformed DAT file. Expected an entities size of ${total_size - 16}, got ${entities_size}.`); } - if (entityType === 1) { // Objects - const objectCount = Math.floor(entitiesSize / OBJECT_SIZE); - const startPosition = cursor.position; + if (entity_type === 1) { // Objects + const object_count = Math.floor(entities_size / OBJECT_SIZE); + const start_position = cursor.position; - for (let i = 0; i < objectCount; ++i) { - const typeId = cursor.u16(); + for (let i = 0; i < object_count; ++i) { + const type_id = cursor.u16(); const unknown1 = cursor.u8_array(10); - const sectionId = cursor.u16(); + const section_id = cursor.u16(); const unknown2 = cursor.u8_array(2); const x = cursor.f32(); const y = cursor.f32(); const z = cursor.f32(); - const rotationX = cursor.i32() / 0xFFFF * 2 * Math.PI; - const rotationY = cursor.i32() / 0xFFFF * 2 * Math.PI; - const rotationZ = cursor.i32() / 0xFFFF * 2 * Math.PI; + const rotation_x = cursor.i32() / 0xFFFF * 2 * Math.PI; + const rotation_y = cursor.i32() / 0xFFFF * 2 * Math.PI; + const rotation_z = cursor.i32() / 0xFFFF * 2 * Math.PI; // The next 3 floats seem to be scale values. const unknown3 = cursor.u8_array(28); objs.push({ - typeId, - sectionId, - position: { x, y, z }, - rotation: { x: rotationX, y: rotationY, z: rotationZ }, - areaId, + type_id, + section_id, + position: new Vec3(x, y, z), + rotation: new Vec3(rotation_x, rotation_y, rotation_z), + area_id, unknown: [unknown1, unknown2, unknown3] }); } - const bytesRead = cursor.position - startPosition; + const bytes_read = cursor.position - start_position; - if (bytesRead !== entitiesSize) { - logger.warn(`Read ${bytesRead} bytes instead of expected ${entitiesSize} for entity type ${entityType} (Object).`); - cursor.seek(entitiesSize - bytesRead); + if (bytes_read !== entities_size) { + logger.warn(`Read ${bytes_read} bytes instead of expected ${entities_size} for entity type ${entity_type} (Object).`); + cursor.seek(entities_size - bytes_read); } - } else if (entityType === 2) { // NPCs - const npcCount = Math.floor(entitiesSize / NPC_SIZE); - const startPosition = cursor.position; + } else if (entity_type === 2) { // NPCs + const npc_count = Math.floor(entities_size / NPC_SIZE); + const start_position = cursor.position; - for (let i = 0; i < npcCount; ++i) { - const typeId = cursor.u16(); + for (let i = 0; i < npc_count; ++i) { + const type_id = cursor.u16(); const unknown1 = cursor.u8_array(10); - const sectionId = cursor.u16(); + const section_id = cursor.u16(); const unknown2 = cursor.u8_array(6); const x = cursor.f32(); const y = cursor.f32(); const z = cursor.f32(); - const rotationX = cursor.i32() / 0xFFFF * 2 * Math.PI; - const rotationY = cursor.i32() / 0xFFFF * 2 * Math.PI; - const rotationZ = cursor.i32() / 0xFFFF * 2 * Math.PI; + const rotation_x = cursor.i32() / 0xFFFF * 2 * Math.PI; + const rotation_y = cursor.i32() / 0xFFFF * 2 * Math.PI; + const rotation_z = cursor.i32() / 0xFFFF * 2 * Math.PI; const unknown3 = cursor.u8_array(4); const flags = cursor.f32(); const unknown4 = cursor.u8_array(12); @@ -112,31 +112,31 @@ export function parseDat(cursor: BufferCursor): DatFile { const unknown5 = cursor.u8_array(4); npcs.push({ - typeId, - sectionId, - position: { x, y, z }, - rotation: { x: rotationX, y: rotationY, z: rotationZ }, + type_id, + section_id, + position: new Vec3(x, y, z), + rotation: new Vec3(rotation_x, rotation_y, rotation_z), skin, - areaId, + area_id, flags, unknown: [unknown1, unknown2, unknown3, unknown4, unknown5] }); } - const bytesRead = cursor.position - startPosition; + const bytes_read = cursor.position - start_position; - if (bytesRead !== entitiesSize) { - logger.warn(`Read ${bytesRead} bytes instead of expected ${entitiesSize} for entity type ${entityType} (NPC).`); - cursor.seek(entitiesSize - bytesRead); + if (bytes_read !== entities_size) { + logger.warn(`Read ${bytes_read} bytes instead of expected ${entities_size} for entity type ${entity_type} (NPC).`); + cursor.seek(entities_size - bytes_read); } } else { // There are also waves (type 3) and unknown entity types 4 and 5. unknowns.push({ - entityType, - totalSize, - areaId, - entitiesSize, - data: cursor.u8_array(entitiesSize) + entity_type, + total_size, + area_id, + entities_size, + data: cursor.u8_array(entities_size) }); } } @@ -145,27 +145,29 @@ export function parseDat(cursor: BufferCursor): DatFile { return { objs, npcs, unknowns }; } -export function writeDat({ objs, npcs, unknowns }: DatFile): BufferCursor { +export function write_dat({ objs, npcs, unknowns }: DatFile): BufferCursor { const cursor = new BufferCursor( - objs.length * OBJECT_SIZE + npcs.length * NPC_SIZE + unknowns.length * 1000, true); + objs.length * OBJECT_SIZE + npcs.length * NPC_SIZE + unknowns.length * 1000, + true + ); - const groupedObjs = groupBy(objs, obj => obj.areaId); - const objAreaIds = Object.keys(groupedObjs) + const grouped_objs = groupBy(objs, obj => obj.area_id); + const obj_area_ids = Object.keys(grouped_objs) .map(key => parseInt(key, 10)) .sort((a, b) => a - b); - for (const areaId of objAreaIds) { - const areaObjs = groupedObjs[areaId]; - const entitiesSize = areaObjs.length * OBJECT_SIZE; + for (const area_id of obj_area_ids) { + const area_objs = grouped_objs[area_id]; + const entities_size = area_objs.length * OBJECT_SIZE; cursor.write_u32(1); // Entity type - cursor.write_u32(entitiesSize + 16); - cursor.write_u32(areaId); - cursor.write_u32(entitiesSize); + cursor.write_u32(entities_size + 16); + cursor.write_u32(area_id); + cursor.write_u32(entities_size); - for (const obj of areaObjs) { - cursor.write_u16(obj.typeId); + for (const obj of area_objs) { + cursor.write_u16(obj.type_id); cursor.write_u8_array(obj.unknown[0]); - cursor.write_u16(obj.sectionId); + cursor.write_u16(obj.section_id); cursor.write_u8_array(obj.unknown[1]); cursor.write_f32(obj.position.x); cursor.write_f32(obj.position.y); @@ -177,23 +179,23 @@ export function writeDat({ objs, npcs, unknowns }: DatFile): BufferCursor { } } - const groupedNpcs = groupBy(npcs, npc => npc.areaId); - const npcAreaIds = Object.keys(groupedNpcs) + const grouped_npcs = groupBy(npcs, npc => npc.area_id); + const npc_area_ids = Object.keys(grouped_npcs) .map(key => parseInt(key, 10)) .sort((a, b) => a - b); - for (const areaId of npcAreaIds) { - const areaNpcs = groupedNpcs[areaId]; - const entitiesSize = areaNpcs.length * NPC_SIZE; + for (const area_id of npc_area_ids) { + const area_npcs = grouped_npcs[area_id]; + const entities_size = area_npcs.length * NPC_SIZE; cursor.write_u32(2); // Entity type - cursor.write_u32(entitiesSize + 16); - cursor.write_u32(areaId); - cursor.write_u32(entitiesSize); + cursor.write_u32(entities_size + 16); + cursor.write_u32(area_id); + cursor.write_u32(entities_size); - for (const npc of areaNpcs) { - cursor.write_u16(npc.typeId); + for (const npc of area_npcs) { + cursor.write_u16(npc.type_id); cursor.write_u8_array(npc.unknown[0]); - cursor.write_u16(npc.sectionId); + cursor.write_u16(npc.section_id); cursor.write_u8_array(npc.unknown[1]); cursor.write_f32(npc.position.x); cursor.write_f32(npc.position.y); @@ -210,10 +212,10 @@ export function writeDat({ objs, npcs, unknowns }: DatFile): BufferCursor { } for (const unknown of unknowns) { - cursor.write_u32(unknown.entityType); - cursor.write_u32(unknown.totalSize); - cursor.write_u32(unknown.areaId); - cursor.write_u32(unknown.entitiesSize); + cursor.write_u32(unknown.entity_type); + cursor.write_u32(unknown.total_size); + cursor.write_u32(unknown.area_id); + cursor.write_u32(unknown.entities_size); cursor.write_u8_array(unknown.data); } diff --git a/src/data_formats/parsing/quest/index.test.ts b/src/data_formats/parsing/quest/index.test.ts index 09fed0ce..2d3941b6 100644 --- a/src/data_formats/parsing/quest/index.test.ts +++ b/src/data_formats/parsing/quest/index.test.ts @@ -16,7 +16,7 @@ test('parse Towards the Future', () => { expect(quest.objects[0].type).toBe(ObjectType.MenuActivation); expect(quest.objects[4].type).toBe(ObjectType.PlayerSet); expect(quest.npcs.length).toBe(216); - expect(testableAreaVariants(quest)).toEqual([ + expect(testable_area_variants(quest)).toEqual([ [0, 0], [2, 0], [11, 0], [5, 4], [12, 0], [7, 4], [13, 0], [8, 4], [10, 4], [14, 0] ]); }); @@ -25,25 +25,25 @@ test('parse Towards the Future', () => { * Parse a QST file, write the resulting Quest object to QST again, then parse that again. * Then check whether the two Quest objects are equal. */ -test('parseQuest and writeQuestQst', () => { +test('parse_quest and write_quest_qst', () => { const buffer = fs.readFileSync('test/resources/tethealla_v0.143_quests/solo/ep1/02.qst').buffer; const cursor = new BufferCursor(buffer, true); - const origQuest = parse_quest(cursor)!; - const testQuest = parse_quest(write_quest_qst(origQuest, '02.qst'))!; + const orig_quest = parse_quest(cursor)!; + const test_quest = parse_quest(write_quest_qst(orig_quest, '02.qst'))!; - expect(testQuest.name).toBe(origQuest.name); - expect(testQuest.short_description).toBe(origQuest.short_description); - expect(testQuest.long_description).toBe(origQuest.long_description); - expect(testQuest.episode).toBe(origQuest.episode); - expect(testableObjects(testQuest)) - .toEqual(testableObjects(origQuest)); - expect(testableNpcs(testQuest)) - .toEqual(testableNpcs(origQuest)); - expect(testableAreaVariants(testQuest)) - .toEqual(testableAreaVariants(origQuest)); + expect(test_quest.name).toBe(orig_quest.name); + expect(test_quest.short_description).toBe(orig_quest.short_description); + expect(test_quest.long_description).toBe(orig_quest.long_description); + expect(test_quest.episode).toBe(orig_quest.episode); + expect(testable_objects(test_quest)) + .toEqual(testable_objects(orig_quest)); + expect(testable_npcs(test_quest)) + .toEqual(testable_npcs(orig_quest)); + expect(testable_area_variants(test_quest)) + .toEqual(testable_area_variants(orig_quest)); }); -function testableObjects(quest: Quest) { +function testable_objects(quest: Quest) { return quest.objects.map(object => [ object.area_id, object.section_id, @@ -52,7 +52,7 @@ function testableObjects(quest: Quest) { ]); } -function testableNpcs(quest: Quest) { +function testable_npcs(quest: Quest) { return quest.npcs.map(npc => [ npc.area_id, npc.section_id, @@ -61,6 +61,6 @@ function testableNpcs(quest: Quest) { ]); } -function testableAreaVariants(quest: Quest) { +function testable_area_variants(quest: Quest) { return quest.area_variants.map(av => [av.area.id, av.id]); } diff --git a/src/data_formats/parsing/quest/index.ts b/src/data_formats/parsing/quest/index.ts index d4460838..cb3a05ab 100644 --- a/src/data_formats/parsing/quest/index.ts +++ b/src/data_formats/parsing/quest/index.ts @@ -1,19 +1,12 @@ +import Logger from 'js-logger'; +import { AreaVariant, NpcType, ObjectType, Quest, QuestNpc, QuestObject } from '../../../domain'; +import { area_store } from '../../../stores/AreaStore'; import { BufferCursor } from '../../BufferCursor'; import * as prs from '../../compression/prs'; -import { parseDat, writeDat, DatObject, DatNpc, DatFile } from './dat'; -import { parseBin, writeBin, Instruction } from './bin'; -import { parseQst as parse_qst, writeQst as write_qst } from './qst'; -import { - Vec3, - AreaVariant, - QuestNpc, - QuestObject, - Quest, - ObjectType, - NpcType -} from '../../../domain'; -import { area_store } from '../../../stores/AreaStore'; -import Logger from 'js-logger'; +import { Vec3 } from "../../Vec3"; +import { Instruction, parse_bin, write_bin } from './bin'; +import { DatFile, DatNpc, DatObject, parse_dat, write_dat } from './dat'; +import { parse_qst, QstContainedFile, write_qst } from './qst'; const logger = Logger.get('data_formats/parsing/quest'); @@ -29,8 +22,8 @@ export function parse_quest(cursor: BufferCursor, lenient: boolean = false): Que return; } - let dat_file = null; - let bin_file = null; + let dat_file: QstContainedFile | undefined; + let bin_file: QstContainedFile | undefined; for (const file of qst.files) { const file_name = file.name.trim().toLowerCase(); @@ -54,29 +47,29 @@ export function parse_quest(cursor: BufferCursor, lenient: boolean = false): Que return; } - const dat = parseDat(prs.decompress(dat_file.data)); - const bin = parseBin(prs.decompress(bin_file.data), lenient); + const dat = parse_dat(prs.decompress(dat_file.data)); + const bin = parse_bin(prs.decompress(bin_file.data), lenient); let episode = 1; let area_variants: AreaVariant[] = []; - if (bin.functionOffsets.length) { - const func_0_ops = get_func_operations(bin.instructions, bin.functionOffsets[0]); + if (bin.function_offsets.length) { + const func_0_ops = get_func_operations(bin.instructions, bin.function_offsets[0]); if (func_0_ops) { episode = get_episode(func_0_ops); area_variants = get_area_variants(dat, episode, func_0_ops, lenient); } else { - logger.warn(`Function 0 offset ${bin.functionOffsets[0]} is invalid.`); + logger.warn(`Function 0 offset ${bin.function_offsets[0]} is invalid.`); } } else { logger.warn('File contains no functions.'); } return new Quest( - bin.questName, - bin.shortDescription, - bin.longDescription, - dat_file.questNo, + dat_file.id, + bin.quest_name, + bin.short_description, + bin.long_description, episode, area_variants, parse_obj_data(dat.objs), @@ -87,12 +80,12 @@ export function parse_quest(cursor: BufferCursor, lenient: boolean = false): Que } export function write_quest_qst(quest: Quest, file_name: string): BufferCursor { - const dat = writeDat({ + const dat = write_dat({ objs: objects_to_dat_data(quest.objects), npcs: npcsToDatData(quest.npcs), unknowns: quest.dat_unknowns }); - const bin = writeBin({ data: quest.bin_data }); + const bin = write_bin({ data: quest.bin_data }); const ext_start = file_name.lastIndexOf('.'); const base_file_name = ext_start === -1 ? file_name : file_name.slice(0, ext_start); @@ -100,12 +93,12 @@ export function write_quest_qst(quest: Quest, file_name: string): BufferCursor { files: [ { name: base_file_name + '.dat', - questNo: quest.quest_no, + id: quest.id, data: prs.compress(dat) }, { name: base_file_name + '.bin', - questNo: quest.quest_no, + id: quest.id, data: prs.compress(bin) } ] @@ -141,11 +134,11 @@ function get_area_variants( const area_variants = new Map(); for (const npc of dat.npcs) { - area_variants.set(npc.areaId, 0); + area_variants.set(npc.area_id, 0); } for (const obj of dat.objs) { - area_variants.set(obj.areaId, 0); + area_variants.set(obj.area_id, 0); } const bb_maps = func_0_ops.filter(op => op.mnemonic === 'BB_Map_Designate'); @@ -158,10 +151,10 @@ function get_area_variants( const area_variants_array = new Array(); - for (const [areaId, variantId] of area_variants.entries()) { + for (const [area_id, variant_id] of area_variants.entries()) { try { area_variants_array.push( - area_store.get_variant(episode, areaId, variantId) + area_store.get_variant(episode, area_id, variant_id) ); } catch (e) { if (lenient) { @@ -178,7 +171,10 @@ function get_area_variants( ); } -function get_func_operations(operations: Instruction[], func_offset: number) { +function get_func_operations( + operations: Instruction[], + func_offset: number +): Instruction[] | undefined { let position = 0; let func_found = false; const func_ops: Instruction[] = []; @@ -200,7 +196,7 @@ function get_func_operations(operations: Instruction[], func_offset: number) { position += operation.size; } - return func_found ? func_ops : null; + return func_found ? func_ops : undefined; } function parse_obj_data(objs: DatObject[]): QuestObject[] { @@ -208,36 +204,36 @@ function parse_obj_data(objs: DatObject[]): QuestObject[] { const { x, y, z } = obj_data.position; const rot = obj_data.rotation; return new QuestObject( - obj_data.areaId, - obj_data.sectionId, + obj_data.area_id, + obj_data.section_id, new Vec3(x, y, z), new Vec3(rot.x, rot.y, rot.z), - ObjectType.from_pso_id(obj_data.typeId), + ObjectType.from_pso_id(obj_data.type_id), obj_data ); }); } function parse_npc_data(episode: number, npcs: DatNpc[]): QuestNpc[] { - return npcs.map(npcData => { - const { x, y, z } = npcData.position; - const rot = npcData.rotation; + return npcs.map(npc_data => { + const { x, y, z } = npc_data.position; + const rot = npc_data.rotation; return new QuestNpc( - npcData.areaId, - npcData.sectionId, + npc_data.area_id, + npc_data.section_id, new Vec3(x, y, z), new Vec3(rot.x, rot.y, rot.z), - get_npc_type(episode, npcData), - npcData + get_npc_type(episode, npc_data), + npc_data ); }); } // TODO: detect Mothmant, St. Rappy, Hallo Rappy, Egg Rappy, Death Gunner, Bulk and Recon. -function get_npc_type(episode: number, { typeId, flags, skin, areaId }: DatNpc): NpcType { +function get_npc_type(episode: number, { type_id, flags, skin, area_id }: DatNpc): NpcType { const regular = Math.abs(flags - 1) > 0.00001; - switch (`${typeId}, ${skin % 3}, ${episode}`) { + switch (`${type_id}, ${skin % 3}, ${episode}`) { case `${0x044}, 0, 1`: return NpcType.Booma; case `${0x044}, 1, 1`: return NpcType.Gobooma; case `${0x044}, 2, 1`: return NpcType.Gigobooma; @@ -265,7 +261,7 @@ function get_npc_type(episode: number, { typeId, flags, skin, areaId }: DatNpc): case `${0x117}, 2, 4`: return NpcType.GoranDetonator; } - switch (`${typeId}, ${skin % 2}, ${episode}`) { + switch (`${type_id}, ${skin % 2}, ${episode}`) { case `${0x040}, 0, 1`: return NpcType.Hildebear; case `${0x040}, 0, 2`: return NpcType.Hildebear2; case `${0x040}, 1, 1`: return NpcType.Hildeblue; @@ -291,8 +287,8 @@ function get_npc_type(episode: number, { typeId, flags, skin, areaId }: DatNpc): case `${0x0DD}, 0, 2`: return NpcType.Dolmolm; case `${0x0DD}, 1, 2`: return NpcType.Dolmdarl; - case `${0x0E0}, 0, 2`: return areaId > 15 ? NpcType.Epsilon : NpcType.SinowZoa; - case `${0x0E0}, 1, 2`: return areaId > 15 ? NpcType.Epsilon : NpcType.SinowZele; + case `${0x0E0}, 0, 2`: return area_id > 15 ? NpcType.Epsilon : NpcType.SinowZoa; + case `${0x0E0}, 1, 2`: return area_id > 15 ? NpcType.Epsilon : NpcType.SinowZele; case `${0x112}, 0, 4`: return NpcType.MerissaA; case `${0x112}, 1, 4`: return NpcType.MerissaAA; @@ -304,7 +300,7 @@ function get_npc_type(episode: number, { typeId, flags, skin, areaId }: DatNpc): case `${0x119}, 1, 4`: return regular ? NpcType.Shambertin : NpcType.Kondrieu; } - switch (`${typeId}, ${episode}`) { + switch (`${type_id}, ${episode}`) { case `${0x042}, 1`: return NpcType.Monest; case `${0x042}, 2`: return NpcType.Monest2; case `${0x043}, 1`: return regular ? NpcType.SavageWolf : NpcType.BarbarousWolf; @@ -312,10 +308,12 @@ function get_npc_type(episode: number, { typeId, flags, skin, areaId }: DatNpc): case `${0x060}, 1`: return NpcType.GrassAssassin; case `${0x060}, 2`: return NpcType.GrassAssassin2; - case `${0x061}, 1`: return areaId > 15 ? NpcType.DelLily : ( - regular ? NpcType.PoisonLily : NpcType.NarLily); - case `${0x061}, 2`: return areaId > 15 ? NpcType.DelLily : ( - regular ? NpcType.PoisonLily2 : NpcType.NarLily2); + case `${0x061}, 1`: return area_id > 15 ? NpcType.DelLily : ( + regular ? NpcType.PoisonLily : NpcType.NarLily + ); + case `${0x061}, 2`: return area_id > 15 ? NpcType.DelLily : ( + regular ? NpcType.PoisonLily2 : NpcType.NarLily2 + ); case `${0x062}, 1`: return NpcType.NanoDragon; case `${0x064}, 1`: return regular ? NpcType.PofuillySlime : NpcType.PouillySlime; case `${0x065}, 1`: return NpcType.PanArms; @@ -366,7 +364,7 @@ function get_npc_type(episode: number, { typeId, flags, skin, areaId }: DatNpc): case `${0x113}, 4`: return NpcType.Girtablulu; } - switch (typeId) { + switch (type_id) { case 0x004: return NpcType.FemaleFat; case 0x005: return NpcType.FemaleMacho; case 0x007: return NpcType.FemaleTall; @@ -391,11 +389,11 @@ function get_npc_type(episode: number, { typeId, flags, skin, areaId }: DatNpc): function objects_to_dat_data(objects: QuestObject[]): DatObject[] { return objects.map(object => ({ - typeId: object.type.pso_id!, - sectionId: object.section_id, + type_id: object.type.pso_id!, + section_id: object.section_id, position: object.section_position, rotation: object.rotation, - areaId: object.area_id, + area_id: object.area_id, unknown: object.dat.unknown })); } @@ -403,170 +401,170 @@ function objects_to_dat_data(objects: QuestObject[]): DatObject[] { function npcsToDatData(npcs: QuestNpc[]): DatNpc[] { return npcs.map(npc => { // If the type is unknown, typeData will be undefined and we use the raw data from the DAT file. - const typeData = npcTypeToDatData(npc.type); + const type_data = npc_type_to_dat_data(npc.type); let flags = npc.dat.flags; - if (typeData) { - flags = (npc.dat.flags & ~0x800000) | (typeData.regular ? 0 : 0x800000); + if (type_data) { + flags = (npc.dat.flags & ~0x800000) | (type_data.regular ? 0 : 0x800000); } return { - typeId: typeData ? typeData.typeId : npc.dat.typeId, - sectionId: npc.section_id, + type_id: type_data ? type_data.type_id : npc.dat.type_id, + section_id: npc.section_id, position: npc.section_position, rotation: npc.rotation, flags, - skin: typeData ? typeData.skin : npc.dat.skin, - areaId: npc.area_id, + skin: type_data ? type_data.skin : npc.dat.skin, + area_id: npc.area_id, unknown: npc.dat.unknown }; }); } -function npcTypeToDatData( +function npc_type_to_dat_data( type: NpcType -): { typeId: number, skin: number, regular: boolean } | null { +): { type_id: number, skin: number, regular: boolean } | undefined { switch (type) { default: throw new Error(`Unexpected type ${type.code}.`); - case NpcType.Unknown: return null; + case NpcType.Unknown: return undefined; - case NpcType.FemaleFat: return { typeId: 0x004, skin: 0, regular: true }; - case NpcType.FemaleMacho: return { typeId: 0x005, skin: 0, regular: true }; - case NpcType.FemaleTall: return { typeId: 0x007, skin: 0, regular: true }; - case NpcType.MaleDwarf: return { typeId: 0x00A, skin: 0, regular: true }; - case NpcType.MaleFat: return { typeId: 0x00B, skin: 0, regular: true }; - case NpcType.MaleMacho: return { typeId: 0x00C, skin: 0, regular: true }; - case NpcType.MaleOld: return { typeId: 0x00D, skin: 0, regular: true }; - case NpcType.BlueSoldier: return { typeId: 0x019, skin: 0, regular: true }; - case NpcType.RedSoldier: return { typeId: 0x01A, skin: 0, regular: true }; - case NpcType.Principal: return { typeId: 0x01B, skin: 0, regular: true }; - case NpcType.Tekker: return { typeId: 0x01C, skin: 0, regular: true }; - case NpcType.GuildLady: return { typeId: 0x01D, skin: 0, regular: true }; - case NpcType.Scientist: return { typeId: 0x01E, skin: 0, regular: true }; - case NpcType.Nurse: return { typeId: 0x01F, skin: 0, regular: true }; - case NpcType.Irene: return { typeId: 0x020, skin: 0, regular: true }; - case NpcType.ItemShop: return { typeId: 0x0F1, skin: 0, regular: true }; - case NpcType.Nurse2: return { typeId: 0x0FE, skin: 0, regular: true }; + case NpcType.FemaleFat: return { type_id: 0x004, skin: 0, regular: true }; + case NpcType.FemaleMacho: return { type_id: 0x005, skin: 0, regular: true }; + case NpcType.FemaleTall: return { type_id: 0x007, skin: 0, regular: true }; + case NpcType.MaleDwarf: return { type_id: 0x00A, skin: 0, regular: true }; + case NpcType.MaleFat: return { type_id: 0x00B, skin: 0, regular: true }; + case NpcType.MaleMacho: return { type_id: 0x00C, skin: 0, regular: true }; + case NpcType.MaleOld: return { type_id: 0x00D, skin: 0, regular: true }; + case NpcType.BlueSoldier: return { type_id: 0x019, skin: 0, regular: true }; + case NpcType.RedSoldier: return { type_id: 0x01A, skin: 0, regular: true }; + case NpcType.Principal: return { type_id: 0x01B, skin: 0, regular: true }; + case NpcType.Tekker: return { type_id: 0x01C, skin: 0, regular: true }; + case NpcType.GuildLady: return { type_id: 0x01D, skin: 0, regular: true }; + case NpcType.Scientist: return { type_id: 0x01E, skin: 0, regular: true }; + case NpcType.Nurse: return { type_id: 0x01F, skin: 0, regular: true }; + case NpcType.Irene: return { type_id: 0x020, skin: 0, regular: true }; + case NpcType.ItemShop: return { type_id: 0x0F1, skin: 0, regular: true }; + case NpcType.Nurse2: return { type_id: 0x0FE, skin: 0, regular: true }; - case NpcType.Hildebear: return { typeId: 0x040, skin: 0, regular: true }; - case NpcType.Hildeblue: return { typeId: 0x040, skin: 1, regular: true }; - case NpcType.RagRappy: return { typeId: 0x041, skin: 0, regular: true }; - case NpcType.AlRappy: return { typeId: 0x041, skin: 1, regular: true }; - case NpcType.Monest: return { typeId: 0x042, skin: 0, regular: true }; - case NpcType.SavageWolf: return { typeId: 0x043, skin: 0, regular: true }; - case NpcType.BarbarousWolf: return { typeId: 0x043, skin: 0, regular: false }; - case NpcType.Booma: return { typeId: 0x044, skin: 0, regular: true }; - case NpcType.Gobooma: return { typeId: 0x044, skin: 1, regular: true }; - case NpcType.Gigobooma: return { typeId: 0x044, skin: 2, regular: true }; - case NpcType.Dragon: return { typeId: 0x0C0, skin: 0, regular: true }; + case NpcType.Hildebear: return { type_id: 0x040, skin: 0, regular: true }; + case NpcType.Hildeblue: return { type_id: 0x040, skin: 1, regular: true }; + case NpcType.RagRappy: return { type_id: 0x041, skin: 0, regular: true }; + case NpcType.AlRappy: return { type_id: 0x041, skin: 1, regular: true }; + case NpcType.Monest: return { type_id: 0x042, skin: 0, regular: true }; + case NpcType.SavageWolf: return { type_id: 0x043, skin: 0, regular: true }; + case NpcType.BarbarousWolf: return { type_id: 0x043, skin: 0, regular: false }; + case NpcType.Booma: return { type_id: 0x044, skin: 0, regular: true }; + case NpcType.Gobooma: return { type_id: 0x044, skin: 1, regular: true }; + case NpcType.Gigobooma: return { type_id: 0x044, skin: 2, regular: true }; + case NpcType.Dragon: return { type_id: 0x0C0, skin: 0, regular: true }; - case NpcType.GrassAssassin: return { typeId: 0x060, skin: 0, regular: true }; - case NpcType.PoisonLily: return { typeId: 0x061, skin: 0, regular: true }; - case NpcType.NarLily: return { typeId: 0x061, skin: 1, regular: true }; - case NpcType.NanoDragon: return { typeId: 0x062, skin: 0, regular: true }; - case NpcType.EvilShark: return { typeId: 0x063, skin: 0, regular: true }; - case NpcType.PalShark: return { typeId: 0x063, skin: 1, regular: true }; - case NpcType.GuilShark: return { typeId: 0x063, skin: 2, regular: true }; - case NpcType.PofuillySlime: return { typeId: 0x064, skin: 0, regular: true }; - case NpcType.PouillySlime: return { typeId: 0x064, skin: 0, regular: false }; - case NpcType.PanArms: return { typeId: 0x065, skin: 0, regular: true }; - case NpcType.DeRolLe: return { typeId: 0x0C1, skin: 0, regular: true }; + case NpcType.GrassAssassin: return { type_id: 0x060, skin: 0, regular: true }; + case NpcType.PoisonLily: return { type_id: 0x061, skin: 0, regular: true }; + case NpcType.NarLily: return { type_id: 0x061, skin: 1, regular: true }; + case NpcType.NanoDragon: return { type_id: 0x062, skin: 0, regular: true }; + case NpcType.EvilShark: return { type_id: 0x063, skin: 0, regular: true }; + case NpcType.PalShark: return { type_id: 0x063, skin: 1, regular: true }; + case NpcType.GuilShark: return { type_id: 0x063, skin: 2, regular: true }; + case NpcType.PofuillySlime: return { type_id: 0x064, skin: 0, regular: true }; + case NpcType.PouillySlime: return { type_id: 0x064, skin: 0, regular: false }; + case NpcType.PanArms: return { type_id: 0x065, skin: 0, regular: true }; + case NpcType.DeRolLe: return { type_id: 0x0C1, skin: 0, regular: true }; - case NpcType.Dubchic: return { typeId: 0x080, skin: 0, regular: true }; - case NpcType.Gilchic: return { typeId: 0x080, skin: 1, regular: true }; - case NpcType.Garanz: return { typeId: 0x081, skin: 0, regular: true }; - case NpcType.SinowBeat: return { typeId: 0x082, skin: 0, regular: true }; - case NpcType.SinowGold: return { typeId: 0x082, skin: 0, regular: false }; - case NpcType.Canadine: return { typeId: 0x083, skin: 0, regular: true }; - case NpcType.Canane: return { typeId: 0x084, skin: 0, regular: true }; - case NpcType.Dubswitch: return { typeId: 0x085, skin: 0, regular: true }; - case NpcType.VolOpt: return { typeId: 0x0C5, skin: 0, regular: true }; + case NpcType.Dubchic: return { type_id: 0x080, skin: 0, regular: true }; + case NpcType.Gilchic: return { type_id: 0x080, skin: 1, regular: true }; + case NpcType.Garanz: return { type_id: 0x081, skin: 0, regular: true }; + case NpcType.SinowBeat: return { type_id: 0x082, skin: 0, regular: true }; + case NpcType.SinowGold: return { type_id: 0x082, skin: 0, regular: false }; + case NpcType.Canadine: return { type_id: 0x083, skin: 0, regular: true }; + case NpcType.Canane: return { type_id: 0x084, skin: 0, regular: true }; + case NpcType.Dubswitch: return { type_id: 0x085, skin: 0, regular: true }; + case NpcType.VolOpt: return { type_id: 0x0C5, skin: 0, regular: true }; - case NpcType.Delsaber: return { typeId: 0x0A0, skin: 0, regular: true }; - case NpcType.ChaosSorcerer: return { typeId: 0x0A1, skin: 0, regular: true }; - case NpcType.DarkGunner: return { typeId: 0x0A2, skin: 0, regular: true }; - case NpcType.ChaosBringer: return { typeId: 0x0A4, skin: 0, regular: true }; - case NpcType.DarkBelra: return { typeId: 0x0A5, skin: 0, regular: true }; - case NpcType.Dimenian: return { typeId: 0x0A6, skin: 0, regular: true }; - case NpcType.LaDimenian: return { typeId: 0x0A6, skin: 1, regular: true }; - case NpcType.SoDimenian: return { typeId: 0x0A6, skin: 2, regular: true }; - case NpcType.Bulclaw: return { typeId: 0x0A7, skin: 0, regular: true }; - case NpcType.Claw: return { typeId: 0x0A8, skin: 0, regular: true }; - case NpcType.DarkFalz: return { typeId: 0x0C8, skin: 0, regular: true }; + case NpcType.Delsaber: return { type_id: 0x0A0, skin: 0, regular: true }; + case NpcType.ChaosSorcerer: return { type_id: 0x0A1, skin: 0, regular: true }; + case NpcType.DarkGunner: return { type_id: 0x0A2, skin: 0, regular: true }; + case NpcType.ChaosBringer: return { type_id: 0x0A4, skin: 0, regular: true }; + case NpcType.DarkBelra: return { type_id: 0x0A5, skin: 0, regular: true }; + case NpcType.Dimenian: return { type_id: 0x0A6, skin: 0, regular: true }; + case NpcType.LaDimenian: return { type_id: 0x0A6, skin: 1, regular: true }; + case NpcType.SoDimenian: return { type_id: 0x0A6, skin: 2, regular: true }; + case NpcType.Bulclaw: return { type_id: 0x0A7, skin: 0, regular: true }; + case NpcType.Claw: return { type_id: 0x0A8, skin: 0, regular: true }; + case NpcType.DarkFalz: return { type_id: 0x0C8, skin: 0, regular: true }; - case NpcType.Hildebear2: return { typeId: 0x040, skin: 0, regular: true }; - case NpcType.Hildeblue2: return { typeId: 0x040, skin: 1, regular: true }; - case NpcType.RagRappy2: return { typeId: 0x041, skin: 0, regular: true }; - case NpcType.LoveRappy: return { typeId: 0x041, skin: 1, regular: true }; - case NpcType.Monest2: return { typeId: 0x042, skin: 0, regular: true }; - case NpcType.PoisonLily2: return { typeId: 0x061, skin: 0, regular: true }; - case NpcType.NarLily2: return { typeId: 0x061, skin: 1, regular: true }; - case NpcType.GrassAssassin2: return { typeId: 0x060, skin: 0, regular: true }; - case NpcType.Dimenian2: return { typeId: 0x0A6, skin: 0, regular: true }; - case NpcType.LaDimenian2: return { typeId: 0x0A6, skin: 1, regular: true }; - case NpcType.SoDimenian2: return { typeId: 0x0A6, skin: 2, regular: true }; - case NpcType.DarkBelra2: return { typeId: 0x0A5, skin: 0, regular: true }; - case NpcType.BarbaRay: return { typeId: 0x0CB, skin: 0, regular: true }; + case NpcType.Hildebear2: return { type_id: 0x040, skin: 0, regular: true }; + case NpcType.Hildeblue2: return { type_id: 0x040, skin: 1, regular: true }; + case NpcType.RagRappy2: return { type_id: 0x041, skin: 0, regular: true }; + case NpcType.LoveRappy: return { type_id: 0x041, skin: 1, regular: true }; + case NpcType.Monest2: return { type_id: 0x042, skin: 0, regular: true }; + case NpcType.PoisonLily2: return { type_id: 0x061, skin: 0, regular: true }; + case NpcType.NarLily2: return { type_id: 0x061, skin: 1, regular: true }; + case NpcType.GrassAssassin2: return { type_id: 0x060, skin: 0, regular: true }; + case NpcType.Dimenian2: return { type_id: 0x0A6, skin: 0, regular: true }; + case NpcType.LaDimenian2: return { type_id: 0x0A6, skin: 1, regular: true }; + case NpcType.SoDimenian2: return { type_id: 0x0A6, skin: 2, regular: true }; + case NpcType.DarkBelra2: return { type_id: 0x0A5, skin: 0, regular: true }; + case NpcType.BarbaRay: return { type_id: 0x0CB, skin: 0, regular: true }; - case NpcType.SavageWolf2: return { typeId: 0x043, skin: 0, regular: true }; - case NpcType.BarbarousWolf2: return { typeId: 0x043, skin: 0, regular: false }; - case NpcType.PanArms2: return { typeId: 0x065, skin: 0, regular: true }; - case NpcType.Dubchic2: return { typeId: 0x080, skin: 0, regular: true }; - case NpcType.Gilchic2: return { typeId: 0x080, skin: 1, regular: true }; - case NpcType.Garanz2: return { typeId: 0x081, skin: 0, regular: true }; - case NpcType.Dubswitch2: return { typeId: 0x085, skin: 0, regular: true }; - case NpcType.Delsaber2: return { typeId: 0x0A0, skin: 0, regular: true }; - case NpcType.ChaosSorcerer2: return { typeId: 0x0A1, skin: 0, regular: true }; - case NpcType.GolDragon: return { typeId: 0x0CC, skin: 0, regular: true }; + case NpcType.SavageWolf2: return { type_id: 0x043, skin: 0, regular: true }; + case NpcType.BarbarousWolf2: return { type_id: 0x043, skin: 0, regular: false }; + case NpcType.PanArms2: return { type_id: 0x065, skin: 0, regular: true }; + case NpcType.Dubchic2: return { type_id: 0x080, skin: 0, regular: true }; + case NpcType.Gilchic2: return { type_id: 0x080, skin: 1, regular: true }; + case NpcType.Garanz2: return { type_id: 0x081, skin: 0, regular: true }; + case NpcType.Dubswitch2: return { type_id: 0x085, skin: 0, regular: true }; + case NpcType.Delsaber2: return { type_id: 0x0A0, skin: 0, regular: true }; + case NpcType.ChaosSorcerer2: return { type_id: 0x0A1, skin: 0, regular: true }; + case NpcType.GolDragon: return { type_id: 0x0CC, skin: 0, regular: true }; - case NpcType.SinowBerill: return { typeId: 0x0D4, skin: 0, regular: true }; - case NpcType.SinowSpigell: return { typeId: 0x0D4, skin: 1, regular: true }; - case NpcType.Merillia: return { typeId: 0x0D5, skin: 0, regular: true }; - case NpcType.Meriltas: return { typeId: 0x0D5, skin: 1, regular: true }; - case NpcType.Mericarol: return { typeId: 0x0D6, skin: 0, regular: true }; - case NpcType.Mericus: return { typeId: 0x0D6, skin: 1, regular: true }; - case NpcType.Merikle: return { typeId: 0x0D6, skin: 2, regular: true }; - case NpcType.UlGibbon: return { typeId: 0x0D7, skin: 0, regular: true }; - case NpcType.ZolGibbon: return { typeId: 0x0D7, skin: 1, regular: true }; - case NpcType.Gibbles: return { typeId: 0x0D8, skin: 0, regular: true }; - case NpcType.Gee: return { typeId: 0x0D9, skin: 0, regular: true }; - case NpcType.GiGue: return { typeId: 0x0DA, skin: 0, regular: true }; - case NpcType.GalGryphon: return { typeId: 0x0C0, skin: 0, regular: true }; + case NpcType.SinowBerill: return { type_id: 0x0D4, skin: 0, regular: true }; + case NpcType.SinowSpigell: return { type_id: 0x0D4, skin: 1, regular: true }; + case NpcType.Merillia: return { type_id: 0x0D5, skin: 0, regular: true }; + case NpcType.Meriltas: return { type_id: 0x0D5, skin: 1, regular: true }; + case NpcType.Mericarol: return { type_id: 0x0D6, skin: 0, regular: true }; + case NpcType.Mericus: return { type_id: 0x0D6, skin: 1, regular: true }; + case NpcType.Merikle: return { type_id: 0x0D6, skin: 2, regular: true }; + case NpcType.UlGibbon: return { type_id: 0x0D7, skin: 0, regular: true }; + case NpcType.ZolGibbon: return { type_id: 0x0D7, skin: 1, regular: true }; + case NpcType.Gibbles: return { type_id: 0x0D8, skin: 0, regular: true }; + case NpcType.Gee: return { type_id: 0x0D9, skin: 0, regular: true }; + case NpcType.GiGue: return { type_id: 0x0DA, skin: 0, regular: true }; + case NpcType.GalGryphon: return { type_id: 0x0C0, skin: 0, regular: true }; - case NpcType.Deldepth: return { typeId: 0x0DB, skin: 0, regular: true }; - case NpcType.Delbiter: return { typeId: 0x0DC, skin: 0, regular: true }; - case NpcType.Dolmolm: return { typeId: 0x0DD, skin: 0, regular: true }; - case NpcType.Dolmdarl: return { typeId: 0x0DD, skin: 1, regular: true }; - case NpcType.Morfos: return { typeId: 0x0DE, skin: 0, regular: true }; - case NpcType.Recobox: return { typeId: 0x0DF, skin: 0, regular: true }; - case NpcType.Epsilon: return { typeId: 0x0E0, skin: 0, regular: true }; - case NpcType.SinowZoa: return { typeId: 0x0E0, skin: 0, regular: true }; - case NpcType.SinowZele: return { typeId: 0x0E0, skin: 1, regular: true }; - case NpcType.IllGill: return { typeId: 0x0E1, skin: 0, regular: true }; - case NpcType.DelLily: return { typeId: 0x061, skin: 0, regular: true }; - case NpcType.OlgaFlow: return { typeId: 0x0CA, skin: 0, regular: true }; + case NpcType.Deldepth: return { type_id: 0x0DB, skin: 0, regular: true }; + case NpcType.Delbiter: return { type_id: 0x0DC, skin: 0, regular: true }; + case NpcType.Dolmolm: return { type_id: 0x0DD, skin: 0, regular: true }; + case NpcType.Dolmdarl: return { type_id: 0x0DD, skin: 1, regular: true }; + case NpcType.Morfos: return { type_id: 0x0DE, skin: 0, regular: true }; + case NpcType.Recobox: return { type_id: 0x0DF, skin: 0, regular: true }; + case NpcType.Epsilon: return { type_id: 0x0E0, skin: 0, regular: true }; + case NpcType.SinowZoa: return { type_id: 0x0E0, skin: 0, regular: true }; + case NpcType.SinowZele: return { type_id: 0x0E0, skin: 1, regular: true }; + case NpcType.IllGill: return { type_id: 0x0E1, skin: 0, regular: true }; + case NpcType.DelLily: return { type_id: 0x061, skin: 0, regular: true }; + case NpcType.OlgaFlow: return { type_id: 0x0CA, skin: 0, regular: true }; - case NpcType.SandRappy: return { typeId: 0x041, skin: 0, regular: true }; - case NpcType.DelRappy: return { typeId: 0x041, skin: 1, regular: true }; - case NpcType.Astark: return { typeId: 0x110, skin: 0, regular: true }; - case NpcType.SatelliteLizard: return { typeId: 0x111, skin: 0, regular: true }; - case NpcType.Yowie: return { typeId: 0x111, skin: 0, regular: false }; - case NpcType.MerissaA: return { typeId: 0x112, skin: 0, regular: true }; - case NpcType.MerissaAA: return { typeId: 0x112, skin: 1, regular: true }; - case NpcType.Girtablulu: return { typeId: 0x113, skin: 0, regular: true }; - case NpcType.Zu: return { typeId: 0x114, skin: 0, regular: true }; - case NpcType.Pazuzu: return { typeId: 0x114, skin: 1, regular: true }; - case NpcType.Boota: return { typeId: 0x115, skin: 0, regular: true }; - case NpcType.ZeBoota: return { typeId: 0x115, skin: 1, regular: true }; - case NpcType.BaBoota: return { typeId: 0x115, skin: 2, regular: true }; - case NpcType.Dorphon: return { typeId: 0x116, skin: 0, regular: true }; - case NpcType.DorphonEclair: return { typeId: 0x116, skin: 1, regular: true }; - case NpcType.Goran: return { typeId: 0x117, skin: 0, regular: true }; - case NpcType.PyroGoran: return { typeId: 0x117, skin: 1, regular: true }; - case NpcType.GoranDetonator: return { typeId: 0x117, skin: 2, regular: true }; - case NpcType.SaintMilion: return { typeId: 0x119, skin: 0, regular: true }; - case NpcType.Shambertin: return { typeId: 0x119, skin: 1, regular: true }; - case NpcType.Kondrieu: return { typeId: 0x119, skin: 0, regular: false }; + case NpcType.SandRappy: return { type_id: 0x041, skin: 0, regular: true }; + case NpcType.DelRappy: return { type_id: 0x041, skin: 1, regular: true }; + case NpcType.Astark: return { type_id: 0x110, skin: 0, regular: true }; + case NpcType.SatelliteLizard: return { type_id: 0x111, skin: 0, regular: true }; + case NpcType.Yowie: return { type_id: 0x111, skin: 0, regular: false }; + case NpcType.MerissaA: return { type_id: 0x112, skin: 0, regular: true }; + case NpcType.MerissaAA: return { type_id: 0x112, skin: 1, regular: true }; + case NpcType.Girtablulu: return { type_id: 0x113, skin: 0, regular: true }; + case NpcType.Zu: return { type_id: 0x114, skin: 0, regular: true }; + case NpcType.Pazuzu: return { type_id: 0x114, skin: 1, regular: true }; + case NpcType.Boota: return { type_id: 0x115, skin: 0, regular: true }; + case NpcType.ZeBoota: return { type_id: 0x115, skin: 1, regular: true }; + case NpcType.BaBoota: return { type_id: 0x115, skin: 2, regular: true }; + case NpcType.Dorphon: return { type_id: 0x116, skin: 0, regular: true }; + case NpcType.DorphonEclair: return { type_id: 0x116, skin: 1, regular: true }; + case NpcType.Goran: return { type_id: 0x117, skin: 0, regular: true }; + case NpcType.PyroGoran: return { type_id: 0x117, skin: 1, regular: true }; + case NpcType.GoranDetonator: return { type_id: 0x117, skin: 2, regular: true }; + case NpcType.SaintMilion: return { type_id: 0x119, skin: 0, regular: true }; + case NpcType.Shambertin: return { type_id: 0x119, skin: 1, regular: true }; + case NpcType.Kondrieu: return { type_id: 0x119, skin: 0, regular: false }; } } diff --git a/src/data_formats/parsing/quest/qst.test.ts b/src/data_formats/parsing/quest/qst.test.ts index 13b1514a..4eeacadc 100644 --- a/src/data_formats/parsing/quest/qst.test.ts +++ b/src/data_formats/parsing/quest/qst.test.ts @@ -1,25 +1,25 @@ import { BufferCursor } from '../../BufferCursor'; -import { parseQst, writeQst } from './qst'; -import { walkQstFiles } from '../../../../test/src/utils'; +import { parse_qst, write_qst } from './qst'; +import { walk_qst_files } from '../../../../test/src/utils'; /** * Parse a file, convert the resulting structure to QST again and check whether the end result is equal to the original. */ -test('parseQst and writeQst', () => { - walkQstFiles((_filePath, _fileName, fileContent) => { - const origQst = new BufferCursor(fileContent.buffer, true); - const origQuest = parseQst(origQst); +test('parse_qst and write_qst', () => { + walk_qst_files((_file_path, _file_name, file_content) => { + const orig_qst = new BufferCursor(file_content.buffer, true); + const orig_quest = parse_qst(orig_qst); - if (origQuest) { - const testQst = writeQst(origQuest); - origQst.seek_start(0); + if (orig_quest) { + const test_qst = write_qst(orig_quest); + orig_qst.seek_start(0); - expect(testQst.size).toBe(origQst.size); + expect(test_qst.size).toBe(orig_qst.size); let match = true; - while (origQst.bytes_left) { - if (testQst.u8() !== origQst.u8()) { + while (orig_qst.bytes_left) { + if (test_qst.u8() !== orig_qst.u8()) { match = false; break; } diff --git a/src/data_formats/parsing/quest/qst.ts b/src/data_formats/parsing/quest/qst.ts index 9778a486..eddc8bd0 100644 --- a/src/data_formats/parsing/quest/qst.ts +++ b/src/data_formats/parsing/quest/qst.ts @@ -3,40 +3,40 @@ import Logger from 'js-logger'; const logger = Logger.get('data_formats/parsing/quest/qst'); -interface QstContainedFile { - name: string; - name2?: string; // Unsure what this is - questNo?: number; - expectedSize?: number; - data: BufferCursor; - chunkNos: Set; +export type QstContainedFile = { + id?: number, + name: string, + name_2?: string, // Unsure what this is + expected_size?: number, + data: BufferCursor, + chunk_nos: Set, } -interface ParseQstResult { - version: string; - files: QstContainedFile[]; +export type ParseQstResult = { + version: string, + files: QstContainedFile[], } /** * Low level parsing function for .qst files. * Can only read the Blue Burst format. */ -export function parseQst(cursor: BufferCursor): ParseQstResult | undefined { +export function parse_qst(cursor: BufferCursor): ParseQstResult | undefined { // A .qst file contains two 88-byte headers that describe the embedded .dat and .bin files. let version = 'PC'; // Detect version. - const versionA = cursor.u8(); + const version_a = cursor.u8(); cursor.seek(1); - const versionB = cursor.u8(); + const version_b = cursor.u8(); - if (versionA === 0x44) { + if (version_a === 0x44) { version = 'Dreamcast/GameCube'; - } else if (versionA === 0x58) { - if (versionB === 0x44) { + } else if (version_a === 0x58) { + if (version_b === 0x44) { version = 'Blue Burst'; } - } else if (versionA === 0xA6) { + } else if (version_a === 0xA6) { version = 'Dreamcast download'; } @@ -44,17 +44,18 @@ export function parseQst(cursor: BufferCursor): ParseQstResult | undefined { // Read headers and contained files. cursor.seek_start(0); - const headers = parseHeaders(cursor); + const headers = parse_headers(cursor); - const files = parseFiles( - cursor, new Map(headers.map(h => [h.fileName, h.size]))); + const files = parse_files( + cursor, new Map(headers.map(h => [h.file_name, h.size])) + ); for (const file of files) { - const header = headers.find(h => h.fileName === file.name); + const header = headers.find(h => h.file_name === file.name); if (header) { - file.questNo = header.questNo; - file.name2 = header.fileName2; + file.id = header.quest_id; + file.name_2 = header.file_name_2; } } @@ -68,64 +69,64 @@ export function parseQst(cursor: BufferCursor): ParseQstResult | undefined { } } -interface SimpleQstContainedFile { - name: string; - name2?: string; - questNo?: number; - data: BufferCursor; +export type SimpleQstContainedFile = { + id?: number, + name: string, + name_2?: string, + data: BufferCursor, } -interface WriteQstParams { - version?: string; - files: SimpleQstContainedFile[]; +export type WriteQstParams = { + version?: string, + files: SimpleQstContainedFile[], } /** - * Always writes in Blue Burst format. + * Always uses Blue Burst format. */ -export function writeQst(params: WriteQstParams): BufferCursor { +export function write_qst(params: WriteQstParams): BufferCursor { const files = params.files; - const totalSize = files + const total_size = files .map(f => 88 + Math.ceil(f.data.size / 1024) * 1056) .reduce((a, b) => a + b); - const cursor = new BufferCursor(totalSize, true); + const cursor = new BufferCursor(total_size, true); - writeFileHeaders(cursor, files); - writeFileChunks(cursor, files); + write_file_headers(cursor, files); + write_file_chunks(cursor, files); - if (cursor.size !== totalSize) { - throw new Error(`Expected a final file size of ${totalSize}, but got ${cursor.size}.`); + if (cursor.size !== total_size) { + throw new Error(`Expected a final file size of ${total_size}, but got ${cursor.size}.`); } return cursor.seek_start(0); } -interface QstHeader { - questNo: number; - fileName: string; - fileName2: string; - size: number; +type QstHeader = { + quest_id: number, + file_name: string, + file_name_2: string, + size: number, } /** * TODO: Read all headers instead of just the first 2. */ -function parseHeaders(cursor: BufferCursor): QstHeader[] { - const headers = []; +function parse_headers(cursor: BufferCursor): QstHeader[] { + const headers: QstHeader[] = []; for (let i = 0; i < 2; ++i) { cursor.seek(4); - const questNo = cursor.u16(); + const quest_id = cursor.u16(); cursor.seek(38); - const fileName = cursor.string_ascii(16, true, true); + const file_name = cursor.string_ascii(16, true, true); const size = cursor.u32(); // Not sure what this is: - const fileName2 = cursor.string_ascii(24, true, true); + const file_name_2 = cursor.string_ascii(24, true, true); headers.push({ - questNo, - fileName, - fileName2, + quest_id, + file_name, + file_name_2, size }); } @@ -133,34 +134,34 @@ function parseHeaders(cursor: BufferCursor): QstHeader[] { return headers; } -function parseFiles(cursor: BufferCursor, expectedSizes: Map): QstContainedFile[] { +function parse_files(cursor: BufferCursor, expected_sizes: Map): QstContainedFile[] { // Files are interleaved in 1056 byte chunks. // Each chunk has a 24 byte header, 1024 byte data segment and an 8 byte trailer. const files = new Map(); while (cursor.bytes_left >= 1056) { - const startPosition = cursor.position; + const start_position = cursor.position; // Read meta data. - const chunkNo = cursor.seek(4).u8(); - const fileName = cursor.seek(3).string_ascii(16, true, true); + const chunk_no = cursor.seek(4).u8(); + const file_name = cursor.seek(3).string_ascii(16, true, true); - let file = files.get(fileName); + let file = files.get(file_name); if (!file) { - const expectedSize = expectedSizes.get(fileName); - files.set(fileName, file = { - name: fileName, - expectedSize, - data: new BufferCursor(expectedSize || (10 * 1024), true), - chunkNos: new Set() + const expected_size = expected_sizes.get(file_name); + files.set(file_name, file = { + name: file_name, + expected_size, + data: new BufferCursor(expected_size || (10 * 1024), true), + chunk_nos: new Set() }); } - if (file.chunkNos.has(chunkNo)) { - logger.warn(`File chunk number ${chunkNo} of file ${fileName} was already encountered, overwriting previous chunk.`); + if (file.chunk_nos.has(chunk_no)) { + logger.warn(`File chunk number ${chunk_no} of file ${file_name} was already encountered, overwriting previous chunk.`); } else { - file.chunkNos.add(chunkNo); + file.chunk_nos.add(chunk_no); } // Read file data. @@ -173,15 +174,15 @@ function parseFiles(cursor: BufferCursor, expectedSizes: Map): Q } const data = cursor.take(size); - const chunkPosition = chunkNo * 1024; - file.data.size = Math.max(chunkPosition + size, file.data.size); - file.data.seek_start(chunkPosition).write_cursor(data); + const chunk_position = chunk_no * 1024; + file.data.size = Math.max(chunk_position + size, file.data.size); + file.data.seek_start(chunk_position).write_cursor(data); // Skip the padding and the trailer. cursor.seek(1032 - data.size); - if (cursor.position !== startPosition + 1056) { - throw new Error(`Read ${cursor.position - startPosition} file chunk message bytes instead of expected 1056.`); + if (cursor.position !== start_position + 1056) { + throw new Error(`Read ${cursor.position - start_position} file chunk message bytes instead of expected 1056.`); } } @@ -192,19 +193,19 @@ function parseFiles(cursor: BufferCursor, expectedSizes: Map): Q for (const file of files.values()) { // Clean up file properties. file.data.seek_start(0); - file.chunkNos = new Set(Array.from(file.chunkNos.values()).sort((a, b) => a - b)); + file.chunk_nos = new Set(Array.from(file.chunk_nos.values()).sort((a, b) => a - b)); // Check whether the expected size was correct. - if (file.expectedSize != null && file.data.size !== file.expectedSize) { - logger.warn(`File ${file.name} has an actual size of ${file.data.size} instead of the expected size ${file.expectedSize}.`); + if (file.expected_size != null && file.data.size !== file.expected_size) { + logger.warn(`File ${file.name} has an actual size of ${file.data.size} instead of the expected size ${file.expected_size}.`); } // Detect missing file chunks. - const actualSize = Math.max(file.data.size, file.expectedSize || 0); + const actual_size = Math.max(file.data.size, file.expected_size || 0); - for (let chunkNo = 0; chunkNo < Math.ceil(actualSize / 1024); ++chunkNo) { - if (!file.chunkNos.has(chunkNo)) { - logger.warn(`File ${file.name} is missing chunk ${chunkNo}.`); + for (let chunk_no = 0; chunk_no < Math.ceil(actual_size / 1024); ++chunk_no) { + if (!file.chunk_nos.has(chunk_no)) { + logger.warn(`File ${file.name} is missing chunk ${chunk_no}.`); } } } @@ -212,7 +213,7 @@ function parseFiles(cursor: BufferCursor, expectedSizes: Map): Q return Array.from(files.values()); } -function writeFileHeaders(cursor: BufferCursor, files: SimpleQstContainedFile[]): void { +function write_file_headers(cursor: BufferCursor, files: SimpleQstContainedFile[]): void { for (const file of files) { if (file.name.length > 16) { throw Error(`File ${file.name} has a name longer than 16 characters.`); @@ -220,7 +221,7 @@ function writeFileHeaders(cursor: BufferCursor, files: SimpleQstContainedFile[]) cursor.write_u16(88); // Header size. cursor.write_u16(0x44); // Magic number. - cursor.write_u16(file.questNo || 0); + cursor.write_u16(file.id || 0); for (let i = 0; i < 38; ++i) { cursor.write_u8(0); @@ -229,40 +230,40 @@ function writeFileHeaders(cursor: BufferCursor, files: SimpleQstContainedFile[]) cursor.write_string_ascii(file.name, 16); cursor.write_u32(file.data.size); - let fileName2: string; + let file_name_2: string; - if (file.name2 == null) { + if (file.name_2 == null) { // Not sure this makes sense. - const dotPos = file.name.lastIndexOf('.'); - fileName2 = dotPos === -1 + const dot_pos = file.name.lastIndexOf('.'); + file_name_2 = dot_pos === -1 ? file.name + '_j' - : file.name.slice(0, dotPos) + '_j' + file.name.slice(dotPos); + : file.name.slice(0, dot_pos) + '_j' + file.name.slice(dot_pos); } else { - fileName2 = file.name2; + file_name_2 = file.name_2; } - if (fileName2.length > 24) { - throw Error(`File ${file.name} has a fileName2 length (${fileName2}) longer than 24 characters.`); + if (file_name_2.length > 24) { + throw Error(`File ${file.name} has a file_name_2 length (${file_name_2}) longer than 24 characters.`); } - cursor.write_string_ascii(fileName2, 24); + cursor.write_string_ascii(file_name_2, 24); } } -function writeFileChunks(cursor: BufferCursor, files: SimpleQstContainedFile[]): void { +function write_file_chunks(cursor: BufferCursor, files: SimpleQstContainedFile[]): void { // Files are interleaved in 1056 byte chunks. // Each chunk has a 24 byte header, 1024 byte data segment and an 8 byte trailer. files = files.slice(); - const chunkNos = new Array(files.length).fill(0); + const chunk_nos = new Array(files.length).fill(0); while (files.length) { let i = 0; while (i < files.length) { - if (!writeFileChunk(cursor, files[i].data, chunkNos[i]++, files[i].name)) { + if (!write_file_chunk(cursor, files[i].data, chunk_nos[i]++, files[i].name)) { // Remove if there are no more chunks to write. files.splice(i, 1); - chunkNos.splice(i, 1); + chunk_nos.splice(i, 1); } else { ++i; } @@ -273,14 +274,14 @@ function writeFileChunks(cursor: BufferCursor, files: SimpleQstContainedFile[]): /** * @returns true if there are bytes left to write in data, false otherwise. */ -function writeFileChunk( +function write_file_chunk( cursor: BufferCursor, data: BufferCursor, - chunkNo: number, + chunk_no: number, name: string ): boolean { cursor.write_u8_array([28, 4, 19, 0]); - cursor.write_u8(chunkNo); + cursor.write_u8(chunk_no); cursor.write_u8_array([0, 0, 0]); cursor.write_string_ascii(name, 16); @@ -295,5 +296,5 @@ function writeFileChunk( cursor.write_u32(size); cursor.write_u32(0); - return !!data.bytes_left; + return data.bytes_left > 0; } diff --git a/src/data_formats/parsing/unitxt.ts b/src/data_formats/parsing/unitxt.ts index 22990301..c70a4b83 100644 --- a/src/data_formats/parsing/unitxt.ts +++ b/src/data_formats/parsing/unitxt.ts @@ -3,27 +3,27 @@ import { decompress } from "../compression/prs"; export type Unitxt = string[][]; -export function parseUnitxt(buf: BufferCursor, compressed: boolean = true): Unitxt { +export function parse_unitxt(buf: BufferCursor, compressed: boolean = true): Unitxt { if (compressed) { buf = decompress(buf); } - const categoryCount = buf.u32(); - const entryCounts = buf.u32_array(categoryCount); - const categoryEntryOffsets: Array> = []; + const category_count = buf.u32(); + const entry_counts = buf.u32_array(category_count); + const category_entry_offsets: Array> = []; - for (const entryCount of entryCounts) { - categoryEntryOffsets.push(buf.u32_array(entryCount)); + for (const entry_count of entry_counts) { + category_entry_offsets.push(buf.u32_array(entry_count)); } const categories: Unitxt = []; - for (const categoryEntryOffset of categoryEntryOffsets) { + for (const category_entry_offset of category_entry_offsets) { const entries: string[] = []; categories.push(entries); - for (const entryOffset of categoryEntryOffset) { - buf.seek_start(entryOffset); + for (const entry_offset of category_entry_offset) { + buf.seek_start(entry_offset); const str = buf.string_utf16(1024, true, true); entries.push(str); } diff --git a/src/domain/NpcType.ts b/src/domain/NpcType.ts index 42afdeda..ad448947 100644 --- a/src/domain/NpcType.ts +++ b/src/domain/NpcType.ts @@ -19,7 +19,7 @@ export class NpcType { readonly ultimate_name: string; readonly episode?: number; readonly enemy: boolean; - rareType?: NpcType; + rare_type?: NpcType; constructor( id: number, @@ -300,10 +300,10 @@ export class NpcType { NpcType.Hildebear = new NpcType(id++, 'Hildebear', 'Hildebear', 'Hildebear', 'Hildelt', 1, true); NpcType.Hildeblue = new NpcType(id++, 'Hildeblue', 'Hildeblue', 'Hildeblue', 'Hildetorr', 1, true); - NpcType.Hildebear.rareType = NpcType.Hildeblue; + NpcType.Hildebear.rare_type = NpcType.Hildeblue; NpcType.RagRappy = new NpcType(id++, 'RagRappy', 'Rag Rappy', 'Rag Rappy', 'El Rappy', 1, true); NpcType.AlRappy = new NpcType(id++, 'AlRappy', 'Al Rappy', 'Al Rappy', 'Pal Rappy', 1, true); - NpcType.RagRappy.rareType = NpcType.AlRappy; + NpcType.RagRappy.rare_type = NpcType.AlRappy; NpcType.Monest = new NpcType(id++, 'Monest', 'Monest', 'Monest', 'Mothvist', 1, true); NpcType.Mothmant = new NpcType(id++, 'Mothmant', 'Mothmant', 'Mothmant', 'Mothvert', 1, true); NpcType.SavageWolf = new NpcType(id++, 'SavageWolf', 'Savage Wolf', 'Savage Wolf', 'Gulgus', 1, true); @@ -318,14 +318,14 @@ export class NpcType { NpcType.GrassAssassin = new NpcType(id++, 'GrassAssassin', 'Grass Assassin', 'Grass Assassin', 'Crimson Assassin', 1, true); NpcType.PoisonLily = new NpcType(id++, 'PoisonLily', 'Poison Lily', 'Poison Lily', 'Ob Lily', 1, true); NpcType.NarLily = new NpcType(id++, 'NarLily', 'Nar Lily', 'Nar Lily', 'Mil Lily', 1, true); - NpcType.PoisonLily.rareType = NpcType.NarLily; + NpcType.PoisonLily.rare_type = NpcType.NarLily; NpcType.NanoDragon = new NpcType(id++, 'NanoDragon', 'Nano Dragon', 'Nano Dragon', 'Nano Dragon', 1, true); NpcType.EvilShark = new NpcType(id++, 'EvilShark', 'Evil Shark', 'Evil Shark', 'Vulmer', 1, true); NpcType.PalShark = new NpcType(id++, 'PalShark', 'Pal Shark', 'Pal Shark', 'Govulmer', 1, true); NpcType.GuilShark = new NpcType(id++, 'GuilShark', 'Guil Shark', 'Guil Shark', 'Melqueek', 1, true); NpcType.PofuillySlime = new NpcType(id++, 'PofuillySlime', 'Pofuilly Slime', 'Pofuilly Slime', 'Pofuilly Slime', 1, true); NpcType.PouillySlime = new NpcType(id++, 'PouillySlime', 'Pouilly Slime', 'Pouilly Slime', 'Pouilly Slime', 1, true); - NpcType.PofuillySlime.rareType = NpcType.PouillySlime; + NpcType.PofuillySlime.rare_type = NpcType.PouillySlime; NpcType.PanArms = new NpcType(id++, 'PanArms', 'Pan Arms', 'Pan Arms', 'Pan Arms', 1, true); NpcType.Migium = new NpcType(id++, 'Migium', 'Migium', 'Migium', 'Migium', 1, true); NpcType.Hidoom = new NpcType(id++, 'Hidoom', 'Hidoom', 'Hidoom', 'Hidoom', 1, true); @@ -363,10 +363,10 @@ export class NpcType { NpcType.Hildebear2 = new NpcType(id++, 'Hildebear2', 'Hildebear (Ep. II)', 'Hildebear', 'Hildelt', 2, true); NpcType.Hildeblue2 = new NpcType(id++, 'Hildeblue2', 'Hildeblue (Ep. II)', 'Hildeblue', 'Hildetorr', 2, true); - NpcType.Hildebear2.rareType = NpcType.Hildeblue2; + NpcType.Hildebear2.rare_type = NpcType.Hildeblue2; NpcType.RagRappy2 = new NpcType(id++, 'RagRappy2', 'Rag Rappy (Ep. II)', 'Rag Rappy', 'El Rappy', 2, true); NpcType.LoveRappy = new NpcType(id++, 'LoveRappy', 'Love Rappy', 'Love Rappy', 'Love Rappy', 2, true); - NpcType.RagRappy2.rareType = NpcType.LoveRappy; + NpcType.RagRappy2.rare_type = NpcType.LoveRappy; NpcType.StRappy = new NpcType(id++, 'StRappy', 'St. Rappy', 'St. Rappy', 'St. Rappy', 2, true); NpcType.HalloRappy = new NpcType(id++, 'HalloRappy', 'Hallo Rappy', 'Hallo Rappy', 'Hallo Rappy', 2, true); NpcType.EggRappy = new NpcType(id++, 'EggRappy', 'Egg Rappy', 'Egg Rappy', 'Egg Rappy', 2, true); @@ -374,7 +374,7 @@ export class NpcType { NpcType.Mothmant2 = new NpcType(id++, 'Mothmant2', 'Mothmant', 'Mothmant', 'Mothvert', 2, true); NpcType.PoisonLily2 = new NpcType(id++, 'PoisonLily2', 'Poison Lily (Ep. II)', 'Poison Lily', 'Ob Lily', 2, true); NpcType.NarLily2 = new NpcType(id++, 'NarLily2', 'Nar Lily (Ep. II)', 'Nar Lily', 'Mil Lily', 2, true); - NpcType.PoisonLily2.rareType = NpcType.NarLily2; + NpcType.PoisonLily2.rare_type = NpcType.NarLily2; NpcType.GrassAssassin2 = new NpcType(id++, 'GrassAssassin2', 'Grass Assassin (Ep. II)', 'Grass Assassin', 'Crimson Assassin', 2, true); NpcType.Dimenian2 = new NpcType(id++, 'Dimenian2', 'Dimenian (Ep. II)', 'Dimenian', 'Arlan', 2, true); NpcType.LaDimenian2 = new NpcType(id++, 'LaDimenian2', 'La Dimenian (Ep. II)', 'La Dimenian', 'Merlan', 2, true); @@ -433,31 +433,31 @@ export class NpcType { NpcType.SandRappy = new NpcType(id++, 'SandRappy', 'Sand Rappy', 'Sand Rappy', 'Sand Rappy', 4, true); NpcType.DelRappy = new NpcType(id++, 'DelRappy', 'Del Rappy', 'Del Rappy', 'Del Rappy', 4, true); - NpcType.SandRappy.rareType = NpcType.DelRappy; + NpcType.SandRappy.rare_type = NpcType.DelRappy; NpcType.Astark = new NpcType(id++, 'Astark', 'Astark', 'Astark', 'Astark', 4, true); NpcType.SatelliteLizard = new NpcType(id++, 'SatelliteLizard', 'Satellite Lizard', 'Satellite Lizard', 'Satellite Lizard', 4, true); NpcType.Yowie = new NpcType(id++, 'Yowie', 'Yowie', 'Yowie', 'Yowie', 4, true); NpcType.MerissaA = new NpcType(id++, 'MerissaA', 'Merissa A', 'Merissa A', 'Merissa A', 4, true); NpcType.MerissaAA = new NpcType(id++, 'MerissaAA', 'Merissa AA', 'Merissa AA', 'Merissa AA', 4, true); - NpcType.MerissaA.rareType = NpcType.MerissaAA; + NpcType.MerissaA.rare_type = NpcType.MerissaAA; NpcType.Girtablulu = new NpcType(id++, 'Girtablulu', 'Girtablulu', 'Girtablulu', 'Girtablulu', 4, true); NpcType.Zu = new NpcType(id++, 'Zu', 'Zu', 'Zu', 'Zu', 4, true); NpcType.Pazuzu = new NpcType(id++, 'Pazuzu', 'Pazuzu', 'Pazuzu', 'Pazuzu', 4, true); - NpcType.Zu.rareType = NpcType.Pazuzu; + NpcType.Zu.rare_type = NpcType.Pazuzu; NpcType.Boota = new NpcType(id++, 'Boota', 'Boota', 'Boota', 'Boota', 4, true); NpcType.ZeBoota = new NpcType(id++, 'ZeBoota', 'Ze Boota', 'Ze Boota', 'Ze Boota', 4, true); NpcType.BaBoota = new NpcType(id++, 'BaBoota', 'Ba Boota', 'Ba Boota', 'Ba Boota', 4, true); NpcType.Dorphon = new NpcType(id++, 'Dorphon', 'Dorphon', 'Dorphon', 'Dorphon', 4, true); NpcType.DorphonEclair = new NpcType(id++, 'DorphonEclair', 'Dorphon Eclair', 'Dorphon Eclair', 'Dorphon Eclair', 4, true); - NpcType.Dorphon.rareType = NpcType.DorphonEclair; + NpcType.Dorphon.rare_type = NpcType.DorphonEclair; NpcType.Goran = new NpcType(id++, 'Goran', 'Goran', 'Goran', 'Goran', 4, true); NpcType.PyroGoran = new NpcType(id++, 'PyroGoran', 'Pyro Goran', 'Pyro Goran', 'Pyro Goran', 4, true); NpcType.GoranDetonator = new NpcType(id++, 'GoranDetonator', 'Goran Detonator', 'Goran Detonator', 'Goran Detonator', 4, true); NpcType.SaintMilion = new NpcType(id++, 'SaintMilion', 'Saint-Milion', 'Saint-Milion', 'Saint-Milion', 4, true); NpcType.Shambertin = new NpcType(id++, 'Shambertin', 'Shambertin', 'Shambertin', 'Shambertin', 4, true); NpcType.Kondrieu = new NpcType(id++, 'Kondrieu', 'Kondrieu', 'Kondrieu', 'Kondrieu', 4, true); - NpcType.SaintMilion.rareType = NpcType.Kondrieu; - NpcType.Shambertin.rareType = NpcType.Kondrieu; + NpcType.SaintMilion.rare_type = NpcType.Kondrieu; + NpcType.Shambertin.rare_type = NpcType.Kondrieu; }()); export const NpcTypes: Array = [ diff --git a/src/domain/index.ts b/src/domain/index.ts index 21773e19..5109ceb1 100644 --- a/src/domain/index.ts +++ b/src/domain/index.ts @@ -4,8 +4,9 @@ import { BufferCursor } from '../data_formats/BufferCursor'; import { DatNpc, DatObject, DatUnknown } from '../data_formats/parsing/quest/dat'; import { NpcType } from './NpcType'; import { ObjectType } from './ObjectType'; -import { enumValues as enum_values } from '../enums'; +import { enum_values } from '../enums'; import { ItemType } from './items'; +import { Vec3 } from '../data_formats/Vec3'; export * from './items'; export * from './NpcType'; @@ -55,29 +56,6 @@ export enum Difficulty { export const Difficulties: Difficulty[] = enum_values(Difficulty); -export class Vec3 { - x: number; - y: number; - z: number; - - constructor(x: number, y: number, z: number) { - this.x = x; - this.y = y; - this.z = z; - } - - add(v: Vec3): Vec3 { - this.x += v.x; - this.y += v.y; - this.z += v.z; - return this; - } - - clone() { - return new Vec3(this.x, this.y, this.z); - } -}; - export class Section { id: number; @observable position: Vec3; @@ -108,10 +86,10 @@ export class Section { } export class Quest { + @observable id?: number; @observable name: string; @observable short_description: string; @observable long_description: string; - @observable quest_no?: number; @observable episode: Episode; @observable area_variants: AreaVariant[]; @observable objects: QuestObject[]; @@ -126,10 +104,10 @@ export class Quest { bin_data: BufferCursor; constructor( + id: number | undefined, name: string, short_description: string, long_description: string, - quest_no: number | undefined, episode: Episode, area_variants: AreaVariant[], objects: QuestObject[], @@ -137,15 +115,15 @@ export class Quest { dat_unknowns: DatUnknown[], bin_data: BufferCursor ) { - if (quest_no != null && (!Number.isInteger(quest_no) || quest_no < 0)) throw new Error('quest_no should be null or a non-negative integer.'); + if (id != null && (!Number.isInteger(id) || id < 0)) throw new Error('id should be undefined or a non-negative integer.'); check_episode(episode); if (!objects || !(objects instanceof Array)) throw new Error('objs is required.'); if (!npcs || !(npcs instanceof Array)) throw new Error('npcs is required.'); + this.id = id; this.name = name; this.short_description = short_description; this.long_description = long_description; - this.quest_no = quest_no; this.episode = episode; this.area_variants = area_variants; this.objects = objects; diff --git a/src/domain/items.ts b/src/domain/items.ts index 40a1a9d3..7da2aae7 100644 --- a/src/domain/items.ts +++ b/src/domain/items.ts @@ -15,11 +15,11 @@ export class WeaponItemType implements ItemType { constructor( readonly id: number, readonly name: string, - readonly minAtp: number, - readonly maxAtp: number, + readonly min_atp: number, + readonly max_atp: number, readonly ata: number, - readonly maxGrind: number, - readonly requiredAtp: number, + readonly max_grind: number, + readonly required_atp: number, ) { } } @@ -29,10 +29,10 @@ export class ArmorItemType implements ItemType { readonly name: string, readonly atp: number, readonly ata: number, - readonly minEvp: number, - readonly maxEvp: number, - readonly minDfp: number, - readonly maxDfp: number, + readonly min_evp: number, + readonly max_evp: number, + readonly min_dfp: number, + readonly max_dfp: number, readonly mst: number, readonly hp: number, readonly lck: number, @@ -45,10 +45,10 @@ export class ShieldItemType implements ItemType { readonly name: string, readonly atp: number, readonly ata: number, - readonly minEvp: number, - readonly maxEvp: number, - readonly minDfp: number, - readonly maxDfp: number, + readonly min_evp: number, + readonly max_evp: number, + readonly min_dfp: number, + readonly max_dfp: number, readonly mst: number, readonly hp: number, readonly lck: number, @@ -90,7 +90,7 @@ export class WeaponItem implements Item { @observable hit: number = 0; @observable grind: number = 0; - @computed get grindAtp(): number { + @computed get grind_atp(): number { return 2 * this.grind; } diff --git a/src/enums.ts b/src/enums.ts index ba634ed7..30439412 100644 --- a/src/enums.ts +++ b/src/enums.ts @@ -1,15 +1,15 @@ -export function enumValues(e: any): E[] { +export function enum_values(e: any): E[] { const values = Object.values(e); - const numberValues = values.filter(v => typeof v === 'number'); + const number_values = values.filter(v => typeof v === 'number'); - if (numberValues.length) { - return numberValues as any as E[]; + if (number_values.length) { + return number_values as any as E[]; } else { return values as any as E[]; } } -export function enumNames(e: any): string[] { +export function enum_names(e: any): string[] { return Object.keys(e).filter(k => typeof (e as any)[k] === 'string'); } @@ -20,11 +20,11 @@ export class EnumMap { private keys: K[]; private values = new Map(); - constructor(enum_: any, initialValue: (key: K) => V) { - this.keys = enumValues(enum_); + constructor(enum_: any, initial_value: (key: K) => V) { + this.keys = enum_values(enum_); for (const key of this.keys) { - this.values.set(key, initialValue(key)); + this.values.set(key, initial_value(key)); } } diff --git a/src/rendering/QuestRenderer.ts b/src/rendering/QuestRenderer.ts index 4e70805d..2688fc76 100644 --- a/src/rendering/QuestRenderer.ts +++ b/src/rendering/QuestRenderer.ts @@ -1,5 +1,6 @@ import { Intersection, Mesh, MeshLambertMaterial, Object3D, Plane, Raycaster, Vector2, Vector3 } from "three"; -import { Area, Quest, QuestEntity, QuestNpc, QuestObject, Section, Vec3 } from "../domain"; +import { Area, Quest, QuestEntity, QuestNpc, QuestObject, Section } from "../domain"; +import { Vec3 } from "../data_formats/Vec3"; import { area_store } from "../stores/AreaStore"; import { quest_editor_store } from "../stores/QuestEditorStore"; import { NPC_COLOR, NPC_HOVER_COLOR, NPC_SELECTED_COLOR, OBJECT_COLOR, OBJECT_HOVER_COLOR, OBJECT_SELECTED_COLOR } from "./entities"; diff --git a/src/rendering/entities.test.ts b/src/rendering/entities.test.ts index d06bc31b..bf5f84c3 100644 --- a/src/rendering/entities.test.ts +++ b/src/rendering/entities.test.ts @@ -1,6 +1,7 @@ import { CylinderBufferGeometry, MeshLambertMaterial, Object3D, Vector3 } from 'three'; import { DatNpc, DatObject } from '../data_formats/parsing/quest/dat'; -import { NpcType, ObjectType, QuestNpc, QuestObject, Vec3 } from '../domain'; +import { NpcType, ObjectType, QuestNpc, QuestObject } from '../domain'; +import { Vec3 } from "../data_formats/Vec3"; import { create_npc_mesh, create_object_mesh, NPC_COLOR, OBJECT_COLOR } from './entities'; const cylinder = new CylinderBufferGeometry(3, 3, 20).translate(0, 10, 0); diff --git a/src/rendering/index.ts b/src/rendering/index.ts index 78e04390..8556f67f 100644 --- a/src/rendering/index.ts +++ b/src/rendering/index.ts @@ -1,4 +1,4 @@ -import { Vec3 } from "../domain"; +import { Vec3 } from "../data_formats/Vec3"; import { Vector3 } from "three"; export function vec3_to_threejs(v: Vec3): Vector3 { diff --git a/src/stores/ApplicationStore.ts b/src/stores/ApplicationStore.ts index be837fff..33d76d4c 100644 --- a/src/stores/ApplicationStore.ts +++ b/src/stores/ApplicationStore.ts @@ -2,7 +2,7 @@ import { observable } from "mobx"; import { Server } from "../domain"; class ApplicationStore { - @observable currentServer: Server = Server.Ephinea; + @observable current_server: Server = Server.Ephinea; } -export const applicationStore = new ApplicationStore(); +export const application_store = new ApplicationStore(); diff --git a/src/stores/AreaStore.ts b/src/stores/AreaStore.ts index 8055abba..94856d00 100644 --- a/src/stores/AreaStore.ts +++ b/src/stores/AreaStore.ts @@ -1,9 +1,9 @@ import { Area, AreaVariant, Section } from '../domain'; import { Object3D } from 'three'; -import { parseCRel, parseNRel } from '../data_formats/parsing/geometry'; +import { parse_c_rel, parse_n_rel } from '../data_formats/parsing/geometry'; import { get_area_render_data, get_area_collision_data } from './binary_assets'; -function area(id: number, name: string, order: number, variants: number) { +function area(id: number, name: string, order: number, variants: number): Area { const area = new Area(id, name, order, []); const varis = Array(variants).fill(null).map((_, i) => new AreaVariant(i, area)); area.area_variants.splice(0, 0, ...varis); @@ -77,7 +77,7 @@ class AreaStore { ]; } - get_variant(episode: number, area_id: number, variant_id: number) { + get_variant(episode: number, area_id: number, variant_id: number): AreaVariant { if (episode !== 1 && episode !== 2 && episode !== 4) throw new Error(`Expected episode to be 1, 2 or 4, got ${episode}.`); @@ -120,7 +120,7 @@ class AreaStore { } else { return this.get_area_sections_and_render_geometry( episode, area_id, area_variant - ).then(({ object3d }) => object3d); + ).then(({ object_3d }) => object_3d); } } @@ -136,7 +136,7 @@ class AreaStore { } else { const object_3d = get_area_collision_data( episode, area_id, area_variant - ).then(parseCRel); + ).then(parse_c_rel); collision_geometry_cache.set(`${area_id}-${area_variant}`, object_3d); return object_3d; } @@ -146,16 +146,16 @@ class AreaStore { episode: number, area_id: number, area_variant: number - ): Promise<{ sections: Section[], object3d: Object3D }> { + ): Promise<{ sections: Section[], object_3d: Object3D }> { const promise = get_area_render_data( episode, area_id, area_variant - ).then(parseNRel); + ).then(parse_n_rel); const sections = new Promise((resolve, reject) => { promise.then(({ sections }) => resolve(sections)).catch(reject); }); const object_3d = new Promise((resolve, reject) => { - promise.then(({ object3d }) => resolve(object3d)).catch(reject); + promise.then(({ object_3d }) => resolve(object_3d)).catch(reject); }); sections_cache.set(`${episode}-${area_id}-${area_variant}`, sections); diff --git a/src/stores/DpsCalcStore.ts b/src/stores/DpsCalcStore.ts index 0d80eee1..ff60c2ee 100644 --- a/src/stores/DpsCalcStore.ts +++ b/src/stores/DpsCalcStore.ts @@ -1,6 +1,6 @@ import { observable, IObservableArray, computed } from "mobx"; import { WeaponItem, WeaponItemType, ArmorItemType, ShieldItemType } from "../domain"; -import { itemTypeStores } from "./ItemTypeStore"; +import { item_type_stores } from "./ItemTypeStore"; const NORMAL_DAMAGE_FACTOR = 0.2 * 0.9; const HEAVY_DAMAGE_FACTOR = NORMAL_DAMAGE_FACTOR * 1.89; @@ -11,60 +11,60 @@ const HEAVY_DAMAGE_FACTOR = NORMAL_DAMAGE_FACTOR * 1.89; class Weapon { readonly item: WeaponItem; - @computed get shiftaAtp(): number { - if (this.item.type.minAtp === this.item.type.maxAtp) { + @computed get shifta_atp(): number { + if (this.item.type.min_atp === this.item.type.max_atp) { return 0; } else { - return this.item.type.maxAtp * this.store.shiftaFactor; + return this.item.type.max_atp * this.store.shifta_factor; } } - @computed get minAtp(): number { - return this.item.type.minAtp + this.item.grindAtp; + @computed get min_atp(): number { + return this.item.type.min_atp + this.item.grind_atp; } - @computed get maxAtp(): number { - return this.item.type.maxAtp + this.item.grindAtp + this.shiftaAtp; + @computed get max_atp(): number { + return this.item.type.max_atp + this.item.grind_atp + this.shifta_atp; } - @computed get finalMinAtp(): number { - return this.minAtp - + this.store.armorAtp - + this.store.shieldAtp - + this.store.baseAtp - + this.store.baseShiftaAtp; + @computed get final_min_atp(): number { + return this.min_atp + + this.store.armor_atp + + this.store.shield_atp + + this.store.base_atp + + this.store.base_shifta_atp; } - @computed get finalMaxAtp(): number { - return this.maxAtp - + this.store.armorAtp - + this.store.shieldAtp - + this.store.baseAtp - + this.store.baseShiftaAtp; + @computed get final_max_atp(): number { + return this.max_atp + + this.store.armor_atp + + this.store.shield_atp + + this.store.base_atp + + this.store.base_shifta_atp; } - @computed get minNormalDamage(): number { - return (this.finalMinAtp - this.store.enemyDfp) * NORMAL_DAMAGE_FACTOR; + @computed get min_normal_damage(): number { + return (this.final_min_atp - this.store.enemy_dfp) * NORMAL_DAMAGE_FACTOR; } - @computed get maxNormalDamage(): number { - return (this.finalMaxAtp - this.store.enemyDfp) * NORMAL_DAMAGE_FACTOR; + @computed get max_normal_damage(): number { + return (this.final_max_atp - this.store.enemy_dfp) * NORMAL_DAMAGE_FACTOR; } - @computed get avgNormalDamage(): number { - return (this.minNormalDamage + this.maxNormalDamage) / 2; + @computed get avg_normal_damage(): number { + return (this.min_normal_damage + this.max_normal_damage) / 2; } - @computed get minHeavyDamage(): number { - return (this.finalMinAtp - this.store.enemyDfp) * HEAVY_DAMAGE_FACTOR; + @computed get min_heavy_damage(): number { + return (this.final_min_atp - this.store.enemy_dfp) * HEAVY_DAMAGE_FACTOR; } - @computed get maxHeavyDamage(): number { - return (this.finalMaxAtp - this.store.enemyDfp) * HEAVY_DAMAGE_FACTOR; + @computed get max_heavy_damage(): number { + return (this.final_max_atp - this.store.enemy_dfp) * HEAVY_DAMAGE_FACTOR; } - @computed get avgHeavyDamage(): number { - return (this.minHeavyDamage + this.maxHeavyDamage) / 2; + @computed get avg_heavy_damage(): number { + return (this.min_heavy_damage + this.max_heavy_damage) / 2; } constructor( @@ -76,20 +76,20 @@ class Weapon { } class DpsCalcStore { - @computed get weaponTypes(): WeaponItemType[] { - return itemTypeStores.current.value.itemTypes.filter(it => + @computed get weapon_types(): WeaponItemType[] { + return item_type_stores.current.value.item_types.filter(it => it instanceof WeaponItemType ) as WeaponItemType[]; } - @computed get armorTypes(): ArmorItemType[] { - return itemTypeStores.current.value.itemTypes.filter(it => + @computed get armor_types(): ArmorItemType[] { + return item_type_stores.current.value.item_types.filter(it => it instanceof ArmorItemType ) as ArmorItemType[]; } - @computed get shieldTypes(): ShieldItemType[] { - return itemTypeStores.current.value.itemTypes.filter(it => + @computed get shield_types(): ShieldItemType[] { + return item_type_stores.current.value.item_types.filter(it => it instanceof ShieldItemType ) as ShieldItemType[]; } @@ -98,41 +98,41 @@ class DpsCalcStore { // Character Details // - @observable charAtp: number = 0; - @observable magPow: number = 0; - @computed get armorAtp(): number { return this.armorType ? this.armorType.atp : 0 } - @computed get shieldAtp(): number { return this.shieldType ? this.shieldType.atp : 0 } - @observable shiftaLvl: number = 0; + @observable char_atp: number = 0; + @observable mag_pow: number = 0; + @computed get armor_atp(): number { return this.armor_type ? this.armor_type.atp : 0 } + @computed get shield_atp(): number { return this.shield_type ? this.shield_type.atp : 0 } + @observable shifta_lvl: number = 0; - @computed get baseAtp(): number { - return this.charAtp + 2 * this.magPow; + @computed get base_atp(): number { + return this.char_atp + 2 * this.mag_pow; } - @computed get shiftaFactor(): number { - return this.shiftaLvl ? 0.013 * (this.shiftaLvl - 1) + 0.1 : 0; + @computed get shifta_factor(): number { + return this.shifta_lvl ? 0.013 * (this.shifta_lvl - 1) + 0.1 : 0; } - @computed get baseShiftaAtp(): number { - return this.baseAtp * this.shiftaFactor; + @computed get base_shifta_atp(): number { + return this.base_atp * this.shifta_factor; } @observable readonly weapons: IObservableArray = observable.array(); - addWeapon = (type: WeaponItemType) => { + add_weapon = (type: WeaponItemType) => { this.weapons.push(new Weapon( this, new WeaponItem(type) )); } - @observable armorType?: ArmorItemType; - @observable shieldType?: ShieldItemType; + @observable armor_type?: ArmorItemType; + @observable shield_type?: ShieldItemType; // // Enemy Details // - @observable enemyDfp: number = 0; + @observable enemy_dfp: number = 0; } -export const dpsCalcStore = new DpsCalcStore(); +export const dps_calc_store = new DpsCalcStore(); diff --git a/src/stores/HuntMethodStore.ts b/src/stores/HuntMethodStore.ts index 5182f47e..df0fc360 100644 --- a/src/stores/HuntMethodStore.ts +++ b/src/stores/HuntMethodStore.ts @@ -9,12 +9,12 @@ const logger = Logger.get('stores/HuntMethodStore'); class HuntMethodStore { @observable methods: ServerMap>> = new ServerMap(server => - new Loadable([], () => this.loadHuntMethods(server)) + new Loadable([], () => this.load_hunt_methods(server)) ); - private storageDisposer?: IReactionDisposer; + private storage_disposer?: IReactionDisposer; - private async loadHuntMethods(server: Server): Promise { + private async load_hunt_methods(server: Server): Promise { const response = await fetch( `${process.env.PUBLIC_URL}/quests.${Server[server].toLowerCase()}.json` ); @@ -22,17 +22,17 @@ class HuntMethodStore { const methods = new Array(); for (const quest of quests) { - let totalCount = 0; - const enemyCounts = new Map(); + let total_count = 0; + const enemy_counts = new Map(); for (const [code, count] of Object.entries(quest.enemyCounts)) { - const npcType = NpcType.by_code(code); + const npc_type = NpcType.by_code(code); - if (!npcType) { + if (!npc_type) { logger.error(`No NpcType found for code ${code}.`); } else { - enemyCounts.set(npcType, count); - totalCount += count; + enemy_counts.set(npc_type, count); + total_count += count; } } @@ -61,56 +61,56 @@ class HuntMethodStore { quest.id, quest.name, quest.episode, - enemyCounts + enemy_counts ), - /^\d-\d.*/.test(quest.name) ? 0.75 : (totalCount > 400 ? 0.75 : 0.5) + /^\d-\d.*/.test(quest.name) ? 0.75 : (total_count > 400 ? 0.75 : 0.5) ) ); } - this.loadFromLocalStorage(methods, server); + this.load_from_local_storage(methods, server); return methods; } - private loadFromLocalStorage = (methods: HuntMethod[], server: Server) => { + private load_from_local_storage = (methods: HuntMethod[], server: Server) => { try { - const methodUserTimesJson = localStorage.getItem( + const method_user_times_json = localStorage.getItem( `HuntMethodStore.methodUserTimes.${Server[server]}` ); - if (methodUserTimesJson) { - const userTimes = JSON.parse(methodUserTimesJson); + if (method_user_times_json) { + const user_times: StoredUserTimes = JSON.parse(method_user_times_json); for (const method of methods) { - method.user_time = userTimes[method.id] as number; + method.user_time = user_times[method.id]; } } - if (this.storageDisposer) { - this.storageDisposer(); + if (this.storage_disposer) { + this.storage_disposer(); } - this.storageDisposer = autorun(() => - this.storeInLocalStorage(methods, server) + this.storage_disposer = autorun(() => + this.store_in_local_storage(methods, server) ); } catch (e) { logger.error(e); } } - private storeInLocalStorage = (methods: HuntMethod[], server: Server) => { + private store_in_local_storage = (methods: HuntMethod[], server: Server) => { try { - const userTimes: any = {}; + const user_times: StoredUserTimes = {}; for (const method of methods) { - if (method.user_time != null) { - userTimes[method.id] = method.user_time; + if (method.user_time != undefined) { + user_times[method.id] = method.user_time; } } localStorage.setItem( `HuntMethodStore.methodUserTimes.${Server[server]}`, - JSON.stringify(userTimes) + JSON.stringify(user_times) ); } catch (e) { logger.error(e); @@ -118,4 +118,6 @@ class HuntMethodStore { } } -export const huntMethodStore = new HuntMethodStore(); +type StoredUserTimes = { [method_id: string]: number }; + +export const hunt_method_store = new HuntMethodStore(); diff --git a/src/stores/HuntOptimizerStore.ts b/src/stores/HuntOptimizerStore.ts index 8b0f84e0..59737a8d 100644 --- a/src/stores/HuntOptimizerStore.ts +++ b/src/stores/HuntOptimizerStore.ts @@ -1,44 +1,44 @@ import solver from 'javascript-lp-solver'; import { autorun, IObservableArray, observable, computed } from "mobx"; import { Difficulties, Difficulty, HuntMethod, ItemType, KONDRIEU_PROB, NpcType, RARE_ENEMY_PROB, SectionId, SectionIds, Server, Episode } from "../domain"; -import { applicationStore } from './ApplicationStore'; -import { huntMethodStore } from "./HuntMethodStore"; -import { itemDropStores } from './ItemDropStore'; -import { itemTypeStores } from './ItemTypeStore'; +import { application_store } from './ApplicationStore'; +import { hunt_method_store } from "./HuntMethodStore"; +import { item_drop_stores as item_drop_stores } from './ItemDropStore'; +import { item_type_stores } from './ItemTypeStore'; import Logger from 'js-logger'; const logger = Logger.get('stores/HuntOptimizerStore'); export class WantedItem { - @observable readonly itemType: ItemType; + @observable readonly item_type: ItemType; @observable amount: number; - constructor(itemType: ItemType, amount: number) { - this.itemType = itemType; + constructor(item_type: ItemType, amount: number) { + this.item_type = item_type; this.amount = amount; } } export class OptimalResult { constructor( - readonly wantedItems: Array, - readonly optimalMethods: Array + readonly wanted_items: Array, + readonly optimal_methods: Array ) { } } export class OptimalMethod { - readonly totalTime: number; + readonly total_time: number; constructor( readonly difficulty: Difficulty, - readonly sectionIds: Array, - readonly methodName: string, - readonly methodEpisode: Episode, - readonly methodTime: number, + readonly section_ids: Array, + readonly method_name: string, + readonly method_episode: Episode, + readonly method_time: number, readonly runs: number, - readonly itemCounts: Map + readonly item_counts: Map ) { - this.totalTime = runs * methodTime; + this.total_time = runs * method_time; } } @@ -50,15 +50,15 @@ export class OptimalMethod { // Can be useful when deciding which item to hunt first. // TODO: boxes. class HuntOptimizerStore { - @computed get huntableItemTypes(): Array { - const itemDropStore = itemDropStores.current.value; - return itemTypeStores.current.value.itemTypes.filter(i => - itemDropStore.enemyDrops.getDropsForItemType(i.id).length + @computed get huntable_item_types(): Array { + const item_drop_store = item_drop_stores.current.value; + return item_type_stores.current.value.item_types.filter(i => + item_drop_store.enemy_drops.get_drops_for_item_type(i.id).length ); } // TODO: wanted items per server. - @observable readonly wantedItems: IObservableArray = observable.array(); + @observable readonly wanted_items: IObservableArray = observable.array(); @observable result?: OptimalResult; constructor() { @@ -66,23 +66,23 @@ class HuntOptimizerStore { } optimize = async () => { - if (!this.wantedItems.length) { + if (!this.wanted_items.length) { this.result = undefined; return; } // Initialize this set before awaiting data, so user changes don't affect this optimization // run from this point on. - const wantedItems = new Set(this.wantedItems.filter(w => w.amount > 0).map(w => w.itemType)); + const wanted_items = new Set(this.wanted_items.filter(w => w.amount > 0).map(w => w.item_type)); - const methods = await huntMethodStore.methods.current.promise; - const dropTable = (await itemDropStores.current.promise).enemyDrops; + const methods = await hunt_method_store.methods.current.promise; + const drop_table = (await item_drop_stores.current.promise).enemy_drops; // Add a constraint per wanted item. - const constraints: { [itemName: string]: { min: number } } = {}; + const constraints: { [item_name: string]: { min: number } } = {}; - for (const wanted of this.wantedItems) { - constraints[wanted.itemType.name] = { min: wanted.amount }; + for (const wanted of this.wanted_items) { + constraints[wanted.item_type.name] = { min: wanted.amount }; } // Add a variable to the LP model per method per difficulty per section ID. @@ -92,107 +92,107 @@ class HuntOptimizerStore { // of enemies that drop the item multiplied by the corresponding drop rate as its value. type Variable = { time: number, - [itemName: string]: number, + [item_name: string]: number, } - const variables: { [methodName: string]: Variable } = {}; + const variables: { [method_name: string]: Variable } = {}; type VariableDetails = { method: HuntMethod, difficulty: Difficulty, - sectionId: SectionId, - splitPanArms: boolean, + section_id: SectionId, + split_pan_arms: boolean, } - const variableDetails: Map = new Map(); + const variable_details: Map = new Map(); for (const method of methods) { // Counts include rare enemies, so they are fractional. const counts = new Map(); for (const [enemy, count] of method.enemy_counts.entries()) { - const oldCount = counts.get(enemy) || 0; + const old_count = counts.get(enemy) || 0; - if (enemy.rareType == null) { - counts.set(enemy, oldCount + count); + if (enemy.rare_type == null) { + counts.set(enemy, old_count + count); } else { - let rate, rareRate; + let rate, rare_rate; - if (enemy.rareType === NpcType.Kondrieu) { + if (enemy.rare_type === NpcType.Kondrieu) { rate = 1 - KONDRIEU_PROB; - rareRate = KONDRIEU_PROB; + rare_rate = KONDRIEU_PROB; } else { rate = 1 - RARE_ENEMY_PROB; - rareRate = RARE_ENEMY_PROB; + rare_rate = RARE_ENEMY_PROB; } - counts.set(enemy, oldCount + count * rate); + counts.set(enemy, old_count + count * rate); counts.set( - enemy.rareType, - (counts.get(enemy.rareType) || 0) + count * rareRate + enemy.rare_type, + (counts.get(enemy.rare_type) || 0) + count * rare_rate ); } } // Create a secondary counts map if there are any pan arms that can be split into // migiums and hidooms. - const countsList: Array> = [counts]; - const panArmsCount = counts.get(NpcType.PanArms); + const counts_list: Array> = [counts]; + const pan_arms_count = counts.get(NpcType.PanArms); - if (panArmsCount) { - const splitCounts = new Map(counts); + if (pan_arms_count) { + const split_counts = new Map(counts); - splitCounts.delete(NpcType.PanArms); - splitCounts.set(NpcType.Migium, panArmsCount); - splitCounts.set(NpcType.Hidoom, panArmsCount); + split_counts.delete(NpcType.PanArms); + split_counts.set(NpcType.Migium, pan_arms_count); + split_counts.set(NpcType.Hidoom, pan_arms_count); - countsList.push(splitCounts); + counts_list.push(split_counts); } - const panArms2Count = counts.get(NpcType.PanArms2); + const pan_arms_2_count = counts.get(NpcType.PanArms2); - if (panArms2Count) { - const splitCounts = new Map(counts); + if (pan_arms_2_count) { + const split_counts = new Map(counts); - splitCounts.delete(NpcType.PanArms2); - splitCounts.set(NpcType.Migium2, panArms2Count); - splitCounts.set(NpcType.Hidoom2, panArms2Count); + split_counts.delete(NpcType.PanArms2); + split_counts.set(NpcType.Migium2, pan_arms_2_count); + split_counts.set(NpcType.Hidoom2, pan_arms_2_count); - countsList.push(splitCounts); + counts_list.push(split_counts); } - for (let i = 0; i < countsList.length; i++) { - const counts = countsList[i]; - const splitPanArms = i === 1; + for (let i = 0; i < counts_list.length; i++) { + const counts = counts_list[i]; + const split_pan_arms = i === 1; - for (const diff of Difficulties) { - for (const sectionId of SectionIds) { + for (const difficulty of Difficulties) { + for (const section_id of SectionIds) { // Will contain an entry per wanted item dropped by enemies in this method/ // difficulty/section ID combo. const variable: Variable = { time: method.time }; // Only add the variable if the method provides at least 1 item we want. - let addVariable = false; + let add_variable = false; - for (const [npcType, count] of counts.entries()) { - const drop = dropTable.getDrop(diff, sectionId, npcType); + for (const [npc_type, count] of counts.entries()) { + const drop = drop_table.get_drop(difficulty, section_id, npc_type); - if (drop && wantedItems.has(drop.item_type)) { + if (drop && wanted_items.has(drop.item_type)) { const value = variable[drop.item_type.name] || 0; variable[drop.item_type.name] = value + count * drop.rate; - addVariable = true; + add_variable = true; } } - if (addVariable) { - const name = this.fullMethodName( - diff, sectionId, method, splitPanArms + if (add_variable) { + const name = this.full_method_name( + difficulty, section_id, method, split_pan_arms ); variables[name] = variable; - variableDetails.set(name, { + variable_details.set(name, { method, - difficulty: diff, - sectionId, - splitPanArms + difficulty, + section_id, + split_pan_arms }); } } @@ -220,23 +220,23 @@ class HuntOptimizerStore { return; } - const optimalMethods: Array = []; + const optimal_methods: Array = []; // Loop over the entries in result, ignore standard properties that aren't variables. - for (const [variableName, runsOrOther] of Object.entries(result)) { - const details = variableDetails.get(variableName); + for (const [variable_name, runs_or_other] of Object.entries(result)) { + const details = variable_details.get(variable_name); if (details) { - const { method, difficulty, sectionId, splitPanArms } = details; - const runs = runsOrOther as number; - const variable = variables[variableName]; + const { method, difficulty, section_id, split_pan_arms } = details; + const runs = runs_or_other as number; + const variable = variables[variable_name]; const items = new Map(); - for (const [itemName, expectedAmount] of Object.entries(variable)) { - for (const item of wantedItems) { - if (itemName === item.name) { - items.set(item, runs * expectedAmount); + for (const [item_name, expected_amount] of Object.entries(variable)) { + for (const item of wanted_items) { + if (item_name === item.name) { + items.set(item, runs * expected_amount); break; } } @@ -245,37 +245,37 @@ class HuntOptimizerStore { // Find all section IDs that provide the same items with the same expected amount. // E.g. if you need a spread needle and a bringer's right arm, using either // purplenum or yellowboze will give you the exact same probabilities. - const sectionIds: Array = []; + const section_ids: Array = []; for (const sid of SectionIds) { - let matchFound = true; + let match_found = true; - if (sid !== sectionId) { + if (sid !== section_id) { const v = variables[ - this.fullMethodName(difficulty, sid, method, splitPanArms) + this.full_method_name(difficulty, sid, method, split_pan_arms) ]; if (!v) { - matchFound = false; + match_found = false; } else { - for (const itemName of Object.keys(variable)) { - if (variable[itemName] !== v[itemName]) { - matchFound = false; + for (const item_name of Object.keys(variable)) { + if (variable[item_name] !== v[item_name]) { + match_found = false; break; } } } } - if (matchFound) { - sectionIds.push(sid); + if (match_found) { + section_ids.push(sid); } } - optimalMethods.push(new OptimalMethod( + optimal_methods.push(new OptimalMethod( difficulty, - sectionIds, - method.name + (splitPanArms ? ' (Split Pan Arms)' : ''), + section_ids, + method.name + (split_pan_arms ? ' (Split Pan Arms)' : ''), method.episode, method.time, runs, @@ -285,62 +285,62 @@ class HuntOptimizerStore { } this.result = new OptimalResult( - [...wantedItems], - optimalMethods + [...wanted_items], + optimal_methods ); } - private fullMethodName( + private full_method_name( difficulty: Difficulty, - sectionId: SectionId, + section_id: SectionId, method: HuntMethod, - splitPanArms: boolean + split_pan_arms: boolean ): string { - let name = `${difficulty}\t${sectionId}\t${method.id}`; - if (splitPanArms) name += '\tspa'; + let name = `${difficulty}\t${section_id}\t${method.id}`; + if (split_pan_arms) name += '\tspa'; return name; } private initialize = async () => { try { - await this.loadFromLocalStorage(); - autorun(this.storeInLocalStorage); + await this.load_from_local_storage(); + autorun(this.store_in_local_storage); } catch (e) { logger.error(e); } } - private loadFromLocalStorage = async () => { - const wantedItemsJson = localStorage.getItem( - `HuntOptimizerStore.wantedItems.${Server[applicationStore.currentServer]}` + private load_from_local_storage = async () => { + const wanted_items_json = localStorage.getItem( + `HuntOptimizerStore.wantedItems.${Server[application_store.current_server]}` ); - if (wantedItemsJson) { - const itemStore = await itemTypeStores.current.promise; - const wi = JSON.parse(wantedItemsJson); + if (wanted_items_json) { + const item_store = await item_type_stores.current.promise; + const wi: StoredWantedItem[] = JSON.parse(wanted_items_json); - const wantedItems: WantedItem[] = []; + const wanted_items: WantedItem[] = []; for (const { itemTypeId, itemKindId, amount } of wi) { - const item = itemTypeId != null - ? itemStore.getById(itemTypeId) - : itemStore.getById(itemKindId); // Legacy name. + const item = itemTypeId != undefined + ? item_store.get_by_id(itemTypeId) + : item_store.get_by_id(itemKindId!); if (item) { - wantedItems.push(new WantedItem(item, amount)); + wanted_items.push(new WantedItem(item, amount)); } } - this.wantedItems.replace(wantedItems); + this.wanted_items.replace(wanted_items); } } - private storeInLocalStorage = () => { + private store_in_local_storage = () => { try { localStorage.setItem( - `HuntOptimizerStore.wantedItems.${Server[applicationStore.currentServer]}`, + `HuntOptimizerStore.wantedItems.${Server[application_store.current_server]}`, JSON.stringify( - this.wantedItems.map(({ itemType, amount }) => ({ + this.wanted_items.map(({ item_type: itemType, amount }): StoredWantedItem => ({ itemTypeId: itemType.id, amount })) @@ -352,4 +352,10 @@ class HuntOptimizerStore { } } -export const huntOptimizerStore = new HuntOptimizerStore(); +type StoredWantedItem = { + itemTypeId?: number, // Should only be undefined if the legacy name is still used. + itemKindId?: number, // Legacy name. + amount: number, +}; + +export const hunt_optimizer_store = new HuntOptimizerStore(); diff --git a/src/stores/ItemDropStore.ts b/src/stores/ItemDropStore.ts index 0d2d8ac2..7a0cde21 100644 --- a/src/stores/ItemDropStore.ts +++ b/src/stores/ItemDropStore.ts @@ -3,101 +3,101 @@ import { Difficulties, Difficulty, EnemyDrop, NpcType, SectionId, SectionIds, Se import { NpcTypes } from "../domain/NpcType"; import { EnemyDropDto } from "../dto"; import { Loadable } from "../Loadable"; -import { itemTypeStores } from "./ItemTypeStore"; +import { item_type_stores } from "./ItemTypeStore"; import { ServerMap } from "./ServerMap"; import Logger from 'js-logger'; const logger = Logger.get('stores/ItemDropStore'); -class EnemyDropTable { +export class EnemyDropTable { // Mapping of difficulties to section IDs to NpcTypes to EnemyDrops. - private table: Array = + private table: EnemyDrop[] = new Array(Difficulties.length * SectionIds.length * NpcTypes.length); // Mapping of ItemType ids to EnemyDrops. - private itemTypeToDrops: Array> = []; + private item_type_to_drops: EnemyDrop[][] = []; - getDrop(difficulty: Difficulty, sectionId: SectionId, npcType: NpcType): EnemyDrop | undefined { + get_drop(difficulty: Difficulty, section_id: SectionId, npc_type: NpcType): EnemyDrop | undefined { return this.table[ difficulty * SectionIds.length * NpcTypes.length - + sectionId * NpcTypes.length - + npcType.id + + section_id * NpcTypes.length + + npc_type.id ]; } - setDrop(difficulty: Difficulty, sectionId: SectionId, npcType: NpcType, drop: EnemyDrop) { + set_drop(difficulty: Difficulty, section_id: SectionId, npc_type: NpcType, drop: EnemyDrop) { this.table[ difficulty * SectionIds.length * NpcTypes.length - + sectionId * NpcTypes.length - + npcType.id + + section_id * NpcTypes.length + + npc_type.id ] = drop; - let drops = this.itemTypeToDrops[drop.item_type.id]; + let drops = this.item_type_to_drops[drop.item_type.id]; if (!drops) { drops = []; - this.itemTypeToDrops[drop.item_type.id] = drops; + this.item_type_to_drops[drop.item_type.id] = drops; } drops.push(drop); } - getDropsForItemType(itemTypeId: number): Array { - return this.itemTypeToDrops[itemTypeId] || []; + get_drops_for_item_type(item_type_id: number): EnemyDrop[] { + return this.item_type_to_drops[item_type_id] || []; } } -class ItemDropStore { - @observable enemyDrops: EnemyDropTable = new EnemyDropTable(); +export class ItemDropStore { + @observable.ref enemy_drops: EnemyDropTable = new EnemyDropTable(); +} - load = async (server: Server): Promise => { - const itemTypeStore = await itemTypeStores.current.promise; - const response = await fetch( - `${process.env.PUBLIC_URL}/enemyDrops.${Server[server].toLowerCase()}.json` - ); - const data: Array = await response.json(); +export const item_drop_stores: ServerMap> = new ServerMap(server => { + const store = new ItemDropStore(); + return new Loadable(store, () => load(store, server)); +}); - const drops = new EnemyDropTable(); +async function load(store: ItemDropStore, server: Server): Promise { + const item_type_store = await item_type_stores.current.promise; + const response = await fetch( + `${process.env.PUBLIC_URL}/enemyDrops.${Server[server].toLowerCase()}.json` + ); + const data: EnemyDropDto[] = await response.json(); - for (const dropDto of data) { - const npcType = NpcType.by_code(dropDto.enemy); + const drops = new EnemyDropTable(); - if (!npcType) { - logger.warn(`Couldn't determine NpcType of episode ${dropDto.episode} ${dropDto.enemy}.`); - continue; - } + for (const drop_dto of data) { + const npc_type = NpcType.by_code(drop_dto.enemy); - const difficulty = (Difficulty as any)[dropDto.difficulty]; - const itemType = itemTypeStore.getById(dropDto.itemTypeId); - - if (!itemType) { - logger.warn(`Couldn't find item kind ${dropDto.itemTypeId}.`); - continue; - } - - const sectionId = (SectionId as any)[dropDto.sectionId]; - - if (sectionId == null) { - logger.warn(`Couldn't find section ID ${dropDto.sectionId}.`); - continue; - } - - drops.setDrop(difficulty, sectionId, npcType, new EnemyDrop( - difficulty, - sectionId, - npcType, - itemType, - dropDto.dropRate, - dropDto.rareRate - )); + if (!npc_type) { + logger.warn(`Couldn't determine NpcType of episode ${drop_dto.episode} ${drop_dto.enemy}.`); + continue; } - this.enemyDrops = drops; - return this; - } -} + const difficulty = (Difficulty as any)[drop_dto.difficulty]; + const item_type = item_type_store.get_by_id(drop_dto.itemTypeId); -export const itemDropStores: ServerMap> = new ServerMap(server => { - const store = new ItemDropStore(); - return new Loadable(store, () => store.load(server)); -}); + if (!item_type) { + logger.warn(`Couldn't find item kind ${drop_dto.itemTypeId}.`); + continue; + } + + const section_id = (SectionId as any)[drop_dto.sectionId]; + + if (section_id == null) { + logger.warn(`Couldn't find section ID ${drop_dto.sectionId}.`); + continue; + } + + drops.set_drop(difficulty, section_id, npc_type, new EnemyDrop( + difficulty, + section_id, + npc_type, + item_type, + drop_dto.dropRate, + drop_dto.rareRate + )); + } + + store.enemy_drops = drops; + return store; +} diff --git a/src/stores/ItemTypeStore.ts b/src/stores/ItemTypeStore.ts index f9d8a415..1af12cf6 100644 --- a/src/stores/ItemTypeStore.ts +++ b/src/stores/ItemTypeStore.ts @@ -4,13 +4,13 @@ import { Loadable } from "../Loadable"; import { ServerMap } from "./ServerMap"; import { ItemTypeDto } from "../dto"; -class ItemTypeStore { - private idToItemType: Array = []; +export class ItemTypeStore { + private id_to_item_type: Array = []; - @observable itemTypes: Array = []; + @observable item_types: Array = []; - getById(id: number): ItemType | undefined { - return this.idToItemType[id]; + get_by_id(id: number): ItemType | undefined { + return this.id_to_item_type[id]; } load = async (server: Server): Promise => { @@ -19,80 +19,80 @@ class ItemTypeStore { ); const data: Array = await response.json(); - const itemTypes = new Array(); + const item_types = new Array(); - for (const itemTypeDto of data) { - let itemType: ItemType; + for (const item_type_dto of data) { + let item_type: ItemType; - switch (itemTypeDto.class) { + switch (item_type_dto.class) { case 'weapon': - itemType = new WeaponItemType( - itemTypeDto.id, - itemTypeDto.name, - itemTypeDto.minAtp, - itemTypeDto.maxAtp, - itemTypeDto.ata, - itemTypeDto.maxGrind, - itemTypeDto.requiredAtp, + item_type = new WeaponItemType( + item_type_dto.id, + item_type_dto.name, + item_type_dto.minAtp, + item_type_dto.maxAtp, + item_type_dto.ata, + item_type_dto.maxGrind, + item_type_dto.requiredAtp, ); break; case 'armor': - itemType = new ArmorItemType( - itemTypeDto.id, - itemTypeDto.name, - itemTypeDto.atp, - itemTypeDto.ata, - itemTypeDto.minEvp, - itemTypeDto.maxEvp, - itemTypeDto.minDfp, - itemTypeDto.maxDfp, - itemTypeDto.mst, - itemTypeDto.hp, - itemTypeDto.lck, + item_type = new ArmorItemType( + item_type_dto.id, + item_type_dto.name, + item_type_dto.atp, + item_type_dto.ata, + item_type_dto.minEvp, + item_type_dto.maxEvp, + item_type_dto.minDfp, + item_type_dto.maxDfp, + item_type_dto.mst, + item_type_dto.hp, + item_type_dto.lck, ); break; case 'shield': - itemType = new ShieldItemType( - itemTypeDto.id, - itemTypeDto.name, - itemTypeDto.atp, - itemTypeDto.ata, - itemTypeDto.minEvp, - itemTypeDto.maxEvp, - itemTypeDto.minDfp, - itemTypeDto.maxDfp, - itemTypeDto.mst, - itemTypeDto.hp, - itemTypeDto.lck, + item_type = new ShieldItemType( + item_type_dto.id, + item_type_dto.name, + item_type_dto.atp, + item_type_dto.ata, + item_type_dto.minEvp, + item_type_dto.maxEvp, + item_type_dto.minDfp, + item_type_dto.maxDfp, + item_type_dto.mst, + item_type_dto.hp, + item_type_dto.lck, ); break; case 'unit': - itemType = new UnitItemType( - itemTypeDto.id, - itemTypeDto.name, + item_type = new UnitItemType( + item_type_dto.id, + item_type_dto.name, ); break; case 'tool': - itemType = new ToolItemType( - itemTypeDto.id, - itemTypeDto.name, + item_type = new ToolItemType( + item_type_dto.id, + item_type_dto.name, ); break; default: continue; } - this.idToItemType[itemType.id] = itemType; - itemTypes.push(itemType); + this.id_to_item_type[item_type.id] = item_type; + item_types.push(item_type); } - this.itemTypes = itemTypes; + this.item_types = item_types; return this; } } -export const itemTypeStores: ServerMap> = new ServerMap(server => { +export const item_type_stores: ServerMap> = new ServerMap(server => { const store = new ItemTypeStore(); return new Loadable(store, () => store.load(server)); }); diff --git a/src/stores/QuestEditorStore.ts b/src/stores/QuestEditorStore.ts index 38f80d7a..c7fb0680 100644 --- a/src/stores/QuestEditorStore.ts +++ b/src/stores/QuestEditorStore.ts @@ -2,7 +2,8 @@ import Logger from 'js-logger'; import { action, observable } from 'mobx'; import { BufferCursor } from '../data_formats/BufferCursor'; import { parse_quest, write_quest_qst } from '../data_formats/parsing/quest'; -import { Area, Quest, QuestEntity, Section, Vec3 } from '../domain'; +import { Area, Quest, QuestEntity, Section } from '../domain'; +import { Vec3 } from "../data_formats/Vec3"; import { create_npc_mesh as create_npc_object_3d, create_object_mesh as create_object_object_3d } from '../rendering/entities'; import { area_store } from './AreaStore'; import { entity_store } from './EntityStore'; diff --git a/src/stores/ServerMap.ts b/src/stores/ServerMap.ts index d4f444bb..0f1aa4f8 100644 --- a/src/stores/ServerMap.ts +++ b/src/stores/ServerMap.ts @@ -1,17 +1,20 @@ import { computed } from "mobx"; import { Server } from "../domain"; -import { applicationStore } from "./ApplicationStore"; +import { application_store } from "./ApplicationStore"; import { EnumMap } from "../enums"; +/** + * Map with a guaranteed value per server. + */ export class ServerMap extends EnumMap { - constructor(initialValue: (server: Server) => V) { - super(Server, initialValue) + constructor(initial_value: (server: Server) => V) { + super(Server, initial_value) } /** - * @returns the value for the current server as set in {@link applicationStore}. + * @returns the value for the current server as set in {@link application_store}. */ @computed get current(): V { - return this.get(applicationStore.currentServer); + return this.get(application_store.current_server); } } diff --git a/src/ui/BigTable.tsx b/src/ui/BigTable.tsx index 0109a710..aae5811e 100644 --- a/src/ui/BigTable.tsx +++ b/src/ui/BigTable.tsx @@ -6,14 +6,14 @@ export type Column = { key?: string, name: string, width: number, - cellRenderer: (record: T) => ReactNode, + cell_renderer: (record: T) => ReactNode, tooltip?: (record: T) => string, - footerValue?: string, - footerTooltip?: string, + footer_value?: string, + footer_tooltip?: string, /** * "number" and "integrated" have special meaning. */ - className?: string, + class_name?: string, sortable?: boolean } @@ -27,20 +27,20 @@ export type ColumnSort = { column: Column, direction: SortDirectionType } export class BigTable extends React.Component<{ width: number, height: number, - rowCount: number, - overscanRowCount?: number, + row_count: number, + overscan_row_count?: number, columns: Array>, - fixedColumnCount?: number, - overscanColumnCount?: number, + fixed_column_count?: number, + overscan_column_count?: number, record: (index: Index) => T, footer?: boolean, /** * When this changes, the DataTable will re-render. */ - updateTrigger?: any, - sort?: (sortColumns: Array>) => void + update_trigger?: any, + sort?: (sort_columns: Array>) => void }> { - private sortColumns = new Array>(); + private sort_columns = new Array>(); render() { return ( @@ -52,30 +52,30 @@ export class BigTable extends React.Component<{ width={this.props.width} height={this.props.height} rowHeight={26} - rowCount={this.props.rowCount + 1 + (this.props.footer ? 1 : 0)} + rowCount={this.props.row_count + 1 + (this.props.footer ? 1 : 0)} fixedRowCount={1} - overscanRowCount={this.props.overscanRowCount} - columnWidth={this.columnWidth} + overscanRowCount={this.props.overscan_row_count} + columnWidth={this.column_width} columnCount={this.props.columns.length} - fixedColumnCount={this.props.fixedColumnCount} - overscanColumnCount={this.props.overscanColumnCount} - cellRenderer={this.cellRenderer} + fixedColumnCount={this.props.fixed_column_count} + overscanColumnCount={this.props.overscan_column_count} + cellRenderer={this.cell_renderer} classNameTopLeftGrid="DataTable-header" classNameTopRightGrid="DataTable-header" - updateTigger={this.props.updateTrigger} + updateTigger={this.props.update_trigger} /> ); } - private columnWidth = ({ index }: Index): number => { + private column_width = ({ index }: Index): number => { return this.props.columns[index].width; } - private cellRenderer: GridCellRenderer = ({ columnIndex, rowIndex, style }) => { + private cell_renderer: GridCellRenderer = ({ columnIndex, rowIndex, style }) => { const column = this.props.columns[columnIndex]; let cell: ReactNode; - let sortIndicator: ReactNode; + let sort_indicator: ReactNode; let title: string | undefined; const classes = ['DataTable-cell']; @@ -90,18 +90,18 @@ export class BigTable extends React.Component<{ if (column.sortable) { classes.push('sortable'); - const sort = this.sortColumns[0]; + const sort = this.sort_columns[0]; if (sort && sort.column === column) { if (sort.direction === SortDirection.ASC) { - sortIndicator = ( + sort_indicator = ( ); } else { - sortIndicator = ( + sort_indicator = ( @@ -112,20 +112,20 @@ export class BigTable extends React.Component<{ } } else { // Record or footer row - if (column.className) { - classes.push(column.className); + if (column.class_name) { + classes.push(column.class_name); } - if (this.props.footer && rowIndex === 1 + this.props.rowCount) { + if (this.props.footer && rowIndex === 1 + this.props.row_count) { // Footer row classes.push('footer-cell'); - cell = column.footerValue == null ? '' : column.footerValue; - title = column.footerTooltip == null ? '' : column.footerTooltip; + cell = column.footer_value == null ? '' : column.footer_value; + title = column.footer_tooltip == null ? '' : column.footer_tooltip; } else { // Record row const result = this.props.record({ index: rowIndex - 1 }); - cell = column.cellRenderer(result); + cell = column.cell_renderer(result); if (column.tooltip) { title = column.tooltip(result); @@ -137,8 +137,8 @@ export class BigTable extends React.Component<{ classes.push('custom'); } - const onClick = rowIndex === 0 && column.sortable - ? () => this.headerClicked(column) + const on_click = rowIndex === 0 && column.sortable + ? () => this.header_clicked(column) : undefined; return ( @@ -147,29 +147,29 @@ export class BigTable extends React.Component<{ key={`${columnIndex}, ${rowIndex}`} style={style} title={title} - onClick={onClick} + onClick={on_click} > {typeof cell === 'string' ? ( {cell} ) : cell} - {sortIndicator} + {sort_indicator} ); } - private headerClicked = (column: Column) => { - const oldIndex = this.sortColumns.findIndex(sc => sc.column === column); - let old = oldIndex === -1 ? undefined : this.sortColumns.splice(oldIndex, 1)[0]; + private header_clicked = (column: Column) => { + const old_index = this.sort_columns.findIndex(sc => sc.column === column); + let old = old_index === -1 ? undefined : this.sort_columns.splice(old_index, 1)[0]; - const direction = oldIndex === 0 && old!.direction === SortDirection.ASC + const direction = old_index === 0 && old!.direction === SortDirection.ASC ? SortDirection.DESC : SortDirection.ASC - this.sortColumns.unshift({ column, direction }); - this.sortColumns.splice(10); + this.sort_columns.unshift({ column, direction }); + this.sort_columns.splice(10); if (this.props.sort) { - this.props.sort(this.sortColumns); + this.props.sort(this.sort_columns); } } } diff --git a/src/ui/SectionIdIcon.tsx b/src/ui/SectionIdIcon.tsx index 52b3be80..19df5405 100644 --- a/src/ui/SectionIdIcon.tsx +++ b/src/ui/SectionIdIcon.tsx @@ -2,11 +2,11 @@ import React from "react"; import { SectionId } from "../domain"; export function SectionIdIcon({ - sectionId, + section_id, size = 28, title }: { - sectionId: SectionId, + section_id: SectionId, size?: number, title?: string }) { @@ -17,7 +17,7 @@ export function SectionIdIcon({ display: 'inline-block', width: size, height: size, - backgroundImage: `url(${process.env.PUBLIC_URL}/images/sectionids/${SectionId[sectionId]}.png)`, + backgroundImage: `url(${process.env.PUBLIC_URL}/images/sectionids/${SectionId[section_id]}.png)`, backgroundSize: size }} /> diff --git a/src/ui/dps_calc/DpsCalcComponent.tsx b/src/ui/dps_calc/DpsCalcComponent.tsx index ad757b47..9fa751a2 100644 --- a/src/ui/dps_calc/DpsCalcComponent.tsx +++ b/src/ui/dps_calc/DpsCalcComponent.tsx @@ -2,8 +2,8 @@ import { InputNumber } from "antd"; import { observer } from "mobx-react"; import React from "react"; import { WeaponItemType, ArmorItemType, ShieldItemType } from "../../domain"; -import { dpsCalcStore } from "../../stores/DpsCalcStore"; -import { itemTypeStores } from "../../stores/ItemTypeStore"; +import { dps_calc_store } from "../../stores/DpsCalcStore"; +import { item_type_stores } from "../../stores/ItemTypeStore"; import { BigSelect } from "../BigSelect"; @observer @@ -16,11 +16,11 @@ export class DpsCalcComponent extends React.Component { ({ + options={dps_calc_store.weapon_types.map(wt => ({ label: wt.name, value: wt.id }))} - onChange={this.addWeapon} + onChange={this.add_weapon} /> @@ -42,111 +42,111 @@ export class DpsCalcComponent extends React.Component { - {dpsCalcStore.weapons.map((weapon, i) => ( + {dps_calc_store.weapons.map((weapon, i) => ( - - + + - - - - - - - - - - + + + + + + + + + + ))}
{weapon.item.type.name}{weapon.item.type.minAtp}{weapon.item.type.maxAtp}{weapon.item.type.min_atp}{weapon.item.type.max_atp} weapon.item.grind = value || 0} /> {weapon.item.grindAtp}{weapon.shiftaAtp.toFixed(1)}{weapon.finalMinAtp.toFixed(1)}{weapon.finalMaxAtp.toFixed(1)}{weapon.minNormalDamage.toFixed(1)}{weapon.maxNormalDamage.toFixed(1)}{weapon.avgNormalDamage.toFixed(1)}{weapon.minHeavyDamage.toFixed(1)}{weapon.maxHeavyDamage.toFixed(1)}{weapon.avgHeavyDamage.toFixed(1)}{weapon.item.grind_atp}{weapon.shifta_atp.toFixed(1)}{weapon.final_min_atp.toFixed(1)}{weapon.final_max_atp.toFixed(1)}{weapon.min_normal_damage.toFixed(1)}{weapon.max_normal_damage.toFixed(1)}{weapon.avg_normal_damage.toFixed(1)}{weapon.min_heavy_damage.toFixed(1)}{weapon.max_heavy_damage.toFixed(1)}{weapon.avg_heavy_damage.toFixed(1)}
Character ATP:
dpsCalcStore.charAtp = value || 0} + onChange={(value) => dps_calc_store.char_atp = value || 0} />
MAG POW:
dpsCalcStore.magPow = value || 0} + onChange={(value) => dps_calc_store.mag_pow = value || 0} />
Armor:
({ + value={dps_calc_store.armor_type && dps_calc_store.armor_type.id} + options={dps_calc_store.armor_types.map(at => ({ label: at.name, value: at.id }))} - onChange={this.armorChanged} + onChange={this.armor_changed} /> - Armor ATP: {dpsCalcStore.armorAtp} + Armor ATP: {dps_calc_store.armor_atp}
Shield:
({ + value={dps_calc_store.shield_type && dps_calc_store.shield_type.id} + options={dps_calc_store.shield_types.map(st => ({ label: st.name, value: st.id }))} - onChange={this.shieldChanged} + onChange={this.shield_changed} /> - Shield ATP: {dpsCalcStore.shieldAtp} + Shield ATP: {dps_calc_store.shield_atp}
Shifta level:
dpsCalcStore.shiftaLvl = value || 0} + onChange={(value) => dps_calc_store.shifta_lvl = value || 0} />
Shifta factor:
-
{dpsCalcStore.shiftaFactor.toFixed(3)}
+
{dps_calc_store.shifta_factor.toFixed(3)}
Base shifta ATP:
-
{dpsCalcStore.baseShiftaAtp.toFixed(2)}
+
{dps_calc_store.base_shifta_atp.toFixed(2)}
); } - private addWeapon = (selected: any) => { + private add_weapon = (selected: any) => { if (selected) { - let type = itemTypeStores.current.value.getById(selected.value)!; - dpsCalcStore.addWeapon(type as WeaponItemType); + let type = item_type_stores.current.value.get_by_id(selected.value)!; + dps_calc_store.add_weapon(type as WeaponItemType); } } - private armorChanged = (selected: any) => { + private armor_changed = (selected: any) => { if (selected) { - let type = itemTypeStores.current.value.getById(selected.value)!; - dpsCalcStore.armorType = (type as ArmorItemType); + let type = item_type_stores.current.value.get_by_id(selected.value)!; + dps_calc_store.armor_type = (type as ArmorItemType); } else { - dpsCalcStore.armorType = undefined; + dps_calc_store.armor_type = undefined; } } - private shieldChanged = (selected: any) => { + private shield_changed = (selected: any) => { if (selected) { - let type = itemTypeStores.current.value.getById(selected.value)!; - dpsCalcStore.shieldType = (type as ShieldItemType); + let type = item_type_stores.current.value.get_by_id(selected.value)!; + dps_calc_store.shield_type = (type as ShieldItemType); } else { - dpsCalcStore.shieldType = undefined; + dps_calc_store.shield_type = undefined; } } } diff --git a/src/ui/hunt_optimizer/MethodsComponent.tsx b/src/ui/hunt_optimizer/MethodsComponent.tsx index ae7e1f37..624ce884 100644 --- a/src/ui/hunt_optimizer/MethodsComponent.tsx +++ b/src/ui/hunt_optimizer/MethodsComponent.tsx @@ -5,7 +5,7 @@ import React from "react"; import { AutoSizer, Index, SortDirection } from "react-virtualized"; import { Episode, HuntMethod } from "../../domain"; import { EnemyNpcTypes, NpcType } from "../../domain/NpcType"; -import { huntMethodStore } from "../../stores/HuntMethodStore"; +import { hunt_method_store } from "../../stores/HuntMethodStore"; import { BigTable, Column, ColumnSort } from "../BigTable"; import "./MethodsComponent.css"; @@ -18,22 +18,22 @@ export class MethodsComponent extends React.Component { key: 'name', name: 'Method', width: 250, - cellRenderer: (method) => method.name, + cell_renderer: (method) => method.name, sortable: true, }, { key: 'episode', name: 'Ep.', width: 34, - cellRenderer: (method) => Episode[method.episode], + cell_renderer: (method) => Episode[method.episode], sortable: true, }, { key: 'time', name: 'Time', width: 50, - cellRenderer: (method) => , - className: 'integrated', + cell_renderer: (method) => , + class_name: 'integrated', sortable: true, }, ]; @@ -44,11 +44,11 @@ export class MethodsComponent extends React.Component { key: enemy.code, name: enemy.name, width: 75, - cellRenderer: (method) => { + cell_renderer: (method) => { const count = method.enemy_counts.get(enemy); return count == null ? '' : count.toString(); }, - className: 'number', + class_name: 'number', sortable: true, }); } @@ -57,7 +57,7 @@ export class MethodsComponent extends React.Component { })(); render() { - const methods = huntMethodStore.methods.current.value; + const methods = hunt_method_store.methods.current.value; return (
@@ -66,12 +66,12 @@ export class MethodsComponent extends React.Component { width={width} height={height} - rowCount={methods.length} + row_count={methods.length} columns={MethodsComponent.columns} - fixedColumnCount={3} + fixed_column_count={3} record={this.record} sort={this.sort} - updateTrigger={huntMethodStore.methods.current.value} + update_trigger={hunt_method_store.methods.current.value} /> )} @@ -80,11 +80,11 @@ export class MethodsComponent extends React.Component { } private record = ({ index }: Index) => { - return huntMethodStore.methods.current.value[index]; + return hunt_method_store.methods.current.value[index]; } private sort = (sorts: ColumnSort[]) => { - const methods = huntMethodStore.methods.current.value.slice(); + const methods = hunt_method_store.methods.current.value.slice(); methods.sort((a, b) => { for (const { column, direction } of sorts) { @@ -112,7 +112,7 @@ export class MethodsComponent extends React.Component { return 0; }); - huntMethodStore.methods.current.value = methods; + hunt_method_store.methods.current.value = methods; } } diff --git a/src/ui/hunt_optimizer/OptimizationResultComponent.tsx b/src/ui/hunt_optimizer/OptimizationResultComponent.tsx index 9d620162..6fab39f7 100644 --- a/src/ui/hunt_optimizer/OptimizationResultComponent.tsx +++ b/src/ui/hunt_optimizer/OptimizationResultComponent.tsx @@ -3,105 +3,105 @@ import { observer } from "mobx-react"; import React from "react"; import { AutoSizer, Index } from "react-virtualized"; import { Difficulty, Episode, SectionId } from "../../domain"; -import { huntOptimizerStore, OptimalMethod } from "../../stores/HuntOptimizerStore"; +import { hunt_optimizer_store, OptimalMethod } from "../../stores/HuntOptimizerStore"; import { BigTable, Column } from "../BigTable"; import { SectionIdIcon } from "../SectionIdIcon"; -import { hoursToString } from "../time"; +import { hours_to_string } from "../time"; import "./OptimizationResultComponent.less"; @observer export class OptimizationResultComponent extends React.Component { @computed private get columns(): Column[] { // Standard columns. - const result = huntOptimizerStore.result; - const optimalMethods = result ? result.optimalMethods : []; - let totalRuns = 0; - let totalTime = 0; + const result = hunt_optimizer_store.result; + const optimal_methods = result ? result.optimal_methods : []; + let total_runs = 0; + let total_time = 0; - for (const method of optimalMethods) { - totalRuns += method.runs; - totalTime += method.totalTime; + for (const method of optimal_methods) { + total_runs += method.runs; + total_time += method.total_time; } const columns: Column[] = [ { name: 'Difficulty', width: 75, - cellRenderer: (result) => Difficulty[result.difficulty], - footerValue: 'Totals:', + cell_renderer: (result) => Difficulty[result.difficulty], + footer_value: 'Totals:', }, { name: 'Method', width: 200, - cellRenderer: (result) => result.methodName, - tooltip: (result) => result.methodName, + cell_renderer: (result) => result.method_name, + tooltip: (result) => result.method_name, }, { name: 'Ep.', width: 34, - cellRenderer: (result) => Episode[result.methodEpisode], + cell_renderer: (result) => Episode[result.method_episode], }, { name: 'Section ID', width: 80, - cellRenderer: (result) => ( + cell_renderer: (result) => (
- {result.sectionIds.map(sid => - + {result.section_ids.map(sid => + )}
), - tooltip: (result) => result.sectionIds.map(sid => SectionId[sid]).join(', '), + tooltip: (result) => result.section_ids.map(sid => SectionId[sid]).join(', '), }, { name: 'Time/Run', width: 80, - cellRenderer: (result) => hoursToString(result.methodTime), - className: 'number', + cell_renderer: (result) => hours_to_string(result.method_time), + class_name: 'number', }, { name: 'Runs', width: 60, - cellRenderer: (result) => result.runs.toFixed(1), + cell_renderer: (result) => result.runs.toFixed(1), tooltip: (result) => result.runs.toString(), - footerValue: totalRuns.toFixed(1), - footerTooltip: totalRuns.toString(), - className: 'number', + footer_value: total_runs.toFixed(1), + footer_tooltip: total_runs.toString(), + class_name: 'number', }, { name: 'Total Hours', width: 90, - cellRenderer: (result) => result.totalTime.toFixed(1), - tooltip: (result) => result.totalTime.toString(), - footerValue: totalTime.toFixed(1), - footerTooltip: totalTime.toString(), - className: 'number', + cell_renderer: (result) => result.total_time.toFixed(1), + tooltip: (result) => result.total_time.toString(), + footer_value: total_time.toFixed(1), + footer_tooltip: total_time.toString(), + class_name: 'number', }, ]; // Add one column per item. if (result) { - for (const item of result.wantedItems) { + for (const item of result.wanted_items) { let totalCount = 0; - for (const method of optimalMethods) { - totalCount += method.itemCounts.get(item) || 0; + for (const method of optimal_methods) { + totalCount += method.item_counts.get(item) || 0; } columns.push({ name: item.name, width: 80, - cellRenderer: (result) => { - const count = result.itemCounts.get(item); + cell_renderer: (result) => { + const count = result.item_counts.get(item); return count ? count.toFixed(2) : ''; }, tooltip: (result) => { - const count = result.itemCounts.get(item); + const count = result.item_counts.get(item); return count ? count.toString() : ''; }, - className: 'number', - footerValue: totalCount.toFixed(2), - footerTooltip: totalCount.toString() + class_name: 'number', + footer_value: totalCount.toFixed(2), + footer_tooltip: totalCount.toString() }); } } @@ -110,13 +110,13 @@ export class OptimizationResultComponent extends React.Component { } // Make sure render is called when result changes. - @computed private get updateTrigger() { - return huntOptimizerStore.result; + @computed private get update_trigger() { + return hunt_optimizer_store.result; } render() { - this.updateTrigger; // eslint-disable-line - const result = huntOptimizerStore.result; + this.update_trigger; // eslint-disable-line + const result = hunt_optimizer_store.result; return (
@@ -127,12 +127,12 @@ export class OptimizationResultComponent extends React.Component { } @@ -142,6 +142,6 @@ export class OptimizationResultComponent extends React.Component { } private record = ({ index }: Index): OptimalMethod => { - return huntOptimizerStore.result!.optimalMethods[index]; + return hunt_optimizer_store.result!.optimal_methods[index]; } } diff --git a/src/ui/hunt_optimizer/WantedItemsComponent.tsx b/src/ui/hunt_optimizer/WantedItemsComponent.tsx index dfa48ace..010677bf 100644 --- a/src/ui/hunt_optimizer/WantedItemsComponent.tsx +++ b/src/ui/hunt_optimizer/WantedItemsComponent.tsx @@ -2,20 +2,20 @@ import { Button, InputNumber, Popover } from "antd"; import { observer } from "mobx-react"; import React from "react"; import { AutoSizer, Column, Table, TableCellRenderer } from "react-virtualized"; -import { huntOptimizerStore, WantedItem } from "../../stores/HuntOptimizerStore"; -import { itemTypeStores } from "../../stores/ItemTypeStore"; +import { hunt_optimizer_store, WantedItem } from "../../stores/HuntOptimizerStore"; +import { item_type_stores } from "../../stores/ItemTypeStore"; import { BigSelect } from "../BigSelect"; import './WantedItemsComponent.less'; @observer export class WantedItemsComponent extends React.Component { state = { - helpVisible: false + help_visible: false } render() { // Make sure render is called on updates. - huntOptimizerStore.wantedItems.slice(0, 0); + hunt_optimizer_store.wanted_items.slice(0, 0); return (
@@ -24,8 +24,8 @@ export class WantedItemsComponent extends React.Component { } trigger="click" - visible={this.state.helpVisible} - onVisibleChange={this.onHelpVisibleChange} + visible={this.state.help_visible} + onVisibleChange={this.on_help_visible_change} >