From 5db78d3ff15074727938752800d0aeb6e9b5b6d9 Mon Sep 17 00:00:00 2001 From: Misode Date: Wed, 27 May 2020 01:09:15 +0200 Subject: [PATCH] Add filtered node + schema selector --- public/index.html | 2 + src/app/app.ts | 74 +++++------------ src/model/DataModel.ts | 5 +- src/nodes/FilteredNode.ts | 53 ++++++++++++ src/schemas/PredicateSchema.ts | 147 +++++++++++++++++++++++++++++++++ src/schemas/SandboxSchema.ts | 53 ++++++++++++ 6 files changed, 280 insertions(+), 54 deletions(-) create mode 100644 src/nodes/FilteredNode.ts create mode 100644 src/schemas/PredicateSchema.ts create mode 100644 src/schemas/SandboxSchema.ts diff --git a/public/index.html b/public/index.html index 5a00546e..6fc1b4de 100644 --- a/public/index.html +++ b/public/index.html @@ -6,6 +6,8 @@ Document + +

diff --git a/src/app/app.ts b/src/app/app.ts index cdfa39e1..bc12fb33 100644 --- a/src/app/app.ts +++ b/src/app/app.ts @@ -1,61 +1,31 @@ -import { RootNode } from '../nodes/RootNode' -import { EnumNode } from '../nodes/EnumNode' -import { ObjectNode } from '../nodes/ObjectNode' -import { StringNode } from '../nodes/StringNode' import { DataModel } from '../model/DataModel' import { TreeView } from '../view/TreeView' import { SourceView } from '../view/SourceView' -import { ListNode } from '../nodes/ListNode' -import { BooleanNode } from '../nodes/BooleanNode' -import { MapNode } from '../nodes/MapNode' -import { NumberNode } from '../nodes/NumberNode' -import { RangeNode } from '../nodes/custom/RangeNode' -import { ResourceNode } from '../nodes/custom/ResourceNode' +import { PredicateSchema } from '../schemas/PredicateSchema' +import { SandboxSchema } from '../schemas/SandboxSchema' -const EntityCollection = ['sheep', 'pig'] +const predicateModel = new DataModel(PredicateSchema) +const sandboxModel = new DataModel(SandboxSchema) -const predicateTree = new RootNode('predicate', { - condition: new EnumNode(['foo', 'bar'], { - default: () => 'bar', - transform: (s: string) => (s === 'foo') ? {test: 'baz'} : s - }), - number: new NumberNode({integer: false, min: 0}), - range: new RangeNode({ - enable: (path) => path.push('condition').get() === 'foo' - }), - predicate: new ObjectNode({ - type: new EnumNode(EntityCollection), - nbt: new ResourceNode({ - default: (v) => "hahaha" - }), - test: new BooleanNode({force: () => true}), - recipes: new MapNode( - new StringNode(), - new RangeNode({ - default: (v) => RangeNode.isExact(v) ? 2 : v - }) - ) - }), - effects: new ListNode( - new ObjectNode({ - type: new EnumNode(EntityCollection), - nbt: new StringNode() - }, { - default: () => ({ - type: 'sheep' - }) - }) - ) -}, { - default: () => ({ - condition: 'foo', - predicate: { - nbt: 'hi' - } - }) -}); +let model = predicateModel -const model = new DataModel(predicateTree) +const modelSelector = document.createElement('select') +modelSelector.value = 'predicate' +modelSelector.innerHTML = ` + + ` +modelSelector.addEventListener('change', evt => { + console.log("hello?") + if (modelSelector.value === 'sandbox') { + model = sandboxModel + } else { + model = predicateModel + } + new TreeView(model, document!.getElementById('view')!) + new SourceView(model, document!.getElementById('source')!) + model.invalidate() +}) +document.getElementById('header')?.append(modelSelector) new TreeView(model, document!.getElementById('view')!) new SourceView(model, document!.getElementById('source')!) diff --git a/src/model/DataModel.ts b/src/model/DataModel.ts index f5e3a5c6..63f83562 100644 --- a/src/model/DataModel.ts +++ b/src/model/DataModel.ts @@ -1,5 +1,6 @@ import { RootNode } from "../nodes/RootNode" import { Path } from "./Path" +import { INode } from "../nodes/AbstractNode" export interface ModelListener { invalidated(model: DataModel): void @@ -7,10 +8,10 @@ export interface ModelListener { export class DataModel { data: any - schema: RootNode + schema: INode listeners: ModelListener[] - constructor(schema: RootNode) { + constructor(schema: INode) { this.schema = schema this.data = schema.default() this.listeners = [] diff --git a/src/nodes/FilteredNode.ts b/src/nodes/FilteredNode.ts new file mode 100644 index 00000000..4f9f63f8 --- /dev/null +++ b/src/nodes/FilteredNode.ts @@ -0,0 +1,53 @@ +import { NodeMods, INode, NodeChildren, AbstractNode, RenderOptions } from './AbstractNode' +import { IObject } from './ObjectNode' +import { Path } from '../model/Path' +import { TreeView } from '../view/TreeView' + +export const Switch = Symbol('switch') + +type NestedNodeChildren = { + [name: string]: NodeChildren +} + +export type FilteredChildren = { + [name: string]: INode + [Switch]: NestedNodeChildren +} + +export class FilteredNode extends AbstractNode { + filter: string + fields: NodeChildren + cases: NestedNodeChildren + + constructor(filter: string, fields: FilteredChildren, mods?: NodeMods) { + super(mods) + this.filter = filter; + const {[Switch]: _switch, ..._fields} = fields + this.fields = _fields + this.cases = _switch + } + + transform(path: Path, value: IObject) { + if (value === undefined) return undefined + value = value ?? {} + const activeCase = this.cases[value[this.filter]] ?? [] + const activeFields = {...this.fields, ...activeCase} + let res: any = {} + Object.keys(activeFields).forEach(f => + res[f] = activeFields[f].transform(path.push(f), value[f]) + ) + return res; + } + + renderRaw(path: Path, value: IObject, view: TreeView, options?: RenderOptions) { + if (value === undefined) return `` + const activeCase = this.cases[value[this.filter]] ?? [] + const activeFields = {...this.fields, ...activeCase} + return `${options?.hideLabel ? `` : ` +
`} + ${Object.keys(activeFields).map(f => { + return activeFields[f].render(path.push(f), value[f], view) + }).join('')} + ${options?.hideLabel ? `` : `
`}` + } +} diff --git a/src/schemas/PredicateSchema.ts b/src/schemas/PredicateSchema.ts new file mode 100644 index 00000000..41c2318e --- /dev/null +++ b/src/schemas/PredicateSchema.ts @@ -0,0 +1,147 @@ +import { EnumNode } from '../nodes/EnumNode'; +import { ResourceNode } from '../nodes/custom/ResourceNode'; +import { NumberNode } from '../nodes/NumberNode'; +import { BooleanNode } from '../nodes/BooleanNode'; +import { FilteredNode, Switch } from '../nodes/FilteredNode'; +import { ListNode } from '../nodes/ListNode'; +import { RangeNode } from '../nodes/custom/RangeNode'; +import { MapNode } from '../nodes/MapNode'; +import { StringNode } from '../nodes/StringNode'; +import { INode } from '../nodes/AbstractNode'; + +const conditions = [ + 'alternative', + 'requirements', + 'inverted', + 'reference', + 'entity_properties', + 'block_state_property', + 'match_tool', + 'damage_source_properties', + 'location_check', + 'weather_check', + 'time_check', + 'entity_scores', + 'random_chance', + 'random_chance_with_looting', + 'table_bonus', + 'killed_by_player', + 'survives_explosion' +] + +const enchantments = [ + 'aqua_affinity', + 'bane_of_arthropods', + 'blast_protection', + 'channeling', + 'binding_curse', + 'vanishing_curse', + 'depth_strider', + 'efficiency', + 'feather_falling', + 'fire_aspect', + 'fire_protection', + 'flame', + 'fortune', + 'frost_walker', + 'impaling', + 'infinity', + 'knockback', + 'looting', + 'loyalty', + 'luck_of_the_sea', + 'lure', + 'mending', + 'multishot', + 'piercing', + 'power', + 'projectile_protection', + 'protection', + 'punch', + 'quick_charge', + 'respiration', + 'riptide', + 'sharpness', + 'silk_touch', + 'smite', + 'sweeping', + 'thorns', + 'unbreaking' +] + +const entitySources = ['this', 'killer', 'killer_player'] + +export let PredicateSchema: FilteredNode +PredicateSchema = new FilteredNode('condition', { + condition: new EnumNode(conditions, 'random_chance'), + [Switch]: { + 'alternative': { + // terms: new ListNode(PredicateSchema()), + }, + 'block_state_property': { + block: new ResourceNode(), + properties: new MapNode( + new StringNode(), + new StringNode() + ), + }, + 'damage_source_properties': { + // predicate: DamageSchema, + }, + 'entity_properties': { + entity: new EnumNode(entitySources, 'this'), + // predicate: EntitySchema, + }, + 'entity_scores': { + entity: new EnumNode(entitySources, 'this'), + scores: new MapNode( + new StringNode(), + new RangeNode() + ) + }, + 'inverted': { + // term: PredicateSchema, + }, + 'killed_by_player': { + inverse: new BooleanNode() + }, + 'location_check': { + offsetX: new NumberNode({integer: true}), + offsetY: new NumberNode({integer: true}), + offsetZ: new NumberNode({integer: true}), + // predicate: LocationSchema, + }, + 'match_tool': { + // predicate: ItemSchema, + }, + 'random_chance': { + chance: new NumberNode({min: 0, max: 1}), + }, + 'random_chance_with_looting': { + chance: new NumberNode({min: 0, max: 1}), + looting_multiplier: new NumberNode(), + }, + 'reference': { + name: new StringNode(), + }, + 'table_bonus': { + enchantment: new EnumNode(enchantments), + chances: new ListNode( + new NumberNode({min: 0, max: 1}) + ), + }, + 'time_check': { + value: new RangeNode(), + period: new NumberNode(), + }, + 'weather_check': { + raining: new BooleanNode(), + thrundering: new BooleanNode(), + } + } +}, { + default: () => ({ + condition: 'random_chance', + chance: 0.5 + }) +}) diff --git a/src/schemas/SandboxSchema.ts b/src/schemas/SandboxSchema.ts new file mode 100644 index 00000000..457e1eb8 --- /dev/null +++ b/src/schemas/SandboxSchema.ts @@ -0,0 +1,53 @@ +import { ObjectNode } from '../nodes/ObjectNode'; +import { EnumNode } from '../nodes/EnumNode'; +import { ResourceNode } from '../nodes/custom/ResourceNode'; +import { NumberNode } from '../nodes/NumberNode'; +import { BooleanNode } from '../nodes/BooleanNode'; +import { RootNode } from '../nodes/RootNode'; +import { RangeNode } from '../nodes/custom/RangeNode'; +import { MapNode } from '../nodes/MapNode'; +import { StringNode } from '../nodes/StringNode'; +import { ListNode } from '../nodes/ListNode'; + +const EntityCollection = ['sheep', 'pig'] + +export const SandboxSchema = new RootNode('predicate', { + condition: new EnumNode(['foo', 'bar'], { + default: () => 'bar', + transform: (s: string) => (s === 'foo') ? {test: 'baz'} : s + }), + number: new NumberNode({integer: false, min: 0}), + range: new RangeNode({ + enable: (path) => path.push('condition').get() === 'foo' + }), + predicate: new ObjectNode({ + type: new EnumNode(EntityCollection), + nbt: new ResourceNode({ + default: (v) => 'hahaha' + }), + test: new BooleanNode({force: () => true}), + recipes: new MapNode( + new StringNode(), + new RangeNode({ + default: (v) => RangeNode.isExact(v) ? 2 : v + }) + ) + }), + effects: new ListNode( + new ObjectNode({ + type: new EnumNode(EntityCollection), + nbt: new StringNode() + }, { + default: () => ({ + type: 'sheep' + }) + }) + ) +}, { + default: () => ({ + condition: 'foo', + predicate: { + nbt: 'hi' + } + }) +});