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'
+ }
+ })
+});