Some renaming.

This commit is contained in:
Daan Vanden Bosch 2019-06-26 17:47:53 +02:00
parent 056eb50b04
commit a639eb683f
62 changed files with 356 additions and 363 deletions

View File

@ -33,8 +33,8 @@
"start": "craco start",
"build": "craco build",
"test": "craco test",
"updateGenericData": "ts-node --project=tsconfig-scripts.json static/updateGenericData.ts",
"updateEphineaData": "ts-node --project=tsconfig-scripts.json static/updateEphineaData.ts"
"update_generic_data": "ts-node --project=tsconfig-scripts.json static/update_generic_data.ts",
"update_ephinea_data": "ts-node --project=tsconfig-scripts.json static/update_ephinea_data.ts"
},
"eslintConfig": {
"extends": "react-app"

View File

@ -39,8 +39,8 @@ export class Loadable<T> {
private _load?: () => Promise<T>;
@observable private _error?: Error;
constructor(initialValue: T, load?: () => Promise<T>) {
this._value = initialValue;
constructor(initial_value: T, load?: () => Promise<T>) {
this._value = initial_value;
this._load = load;
}
@ -52,7 +52,7 @@ export class Loadable<T> {
// Load value on first use and return initial placeholder value.
if (this._state === LoadableState.Uninitialized) {
// Defer loading value to avoid side effects in computed value.
defer(() => this.loadValue());
defer(() => this.load_value());
}
return this._value;
@ -69,7 +69,7 @@ export class Loadable<T> {
get promise(): Promise<T> {
// Load value on first use.
if (this._state === LoadableState.Uninitialized) {
return this.loadValue();
return this.load_value();
} else {
return this._promise;
}
@ -83,14 +83,14 @@ export class Loadable<T> {
* @returns true if the initial data load has happened. It may or may not have succeeded.
* Check [error]{@link Loadable#error} to know whether an error occurred.
*/
@computed get isInitialized(): boolean {
@computed get is_initialized(): boolean {
return this._state !== LoadableState.Uninitialized;
}
/**
* @returns true if a data load is underway. This may be the initializing load or a later load.
*/
@computed get isLoading(): boolean {
@computed get is_loading(): boolean {
switch (this._state) {
case LoadableState.Initializing:
case LoadableState.Reloading:
@ -111,11 +111,11 @@ export class Loadable<T> {
* Load the data. Initializes the Loadable if it is uninitialized.
*/
load(): Promise<T> {
return this.loadValue();
return this.load_value();
}
private async loadValue(): Promise<T> {
if (this.isLoading) return this._promise;
private async load_value(): Promise<T> {
if (this.is_loading) return this._promise;
this._state = LoadableState.Initializing;

View File

@ -1,7 +1,3 @@
/**
* This code is based on the Sylverant PRS compression code written by Lawrence Sebald.
*/
import { BufferCursor } from '../../BufferCursor';
export function compress(src: BufferCursor): BufferCursor {

View File

@ -1,10 +1,7 @@
/**
* This code is based on the Sylverant PRS decompression code written by Lawrence Sebald.
*/
import { BufferCursor } from '../../BufferCursor';
import Logger from 'js-logger';
const logger = Logger.get('bin-data/compression/prs/decompress');
const logger = Logger.get('bin_data/compression/prs/decompress');
export function decompress(cursor: BufferCursor) {
const ctx = new Context(cursor);
@ -103,16 +100,16 @@ class Context {
}
// The length can be larger than -offset, in that case we copy -offset bytes size/-offset times.
const bufSize = Math.min(-offset, length);
const buf_size = Math.min(-offset, length);
this.dst.seek(offset);
const buf = this.dst.take(bufSize);
this.dst.seek(-offset - bufSize);
const buf = this.dst.take(buf_size);
this.dst.seek(-offset - buf_size);
for (let i = 0; i < Math.floor(length / bufSize); ++i) {
for (let i = 0; i < Math.floor(length / buf_size); ++i) {
this.dst.write_cursor(buf);
}
this.dst.write_cursor(buf.take(length % bufSize));
this.dst.write_cursor(buf.take(length % buf_size));
}
}

View File

@ -1,7 +1,7 @@
import { BufferCursor } from '../../BufferCursor';
import { compress, decompress } from '../prs';
function testWithBytes(bytes: number[], expectedCompressedSize: number) {
function test_with_bytes(bytes: number[], expected_compressed_size: number) {
const cursor = new BufferCursor(new Uint8Array(bytes).buffer, true);
for (const byte of bytes) {
@ -9,36 +9,36 @@ function testWithBytes(bytes: number[], expectedCompressedSize: number) {
}
cursor.seek_start(0);
const compressedCursor = compress(cursor);
const compressed_cursor = compress(cursor);
expect(compressedCursor.size).toBe(expectedCompressedSize);
expect(compressed_cursor.size).toBe(expected_compressed_size);
const testCursor = decompress(compressedCursor);
const test_cursor = decompress(compressed_cursor);
cursor.seek_start(0);
expect(testCursor.size).toBe(cursor.size);
expect(test_cursor.size).toBe(cursor.size);
while (cursor.bytes_left) {
if (cursor.u8() !== testCursor.u8()) {
if (cursor.u8() !== test_cursor.u8()) {
cursor.seek(-1);
testCursor.seek(-1);
test_cursor.seek(-1);
break;
}
}
expect(testCursor.position).toBe(testCursor.size);
expect(test_cursor.position).toBe(test_cursor.size);
}
test('PRS compression and decompression, best case', () => {
// Compression factor: 0.018
testWithBytes(new Array(1000).fill(128), 18);
test_with_bytes(new Array(1000).fill(128), 18);
});
test('PRS compression and decompression, worst case', () => {
const prng = new Prng();
// Compression factor: 1.124
testWithBytes(new Array(1000).fill(0).map(_ => prng.nextInteger(0, 255)), 1124);
test_with_bytes(new Array(1000).fill(0).map(_ => prng.next_integer(0, 255)), 1124);
});
test('PRS compression and decompression, typical case', () => {
@ -46,27 +46,27 @@ test('PRS compression and decompression, typical case', () => {
const pattern = [0, 0, 2, 0, 3, 0, 5, 0, 0, 0, 7, 9, 11, 13, 0, 0];
const arrays = new Array(100)
.fill(pattern)
.map(array => array.map((e: number) => e + prng.nextInteger(0, 10)));
const flattenedArray = [].concat.apply([], arrays);
.map(array => array.map((e: number) => e + prng.next_integer(0, 10)));
const flattened_array = [].concat.apply([], arrays);
// Compression factor: 0.834
testWithBytes(flattenedArray, 1335);
test_with_bytes(flattened_array, 1335);
});
test('PRS compression and decompression, 0 bytes', () => {
testWithBytes([], 3);
test_with_bytes([], 3);
});
test('PRS compression and decompression, 1 byte', () => {
testWithBytes([111], 4);
test_with_bytes([111], 4);
});
test('PRS compression and decompression, 2 bytes', () => {
testWithBytes([111, 224], 5);
test_with_bytes([111, 224], 5);
});
test('PRS compression and decompression, 3 bytes', () => {
testWithBytes([56, 237, 158], 6);
test_with_bytes([56, 237, 158], 6);
});
class Prng {
@ -77,7 +77,7 @@ class Prng {
return x - Math.floor(x);
}
nextInteger(min: number, max: number): number {
next_integer(min: number, max: number): number {
return Math.floor(this.next() * (max + 1 - min)) + min;
}
}

View File

@ -200,9 +200,9 @@ function objectTypeToUrl(objectType: ObjectType): string {
case ObjectType.FallingRock:
case ObjectType.DesertFixedTypeBoxBreakableCrystals:
case ObjectType.BeeHive:
return `/objects/${String(objectType.psoId)}.nj`;
return `/objects/${String(objectType.pso_id)}.nj`;
default:
return `/objects/${String(objectType.psoId)}.xj`;
return `/objects/${String(objectType.pso_id)}.xj`;
}
}

View File

@ -14,7 +14,7 @@ import {
import { Vec3, Section } from '../../domain';
import Logger from 'js-logger';
const logger = Logger.get('bin-data/parsing/geometry');
const logger = Logger.get('bin_data/parsing/geometry');
export function parseCRel(arrayBuffer: ArrayBuffer): Object3D {
const dv = new DataView(arrayBuffer);
@ -263,8 +263,8 @@ export function parseNRel(
const x = dv.getFloat32(k, true);
const y = dv.getFloat32(k + 4, true);
const z = dv.getFloat32(k + 8, true);
const rotatedX = section.cosYAxisRotation * x + section.sinYAxisRotation * z;
const rotatedZ = -section.sinYAxisRotation * x + section.cosYAxisRotation * z;
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;
geomPositions.push(sectionX + rotatedX);
geomPositions.push(sectionY + y);

View File

@ -2,7 +2,7 @@ import { Matrix3, Matrix4, Vector3 } from 'three';
import { BufferCursor } from '../../BufferCursor';
import Logger from 'js-logger';
const logger = Logger.get('bin-data/parsing/ninja/nj');
const logger = Logger.get('bin_data/parsing/ninja/nj');
// TODO:
// - deal with multiple NJCM chunks

View File

@ -1,7 +1,7 @@
import Logger from 'js-logger';
import { BufferCursor } from '../../BufferCursor';
const logger = Logger.get('bin-data/parsing/ninja/njm2');
const logger = Logger.get('bin_data/parsing/ninja/njm2');
export type NjAction = {
object_offset: number,

View File

@ -3,7 +3,7 @@ import { decrypt } from "../encryption/prc";
import { decompress } from "../compression/prs";
import Logger from 'js-logger';
const logger = Logger.get('bin-data/parsing/prc');
const logger = Logger.get('bin_data/parsing/prc');
/**
* Decrypts and decompresses a .prc file.

View File

@ -1,7 +1,7 @@
import { BufferCursor } from '../../BufferCursor';
import Logger from 'js-logger';
const logger = Logger.get('bin-data/parsing/quest/bin');
const logger = Logger.get('bin_data/parsing/quest/bin');
export interface BinFile {
questNumber: number;

View File

@ -2,7 +2,7 @@ import { groupBy } from 'lodash';
import { BufferCursor } from '../../BufferCursor';
import Logger from 'js-logger';
const logger = Logger.get('bin-data/parsing/quest/dat');
const logger = Logger.get('bin_data/parsing/quest/dat');
const OBJECT_SIZE = 68;
const NPC_SIZE = 72;

View File

@ -1,16 +1,16 @@
import * as fs from 'fs';
import { BufferCursor } from '../../BufferCursor';
import { parseQuest, writeQuestQst } from '.';
import { parse_quest, write_quest_qst } from '../quest';
import { ObjectType, Quest } from '../../../domain';
test('parse Towards the Future', () => {
const buffer = fs.readFileSync('test/resources/quest118_e.qst').buffer;
const cursor = new BufferCursor(buffer, true);
const quest = parseQuest(cursor)!;
const quest = parse_quest(cursor)!;
expect(quest.name).toBe('Towards the Future');
expect(quest.shortDescription).toBe('Challenge the\nnew simulator.');
expect(quest.longDescription).toBe('Client: Principal\nQuest: Wishes to have\nhunters challenge the\nnew simulator\nReward: ??? Meseta');
expect(quest.short_description).toBe('Challenge the\nnew simulator.');
expect(quest.long_description).toBe('Client: Principal\nQuest: Wishes to have\nhunters challenge the\nnew simulator\nReward: ??? Meseta');
expect(quest.episode).toBe(1);
expect(quest.objects.length).toBe(277);
expect(quest.objects[0].type).toBe(ObjectType.MenuActivation);
@ -28,12 +28,12 @@ test('parse Towards the Future', () => {
test('parseQuest and writeQuestQst', () => {
const buffer = fs.readFileSync('test/resources/tethealla_v0.143_quests/solo/ep1/02.qst').buffer;
const cursor = new BufferCursor(buffer, true);
const origQuest = parseQuest(cursor)!;
const testQuest = parseQuest(writeQuestQst(origQuest, '02.qst'))!;
const origQuest = parse_quest(cursor)!;
const testQuest = parse_quest(write_quest_qst(origQuest, '02.qst'))!;
expect(testQuest.name).toBe(origQuest.name);
expect(testQuest.shortDescription).toBe(origQuest.shortDescription);
expect(testQuest.longDescription).toBe(origQuest.longDescription);
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));
@ -45,8 +45,8 @@ test('parseQuest and writeQuestQst', () => {
function testableObjects(quest: Quest) {
return quest.objects.map(object => [
object.areaId,
object.sectionId,
object.area_id,
object.section_id,
object.position,
object.type
]);
@ -54,13 +54,13 @@ function testableObjects(quest: Quest) {
function testableNpcs(quest: Quest) {
return quest.npcs.map(npc => [
npc.areaId,
npc.sectionId,
npc.area_id,
npc.section_id,
npc.position,
npc.type
]);
}
function testableAreaVariants(quest: Quest) {
return quest.areaVariants.map(av => [av.area.id, av.id]);
return quest.area_variants.map(av => [av.area.id, av.id]);
}

View File

@ -2,7 +2,7 @@ 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, writeQst } from './qst';
import { parseQst as parse_qst, writeQst as write_qst } from './qst';
import {
Vec3,
AreaVariant,
@ -12,59 +12,59 @@ import {
ObjectType,
NpcType
} from '../../../domain';
import { areaStore } from '../../../stores/AreaStore';
import { area_store } from '../../../stores/AreaStore';
import Logger from 'js-logger';
const logger = Logger.get('bin-data/parsing/quest');
const logger = Logger.get('bin_data/parsing/quest');
/**
* High level parsing function that delegates to lower level parsing functions.
*
* Always delegates to parseQst at the moment.
*/
export function parseQuest(cursor: BufferCursor, lenient: boolean = false): Quest | undefined {
const qst = parseQst(cursor);
export function parse_quest(cursor: BufferCursor, lenient: boolean = false): Quest | undefined {
const qst = parse_qst(cursor);
if (!qst) {
return;
}
let datFile = null;
let binFile = null;
let dat_file = null;
let bin_file = null;
for (const file of qst.files) {
const fileName = file.name.trim().toLowerCase();
const file_name = file.name.trim().toLowerCase();
if (fileName.endsWith('.dat')) {
datFile = file;
} else if (fileName.endsWith('.bin')) {
binFile = file;
if (file_name.endsWith('.dat')) {
dat_file = file;
} else if (file_name.endsWith('.bin')) {
bin_file = file;
}
}
// TODO: deal with missing/multiple DAT or BIN file.
if (!datFile) {
if (!dat_file) {
logger.error('File contains no DAT file.');
return;
}
if (!binFile) {
if (!bin_file) {
logger.error('File contains no BIN file.');
return;
}
const dat = parseDat(prs.decompress(datFile.data));
const bin = parseBin(prs.decompress(binFile.data), lenient);
const dat = parseDat(prs.decompress(dat_file.data));
const bin = parseBin(prs.decompress(bin_file.data), lenient);
let episode = 1;
let areaVariants: AreaVariant[] = [];
let area_variants: AreaVariant[] = [];
if (bin.functionOffsets.length) {
const func0Ops = getFuncOperations(bin.instructions, bin.functionOffsets[0]);
const func_0_ops = get_func_operations(bin.instructions, bin.functionOffsets[0]);
if (func0Ops) {
episode = getEpisode(func0Ops);
areaVariants = getAreaVariants(dat, episode, func0Ops, lenient);
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.`);
}
@ -76,36 +76,36 @@ export function parseQuest(cursor: BufferCursor, lenient: boolean = false): Ques
bin.questName,
bin.shortDescription,
bin.longDescription,
datFile.questNo,
dat_file.questNo,
episode,
areaVariants,
parseObjData(dat.objs),
parseNpcData(episode, dat.npcs),
area_variants,
parse_obj_data(dat.objs),
parse_npc_data(episode, dat.npcs),
dat.unknowns,
bin.data
);
}
export function writeQuestQst(quest: Quest, fileName: string): BufferCursor {
export function write_quest_qst(quest: Quest, file_name: string): BufferCursor {
const dat = writeDat({
objs: objectsToDatData(quest.objects),
objs: objects_to_dat_data(quest.objects),
npcs: npcsToDatData(quest.npcs),
unknowns: quest.datUnkowns
unknowns: quest.dat_unknowns
});
const bin = writeBin({ data: quest.binData });
const extStart = fileName.lastIndexOf('.');
const baseFileName = extStart === -1 ? fileName : fileName.slice(0, extStart);
const bin = writeBin({ 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);
return writeQst({
return write_qst({
files: [
{
name: baseFileName + '.dat',
questNo: quest.questNo,
name: base_file_name + '.dat',
questNo: quest.quest_no,
data: prs.compress(dat)
},
{
name: baseFileName + '.bin',
questNo: quest.questNo,
name: base_file_name + '.bin',
questNo: quest.quest_no,
data: prs.compress(bin)
}
]
@ -115,11 +115,11 @@ export function writeQuestQst(quest: Quest, fileName: string): BufferCursor {
/**
* Defaults to episode I.
*/
function getEpisode(func0Ops: Instruction[]): number {
const setEpisode = func0Ops.find(op => op.mnemonic === 'set_episode');
function get_episode(func_0_ops: Instruction[]): number {
const set_episode = func_0_ops.find(op => op.mnemonic === 'set_episode');
if (setEpisode) {
switch (setEpisode.args[0]) {
if (set_episode) {
switch (set_episode.args[0]) {
default:
case 0: return 1;
case 1: return 2;
@ -131,37 +131,37 @@ function getEpisode(func0Ops: Instruction[]): number {
}
}
function getAreaVariants(
function get_area_variants(
dat: DatFile,
episode: number,
func0Ops: Instruction[],
func_0_ops: Instruction[],
lenient: boolean
): AreaVariant[] {
// Add area variants that have npcs or objects even if there are no BB_Map_Designate instructions for them.
const areaVariants = new Map();
const area_variants = new Map();
for (const npc of dat.npcs) {
areaVariants.set(npc.areaId, 0);
area_variants.set(npc.areaId, 0);
}
for (const obj of dat.objs) {
areaVariants.set(obj.areaId, 0);
area_variants.set(obj.areaId, 0);
}
const bbMaps = func0Ops.filter(op => op.mnemonic === 'BB_Map_Designate');
const bb_maps = func_0_ops.filter(op => op.mnemonic === 'BB_Map_Designate');
for (const bbMap of bbMaps) {
const areaId = bbMap.args[0];
const variantId = bbMap.args[2];
areaVariants.set(areaId, variantId);
for (const bb_map of bb_maps) {
const area_id = bb_map.args[0];
const variant_id = bb_map.args[2];
area_variants.set(area_id, variant_id);
}
const areaVariantsArray = new Array<AreaVariant>();
const area_variants_array = new Array<AreaVariant>();
for (const [areaId, variantId] of areaVariants.entries()) {
for (const [areaId, variantId] of area_variants.entries()) {
try {
areaVariantsArray.push(
areaStore.getVariant(episode, areaId, variantId)
area_variants_array.push(
area_store.get_variant(episode, areaId, variantId)
);
} catch (e) {
if (lenient) {
@ -173,23 +173,23 @@ function getAreaVariants(
}
// Sort by area order and then variant id.
return areaVariantsArray.sort((a, b) =>
return area_variants_array.sort((a, b) =>
a.area.order - b.area.order || a.id - b.id
);
}
function getFuncOperations(operations: Instruction[], funcOffset: number) {
function get_func_operations(operations: Instruction[], func_offset: number) {
let position = 0;
let funcFound = false;
const funcOps: Instruction[] = [];
let func_found = false;
const func_ops: Instruction[] = [];
for (const operation of operations) {
if (position === funcOffset) {
funcFound = true;
if (position === func_offset) {
func_found = true;
}
if (funcFound) {
funcOps.push(operation);
if (func_found) {
func_ops.push(operation);
// Break when ret is encountered.
if (operation.opcode === 1) {
@ -200,25 +200,25 @@ function getFuncOperations(operations: Instruction[], funcOffset: number) {
position += operation.size;
}
return funcFound ? funcOps : null;
return func_found ? func_ops : null;
}
function parseObjData(objs: DatObject[]): QuestObject[] {
return objs.map(objData => {
const { x, y, z } = objData.position;
const rot = objData.rotation;
function parse_obj_data(objs: DatObject[]): QuestObject[] {
return objs.map(obj_data => {
const { x, y, z } = obj_data.position;
const rot = obj_data.rotation;
return new QuestObject(
objData.areaId,
objData.sectionId,
obj_data.areaId,
obj_data.sectionId,
new Vec3(x, y, z),
new Vec3(rot.x, rot.y, rot.z),
ObjectType.fromPsoId(objData.typeId),
objData
ObjectType.from_pso_id(obj_data.typeId),
obj_data
);
});
}
function parseNpcData(episode: number, npcs: DatNpc[]): QuestNpc[] {
function parse_npc_data(episode: number, npcs: DatNpc[]): QuestNpc[] {
return npcs.map(npcData => {
const { x, y, z } = npcData.position;
const rot = npcData.rotation;
@ -227,14 +227,14 @@ function parseNpcData(episode: number, npcs: DatNpc[]): QuestNpc[] {
npcData.sectionId,
new Vec3(x, y, z),
new Vec3(rot.x, rot.y, rot.z),
getNpcType(episode, npcData),
get_npc_type(episode, npcData),
npcData
);
});
}
// TODO: detect Mothmant, St. Rappy, Hallo Rappy, Egg Rappy, Death Gunner, Bulk and Recon.
function getNpcType(episode: number, { typeId, flags, skin, areaId }: DatNpc): NpcType {
function get_npc_type(episode: number, { typeId, flags, skin, areaId }: DatNpc): NpcType {
const regular = Math.abs(flags - 1) > 0.00001;
switch (`${typeId}, ${skin % 3}, ${episode}`) {
@ -389,13 +389,13 @@ function getNpcType(episode: number, { typeId, flags, skin, areaId }: DatNpc): N
return NpcType.Unknown;
}
function objectsToDatData(objects: QuestObject[]): DatObject[] {
function objects_to_dat_data(objects: QuestObject[]): DatObject[] {
return objects.map(object => ({
typeId: object.type.psoId!,
sectionId: object.sectionId,
position: object.sectionPosition,
typeId: object.type.pso_id!,
sectionId: object.section_id,
position: object.section_position,
rotation: object.rotation,
areaId: object.areaId,
areaId: object.area_id,
unknown: object.dat.unknown
}));
}
@ -412,12 +412,12 @@ function npcsToDatData(npcs: QuestNpc[]): DatNpc[] {
return {
typeId: typeData ? typeData.typeId : npc.dat.typeId,
sectionId: npc.sectionId,
position: npc.sectionPosition,
sectionId: npc.section_id,
position: npc.section_position,
rotation: npc.rotation,
flags,
skin: typeData ? typeData.skin : npc.dat.skin,
areaId: npc.areaId,
areaId: npc.area_id,
unknown: npc.dat.unknown
};
});

View File

@ -1,7 +1,7 @@
import { BufferCursor } from '../../BufferCursor';
import Logger from 'js-logger';
const logger = Logger.get('bin-data/parsing/quest/qst');
const logger = Logger.get('bin_data/parsing/quest/qst');
interface QstContainedFile {
name: string;

View File

@ -2,7 +2,7 @@ import { BufferCursor } from "../BufferCursor";
import Logger from 'js-logger';
import { parse_prc } from "./prc";
const logger = Logger.get('bin-data/parsing/rlc');
const logger = Logger.get('bin_data/parsing/rlc');
const MARKER = 'RelChunkVer0.20';
/**

View File

@ -1,4 +1,4 @@
import { Episode, checkEpisode } from ".";
import { Episode, check_episode } from ".";
export class NpcType {
readonly id: number;
@ -15,8 +15,8 @@ export class NpcType {
* Name used in the game.
* Might conflict with other NPC names (e.g. Delsaber from ep. I and ep. II).
*/
readonly simpleName: string;
readonly ultimateName: string;
readonly simple_name: string;
readonly ultimate_name: string;
readonly episode?: number;
readonly enemy: boolean;
rareType?: NpcType;
@ -25,8 +25,8 @@ export class NpcType {
id: number,
code: string,
name: string,
simpleName: string,
ultimateName: string,
simple_name: string,
ultimate_name: string,
episode: number | undefined,
enemy: boolean
) {
@ -34,48 +34,48 @@ export class NpcType {
throw new Error(`Expected id to be an integer greater than or equal to 0, got ${id}.`);
if (!code) throw new Error('code is required.');
if (!name) throw new Error('name is required.');
if (!simpleName) throw new Error('simpleName is required.');
if (!ultimateName) throw new Error('ultimateName is required.');
if (!simple_name) throw new Error('simple_name is required.');
if (!ultimate_name) throw new Error('ultimate_name is required.');
if (episode != null && episode !== 1 && episode !== 2 && episode !== 4)
throw new Error(`episode should be undefined, 1, 2 or 4, got ${episode}.`);
if (typeof enemy !== 'boolean') throw new Error('enemy is required.');
this.id = id;
this.code = code;
this.simpleName = simpleName;
this.ultimateName = ultimateName;
this.simple_name = simple_name;
this.ultimate_name = ultimate_name;
this.name = name;
this.episode = episode;
this.enemy = enemy;
NpcType.byCodeMap.set(code, this);
NpcType.by_code_map.set(code, this);
if (episode) {
const map = NpcType.byEpAndName[episode];
const map = NpcType.by_ep_and_name[episode];
if (map) {
map.set(simpleName, this);
map.set(ultimateName, this);
map.set(simple_name, this);
map.set(ultimate_name, this);
}
}
}
private static byCodeMap = new Map<string, NpcType>();
private static by_code_map = new Map<string, NpcType>();
private static byEpAndName = [
private static by_ep_and_name = [
undefined, new Map<string, NpcType>(), new Map<string, NpcType>(), undefined, new Map<string, NpcType>()
];
static byCode(code: string): NpcType | undefined {
return this.byCodeMap.get(code);
static by_code(code: string): NpcType | undefined {
return this.by_code_map.get(code);
}
/**
* Uniquely identifies an NPC. Tries to match on simpleName and ultimateName.
* Uniquely identifies an NPC. Tries to match on simple_name and ultimate_name.
*/
static byNameAndEpisode(name: string, episode: Episode): NpcType | undefined {
checkEpisode(episode);
return this.byEpAndName[episode]!.get(name);
static by_name_and_episode(name: string, episode: Episode): NpcType | undefined {
check_episode(episode);
return this.by_ep_and_name[episode]!.get(name);
}
//

View File

@ -1,17 +1,17 @@
export class ObjectType {
id: number;
psoId?: number;
pso_id?: number;
name: string;
constructor(id: number, psoId: number | undefined, name: string) {
constructor(id: number, pso_id: number | undefined, name: string) {
if (!Number.isInteger(id) || id < 1)
throw new Error(`Expected id to be an integer greater than or equal to 1, got ${id}.`);
if (psoId != null && (!Number.isInteger(psoId) || psoId < 0))
throw new Error(`Expected psoId to be null or an integer greater than or equal to 0, got ${psoId}.`);
if (pso_id != null && (!Number.isInteger(pso_id) || pso_id < 0))
throw new Error(`Expected pso_id to be null or an integer greater than or equal to 0, got ${pso_id}.`);
if (!name) throw new Error('name is required.');
this.id = id;
this.psoId = psoId;
this.pso_id = pso_id;
this.name = name;
}
@ -296,7 +296,7 @@ export class ObjectType {
static TopOfSaintMillionEgg: ObjectType;
static UnknownItem961: ObjectType;
static fromPsoId(psoId: number): ObjectType {
static from_pso_id(psoId: number): ObjectType {
switch (psoId) {
default: return ObjectType.Unknown;

View File

@ -1,10 +1,10 @@
import { computed, observable } from 'mobx';
import { Object3D } from 'three';
import { BufferCursor } from '../bin-data/BufferCursor';
import { DatNpc, DatObject, DatUnknown } from '../bin-data/parsing/quest/dat';
import { BufferCursor } from '../bin_data/BufferCursor';
import { DatNpc, DatObject, DatUnknown } from '../bin_data/parsing/quest/dat';
import { NpcType } from './NpcType';
import { ObjectType } from './ObjectType';
import { enumValues } from '../enums';
import { enumValues as enum_values } from '../enums';
import { ItemType } from './items';
export * from './items';
@ -18,7 +18,7 @@ export enum Server {
Ephinea = 'Ephinea'
}
export const Servers: Server[] = enumValues(Server);
export const Servers: Server[] = enum_values(Server);
export enum Episode {
I = 1,
@ -26,9 +26,9 @@ export enum Episode {
IV = 4
}
export const Episodes: Episode[] = enumValues(Episode);
export const Episodes: Episode[] = enum_values(Episode);
export function checkEpisode(episode: Episode) {
export function check_episode(episode: Episode) {
if (!Episode[episode]) {
throw new Error(`Invalid episode ${episode}.`);
}
@ -47,13 +47,13 @@ export enum SectionId {
Whitill,
}
export const SectionIds: SectionId[] = enumValues(SectionId);
export const SectionIds: SectionId[] = enum_values(SectionId);
export enum Difficulty {
Normal, Hard, VHard, Ultimate
}
export const Difficulties: Difficulty[] = enumValues(Difficulty);
export const Difficulties: Difficulty[] = enum_values(Difficulty);
export class Vec3 {
x: number;
@ -84,77 +84,77 @@ export class Vec3 {
export class Section {
id: number;
@observable position: Vec3;
@observable yAxisRotation: number;
@observable y_axis_rotation: number;
@computed get sinYAxisRotation(): number {
return Math.sin(this.yAxisRotation);
@computed get sin_y_axis_rotation(): number {
return Math.sin(this.y_axis_rotation);
}
@computed get cosYAxisRotation(): number {
return Math.cos(this.yAxisRotation);
@computed get cos_y_axis_rotation(): number {
return Math.cos(this.y_axis_rotation);
}
constructor(
id: number,
position: Vec3,
yAxisRotation: number
y_axis_rotation: number
) {
if (!Number.isInteger(id) || id < -1)
throw new Error(`Expected id to be an integer greater than or equal to -1, got ${id}.`);
if (!position) throw new Error('position is required.');
if (typeof yAxisRotation !== 'number') throw new Error('yAxisRotation is required.');
if (typeof y_axis_rotation !== 'number') throw new Error('y_axis_rotation is required.');
this.id = id;
this.position = position;
this.yAxisRotation = yAxisRotation;
this.y_axis_rotation = y_axis_rotation;
}
}
export class Quest {
@observable name: string;
@observable shortDescription: string;
@observable longDescription: string;
@observable questNo?: number;
@observable short_description: string;
@observable long_description: string;
@observable quest_no?: number;
@observable episode: Episode;
@observable areaVariants: AreaVariant[];
@observable area_variants: AreaVariant[];
@observable objects: QuestObject[];
@observable npcs: QuestNpc[];
/**
* (Partial) raw DAT data that can't be parsed yet by Phantasmal.
*/
datUnkowns: DatUnknown[];
dat_unknowns: DatUnknown[];
/**
* (Partial) raw BIN data that can't be parsed yet by Phantasmal.
*/
binData: BufferCursor;
bin_data: BufferCursor;
constructor(
name: string,
shortDescription: string,
longDescription: string,
questNo: number | undefined,
short_description: string,
long_description: string,
quest_no: number | undefined,
episode: Episode,
areaVariants: AreaVariant[],
area_variants: AreaVariant[],
objects: QuestObject[],
npcs: QuestNpc[],
datUnknowns: DatUnknown[],
binData: BufferCursor
dat_unknowns: DatUnknown[],
bin_data: BufferCursor
) {
if (questNo != null && (!Number.isInteger(questNo) || questNo < 0)) throw new Error('questNo should be null or a non-negative integer.');
checkEpisode(episode);
if (quest_no != null && (!Number.isInteger(quest_no) || quest_no < 0)) throw new Error('quest_no should be null 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.name = name;
this.shortDescription = shortDescription;
this.longDescription = longDescription;
this.questNo = questNo;
this.short_description = short_description;
this.long_description = long_description;
this.quest_no = quest_no;
this.episode = episode;
this.areaVariants = areaVariants;
this.area_variants = area_variants;
this.objects = objects;
this.npcs = npcs;
this.datUnkowns = datUnknowns;
this.binData = binData;
this.dat_unknowns = dat_unknowns;
this.bin_data = bin_data;
}
}
@ -162,12 +162,12 @@ export class Quest {
* Abstract class from which QuestNpc and QuestObject derive.
*/
export class QuestEntity {
@observable areaId: number;
@observable area_id: number;
private _sectionId: number;
private _section_id: number;
@computed get sectionId(): number {
return this.section ? this.section.id : this._sectionId;
@computed get section_id(): number {
return this.section ? this.section.id : this._section_id;
}
@observable section?: Section;
@ -182,15 +182,15 @@ export class QuestEntity {
/**
* Section-relative position
*/
@computed get sectionPosition(): Vec3 {
@computed get section_position(): Vec3 {
let { x, y, z } = this.position;
if (this.section) {
const relX = x - this.section.position.x;
const relY = y - this.section.position.y;
const relZ = z - this.section.position.z;
const sin = -this.section.sinYAxisRotation;
const cos = this.section.cosYAxisRotation;
const sin = -this.section.sin_y_axis_rotation;
const cos = this.section.cos_y_axis_rotation;
const rotX = cos * relX + sin * relZ;
const rotZ = -sin * relX + cos * relZ;
x = rotX;
@ -201,12 +201,12 @@ export class QuestEntity {
return new Vec3(x, y, z);
}
set sectionPosition(sectPos: Vec3) {
set section_position(sectPos: Vec3) {
let { x: relX, y: relY, z: relZ } = sectPos;
if (this.section) {
const sin = -this.section.sinYAxisRotation;
const cos = this.section.cosYAxisRotation;
const sin = -this.section.sin_y_axis_rotation;
const cos = this.section.cos_y_axis_rotation;
const rotX = cos * relX - sin * relZ;
const rotZ = sin * relX + cos * relZ;
const x = rotX + this.section.position.x;
@ -219,22 +219,22 @@ export class QuestEntity {
object3d?: Object3D;
constructor(
areaId: number,
sectionId: number,
area_id: number,
section_id: number,
position: Vec3,
rotation: Vec3
) {
if (Object.getPrototypeOf(this) === Object.getPrototypeOf(QuestEntity))
throw new Error('Abstract class should not be instantiated directly.');
if (!Number.isInteger(areaId) || areaId < 0)
throw new Error(`Expected areaId to be a non-negative integer, got ${areaId}.`);
if (!Number.isInteger(sectionId) || sectionId < 0)
throw new Error(`Expected sectionId to be a non-negative integer, got ${sectionId}.`);
if (!Number.isInteger(area_id) || area_id < 0)
throw new Error(`Expected area_id to be a non-negative integer, got ${area_id}.`);
if (!Number.isInteger(section_id) || section_id < 0)
throw new Error(`Expected section_id to be a non-negative integer, got ${section_id}.`);
if (!position) throw new Error('position is required.');
if (!rotation) throw new Error('rotation is required.');
this.areaId = areaId;
this._sectionId = sectionId;
this.area_id = area_id;
this._section_id = section_id;
this.position = position;
this.rotation = rotation;
}
@ -248,14 +248,14 @@ export class QuestObject extends QuestEntity {
dat: DatObject;
constructor(
areaId: number,
sectionId: number,
area_id: number,
section_id: number,
position: Vec3,
rotation: Vec3,
type: ObjectType,
dat: DatObject
) {
super(areaId, sectionId, position, rotation);
super(area_id, section_id, position, rotation);
if (!type) throw new Error('type is required.');
@ -272,14 +272,14 @@ export class QuestNpc extends QuestEntity {
dat: DatNpc;
constructor(
areaId: number,
sectionId: number,
area_id: number,
section_id: number,
position: Vec3,
rotation: Vec3,
type: NpcType,
dat: DatNpc
) {
super(areaId, sectionId, position, rotation);
super(area_id, section_id, position, rotation);
if (!type) throw new Error('type is required.');
@ -292,18 +292,18 @@ export class Area {
id: number;
name: string;
order: number;
areaVariants: AreaVariant[];
area_variants: AreaVariant[];
constructor(id: number, name: string, order: number, areaVariants: AreaVariant[]) {
constructor(id: number, name: string, order: number, area_variants: AreaVariant[]) {
if (!Number.isInteger(id) || id < 0)
throw new Error(`Expected id to be a non-negative integer, got ${id}.`);
if (!name) throw new Error('name is required.');
if (!areaVariants) throw new Error('areaVariants is required.');
if (!area_variants) throw new Error('area_variants is required.');
this.id = id;
this.name = name;
this.order = order;
this.areaVariants = areaVariants;
this.area_variants = area_variants;
}
}
@ -317,9 +317,9 @@ export class AreaVariant {
}
type ItemDrop = {
itemType: ItemType,
anythingRate: number,
rareRate: number
item_type: ItemType,
anything_rate: number,
rare_rate: number
}
export class EnemyDrop implements ItemDrop {
@ -327,13 +327,13 @@ export class EnemyDrop implements ItemDrop {
constructor(
readonly difficulty: Difficulty,
readonly sectionId: SectionId,
readonly npcType: NpcType,
readonly itemType: ItemType,
readonly anythingRate: number,
readonly rareRate: number
readonly section_id: SectionId,
readonly npc_type: NpcType,
readonly item_type: ItemType,
readonly anything_rate: number,
readonly rare_rate: number
) {
this.rate = anythingRate * rareRate;
this.rate = anything_rate * rare_rate;
}
}
@ -342,28 +342,28 @@ export class HuntMethod {
readonly name: string;
readonly episode: Episode;
readonly quest: SimpleQuest;
readonly enemyCounts: Map<NpcType, number>;
readonly enemy_counts: Map<NpcType, number>;
/**
* The time it takes to complete the quest in hours.
*/
readonly defaultTime: number;
readonly default_time: number;
/**
* The time it takes to complete the quest in hours as specified by the user.
*/
@observable userTime?: number;
@observable user_time?: number;
@computed get time(): number {
return this.userTime != null ? this.userTime : this.defaultTime;
return this.user_time != null ? this.user_time : this.default_time;
}
constructor(
id: string,
name: string,
quest: SimpleQuest,
defaultTime: number
default_time: number
) {
if (!id) throw new Error('id is required.');
if (defaultTime <= 0) throw new Error('defaultTime must be greater than zero.');
if (default_time <= 0) throw new Error('default_time must be greater than zero.');
if (!name) throw new Error('name is required.');
if (!quest) throw new Error('quest is required.');
@ -371,8 +371,8 @@ export class HuntMethod {
this.name = name;
this.episode = quest.episode;
this.quest = quest;
this.enemyCounts = quest.enemyCounts;
this.defaultTime = defaultTime;
this.enemy_counts = quest.enemy_counts;
this.default_time = default_time;
}
}
@ -381,10 +381,10 @@ export class SimpleQuest {
public readonly id: number,
public readonly name: string,
public readonly episode: Episode,
public readonly enemyCounts: Map<NpcType, number>
public readonly enemy_counts: Map<NpcType, number>
) {
if (!id) throw new Error('id is required.');
if (!name) throw new Error('name is required.');
if (!enemyCounts) throw new Error('enemyCounts is required.');
if (!enemy_counts) throw new Error('enemyCounts is required.');
}
}

View File

@ -1,7 +1,7 @@
import * as THREE from 'three';
import { Color, HemisphereLight, Intersection, Mesh, MeshLambertMaterial, MOUSE, Object3D, PerspectiveCamera, Plane, Raycaster, Scene, Vector2, Vector3, WebGLRenderer } from 'three';
import OrbitControlsCreator from 'three-orbit-controls';
import { getAreaCollisionGeometry, getAreaRenderGeometry } from '../bin-data/loading/areas';
import { getAreaCollisionGeometry, getAreaRenderGeometry } from '../bin_data/loading/areas';
import { Area, Quest, QuestEntity, QuestNpc, QuestObject, Section, Vec3 } from '../domain';
import { questEditorStore } from '../stores/QuestEditorStore';
import { NPC_COLOR, NPC_HOVER_COLOR, NPC_SELECTED_COLOR, OBJECT_COLOR, OBJECT_HOVER_COLOR, OBJECT_SELECTED_COLOR } from './entities';
@ -97,15 +97,15 @@ export class Renderer {
if (quest) {
for (const obj of quest.objects) {
const array = this.objs.get(obj.areaId) || [];
const array = this.objs.get(obj.area_id) || [];
array.push(obj);
this.objs.set(obj.areaId, array);
this.objs.set(obj.area_id, array);
}
for (const npc of quest.npcs) {
const array = this.npcs.get(npc.areaId) || [];
const array = this.npcs.get(npc.area_id) || [];
array.push(npc);
this.npcs.set(npc.areaId, array);
this.npcs.set(npc.area_id, array);
}
}
@ -150,7 +150,7 @@ export class Renderer {
if (this.quest && this.area) {
const episode = this.quest.episode;
const areaId = this.area.id;
const variant = this.quest.areaVariants.find(v => v.area.id === areaId);
const variant = this.quest.area_variants.find(v => v.area.id === areaId);
const variantId = (variant && variant.id) || 0;
getAreaCollisionGeometry(episode, areaId, variantId).then(geometry => {
@ -191,7 +191,7 @@ export class Renderer {
let loaded = true;
for (const object of this.quest.objects) {
if (object.areaId === this.area.id) {
if (object.area_id === this.area.id) {
if (object.object3d) {
this.objGeometry.add(object.object3d);
} else {
@ -201,7 +201,7 @@ export class Renderer {
}
for (const npc of this.quest.npcs) {
if (npc.areaId === this.area.id) {
if (npc.area_id === this.area.id) {
if (npc.object3d) {
this.npcGeometry.add(npc.object3d);
} else {

View File

@ -1,5 +1,5 @@
import { CylinderBufferGeometry, MeshLambertMaterial, Object3D, Vector3 } from 'three';
import { DatNpc, DatObject } from '../bin-data/parsing/quest/dat';
import { DatNpc, DatObject } from '../bin_data/parsing/quest/dat';
import { NpcType, ObjectType, QuestNpc, QuestObject, Vec3 } from '../domain';
import { createNpcMesh, createObjectMesh, NPC_COLOR, OBJECT_COLOR } from './entities';

View File

@ -3,7 +3,7 @@ import { Area, AreaVariant } from '../domain';
function area(id: number, name: string, order: number, variants: number) {
const area = new Area(id, name, order, []);
const varis = Array(variants).fill(null).map((_, i) => new AreaVariant(i, area));
area.areaVariants.splice(0, 0, ...varis);
area.area_variants.splice(0, 0, ...varis);
return area;
}
@ -70,20 +70,20 @@ class AreaStore {
];
}
getVariant(episode: number, areaId: number, variantId: number) {
get_variant(episode: number, area_id: number, variant_id: number) {
if (episode !== 1 && episode !== 2 && episode !== 4)
throw new Error(`Expected episode to be 1, 2 or 4, got ${episode}.`);
const area = this.areas[episode].find(a => a.id === areaId);
const area = this.areas[episode].find(a => a.id === area_id);
if (!area)
throw new Error(`Area id ${areaId} for episode ${episode} is invalid.`);
throw new Error(`Area id ${area_id} for episode ${episode} is invalid.`);
const areaVariant = area.areaVariants[variantId];
if (!areaVariant)
throw new Error(`Area variant id ${variantId} for area ${areaId} of episode ${episode} is invalid.`);
const area_variant = area.area_variants[variant_id];
if (!area_variant)
throw new Error(`Area variant id ${variant_id} for area ${area_id} of episode ${episode} is invalid.`);
return areaVariant;
return area_variant;
}
}
export const areaStore = new AreaStore();
export const area_store = new AreaStore();

View File

@ -26,7 +26,7 @@ class HuntMethodStore {
const enemyCounts = new Map<NpcType, number>();
for (const [code, count] of Object.entries(quest.enemyCounts)) {
const npcType = NpcType.byCode(code);
const npcType = NpcType.by_code(code);
if (!npcType) {
logger.error(`No NpcType found for code ${code}.`);
@ -82,7 +82,7 @@ class HuntMethodStore {
const userTimes = JSON.parse(methodUserTimesJson);
for (const method of methods) {
method.userTime = userTimes[method.id] as number;
method.user_time = userTimes[method.id] as number;
}
}
@ -103,8 +103,8 @@ class HuntMethodStore {
const userTimes: any = {};
for (const method of methods) {
if (method.userTime != null) {
userTimes[method.id] = method.userTime;
if (method.user_time != null) {
userTimes[method.id] = method.user_time;
}
}

View File

@ -108,7 +108,7 @@ class HuntOptimizerStore {
// Counts include rare enemies, so they are fractional.
const counts = new Map<NpcType, number>();
for (const [enemy, count] of method.enemyCounts.entries()) {
for (const [enemy, count] of method.enemy_counts.entries()) {
const oldCount = counts.get(enemy) || 0;
if (enemy.rareType == null) {
@ -176,9 +176,9 @@ class HuntOptimizerStore {
for (const [npcType, count] of counts.entries()) {
const drop = dropTable.getDrop(diff, sectionId, npcType);
if (drop && wantedItems.has(drop.itemType)) {
const value = variable[drop.itemType.name] || 0;
variable[drop.itemType.name] = value + count * drop.rate;
if (drop && wantedItems.has(drop.item_type)) {
const value = variable[drop.item_type.name] || 0;
variable[drop.item_type.name] = value + count * drop.rate;
addVariable = true;
}
}

View File

@ -32,11 +32,11 @@ class EnemyDropTable {
+ npcType.id
] = drop;
let drops = this.itemTypeToDrops[drop.itemType.id];
let drops = this.itemTypeToDrops[drop.item_type.id];
if (!drops) {
drops = [];
this.itemTypeToDrops[drop.itemType.id] = drops;
this.itemTypeToDrops[drop.item_type.id] = drops;
}
drops.push(drop);
@ -60,7 +60,7 @@ class ItemDropStore {
const drops = new EnemyDropTable();
for (const dropDto of data) {
const npcType = NpcType.byCode(dropDto.enemy);
const npcType = NpcType.by_code(dropDto.enemy);
if (!npcType) {
logger.warn(`Couldn't determine NpcType of episode ${dropDto.episode} ${dropDto.enemy}.`);

View File

@ -1,10 +1,10 @@
import { observable, action } from 'mobx';
import { Object3D } from 'three';
import { BufferCursor } from '../bin-data/BufferCursor';
import { getAreaSections } from '../bin-data/loading/areas';
import { getNpcGeometry, getObjectGeometry } from '../bin-data/loading/entities';
import { parseNj, parseXj } from '../bin-data/parsing/ninja';
import { parseQuest, writeQuestQst } from '../bin-data/parsing/quest';
import { BufferCursor } from '../bin_data/BufferCursor';
import { getAreaSections } from '../bin_data/loading/areas';
import { getNpcGeometry, getObjectGeometry } from '../bin_data/loading/entities';
import { parseNj, parseXj } from '../bin_data/parsing/ninja';
import { parse_quest, write_quest_qst } from '../bin_data/parsing/quest';
import { Area, Quest, QuestEntity, Section, Vec3 } from '../domain';
import { createNpcMesh, createObjectMesh } from '../rendering/entities';
import { createModelMesh } from '../rendering/models';
@ -27,8 +27,8 @@ class QuestEditorStore {
this.resetModelAndQuestState();
this.currentQuest = quest;
if (quest && quest.areaVariants.length) {
this.currentArea = quest.areaVariants[0].area;
if (quest && quest.area_variants.length) {
this.currentArea = quest.area_variants[0].area;
}
})
@ -49,7 +49,7 @@ class QuestEditorStore {
if (areaId == null) {
this.currentArea = undefined;
} else if (this.currentQuest) {
const areaVariant = this.currentQuest.areaVariants.find(
const areaVariant = this.currentQuest.area_variants.find(
variant => variant.area.id === areaId
);
this.currentArea = areaVariant && areaVariant.area;
@ -74,17 +74,17 @@ class QuestEditorStore {
} else if (file.name.endsWith('.xj')) {
this.setModel(createModelMesh(parseXj(new BufferCursor(reader.result, true))));
} else {
const quest = parseQuest(new BufferCursor(reader.result, true));
const quest = parse_quest(new BufferCursor(reader.result, true));
this.setQuest(quest);
if (quest) {
// Load section data.
for (const variant of quest.areaVariants) {
for (const variant of quest.area_variants) {
const sections = await getAreaSections(quest.episode, variant.area.id, variant.id);
variant.sections = sections;
// Generate object geometry.
for (const object of quest.objects.filter(o => o.areaId === variant.area.id)) {
for (const object of quest.objects.filter(o => o.area_id === variant.area.id)) {
try {
const geometry = await getObjectGeometry(object.type);
this.setSectionOnVisibleQuestEntity(object, sections);
@ -95,7 +95,7 @@ class QuestEditorStore {
}
// Generate NPC geometry.
for (const npc of quest.npcs.filter(npc => npc.areaId === variant.area.id)) {
for (const npc of quest.npcs.filter(npc => npc.area_id === variant.area.id)) {
try {
const geometry = await getNpcGeometry(npc.type);
this.setSectionOnVisibleQuestEntity(npc, sections);
@ -114,18 +114,18 @@ class QuestEditorStore {
private setSectionOnVisibleQuestEntity = async (entity: QuestEntity, sections: Section[]) => {
let { x, y, z } = entity.position;
const section = sections.find(s => s.id === entity.sectionId);
const section = sections.find(s => s.id === entity.section_id);
entity.section = section;
if (section) {
const { x: secX, y: secY, z: secZ } = section.position;
const rotX = section.cosYAxisRotation * x + section.sinYAxisRotation * z;
const rotZ = -section.sinYAxisRotation * x + section.cosYAxisRotation * z;
const rotX = section.cos_y_axis_rotation * x + section.sin_y_axis_rotation * z;
const rotZ = -section.sin_y_axis_rotation * x + section.cos_y_axis_rotation * z;
x = rotX + secX;
y += secY;
z = rotZ + secZ;
} else {
logger.warn(`Section ${entity.sectionId} not found.`);
logger.warn(`Section ${entity.section_id} not found.`);
}
entity.position = new Vec3(x, y, z);
@ -133,7 +133,7 @@ class QuestEditorStore {
saveCurrentQuestToFile = (fileName: string) => {
if (this.currentQuest) {
const cursor = writeQuestQst(this.currentQuest, fileName);
const cursor = write_quest_qst(this.currentQuest, fileName);
if (!fileName.endsWith('.qst')) {
fileName += '.qst';

View File

@ -3,32 +3,32 @@ import { ClickParam } from 'antd/lib/menu';
import { observer } from 'mobx-react';
import React from 'react';
import './ApplicationComponent.less';
import { withErrorBoundary } from './ErrorBoundary';
import { HuntOptimizerComponent } from './hunt-optimizer/HuntOptimizerComponent';
import { QuestEditorComponent } from './quest-editor/QuestEditorComponent';
import { DpsCalcComponent } from './dps-calc/DpsCalcComponent';
import { with_error_boundary } from './ErrorBoundary';
import { HuntOptimizerComponent } from './hunt_optimizer/HuntOptimizerComponent';
import { QuestEditorComponent } from './quest_editor/QuestEditorComponent';
import { DpsCalcComponent } from './dps_calc/DpsCalcComponent';
import { Server } from '../domain';
const QuestEditor = withErrorBoundary(QuestEditorComponent);
const HuntOptimizer = withErrorBoundary(HuntOptimizerComponent);
const DpsCalc = withErrorBoundary(DpsCalcComponent);
const QuestEditor = with_error_boundary(QuestEditorComponent);
const HuntOptimizer = with_error_boundary(HuntOptimizerComponent);
const DpsCalc = with_error_boundary(DpsCalcComponent);
@observer
export class ApplicationComponent extends React.Component {
state = { tool: this.initTool() }
state = { tool: this.init_tool() }
render() {
let toolComponent;
let tool_component;
switch (this.state.tool) {
case 'questEditor':
toolComponent = <QuestEditor />;
tool_component = <QuestEditor />;
break;
case 'huntOptimizer':
toolComponent = <HuntOptimizer />;
tool_component = <HuntOptimizer />;
break;
case 'dpsCalc':
toolComponent = <DpsCalc />;
tool_component = <DpsCalc />;
break;
}
@ -40,7 +40,7 @@ export class ApplicationComponent extends React.Component {
</h1>
<Menu
className="ApplicationComponent-heading-menu"
onClick={this.menuClicked}
onClick={this.menu_clicked}
selectedKeys={[this.state.tool]}
mode="horizontal"
>
@ -62,17 +62,17 @@ export class ApplicationComponent extends React.Component {
</div>
</div>
<div className="ApplicationComponent-main">
{toolComponent}
{tool_component}
</div>
</div>
);
}
private menuClicked = (e: ClickParam) => {
private menu_clicked = (e: ClickParam) => {
this.setState({ tool: e.key });
};
private initTool(): string {
private init_tool(): string {
const param = window.location.search.slice(1).split('&').find(p => p.startsWith('tool='));
return param ? param.slice(5) : 'questEditor';
}

View File

@ -4,11 +4,11 @@ import './ErrorBoundary.css';
export class ErrorBoundary extends React.Component {
state = {
hasError: false
has_error: false
};
render() {
if (this.state.hasError) {
if (this.state.has_error) {
return (
<div className="ErrorBoundary-error">
<div>
@ -26,6 +26,6 @@ export class ErrorBoundary extends React.Component {
}
}
export function withErrorBoundary(Component: React.ComponentType) {
export function with_error_boundary(Component: React.ComponentType) {
return () => <ErrorBoundary><Component /></ErrorBoundary>;
}

View File

@ -45,7 +45,7 @@ export class MethodsComponent extends React.Component {
name: enemy.name,
width: 75,
cellRenderer: (method) => {
const count = method.enemyCounts.get(enemy);
const count = method.enemy_counts.get(enemy);
return count == null ? '' : count.toString();
},
className: 'number',
@ -97,10 +97,10 @@ export class MethodsComponent extends React.Component {
} else if (column.key === 'time') {
cmp = a.time - b.time;
} else if (column.key) {
const type = NpcType.byCode(column.key);
const type = NpcType.by_code(column.key);
if (type) {
cmp = (a.enemyCounts.get(type) || 0) - (b.enemyCounts.get(type) || 0);
cmp = (a.enemy_counts.get(type) || 0) - (b.enemy_counts.get(type) || 0);
}
}
@ -137,6 +137,6 @@ class TimeComponent extends React.Component<{ method: HuntMethod }> {
}
private change = (time: Moment) => {
this.props.method.userTime = time.hour() + time.minute() / 60;
this.props.method.user_time = time.hour() + time.minute() / 60;
}
}

View File

@ -14,7 +14,7 @@ export class EntityInfoComponent extends React.Component<Props> {
const entity = this.props.entity;
if (entity) {
const sectionId = entity.section ? entity.section.id : entity.sectionId;
const section_id = entity.section ? entity.section.id : entity.section_id;
let name = null;
if (entity instanceof QuestObject) {
@ -37,7 +37,7 @@ export class EntityInfoComponent extends React.Component<Props> {
<tbody>
{name}
<tr>
<td>Section: </td><td>{sectionId}</td>
<td>Section: </td><td>{section_id}</td>
</tr>
<tr>
<td colSpan={2}>World position: </td>
@ -46,9 +46,9 @@ export class EntityInfoComponent extends React.Component<Props> {
<td colSpan={2}>
<table>
<tbody>
<CoordRow entity={entity} positionType="position" coord="x" />
<CoordRow entity={entity} positionType="position" coord="y" />
<CoordRow entity={entity} positionType="position" coord="z" />
<CoordRow entity={entity} position_type="position" coord="x" />
<CoordRow entity={entity} position_type="position" coord="y" />
<CoordRow entity={entity} position_type="position" coord="z" />
</tbody>
</table>
</td>
@ -60,9 +60,9 @@ export class EntityInfoComponent extends React.Component<Props> {
<td colSpan={2}>
<table>
<tbody>
<CoordRow entity={entity} positionType="sectionPosition" coord="x" />
<CoordRow entity={entity} positionType="sectionPosition" coord="y" />
<CoordRow entity={entity} positionType="sectionPosition" coord="z" />
<CoordRow entity={entity} position_type="section_position" coord="x" />
<CoordRow entity={entity} position_type="section_position" coord="y" />
<CoordRow entity={entity} position_type="section_position" coord="z" />
</tbody>
</table>
</td>
@ -80,12 +80,12 @@ export class EntityInfoComponent extends React.Component<Props> {
@observer
class CoordRow extends React.Component<{
entity: QuestEntity,
positionType: 'position' | 'sectionPosition',
position_type: 'position' | 'section_position',
coord: 'x' | 'y' | 'z'
}> {
render() {
const entity = this.props.entity;
const value = entity[this.props.positionType][this.props.coord];
const value = entity[this.props.position_type][this.props.coord];
return (
<tr>
<td>{this.props.coord.toUpperCase()}: </td>
@ -105,10 +105,10 @@ class CoordRow extends React.Component<{
private changed = (value?: number) => {
if (value != null) {
const entity = this.props.entity;
const posType = this.props.positionType;
const pos = entity[posType].clone();
const pos_type = this.props.position_type;
const pos = entity[pos_type].clone();
pos[this.props.coord] = value;
entity[posType] = pos;
entity[pos_type] = pos;
}
}
}

View File

@ -81,7 +81,7 @@ class Toolbar extends React.Component<{ onSaveAsClicked: (filename?: string) =>
render() {
const quest = questEditorStore.currentQuest;
const areas = quest && Array.from(quest.areaVariants).map(a => a.area);
const areas = quest && Array.from(quest.area_variants).map(a => a.area);
const area = questEditorStore.currentArea;
const areaId = area && area.id;

View File

@ -39,12 +39,12 @@ export function QuestInfoComponent({ quest }: { quest?: Quest }) {
</tr>
<tr>
<td colSpan={2}>
<pre>{quest.shortDescription}</pre>
<pre>{quest.short_description}</pre>
</td>
</tr>
<tr>
<td colSpan={2}>
<pre>{quest.longDescription}</pre>
<pre>{quest.long_description}</pre>
</td>
</tr>
</tbody>

View File

@ -5,7 +5,7 @@ import { Difficulty, NpcType, SectionId, SectionIds } from '../src/domain';
import { BoxDropDto, EnemyDropDto, ItemTypeDto } from '../src/dto';
import Logger from 'js-logger';
const logger = Logger.get('static/updateDropsEphinea');
const logger = Logger.get('static/update_drops_ephinea');
export async function updateDropsFromWebsite(itemTypes: ItemTypeDto[]) {
logger.info('Updating item drops.');

View File

@ -1,21 +1,21 @@
import fs from 'fs';
import { ArrayBufferCursor } from '../src/bin-data/ArrayBufferCursor';
import { parseItemPmt, ItemPmt } from '../src/bin-data/parsing/itempmt';
import { parseUnitxt, Unitxt } from '../src/bin-data/parsing/unitxt';
import { BufferCursor } from '../src/bin_data/BufferCursor';
import { parseItemPmt, ItemPmt } from '../src/bin_data/parsing/itempmt';
import { parseUnitxt, Unitxt } from '../src/bin_data/parsing/unitxt';
import { Difficulties, Difficulty, Episode, Episodes, NpcType, SectionId, SectionIds } from '../src/domain';
import { NpcTypes } from '../src/domain/NpcType';
import { BoxDropDto, EnemyDropDto, ItemTypeDto, QuestDto } from '../src/dto';
import { updateDropsFromWebsite } from './updateDropsEphinea';
import { parseQuest } from '../src/bin-data/parsing/quest';
import { updateDropsFromWebsite } from './update_drops_ephinea';
import { parseQuest } from '../src/bin_data/parsing/quest';
import Logger from 'js-logger';
const logger = Logger.get('static/updateEphineaData');
const logger = Logger.get('static/update_ephinea_data');
Logger.useDefaults({ defaultLevel: Logger.ERROR });
logger.setLevel(Logger.INFO);
Logger.get('static/updateDropsEphinea').setLevel(Logger.INFO);
Logger.get('bin-data/parsing/quest').setLevel(Logger.OFF);
Logger.get('bin-data/parsing/quest/bin').setLevel(Logger.OFF);
Logger.get('static/update_drops_ephinea').setLevel(Logger.INFO);
Logger.get('bin_data/parsing/quest').setLevel(Logger.OFF);
Logger.get('bin_data/parsing/quest/bin').setLevel(Logger.OFF);
/**
* Used by static data generation scripts.
@ -104,7 +104,7 @@ function processQuestDir(path: string, quests: QuestDto[]) {
function processQuest(path: string, quests: QuestDto[]) {
try {
const buf = fs.readFileSync(path);
const q = parseQuest(new ArrayBufferCursor(buf.buffer, true), true);
const q = parseQuest(new BufferCursor(buf.buffer, true), true);
if (q) {
logger.trace(`Processing quest "${q.name}".`);
@ -142,7 +142,7 @@ function loadUnitxt(): Unitxt {
`${RESOURCE_DIR}/client/data/unitxt_j.prs`
);
const unitxt = parseUnitxt(new ArrayBufferCursor(buf.buffer, true));
const unitxt = parseUnitxt(new BufferCursor(buf.buffer, true));
// Strip custom Ephinea items until we have the Ephinea ItemPMT.bin.
unitxt[1].splice(177, 50);
unitxt[1].splice(639, 59);
@ -158,7 +158,7 @@ function updateItems(itemNames: Array<string>): ItemTypeDto[] {
`${RESOURCE_DIR}/ship-config/param/ItemPMT.bin`
);
const itemPmt = parseItemPmt(new ArrayBufferCursor(buf.buffer, true));
const itemPmt = parseItemPmt(new BufferCursor(buf.buffer, true));
const itemTypes = new Array<ItemTypeDto>();
const ids = new Set<number>();
@ -309,7 +309,7 @@ async function loadItemPt(): Promise<ItemPt> {
const buf = await fs.promises.readFile(
`${RESOURCE_DIR}/ship-config/param/ItemPT.gsl`
);
const cursor = new ArrayBufferCursor(buf.buffer, false);
const cursor = new BufferCursor(buf.buffer, false);
cursor.seek(0x3000);
@ -330,7 +330,7 @@ async function loadItemPt(): Promise<ItemPt> {
const startPos = cursor.position;
cursor.seek(1608);
const enemyDar = cursor.u8Array(100);
const enemyDar = cursor.u8_array(100);
for (const npc of NpcTypes) {
if (npc.episode !== episode) continue;
@ -358,7 +358,7 @@ async function loadItemPt(): Promise<ItemPt> {
}
}
cursor.seekStart(startPos + 0x1000);
cursor.seek_start(startPos + 0x1000);
}
}
}

View File

@ -1,7 +1,7 @@
import fs from "fs";
import { BufferCursor } from "../src/bin-data/BufferCursor";
import { parse_rlc } from "../src/bin-data/parsing/rlc";
import { parse_njm2 } from "../src/bin-data/parsing/ninja/njm2";
import { BufferCursor } from "../src/bin_data/BufferCursor";
import { parse_rlc } from "../src/bin_data/parsing/rlc";
import { parse_njm2 } from "../src/bin_data/parsing/ninja/njm2";
import Logger from 'js-logger';
const logger = Logger.get('static/updateGenericData');