Ported new quest button to new GUI system.

This commit is contained in:
Daan Vanden Bosch 2019-08-30 00:06:32 +02:00
parent 31c51ca83d
commit 24f0cdb461
15 changed files with 254 additions and 574 deletions

View File

@ -55,7 +55,8 @@
.core_Button_left,
.core_Button_right {
display: inline-flex;
align-content: stretch;
align-content: center;
font-size: 11px;
}
.core_Button_left {

View File

@ -1,3 +1,3 @@
import { Widget } from "./Widget";
export abstract class Control<E extends HTMLElement> extends Widget<E> {}
export abstract class Control<E extends HTMLElement = HTMLElement> extends Widget<E> {}

View File

@ -0,0 +1,9 @@
.core_DropDownButton {
position: relative;
}
.core_DropDownButton .core_Menu {
top: 25px;
left: 0;
min-width: 100%;
}

View File

@ -0,0 +1,75 @@
import { disposable_listener, el, Icon } from "./dom";
import "./DropDownButton.css";
import { Property } from "../observable/property/Property";
import { Button, ButtonOptions } from "./Button";
import { Menu } from "./Menu";
import { Control } from "./Control";
import { Observable } from "../observable/Observable";
import { Emitter } from "../observable/Emitter";
import { emitter } from "../observable";
export type DropDownButtonOptions = ButtonOptions;
export class DropDownButton<T> extends Control {
readonly chosen: Observable<T>;
private readonly button: Button;
private readonly menu: Menu<T>;
private readonly _chosen: Emitter<T>;
private just_opened: boolean;
constructor(
text: string,
items: T[] | Property<T[]>,
to_label: (element: T) => string,
options?: DropDownButtonOptions,
) {
const button = new Button(text, {
icon_left: options && options.icon_left,
icon_right: Icon.TriangleDown,
});
const menu = new Menu<T>(items, to_label);
super(el.div({ class: "core_DropDownButton" }, button.element, menu.element), options);
this.button = this.disposable(button);
this.menu = this.disposable(menu);
this._chosen = emitter();
this.chosen = this._chosen;
this.just_opened = false;
this.disposables(
disposable_listener(button.element, "mousedown", e => this.button_mousedown(e)),
button.mouseup.observe(() => this.button_mouseup()),
this.menu.selected.observe(({ value }) => {
if (value) {
this._chosen.emit({ value });
this.menu.selected.val = undefined;
}
}),
);
}
protected set_enabled(enabled: boolean): void {
super.set_enabled(enabled);
this.button.enabled.val = enabled;
}
private button_mousedown(e: Event): void {
e.stopPropagation();
this.just_opened = !this.menu.visible.val;
this.menu.visible.val = true;
}
private button_mouseup(): void {
if (!this.just_opened) {
this.menu.visible.val = false;
}
this.just_opened = false;
}
}

20
src/core/gui/Menu.css Normal file
View File

@ -0,0 +1,20 @@
.core_Menu {
z-index: 1000;
position: absolute;
box-sizing: border-box;
border: var(--control-border);
}
.core_Menu .core_Menu_inner {
background-color: var(--control-bg-color);
border: var(--control-inner-border);
}
.core_Menu .core_Menu_inner > * {
padding: 5px 8px;
white-space: nowrap;
}
.core_Menu .core_Menu_inner > *:hover {
background-color: var(--control-bg-color-hover);
}

70
src/core/gui/Menu.ts Normal file
View File

@ -0,0 +1,70 @@
import { disposable_listener, el } from "./dom";
import { Widget } from "./Widget";
import { Property } from "../observable/property/Property";
import { property } from "../observable";
import { WritableProperty } from "../observable/property/WritableProperty";
import { WidgetProperty } from "../observable/property/WidgetProperty";
import "./Menu.css";
export class Menu<T> extends Widget {
readonly selected: WritableProperty<T | undefined>;
private readonly to_label: (element: T) => string;
private readonly items: Property<T[]>;
private readonly _selected: WidgetProperty<T | undefined>;
constructor(items: T[] | Property<T[]>, to_label: (element: T) => string) {
super(el.div({ class: "core_Menu" }));
this.element.hidden = true;
this.element.onmouseup = (e: Event) => this.mouseup(e);
const inner_element = el.div({ class: "core_Menu_inner" });
this.element.append(inner_element);
this.to_label = to_label;
this.items = Array.isArray(items) ? property(items) : items;
this._selected = new WidgetProperty<T | undefined>(this, undefined, this.set_selected);
this.selected = this._selected;
this.disposables(
this.items.observe(
({ value: items }) => {
inner_element.innerHTML = "";
inner_element.append(
...items.map((item, index) =>
el.div({ text: to_label(item), data: { index: index.toString() } }),
),
);
},
{ call_now: true },
),
disposable_listener(document, "mousedown", (e: Event) => this.document_mousedown(e)),
);
}
protected set_selected(): void {
// Noop
}
private mouseup(e: Event): void {
if (!(e.target instanceof HTMLElement)) return;
const index_str = e.target.dataset.index;
if (index_str == undefined) return;
const element = this.items.val[parseInt(index_str, 10)];
if (!element) return;
this.selected.set_val(element, { silent: false });
this.visible.val = false;
}
private document_mousedown(e: Event): void {
if (this.visible.val && !this.element.contains(e.target as Node)) {
this.visible.val = false;
}
}
}

View File

@ -8,26 +8,8 @@
flex: 1;
}
.core_Select .core_Select_elements {
z-index: 1000;
position: absolute;
box-sizing: border-box;
.core_Select .core_Menu {
top: 25px;
left: 0;
min-width: 100%;
border: var(--control-border);
}
.core_Select .core_Select_elements_inner {
background-color: var(--control-bg-color);
border: var(--control-inner-border);
}
.core_Select .core_Select_elements_inner > * {
padding: 5px 8px;
white-space: nowrap;
}
.core_Select .core_Select_elements_inner > *:hover {
background-color: var(--control-bg-color-hover);
}

View File

@ -1,11 +1,11 @@
import { LabelledControl, LabelledControlOptions, LabelPosition } from "./LabelledControl";
import { disposable_listener, el, Icon } from "./dom";
import "./Select.css";
import "./Button.css";
import { is_any_property, Property } from "../observable/property/Property";
import { Button } from "./Button";
import { WritableProperty } from "../observable/property/WritableProperty";
import { WidgetProperty } from "../observable/property/WidgetProperty";
import { Menu } from "./Menu";
export type SelectOptions<T> = LabelledControlOptions & {
selected: T | Property<T>;
@ -18,38 +18,27 @@ export class Select<T> extends LabelledControl {
private readonly to_label: (element: T) => string;
private readonly button: Button;
private readonly element_container: HTMLElement;
private readonly elements: Property<T[]>;
private readonly menu: Menu<T>;
private readonly _selected: WidgetProperty<T | undefined>;
private just_opened: boolean;
constructor(
elements: Property<T[]>,
items: T[] | Property<T[]>,
to_label: (element: T) => string,
options?: SelectOptions<T>,
) {
const button = new Button("", {
icon_right: Icon.TriangleDown,
});
const menu = new Menu<T>(items, to_label);
const element_container = el.div({ class: "core_Select_elements" });
super(el.div({ class: "core_Select" }, button.element, element_container), options);
this.element_container = element_container;
this.element_container.hidden = true;
this.element_container.onmouseup = (e: Event) => this.element_container_mouseup(e);
const element_container_inner = el.div({ class: "core_Select_elements_inner" });
element_container.append(element_container_inner);
super(el.div({ class: "core_Select" }, button.element, menu.element), options);
this.preferred_label_position = "left";
this.to_label = to_label;
this.button = this.disposable(button);
this.elements = elements;
this.menu = this.disposable(menu);
this._selected = new WidgetProperty<T | undefined>(this, undefined, this.set_selected);
this.selected = this._selected;
@ -57,23 +46,13 @@ export class Select<T> extends LabelledControl {
this.just_opened = false;
this.disposables(
elements.observe(
({ value: opts }) => {
element_container_inner.innerHTML = "";
element_container_inner.append(
...opts.map((opt, index) =>
el.div({ text: to_label(opt), data: { index: index.toString() } }),
),
);
},
{ call_now: true },
),
button.mousedown.observe(() => this.button_mousedown()),
disposable_listener(button.element, "mousedown", e => this.button_mousedown(e)),
button.mouseup.observe(() => this.button_mouseup()),
disposable_listener(document, "mousedown", (e: Event) => this.document_mousedown(e)),
this.menu.selected.observe(({ value }) =>
this._selected.set_val(value, { silent: false }),
),
);
if (options) {
@ -92,45 +71,20 @@ export class Select<T> extends LabelledControl {
protected set_selected(selected?: T): void {
this.button.text.val = selected ? this.to_label(selected) : "";
this.menu.selected.val = selected;
}
private button_mousedown(): void {
this.just_opened = this.element_container.hidden;
this.show_menu();
private button_mousedown(e: Event): void {
e.stopPropagation();
this.just_opened = !this.menu.visible.val;
this.menu.visible.val = true;
}
private button_mouseup(): void {
if (!this.just_opened) {
this.hide_menu();
this.menu.visible.val = false;
}
this.just_opened = false;
}
private element_container_mouseup(e: Event): void {
if (!(e.target instanceof HTMLElement)) return;
const index_str = e.target.dataset.index;
if (index_str == undefined) return;
const element = this.elements.val[parseInt(index_str, 10)];
if (!element) return;
this.selected.set_val(element, { silent: false });
this.hide_menu();
}
private document_mousedown(e: Event): void {
if (!this.element.contains(e.target as Node)) {
this.hide_menu();
}
}
private show_menu(): void {
this.element_container.hidden = false;
}
private hide_menu(): void {
this.element_container.hidden = true;
}
}

View File

@ -89,6 +89,7 @@ export function bind_hidden(element: HTMLElement, observable: Observable<boolean
export enum Icon {
File,
NewFile,
Save,
TriangleDown,
Undo,
@ -102,6 +103,9 @@ export function icon(icon: Icon): HTMLElement {
case Icon.File:
icon_str = "fa-file";
break;
case Icon.NewFile:
icon_str = "fa-file-medical";
break;
case Icon.Save:
icon_str = "fa-save";
break;
@ -123,12 +127,13 @@ export function disposable_listener(
element: DocumentAndElementEventHandlers,
event: string,
listener: EventListenerOrEventListenerObject,
options?: AddEventListenerOptions,
): Disposable {
element.addEventListener(event, listener);
return {
dispose(): void {
element.removeEventListener(event, listener);
element.removeEventListener(event, listener, options);
},
};
}

View File

@ -1,37 +1,8 @@
import Logger from "js-logger";
import { action, flow, observable } from "mobx";
import { Endianness } from "../../../core/data_formats/Endianness";
import { ArrayBufferCursor } from "../../../core/data_formats/cursor/ArrayBufferCursor";
import { parse_quest, write_quest_qst } from "../../../core/data_formats/parsing/quest";
import { Vec3 } from "../../../core/data_formats/vector";
import { read_file } from "../../../core/read_file";
import { SimpleUndo, UndoStack } from "../../core/undo";
import { area_store } from "./AreaStore";
import { create_new_quest } from "../../../quest_editor/stores/quest_creation";
import { Episode } from "../../../core/data_formats/parsing/quest/Episode";
import { entity_data } from "../../../core/data_formats/parsing/quest/entities";
import { ObservableQuest } from "../domain/QuestModel";
import { AreaModel } from "../../../quest_editor/model/AreaModel";
import { SectionModel } from "../../../quest_editor/model/SectionModel";
import {
QuestEntityModel,
ObservableQuestNpc,
ObservableQuestObject,
} from "../domain/observable_quest_entities";
const logger = Logger.get("stores/QuestEditorStore");
import { action, observable } from "mobx";
import { write_quest_qst } from "../../../core/data_formats/parsing/quest";
class QuestEditorStore {
@observable debug = false;
readonly undo = new UndoStack();
readonly script_undo = new SimpleUndo("Text edits", () => {}, () => {});
@observable current_quest_filename?: string;
@observable current_quest?: ObservableQuest;
@observable current_area?: AreaModel;
@observable selected_entity?: QuestEntityModel;
@observable save_dialog_filename?: string;
@observable save_dialog_open: boolean = false;
@ -52,92 +23,6 @@ class QuestEditorStore {
// application_store.on_global_keyup("quest_editor", "Ctrl-Alt-D", this.toggle_debug);
}
@action
toggle_debug = () => {
this.debug = !this.debug;
};
@action
set_selected_entity = (entity?: QuestEntityModel) => {
if (entity) {
this.set_current_area_id(entity.area_id);
}
this.selected_entity = entity;
};
@action
set_current_area_id = (area_id?: number) => {
this.selected_entity = undefined;
if (area_id == undefined) {
this.current_area = undefined;
} else if (this.current_quest) {
this.current_area = area_store.get_area(this.current_quest.episode, area_id);
}
};
@action
new_quest = (episode: Episode) => {
this.set_quest(create_new_quest(episode));
};
// TODO: notify user of problems.
open_file = flow(function* open_file(this: QuestEditorStore, filename: string, file: File) {
try {
const buffer = yield read_file(file);
const quest = parse_quest(new ArrayBufferCursor(buffer, Endianness.Little));
this.set_quest(
quest &&
new ObservableQuest(
quest.id,
quest.language,
quest.name,
quest.short_description,
quest.long_description,
quest.episode,
quest.map_designations,
quest.objects.map(
obj =>
new ObservableQuestObject(
obj.type,
obj.id,
obj.group_id,
obj.area_id,
obj.section_id,
obj.position,
obj.rotation,
obj.properties,
obj.unknown,
),
),
quest.npcs.map(
npc =>
new ObservableQuestNpc(
npc.type,
npc.pso_type_id,
npc.npc_id,
npc.script_label,
npc.roaming,
npc.area_id,
npc.section_id,
npc.position,
npc.rotation,
npc.scale,
npc.unknown,
),
),
quest.dat_unknowns,
quest.object_code,
quest.shop_items,
),
filename,
);
} catch (e) {
logger.error("Couldn't read file.", e);
}
});
@action
open_save_dialog = () => {
this.save_dialog_filename = this.current_quest_filename
@ -218,165 +103,4 @@ class QuestEditorStore {
this.save_dialog_open = false;
};
@action
push_id_edit_action = (old_id: number, new_id: number) => {
const quest = quest_editor_store.current_quest;
if (quest) quest.set_id(new_id);
this.undo.push_action(
`Edit ID`,
() => {
const quest = quest_editor_store.current_quest;
if (quest) quest.set_id(old_id);
},
() => {
const quest = quest_editor_store.current_quest;
if (quest) quest.set_id(new_id);
},
);
};
@action
push_name_edit_action = (old_name: string, new_name: string) => {
const quest = quest_editor_store.current_quest;
if (quest) quest.set_name(new_name);
this.undo.push_action(
`Edit name`,
() => {
const quest = quest_editor_store.current_quest;
if (quest) quest.set_name(old_name);
},
() => {
const quest = quest_editor_store.current_quest;
if (quest) quest.set_name(new_name);
},
);
};
@action
push_short_description_edit_action = (
old_short_description: string,
new_short_description: string,
) => {
const quest = quest_editor_store.current_quest;
if (quest) quest.set_short_description(new_short_description);
this.undo.push_action(
`Edit short description`,
() => {
const quest = quest_editor_store.current_quest;
if (quest) quest.set_short_description(old_short_description);
},
() => {
const quest = quest_editor_store.current_quest;
if (quest) quest.set_short_description(new_short_description);
},
);
};
@action
push_long_description_edit_action = (
old_long_description: string,
new_long_description: string,
) => {
const quest = quest_editor_store.current_quest;
if (quest) quest.set_long_description(new_long_description);
this.undo.push_action(
`Edit long description`,
() => {
const quest = quest_editor_store.current_quest;
if (quest) quest.set_long_description(old_long_description);
},
() => {
const quest = quest_editor_store.current_quest;
if (quest) quest.set_long_description(new_long_description);
},
);
};
@action
push_entity_move_action = (
entity: QuestEntityModel,
old_position: Vec3,
new_position: Vec3,
) => {
this.undo.push_action(
`Move ${entity_data(entity.type).name}`,
() => {
entity.world_position = old_position;
quest_editor_store.set_selected_entity(entity);
},
() => {
entity.world_position = new_position;
quest_editor_store.set_selected_entity(entity);
},
);
};
@action
private set_quest = flow(function* set_quest(
this: QuestEditorStore,
quest?: ObservableQuest,
filename?: string,
) {
this.current_quest_filename = filename;
if (quest !== this.current_quest) {
this.undo.reset();
this.script_undo.reset();
this.selected_entity = undefined;
this.current_quest = quest;
if (quest) {
this.current_area = area_store.get_area(quest.episode, 0);
} else {
this.current_area = undefined;
}
if (quest) {
// Load section data.
for (const variant of quest.area_variants) {
const sections = yield area_store.get_area_sections(
quest.episode,
variant.area.id,
variant.id,
);
variant.sections.replace(sections);
for (const object of quest.objects.filter(o => o.area_id === variant.area.id)) {
try {
this.set_section_on_quest_entity(object, sections);
} catch (e) {
logger.error(e);
}
}
for (const npc of quest.npcs.filter(npc => npc.area_id === variant.area.id)) {
try {
this.set_section_on_quest_entity(npc, sections);
} catch (e) {
logger.error(e);
}
}
}
} else {
logger.error("Couldn't parse quest file.");
}
}
});
private set_section_on_quest_entity = (entity: QuestEntityModel, sections: SectionModel[]) => {
const section = sections.find(s => s.id === entity.section_id);
if (section) {
entity.section = section;
} else {
logger.warn(`Section ${entity.section_id} not found.`);
}
};
}
export const quest_editor_store = new QuestEditorStore();

View File

@ -1,29 +0,0 @@
.main {
width: 100%;
height: 100%;
padding: 2px 10px 10px 10px;
display: flex;
flex-direction: column;
overflow: auto;
}
.table {
border-collapse: collapse;
}
.table th:not([colspan]) {
width: 65px;
}
.coord_label {
padding: 0px 5px 0px 10px;
}
.coord {
width: 100px !important;
}
.coord input {
text-align: right;
padding-right: 24px !important;
}

View File

@ -1,152 +0,0 @@
import { InputNumber } from "antd";
import { autorun, IReactionDisposer } from "mobx";
import { observer } from "mobx-react";
import React, { Component, PureComponent, ReactNode } from "react";
import { Vec3 } from "../../../core/data_formats/vector";
import { quest_editor_store } from "../stores/QuestEditorStore";
import { DisabledTextComponent } from "../../core/ui/DisabledTextComponent";
import styles from "./EntityInfoComponent.css";
import { entity_data, entity_type_to_string } from "../../../core/data_formats/parsing/quest/entities";
import { QuestEntityModel, ObservableQuestNpc } from "../domain/observable_quest_entities";
@observer
export class EntityInfoComponent extends Component {
render(): ReactNode {
const entity = quest_editor_store.selected_entity;
let body: ReactNode;
if (entity) {
const section_id = entity.section ? entity.section.id : entity.section_id;
body = (
<table className={styles.table}>
<tbody>
<tr>
<th>{entity instanceof ObservableQuestNpc ? "NPC" : "Object"}:</th>
<td>{entity_data(entity.type).name}</td>
</tr>
<tr>
<th>Section:</th>
<td>{section_id}</td>
</tr>
<tr>
<th colSpan={2}>Section position:</th>
</tr>
<CoordRow entity={entity} position_type="position" coord="x" />
<CoordRow entity={entity} position_type="position" coord="y" />
<CoordRow entity={entity} position_type="position" coord="z" />
<tr>
<th colSpan={2}>World position:</th>
</tr>
<CoordRow entity={entity} position_type="world_position" coord="x" />
<CoordRow entity={entity} position_type="world_position" coord="y" />
<CoordRow entity={entity} position_type="world_position" coord="z" />
</tbody>
</table>
);
} else {
body = <DisabledTextComponent>No entity selected.</DisabledTextComponent>;
}
return (
<div className={styles.main} tabIndex={-1}>
{body}
</div>
);
}
}
type CoordProps = {
entity: QuestEntityModel;
position_type: "position" | "world_position";
coord: "x" | "y" | "z";
};
class CoordRow extends PureComponent<CoordProps> {
render(): ReactNode {
return (
<tr>
<th className={styles.coord_label}>{this.props.coord.toUpperCase()}:</th>
<td>
<CoordInput {...this.props} />
</td>
</tr>
);
}
}
class CoordInput extends Component<CoordProps, { value: number; initial_position: Vec3 }> {
private disposer?: IReactionDisposer;
state = { value: 0, initial_position: new Vec3(0, 0, 0) };
componentDidMount(): void {
this.start_observing();
}
componentWillUnmount(): void {
if (this.disposer) this.disposer();
}
componentDidUpdate(prev_props: CoordProps): void {
if (this.props.entity !== prev_props.entity) {
this.start_observing();
}
}
render(): ReactNode {
return (
<InputNumber
value={this.state.value}
size="small"
precision={3}
className={styles.coord}
onFocus={this.focus}
onBlur={this.blur}
onChange={this.changed}
/>
);
}
private start_observing(): void {
if (this.disposer) this.disposer();
this.disposer = autorun(
() => {
this.setState({
value: this.props.entity[this.props.position_type][this.props.coord],
});
},
{
name: `${entity_type_to_string(this.props.entity.type)}.${
this.props.position_type
}.${this.props.coord} changed`,
delay: 50,
},
);
}
private focus = () => {
this.setState({ initial_position: this.props.entity.world_position });
};
private blur = () => {
if (!this.state.initial_position.equals(this.props.entity.world_position)) {
quest_editor_store.push_entity_move_action(
this.props.entity,
this.state.initial_position,
this.props.entity.world_position,
);
}
};
private changed = (value?: number) => {
if (value != null) {
const entity = this.props.entity;
const pos_type = this.props.position_type;
const pos = entity[pos_type].clone();
pos[this.props.coord] = value;
entity[pos_type] = pos;
}
};
}

View File

@ -7,9 +7,19 @@ import { Select } from "../../core/gui/Select";
import { array_property } from "../../core/observable";
import { AreaModel } from "../model/AreaModel";
import { Icon } from "../../core/gui/dom";
import { DropDownButton } from "../../core/gui/DropDownButton";
import { Episode } from "../../core/data_formats/parsing/quest/Episode";
export class QuestEditorToolBar extends ToolBar {
constructor() {
const new_quest_button = new DropDownButton(
"New quest",
[Episode.I],
episode => `Episode ${Episode[episode]}`,
{
icon_left: Icon.NewFile,
},
);
const open_file_button = new FileButton("Open file...", {
icon_left: Icon.File,
accept: ".qst",
@ -41,12 +51,23 @@ export class QuestEditorToolBar extends ToolBar {
);
super({
children: [open_file_button, save_as_button, undo_button, redo_button, area_select],
children: [
new_quest_button,
open_file_button,
save_as_button,
undo_button,
redo_button,
area_select,
],
});
const quest_loaded = quest_editor_store.current_quest.map(q => q != undefined);
this.disposables(
new_quest_button.chosen.observe(({ value: episode }) =>
quest_editor_store.new_quest(episode),
),
open_file_button.files.observe(({ value: files }) => {
if (files.length) {
quest_editor_store.open_file(files[0]);

View File

@ -10,6 +10,7 @@ import { AssemblyError, AssemblyWarning } from "../scripting/assembly";
import { Observable } from "../../core/observable/Observable";
import { emitter, property } from "../../core/observable";
import { WritableProperty } from "../../core/observable/property/WritableProperty";
import { Property } from "../../core/observable/property/Property";
import SignatureHelp = languages.SignatureHelp;
import ITextModel = editor.ITextModel;
import CompletionList = languages.CompletionList;
@ -59,15 +60,9 @@ languages.setLanguageConfiguration("psoasm", {
});
export class AsmEditorStore implements Disposable {
private readonly _model: WritableProperty<ITextModel | undefined> = property(undefined);
readonly model = this._model;
private readonly _did_undo = emitter<string>();
readonly did_undo: Observable<string> = this._did_undo;
private readonly _did_redo = emitter<string>();
readonly did_redo: Observable<string> = this._did_redo;
readonly model: Property<ITextModel | undefined>;
readonly did_undo: Observable<string>;
readonly did_redo: Observable<string>;
readonly undo = new SimpleUndo(
"Text edits",
() => this._did_undo.emit({ value: "asm undo" }),
@ -76,8 +71,15 @@ export class AsmEditorStore implements Disposable {
private readonly disposer = new Disposer();
private readonly model_disposer = this.disposer.add(new Disposer());
private readonly _model: WritableProperty<ITextModel | undefined> = property(undefined);
private readonly _did_undo = emitter<string>();
private readonly _did_redo = emitter<string>();
constructor() {
this.model = this._model;
this.did_undo = this._did_undo;
this.did_redo = this._did_redo;
this.disposer.add_all(
quest_editor_store.current_quest.observe(({ value }) => this.quest_changed(value), {
call_now: true,

View File

@ -22,30 +22,32 @@ import { EditShortDescriptionAction } from "../actions/EditShortDescriptionActio
import { EditLongDescriptionAction } from "../actions/EditLongDescriptionAction";
import { EditNameAction } from "../actions/EditNameAction";
import { EditIdAction } from "../actions/EditIdAction";
import { Episode } from "../../core/data_formats/parsing/quest/Episode";
import { create_new_quest } from "./quest_creation";
import Logger = require("js-logger");
const logger = Logger.get("quest_editor/gui/QuestEditorStore");
export class QuestEditorStore implements Disposable {
readonly debug: WritableProperty<boolean> = property(false);
readonly undo = new UndoStack();
private readonly _current_quest_filename = property<string | undefined>(undefined);
readonly current_quest_filename: Property<string | undefined> = this._current_quest_filename;
private readonly _current_quest = property<QuestModel | undefined>(undefined);
readonly current_quest: Property<QuestModel | undefined> = this._current_quest;
private readonly _current_area = property<AreaModel | undefined>(undefined);
readonly current_area: Property<AreaModel | undefined> = this._current_area;
private readonly _selected_entity = property<QuestEntityModel | undefined>(undefined);
readonly selected_entity: Property<QuestEntityModel | undefined> = this._selected_entity;
readonly current_quest_filename: Property<string | undefined>;
readonly current_quest: Property<QuestModel | undefined>;
readonly current_area: Property<AreaModel | undefined>;
readonly selected_entity: Property<QuestEntityModel | undefined>;
private readonly disposer = new Disposer();
private readonly _current_quest_filename = property<string | undefined>(undefined);
private readonly _current_quest = property<QuestModel | undefined>(undefined);
private readonly _current_area = property<AreaModel | undefined>(undefined);
private readonly _selected_entity = property<QuestEntityModel | undefined>(undefined);
constructor() {
this.current_quest_filename = this._current_quest_filename;
this.current_quest = this._current_quest;
this.current_area = this._current_area;
this.selected_entity = this._selected_entity;
this.disposer.add(
gui_store.tool.observe(
({ value: tool }) => {
@ -62,14 +64,6 @@ export class QuestEditorStore implements Disposable {
this.disposer.dispose();
}
set_current_area_id = (area_id?: number) => {
if (area_id == undefined) {
this.set_current_area(undefined);
} else if (this.current_quest.val) {
this.set_current_area(area_store.get_area(this.current_quest.val.episode, area_id));
}
};
set_current_area = (area?: AreaModel) => {
this._selected_entity.val = undefined;
@ -87,6 +81,10 @@ export class QuestEditorStore implements Disposable {
this._selected_entity.val = entity;
};
new_quest = (episode: Episode) => {
this.set_quest(create_new_quest(episode));
};
// TODO: notify user of problems.
open_file = async (file: File) => {
try {