Add loot table schema

This commit is contained in:
Misode
2020-05-30 20:54:27 +02:00
parent c91e0a5300
commit e7fb692ac4
4 changed files with 351 additions and 4 deletions

View File

@@ -2,22 +2,27 @@ import { DataModel } from '../model/DataModel'
import { TreeView } from '../view/TreeView'
import { SourceView } from '../view/SourceView'
import { ConditionSchema } from '../minecraft/schemas/Condition'
import { LootTableSchema } from '../minecraft/schemas/LootTable'
import { SandboxSchema } from './Sandbox'
import { LOCALES } from '../Registries'
const predicateModel = new DataModel(ConditionSchema)
const lootTableModel = new DataModel(LootTableSchema)
const sandboxModel = new DataModel(SandboxSchema)
let model = predicateModel
let model = lootTableModel
const modelSelector = document.createElement('select')
modelSelector.value = 'predicate'
modelSelector.innerHTML = `
<option value="loot-table">Loot Table</option>
<option value="predicate">Predicate</option>
<option value="sandbox">Sandbox</option>`
modelSelector.addEventListener('change', evt => {
if (modelSelector.value === 'sandbox') {
model = sandboxModel
} else if (modelSelector.value === 'loot-table') {
model = lootTableModel
} else {
model = predicateModel
}

View File

@@ -20,6 +20,40 @@ COLLECTIONS.register('conditions', [
'survives_explosion'
])
COLLECTIONS.register('loot-entries', [
'empty',
'item',
'tag',
'loot_table',
'alternatives',
'sequence',
'group',
'dynamic'
])
COLLECTIONS.register('loot-functions', [
'set_count',
'set_damage',
'set_name',
'set_lore',
'set_nbt',
'set_attributes',
'set_contents',
'enchant_randomly',
'enchant_with_levels',
'looting_enchant',
'limit_count',
'furnace_smelt',
'explosion_decay',
'fill_player_head',
'copy_name',
'copy_nbt',
'copy_state',
'apply_bonus',
'exploration_map',
'set_stew_effect'
])
COLLECTIONS.register('enchantments', [
'aqua_affinity',
'bane_of_arthropods',
@@ -232,3 +266,56 @@ COLLECTIONS.register('entity-sources', [
'killer',
'killer_player'
])
COLLECTIONS.register('copy-sources', [
'block_entity',
'this',
'killer',
'killer_player'
])
COLLECTIONS.register('attributes', [
'generic.max_health',
'generic.follow_range',
'generic.knockback_resistance',
'generic.movement_speed',
'generic.attack_damage',
'generic.armor',
'generic.armor_toughness',
'generic.attack_speed',
'generic.luck',
'horse.jump_strength',
'generic.attack_knockback',
'generic.flying_speed',
'zombie.spawn_reinforcements'
])
COLLECTIONS.register('map-decorations', [
'mansion',
'monument',
'player',
'frame',
'red_marker',
'blue_marker',
'target_x',
'target_point',
'player_off_map',
'player_off_limits',
'red_x',
'banner_white',
'banner_orange',
'banner_magenta',
'banner_light_blue',
'banner_yellow',
'banner_lime',
'banner_pink',
'banner_gray',
'banner_light_gray',
'banner_cyan',
'banner_purple',
'banner_blue',
'banner_brown',
'banner_green',
'banner_red',
'banner_black'
])

View File

@@ -0,0 +1,247 @@
import { EnumNode } from '../../nodes/EnumNode';
import { ResourceNode } from '../nodes/ResourceNode';
import { NumberNode } from '../../nodes/NumberNode';
import { BooleanNode } from '../../nodes/BooleanNode';
import { ObjectNode, Switch, Case } from '../../nodes/ObjectNode';
import { ListNode } from '../../nodes/ListNode';
import { RangeNode } from '../nodes/RangeNode';
import { MapNode } from '../../nodes/MapNode';
import { StringNode } from '../../nodes/StringNode';
import { ReferenceNode } from '../../nodes/ReferenceNode';
import { SCHEMAS, COLLECTIONS } from '../../Registries';
import './Predicates'
const conditions = {
conditions: new ListNode(
new ReferenceNode('condition')
)
}
const functionsAndConditions = {
functions: new ListNode(
new ReferenceNode('loot-function')
),
...conditions
}
SCHEMAS.register('loot-table', new ObjectNode({
pools: new ListNode(
new ObjectNode({
rolls: new RangeNode(),
entries: new ListNode(
new ReferenceNode('loot-entry')
)
})
),
...functionsAndConditions
}, {
default: () => ({
pools: [
{
rolls: 1,
entries: [
{
type: 'item',
name: 'minecraft:stone'
}
]
}
]
})
}))
SCHEMAS.register('loot-entry', new ObjectNode({
type: new EnumNode(COLLECTIONS.get('loot-entries'), {default: () => 'item'}),
weight: new NumberNode({
integer: true,
min: 1,
enable: path => path.pop().get()?.length > 1
&& !['alternatives', 'group', 'sequence'].includes(path.push('type').get())
}),
[Switch]: 'type',
[Case]: {
'alternatives': {
children: new ListNode(
new ReferenceNode('loot-entry')
),
...functionsAndConditions
},
'dynamic': {
name: new StringNode(),
...functionsAndConditions
},
'group': {
children: new ListNode(
new ReferenceNode('loot-entry')
),
...functionsAndConditions
},
'item': {
name: new StringNode(),
...functionsAndConditions
},
'loot_table': {
name: new StringNode(),
...functionsAndConditions
},
'sequence': {
children: new ListNode(
new ReferenceNode('loot-entry')
),
...functionsAndConditions
},
'tag': {
name: new StringNode(),
expand: new BooleanNode(),
...functionsAndConditions
}
}
}))
SCHEMAS.register('loot-function', new ObjectNode({
function: new EnumNode(COLLECTIONS.get('loot-functions'), {default: () => 'set_count'}),
[Switch]: 'function',
[Case]: {
'apply_bonus': {
enchantment: new EnumNode(COLLECTIONS.get('enchantments')),
formula: new EnumNode([
'uniform_bonus_count',
'binomial_with_bonus_count',
'ore_drops'
]),
parameters: new ObjectNode({
bonusMultiplier: new NumberNode({
enable: path => path.pop().push('formula').get() === 'uniform_bonus_count'
}),
extra: new NumberNode({
enable: path => path.pop().push('formula').get() === 'binomial_with_bonus_count'
}),
probability: new NumberNode({
enable: path => path.pop().push('formula').get() === 'binomial_with_bonus_count'
})
}, {
enable: path => path.push('formula').get() !== 'ore_drops'
}),
...conditions
},
'copy_name': {
source: new EnumNode(COLLECTIONS.get('copy-sources')),
...conditions
},
'copy_nbt': {
source: new EnumNode(COLLECTIONS.get('copy-sources')),
ops: new ListNode(
new ObjectNode({
source: new StringNode(),
target: new StringNode(),
op: new EnumNode(['replace', 'append', 'merge'])
})
),
...conditions
},
'copy_state': {
block: new ResourceNode(COLLECTIONS.get('blocks')),
properties: new ListNode(
new StringNode()
),
...conditions
},
'enchant_randomly': {
enchantments: new ListNode(
new EnumNode(COLLECTIONS.get('enchantments'))
),
...conditions
},
'enchant_with_levels': {
levels: new RangeNode(),
treasure: new BooleanNode(),
...conditions
},
'exploration_map': {
destination: new EnumNode(COLLECTIONS.get('structures')),
decoration: new EnumNode(COLLECTIONS.get('map-decorations')),
zoom: new NumberNode({integer: true}),
search_radius: new NumberNode({integer: true}),
skip_existing_chunks: new BooleanNode(),
...conditions
},
'fill_player_head': {
entity: new EnumNode(COLLECTIONS.get('entity-sources')),
...conditions
},
'limit_count': {
limit: new RangeNode(),
...conditions
},
'looting_enchant': {
count: new RangeNode(),
limit: new NumberNode({integer: true}),
...conditions
},
'set_attributes': {
modifiers: new ListNode(
new ReferenceNode('attribute-modifier')
),
...conditions
},
'set_contents': {
entries: new ListNode(
new ReferenceNode('loot-entry')
),
...conditions
},
'set_count': {
count: new RangeNode(),
...conditions
},
'set_damage': {
damage: new RangeNode(),
...conditions
},
'set_lore': {
entity: new EnumNode(COLLECTIONS.get('entity-sources')),
lore: new ListNode(
new StringNode()
),
replace: new BooleanNode(),
...conditions
},
'set_name': {
entity: new EnumNode(COLLECTIONS.get('entity-sources')),
name: new StringNode(),
...conditions
},
'set_nbt': {
tag: new StringNode(),
...conditions
},
'set_stew_effect': {
effects: new ListNode(
new ReferenceNode('potion-effect')
),
...conditions
}
}
}, {
default: () => ({
function: 'set_count',
count: 1
})
}))
SCHEMAS.register('attribute-modifier', new ObjectNode({
attribute: new EnumNode(COLLECTIONS.get('attributes')),
name: new StringNode(),
amount: new RangeNode(),
operation: new EnumNode([
'addition',
'multiply_base',
'multiply_total'
]),
slot: new ListNode(
new EnumNode(COLLECTIONS.get('slots'))
)
}))
export const LootTableSchema = SCHEMAS.get('loot-table')

View File

@@ -1,6 +1,11 @@
import { DataModel, ModelListener } from "../model/DataModel"
import { Path } from "../model/Path"
type SourceViewOptions = {
indentation?: number | string,
rows?: number
}
/**
* JSON representation view of the model.
* Renders the result in a <textarea>.
@@ -8,14 +13,17 @@ import { Path } from "../model/Path"
export class SourceView implements ModelListener {
model: DataModel
target: HTMLElement
options?: SourceViewOptions
/**
* @param model data model this view represents and listens to
* @param target DOM element to render the view
* @param options optional options for the view
*/
constructor(model: DataModel, target: HTMLElement) {
constructor(model: DataModel, target: HTMLElement, options?: SourceViewOptions) {
this.model = model
this.target = target
this.options = options
model.addListener(this)
}
@@ -23,8 +31,8 @@ export class SourceView implements ModelListener {
const transformed = this.model.schema.transform(new Path([], this.model), this.model.data)
const textarea = document.createElement('textarea')
textarea.style.width = 'calc(100% - 6px)'
textarea.rows = 10
textarea.textContent = JSON.stringify(transformed)
textarea.rows = this.options?.rows ?? 20
textarea.textContent = JSON.stringify(transformed, null, this.options?.indentation)
textarea.addEventListener('change', evt => {
const parsed = JSON.parse(textarea.value)
this.model.reset(parsed)