diff --git a/FEATURES.md b/FEATURES.md index b1dda2e0..bd8d2271 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -73,9 +73,16 @@ Features that are in ***bold italics*** are planned and not yet implemented. ## Events -- ***Create events*** -- ***Delete events*** -- ***Edit events*** +- ***Event chain list*** +- ***Add chain of events*** +- ***Add event to chain*** +- ***Delete event*** + - ***Delete coupled NPCs if requested*** +- ***Edit event section*** + +### Event Actions + +- ***Add/Delete*** ## Script Object Code @@ -122,10 +129,6 @@ Features that are in ***bold italics*** are planned and not yet implemented. - ***When saving, ask user whether to really save when asm contains errors*** - ***Theme selection*** -## Enemy Waves - -- ***Figure out how they work*** - ## Non-BlueBurst Support - ***Support different sets of instructions (older versions had no stack)*** diff --git a/src/core/data_formats/parsing/quest/dat.ts b/src/core/data_formats/parsing/quest/dat.ts index 7ec77260..c5ac7627 100644 --- a/src/core/data_formats/parsing/quest/dat.ts +++ b/src/core/data_formats/parsing/quest/dat.ts @@ -15,7 +15,7 @@ const NPC_SIZE = 72; export type DatFile = { readonly objs: readonly DatObject[]; readonly npcs: readonly DatNpc[]; - readonly waves: readonly DatWave[]; + readonly waves: readonly DatEvent[]; readonly unknowns: readonly DatUnknown[]; }; @@ -41,47 +41,47 @@ export type DatNpc = DatEntity & { readonly roaming: number; }; -export type DatWave = { +export type DatEvent = { readonly id: number; readonly section_id: number; readonly wave: number; readonly delay: number; - readonly actions: readonly DatWaveAction[]; + readonly actions: readonly DatEventAction[]; readonly area_id: number; readonly unknown: number; }; -export enum DatWaveActionType { +export enum DatEventActionType { SpawnNpcs = 0x8, Unlock = 0xa, Lock = 0xb, SpawnWave = 0xc, } -export type DatWaveAction = - | DatWaveActionSpawnNpcs - | DatWaveActionUnlock - | DatWaveActionLock - | DatWaveActionSpawnWave; +export type DatEventAction = + | DatEventActionSpawnNpcs + | DatEventActionUnlock + | DatEventActionLock + | DatEventActionSpawnWave; -export type DatWaveActionSpawnNpcs = { - readonly type: DatWaveActionType.SpawnNpcs; +export type DatEventActionSpawnNpcs = { + readonly type: DatEventActionType.SpawnNpcs; readonly section_id: number; readonly appear_flag: number; }; -export type DatWaveActionUnlock = { - readonly type: DatWaveActionType.Unlock; +export type DatEventActionUnlock = { + readonly type: DatEventActionType.Unlock; readonly door_id: number; }; -export type DatWaveActionLock = { - readonly type: DatWaveActionType.Lock; +export type DatEventActionLock = { + readonly type: DatEventActionType.Lock; readonly door_id: number; }; -export type DatWaveActionSpawnWave = { - readonly type: DatWaveActionType.SpawnWave; +export type DatEventActionSpawnWave = { + readonly type: DatEventActionType.SpawnWave; readonly wave_id: number; }; @@ -96,7 +96,7 @@ export type DatUnknown = { export function parse_dat(cursor: Cursor): DatFile { const objs: DatObject[] = []; const npcs: DatNpc[] = []; - const waves: DatWave[] = []; + const waves: DatEvent[] = []; const unknowns: DatUnknown[] = []; while (cursor.bytes_left) { @@ -249,7 +249,7 @@ function parse_npcs(cursor: Cursor, area_id: number, npcs: DatNpc[]): void { } } -function parse_waves(cursor: Cursor, area_id: number, waves: DatWave[]): void { +function parse_waves(cursor: Cursor, area_id: number, waves: DatEvent[]): void { const actions_offset = cursor.u32(); cursor.seek(4); // Always 0x10 const wave_count = cursor.u32(); @@ -312,8 +312,8 @@ function parse_waves(cursor: Cursor, area_id: number, waves: DatWave[]): void { cursor.seek_start(actions_offset + actions_cursor.position); } -function parse_wave_actions(cursor: Cursor): DatWaveAction[] { - const actions: DatWaveAction[] = []; +function parse_wave_actions(cursor: Cursor): DatEventAction[] { + const actions: DatEventAction[] = []; outer: while (cursor.bytes_left) { const type = cursor.u8(); @@ -322,31 +322,31 @@ function parse_wave_actions(cursor: Cursor): DatWaveAction[] { case 1: break outer; - case DatWaveActionType.SpawnNpcs: + case DatEventActionType.SpawnNpcs: actions.push({ - type: DatWaveActionType.SpawnNpcs, + type: DatEventActionType.SpawnNpcs, section_id: cursor.u16(), appear_flag: cursor.u16(), }); break; - case DatWaveActionType.Unlock: + case DatEventActionType.Unlock: actions.push({ - type: DatWaveActionType.Unlock, + type: DatEventActionType.Unlock, door_id: cursor.u16(), }); break; - case DatWaveActionType.Lock: + case DatEventActionType.Lock: actions.push({ - type: DatWaveActionType.Lock, + type: DatEventActionType.Lock, door_id: cursor.u16(), }); break; - case DatWaveActionType.SpawnWave: + case DatEventActionType.SpawnWave: actions.push({ - type: DatWaveActionType.SpawnWave, + type: DatEventActionType.SpawnWave, wave_id: cursor.u32(), }); break; @@ -458,7 +458,7 @@ function write_npcs(cursor: WritableCursor, npcs: readonly DatNpc[]): void { } } -function write_waves(cursor: WritableCursor, waves: readonly DatWave[]): void { +function write_waves(cursor: WritableCursor, waves: readonly DatEvent[]): void { const grouped_waves = groupBy(waves, wave => wave.area_id); const wave_area_ids = Object.keys(grouped_waves) .map(key => parseInt(key, 10)) @@ -506,20 +506,20 @@ function write_waves(cursor: WritableCursor, waves: readonly DatWave[]): void { cursor.write_u8(action.type); switch (action.type) { - case DatWaveActionType.SpawnNpcs: + case DatEventActionType.SpawnNpcs: cursor.write_u16(action.section_id); cursor.write_u16(action.appear_flag); break; - case DatWaveActionType.Unlock: + case DatEventActionType.Unlock: cursor.write_u16(action.door_id); break; - case DatWaveActionType.Lock: + case DatEventActionType.Lock: cursor.write_u16(action.door_id); break; - case DatWaveActionType.SpawnWave: + case DatEventActionType.SpawnWave: cursor.write_u32(action.wave_id); break; diff --git a/src/core/data_formats/parsing/quest/entities.ts b/src/core/data_formats/parsing/quest/entities.ts index ce939466..3509631f 100644 --- a/src/core/data_formats/parsing/quest/entities.ts +++ b/src/core/data_formats/parsing/quest/entities.ts @@ -1,7 +1,7 @@ import { Vec3 } from "../../vector"; import { npc_data, NpcType, NpcTypeData } from "./npc_types"; import { object_data, ObjectType, ObjectTypeData } from "./object_types"; -import { DatWave } from "./dat"; +import { DatEvent } from "./dat"; export type QuestNpc = { readonly type: NpcType; @@ -47,7 +47,7 @@ export type QuestObject = { readonly unknown: readonly number[][]; }; -export type QuestWave = DatWave; +export type QuestEvent = DatEvent; export type EntityTypeData = NpcTypeData | ObjectTypeData; diff --git a/src/core/data_formats/parsing/quest/index.ts b/src/core/data_formats/parsing/quest/index.ts index bfbe0ff4..0cfda0d3 100644 --- a/src/core/data_formats/parsing/quest/index.ts +++ b/src/core/data_formats/parsing/quest/index.ts @@ -14,7 +14,7 @@ import { ResizableBufferCursor } from "../../cursor/ResizableBufferCursor"; import { Endianness } from "../../Endianness"; import { parse_bin, write_bin } from "./bin"; import { DatFile, DatNpc, DatObject, DatUnknown, parse_dat, write_dat } from "./dat"; -import { QuestNpc, QuestObject, QuestWave } from "./entities"; +import { QuestNpc, QuestObject, QuestEvent } from "./entities"; import { Episode } from "./Episode"; import { object_data, ObjectType, pso_id_to_object_type } from "./object_types"; import { parse_qst, QstContainedFile, write_qst } from "./qst"; @@ -32,7 +32,7 @@ export type Quest = { readonly episode: Episode; readonly objects: readonly QuestObject[]; readonly npcs: readonly QuestNpc[]; - readonly waves: readonly QuestWave[]; + readonly waves: readonly QuestEvent[]; /** * (Partial) raw DAT data that can't be parsed yet by Phantasmal. */ diff --git a/src/quest_editor/model/QuestWaveActionModel.ts b/src/quest_editor/model/QuestEventActionModel.ts similarity index 64% rename from src/quest_editor/model/QuestWaveActionModel.ts rename to src/quest_editor/model/QuestEventActionModel.ts index d3128dd0..f423d2d2 100644 --- a/src/quest_editor/model/QuestWaveActionModel.ts +++ b/src/quest_editor/model/QuestEventActionModel.ts @@ -1,6 +1,6 @@ -export abstract class QuestWaveActionModel {} +export abstract class QuestEventActionModel {} -export class QuestWaveActionSpawnNpcsModel extends QuestWaveActionModel { +export class QuestEventActionSpawnNpcsModel extends QuestEventActionModel { readonly section_id: number; readonly appear_flag: number; @@ -12,7 +12,7 @@ export class QuestWaveActionSpawnNpcsModel extends QuestWaveActionModel { } } -export class QuestWaveActionUnlockModel extends QuestWaveActionModel { +export class QuestEventActionUnlockModel extends QuestEventActionModel { readonly door_id: number; constructor(door_id: number) { @@ -22,7 +22,7 @@ export class QuestWaveActionUnlockModel extends QuestWaveActionModel { } } -export class QuestWaveActionLockModel extends QuestWaveActionModel { +export class QuestEventActionLockModel extends QuestEventActionModel { readonly door_id: number; constructor(door_id: number) { @@ -32,7 +32,7 @@ export class QuestWaveActionLockModel extends QuestWaveActionModel { } } -export class QuestWaveActionSpawnWaveModel extends QuestWaveActionModel { +export class QuestEventActionSpawnWaveModel extends QuestEventActionModel { readonly wave_id: number; constructor(wave_id: number) { diff --git a/src/quest_editor/model/QuestWaveModel.ts b/src/quest_editor/model/QuestEventModel.ts similarity index 85% rename from src/quest_editor/model/QuestWaveModel.ts rename to src/quest_editor/model/QuestEventModel.ts index d128c445..76b31151 100644 --- a/src/quest_editor/model/QuestWaveModel.ts +++ b/src/quest_editor/model/QuestEventModel.ts @@ -1,11 +1,11 @@ -import { QuestWaveActionModel } from "./QuestWaveActionModel"; +import { QuestEventActionModel } from "./QuestEventActionModel"; -export class QuestWaveModel { +export class QuestEventModel { readonly id: number; readonly section_id: number; readonly wave: number; readonly delay: number; - readonly actions: readonly QuestWaveActionModel[]; + readonly actions: readonly QuestEventActionModel[]; readonly area_id: number; readonly unknown: number; @@ -14,7 +14,7 @@ export class QuestWaveModel { section_id: number, wave: number, delay: number, - actions: readonly QuestWaveActionModel[], + actions: readonly QuestEventActionModel[], area_id: number, unknown: number, ) { diff --git a/src/quest_editor/model/QuestModel.ts b/src/quest_editor/model/QuestModel.ts index 812c24a1..6bb51b47 100644 --- a/src/quest_editor/model/QuestModel.ts +++ b/src/quest_editor/model/QuestModel.ts @@ -13,7 +13,7 @@ import { ListProperty } from "../../core/observable/property/list/ListProperty"; import { WritableListProperty } from "../../core/observable/property/list/WritableListProperty"; import { QuestEntityModel } from "./QuestEntityModel"; import { entity_type_to_string } from "../../core/data_formats/parsing/quest/entities"; -import { QuestWaveModel } from "./QuestWaveModel"; +import { QuestEventModel } from "./QuestEventModel"; const logger = Logger.get("quest_editor/model/QuestModel"); @@ -27,7 +27,7 @@ export class QuestModel { private readonly _area_variants: WritableListProperty = list_property(); private readonly _objects: WritableListProperty; private readonly _npcs: WritableListProperty; - private readonly _waves: WritableListProperty; + private readonly _waves: WritableListProperty; readonly id: Property = this._id; @@ -60,7 +60,7 @@ export class QuestModel { readonly npcs: ListProperty; - readonly waves: ListProperty; + readonly waves: ListProperty; /** * (Partial) raw DAT data that can't be parsed yet by Phantasmal. @@ -81,7 +81,7 @@ export class QuestModel { map_designations: Map, objects: readonly QuestObjectModel[], npcs: readonly QuestNpcModel[], - waves: readonly QuestWaveModel[], + waves: readonly QuestEventModel[], dat_unknowns: readonly DatUnknown[], object_code: readonly Segment[], shop_items: readonly number[], diff --git a/src/quest_editor/stores/QuestEditorStore.ts b/src/quest_editor/stores/QuestEditorStore.ts index 3a325e1a..58b3d983 100644 --- a/src/quest_editor/stores/QuestEditorStore.ts +++ b/src/quest_editor/stores/QuestEditorStore.ts @@ -29,14 +29,14 @@ import { Euler, Vector3 } from "three"; import { vec3_to_threejs } from "../../core/rendering/conversion"; import { RotateEntityAction } from "../actions/RotateEntityAction"; import { ExecutionResult, VirtualMachine } from "../scripting/vm"; -import { QuestWaveModel } from "../model/QuestWaveModel"; -import { DatWaveActionType } from "../../core/data_formats/parsing/quest/dat"; +import { QuestEventModel } from "../model/QuestEventModel"; +import { DatEventActionType } from "../../core/data_formats/parsing/quest/dat"; import { - QuestWaveActionLockModel, - QuestWaveActionSpawnNpcsModel, - QuestWaveActionSpawnWaveModel, - QuestWaveActionUnlockModel, -} from "../model/QuestWaveActionModel"; + QuestEventActionLockModel, + QuestEventActionSpawnNpcsModel, + QuestEventActionSpawnWaveModel, + QuestEventActionUnlockModel, +} from "../model/QuestEventActionModel"; import Logger = require("js-logger"); const logger = Logger.get("quest_editor/gui/QuestEditorStore"); @@ -170,26 +170,26 @@ export class QuestEditorStore implements Disposable { ), quest.waves.map( wave => - new QuestWaveModel( + new QuestEventModel( wave.id, wave.section_id, wave.wave, wave.delay, wave.actions.map(action => { switch (action.type) { - case DatWaveActionType.SpawnNpcs: - return new QuestWaveActionSpawnNpcsModel( + case DatEventActionType.SpawnNpcs: + return new QuestEventActionSpawnNpcsModel( action.section_id, action.appear_flag, ); - case DatWaveActionType.Unlock: - return new QuestWaveActionUnlockModel( + case DatEventActionType.Unlock: + return new QuestEventActionUnlockModel( action.door_id, ); - case DatWaveActionType.Lock: - return new QuestWaveActionLockModel(action.door_id); - case DatWaveActionType.SpawnWave: - return new QuestWaveActionSpawnWaveModel( + case DatEventActionType.Lock: + return new QuestEventActionLockModel(action.door_id); + case DatEventActionType.SpawnWave: + return new QuestEventActionSpawnWaveModel( action.wave_id, ); } @@ -261,25 +261,25 @@ export class QuestEditorStore implements Disposable { wave: wave.wave, delay: wave.delay, actions: wave.actions.map(action => { - if (action instanceof QuestWaveActionSpawnNpcsModel) { + if (action instanceof QuestEventActionSpawnNpcsModel) { return { - type: DatWaveActionType.SpawnNpcs, + type: DatEventActionType.SpawnNpcs, section_id: action.section_id, appear_flag: action.appear_flag, }; - } else if (action instanceof QuestWaveActionUnlockModel) { + } else if (action instanceof QuestEventActionUnlockModel) { return { - type: DatWaveActionType.Unlock, + type: DatEventActionType.Unlock, door_id: action.door_id, }; - } else if (action instanceof QuestWaveActionLockModel) { + } else if (action instanceof QuestEventActionLockModel) { return { - type: DatWaveActionType.Lock, + type: DatEventActionType.Lock, door_id: action.door_id, }; - } else if (action instanceof QuestWaveActionSpawnWaveModel) { + } else if (action instanceof QuestEventActionSpawnWaveModel) { return { - type: DatWaveActionType.SpawnWave, + type: DatEventActionType.SpawnWave, wave_id: action.wave_id, }; } else { diff --git a/src/quest_editor/stores/quest_creation.ts b/src/quest_editor/stores/quest_creation.ts index 21629fce..38d75f39 100644 --- a/src/quest_editor/stores/quest_creation.ts +++ b/src/quest_editor/stores/quest_creation.ts @@ -17,7 +17,7 @@ import { import { QuestObjectModel } from "../model/QuestObjectModel"; import { QuestNpcModel } from "../model/QuestNpcModel"; import { Euler, Vector3 } from "three"; -import { QuestWaveModel } from "../model/QuestWaveModel"; +import { QuestEventModel } from "../model/QuestEventModel"; export function create_new_quest(episode: Episode): QuestModel { if (episode === Episode.II) throw new Error("Episode II not yet supported."); @@ -807,6 +807,6 @@ function create_default_npcs(): QuestNpcModel[] { ]; } -function create_default_waves(): QuestWaveModel[] { +function create_default_waves(): QuestEventModel[] { return []; }