Fix bugs
This commit is contained in:
parent
2d4a59f2b3
commit
0ad5c60cdd
|
|
@ -1,12 +1,61 @@
|
||||||
import type { RPGActions, RPGVariables } from "../types";
|
import type { RPGActions, RPGVariables } from "../types";
|
||||||
import { ACTION_KEYS, VARIABLE_KEYS } from "../decorators";
|
import { ACTION_KEYS, VARIABLE_KEYS } from "../decorators";
|
||||||
|
|
||||||
export interface RPGComponent {
|
export interface RPGEvent<T = unknown> {
|
||||||
getVariables: () => RPGVariables;
|
target: RPGComponent;
|
||||||
getActions: () => RPGActions;
|
data?: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class RPGComponentBase implements RPGComponent {
|
export interface RPGContext {
|
||||||
|
dispatch(event: string, payload: RPGEvent): void;
|
||||||
|
on(event: string, handler: (payload: RPGEvent) => void): () => void;
|
||||||
|
off(event: string, handler: (payload: RPGEvent) => void): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class RPGComponent {
|
||||||
|
#path: string = '';
|
||||||
|
protected _ctx: RPGContext | null = null;
|
||||||
|
|
||||||
|
get path(): string {
|
||||||
|
return this.#path;
|
||||||
|
}
|
||||||
|
|
||||||
|
set path(path: string) {
|
||||||
|
this.#path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
attach(ctx: RPGContext, path: string): void {
|
||||||
|
this.path = path;
|
||||||
|
this._ctx = ctx;
|
||||||
|
this.onAttach();
|
||||||
|
}
|
||||||
|
|
||||||
|
detach(): void {
|
||||||
|
this.onDetach();
|
||||||
|
this.path = '';
|
||||||
|
this._ctx = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected onAttach(): void {}
|
||||||
|
protected onDetach(): void {}
|
||||||
|
|
||||||
|
protected emit<T = unknown>(event: string, data?: T): void {
|
||||||
|
this._ctx?.dispatch(this.resolve(event), { target: this, data });
|
||||||
|
}
|
||||||
|
|
||||||
|
on<T = unknown>(event: string, handler: (payload: RPGEvent<T>) => void): () => void {
|
||||||
|
return this._ctx?.on(this.resolve(event), handler as (e: RPGEvent) => void) ?? (() => {});
|
||||||
|
}
|
||||||
|
|
||||||
|
off<T = unknown>(event: string, handler: (payload: RPGEvent<T>) => void): void {
|
||||||
|
this._ctx?.off(this.resolve(event), handler as (e: RPGEvent) => void);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected resolve(name: string): string {
|
||||||
|
if (name.startsWith('$.')) return name.slice(2);
|
||||||
|
return this.path ? `${this.path}.${name}` : name;
|
||||||
|
}
|
||||||
|
|
||||||
getVariables(): RPGVariables {
|
getVariables(): RPGVariables {
|
||||||
const meta = (this.constructor as Function)[Symbol.metadata];
|
const meta = (this.constructor as Function)[Symbol.metadata];
|
||||||
const keys = meta?.[VARIABLE_KEYS] as Map<string | symbol, string> | undefined;
|
const keys = meta?.[VARIABLE_KEYS] as Map<string | symbol, string> | undefined;
|
||||||
|
|
@ -40,24 +89,92 @@ export abstract class RPGComponentBase implements RPGComponent {
|
||||||
}
|
}
|
||||||
return actions;
|
return actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tick(_dt: number) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RPGEntity implements RPGComponent {
|
function matchesWildcard(event: string, pattern: string): boolean {
|
||||||
|
const ep = event.split('.');
|
||||||
|
const pp = pattern.split('.');
|
||||||
|
return (function match(ei: number, pi: number): boolean {
|
||||||
|
if (pi === pp.length) return ei === ep.length;
|
||||||
|
if (pp[pi] === '*') {
|
||||||
|
for (let skip = 0; ei + skip <= ep.length; skip++) {
|
||||||
|
if (match(ei + skip, pi + 1)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return ei < ep.length && pp[pi] === ep[ei] && match(ei + 1, pi + 1);
|
||||||
|
})(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RPGEntity extends RPGComponent implements RPGContext {
|
||||||
|
private readonly handlers = new Map<string, Set<(payload: RPGEvent) => void>>();
|
||||||
|
private readonly wildcardHandlers = new Map<string, Set<(payload: RPGEvent) => void>>();
|
||||||
private components = new Map<string, RPGComponent>();
|
private components = new Map<string, RPGComponent>();
|
||||||
|
|
||||||
|
constructor(public readonly id: string) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
// RPGContext: raw dispatch into this entity's handler map
|
||||||
|
dispatch(event: string, payload: RPGEvent): void {
|
||||||
|
this.handlers.get(event)?.forEach(h => h(payload));
|
||||||
|
for (const [pattern, handlers] of this.wildcardHandlers) {
|
||||||
|
if (matchesWildcard(event, pattern)) handlers.forEach(h => h(payload));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RPGContext: register locally if root, delegate up if nested
|
||||||
|
override on<T = unknown>(event: string, handler: (payload: RPGEvent<T>) => void): () => void {
|
||||||
|
const resolved = this.resolve(event);
|
||||||
|
const h = handler as (payload: RPGEvent) => void;
|
||||||
|
if (this._ctx) {
|
||||||
|
return this._ctx.on(resolved, h);
|
||||||
|
}
|
||||||
|
const map = resolved.includes('*') ? this.wildcardHandlers : this.handlers;
|
||||||
|
if (!map.has(resolved)) map.set(resolved, new Set());
|
||||||
|
map.get(resolved)!.add(h);
|
||||||
|
return () => map.get(resolved)?.delete(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
override off<T = unknown>(event: string, handler: (payload: RPGEvent<T>) => void): void {
|
||||||
|
const resolved = this.resolve(event);
|
||||||
|
const h = handler as (payload: RPGEvent) => void;
|
||||||
|
if (this._ctx) {
|
||||||
|
this._ctx.off(resolved, h);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const map = resolved.includes('*') ? this.wildcardHandlers : this.handlers;
|
||||||
|
map.get(resolved)?.delete(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
override attach(ctx: RPGContext, path: string): void {
|
||||||
|
super.attach(ctx, path);
|
||||||
|
for (const [key, component] of this.components) {
|
||||||
|
component.attach(ctx, `${path}.${key}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
addComponent(id: string, component: RPGComponent): void {
|
addComponent(id: string, component: RPGComponent): void {
|
||||||
this.components.set(id, component);
|
this.components.set(id, component);
|
||||||
|
const path = this.path ? `${this.path}.${id}` : id;
|
||||||
|
component.attach(this._ctx ?? this, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeComponent(id: string): void {
|
removeComponent(id: string): void {
|
||||||
this.components.delete(id);
|
const component = this.components.get(id);
|
||||||
|
if (component) {
|
||||||
|
component.detach();
|
||||||
|
this.components.delete(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getComponent<T extends RPGComponent>(id: string): T | undefined {
|
getComponent<T extends RPGComponent>(id: string): T | undefined {
|
||||||
return this.components.get(id) as T | undefined;
|
return this.components.get(id) as T | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
getVariables(): RPGVariables {
|
override getVariables(): RPGVariables {
|
||||||
const variables: RPGVariables = {};
|
const variables: RPGVariables = {};
|
||||||
for (const [componentKey, component] of this.components) {
|
for (const [componentKey, component] of this.components) {
|
||||||
for (const [key, value] of Object.entries(component.getVariables())) {
|
for (const [key, value] of Object.entries(component.getVariables())) {
|
||||||
|
|
@ -69,7 +186,7 @@ export class RPGEntity implements RPGComponent {
|
||||||
return variables;
|
return variables;
|
||||||
}
|
}
|
||||||
|
|
||||||
getActions() {
|
override getActions(): RPGActions {
|
||||||
const actions: RPGActions = {};
|
const actions: RPGActions = {};
|
||||||
for (const [componentKey, component] of this.components) {
|
for (const [componentKey, component] of this.components) {
|
||||||
for (const [key, action] of Object.entries(component.getActions())) {
|
for (const [key, action] of Object.entries(component.getActions())) {
|
||||||
|
|
@ -78,4 +195,17 @@ export class RPGEntity implements RPGComponent {
|
||||||
}
|
}
|
||||||
return actions;
|
return actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override tick(dt: number) {
|
||||||
|
for (const component of this.components.values()) {
|
||||||
|
component.tick(dt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override set path(value: string) {
|
||||||
|
super.path = value;
|
||||||
|
for (const [key, component] of this.components) {
|
||||||
|
component.path = value ? `${value}.${key}` : key;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import type { InventoryOptions, InventorySlotInput, SlotId } from "../types";
|
import type { InventoryOptions, InventorySlotInput, SlotId } from "../types";
|
||||||
import { action } from "../decorators";
|
import { action } from "../decorators";
|
||||||
import { RPGComponentBase } from "./entity";
|
import { RPGComponent } from "./entity";
|
||||||
|
|
||||||
interface SlotEntry {
|
interface SlotEntry {
|
||||||
readonly slotId: SlotId;
|
readonly slotId: SlotId;
|
||||||
|
|
@ -13,7 +13,7 @@ interface SlotUpdateArgs {
|
||||||
slotId?: SlotId;
|
slotId?: SlotId;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Inventory extends RPGComponentBase {
|
export class Inventory extends RPGComponent {
|
||||||
private readonly slots: Map<SlotId, SlotEntry>;
|
private readonly slots: Map<SlotId, SlotEntry>;
|
||||||
private readonly maxAmountPerItem: Record<string, number>;
|
private readonly maxAmountPerItem: Record<string, number>;
|
||||||
|
|
||||||
|
|
@ -43,7 +43,7 @@ export class Inventory extends RPGComponentBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
addItem({ itemId, amount, slotId }: SlotUpdateArgs): boolean {
|
add({ itemId, amount, slotId }: SlotUpdateArgs): boolean {
|
||||||
if (amount < 0) return false;
|
if (amount < 0) return false;
|
||||||
if (amount === 0) return true;
|
if (amount === 0) return true;
|
||||||
|
|
||||||
|
|
@ -52,6 +52,7 @@ export class Inventory extends RPGComponentBase {
|
||||||
if (!slot) return false;
|
if (!slot) return false;
|
||||||
if (this.slotRoomFor(slot, itemId) < amount) return false;
|
if (this.slotRoomFor(slot, itemId) < amount) return false;
|
||||||
slot.state = { itemId, amount: (slot.state?.amount ?? 0) + amount };
|
slot.state = { itemId, amount: (slot.state?.amount ?? 0) + amount };
|
||||||
|
this.emit('add', { itemId, amount, slotIds: [slotId] });
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,20 +68,29 @@ export class Inventory extends RPGComponentBase {
|
||||||
|
|
||||||
// Apply
|
// Apply
|
||||||
remaining = amount;
|
remaining = amount;
|
||||||
for (const slot of this.slots.values()) {
|
const slotIds: SlotId[] = [];
|
||||||
|
for (const [id, slot] of this.slots) {
|
||||||
if (slot.state?.itemId === itemId) {
|
if (slot.state?.itemId === itemId) {
|
||||||
const take = Math.min(this.slotRoomFor(slot, itemId), remaining);
|
const take = Math.min(this.slotRoomFor(slot, itemId), remaining);
|
||||||
slot.state.amount += take;
|
slot.state.amount += take;
|
||||||
remaining -= take;
|
remaining -= take;
|
||||||
if (remaining === 0) return true;
|
slotIds.push(id);
|
||||||
|
if (remaining === 0) {
|
||||||
|
this.emit('add', { itemId, amount, slotIds });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const slot of this.slots.values()) {
|
for (const [id, slot] of this.slots) {
|
||||||
if (slot.state === null) {
|
if (slot.state === null) {
|
||||||
const take = Math.min(this.slotCapFor(slot, itemId), remaining);
|
const take = Math.min(this.slotCapFor(slot, itemId), remaining);
|
||||||
slot.state = { itemId, amount: take };
|
slot.state = { itemId, amount: take };
|
||||||
remaining -= take;
|
remaining -= take;
|
||||||
if (remaining === 0) return true;
|
slotIds.push(id);
|
||||||
|
if (remaining === 0) {
|
||||||
|
this.emit('add', { itemId, amount, slotIds });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -88,7 +98,7 @@ export class Inventory extends RPGComponentBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
removeItem({ itemId, amount, slotId }: SlotUpdateArgs): boolean {
|
remove({ itemId, amount, slotId }: SlotUpdateArgs): boolean {
|
||||||
if (amount < 0) return false;
|
if (amount < 0) return false;
|
||||||
if (amount === 0) return true;
|
if (amount === 0) return true;
|
||||||
|
|
||||||
|
|
@ -98,23 +108,34 @@ export class Inventory extends RPGComponentBase {
|
||||||
if (slot.state.amount < amount) return false;
|
if (slot.state.amount < amount) return false;
|
||||||
slot.state.amount -= amount;
|
slot.state.amount -= amount;
|
||||||
if (slot.state.amount === 0) slot.state = null;
|
if (slot.state.amount === 0) slot.state = null;
|
||||||
|
this.emit('remove', { itemId, amount, slotIds: [slotId] });
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.getAmount(itemId) < amount) return false;
|
if (this.getAmount(itemId) < amount) return false;
|
||||||
|
|
||||||
let remaining = amount;
|
let remaining = amount;
|
||||||
for (const slot of this.slots.values()) {
|
const slotIds: SlotId[] = [];
|
||||||
|
for (const [id, slot] of this.slots) {
|
||||||
if (slot.state?.itemId === itemId) {
|
if (slot.state?.itemId === itemId) {
|
||||||
const take = Math.min(slot.state.amount, remaining);
|
const take = Math.min(slot.state.amount, remaining);
|
||||||
slot.state.amount -= take;
|
slot.state.amount -= take;
|
||||||
if (slot.state.amount === 0) slot.state = null;
|
if (slot.state.amount === 0) slot.state = null;
|
||||||
remaining -= take;
|
remaining -= take;
|
||||||
if (remaining === 0) return true;
|
slotIds.push(id);
|
||||||
|
if (remaining === 0) {
|
||||||
|
this.emit('remove', { itemId, amount, slotIds });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return remaining === 0;
|
if (remaining === 0) {
|
||||||
|
this.emit('remove', { itemId, amount, slotIds });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
getAmount(itemId: string, slotId?: SlotId): number {
|
getAmount(itemId: string, slotId?: SlotId): number {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { action, variable } from "../decorators";
|
import { action, variable } from "../decorators";
|
||||||
import { RPGComponentBase } from "./entity";
|
import { RPGComponent } from "./entity";
|
||||||
|
|
||||||
export class Stat extends RPGComponentBase {
|
export class Stat extends RPGComponent {
|
||||||
@variable private value: number;
|
@variable private value: number;
|
||||||
@variable private maxValue: number | undefined;
|
@variable private maxValue: number | undefined;
|
||||||
|
|
||||||
|
|
@ -13,9 +13,13 @@ export class Stat extends RPGComponentBase {
|
||||||
|
|
||||||
@action
|
@action
|
||||||
update(amount: number) {
|
update(amount: number) {
|
||||||
|
const prev = this.value;
|
||||||
this.value = Math.max(0, this.value + amount);
|
this.value = Math.max(0, this.value + amount);
|
||||||
if (this.maxValue) {
|
if (this.maxValue != null) {
|
||||||
this.value = Math.min(this.value, this.maxValue);
|
this.value = Math.min(this.value, this.maxValue);
|
||||||
}
|
}
|
||||||
|
if (prev !== this.value) {
|
||||||
|
this.emit('update', { prev, value: this.value });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import type { RPGVariables } from "../types";
|
import type { RPGVariables } from "../types";
|
||||||
import { action } from "../decorators";
|
import { action } from "../decorators";
|
||||||
import { RPGComponentBase } from "./entity";
|
import { RPGComponent } from "./entity";
|
||||||
|
|
||||||
interface Var {
|
interface Var {
|
||||||
key: string;
|
key: string;
|
||||||
value: RPGVariables[string];
|
value: RPGVariables[string];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Variables extends RPGComponentBase {
|
export class Variables extends RPGComponent {
|
||||||
private readonly variables: RPGVariables = {};
|
private readonly variables: RPGVariables = {};
|
||||||
|
|
||||||
override getVariables() {
|
override getVariables() {
|
||||||
|
|
@ -16,13 +16,19 @@ export class Variables extends RPGComponentBase {
|
||||||
|
|
||||||
@action
|
@action
|
||||||
set({ key, value }: Var) {
|
set({ key, value }: Var) {
|
||||||
|
const prev = this.variables[key];
|
||||||
this.variables[key] = value;
|
this.variables[key] = value;
|
||||||
|
|
||||||
|
this.emit('set', { key, value, prev });
|
||||||
|
|
||||||
return this.variables;
|
return this.variables;
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
unset(key: string) {
|
unset(key: string) {
|
||||||
|
const prev = this.variables[key];
|
||||||
delete this.variables[key];
|
delete this.variables[key];
|
||||||
|
this.emit('unset', { key, prev });
|
||||||
return this.variables;
|
return this.variables;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -30,7 +36,7 @@ export class Variables extends RPGComponentBase {
|
||||||
increment({ key, value }: Var) {
|
increment({ key, value }: Var) {
|
||||||
const currentValue = this.variables[key] ?? 0;
|
const currentValue = this.variables[key] ?? 0;
|
||||||
if (typeof currentValue === 'number' && typeof value === 'number') {
|
if (typeof currentValue === 'number' && typeof value === 'number') {
|
||||||
this.variables[key] = currentValue + value;
|
this.set({ key, value: currentValue + value });
|
||||||
}
|
}
|
||||||
return this.variables;
|
return this.variables;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ export namespace Dialogs {
|
||||||
actions.add(action.type);
|
actions.add(action.type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return Array.from(actions);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function validate(
|
export function validate(
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { RPGComponentBase } from "./components/entity";
|
import { RPGComponent } from "./components/entity";
|
||||||
import { evaluateConditions } from "./conditions";
|
import { evaluateCondition, parseCondition } from "./conditions";
|
||||||
import { action, variable } from "./decorators";
|
import { action, variable } from "./decorators";
|
||||||
import {
|
import {
|
||||||
isQuest,
|
isQuest,
|
||||||
|
|
@ -39,7 +39,7 @@ export namespace Quests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class QuestEngine extends RPGComponentBase {
|
export class QuestEngine extends RPGComponent {
|
||||||
@variable('status') private _status: QuestStatus = 'inactive';
|
@variable('status') private _status: QuestStatus = 'inactive';
|
||||||
@variable('stage') private _stageIndex: number = 0;
|
@variable('stage') private _stageIndex: number = 0;
|
||||||
|
|
||||||
|
|
@ -54,14 +54,23 @@ export class QuestEngine extends RPGComponentBase {
|
||||||
return this.quest.id;
|
return this.quest.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private evaluateConditions(conditions: string[] | undefined) {
|
||||||
|
const variables = this.options.getVariables();
|
||||||
|
return (conditions ?? []).every(condition => {
|
||||||
|
const parsed = parseCondition(condition);
|
||||||
|
return evaluateCondition({ ...parsed, variable: this.resolve(parsed.variable) }, variables);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
isAvailable(): boolean {
|
isAvailable(): boolean {
|
||||||
return evaluateConditions(this.quest.conditions ?? [], this.options.getVariables());
|
return this.evaluateConditions(this.quest.conditions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
start(): void {
|
start(): void {
|
||||||
this._status = 'active';
|
this._status = 'active';
|
||||||
this._stageIndex = 0;
|
this._stageIndex = 0;
|
||||||
|
this.emit('started');
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
|
@ -71,7 +80,7 @@ export class QuestEngine extends RPGComponentBase {
|
||||||
const stage = this.quest.stages[this._stageIndex];
|
const stage = this.quest.stages[this._stageIndex];
|
||||||
if (!stage) return;
|
if (!stage) return;
|
||||||
|
|
||||||
const allDone = evaluateConditions(stage.objectives.map(obj => obj.condition), this.options.getVariables());
|
const allDone = this.evaluateConditions(stage.objectives.map(obj => obj.condition));
|
||||||
|
|
||||||
if (!allDone) return;
|
if (!allDone) return;
|
||||||
|
|
||||||
|
|
@ -82,8 +91,10 @@ export class QuestEngine extends RPGComponentBase {
|
||||||
|
|
||||||
if (this._stageIndex + 1 < this.quest.stages.length) {
|
if (this._stageIndex + 1 < this.quest.stages.length) {
|
||||||
this._stageIndex++;
|
this._stageIndex++;
|
||||||
|
this.emit('stage', { index: this._stageIndex, stage: this.quest.stages[this._stageIndex] });
|
||||||
} else {
|
} else {
|
||||||
this._status = 'completed';
|
this._status = 'completed';
|
||||||
|
this.emit('completed');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -96,7 +107,7 @@ export class QuestEngine extends RPGComponentBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class QuestManager extends RPGComponentBase {
|
export class QuestManager extends RPGComponent {
|
||||||
private readonly engines: Map<string, QuestEngine>;
|
private readonly engines: Map<string, QuestEngine>;
|
||||||
|
|
||||||
constructor(quests: Quest[], options: QuestRuntimeOptions) {
|
constructor(quests: Quest[], options: QuestRuntimeOptions) {
|
||||||
|
|
@ -104,6 +115,12 @@ export class QuestManager extends RPGComponentBase {
|
||||||
this.engines = new Map(quests.map(q => [q.id, new QuestEngine(q, options)]));
|
this.engines = new Map(quests.map(q => [q.id, new QuestEngine(q, options)]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override onAttach(): void {
|
||||||
|
for (const [questId, engine] of this.engines) {
|
||||||
|
engine.attach(this._ctx!, `${this.path}.${questId}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
start(questId: string): void {
|
start(questId: string): void {
|
||||||
this.engines.get(questId)?.start();
|
this.engines.get(questId)?.start();
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ import { Variables } from "@common/rpg/components/variables";
|
||||||
import { QuestManager } from "@common/rpg/quest";
|
import { QuestManager } from "@common/rpg/quest";
|
||||||
|
|
||||||
export default async function main() {
|
export default async function main() {
|
||||||
const game = new RPGEntity();
|
const game = new RPGEntity('game');
|
||||||
const player = new RPGEntity();
|
const player = new RPGEntity('player');
|
||||||
const inventory = new Inventory(['head', 'legs']);
|
const inventory = new Inventory(['head', 'legs']);
|
||||||
const quests = new QuestManager([{
|
const quests = new QuestManager([{
|
||||||
id: 'test',
|
id: 'test',
|
||||||
|
|
@ -23,7 +23,7 @@ export default async function main() {
|
||||||
player.addComponent('health', new Stat(100));
|
player.addComponent('health', new Stat(100));
|
||||||
console.log(game.getActions());
|
console.log(game.getActions());
|
||||||
|
|
||||||
inventory.addItem({
|
inventory.add({
|
||||||
itemId: 'helmet',
|
itemId: 'helmet',
|
||||||
amount: 1,
|
amount: 1,
|
||||||
slotId: 'head',
|
slotId: 'head',
|
||||||
|
|
@ -32,7 +32,7 @@ export default async function main() {
|
||||||
itemId: 'boots',
|
itemId: 'boots',
|
||||||
amount: 2,
|
amount: 2,
|
||||||
});
|
});
|
||||||
inventory.addItem({
|
inventory.add({
|
||||||
itemId: 'belt',
|
itemId: 'belt',
|
||||||
amount: 1,
|
amount: 1,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue