Moved inventory computation to after toggles, added class levels computation
This commit is contained in:
@@ -3,8 +3,11 @@ import walkDown from '/imports/api/creature/computation/newEngine/utility/walkdo
|
||||
export default function computeInactiveStatus(node){
|
||||
const prop = node.node;
|
||||
if (isActive(prop)) return;
|
||||
prop.inactive = true;
|
||||
prop.deactivatedBySelf = true;
|
||||
// Unequipped items disable their children, but are not disabled themselves
|
||||
if (prop.type !== 'item'){
|
||||
prop.inactive = true;
|
||||
prop.deactivatedBySelf = true;
|
||||
}
|
||||
// Mark children as inactive due to ancestor
|
||||
walkDown(node.children, child => {
|
||||
child.node.inactive = true;
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
/**
|
||||
* Performs a depth first traversal of the character tree, summing the container
|
||||
* and inventory contents on the way up the tree
|
||||
*/
|
||||
export default function computeInventory(forest, dependencyGraph){
|
||||
const data = {
|
||||
weightTotal: 0,
|
||||
weightEquipment: 0,
|
||||
weightCarried: 0,
|
||||
valueTotal: 0,
|
||||
valueEquipment: 0,
|
||||
valueCarried: 0,
|
||||
itemsAttuned: 0,
|
||||
};
|
||||
// The stack of properties to still navigate
|
||||
const stack = [...forest];
|
||||
// The current containers we are inside of
|
||||
const containerStack = [];
|
||||
|
||||
while(stack.length){
|
||||
const top = stack[stack.length - 1];
|
||||
const prop = top.node;
|
||||
if (prop._computationDetails.inventoryChildrenVisited){
|
||||
stack.pop();
|
||||
handleProp(prop, containerStack, data, dependencyGraph);
|
||||
} else {
|
||||
// Add all containers to the stack when we first visit them
|
||||
if (prop.type === 'container'){
|
||||
containerStack.push(top.node);
|
||||
setDefaultContainerData(prop);
|
||||
}
|
||||
// Push children onto the stack and mark this as children are visited
|
||||
stack.push(...top.children);
|
||||
prop._computationDetails.inventoryChildrenVisited = true;
|
||||
}
|
||||
}
|
||||
// Store all the computed values on the dependency graph variables
|
||||
for (let key in data){
|
||||
dependencyGraph.addNode(key, {engineValue: data[key]});
|
||||
}
|
||||
}
|
||||
|
||||
function setDefaultContainerData(container){
|
||||
container.contentsWeight = 0;
|
||||
container.carriedWeight = 0;
|
||||
container.contentsValue = 0;
|
||||
container.carriedValue = 0;
|
||||
}
|
||||
|
||||
function handleProp(prop, containerStack, data, dependencyGraph){
|
||||
// Determine if this property is carried, items are carried by default
|
||||
let carried = prop.type === 'container' ? prop.carried : true;
|
||||
|
||||
// Weight and value for this property
|
||||
const weight = (prop.weight || 0) + (prop.contentsWeight || 0);
|
||||
const carriedWeight = (prop.weight || 0) + (prop.carriedWeight || 0);
|
||||
const value = (prop.value || 0) + (prop.value || 0);
|
||||
const carriedValue = (prop.value || 0) + (prop.carriedValue || 0);
|
||||
|
||||
// Sum the item-specific data
|
||||
if (prop.type === 'item'){
|
||||
dependencyGraph.addLink('itemsAttuned', prop._id, 'inventory');
|
||||
if (prop.attuned) data.itemsAttuned += 1;
|
||||
if (prop.equipped){
|
||||
dependencyGraph.addLink('weightEquipment', prop._id, 'inventory');
|
||||
data.weightEquipment += weight;
|
||||
dependencyGraph.addLink('valueEquipment', prop._id, 'inventory');
|
||||
data.valueEquipment += value;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the parent container
|
||||
const container = containerStack[containerStack.length - 1];
|
||||
|
||||
if (container){
|
||||
// The container depends on this prop for its contents data
|
||||
dependencyGraph.addLink(container._id, prop._id, 'inventory');
|
||||
// Add this property's weights and values to the container
|
||||
if (!container.weightless){
|
||||
container.contentsWeight += weight;
|
||||
if (carried) container.carriedWeight += carriedWeight;
|
||||
}
|
||||
container.contentsValue += value;
|
||||
if (carried) container.carriedValue += carriedValue;
|
||||
} else {
|
||||
// There is no parent container, add weights/value to the character data
|
||||
dependencyGraph.addLink('weightTotal', prop._id, 'inventory');
|
||||
data.weightTotal += weight;
|
||||
dependencyGraph.addLink('valueTotal', prop._id, 'inventory');
|
||||
data.valueTotal += value;
|
||||
if (carried){
|
||||
dependencyGraph.addLink('weightCarried', prop._id, 'inventory');
|
||||
data.weightCarried += carriedWeight;
|
||||
dependencyGraph.addLink('valueCarried', prop._id, 'inventory');
|
||||
data.valueCarried += carriedValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
export default function computeSlotQuantityFilled(node, dependencyGraph){
|
||||
let slot = node.node;
|
||||
if (slot.type !== 'propertySlot' || slot.type !== 'characterClass') return;
|
||||
slot.totalFilled = 0;
|
||||
node.children.forEach(child => {
|
||||
let childProp = child.node;
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Performs a depth first traversal of the character tree, summing the container
|
||||
* and inventory contents on the way up the tree
|
||||
*/
|
||||
export default function linkInventory(forest, dependencyGraph){
|
||||
// The stack of properties to still navigate
|
||||
const stack = [...forest];
|
||||
// The current containers we are inside of
|
||||
const containerStack = [];
|
||||
|
||||
while(stack.length){
|
||||
const top = stack[stack.length - 1];
|
||||
const prop = top.node;
|
||||
if (prop._computationDetails.inventoryChildrenVisited){
|
||||
stack.pop();
|
||||
handleProp(prop, containerStack, dependencyGraph);
|
||||
} else {
|
||||
// Add all containers to the stack when we first visit them
|
||||
if (prop.type === 'container'){
|
||||
containerStack.push(top.node);
|
||||
}
|
||||
// Push children onto the stack and mark this as children are visited
|
||||
stack.push(...top.children);
|
||||
prop._computationDetails.inventoryChildrenVisited = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleProp(prop, containerStack, dependencyGraph){
|
||||
// Determine if this property is carried, items are carried by default
|
||||
let carried = prop.type === 'container' ? prop.carried : true;
|
||||
|
||||
// Item-specific links
|
||||
if (prop.type === 'item'){
|
||||
if (prop.attuned){
|
||||
dependencyGraph.addLink('itemsAttuned', prop._id, 'attunedItem');
|
||||
}
|
||||
if (prop.equipped){
|
||||
dependencyGraph.addLink('weightEquipment', prop._id, 'equippedItem');
|
||||
dependencyGraph.addLink('valueEquipment', prop._id, 'equippedItem');
|
||||
}
|
||||
}
|
||||
|
||||
// Get the parent container
|
||||
const container = containerStack[containerStack.length - 1];
|
||||
|
||||
if (container){
|
||||
// The container depends on this prop for its contents data
|
||||
dependencyGraph.addLink(container._id, prop._id, 'containerContents');
|
||||
} else {
|
||||
// There is no parent container, the character totals depend on this prop
|
||||
dependencyGraph.addLink('weightTotal', prop._id, 'inventoryStats');
|
||||
dependencyGraph.addLink('valueTotal', prop._id, 'inventoryStats');
|
||||
if (carried){
|
||||
dependencyGraph.addLink('weightCarried', prop._id, 'inventoryStats');
|
||||
dependencyGraph.addLink('valueCarried', prop._id, 'inventoryStats');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,8 @@ const linkDependenciesByType = {
|
||||
action: linkResources,
|
||||
attack: linkResources,
|
||||
attribute: linkAttribute,
|
||||
classLevel: linkVariableName,
|
||||
characterClass: linkVariableName,
|
||||
classLevel: linkClassLevel,
|
||||
constant: linkVariableName,
|
||||
damageMultiplier: linkDamageMultiplier,
|
||||
proficiency: linkStats,
|
||||
@@ -15,6 +16,18 @@ export default function linkTypeDependencies(dependencyGraph, prop){
|
||||
linkDependenciesByType[prop.type]?.(prop);
|
||||
}
|
||||
|
||||
function linkClassLevel(dependencyGraph, prop){
|
||||
// The variableName of the prop depends on the prop
|
||||
if (prop.variableName && prop.level){
|
||||
dependencyGraph.addLink(prop.variableName, prop._id, 'classLevel');
|
||||
// The level variable depends on the class variableName variable
|
||||
let existingLevelLink = dependencyGraph.getLink('level', prop.variableName);
|
||||
if (!existingLevelLink){
|
||||
dependencyGraph.addLink('level', prop.variableName, 'level');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function linkVariableName(dependencyGraph, prop){
|
||||
// The variableName of the prop depends on the prop
|
||||
if (prop.variableName){
|
||||
|
||||
@@ -7,7 +7,7 @@ import computedSchemas from '/imports/api/properties/computedPropertySchemasInde
|
||||
import applyFnToKey from '/imports/api/creature/computation/newEngine/utility/applyFnToKey.js';
|
||||
import { cloneDeep, unset } from 'lodash';
|
||||
import createGraph from 'ngraph.graph';
|
||||
import computeInventory from '/imports/api/creature/computation/newEngine/buildComputation/computeInventory.js';
|
||||
import linkInventory from '/imports/api/creature/computation/newEngine/buildComputation/linkInventory.js';
|
||||
import walkDown from '/imports/api/creature/computation/newEngine/utility/walkdown.js';
|
||||
import parseCalculationFields from '/imports/api/creature/computation/newEngine/buildComputation/parseCalculationFields.js';
|
||||
import computeInactiveStatus from '/imports/api/creature/computation/newEngine/buildComputation/computeInactiveStatus.js';
|
||||
@@ -36,19 +36,26 @@ import computeSlotQuantityFilled from '/imports/api/creature/computation/newEngi
|
||||
*/
|
||||
|
||||
export default function buildCreatureComputation(creatureId){
|
||||
let properties = CreatureProperties.find({
|
||||
const properties = getProperties(creatureId);
|
||||
return buildComputationFromProps(properties);
|
||||
}
|
||||
|
||||
function getProperties(creatureId){
|
||||
return CreatureProperties.find({
|
||||
'ancestors.id': creatureId,
|
||||
'removed': {$ne: true},
|
||||
}, {
|
||||
sort: {order: 1}
|
||||
});
|
||||
}
|
||||
|
||||
export function buildComputationFromProps(properties){
|
||||
// Dependency graph where edge(a, b) means a depends on b
|
||||
// The graph includes all dependencies even of inactive properties
|
||||
// such that any properties changing without changing their dependencies
|
||||
// can limit the recompute to connected parts of the graph
|
||||
// Each node's data represents a prop or a virtual prop like a variable
|
||||
// Each link's data: {type: String, data: Object, requiresComputation: Boolean}
|
||||
// Each link's data is a string representing the link type
|
||||
const dependencyGraph = createGraph();
|
||||
|
||||
const computation = {
|
||||
@@ -86,7 +93,7 @@ export default function buildCreatureComputation(creatureId){
|
||||
};
|
||||
|
||||
// Parse all the calculations
|
||||
parseCalculationFields(prop, computedSchemas)
|
||||
parseCalculationFields(prop, computedSchemas);
|
||||
});
|
||||
|
||||
// Get all the properties as trees based on their ancestors
|
||||
@@ -98,8 +105,8 @@ export default function buildCreatureComputation(creatureId){
|
||||
computeSlotQuantityFilled(node);
|
||||
});
|
||||
|
||||
// Compute the inventory
|
||||
computeInventory(forest, dependencyGraph);
|
||||
// Link the inventory dependencies
|
||||
linkInventory(forest, dependencyGraph);
|
||||
|
||||
// Graph functions that rely on the props being stored first
|
||||
properties.forEach(prop => {
|
||||
|
||||
@@ -2,6 +2,7 @@ import aggregate from '/imports/api/creature/computation/newEngine/computeComput
|
||||
import computeVariableAsAttribute from '/imports/api/creature/computation/newEngine/computeComputation/computeVariableAsType/computeVariableAsAttribute.js';
|
||||
import computeVariableAsSkill from '/imports/api/creature/computation/newEngine/computeComputation/computeVariableAsType/computeVariableAsSkill.js';
|
||||
import computeVariableAsConstant from '/imports/api/creature/computation/newEngine/computeComputation/computeByType/computeVariable/computeVariableAsConstant.js';
|
||||
import computeVariableAsClass from '/imports/api/creature/computation/newEngine/computeComputation/computeVariable/computeVariableAsClass.js';
|
||||
import computeImplicitVariable from '/imports/api/creature/computation/newEngine/computeComputation/computeVariable/computeImplicitVariable.js';
|
||||
|
||||
export default function computeVariable(graph, node, scope){
|
||||
@@ -26,9 +27,11 @@ function aggregateLinks(graph, node){
|
||||
if (linkedNode.data.inactive) return;
|
||||
// Apply all the aggregations
|
||||
let arg = {node, linkedNode, link};
|
||||
aggregate.definition(arg);
|
||||
aggregate.classLevel(arg);
|
||||
aggregate.damageMultiplier(arg);
|
||||
aggregate.definition(arg);
|
||||
aggregate.effect(arg);
|
||||
aggregate.inventory(arg);
|
||||
aggregate.proficiency(arg);
|
||||
},
|
||||
true // enumerate only outbound links
|
||||
@@ -50,6 +53,8 @@ function computeVariableProp(node, prop, scope){
|
||||
computeVariableAsSkill(node, prop, scope)
|
||||
} else if (prop.type === 'constant'){
|
||||
computeVariableAsConstant(node, prop, scope)
|
||||
} else if (prop.type === 'characterClass'){
|
||||
computeVariableAsClass(node, prop, scope)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
export default function aggregateClassLevel({node, linkedNode, link}){
|
||||
if (link.data === 'classLevel'){
|
||||
if (node.data.inactive) return;
|
||||
if (!node.data.classLevelAggregator) node.data.classLevelAggregator = {
|
||||
levelsFilled: [true], // Level 0 is always filled
|
||||
level: 0,
|
||||
};
|
||||
let linkedProp = linkedNode.data;
|
||||
let aggregator = node.data.classLevelAggregator;
|
||||
if (linkedProp.level > aggregator.level) aggregator.level = linkedProp.level;
|
||||
aggregator.levelsFilled[linkedProp.level] = true;
|
||||
} else if (link.data === 'level'){
|
||||
node.baseValue = (node.baseValue || 0) + node.data.classLevelAggregator.level;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
export default function aggregateInventory({node, linkedNode, link}){
|
||||
let linkedProp = linkedNode.data || {};
|
||||
const prop = node.data;
|
||||
|
||||
switch (link.data){
|
||||
case 'attunedItem':
|
||||
prop.baseValue = (prop.baseValue || 0) + 1;
|
||||
return;
|
||||
|
||||
case 'equippedItem':
|
||||
if (node.id === 'weightEquipment'){
|
||||
prop.baseValue = (prop.baseValue || 0) + weight(linkedProp);
|
||||
} else if (node.id === 'valueEquipment'){
|
||||
prop.baseValue = (prop.baseValue || 0) + value(linkedProp);
|
||||
}
|
||||
return;
|
||||
|
||||
case 'containerContents':
|
||||
// Add this property's weights and values to the container
|
||||
if (!prop.weightless){
|
||||
prop.contentsWeight = (prop.contentsWeight || 0) + weight(linkedProp);
|
||||
if (prop.carried){
|
||||
prop.carriedWeight = (prop.carriedWeight || 0) + carriedWeight(linkedProp);
|
||||
}
|
||||
}
|
||||
prop.contentsValue = (prop.contentsValue || 0) + value(linkedProp);
|
||||
if (prop.carried) {
|
||||
prop.carriedValue = (prop.carriedValue || 0) + carriedValue(linkedProp);
|
||||
}
|
||||
return;
|
||||
|
||||
case 'inventoryStats':
|
||||
if (node.id === 'weightTotal'){
|
||||
prop.baseValue = (prop.baseValue || 0) + weight(linkedProp);
|
||||
} else if (node.id === 'valueTotal'){
|
||||
prop.baseValue = (prop.baseValue || 0) + value(linkedProp);
|
||||
} else if (node.did === 'weightCarried'){
|
||||
prop.baseValue = (prop.baseValue || 0) + carriedWeight(linkedProp);
|
||||
} else if (node.did === 'valueCarried'){
|
||||
prop.carriedValue = (prop.carriedValue || 0) + carriedValue(linkedProp);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function weight(prop){
|
||||
return (prop.weight || 0) + (prop.contentsWeight || 0);
|
||||
}
|
||||
|
||||
function carriedWeight(prop){
|
||||
return (prop.weight || 0) + (prop.carriedWeight || 0);
|
||||
}
|
||||
|
||||
function value (prop){
|
||||
return (prop.value || 0) + (prop.value || 0);
|
||||
}
|
||||
|
||||
function carriedValue (prop){
|
||||
return (prop.value || 0) + (prop.carriedValue || 0);
|
||||
}
|
||||
@@ -2,10 +2,14 @@ import definition from './aggregateDefinition.js';
|
||||
import damageMultiplier from './aggregateDamageMultiplier.js';
|
||||
import effect from './aggregateEffect.js';
|
||||
import proficiency from './aggregateProficiency.js';
|
||||
import classLevel from './aggregateClassLevel.js';
|
||||
import inventory from './aggregateInventory.js';
|
||||
|
||||
export default Object.freeze({
|
||||
definition,
|
||||
classLevel,
|
||||
damageMultiplier,
|
||||
definition,
|
||||
effect,
|
||||
inventory,
|
||||
proficiency,
|
||||
});
|
||||
|
||||
@@ -11,25 +11,33 @@ import getAggregatorResult from '/imports/api/creature/computation/newEngine/com
|
||||
prop.value = result;
|
||||
prop.proficiency = node.data.proficiency;
|
||||
|
||||
// denormalise the aggregator fields
|
||||
const aggregator = node.data.effectAggregator;
|
||||
if (aggregator.advantage && !aggregator.disadvantage){
|
||||
prop.advantage = 1;
|
||||
} else if (aggregator.disadvantage && !aggregator.advantage){
|
||||
prop.advantage = -1;
|
||||
} else {
|
||||
prop.advantage = 0;
|
||||
// denormalise class level aggregator
|
||||
let classLevelAgg = node.data.classLevelAggregator;
|
||||
if (classLevelAgg){
|
||||
prop.level = classLevelAgg.level;
|
||||
}
|
||||
|
||||
// denormalise the effect aggregator fields
|
||||
const aggregator = node.data.effectAggregator;
|
||||
if (aggregator){
|
||||
if (aggregator.advantage && !aggregator.disadvantage){
|
||||
prop.advantage = 1;
|
||||
} else if (aggregator.disadvantage && !aggregator.advantage){
|
||||
prop.advantage = -1;
|
||||
} else {
|
||||
prop.advantage = 0;
|
||||
}
|
||||
// Passive bonus
|
||||
prop.passiveBonus = aggregator.passiveAdd;
|
||||
// conditional benefits
|
||||
prop.conditionalBenefits = aggregator.conditional;
|
||||
// Roll bonuses
|
||||
prop.rollBonus = aggregator.rollBonus;
|
||||
// Forced to fail
|
||||
prop.fail = aggregator.fail;
|
||||
// Rollbonus
|
||||
prop.rollBonuses = aggregator.rollBonus;
|
||||
}
|
||||
// Passive bonus
|
||||
prop.passiveBonus = aggregator.passiveAdd;
|
||||
// conditional benefits
|
||||
prop.conditionalBenefits = aggregator.conditional;
|
||||
// Roll bonuses
|
||||
prop.rollBonus = aggregator.rollBonus;
|
||||
// Forced to fail
|
||||
prop.fail = aggregator.fail;
|
||||
// Rollbonus
|
||||
prop.rollBonuses = aggregator.rollBonus;
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ export default function computeVariableAsAttribute(node, prop, scope){
|
||||
|
||||
// Ability scores get modifiers
|
||||
if (prop.attributeType === 'ability'){
|
||||
prop.modifier = Math.floor((prop.currentValue - 10) / 2);
|
||||
prop.modifier = Math.floor((prop.value - 10) / 2);
|
||||
}
|
||||
|
||||
// Hit dice denormalise constitution modifier
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
export default function computeVariableAsAttribute(node, prop){
|
||||
let classLevelAgg = node.data.classLevelAggregator;
|
||||
if (!classLevelAgg) return;
|
||||
prop.level = classLevelAgg.level;
|
||||
classLevelAgg.levelsFilled.forEach((filled, index) => {
|
||||
if (!filled){
|
||||
if (!prop.missingLevels) prop.missingLevels = [];
|
||||
prop.missingLevels.push(index);
|
||||
}
|
||||
});
|
||||
prop.missingLevels?.sort((a, b) => a - b);
|
||||
}
|
||||
@@ -16,8 +16,7 @@ function evaluateCalculation(calculation, scope){
|
||||
const parseNode = calculation._parsedCalculation;
|
||||
const fn = calculation._parseLevel || 'reduce';
|
||||
const calculationScope = {...calculation._localScope, ...scope};
|
||||
const result = parseNode[fn](calculationScope, context);
|
||||
calculation.value = result;
|
||||
calculation.value = parseNode[fn](calculationScope, context);
|
||||
calculation.errors = context.errors;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import computeByType from '/imports/api/creature/computation/newEngine/computeCo
|
||||
|
||||
export default function computeCreatureComputation(computation){
|
||||
const stack = [];
|
||||
// dict of computed nodes by id
|
||||
// Computation scope of {variableName: prop}
|
||||
const scope = {};
|
||||
const graph = computation.dependencyGraph;
|
||||
// Add all nodes to the stack
|
||||
@@ -44,10 +44,7 @@ function compute(graph, node, scope){
|
||||
function pushDependenciesToStack(nodeId, graph, stack){
|
||||
graph.forEachLinkedNode(
|
||||
nodeId,
|
||||
(linkedNode, link) => {
|
||||
// Ignore inventory links, they are already fully computed when they are
|
||||
// created
|
||||
if (link.data === 'inventory' || link.data === 'classLevel') return;
|
||||
(linkedNode) => {
|
||||
stack.push({
|
||||
node: linkedNode,
|
||||
visited: false,
|
||||
|
||||
@@ -9,7 +9,6 @@ let ClassLevelSchema = createPropertySchema({
|
||||
optional: true,
|
||||
max: STORAGE_LIMITS.name,
|
||||
},
|
||||
// Only used by slot filling dialog, not computed
|
||||
description: {
|
||||
type: 'inlineCalculationFieldToCompute',
|
||||
optional: true,
|
||||
@@ -24,12 +23,7 @@ let ClassLevelSchema = createPropertySchema({
|
||||
level: {
|
||||
type: SimpleSchema.Integer,
|
||||
defaultValue: 1,
|
||||
},
|
||||
// Same as in SlotFillers.js
|
||||
slotFillerCondition: {
|
||||
type: String,
|
||||
optional: true,
|
||||
max: STORAGE_LIMITS.calculation,
|
||||
max: STORAGE_LIMITS.levelMax,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
91
app/imports/api/properties/Classes.js
Normal file
91
app/imports/api/properties/Classes.js
Normal file
@@ -0,0 +1,91 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||
import createPropertySchema from '/imports/api/properties/subSchemas/createPropertySchema.js';
|
||||
|
||||
// Classes are like slots, except they only take class levels and enforce that
|
||||
// lower levels are taken before higher levels
|
||||
let ClassSchema = createPropertySchema({
|
||||
name: {
|
||||
type: String,
|
||||
optional: true,
|
||||
max: STORAGE_LIMITS.name,
|
||||
},
|
||||
description: {
|
||||
type: 'inlineCalculationFieldToCompute',
|
||||
optional: true,
|
||||
},
|
||||
// Only `classLevel`s with the same variable name can fill the class
|
||||
variableName: {
|
||||
type: String,
|
||||
optional: true,
|
||||
max: STORAGE_LIMITS.variableName,
|
||||
},
|
||||
classType: {
|
||||
type: String,
|
||||
allowedValues: ['startingClass', 'multiClass'],
|
||||
defaultValue: 'startingClass',
|
||||
},
|
||||
// Same tag format as Slots to match library classLevels against
|
||||
slotTags: {
|
||||
type: Array,
|
||||
defaultValue: [],
|
||||
maxCount: STORAGE_LIMITS.tagCount,
|
||||
},
|
||||
'slotTags.$': {
|
||||
type: String,
|
||||
max: STORAGE_LIMITS.tagLength,
|
||||
},
|
||||
extraTags: {
|
||||
type: Array,
|
||||
defaultValue: [],
|
||||
maxCount: STORAGE_LIMITS.extraTagsCount,
|
||||
},
|
||||
'extraTags.$': {
|
||||
type: Object,
|
||||
},
|
||||
'extraTags.$._id': {
|
||||
type: String,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
autoValue(){
|
||||
if (!this.isSet) return Random.id();
|
||||
}
|
||||
},
|
||||
'extraTags.$.operation': {
|
||||
type: String,
|
||||
allowedValues: ['OR', 'NOT'],
|
||||
defaultValue: 'OR',
|
||||
},
|
||||
'extraTags.$.tags': {
|
||||
type: Array,
|
||||
defaultValue: [],
|
||||
maxCount: STORAGE_LIMITS.tagCount,
|
||||
},
|
||||
'extraTags.$.tags.$': {
|
||||
type: String,
|
||||
max: STORAGE_LIMITS.tagLength,
|
||||
},
|
||||
});
|
||||
|
||||
const ComputedOnlyClassSchema = createPropertySchema({
|
||||
description: {
|
||||
type: 'inlineCalculationFieldToCompute',
|
||||
optional: true,
|
||||
},
|
||||
level: {
|
||||
type: SimpleSchema.Integer,
|
||||
optional: true,
|
||||
},
|
||||
missingLevels: {
|
||||
type: Array,
|
||||
optional: true,
|
||||
},
|
||||
'missingLevels.$': {
|
||||
type: SimpleSchema.Integer,
|
||||
},
|
||||
});
|
||||
|
||||
const ComputedClassSchema = new SimpleSchema()
|
||||
.extend(ClassSchema)
|
||||
.extend(ComputedOnlyClassSchema);
|
||||
|
||||
export { ClassSchema, ComputedOnlyClassSchema, ComputedClassSchema };
|
||||
@@ -17,44 +17,44 @@ let SlotSchema = createPropertySchema({
|
||||
optional: true,
|
||||
max: STORAGE_LIMITS.variableName,
|
||||
},
|
||||
slotTags: {
|
||||
slotTags: {
|
||||
type: Array,
|
||||
defaultValue: [],
|
||||
defaultValue: [],
|
||||
maxCount: STORAGE_LIMITS.tagCount,
|
||||
},
|
||||
'slotTags.$': {
|
||||
type: String,
|
||||
'slotTags.$': {
|
||||
type: String,
|
||||
max: STORAGE_LIMITS.tagLength,
|
||||
},
|
||||
extraTags: {
|
||||
},
|
||||
extraTags: {
|
||||
type: Array,
|
||||
defaultValue: [],
|
||||
defaultValue: [],
|
||||
maxCount: STORAGE_LIMITS.extraTagsCount,
|
||||
},
|
||||
'extraTags.$': {
|
||||
type: Object,
|
||||
},
|
||||
'extraTags.$': {
|
||||
type: Object,
|
||||
},
|
||||
'extraTags.$._id': {
|
||||
type: String,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
autoValue(){
|
||||
if (!this.isSet) return Random.id();
|
||||
}
|
||||
},
|
||||
},
|
||||
'extraTags.$.operation': {
|
||||
type: String,
|
||||
type: String,
|
||||
allowedValues: ['OR', 'NOT'],
|
||||
defaultValue: 'OR',
|
||||
},
|
||||
},
|
||||
'extraTags.$.tags': {
|
||||
type: Array,
|
||||
type: Array,
|
||||
defaultValue: [],
|
||||
maxCount: STORAGE_LIMITS.tagCount,
|
||||
},
|
||||
},
|
||||
'extraTags.$.tags.$': {
|
||||
type: String,
|
||||
type: String,
|
||||
max: STORAGE_LIMITS.tagLength,
|
||||
},
|
||||
},
|
||||
quantityExpected: {
|
||||
type: 'fieldToCompute',
|
||||
optional: true,
|
||||
@@ -88,6 +88,10 @@ let SlotSchema = createPropertySchema({
|
||||
|
||||
const ComputedOnlySlotSchema = createPropertySchema({
|
||||
// Computed fields
|
||||
description: {
|
||||
type: 'inlineCalculationFieldToCompute',
|
||||
optional: true,
|
||||
},
|
||||
quantityExpected: {
|
||||
type: 'computedOnlyField',
|
||||
optional: true,
|
||||
@@ -109,7 +113,7 @@ const ComputedOnlySlotSchema = createPropertySchema({
|
||||
});
|
||||
|
||||
const ComputedSlotSchema = new SimpleSchema()
|
||||
.extend(ComputedOnlySlotSchema)
|
||||
.extend(SlotSchema);
|
||||
.extend(ComputedOnlySlotSchema)
|
||||
.extend(SlotSchema);
|
||||
|
||||
export { SlotSchema, ComputedSlotSchema, ComputedOnlySlotSchema };
|
||||
|
||||
@@ -4,6 +4,7 @@ import { ComputedOnlyAdjustmentSchema } from '/imports/api/properties/Adjustment
|
||||
import { ComputedOnlyAttackSchema } from '/imports/api/properties/Attacks.js';
|
||||
import { ComputedOnlyAttributeSchema } from '/imports/api/properties/Attributes.js';
|
||||
import { ComputedOnlyBuffSchema } from '/imports/api/properties/Buffs.js';
|
||||
import { ComputedOnlyClassSchema } from '/imports/api/properties/Classes.js';
|
||||
import { ClassLevelSchema } from '/imports/api/properties/ClassLevels.js';
|
||||
import { ConstantSchema } from '/imports/api/properties/Constants.js';
|
||||
import { ComputedOnlyContainerSchema } from '/imports/api/properties/Containers.js';
|
||||
@@ -31,6 +32,7 @@ const propertySchemasIndex = {
|
||||
attack: ComputedOnlyAttackSchema,
|
||||
attribute: ComputedOnlyAttributeSchema,
|
||||
buff: ComputedOnlyBuffSchema,
|
||||
characterClass: ComputedOnlyClassSchema,
|
||||
classLevel: ClassLevelSchema,
|
||||
constant: ConstantSchema,
|
||||
container: ComputedOnlyContainerSchema,
|
||||
|
||||
@@ -4,6 +4,7 @@ import { ComputedAdjustmentSchema } from '/imports/api/properties/Adjustments.js
|
||||
import { ComputedAttackSchema } from '/imports/api/properties/Attacks.js';
|
||||
import { ComputedAttributeSchema } from '/imports/api/properties/Attributes.js';
|
||||
import { ComputedBuffSchema } from '/imports/api/properties/Buffs.js';
|
||||
import { ComputedClassSchema } from '/imports/api/properties/Classes.js';
|
||||
import { ClassLevelSchema } from '/imports/api/properties/ClassLevels.js';
|
||||
import { ConstantSchema } from '/imports/api/properties/Constants.js';
|
||||
import { ComputedContainerSchema } from '/imports/api/properties/Containers.js';
|
||||
@@ -31,6 +32,7 @@ const propertySchemasIndex = {
|
||||
attack: ComputedAttackSchema,
|
||||
attribute: ComputedAttributeSchema,
|
||||
buff: ComputedBuffSchema,
|
||||
characterClass: ComputedClassSchema,
|
||||
classLevel: ClassLevelSchema,
|
||||
constant: ConstantSchema,
|
||||
damage: ComputedDamageSchema,
|
||||
|
||||
@@ -4,6 +4,7 @@ import { AdjustmentSchema } from '/imports/api/properties/Adjustments.js';
|
||||
import { AttackSchema } from '/imports/api/properties/Attacks.js';
|
||||
import { AttributeSchema } from '/imports/api/properties/Attributes.js';
|
||||
import { BuffSchema } from '/imports/api/properties/Buffs.js';
|
||||
import { ClassSchema } from '/imports/api/properties/Classes.js';
|
||||
import { ClassLevelSchema } from '/imports/api/properties/ClassLevels.js';
|
||||
import { ConstantSchema } from '/imports/api/properties/Constants.js';
|
||||
import { DamageSchema } from '/imports/api/properties/Damages.js';
|
||||
@@ -31,6 +32,7 @@ const propertySchemasIndex = {
|
||||
attack: AttackSchema,
|
||||
attribute: AttributeSchema,
|
||||
buff: BuffSchema,
|
||||
characterClass: ClassSchema,
|
||||
classLevel: ClassLevelSchema,
|
||||
constant: ConstantSchema,
|
||||
damage: DamageSchema,
|
||||
|
||||
@@ -30,11 +30,17 @@ const PROPERTIES = Object.freeze({
|
||||
helpText: 'When a buff is activated as a child of an action, it will copy the properties under itself onto a target character.',
|
||||
suggestedParents: ['action', 'attack', 'savingThrow', 'spell'],
|
||||
},
|
||||
characterClass: {
|
||||
icon: 'mdi-card-account-details',
|
||||
name: 'Class',
|
||||
helpText: 'Your character should ideally have one starting class. Classes hold class levels',
|
||||
suggestedParents: ['class'],
|
||||
},
|
||||
classLevel: {
|
||||
icon: '$vuetify.icons.class_level',
|
||||
name: 'Class level',
|
||||
helpText: 'Class levels represent a single level gained in a class',
|
||||
suggestedParents: ['class'],
|
||||
suggestedParents: [],
|
||||
},
|
||||
constant: {
|
||||
icon: 'mdi-anchor',
|
||||
|
||||
@@ -13,6 +13,9 @@ const STORAGE_LIMITS = Object.freeze({
|
||||
url: 256,
|
||||
variableName: 64,
|
||||
|
||||
// Number limits
|
||||
levelMax: 128,
|
||||
|
||||
//Array counts
|
||||
ancestorCount: 100,
|
||||
damageTypeCount: 32,
|
||||
|
||||
Reference in New Issue
Block a user