Split code into one module per tool + core and application module.

This commit is contained in:
Daan Vanden Bosch 2019-08-10 22:09:06 +02:00
parent cf1cd26c41
commit 66127253d3
171 changed files with 1743 additions and 1705 deletions

View File

@ -3,8 +3,12 @@ import { writeFileSync } from "fs";
import "isomorphic-fetch"; import "isomorphic-fetch";
import Logger from "js-logger"; import Logger from "js-logger";
import { ASSETS_DIR } from "."; import { ASSETS_DIR } from ".";
import { Difficulty, NpcType, SectionId, SectionIds } from "../src/domain"; import { Difficulty, SectionId, SectionIds } from "../src/core/domain";
import { BoxDropDto, EnemyDropDto, ItemTypeDto } from "../src/dto"; import { BoxDropDto, EnemyDropDto, ItemTypeDto } from "../src/core/dto";
import {
name_and_episode_to_npc_type,
NpcType,
} from "../src/core/data_formats/parsing/quest/npc_types";
const logger = Logger.get("assets_generation/update_drops_ephinea"); const logger = Logger.get("assets_generation/update_drops_ephinea");
@ -19,7 +23,7 @@ export async function update_drops_from_website(item_types: ItemTypeDto[]): Prom
const enemy_json = JSON.stringify( const enemy_json = JSON.stringify(
[...normal.enemy_drops, ...hard.enemy_drops, ...vhard.enemy_drops, ...ultimate.enemy_drops], [...normal.enemy_drops, ...hard.enemy_drops, ...vhard.enemy_drops, ...ultimate.enemy_drops],
null, null,
4 4,
); );
writeFileSync(`${ASSETS_DIR}/enemyDrops.ephinea.json`, enemy_json); writeFileSync(`${ASSETS_DIR}/enemyDrops.ephinea.json`, enemy_json);
@ -27,7 +31,7 @@ export async function update_drops_from_website(item_types: ItemTypeDto[]): Prom
const box_json = JSON.stringify( const box_json = JSON.stringify(
[...normal.box_drops, ...hard.box_drops, ...vhard.box_drops, ...ultimate.box_drops], [...normal.box_drops, ...hard.box_drops, ...vhard.box_drops, ...ultimate.box_drops],
null, null,
4 4,
); );
writeFileSync(`${ASSETS_DIR}/boxDrops.ephinea.json`, box_json); writeFileSync(`${ASSETS_DIR}/boxDrops.ephinea.json`, box_json);
@ -38,7 +42,7 @@ export async function update_drops_from_website(item_types: ItemTypeDto[]): Prom
async function download( async function download(
item_types: ItemTypeDto[], item_types: ItemTypeDto[],
difficulty: Difficulty, difficulty: Difficulty,
difficulty_url: string = Difficulty[difficulty].toLowerCase() difficulty_url: string = Difficulty[difficulty].toLowerCase(),
): Promise<{ enemy_drops: EnemyDropDto[]; box_drops: BoxDropDto[]; items: Set<string> }> { ): Promise<{ enemy_drops: EnemyDropDto[]; box_drops: BoxDropDto[]; items: Set<string> }> {
const response = await fetch(`https://ephinea.pioneer2.net/drop-charts/${difficulty_url}/`); const response = await fetch(`https://ephinea.pioneer2.net/drop-charts/${difficulty_url}/`);
const body = await response.text(); const body = await response.text();
@ -125,7 +129,7 @@ async function download(
throw new Error(`No item type found with name "${item}".`); throw new Error(`No item type found with name "${item}".`);
} }
const npc_type = NpcType.by_name_and_episode(enemy_or_box, episode); const npc_type = name_and_episode_to_npc_type(enemy_or_box, episode);
if (!npc_type) { if (!npc_type) {
throw new Error(`Couldn't retrieve NpcType.`); throw new Error(`Couldn't retrieve NpcType.`);
@ -149,7 +153,7 @@ async function download(
difficulty: Difficulty[difficulty], difficulty: Difficulty[difficulty],
episode, episode,
sectionId: SectionId[section_id], sectionId: SectionId[section_id],
enemy: npc_type.code, enemy: NpcType[npc_type],
itemTypeId: item_type.id, itemTypeId: item_type.id,
dropRate: drop_rate_num / drop_rate_denom, dropRate: drop_rate_num / drop_rate_denom,
rareRate: rare_rate_num / rare_rate_denom, rareRate: rare_rate_num / rare_rate_denom,
@ -159,7 +163,7 @@ async function download(
} catch (e) { } catch (e) {
logger.error( logger.error(
`Error while processing item ${item} of ${enemy_or_box} in episode ${episode} ${Difficulty[difficulty]}.`, `Error while processing item ${item} of ${enemy_or_box} in episode ${episode} ${Difficulty[difficulty]}.`,
e e,
); );
} }
} }
@ -167,7 +171,7 @@ async function download(
} catch (e) { } catch (e) {
logger.error( logger.error(
`Error while processing ${enemy_or_box_text} in episode ${episode} ${difficulty}.`, `Error while processing ${enemy_or_box_text} in episode ${episode} ${difficulty}.`,
e e,
); );
} }
}); });

View File

@ -1,23 +1,16 @@
import { readdirSync, readFileSync, statSync, writeFileSync } from "fs"; import { readdirSync, readFileSync, statSync, writeFileSync } from "fs";
import Logger from "js-logger"; import Logger from "js-logger";
import { ASSETS_DIR, RESOURCE_DIR } from "."; import { ASSETS_DIR, RESOURCE_DIR } from ".";
import { Endianness } from "../src/data_formats"; import { BufferCursor } from "../src/core/data_formats/cursor/BufferCursor";
import { BufferCursor } from "../src/data_formats/cursor/BufferCursor"; import { ItemPmt, parse_item_pmt } from "../src/core/data_formats/parsing/itempmt";
import { ItemPmt, parse_item_pmt } from "../src/data_formats/parsing/itempmt"; import { parse_quest } from "../src/core/data_formats/parsing/quest";
import { parse_quest } from "../src/data_formats/parsing/quest"; import { parse_unitxt, Unitxt } from "../src/core/data_formats/parsing/unitxt";
import { parse_unitxt, Unitxt } from "../src/data_formats/parsing/unitxt"; import { Difficulties, Difficulty, SectionId, SectionIds } from "../src/core/domain";
import { import { BoxDropDto, EnemyDropDto, ItemTypeDto, QuestDto } from "../src/core/dto";
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 { update_drops_from_website } from "./update_drops_ephinea"; import { update_drops_from_website } from "./update_drops_ephinea";
import { Episode, EPISODES } from "../src/core/data_formats/parsing/quest/Episode";
import { npc_data, NPC_TYPES, NpcType } from "../src/core/data_formats/parsing/quest/npc_types";
import { Endianness } from "../src/core/data_formats/Endianness";
const logger = Logger.get("assets_generation/update_ephinea_data"); const logger = Logger.get("assets_generation/update_ephinea_data");
@ -87,7 +80,7 @@ function update_quests(): void {
const id_counts = quests.reduce( const id_counts = quests.reduce(
(counts, q) => counts.set(q.id, (counts.get(q.id) || 0) + 1), (counts, q) => counts.set(q.id, (counts.get(q.id) || 0) + 1),
new Map<number, number>() new Map<number, number>(),
); );
for (const [id, count] of id_counts.entries()) { for (const [id, count] of id_counts.entries()) {
@ -129,8 +122,8 @@ function process_quest(path: string, quests: QuestDto[]): void {
const enemy_counts: { [npc_type_code: string]: number } = {}; const enemy_counts: { [npc_type_code: string]: number } = {};
for (const npc of q.npcs) { for (const npc of q.npcs) {
if (npc.type.enemy) { if (npc_data(npc.type).enemy) {
enemy_counts[npc.type.code] = (enemy_counts[npc.type.code] || 0) + 1; enemy_counts[NpcType[npc.type]] = (enemy_counts[NpcType[npc.type]] || 0) + 1;
} }
} }
@ -273,7 +266,7 @@ function update_drops(item_pt: ItemPt): void {
const enemy_drops = new Array<EnemyDropDto>(); const enemy_drops = new Array<EnemyDropDto>();
for (const diff of Difficulties) { for (const diff of Difficulties) {
for (const ep of Episodes) { for (const ep of EPISODES) {
for (const sid of SectionIds) { for (const sid of SectionIds) {
enemy_drops.push(...load_enemy_drops(item_pt, diff, ep, sid)); enemy_drops.push(...load_enemy_drops(item_pt, diff, ep, sid));
} }
@ -285,7 +278,7 @@ function update_drops(item_pt: ItemPt): void {
const box_drops = new Array<BoxDropDto>(); const box_drops = new Array<BoxDropDto>();
for (const diff of Difficulties) { for (const diff of Difficulties) {
for (const ep of Episodes) { for (const ep of EPISODES) {
for (const sid of SectionIds) { for (const sid of SectionIds) {
box_drops.push(...load_box_drops(diff, ep, sid)); box_drops.push(...load_box_drops(diff, ep, sid));
} }
@ -330,8 +323,8 @@ function load_item_pt(): ItemPt {
cursor.seek(1608); cursor.seek(1608);
const enemy_dar = cursor.u8_array(100); const enemy_dar = cursor.u8_array(100);
for (const npc of NpcTypes) { for (const npc of NPC_TYPES) {
if (npc.episode !== episode) continue; if (npc_data(npc).episode !== episode) continue;
switch (npc) { switch (npc) {
case NpcType.Dragon: case NpcType.Dragon:
@ -373,134 +366,134 @@ function load_item_pt(): ItemPt {
dar_table, dar_table,
}; };
for (const npc of NpcTypes) { for (const npc of NPC_TYPES) {
if (npc.episode !== Episode.IV) continue; if (npc_data(npc).episode !== Episode.IV) continue;
switch (npc) { switch (npc) {
case NpcType.SandRappy: case NpcType.SandRappy:
dar_table.set( dar_table.set(
npc, npc,
table[Episode.I][diff][sid].dar_table.get(NpcType.RagRappy)! table[Episode.I][diff][sid].dar_table.get(NpcType.RagRappy)!,
); );
break; break;
case NpcType.DelRappy: case NpcType.DelRappy:
dar_table.set( dar_table.set(
npc, npc,
table[Episode.I][diff][sid].dar_table.get(NpcType.AlRappy)! table[Episode.I][diff][sid].dar_table.get(NpcType.AlRappy)!,
); );
break; break;
case NpcType.Astark: case NpcType.Astark:
dar_table.set( dar_table.set(
npc, npc,
table[Episode.I][diff][sid].dar_table.get(NpcType.Hildebear)! table[Episode.I][diff][sid].dar_table.get(NpcType.Hildebear)!,
); );
break; break;
case NpcType.SatelliteLizard: case NpcType.SatelliteLizard:
dar_table.set( dar_table.set(
npc, npc,
table[Episode.I][diff][sid].dar_table.get(NpcType.SavageWolf)! table[Episode.I][diff][sid].dar_table.get(NpcType.SavageWolf)!,
); );
break; break;
case NpcType.Yowie: case NpcType.Yowie:
dar_table.set( dar_table.set(
npc, npc,
table[Episode.I][diff][sid].dar_table.get(NpcType.BarbarousWolf)! table[Episode.I][diff][sid].dar_table.get(NpcType.BarbarousWolf)!,
); );
break; break;
case NpcType.MerissaA: case NpcType.MerissaA:
dar_table.set( dar_table.set(
npc, npc,
table[Episode.I][diff][sid].dar_table.get(NpcType.PofuillySlime)! table[Episode.I][diff][sid].dar_table.get(NpcType.PofuillySlime)!,
); );
break; break;
case NpcType.MerissaAA: case NpcType.MerissaAA:
dar_table.set( dar_table.set(
npc, npc,
table[Episode.I][diff][sid].dar_table.get(NpcType.PouillySlime)! table[Episode.I][diff][sid].dar_table.get(NpcType.PouillySlime)!,
); );
break; break;
case NpcType.Girtablulu: case NpcType.Girtablulu:
dar_table.set( dar_table.set(
npc, npc,
table[Episode.II][diff][sid].dar_table.get(NpcType.Mericarol)! table[Episode.II][diff][sid].dar_table.get(NpcType.Mericarol)!,
); );
break; break;
case NpcType.Zu: case NpcType.Zu:
dar_table.set( dar_table.set(
npc, npc,
table[Episode.II][diff][sid].dar_table.get(NpcType.GiGue)! table[Episode.II][diff][sid].dar_table.get(NpcType.GiGue)!,
); );
break; break;
case NpcType.Pazuzu: case NpcType.Pazuzu:
dar_table.set( dar_table.set(
npc, npc,
table[Episode.I][diff][sid].dar_table.get(NpcType.Hildeblue)! table[Episode.I][diff][sid].dar_table.get(NpcType.Hildeblue)!,
); );
break; break;
case NpcType.Boota: case NpcType.Boota:
dar_table.set( dar_table.set(
npc, npc,
table[Episode.I][diff][sid].dar_table.get(NpcType.Booma)! table[Episode.I][diff][sid].dar_table.get(NpcType.Booma)!,
); );
break; break;
case NpcType.ZeBoota: case NpcType.ZeBoota:
dar_table.set( dar_table.set(
npc, npc,
table[Episode.I][diff][sid].dar_table.get(NpcType.Gobooma)! table[Episode.I][diff][sid].dar_table.get(NpcType.Gobooma)!,
); );
break; break;
case NpcType.BaBoota: case NpcType.BaBoota:
dar_table.set( dar_table.set(
npc, npc,
table[Episode.I][diff][sid].dar_table.get(NpcType.Gigobooma)! table[Episode.I][diff][sid].dar_table.get(NpcType.Gigobooma)!,
); );
break; break;
case NpcType.Dorphon: case NpcType.Dorphon:
dar_table.set( dar_table.set(
npc, npc,
table[Episode.II][diff][sid].dar_table.get(NpcType.Delbiter)! table[Episode.II][diff][sid].dar_table.get(NpcType.Delbiter)!,
); );
break; break;
case NpcType.DorphonEclair: case NpcType.DorphonEclair:
dar_table.set( dar_table.set(
npc, npc,
table[Episode.I][diff][sid].dar_table.get(NpcType.Hildeblue)! table[Episode.I][diff][sid].dar_table.get(NpcType.Hildeblue)!,
); );
break; break;
case NpcType.Goran: case NpcType.Goran:
dar_table.set( dar_table.set(
npc, npc,
table[Episode.I][diff][sid].dar_table.get(NpcType.Dimenian)! table[Episode.I][diff][sid].dar_table.get(NpcType.Dimenian)!,
); );
break; break;
case NpcType.PyroGoran: case NpcType.PyroGoran:
dar_table.set( dar_table.set(
npc, npc,
table[Episode.I][diff][sid].dar_table.get(NpcType.LaDimenian)! table[Episode.I][diff][sid].dar_table.get(NpcType.LaDimenian)!,
); );
break; break;
case NpcType.GoranDetonator: case NpcType.GoranDetonator:
dar_table.set( dar_table.set(
npc, npc,
table[Episode.I][diff][sid].dar_table.get(NpcType.SoDimenian)! table[Episode.I][diff][sid].dar_table.get(NpcType.SoDimenian)!,
); );
break; break;
case NpcType.SaintMilion: case NpcType.SaintMilion:
dar_table.set( dar_table.set(
npc, npc,
table[Episode.I][diff][sid].dar_table.get(NpcType.DarkFalz)! table[Episode.I][diff][sid].dar_table.get(NpcType.DarkFalz)!,
); );
break; break;
case NpcType.Shambertin: case NpcType.Shambertin:
dar_table.set( dar_table.set(
npc, npc,
table[Episode.I][diff][sid].dar_table.get(NpcType.DarkFalz)! table[Episode.I][diff][sid].dar_table.get(NpcType.DarkFalz)!,
); );
break; break;
case NpcType.Kondrieu: case NpcType.Kondrieu:
dar_table.set( dar_table.set(
npc, npc,
table[Episode.I][diff][sid].dar_table.get(NpcType.DarkFalz)! table[Episode.I][diff][sid].dar_table.get(NpcType.DarkFalz)!,
); );
break; break;
} }
@ -516,11 +509,11 @@ function load_enemy_drops(
item_pt: ItemPt, item_pt: ItemPt,
difficulty: Difficulty, difficulty: Difficulty,
episode: Episode, episode: Episode,
section_id: SectionId section_id: SectionId,
): EnemyDropDto[] { ): EnemyDropDto[] {
const drops: EnemyDropDto[] = []; const drops: EnemyDropDto[] = [];
const drops_buf = readFileSync( const drops_buf = readFileSync(
`${EPHINEA_RESOURCE_DIR}/login-config/drop/ep${episode}_mob_${difficulty}_${section_id}.txt` `${EPHINEA_RESOURCE_DIR}/login-config/drop/ep${episode}_mob_${difficulty}_${section_id}.txt`,
); );
let line_no = 0; let line_no = 0;
@ -539,13 +532,13 @@ function load_enemy_drops(
const dar = item_pt[episode][difficulty][section_id].dar_table.get(enemy); const dar = item_pt[episode][difficulty][section_id].dar_table.get(enemy);
if (dar == null) { if (dar == null) {
logger.error(`No DAR found for ${enemy.name}.`); logger.error(`No DAR found for ${NpcType[enemy]}.`);
} else if (rare_rate > 0 && item_type_id) { } else if (rare_rate > 0 && item_type_id) {
drops.push({ drops.push({
difficulty: Difficulty[difficulty], difficulty: Difficulty[difficulty],
episode, episode,
sectionId: SectionId[section_id], sectionId: SectionId[section_id],
enemy: enemy.code, enemy: NpcType[enemy],
itemTypeId: item_type_id, itemTypeId: item_type_id,
dropRate: dar, dropRate: dar,
rareRate: rare_rate, rareRate: rare_rate,
@ -564,11 +557,11 @@ function load_enemy_drops(
function load_box_drops( function load_box_drops(
difficulty: Difficulty, difficulty: Difficulty,
episode: Episode, episode: Episode,
section_id: SectionId section_id: SectionId,
): BoxDropDto[] { ): BoxDropDto[] {
const drops: BoxDropDto[] = []; const drops: BoxDropDto[] = [];
const drops_buf = readFileSync( const drops_buf = readFileSync(
`${EPHINEA_RESOURCE_DIR}/login-config/drop/ep${episode}_box_${difficulty}_${section_id}.txt` `${EPHINEA_RESOURCE_DIR}/login-config/drop/ep${episode}_box_${difficulty}_${section_id}.txt`,
); );
let line_no = 0; let line_no = 0;
@ -606,7 +599,7 @@ function load_box_drops(
function get_stat_boosts( function get_stat_boosts(
item_pmt: ItemPmt, item_pmt: ItemPmt,
stat_boost_index: number stat_boost_index: number,
): { ): {
atp: number; atp: number;
ata: number; ata: number;

View File

@ -1,10 +1,10 @@
import { readFileSync, writeFileSync } from "fs"; import { readFileSync, writeFileSync } from "fs";
import Logger from "js-logger"; import Logger from "js-logger";
import { ASSETS_DIR, RESOURCE_DIR, SRC_DIR } from "."; import { ASSETS_DIR, RESOURCE_DIR, SRC_DIR } from ".";
import { Endianness } from "../src/data_formats"; import { BufferCursor } from "../src/core/data_formats/cursor/BufferCursor";
import { BufferCursor } from "../src/data_formats/cursor/BufferCursor"; import { parse_rlc } from "../src/core/data_formats/parsing/rlc";
import { parse_rlc } from "../src/data_formats/parsing/rlc";
import YAML from "yaml"; import YAML from "yaml";
import { Endianness } from "../src/core/data_formats/Endianness";
const logger = Logger.get("assets_generation/update_generic_data"); const logger = Logger.get("assets_generation/update_generic_data");
@ -30,7 +30,7 @@ function extract_player_animations(): void {
for (const file of parse_rlc(new BufferCursor(buf, Endianness.Big))) { for (const file of parse_rlc(new BufferCursor(buf, Endianness.Big))) {
writeFileSync( writeFileSync(
`${ASSETS_DIR}/player/animation/animation_${(i++).toString().padStart(3, "0")}.njm`, `${ASSETS_DIR}/player/animation/animation_${(i++).toString().padStart(3, "0")}.njm`,
new Uint8Array(file.array_buffer()) new Uint8Array(file.array_buffer()),
); );
} }
@ -137,7 +137,7 @@ function opcode_to_code(output: string[], code: number, opcode?: any): void {
} }
} }
function params_to_code(params: any[]) { function params_to_code(params: any[]): string {
return params return params
.map((param: any) => { .map((param: any) => {
let type: string; let type: string;
@ -181,7 +181,7 @@ function params_to_code(params: any[]) {
break; break;
case "reg_tup_ref": case "reg_tup_ref":
type = `{ kind: Kind.RegTupRef, register_tuples: [${params_to_code( type = `{ kind: Kind.RegTupRef, register_tuples: [${params_to_code(
param.reg_tup param.reg_tup,
)}] }`; )}] }`;
break; break;
case "reg_ref_var": case "reg_ref_var":

View File

@ -1,5 +1,5 @@
import { autorun, observable } from "mobx"; import { autorun, observable } from "mobx";
import { Server } from "../domain"; import { Server } from "../../core/domain";
class ApplicationStore { class ApplicationStore {
@observable current_server: Server = Server.Ephinea; @observable current_server: Server = Server.Ephinea;

View File

@ -2,13 +2,13 @@ import { Menu, Select } from "antd";
import { ClickParam } from "antd/lib/menu"; import { ClickParam } from "antd/lib/menu";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import React, { ReactNode, Component } from "react"; import React, { ReactNode, Component } from "react";
import { Server } from "../domain"; import { Server } from "../../core/domain";
import styles from "./ApplicationComponent.css"; import styles from "./ApplicationComponent.css";
import { DpsCalcComponent } from "./dps_calc/DpsCalcComponent"; import { DpsCalcComponent } from "../../dps_calc/ui/DpsCalcComponent";
import { with_error_boundary } from "./ErrorBoundary"; import { with_error_boundary } from "../../core/ui/ErrorBoundary";
import { HuntOptimizerComponent } from "./hunt_optimizer/HuntOptimizerComponent"; import { HuntOptimizerComponent } from "../../hunt_optimizer/ui/HuntOptimizerComponent";
import { QuestEditorComponent } from "./quest_editor/QuestEditorComponent"; import { QuestEditorComponent } from "../../quest_editor/ui/QuestEditorComponent";
import { ViewerComponent } from "./viewer/ViewerComponent"; import { ViewerComponent } from "../../viewer/ui/ViewerComponent";
import { application_store } from "../stores/ApplicationStore"; import { application_store } from "../stores/ApplicationStore";
const Viewer = with_error_boundary(ViewerComponent); const Viewer = with_error_boundary(ViewerComponent);

View File

@ -65,7 +65,7 @@ class Context {
this.src = cursor; this.src = cursor;
this.dst = new ResizableBufferCursor( this.dst = new ResizableBufferCursor(
new ResizableBuffer(Math.floor(1.5 * cursor.size)), new ResizableBuffer(Math.floor(1.5 * cursor.size)),
cursor.endianness cursor.endianness,
); );
this.flags = 0; this.flags = 0;
this.flag_bits_left = 0; this.flag_bits_left = 0;

View File

@ -83,7 +83,7 @@ test("PRS compression and decompression of quest118_e.bin", () => {
if (test_byte !== orig_byte) { if (test_byte !== orig_byte) {
throw new Error( throw new Error(
`Byte ${matching_bytes} didn't match, expected ${orig_byte}, got ${test_byte}.` `Byte ${matching_bytes} didn't match, expected ${orig_byte}, got ${test_byte}.`,
); );
} }

View File

@ -189,7 +189,11 @@ export abstract class AbstractCursor implements Cursor {
return new Vec3(this.f32(), this.f32(), this.f32()); return new Vec3(this.f32(), this.f32(), this.f32());
} }
string_ascii(max_byte_length: number, null_terminated: boolean, drop_remaining: boolean) { string_ascii(
max_byte_length: number,
null_terminated: boolean,
drop_remaining: boolean,
): string {
let code_points: number[] = []; let code_points: number[] = [];
for (let i = 0; i < max_byte_length; i++) { for (let i = 0; i < max_byte_length; i++) {
@ -209,7 +213,11 @@ export abstract class AbstractCursor implements Cursor {
return String.fromCodePoint(...code_points); return String.fromCodePoint(...code_points);
} }
string_utf16(max_byte_length: number, null_terminated: boolean, drop_remaining: boolean) { string_utf16(
max_byte_length: number,
null_terminated: boolean,
drop_remaining: boolean,
): string {
let code_points: number[] = []; let code_points: number[] = [];
let len = Math.floor(max_byte_length / 2); let len = Math.floor(max_byte_length / 2);
@ -234,7 +242,7 @@ export abstract class AbstractCursor implements Cursor {
this.check_size("size", size, size); this.check_size("size", size, size);
const r = this.backing_buffer.slice( const r = this.backing_buffer.slice(
this.offset + this.position, this.offset + this.position,
this.offset + this.position + size this.offset + this.position + size,
); );
this._position += size; this._position += size;
return r; return r;

View File

@ -106,7 +106,7 @@ export abstract class AbstractWritableCursor extends AbstractCursor implements W
other.copy_to_uint8_array( other.copy_to_uint8_array(
new Uint8Array(this.backing_buffer, this.offset + this.position, size), new Uint8Array(this.backing_buffer, this.offset + this.position, size),
size size,
); );
this._position += size; this._position += size;

View File

@ -33,7 +33,7 @@ export class ArrayBufferCursor extends AbstractWritableCursor implements Writabl
buffer: ArrayBuffer, buffer: ArrayBuffer,
endianness: Endianness, endianness: Endianness,
offset: number = 0, offset: number = 0,
size: number = buffer.byteLength - offset size: number = buffer.byteLength - offset,
) { ) {
super(endianness, offset); super(endianness, offset);
this._size = size; this._size = size;

View File

@ -23,7 +23,7 @@ export class BufferCursor extends AbstractCursor implements Cursor {
buffer: Buffer, buffer: Buffer,
endianness: Endianness, endianness: Endianness,
offset: number = 0, offset: number = 0,
size: number = buffer.byteLength - offset size: number = buffer.byteLength - offset,
) { ) {
if (offset < 0 || offset > buffer.byteLength) { if (offset < 0 || offset > buffer.byteLength) {
throw new Error(`Offset ${offset} is out of bounds.`); throw new Error(`Offset ${offset} is out of bounds.`);

View File

@ -16,7 +16,7 @@ import { ResizableBufferCursor } from "./ResizableBufferCursor";
function test_all( function test_all(
name: string, name: string,
bytes: (endianness: Endianness) => number[], bytes: (endianness: Endianness) => number[],
run_test: (cursor: Cursor, endianness: Endianness) => void run_test: (cursor: Cursor, endianness: Endianness) => void,
): void { ): void {
const endiannesses = enum_values<Endianness>(Endianness); const endiannesses = enum_values<Endianness>(Endianness);
@ -69,7 +69,7 @@ test_all(
expect(cursor.size).toBe(cursor.position + cursor.bytes_left); expect(cursor.size).toBe(cursor.position + cursor.bytes_left);
expect(cursor.endianness).toBe(endianness); expect(cursor.endianness).toBe(endianness);
} }
} },
); );
test_all( test_all(
@ -81,7 +81,7 @@ test_all(
} else { } else {
expect(cursor.u32()).toBe(0x01020304); expect(cursor.u32()).toBe(0x01020304);
} }
} },
); );
/** /**
@ -120,7 +120,7 @@ function test_integer_read(method_name: string): void {
expect((cursor as any)[method_name]()).toBe(expected_number_2); expect((cursor as any)[method_name]()).toBe(expected_number_2);
expect(cursor.position).toBe(2 * byte_count); expect(cursor.position).toBe(2 * byte_count);
} },
); );
} }
@ -138,7 +138,7 @@ test_all(
expect(cursor.u8_array(3)).toEqual([1, 2, 3]); expect(cursor.u8_array(3)).toEqual([1, 2, 3]);
expect(cursor.seek_start(2).u8_array(4)).toEqual([3, 4, 5, 6]); expect(cursor.seek_start(2).u8_array(4)).toEqual([3, 4, 5, 6]);
expect(cursor.seek_start(5).u8_array(3)).toEqual([6, 7, 8]); expect(cursor.seek_start(5).u8_array(3)).toEqual([6, 7, 8]);
} },
); );
test_all( test_all(
@ -148,7 +148,7 @@ test_all(
expect(cursor.u16_array(3)).toEqual([0x0101, 0x0202, 0x0303]); expect(cursor.u16_array(3)).toEqual([0x0101, 0x0202, 0x0303]);
expect(cursor.seek_start(4).u16_array(4)).toEqual([0x0303, 0x0404, 0x0505, 0x0606]); expect(cursor.seek_start(4).u16_array(4)).toEqual([0x0303, 0x0404, 0x0505, 0x0606]);
expect(cursor.seek_start(10).u16_array(3)).toEqual([0x0606, 0x0707, 0x0808]); expect(cursor.seek_start(10).u16_array(3)).toEqual([0x0606, 0x0707, 0x0808]);
} },
); );
function test_string_read(method_name: string, char_size: number): void { function test_string_read(method_name: string, char_size: number): void {
@ -194,7 +194,7 @@ function test_string_read(method_name: string, char_size: number): void {
cursor.seek_start(char_size); cursor.seek_start(char_size);
expect((cursor as any)[method_name](4 * char_size, false, false)).toBe("AB\0ÿ"); expect((cursor as any)[method_name](4 * char_size, false, false)).toBe("AB\0ÿ");
expect(cursor.position).toBe(5 * char_size); expect(cursor.position).toBe(5 * char_size);
} },
); );
} }

View File

@ -157,7 +157,7 @@ export interface Cursor {
string_ascii( string_ascii(
max_byte_length: number, max_byte_length: number,
null_terminated: boolean, null_terminated: boolean,
drop_remaining: boolean drop_remaining: boolean,
): string; ): string;
/** /**
@ -166,7 +166,7 @@ export interface Cursor {
string_utf16( string_utf16(
max_byte_length: number, max_byte_length: number,
null_terminated: boolean, null_terminated: boolean,
drop_remaining: boolean drop_remaining: boolean,
): string; ): string;
array_buffer(size?: number): ArrayBuffer; array_buffer(size?: number): ArrayBuffer;

View File

@ -38,7 +38,7 @@ export class ResizableBufferCursor extends AbstractWritableCursor implements Wri
buffer: ResizableBuffer, buffer: ResizableBuffer,
endianness: Endianness, endianness: Endianness,
offset: number = 0, offset: number = 0,
size: number = buffer.size - offset size: number = buffer.size - offset,
) { ) {
if (offset < 0 || offset > buffer.size) { if (offset < 0 || offset > buffer.size) {
throw new Error(`Offset ${offset} is out of bounds.`); throw new Error(`Offset ${offset} is out of bounds.`);

View File

@ -15,7 +15,7 @@ import { WritableCursor } from "./WritableCursor";
function test_all( function test_all(
name: string, name: string,
bytes: (endianness: Endianness) => number[], bytes: (endianness: Endianness) => number[],
run_test: (cursor: WritableCursor, endianness: Endianness) => void run_test: (cursor: WritableCursor, endianness: Endianness) => void,
): void { ): void {
const endiannesses = enum_values<Endianness>(Endianness); const endiannesses = enum_values<Endianness>(Endianness);
@ -69,7 +69,7 @@ test_all(
expect(cursor.position).toBe(3); expect(cursor.position).toBe(3);
expect(cursor.bytes_left).toBe(7); expect(cursor.bytes_left).toBe(7);
expect(cursor.endianness).toBe(endianness); expect(cursor.endianness).toBe(endianness);
} },
); );
/** /**
@ -106,7 +106,7 @@ function test_integer_write(method_name: string): void {
expect((cursor as any)[read_method_name]()).toBe(expected_number_1); expect((cursor as any)[read_method_name]()).toBe(expected_number_1);
expect((cursor as any)[read_method_name]()).toBe(expected_number_2); expect((cursor as any)[read_method_name]()).toBe(expected_number_2);
} },
); );
} }
@ -135,7 +135,7 @@ test_all(
expect(cursor.f32()).toBeCloseTo(103.502, 3); expect(cursor.f32()).toBeCloseTo(103.502, 3);
expect(cursor.position).toBe(8); expect(cursor.position).toBe(8);
} },
); );
test_all( test_all(
@ -169,7 +169,7 @@ test_all(
} }
expect(cursor.position).toBe(20); expect(cursor.position).toBe(20);
} },
); );
test_all( test_all(
@ -189,5 +189,5 @@ test_all(
expect(new_cursor.position).toBe(0); expect(new_cursor.position).toBe(0);
expect(new_cursor.u32()).toBe(3); expect(new_cursor.u32()).toBe(3);
expect(new_cursor.u32()).toBe(4); expect(new_cursor.u32()).toBe(4);
} },
); );

View File

@ -41,7 +41,7 @@ export function parse_area_geometry(cursor: Cursor): RenderObject {
const section_rotation = new Vec3( const section_rotation = new Vec3(
cursor.u32() * ANGLE_TO_RAD, cursor.u32() * ANGLE_TO_RAD,
cursor.u32() * ANGLE_TO_RAD, cursor.u32() * ANGLE_TO_RAD,
cursor.u32() * ANGLE_TO_RAD cursor.u32() * ANGLE_TO_RAD,
); );
cursor.seek(4); cursor.seek(4);
@ -56,7 +56,7 @@ export function parse_area_geometry(cursor: Cursor): RenderObject {
const objects = parse_geometry_table( const objects = parse_geometry_table(
cursor, cursor,
simple_geometry_offset_table_offset, simple_geometry_offset_table_offset,
simple_geometry_offset_count simple_geometry_offset_count,
); );
sections.push({ sections.push({
@ -74,7 +74,7 @@ export function parse_area_geometry(cursor: Cursor): RenderObject {
function parse_geometry_table( function parse_geometry_table(
cursor: Cursor, cursor: Cursor,
table_offset: number, table_offset: number,
table_entry_count: number table_entry_count: number,
): NjObject<XjModel>[] { ): NjObject<XjModel>[] {
const objects: NjObject<XjModel>[] = []; const objects: NjObject<XjModel>[] = [];

View File

@ -35,7 +35,7 @@ export class NjObject<M extends NjModel> {
position: Vec3, position: Vec3,
rotation: Vec3, // Euler angles in radians. rotation: Vec3, // Euler angles in radians.
scale: Vec3, scale: Vec3,
children: NjObject<M>[] children: NjObject<M>[],
) { ) {
this.evaluation_flags = evaluation_flags; this.evaluation_flags = evaluation_flags;
this.model = model; this.model = model;
@ -70,7 +70,7 @@ export class NjObject<M extends NjModel> {
private get_bone_internal( private get_bone_internal(
object: NjObject<M>, object: NjObject<M>,
bone_id: number, bone_id: number,
id_ref: [number] id_ref: [number],
): NjObject<M> | undefined { ): NjObject<M> | undefined {
if (!object.evaluation_flags.skip) { if (!object.evaluation_flags.skip) {
const id = id_ref[0]++; const id = id_ref[0]++;
@ -125,7 +125,7 @@ export function parse_xj_object(cursor: Cursor): NjObject<XjModel>[] {
function parse_ninja<M extends NjModel>( function parse_ninja<M extends NjModel>(
cursor: Cursor, cursor: Cursor,
parse_model: (cursor: Cursor, context: any) => M, parse_model: (cursor: Cursor, context: any) => M,
context: any context: any,
): NjObject<M>[] { ): NjObject<M>[] {
// POF0 and other chunks types are ignored. // POF0 and other chunks types are ignored.
const njcm_chunks = parse_iff(cursor).filter(chunk => chunk.type === NJCM); const njcm_chunks = parse_iff(cursor).filter(chunk => chunk.type === NJCM);
@ -142,7 +142,7 @@ function parse_ninja<M extends NjModel>(
function parse_sibling_objects<M extends NjModel>( function parse_sibling_objects<M extends NjModel>(
cursor: Cursor, cursor: Cursor,
parse_model: (cursor: Cursor, context: any) => M, parse_model: (cursor: Cursor, context: any) => M,
context: any context: any,
): NjObject<M>[] { ): NjObject<M>[] {
const eval_flags = cursor.u32(); const eval_flags = cursor.u32();
const no_translate = (eval_flags & 0b1) !== 0; const no_translate = (eval_flags & 0b1) !== 0;
@ -205,7 +205,7 @@ function parse_sibling_objects<M extends NjModel>(
new Vec3(pos_x, pos_y, pos_z), new Vec3(pos_x, pos_y, pos_z),
new Vec3(rotation_x, rotation_y, rotation_z), new Vec3(rotation_x, rotation_y, rotation_z),
new Vec3(scale_x, scale_y, scale_z), new Vec3(scale_x, scale_y, scale_z),
children children,
); );
return [object, ...siblings]; return [object, ...siblings];

View File

@ -1,4 +1,4 @@
import { ANGLE_TO_RAD } from "."; import { ANGLE_TO_RAD } from "./index";
import { Cursor } from "../../cursor/Cursor"; import { Cursor } from "../../cursor/Cursor";
import { Vec3 } from "../../vector"; import { Vec3 } from "../../vector";
@ -201,7 +201,7 @@ function parse_motion_data_f(cursor: Cursor, count: number): NjKeyframeF[] {
function parse_motion_data_a( function parse_motion_data_a(
cursor: Cursor, cursor: Cursor,
keyframe_count: number, keyframe_count: number,
frame_count: number frame_count: number,
): NjKeyframeA[] { ): NjKeyframeA[] {
const frames: NjKeyframeA[] = []; const frames: NjKeyframeA[] = [];
const start_pos = cursor.position; const start_pos = cursor.position;
@ -212,7 +212,7 @@ function parse_motion_data_a(
value: new Vec3( value: new Vec3(
cursor.u16() * ANGLE_TO_RAD, cursor.u16() * ANGLE_TO_RAD,
cursor.u16() * ANGLE_TO_RAD, cursor.u16() * ANGLE_TO_RAD,
cursor.u16() * ANGLE_TO_RAD cursor.u16() * ANGLE_TO_RAD,
), ),
}); });
} }
@ -240,7 +240,7 @@ function parse_motion_data_a_wide(cursor: Cursor, keyframe_count: number): NjKey
value: new Vec3( value: new Vec3(
cursor.i32() * ANGLE_TO_RAD, cursor.i32() * ANGLE_TO_RAD,
cursor.i32() * ANGLE_TO_RAD, cursor.i32() * ANGLE_TO_RAD,
cursor.i32() * ANGLE_TO_RAD cursor.i32() * ANGLE_TO_RAD,
), ),
}); });
} }

View File

@ -201,7 +201,7 @@ export function parse_njcm_model(cursor: Cursor, cached_chunk_offsets: number[])
function parse_chunks( function parse_chunks(
cursor: Cursor, cursor: Cursor,
cached_chunk_offsets: number[], cached_chunk_offsets: number[],
wide_end_chunks: boolean wide_end_chunks: boolean,
): NjcmChunk[] { ): NjcmChunk[] {
const chunks: NjcmChunk[] = []; const chunks: NjcmChunk[] = [];
let loop = true; let loop = true;
@ -313,7 +313,7 @@ function parse_chunks(
function parse_vertex_chunk( function parse_vertex_chunk(
cursor: Cursor, cursor: Cursor,
chunk_type_id: number, chunk_type_id: number,
flags: number flags: number,
): NjcmChunkVertex[] { ): NjcmChunkVertex[] {
if (chunk_type_id < 32 || chunk_type_id > 50) { if (chunk_type_id < 32 || chunk_type_id > 50) {
logger.warn(`Unknown vertex chunk type ${chunk_type_id}.`); logger.warn(`Unknown vertex chunk type ${chunk_type_id}.`);
@ -375,7 +375,7 @@ function parse_vertex_chunk(
vertex.normal = new Vec3( vertex.normal = new Vec3(
((normal >> 20) & 0x3ff) / 0x3ff, ((normal >> 20) & 0x3ff) / 0x3ff,
((normal >> 10) & 0x3ff) / 0x3ff, ((normal >> 10) & 0x3ff) / 0x3ff,
(normal & 0x3ff) / 0x3ff (normal & 0x3ff) / 0x3ff,
); );
if (chunk_type_id >= 49) { if (chunk_type_id >= 49) {
@ -393,7 +393,7 @@ function parse_vertex_chunk(
function parse_triangle_strip_chunk( function parse_triangle_strip_chunk(
cursor: Cursor, cursor: Cursor,
chunk_type_id: number, chunk_type_id: number,
flags: number flags: number,
): NjcmTriangleStrip[] { ): NjcmTriangleStrip[] {
const render_flags = { const render_flags = {
ignore_light: (flags & 0b1) !== 0, ignore_light: (flags & 0b1) !== 0,
@ -474,7 +474,7 @@ function parse_triangle_strip_chunk(
vertex.normal = new Vec3( vertex.normal = new Vec3(
cursor.u16() / 255, cursor.u16() / 255,
cursor.u16() / 255, cursor.u16() / 255,
cursor.u16() / 255 cursor.u16() / 255,
); );
} }

View File

@ -37,7 +37,7 @@ export function parse_xvm(cursor: Cursor): Xvm {
logger.warn("No header found."); logger.warn("No header found.");
} else if (header.texture_count !== textures.length) { } else if (header.texture_count !== textures.length) {
logger.warn( logger.warn(
`Found ${textures.length} textures instead of ${header.texture_count} as defined in the header.` `Found ${textures.length} textures instead of ${header.texture_count} as defined in the header.`,
); );
} }

View File

@ -65,15 +65,15 @@ export function parse_xj_model(cursor: Cursor): XjModel {
} }
model.meshes.push( model.meshes.push(
...parse_triangle_strip_table(cursor, triangle_strip_table_offset, triangle_strip_count) ...parse_triangle_strip_table(cursor, triangle_strip_table_offset, triangle_strip_count),
); );
model.meshes.push( model.meshes.push(
...parse_triangle_strip_table( ...parse_triangle_strip_table(
cursor, cursor,
transparent_triangle_strip_table_offset, transparent_triangle_strip_table_offset,
transparent_triangle_strip_count transparent_triangle_strip_count,
) ),
); );
return model; return model;
@ -133,7 +133,7 @@ function parse_vertex_info_table(cursor: Cursor, vertex_info_table_offset: numbe
function parse_triangle_strip_table( function parse_triangle_strip_table(
cursor: Cursor, cursor: Cursor,
triangle_strip_list_offset: number, triangle_strip_list_offset: number,
triangle_strip_count: number triangle_strip_count: number,
): XjMesh[] { ): XjMesh[] {
const strips: XjMesh[] = []; const strips: XjMesh[] = [];
@ -148,7 +148,7 @@ function parse_triangle_strip_table(
const material_properties = parse_triangle_strip_material_properties( const material_properties = parse_triangle_strip_material_properties(
cursor, cursor,
material_table_offset, material_table_offset,
material_table_size material_table_size,
); );
cursor.seek_start(index_list_offset); cursor.seek_start(index_list_offset);
@ -166,7 +166,7 @@ function parse_triangle_strip_table(
function parse_triangle_strip_material_properties( function parse_triangle_strip_material_properties(
cursor: Cursor, cursor: Cursor,
offset: number, offset: number,
size: number size: number,
): XjMaterialProperties { ): XjMaterialProperties {
const props: XjMaterialProperties = {}; const props: XjMaterialProperties = {};

View File

@ -16,7 +16,7 @@ export function parse_prc(cursor: Cursor): Cursor {
if (out.size !== size) { if (out.size !== size) {
logger.warn( logger.warn(
`Size of decrypted, decompressed file was ${out.size} instead of expected ${size}.` `Size of decrypted, decompressed file was ${out.size} instead of expected ${size}.`,
); );
} }

View File

@ -8,7 +8,7 @@ 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. * Parse a file, convert the resulting structure to BIN again and check whether the end result is equal to the original.
*/ */
function test_quest(path: string) { function test_quest(path: string): void {
const orig_buffer = readFileSync(path); const orig_buffer = readFileSync(path);
const orig_bin = prs_decompress(new BufferCursor(orig_buffer, Endianness.Little)); const orig_bin = prs_decompress(new BufferCursor(orig_buffer, Endianness.Little));
const test_buffer = write_bin(parse_bin(orig_bin)); const test_buffer = write_bin(parse_bin(orig_bin));
@ -25,7 +25,7 @@ function test_quest(path: string) {
if (test_byte !== orig_byte) { if (test_byte !== orig_byte) {
throw new Error( throw new Error(
`Byte ${matching_bytes} didn't match, expected ${orig_byte}, got ${test_byte}.` `Byte ${matching_bytes} didn't match, expected ${orig_byte}, got ${test_byte}.`,
); );
} }

View File

@ -1,8 +1,8 @@
import Logger from "js-logger"; import Logger from "js-logger";
import { Endianness } from "../../Endianness"; import { Endianness } from "../../Endianness";
import { ControlFlowGraph } from "../../../scripting/data_flow_analysis/ControlFlowGraph"; import { ControlFlowGraph } from "../../../../quest_editor/scripting/data_flow_analysis/ControlFlowGraph";
import { register_value } from "../../../scripting/data_flow_analysis/register_value"; import { register_value } from "../../../../quest_editor/scripting/data_flow_analysis/register_value";
import { stack_value } from "../../../scripting/data_flow_analysis/stack_value"; import { stack_value } from "../../../../quest_editor/scripting/data_flow_analysis/stack_value";
import { import {
Arg, Arg,
DataSegment, DataSegment,
@ -11,8 +11,13 @@ import {
Segment, Segment,
SegmentType, SegmentType,
StringSegment, StringSegment,
} from "../../../scripting/instructions"; } from "../../../../quest_editor/scripting/instructions";
import { Kind, Opcode, OPCODES, StackInteraction } from "../../../scripting/opcodes"; import {
Kind,
Opcode,
OPCODES,
StackInteraction,
} from "../../../../quest_editor/scripting/opcodes";
import { ArrayBufferCursor } from "../../cursor/ArrayBufferCursor"; import { ArrayBufferCursor } from "../../cursor/ArrayBufferCursor";
import { Cursor } from "../../cursor/Cursor"; import { Cursor } from "../../cursor/Cursor";
import { ResizableBufferCursor } from "../../cursor/ResizableBufferCursor"; import { ResizableBufferCursor } from "../../cursor/ResizableBufferCursor";

View File

@ -58,7 +58,7 @@ export function parse_dat(cursor: Cursor): DatFile {
if (entities_size !== total_size - 16) { if (entities_size !== total_size - 16) {
throw Error( throw Error(
`Malformed DAT file. Expected an entities size of ${total_size - `Malformed DAT file. Expected an entities size of ${total_size -
16}, got ${entities_size}.` 16}, got ${entities_size}.`,
); );
} }
@ -94,7 +94,7 @@ export function parse_dat(cursor: Cursor): DatFile {
if (bytes_read !== entities_size) { if (bytes_read !== entities_size) {
logger.warn( logger.warn(
`Read ${bytes_read} bytes instead of expected ${entities_size} for entity type ${entity_type} (Object).` `Read ${bytes_read} bytes instead of expected ${entities_size} for entity type ${entity_type} (Object).`,
); );
cursor.seek(entities_size - bytes_read); cursor.seek(entities_size - bytes_read);
} }
@ -133,7 +133,7 @@ export function parse_dat(cursor: Cursor): DatFile {
if (bytes_read !== entities_size) { if (bytes_read !== entities_size) {
logger.warn( logger.warn(
`Read ${bytes_read} bytes instead of expected ${entities_size} for entity type ${entity_type} (NPC).` `Read ${bytes_read} bytes instead of expected ${entities_size} for entity type ${entity_type} (NPC).`,
); );
cursor.seek(entities_size - bytes_read); cursor.seek(entities_size - bytes_read);
} }
@ -157,7 +157,7 @@ export function write_dat({ objs, npcs, unknowns }: DatFile): ResizableBuffer {
const buffer = new ResizableBuffer( const buffer = new ResizableBuffer(
objs.length * (16 + OBJECT_SIZE) + objs.length * (16 + OBJECT_SIZE) +
npcs.length * (16 + NPC_SIZE) + npcs.length * (16 + NPC_SIZE) +
unknowns.reduce((a, b) => a + b.total_size, 0) unknowns.reduce((a, b) => a + b.total_size, 0),
); );
const cursor = new ResizableBufferCursor(buffer, Endianness.Little); const cursor = new ResizableBufferCursor(buffer, Endianness.Little);

View File

@ -1,6 +1,6 @@
import { readFileSync } from "fs"; import { readFileSync } from "fs";
import { Endianness } from "../../Endianness"; import { Endianness } from "../../Endianness";
import { walk_qst_files } from "../../../../test/src/utils"; import { walk_qst_files } from "../../../../../test/src/utils";
import { ArrayBufferCursor } from "../../cursor/ArrayBufferCursor"; import { ArrayBufferCursor } from "../../cursor/ArrayBufferCursor";
import { BufferCursor } from "../../cursor/BufferCursor"; import { BufferCursor } from "../../cursor/BufferCursor";
import { parse_quest, write_quest_qst } from "./index"; import { parse_quest, write_quest_qst } from "./index";

View File

@ -4,8 +4,8 @@ import {
InstructionSegment, InstructionSegment,
Segment, Segment,
SegmentType, SegmentType,
} from "../../../scripting/instructions"; } from "../../../../quest_editor/scripting/instructions";
import { Opcode } from "../../../scripting/opcodes"; import { Opcode } from "../../../../quest_editor/scripting/opcodes";
import { prs_compress } from "../../compression/prs/compress"; import { prs_compress } from "../../compression/prs/compress";
import { prs_decompress } from "../../compression/prs/decompress"; import { prs_decompress } from "../../compression/prs/decompress";
import { ArrayBufferCursor } from "../../cursor/ArrayBufferCursor"; import { ArrayBufferCursor } from "../../cursor/ArrayBufferCursor";

View File

@ -1,5 +1,6 @@
import { Episode, check_episode } from "./Episode"; import { Episode, check_episode } from "./Episode";
// Make sure ObjectType does not overlap NpcType.
export enum NpcType { export enum NpcType {
// //
// Unknown NPCs // Unknown NPCs
@ -181,7 +182,6 @@ export enum NpcType {
GoranDetonator, GoranDetonator,
SaintMilion, SaintMilion,
Shambertin, Shambertin,
// Kondrieu should be last to make sure ObjectType does not overlap NpcType. See code below.
Kondrieu, Kondrieu,
} }
@ -201,6 +201,7 @@ export type NpcTypeData = {
readonly rare_type?: NpcType; readonly rare_type?: NpcType;
}; };
export const NPC_TYPES: NpcType[] = [];
export const ENEMY_NPC_TYPES: NpcType[] = []; export const ENEMY_NPC_TYPES: NpcType[] = [];
export function npc_data(type: NpcType): NpcTypeData { export function npc_data(type: NpcType): NpcTypeData {
@ -234,13 +235,10 @@ function define_npc_type_data(
enemy: boolean, enemy: boolean,
rare_type?: NpcType, rare_type?: NpcType,
): void { ): void {
if (episode) { NPC_TYPES.push(npc_type);
const map = EP_AND_NAME_TO_NPC_TYPE[episode];
if (map) { if (enemy) {
map.set(simple_name, npc_type); ENEMY_NPC_TYPES.push(npc_type);
map.set(ultimate_name, npc_type);
}
} }
NPC_TYPE_DATA[npc_type] = { NPC_TYPE_DATA[npc_type] = {
@ -252,8 +250,13 @@ function define_npc_type_data(
rare_type, rare_type,
}; };
if (enemy) { if (episode) {
ENEMY_NPC_TYPES.push(npc_type); const map = EP_AND_NAME_TO_NPC_TYPE[episode];
if (map) {
map.set(simple_name, npc_type);
map.set(ultimate_name, npc_type);
}
} }
} }

View File

@ -1,4 +1,4 @@
import { walk_qst_files } from "../../../../test/src/utils"; import { walk_qst_files } from "../../../../../test/src/utils";
import { parse_qst, write_qst } from "./qst"; import { parse_qst, write_qst } from "./qst";
import { Endianness } from "../../Endianness"; import { Endianness } from "../../Endianness";
import { BufferCursor } from "../../cursor/BufferCursor"; import { BufferCursor } from "../../cursor/BufferCursor";

View File

@ -167,16 +167,16 @@ function parse_files(cursor: Cursor, expected_sizes: Map<string, number>): QstCo
expected_size, expected_size,
cursor: new ResizableBufferCursor( cursor: new ResizableBufferCursor(
new ResizableBuffer(expected_size || 10 * 1024), new ResizableBuffer(expected_size || 10 * 1024),
Endianness.Little Endianness.Little,
), ),
chunk_nos: new Set(), chunk_nos: new Set(),
}) }),
); );
} }
if (file.chunk_nos.has(chunk_no)) { if (file.chunk_nos.has(chunk_no)) {
logger.warn( logger.warn(
`File chunk number ${chunk_no} of file ${file_name} was already encountered, overwriting previous chunk.` `File chunk number ${chunk_no} of file ${file_name} was already encountered, overwriting previous chunk.`,
); );
} else { } else {
file.chunk_nos.add(chunk_no); file.chunk_nos.add(chunk_no);
@ -188,7 +188,7 @@ function parse_files(cursor: Cursor, expected_sizes: Map<string, number>): QstCo
if (size > 1024) { if (size > 1024) {
logger.warn( logger.warn(
`Data segment size of ${size} is larger than expected maximum size, reading just 1024 bytes.` `Data segment size of ${size} is larger than expected maximum size, reading just 1024 bytes.`,
); );
size = 1024; size = 1024;
} }
@ -204,7 +204,7 @@ function parse_files(cursor: Cursor, expected_sizes: Map<string, number>): QstCo
if (cursor.position !== start_position + 1056) { if (cursor.position !== start_position + 1056) {
throw new Error( throw new Error(
`Read ${cursor.position - `Read ${cursor.position -
start_position} file chunk message bytes instead of expected 1056.` start_position} file chunk message bytes instead of expected 1056.`,
); );
} }
} }
@ -221,7 +221,7 @@ function parse_files(cursor: Cursor, expected_sizes: Map<string, number>): QstCo
// Check whether the expected size was correct. // Check whether the expected size was correct.
if (file.expected_size != null && file.cursor.size !== file.expected_size) { if (file.expected_size != null && file.cursor.size !== file.expected_size) {
logger.warn( logger.warn(
`File ${file.name} has an actual size of ${file.cursor.size} instead of the expected size ${file.expected_size}.` `File ${file.name} has an actual size of ${file.cursor.size} instead of the expected size ${file.expected_size}.`,
); );
} }
@ -279,7 +279,7 @@ function write_file_headers(cursor: WritableCursor, files: QstContainedFileParam
if (file_name_2.length > 24) { if (file_name_2.length > 24) {
throw Error( throw Error(
`File ${file.name} has a file_name_2 length (${file_name_2}) longer than 24 characters.` `File ${file.name} has a file_name_2 length (${file_name_2}) longer than 24 characters.`,
); );
} }
@ -305,7 +305,7 @@ function write_file_chunks(cursor: WritableCursor, files: QstContainedFileParam[
cursor, cursor,
file_to_chunk.data, file_to_chunk.data,
file_to_chunk.no++, file_to_chunk.no++,
file_to_chunk.name file_to_chunk.name,
) )
) { ) {
done++; done++;
@ -319,7 +319,7 @@ function write_file_chunks(cursor: WritableCursor, files: QstContainedFileParam[
if (file_to_chunk.no !== expected_chunks) { if (file_to_chunk.no !== expected_chunks) {
throw new Error( throw new Error(
`Expected to write ${expected_chunks} chunks for file "${file_to_chunk.name}" but ${file_to_chunk.no} where written.` `Expected to write ${expected_chunks} chunks for file "${file_to_chunk.name}" but ${file_to_chunk.no} where written.`,
); );
} }
} }
@ -332,7 +332,7 @@ function write_file_chunk(
cursor: WritableCursor, cursor: WritableCursor,
data: Cursor, data: Cursor,
chunk_no: number, chunk_no: number,
name: string name: string,
): boolean { ): boolean {
cursor.write_u8_array([28, 4, 19, 0]); cursor.write_u8_array([28, 4, 19, 0]);
cursor.write_u8(chunk_no); cursor.write_u8(chunk_no);

34
src/core/domain/index.ts Normal file
View File

@ -0,0 +1,34 @@
import { enum_values } from "../enums";
export const RARE_ENEMY_PROB = 1 / 512;
export const KONDRIEU_PROB = 1 / 10;
export enum Server {
Ephinea = "Ephinea",
}
export const Servers: Server[] = enum_values(Server);
export enum SectionId {
Viridia,
Greenill,
Skyly,
Bluefull,
Purplenum,
Pinkal,
Redria,
Oran,
Yellowboze,
Whitill,
}
export const SectionIds: SectionId[] = enum_values(SectionId);
export enum Difficulty {
Normal,
Hard,
VHard,
Ultimate,
}
export const Difficulties: Difficulty[] = enum_values(Difficulty);

View File

@ -19,7 +19,7 @@ export class WeaponItemType implements ItemType {
readonly max_atp: number, readonly max_atp: number,
readonly ata: number, readonly ata: number,
readonly max_grind: number, readonly max_grind: number,
readonly required_atp: number readonly required_atp: number,
) {} ) {}
} }
@ -35,7 +35,7 @@ export class ArmorItemType implements ItemType {
readonly max_dfp: number, readonly max_dfp: number,
readonly mst: number, readonly mst: number,
readonly hp: number, readonly hp: number,
readonly lck: number readonly lck: number,
) {} ) {}
} }
@ -51,7 +51,7 @@ export class ShieldItemType implements ItemType {
readonly max_dfp: number, readonly max_dfp: number,
readonly mst: number, readonly mst: number,
readonly hp: number, readonly hp: number,
readonly lck: number readonly lck: number,
) {} ) {}
} }

View File

@ -1,5 +1,5 @@
import Logger from "js-logger"; import Logger from "js-logger";
import { Server } from "../domain"; import { Server } from "./domain";
const logger = Logger.get("persistence/Persister"); const logger = Logger.get("persistence/Persister");

View File

@ -96,7 +96,7 @@ export class Renderer<C extends PerspectiveCamera | OrthographicCamera> {
position.z, position.z,
look_at.x, look_at.x,
look_at.y, look_at.y,
look_at.z look_at.z,
); );
} }

View File

@ -58,7 +58,7 @@ export class GeometryBuilder {
return new Vector3( return new Vector3(
this.positions[3 * index], this.positions[3 * index],
this.positions[3 * index + 1], this.positions[3 * index + 1],
this.positions[3 * index + 2] this.positions[3 * index + 2],
); );
} }
@ -66,7 +66,7 @@ export class GeometryBuilder {
return new Vector3( return new Vector3(
this.normals[3 * index], this.normals[3 * index],
this.normals[3 * index + 1], this.normals[3 * index + 1],
this.normals[3 * index + 2] this.normals[3 * index + 2],
); );
} }

View File

@ -26,7 +26,7 @@ const DEFAULT_SKINNED_MATERIAL = new MeshLambertMaterial({
export function create_mesh( export function create_mesh(
geometry: BufferGeometry, geometry: BufferGeometry,
material?: Material | Material[], material?: Material | Material[],
default_material: Material = DEFAULT_MATERIAL default_material: Material = DEFAULT_MATERIAL,
): Mesh { ): Mesh {
return create(geometry, material, default_material, Mesh); return create(geometry, material, default_material, Mesh);
} }
@ -34,7 +34,7 @@ export function create_mesh(
export function create_skinned_mesh( export function create_skinned_mesh(
geometry: BufferGeometry, geometry: BufferGeometry,
material?: Material | Material[], material?: Material | Material[],
default_material: Material = DEFAULT_SKINNED_MATERIAL default_material: Material = DEFAULT_SKINNED_MATERIAL,
): SkinnedMesh { ): SkinnedMesh {
return create(geometry, material, default_material, SkinnedMesh); return create(geometry, material, default_material, SkinnedMesh);
} }
@ -43,7 +43,7 @@ function create<M extends Mesh>(
geometry: BufferGeometry, geometry: BufferGeometry,
material: Material | Material[] | undefined, material: Material | Material[] | undefined,
default_material: Material, default_material: Material,
mesh_constructor: new (geometry: BufferGeometry, material: Material | Material[]) => M mesh_constructor: new (geometry: BufferGeometry, material: Material | Material[]) => M,
): M { ): M {
const { const {
created_by_geometry_builder, created_by_geometry_builder,

View File

@ -19,7 +19,7 @@ export const PSO_FRAME_RATE = 30;
export function create_animation_clip( export function create_animation_clip(
nj_object: NjObject<NjModel>, nj_object: NjObject<NjModel>,
nj_motion: NjMotion nj_motion: NjMotion,
): AnimationClip { ): AnimationClip {
const interpolation = const interpolation =
nj_motion.interpolation === NjInterpolation.Spline ? InterpolateSmooth : InterpolateLinear; nj_motion.interpolation === NjInterpolation.Spline ? InterpolateSmooth : InterpolateLinear;
@ -40,7 +40,7 @@ export function create_animation_clip(
if (type === NjKeyframeTrackType.Rotation) { if (type === NjKeyframeTrackType.Rotation) {
const order = bone.evaluation_flags.zxy_rotation_order ? "ZXY" : "ZYX"; const order = bone.evaluation_flags.zxy_rotation_order ? "ZXY" : "ZYX";
const quat = new Quaternion().setFromEuler( const quat = new Quaternion().setFromEuler(
new Euler(keyframe.value.x, keyframe.value.y, keyframe.value.z, order) new Euler(keyframe.value.x, keyframe.value.y, keyframe.value.z, order),
); );
values.push(quat.x, quat.y, quat.z, quat.w); values.push(quat.x, quat.y, quat.z, quat.w);
@ -55,8 +55,8 @@ export function create_animation_clip(
`.bones[${bone_id}].quaternion`, `.bones[${bone_id}].quaternion`,
times, times,
values, values,
interpolation interpolation,
) ),
); );
} else { } else {
const name = const name =
@ -72,6 +72,6 @@ export function create_animation_clip(
return new AnimationClip( return new AnimationClip(
"Animation", "Animation",
(nj_motion.frame_count - 1) / PSO_FRAME_RATE, (nj_motion.frame_count - 1) / PSO_FRAME_RATE,
tracks tracks,
).optimize(); ).optimize();
} }

View File

@ -1,5 +1,5 @@
import { Bone, BufferGeometry, Euler, Matrix3, Matrix4, Quaternion, Vector2, Vector3 } from "three"; import { Bone, BufferGeometry, Euler, Matrix3, Matrix4, Quaternion, Vector2, Vector3 } from "three";
import { vec3_to_threejs } from "."; import { vec3_to_threejs } from "./index";
import { is_njcm_model, NjModel, NjObject } from "../../data_formats/parsing/ninja"; import { is_njcm_model, NjModel, NjObject } from "../../data_formats/parsing/ninja";
import { NjcmModel } from "../../data_formats/parsing/ninja/njcm"; import { NjcmModel } from "../../data_formats/parsing/ninja/njcm";
import { XjModel } from "../../data_formats/parsing/ninja/xj"; import { XjModel } from "../../data_formats/parsing/ninja/xj";
@ -13,7 +13,7 @@ const NO_SCALE = new Vector3(1, 1, 1);
export function ninja_object_to_geometry_builder( export function ninja_object_to_geometry_builder(
object: NjObject<NjModel>, object: NjObject<NjModel>,
builder: GeometryBuilder builder: GeometryBuilder,
): void { ): void {
new GeometryCreator(builder).to_geometry_builder(object); new GeometryCreator(builder).to_geometry_builder(object);
} }
@ -74,7 +74,7 @@ class GeometryCreator {
private object_to_geometry( private object_to_geometry(
object: NjObject<NjModel>, object: NjObject<NjModel>,
parent_bone: Bone | undefined, parent_bone: Bone | undefined,
parent_matrix: Matrix4 parent_matrix: Matrix4,
): void { ): void {
const { const {
no_translate, no_translate,
@ -91,13 +91,13 @@ class GeometryCreator {
rotation.x, rotation.x,
rotation.y, rotation.y,
rotation.z, rotation.z,
zxy_rotation_order ? "ZXY" : "ZYX" zxy_rotation_order ? "ZXY" : "ZYX",
); );
const matrix = new Matrix4() const matrix = new Matrix4()
.compose( .compose(
no_translate ? NO_TRANSLATION : vec3_to_threejs(position), no_translate ? NO_TRANSLATION : vec3_to_threejs(position),
no_rotate ? NO_ROTATION : new Quaternion().setFromEuler(euler), no_rotate ? NO_ROTATION : new Quaternion().setFromEuler(euler),
no_scale ? NO_SCALE : vec3_to_threejs(scale) no_scale ? NO_SCALE : vec3_to_threejs(scale),
) )
.premultiply(parent_matrix); .premultiply(parent_matrix);
@ -178,7 +178,7 @@ class GeometryCreator {
this.builder.add_vertex( this.builder.add_vertex(
vertex.position, vertex.position,
normal, normal,
mesh.has_tex_coords ? mesh_vertex.tex_coords! : DEFAULT_UV mesh.has_tex_coords ? mesh_vertex.tex_coords! : DEFAULT_UV,
); );
if (i >= 2) { if (i >= 2) {
@ -209,7 +209,7 @@ class GeometryCreator {
this.builder.add_group( this.builder.add_group(
start_index_count, start_index_count,
this.builder.index_count - start_index_count, this.builder.index_count - start_index_count,
mesh.texture_id mesh.texture_id,
); );
} }
} }
@ -286,7 +286,7 @@ class GeometryCreator {
this.builder.add_group( this.builder.add_group(
start_index_count, start_index_count,
this.builder.index_count - start_index_count, this.builder.index_count - start_index_count,
current_mat_idx current_mat_idx,
); );
} }
} }

View File

@ -41,7 +41,7 @@ export function xvm_texture_to_texture(tex: XvmTexture): Texture {
], ],
tex.width, tex.width,
tex.height, tex.height,
format format,
); );
texture_3js.minFilter = LinearFilter; texture_3js.minFilter = LinearFilter;

View File

@ -1,16 +1,16 @@
import { observable } from "mobx"; import { observable } from "mobx";
import { import {
ItemType,
Server,
WeaponItemType,
ArmorItemType, ArmorItemType,
ItemType,
ShieldItemType, ShieldItemType,
ToolItemType, ToolItemType,
UnitItemType, UnitItemType,
} from "../domain"; WeaponItemType,
} from "../domain/items";
import { Loadable } from "../Loadable"; import { Loadable } from "../Loadable";
import { ServerMap } from "./ServerMap"; import { ServerMap } from "./ServerMap";
import { ItemTypeDto } from "../dto"; import { ItemTypeDto } from "../dto";
import { Server } from "../domain";
export class ItemTypeStore { export class ItemTypeStore {
private id_to_item_type: ItemType[] = []; private id_to_item_type: ItemType[] = [];
@ -23,7 +23,7 @@ export class ItemTypeStore {
load = async (server: Server): Promise<ItemTypeStore> => { load = async (server: Server): Promise<ItemTypeStore> => {
const response = await fetch( const response = await fetch(
`${process.env.PUBLIC_URL}/itemTypes.${Server[server].toLowerCase()}.json` `${process.env.PUBLIC_URL}/itemTypes.${Server[server].toLowerCase()}.json`,
); );
const data: ItemTypeDto[] = await response.json(); const data: ItemTypeDto[] = await response.json();
@ -41,7 +41,7 @@ export class ItemTypeStore {
item_type_dto.maxAtp, item_type_dto.maxAtp,
item_type_dto.ata, item_type_dto.ata,
item_type_dto.maxGrind, item_type_dto.maxGrind,
item_type_dto.requiredAtp item_type_dto.requiredAtp,
); );
break; break;
case "armor": case "armor":
@ -56,7 +56,7 @@ export class ItemTypeStore {
item_type_dto.maxDfp, item_type_dto.maxDfp,
item_type_dto.mst, item_type_dto.mst,
item_type_dto.hp, item_type_dto.hp,
item_type_dto.lck item_type_dto.lck,
); );
break; break;
case "shield": case "shield":
@ -71,7 +71,7 @@ export class ItemTypeStore {
item_type_dto.maxDfp, item_type_dto.maxDfp,
item_type_dto.mst, item_type_dto.mst,
item_type_dto.hp, item_type_dto.hp,
item_type_dto.lck item_type_dto.lck,
); );
break; break;
case "unit": case "unit":

View File

@ -1,6 +1,6 @@
import { computed } from "mobx"; import { computed } from "mobx";
import { Server } from "../domain"; import { Server } from "../domain";
import { application_store } from "./ApplicationStore"; import { application_store } from "../../application/stores/ApplicationStore";
import { EnumMap } from "../enums"; import { EnumMap } from "../enums";
/** /**

View File

@ -107,8 +107,8 @@ export class BigTable<T> extends Component<{
height="18" height="18"
viewBox="0 0 24 24" viewBox="0 0 24 24"
> >
<path d="M7 14l5-5 5 5z"></path> <path d="M7 14l5-5 5 5z" />
<path d="M0 0h24v24H0z" fill="none"></path> <path d="M0 0h24v24H0z" fill="none" />
</svg> </svg>
); );
} else { } else {
@ -119,8 +119,8 @@ export class BigTable<T> extends Component<{
height="18" height="18"
viewBox="0 0 24 24" viewBox="0 0 24 24"
> >
<path d="M7 10l5 5 5-5z"></path> <path d="M7 10l5 5 5-5z" />
<path d="M0 0h24v24H0z" fill="none"></path> <path d="M0 0h24v24H0z" fill="none" />
</svg> </svg>
); );
} }

View File

@ -4,7 +4,7 @@ export class Action {
constructor( constructor(
readonly description: string, readonly description: string,
readonly undo: () => void, readonly undo: () => void,
readonly redo: () => void readonly redo: () => void,
) {} ) {}
} }

View File

@ -1,6 +1,6 @@
import { observable, IObservableArray, computed } from "mobx"; import { observable, IObservableArray, computed } from "mobx";
import { WeaponItem, WeaponItemType, ArmorItemType, ShieldItemType } from "../domain"; import { WeaponItem, WeaponItemType, ArmorItemType, ShieldItemType } from "../../core/domain/items";
import { item_type_stores } from "./ItemTypeStore"; import { item_type_stores } from "../../core/stores/ItemTypeStore";
const NORMAL_DAMAGE_FACTOR = 0.2 * 0.9; const NORMAL_DAMAGE_FACTOR = 0.2 * 0.9;
const HEAVY_DAMAGE_FACTOR = NORMAL_DAMAGE_FACTOR * 1.89; const HEAVY_DAMAGE_FACTOR = NORMAL_DAMAGE_FACTOR * 1.89;
@ -81,19 +81,19 @@ class Weapon {
class DpsCalcStore { class DpsCalcStore {
@computed get weapon_types(): WeaponItemType[] { @computed get weapon_types(): WeaponItemType[] {
return item_type_stores.current.value.item_types.filter( return item_type_stores.current.value.item_types.filter(
it => it instanceof WeaponItemType it => it instanceof WeaponItemType,
) as WeaponItemType[]; ) as WeaponItemType[];
} }
@computed get armor_types(): ArmorItemType[] { @computed get armor_types(): ArmorItemType[] {
return item_type_stores.current.value.item_types.filter( return item_type_stores.current.value.item_types.filter(
it => it instanceof ArmorItemType it => it instanceof ArmorItemType,
) as ArmorItemType[]; ) as ArmorItemType[];
} }
@computed get shield_types(): ShieldItemType[] { @computed get shield_types(): ShieldItemType[] {
return item_type_stores.current.value.item_types.filter( return item_type_stores.current.value.item_types.filter(
it => it instanceof ShieldItemType it => it instanceof ShieldItemType,
) as ShieldItemType[]; ) as ShieldItemType[];
} }

View File

@ -1,10 +1,10 @@
import { InputNumber } from "antd"; import { InputNumber } from "antd";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import React, { Component, ReactNode } from "react"; import React, { Component, ReactNode } from "react";
import { ArmorItemType, ShieldItemType, WeaponItemType } from "../../domain"; import { ArmorItemType, ShieldItemType, WeaponItemType } from "../../core/domain/items";
import { dps_calc_store } from "../../stores/DpsCalcStore"; import { dps_calc_store } from "../stores/DpsCalcStore";
import { item_type_stores } from "../../stores/ItemTypeStore"; import { item_type_stores } from "../../core/stores/ItemTypeStore";
import { BigSelect } from "../BigSelect"; import { BigSelect } from "../../core/ui/BigSelect";
@observer @observer
export class DpsCalcComponent extends Component { export class DpsCalcComponent extends Component {

View File

@ -0,0 +1,73 @@
import { Episode } from "../../core/data_formats/parsing/quest/Episode";
import { NpcType } from "../../core/data_formats/parsing/quest/npc_types";
import { computed, observable } from "mobx";
import { ItemType } from "../../core/domain/items";
import { Difficulty, SectionId } from "../../core/domain";
export class HuntMethod {
readonly id: string;
readonly name: string;
readonly episode: Episode;
readonly quest: SimpleQuest;
readonly enemy_counts: Map<NpcType, number>;
/**
* The time it takes to complete the quest in hours.
*/
readonly default_time: number;
/**
* The time it takes to complete the quest in hours as specified by the user.
*/
@observable user_time?: number;
@computed get time(): number {
return this.user_time != null ? this.user_time : this.default_time;
}
constructor(id: string, name: string, quest: SimpleQuest, default_time: number) {
if (!id) throw new Error("id is required.");
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.");
this.id = id;
this.name = name;
this.episode = quest.episode;
this.quest = quest;
this.enemy_counts = quest.enemy_counts;
this.default_time = default_time;
}
}
export class SimpleQuest {
constructor(
readonly id: number,
readonly name: string,
readonly episode: Episode,
readonly enemy_counts: Map<NpcType, number>,
) {
if (!id) throw new Error("id is required.");
if (!name) throw new Error("name is required.");
if (!enemy_counts) throw new Error("enemyCounts is required.");
}
}
type ItemDrop = {
item_type: ItemType;
anything_rate: number;
rare_rate: number;
};
export class EnemyDrop implements ItemDrop {
readonly rate: number;
constructor(
readonly difficulty: Difficulty,
readonly section_id: SectionId,
readonly npc_type: NpcType,
readonly item_type: ItemType,
readonly anything_rate: number,
readonly rare_rate: number,
) {
this.rate = anything_rate * rare_rate;
}
}

View File

@ -1,5 +1,6 @@
import { Persister } from "./Persister"; import { Persister } from "../../core/persistence";
import { Server, HuntMethod } from "../domain"; import { Server } from "../../core/domain";
import { HuntMethod } from "../domain";
const METHOD_USER_TIMES_KEY = "HuntMethodStore.methodUserTimes"; const METHOD_USER_TIMES_KEY = "HuntMethodStore.methodUserTimes";
@ -18,11 +19,11 @@ class HuntMethodPersister extends Persister {
async load_method_user_times( async load_method_user_times(
hunt_methods: HuntMethod[], hunt_methods: HuntMethod[],
server: Server server: Server,
): Promise<HuntMethod[]> { ): Promise<HuntMethod[]> {
const user_times = await this.load_for_server<PersistedUserTimes>( const user_times = await this.load_for_server<PersistedUserTimes>(
server, server,
METHOD_USER_TIMES_KEY METHOD_USER_TIMES_KEY,
); );
if (user_times) { if (user_times) {

View File

@ -1,7 +1,7 @@
import { Server } from "../domain"; import { Server } from "../../core/domain";
import { WantedItem } from "../stores/HuntOptimizerStore"; import { WantedItem } from "../stores/HuntOptimizerStore";
import { item_type_stores } from "../stores/ItemTypeStore"; import { item_type_stores } from "../../core/stores/ItemTypeStore";
import { Persister } from "./Persister"; import { Persister } from "../../core/persistence";
const WANTED_ITEMS_KEY = "HuntOptimizerStore.wantedItems"; const WANTED_ITEMS_KEY = "HuntOptimizerStore.wantedItems";
@ -14,8 +14,8 @@ class HuntOptimizerPersister extends Persister {
({ item_type, amount }): PersistedWantedItem => ({ ({ item_type, amount }): PersistedWantedItem => ({
itemTypeId: item_type.id, itemTypeId: item_type.id,
amount, amount,
}) }),
) ),
); );
} }
@ -24,7 +24,7 @@ class HuntOptimizerPersister extends Persister {
const persisted_wanted_items = await this.load_for_server<PersistedWantedItem[]>( const persisted_wanted_items = await this.load_for_server<PersistedWantedItem[]>(
server, server,
WANTED_ITEMS_KEY WANTED_ITEMS_KEY,
); );
const wanted_items: WantedItem[] = []; const wanted_items: WantedItem[] = [];

View File

@ -1,11 +1,12 @@
import Logger from "js-logger"; import Logger from "js-logger";
import { autorun, IReactionDisposer, observable } from "mobx"; import { autorun, IReactionDisposer, observable } from "mobx";
import { HuntMethod, Server, SimpleQuest } from "../domain"; import { Server } from "../../core/domain";
import { QuestDto } from "../dto"; import { QuestDto } from "../../core/dto";
import { Loadable } from "../Loadable"; import { Loadable } from "../../core/Loadable";
import { hunt_method_persister } from "../persistence/HuntMethodPersister"; import { hunt_method_persister } from "../persistence/HuntMethodPersister";
import { ServerMap } from "./ServerMap"; import { ServerMap } from "../../core/stores/ServerMap";
import { NpcType } from "../data_formats/parsing/quest/npc_types"; import { NpcType } from "../../core/data_formats/parsing/quest/npc_types";
import { HuntMethod, SimpleQuest } from "../domain";
const logger = Logger.get("stores/HuntMethodStore"); const logger = Logger.get("stores/HuntMethodStore");

View File

@ -3,20 +3,20 @@ import { autorun, computed, IObservableArray, observable } from "mobx";
import { import {
Difficulties, Difficulties,
Difficulty, Difficulty,
HuntMethod,
ItemType,
KONDRIEU_PROB, KONDRIEU_PROB,
RARE_ENEMY_PROB, RARE_ENEMY_PROB,
SectionId, SectionId,
SectionIds, SectionIds,
} from "../domain"; } from "../../core/domain";
import { hunt_optimizer_persister } from "../persistence/HuntOptimizerPersister"; import { hunt_optimizer_persister } from "../persistence/HuntOptimizerPersister";
import { application_store } from "./ApplicationStore"; import { application_store } from "../../application/stores/ApplicationStore";
import { hunt_method_store } from "./HuntMethodStore"; import { hunt_method_store } from "./HuntMethodStore";
import { item_drop_stores } from "./ItemDropStore"; import { item_drop_stores } from "./ItemDropStore";
import { item_type_stores } from "./ItemTypeStore"; import { item_type_stores } from "../../core/stores/ItemTypeStore";
import { Episode } from "../data_formats/parsing/quest/Episode"; import { Episode } from "../../core/data_formats/parsing/quest/Episode";
import { npc_data, NpcType } from "../data_formats/parsing/quest/npc_types"; import { npc_data, NpcType } from "../../core/data_formats/parsing/quest/npc_types";
import { HuntMethod } from "../domain";
import { ItemType } from "../../core/domain/items";
export class WantedItem { export class WantedItem {
@observable readonly item_type: ItemType; @observable readonly item_type: ItemType;

View File

@ -1,11 +1,12 @@
import { observable } from "mobx"; import { observable } from "mobx";
import { Difficulties, Difficulty, EnemyDrop, SectionId, SectionIds, Server } from "../domain"; import { Difficulties, Difficulty, SectionId, SectionIds, Server } from "../../core/domain";
import { EnemyDropDto } from "../dto"; import { EnemyDropDto } from "../../core/dto";
import { Loadable } from "../Loadable"; import { Loadable } from "../../core/Loadable";
import { item_type_stores } from "./ItemTypeStore"; import { item_type_stores } from "../../core/stores/ItemTypeStore";
import { ServerMap } from "./ServerMap"; import { ServerMap } from "../../core/stores/ServerMap";
import Logger from "js-logger"; import Logger from "js-logger";
import { NpcType } from "../data_formats/parsing/quest/npc_types"; import { NpcType } from "../../core/data_formats/parsing/quest/npc_types";
import { EnemyDrop } from "../domain";
const logger = Logger.get("stores/ItemDropStore"); const logger = Logger.get("stores/ItemDropStore");

View File

@ -3,12 +3,16 @@ import { observer } from "mobx-react";
import moment, { Moment } from "moment"; import moment, { Moment } from "moment";
import React, { Component, ReactNode } from "react"; import React, { Component, ReactNode } from "react";
import { AutoSizer, Index, SortDirection } from "react-virtualized"; import { AutoSizer, Index, SortDirection } from "react-virtualized";
import { hunt_method_store } from "../../stores/HuntMethodStore"; import { hunt_method_store } from "../stores/HuntMethodStore";
import { BigTable, Column, ColumnSort } from "../BigTable"; import { BigTable, Column, ColumnSort } from "../../core/ui/BigTable";
import styles from "./MethodsComponent.css"; import styles from "./MethodsComponent.css";
import { HuntMethod } from "../../domain"; import { Episode } from "../../core/data_formats/parsing/quest/Episode";
import { Episode } from "../../data_formats/parsing/quest/Episode"; import {
import { ENEMY_NPC_TYPES, npc_data, NpcType } from "../../data_formats/parsing/quest/npc_types"; ENEMY_NPC_TYPES,
npc_data,
NpcType,
} from "../../core/data_formats/parsing/quest/npc_types";
import { HuntMethod } from "../domain";
@observer @observer
export class MethodsComponent extends Component { export class MethodsComponent extends Component {

View File

@ -2,13 +2,13 @@ import { computed } from "mobx";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import React, { Component, ReactNode } from "react"; import React, { Component, ReactNode } from "react";
import { AutoSizer, Index } from "react-virtualized"; import { AutoSizer, Index } from "react-virtualized";
import { Difficulty, SectionId } from "../../domain"; import { Difficulty, SectionId } from "../../core/domain";
import { hunt_optimizer_store, OptimalMethod } from "../../stores/HuntOptimizerStore"; import { hunt_optimizer_store, OptimalMethod } from "../stores/HuntOptimizerStore";
import { BigTable, Column } from "../BigTable"; import { BigTable, Column } from "../../core/ui/BigTable";
import { SectionIdIcon } from "../SectionIdIcon"; import { SectionIdIcon } from "../../core/ui/SectionIdIcon";
import { hours_to_string } from "../time"; import { hours_to_string } from "../../core/ui/time";
import styles from "./OptimizationResultComponent.css"; import styles from "./OptimizationResultComponent.css";
import { Episode } from "../../data_formats/parsing/quest/Episode"; import { Episode } from "../../core/data_formats/parsing/quest/Episode";
@observer @observer
export class OptimizationResultComponent extends Component { export class OptimizationResultComponent extends Component {

Some files were not shown because too many files have changed in this diff Show More