mirror of
https://github.com/DaanVandenBosch/phantasmal-world.git
synced 2025-04-08 01:01:36 +08:00
Refactored code so that new tools can now be added easily.
This commit is contained in:
parent
eacf826fc8
commit
f31570d5f5
@ -1,14 +1,14 @@
|
|||||||
import { action } from 'mobx';
|
import { action } from 'mobx';
|
||||||
import { Object3D } from 'three';
|
import { Object3D } from 'three';
|
||||||
import { ArrayBufferCursor } from '../data/ArrayBufferCursor';
|
import { ArrayBufferCursor } from '../../data/ArrayBufferCursor';
|
||||||
import { getAreaSections } from '../data/loading/areas';
|
import { getAreaSections } from '../../data/loading/areas';
|
||||||
import { getNpcGeometry, getObjectGeometry } from '../data/loading/entities';
|
import { getNpcGeometry, getObjectGeometry } from '../../data/loading/entities';
|
||||||
import { parseNj, parseXj } from '../data/parsing/ninja';
|
import { parseNj, parseXj } from '../../data/parsing/ninja';
|
||||||
import { parseQuest } from '../data/parsing/quest';
|
import { parseQuest } from '../../data/parsing/quest';
|
||||||
import { AreaVariant, Section, Vec3, VisibleQuestEntity } from '../domain';
|
import { AreaVariant, Section, Vec3, VisibleQuestEntity } from '../../domain';
|
||||||
import { createNpcMesh, createObjectMesh } from '../rendering/entities';
|
import { createNpcMesh, createObjectMesh } from '../../rendering/entities';
|
||||||
import { createModelMesh } from '../rendering/models';
|
import { createModelMesh } from '../../rendering/models';
|
||||||
import { setModel, setQuest } from './appState';
|
import { setModel, setQuest } from './questEditor';
|
||||||
|
|
||||||
export function loadFile(file: File) {
|
export function loadFile(file: File) {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
@ -1,6 +1,6 @@
|
|||||||
import { writeQuestQst } from '../data/parsing/quest';
|
import { writeQuestQst } from '../../data/parsing/quest';
|
||||||
import { VisibleQuestEntity, Quest } from '../domain';
|
import { VisibleQuestEntity, Quest } from '../../domain';
|
||||||
import { appStateStore } from '../stores/AppStateStore';
|
import { questEditorStore } from '../../stores/QuestEditorStore';
|
||||||
import { action } from 'mobx';
|
import { action } from 'mobx';
|
||||||
import { Object3D } from 'three';
|
import { Object3D } from 'three';
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ import { Object3D } from 'three';
|
|||||||
*/
|
*/
|
||||||
export const setModel = action('setModel', (model?: Object3D) => {
|
export const setModel = action('setModel', (model?: Object3D) => {
|
||||||
resetModelAndQuestState();
|
resetModelAndQuestState();
|
||||||
appStateStore.currentModel = model;
|
questEditorStore.currentModel = model;
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -17,39 +17,39 @@ export const setModel = action('setModel', (model?: Object3D) => {
|
|||||||
*/
|
*/
|
||||||
export const setQuest = action('setQuest', (quest?: Quest) => {
|
export const setQuest = action('setQuest', (quest?: Quest) => {
|
||||||
resetModelAndQuestState();
|
resetModelAndQuestState();
|
||||||
appStateStore.currentQuest = quest;
|
questEditorStore.currentQuest = quest;
|
||||||
|
|
||||||
if (quest && quest.areaVariants.length) {
|
if (quest && quest.areaVariants.length) {
|
||||||
appStateStore.currentArea = quest.areaVariants[0].area;
|
questEditorStore.currentArea = quest.areaVariants[0].area;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function resetModelAndQuestState() {
|
function resetModelAndQuestState() {
|
||||||
appStateStore.currentQuest = undefined;
|
questEditorStore.currentQuest = undefined;
|
||||||
appStateStore.currentArea = undefined;
|
questEditorStore.currentArea = undefined;
|
||||||
appStateStore.selectedEntity = undefined;
|
questEditorStore.selectedEntity = undefined;
|
||||||
appStateStore.currentModel = undefined;
|
questEditorStore.currentModel = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const setSelectedEntity = action('setSelectedEntity', (entity?: VisibleQuestEntity) => {
|
export const setSelectedEntity = action('setSelectedEntity', (entity?: VisibleQuestEntity) => {
|
||||||
appStateStore.selectedEntity = entity;
|
questEditorStore.selectedEntity = entity;
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setCurrentAreaId = action('setCurrentAreaId', (areaId?: number) => {
|
export const setCurrentAreaId = action('setCurrentAreaId', (areaId?: number) => {
|
||||||
appStateStore.selectedEntity = undefined;
|
questEditorStore.selectedEntity = undefined;
|
||||||
|
|
||||||
if (areaId == null) {
|
if (areaId == null) {
|
||||||
appStateStore.currentArea = undefined;
|
questEditorStore.currentArea = undefined;
|
||||||
} else if (appStateStore.currentQuest) {
|
} else if (questEditorStore.currentQuest) {
|
||||||
const areaVariant = appStateStore.currentQuest.areaVariants.find(
|
const areaVariant = questEditorStore.currentQuest.areaVariants.find(
|
||||||
variant => variant.area.id === areaId);
|
variant => variant.area.id === areaId);
|
||||||
appStateStore.currentArea = areaVariant && areaVariant.area;
|
questEditorStore.currentArea = areaVariant && areaVariant.area;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export const saveCurrentQuestToFile = (fileName: string) => {
|
export const saveCurrentQuestToFile = (fileName: string) => {
|
||||||
if (appStateStore.currentQuest) {
|
if (questEditorStore.currentQuest) {
|
||||||
const cursor = writeQuestQst(appStateStore.currentQuest, fileName);
|
const cursor = writeQuestQst(questEditorStore.currentQuest, fileName);
|
||||||
|
|
||||||
if (!fileName.endsWith('.qst')) {
|
if (!fileName.endsWith('.qst')) {
|
||||||
fileName += '.qst';
|
fileName += '.qst';
|
@ -1,5 +1,5 @@
|
|||||||
import { action } from "mobx";
|
import { action } from "mobx";
|
||||||
import { VisibleQuestEntity, Vec3, Section } from "../domain";
|
import { VisibleQuestEntity, Vec3, Section } from "../../domain";
|
||||||
|
|
||||||
export const setPositionOnVisibleQuestEntity = action('setPositionOnVisibleQuestEntity',
|
export const setPositionOnVisibleQuestEntity = action('setPositionOnVisibleQuestEntity',
|
||||||
(entity: VisibleQuestEntity, position: Vec3, section?: Section) => {
|
(entity: VisibleQuestEntity, position: Vec3, section?: Section) => {
|
@ -26,8 +26,8 @@ import {
|
|||||||
NPC_HOVER_COLOR,
|
NPC_HOVER_COLOR,
|
||||||
NPC_SELECTED_COLOR
|
NPC_SELECTED_COLOR
|
||||||
} from './entities';
|
} from './entities';
|
||||||
import { setSelectedEntity } from '../actions/appState';
|
import { setSelectedEntity } from '../actions/quest-editor/questEditor';
|
||||||
import { setPositionOnVisibleQuestEntity as setPositionAndSectionOnVisibleQuestEntity } from '../actions/visibleQuestEntities';
|
import { setPositionOnVisibleQuestEntity as setPositionAndSectionOnVisibleQuestEntity } from '../actions/quest-editor/visibleQuestEntities';
|
||||||
|
|
||||||
const OrbitControls = OrbitControlsCreator(THREE);
|
const OrbitControls = OrbitControlsCreator(THREE);
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ interface PickEntityResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders one quest area at a time.
|
* Renders a quest area or an NJ/XJ model.
|
||||||
*/
|
*/
|
||||||
export class Renderer {
|
export class Renderer {
|
||||||
private renderer = new WebGLRenderer({ antialias: true });
|
private renderer = new WebGLRenderer({ antialias: true });
|
||||||
|
@ -2,11 +2,11 @@ import { observable } from 'mobx';
|
|||||||
import { Object3D } from 'three';
|
import { Object3D } from 'three';
|
||||||
import { Area, Quest, VisibleQuestEntity } from '../domain';
|
import { Area, Quest, VisibleQuestEntity } from '../domain';
|
||||||
|
|
||||||
class AppStateStore {
|
class QuestEditorStore {
|
||||||
@observable currentModel?: Object3D;
|
@observable currentModel?: Object3D;
|
||||||
@observable currentQuest?: Quest;
|
@observable currentQuest?: Quest;
|
||||||
@observable currentArea?: Area;
|
@observable currentArea?: Area;
|
||||||
@observable selectedEntity?: VisibleQuestEntity;
|
@observable selectedEntity?: VisibleQuestEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const appStateStore = new AppStateStore();
|
export const questEditorStore = new QuestEditorStore();
|
@ -8,8 +8,8 @@
|
|||||||
right: 0;
|
right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ApplicationComponent-heading {
|
div.ApplicationComponent .ApplicationComponent-heading {
|
||||||
font-size: 22px !important;
|
font-size: 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ApplicationComponent-beta {
|
.ApplicationComponent-beta {
|
||||||
@ -18,16 +18,13 @@
|
|||||||
margin-left: 2;
|
margin-left: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ApplicationComponent-button-bar > * {
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ApplicationComponent-main {
|
.ApplicationComponent-main {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ApplicationComponent-main div:nth-child(2) {
|
.ApplicationComponent-main>* {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
@ -1,32 +1,22 @@
|
|||||||
import { Button, Dialog, Intent, Classes, Navbar, NavbarGroup, NavbarHeading, FileInput, HTMLSelect, FormGroup, InputGroup } from '@blueprintjs/core';
|
import { Classes, Navbar, NavbarGroup, NavbarHeading, Button } from '@blueprintjs/core';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import React, { ChangeEvent, KeyboardEvent } from 'react';
|
import React from 'react';
|
||||||
import { saveCurrentQuestToFile, setCurrentAreaId } from '../actions/appState';
|
|
||||||
import { loadFile } from '../actions/loadFile';
|
|
||||||
import { appStateStore } from '../stores/AppStateStore';
|
|
||||||
import './ApplicationComponent.css';
|
import './ApplicationComponent.css';
|
||||||
import { RendererComponent } from './RendererComponent';
|
import { QuestEditorComponent } from './quest-editor/QuestEditorComponent';
|
||||||
import { EntityInfoComponent } from './EntityInfoComponent';
|
import { observable, action } from 'mobx';
|
||||||
import { QuestInfoComponent } from './QuestInfoComponent';
|
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class ApplicationComponent extends React.Component<{}, {
|
export class ApplicationComponent extends React.Component {
|
||||||
filename?: string,
|
@observable private tool = 'quest-editor';
|
||||||
saveDialogOpen: boolean,
|
|
||||||
saveDialogFilename: string
|
|
||||||
}> {
|
|
||||||
state = {
|
|
||||||
filename: undefined,
|
|
||||||
saveDialogOpen: false,
|
|
||||||
saveDialogFilename: 'Untitled',
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const quest = appStateStore.currentQuest;
|
let toolComponent;
|
||||||
const model = appStateStore.currentModel;
|
|
||||||
const areas = quest && Array.from(quest.areaVariants).map(a => a.area);
|
switch (this.tool) {
|
||||||
const area = appStateStore.currentArea;
|
case 'quest-editor':
|
||||||
const areaId = area && String(area.id);
|
toolComponent = <QuestEditorComponent />;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`ApplicationComponent ${Classes.DARK}`}>
|
<div className={`ApplicationComponent ${Classes.DARK}`}>
|
||||||
@ -34,121 +24,22 @@ export class ApplicationComponent extends React.Component<{}, {
|
|||||||
<NavbarGroup className="ApplicationComponent-button-bar">
|
<NavbarGroup className="ApplicationComponent-button-bar">
|
||||||
<NavbarHeading className="ApplicationComponent-heading">
|
<NavbarHeading className="ApplicationComponent-heading">
|
||||||
Phantasmal World
|
Phantasmal World
|
||||||
<sup className="ApplicationComponent-beta">BETA</sup>
|
|
||||||
</NavbarHeading>
|
</NavbarHeading>
|
||||||
<FileInput
|
|
||||||
text={this.state.filename || 'Choose file...'}
|
|
||||||
inputProps={{
|
|
||||||
type: 'file',
|
|
||||||
accept: '.nj, .qst, .xj',
|
|
||||||
onChange: this.onFileChange
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{areas ? (
|
|
||||||
<HTMLSelect
|
|
||||||
onChange={this.onAreaSelectChange}
|
|
||||||
defaultValue={areaId}
|
|
||||||
>
|
|
||||||
{areas.map(area =>
|
|
||||||
<option key={area.id} value={area.id}>{area.name}</option>
|
|
||||||
)}
|
|
||||||
</HTMLSelect>
|
|
||||||
) : null}
|
|
||||||
{quest ? (
|
|
||||||
<Button
|
<Button
|
||||||
text="Save as..."
|
text="Quest Editor (Beta)"
|
||||||
icon="floppy-disk"
|
minimal={true}
|
||||||
onClick={this.onSaveAsClick}
|
onClick={() => this.setTool('quest-editor')}
|
||||||
/>
|
/>
|
||||||
) : null}
|
|
||||||
</NavbarGroup>
|
</NavbarGroup>
|
||||||
</Navbar>
|
</Navbar>
|
||||||
<div className="ApplicationComponent-main">
|
<div className="ApplicationComponent-main">
|
||||||
<QuestInfoComponent
|
{toolComponent}
|
||||||
quest={quest} />
|
|
||||||
<RendererComponent
|
|
||||||
quest={quest}
|
|
||||||
area={area}
|
|
||||||
model={model} />
|
|
||||||
<EntityInfoComponent entity={appStateStore.selectedEntity} />
|
|
||||||
</div>
|
</div>
|
||||||
<Dialog
|
|
||||||
title="Save as..."
|
|
||||||
icon="floppy-disk"
|
|
||||||
className={Classes.DARK}
|
|
||||||
style={{ width: 360 }}
|
|
||||||
isOpen={this.state.saveDialogOpen}
|
|
||||||
onClose={this.onSaveDialogClose}>
|
|
||||||
<div className={Classes.DIALOG_BODY}>
|
|
||||||
<FormGroup label="Name:" labelFor="file-name-input">
|
|
||||||
<InputGroup
|
|
||||||
id="file-name-input"
|
|
||||||
autoFocus={true}
|
|
||||||
value={this.state.saveDialogFilename}
|
|
||||||
maxLength={12}
|
|
||||||
onChange={this.onSaveDialogNameChange}
|
|
||||||
onKeyUp={this.onSaveDialogNameKeyUp}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
</div>
|
|
||||||
<div className={Classes.DIALOG_FOOTER}>
|
|
||||||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
|
||||||
<Button
|
|
||||||
text="Save"
|
|
||||||
style={{ marginLeft: 10 }}
|
|
||||||
onClick={this.onSaveDialogSaveClick}
|
|
||||||
intent={Intent.PRIMARY} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Dialog>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onFileChange = (e: ChangeEvent<HTMLInputElement>) => {
|
private setTool = action('setTool', (tool: string) => {
|
||||||
if (e.currentTarget.files) {
|
this.tool = tool;
|
||||||
const file = e.currentTarget.files[0];
|
|
||||||
|
|
||||||
if (file) {
|
|
||||||
this.setState({
|
|
||||||
filename: file.name
|
|
||||||
});
|
});
|
||||||
loadFile(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private onAreaSelectChange = (e: ChangeEvent<HTMLSelectElement>) => {
|
|
||||||
const areaId = parseInt(e.currentTarget.value, 10);
|
|
||||||
setCurrentAreaId(areaId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private onSaveAsClick = () => {
|
|
||||||
let name = this.state.filename || 'Untitled';
|
|
||||||
name = name.endsWith('.qst') ? name.slice(0, -4) : name;
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
saveDialogOpen: true,
|
|
||||||
saveDialogFilename: name
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private onSaveDialogNameChange = (e: ChangeEvent<HTMLInputElement>) => {
|
|
||||||
this.setState({ saveDialogFilename: e.currentTarget.value });
|
|
||||||
}
|
|
||||||
|
|
||||||
private onSaveDialogNameKeyUp = (e: KeyboardEvent<HTMLInputElement>) => {
|
|
||||||
if (e.key === 'Enter') {
|
|
||||||
this.onSaveDialogSaveClick();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private onSaveDialogSaveClick = () => {
|
|
||||||
saveCurrentQuestToFile(this.state.saveDialogFilename);
|
|
||||||
this.setState({ saveDialogOpen: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
private onSaveDialogClose = () => {
|
|
||||||
this.setState({ saveDialogOpen: false });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { NumericInput } from '@blueprintjs/core';
|
import { NumericInput } from '@blueprintjs/core';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { QuestNpc, QuestObject, VisibleQuestEntity } from '../domain';
|
import { QuestNpc, QuestObject, VisibleQuestEntity } from '../../domain';
|
||||||
import './EntityInfoComponent.css';
|
import './EntityInfoComponent.css';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
18
src/ui/quest-editor/QuestEditorComponent.css
Normal file
18
src/ui/quest-editor/QuestEditorComponent.css
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
.QuestEditorComponent {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.QuestEditorComponent-main {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.QuestEditorComponent-button-bar>* {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.QuestEditorComponent-main div:nth-child(2) {
|
||||||
|
flex: 1;
|
||||||
|
}
|
149
src/ui/quest-editor/QuestEditorComponent.tsx
Normal file
149
src/ui/quest-editor/QuestEditorComponent.tsx
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
import { Button, Classes, Dialog, FileInput, FormGroup, HTMLSelect, InputGroup, Intent, Navbar, NavbarGroup } from "@blueprintjs/core";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
|
import React, { ChangeEvent, KeyboardEvent } from "react";
|
||||||
|
import { saveCurrentQuestToFile, setCurrentAreaId } from "../../actions/quest-editor/questEditor";
|
||||||
|
import { loadFile } from "../../actions/quest-editor/loadFile";
|
||||||
|
import { questEditorStore } from "../../stores/QuestEditorStore";
|
||||||
|
import { EntityInfoComponent } from "./EntityInfoComponent";
|
||||||
|
import './QuestEditorComponent.css';
|
||||||
|
import { QuestInfoComponent } from "./QuestInfoComponent";
|
||||||
|
import { RendererComponent } from "./RendererComponent";
|
||||||
|
|
||||||
|
@observer
|
||||||
|
export class QuestEditorComponent extends React.Component<{}, {
|
||||||
|
filename?: string,
|
||||||
|
saveDialogOpen: boolean,
|
||||||
|
saveDialogFilename: string
|
||||||
|
}> {
|
||||||
|
state = {
|
||||||
|
filename: undefined,
|
||||||
|
saveDialogOpen: false,
|
||||||
|
saveDialogFilename: 'Untitled',
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const quest = questEditorStore.currentQuest;
|
||||||
|
const model = questEditorStore.currentModel;
|
||||||
|
const areas = quest && Array.from(quest.areaVariants).map(a => a.area);
|
||||||
|
const area = questEditorStore.currentArea;
|
||||||
|
const areaId = area && String(area.id);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="QuestEditorComponent">
|
||||||
|
<Navbar>
|
||||||
|
<NavbarGroup className="QuestEditorComponent-button-bar">
|
||||||
|
<FileInput
|
||||||
|
text={this.state.filename || 'Choose file...'}
|
||||||
|
inputProps={{
|
||||||
|
type: 'file',
|
||||||
|
accept: '.nj, .qst, .xj',
|
||||||
|
onChange: this.onFileChange
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{areas ? (
|
||||||
|
<HTMLSelect
|
||||||
|
onChange={this.onAreaSelectChange}
|
||||||
|
defaultValue={areaId}
|
||||||
|
>
|
||||||
|
{areas.map(area =>
|
||||||
|
<option key={area.id} value={area.id}>{area.name}</option>
|
||||||
|
)}
|
||||||
|
</HTMLSelect>
|
||||||
|
) : null}
|
||||||
|
{quest ? (
|
||||||
|
<Button
|
||||||
|
text="Save as..."
|
||||||
|
icon="floppy-disk"
|
||||||
|
onClick={this.onSaveAsClick}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</NavbarGroup>
|
||||||
|
</Navbar>
|
||||||
|
<div className="QuestEditorComponent-main">
|
||||||
|
<QuestInfoComponent quest={quest} />
|
||||||
|
<RendererComponent
|
||||||
|
quest={quest}
|
||||||
|
area={area}
|
||||||
|
model={model} />
|
||||||
|
<EntityInfoComponent entity={questEditorStore.selectedEntity} />
|
||||||
|
</div>
|
||||||
|
<Dialog
|
||||||
|
title="Save as..."
|
||||||
|
icon="floppy-disk"
|
||||||
|
className={Classes.DARK}
|
||||||
|
style={{ width: 360 }}
|
||||||
|
isOpen={this.state.saveDialogOpen}
|
||||||
|
onClose={this.onSaveDialogClose}>
|
||||||
|
<div className={Classes.DIALOG_BODY}>
|
||||||
|
<FormGroup label="Name:" labelFor="file-name-input">
|
||||||
|
<InputGroup
|
||||||
|
id="file-name-input"
|
||||||
|
autoFocus={true}
|
||||||
|
value={this.state.saveDialogFilename}
|
||||||
|
maxLength={12}
|
||||||
|
onChange={this.onSaveDialogNameChange}
|
||||||
|
onKeyUp={this.onSaveDialogNameKeyUp}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</div>
|
||||||
|
<div className={Classes.DIALOG_FOOTER}>
|
||||||
|
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||||
|
<Button
|
||||||
|
text="Save"
|
||||||
|
style={{ marginLeft: 10 }}
|
||||||
|
onClick={this.onSaveDialogSaveClick}
|
||||||
|
intent={Intent.PRIMARY} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private onFileChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
if (e.currentTarget.files) {
|
||||||
|
const file = e.currentTarget.files[0];
|
||||||
|
|
||||||
|
if (file) {
|
||||||
|
this.setState({
|
||||||
|
filename: file.name
|
||||||
|
});
|
||||||
|
loadFile(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onAreaSelectChange = (e: ChangeEvent<HTMLSelectElement>) => {
|
||||||
|
const areaId = parseInt(e.currentTarget.value, 10);
|
||||||
|
setCurrentAreaId(areaId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private onSaveAsClick = () => {
|
||||||
|
let name = this.state.filename || 'Untitled';
|
||||||
|
name = name.endsWith('.qst') ? name.slice(0, -4) : name;
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
saveDialogOpen: true,
|
||||||
|
saveDialogFilename: name
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private onSaveDialogNameChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
this.setState({ saveDialogFilename: e.currentTarget.value });
|
||||||
|
}
|
||||||
|
|
||||||
|
private onSaveDialogNameKeyUp = (e: KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
this.onSaveDialogSaveClick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onSaveDialogSaveClick = () => {
|
||||||
|
saveCurrentQuestToFile(this.state.saveDialogFilename);
|
||||||
|
this.setState({ saveDialogOpen: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
private onSaveDialogClose = () => {
|
||||||
|
this.setState({ saveDialogOpen: false });
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { NpcType, Quest } from '../domain';
|
import { NpcType, Quest } from '../../domain';
|
||||||
import { Pre } from '@blueprintjs/core';
|
import { Pre } from '@blueprintjs/core';
|
||||||
import './QuestInfoComponent.css';
|
import './QuestInfoComponent.css';
|
||||||
|
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Object3D } from 'three';
|
import { Object3D } from 'three';
|
||||||
import { Area, Quest } from '../domain';
|
import { Area, Quest } from '../../domain';
|
||||||
import { Renderer } from '../rendering/Renderer';
|
import { Renderer } from '../../rendering/Renderer';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
quest?: Quest;
|
quest?: Quest;
|
Loading…
Reference in New Issue
Block a user