Merge commit 'd9b978cb82109b71c05d03807a8558ba1dc537a4' into feature-tabletop
This commit is contained in:
@@ -3,7 +3,6 @@
|
||||
# 'meteor add' and 'meteor remove' will edit this file for you,
|
||||
# but you can also edit it by hand.
|
||||
|
||||
zegenie:redis-oplog
|
||||
accounts-password@2.3.4
|
||||
random@1.2.1
|
||||
underscore@1.0.13
|
||||
|
||||
@@ -124,5 +124,4 @@ underscore@1.0.13
|
||||
url@1.3.2
|
||||
webapp@1.13.5
|
||||
webapp-hashing@1.1.1
|
||||
zegenie:redis-oplog@2.0.16
|
||||
zer0th:meteor-vuetify-loader@0.1.41
|
||||
|
||||
@@ -132,6 +132,9 @@ function insertNodeFromProperty(propId, ancestors, order, method) {
|
||||
prop.order = order;
|
||||
}
|
||||
|
||||
// Clean the props
|
||||
props = cleanProps(props);
|
||||
|
||||
// Insert the props as library nodes
|
||||
LibraryNodes.batchInsert(props);
|
||||
return prop;
|
||||
@@ -186,4 +189,11 @@ function assertSourceLibraryCopyPermission(props, method) {
|
||||
});
|
||||
}
|
||||
|
||||
function cleanProps(props) {
|
||||
return props.map(prop => {
|
||||
let schema = LibraryNodes.simpleSchema(prop);
|
||||
return schema.clean(prop);
|
||||
});
|
||||
}
|
||||
|
||||
export default copyPropertyToLibrary;
|
||||
|
||||
@@ -19,6 +19,7 @@ const applyPropertyByType = {
|
||||
damage,
|
||||
folder,
|
||||
note,
|
||||
propertySlot: folder,
|
||||
roll,
|
||||
savingThrow,
|
||||
spell: action,
|
||||
@@ -26,6 +27,7 @@ const applyPropertyByType = {
|
||||
};
|
||||
|
||||
export default function applyProperty(node, actionContext, ...rest) {
|
||||
if (node.node.deactivatedByToggle) return;
|
||||
actionContext.scope[`#${node.node.type}`] = node.node;
|
||||
applyPropertyByType[node.node.type]?.(node, actionContext, ...rest);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import recalculateCalculation from './shared/recalculateCalculation.js';
|
||||
import rollDice from '/imports/parser/rollDice.js';
|
||||
import applyProperty from '../applyProperty.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js';
|
||||
import { adjustQuantityWork } from '/imports/api/creature/creatureProperties/methods/adjustQuantity.js';
|
||||
import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js';
|
||||
import numberToSignedString from '/imports/api/utility/numberToSignedString.js';
|
||||
@@ -187,11 +188,6 @@ function applyCrits(value, scope) {
|
||||
return { criticalHit, criticalMiss };
|
||||
}
|
||||
|
||||
function applyChildren(node, actionContext) {
|
||||
applyNodeTriggers(node, 'after', actionContext);
|
||||
node.children.forEach(child => applyProperty(child, actionContext));
|
||||
}
|
||||
|
||||
function spendResources(prop, actionContext) {
|
||||
// Check Uses
|
||||
if (prop.usesLeft <= 0) {
|
||||
@@ -276,9 +272,10 @@ function spendResources(prop, actionContext) {
|
||||
recalculateCalculation(attConsumed.quantity, actionContext);
|
||||
|
||||
if (!attConsumed.quantity?.value) return;
|
||||
if (!attConsumed.variableName) return;
|
||||
let stat = actionContext.scope[attConsumed.variableName];
|
||||
if (!stat) {
|
||||
spendLog.push(stat.name + ': ' + ' not found');
|
||||
spendLog.push(attConsumed.variableName + ': ' + ' not found');
|
||||
return;
|
||||
}
|
||||
damagePropertyWork({
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import applyProperty from '../applyProperty.js';
|
||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js';
|
||||
import recalculateCalculation from './shared/recalculateCalculation.js';
|
||||
import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||
|
||||
export default function applyAdjustment(node, actionContext){
|
||||
export default function applyAdjustment(node, actionContext) {
|
||||
applyNodeTriggers(node, 'before', actionContext);
|
||||
const prop = node.node;
|
||||
const damageTargets = prop.target === 'self' ? [actionContext.creature] : actionContext.targets;
|
||||
@@ -39,7 +39,7 @@ export default function applyAdjustment(node, actionContext){
|
||||
if (!prop.silent) actionContext.addLog({
|
||||
name: 'Attribute damage',
|
||||
value: `${prop.stat}${prop.operation === 'set' ? ' set to' : ''}` +
|
||||
` ${value}`,
|
||||
` ${value}`,
|
||||
inline: true,
|
||||
});
|
||||
});
|
||||
@@ -47,15 +47,10 @@ export default function applyAdjustment(node, actionContext){
|
||||
if (!prop.silent) actionContext.addLog({
|
||||
name: 'Attribute damage',
|
||||
value: `${prop.stat}${prop.operation === 'set' ? ' set to' : ''}` +
|
||||
` ${value}`,
|
||||
` ${value}`,
|
||||
inline: true,
|
||||
});
|
||||
}
|
||||
|
||||
return applyChildren(node, actionContext);
|
||||
}
|
||||
|
||||
function applyChildren(node, actionContext){
|
||||
applyNodeTriggers(node, 'after', actionContext);
|
||||
node.children.forEach(child => applyProperty(child, actionContext));
|
||||
}
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
import applyProperty from '../applyProperty.js';
|
||||
import recalculateCalculation from './shared/recalculateCalculation.js';
|
||||
import rollDice from '/imports/parser/rollDice.js';
|
||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||
|
||||
export default function applyBranch(node, actionContext) {
|
||||
applyNodeTriggers(node, 'before', actionContext);
|
||||
const applyChildren = function () {
|
||||
applyNodeTriggers(node, 'after', actionContext);
|
||||
node.children.forEach(child => applyProperty(child, actionContext));
|
||||
};
|
||||
const scope = actionContext.scope;
|
||||
const targets = actionContext.targets;
|
||||
const prop = node.node;
|
||||
switch (prop.branchType) {
|
||||
case 'if':
|
||||
recalculateCalculation(prop.condition, actionContext);
|
||||
if (prop.condition?.value) applyChildren();
|
||||
if (prop.condition?.value) applyChildren(node, actionContext);
|
||||
break;
|
||||
case 'index':
|
||||
if (node.children.length) {
|
||||
@@ -32,30 +29,31 @@ export default function applyBranch(node, actionContext) {
|
||||
if (index > node.children.length) index = node.children.length;
|
||||
applyNodeTriggers(node, 'after', actionContext);
|
||||
applyProperty(node.children[index - 1], actionContext);
|
||||
applyNodeTriggers(node, 'afterChildren', actionContext);
|
||||
}
|
||||
break;
|
||||
case 'hit':
|
||||
if (scope['~attackHit']?.value) {
|
||||
if (!targets.length && !prop.silent) actionContext.addLog({ value: '**On hit**' });
|
||||
applyChildren();
|
||||
applyChildren(node, actionContext);
|
||||
}
|
||||
break;
|
||||
case 'miss':
|
||||
if (scope['~attackMiss']?.value) {
|
||||
if (!targets.length && !prop.silent) actionContext.addLog({ value: '**On miss**' });
|
||||
applyChildren();
|
||||
applyChildren(node, actionContext);
|
||||
}
|
||||
break;
|
||||
case 'failedSave':
|
||||
if (scope['~saveFailed']?.value) {
|
||||
if (!targets.length && !prop.silent) actionContext.addLog({ value: '**On failed save**' });
|
||||
applyChildren();
|
||||
applyChildren(node, actionContext);
|
||||
}
|
||||
break;
|
||||
case 'successfulSave':
|
||||
if (scope['~saveSucceeded']?.value) {
|
||||
if (!targets.length && !prop.silent) actionContext.addLog({ value: '**On save**', });
|
||||
applyChildren();
|
||||
applyChildren(node, actionContext);
|
||||
}
|
||||
break;
|
||||
case 'random':
|
||||
@@ -63,6 +61,7 @@ export default function applyBranch(node, actionContext) {
|
||||
let index = rollDice(1, node.children.length)[0] - 1;
|
||||
applyNodeTriggers(node, 'after', actionContext);
|
||||
applyProperty(node.children[index], actionContext);
|
||||
applyNodeTriggers(node, 'afterChildren', actionContext);
|
||||
}
|
||||
break;
|
||||
case 'eachTarget':
|
||||
@@ -71,9 +70,10 @@ export default function applyBranch(node, actionContext) {
|
||||
applyNodeTriggers(node, 'after', actionContext);
|
||||
actionContext.targets = [target]
|
||||
node.children.forEach(child => applyProperty(child, actionContext));
|
||||
applyNodeTriggers(node, 'afterChildren', actionContext);
|
||||
});
|
||||
} else {
|
||||
applyChildren();
|
||||
applyChildren(node, actionContext);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -77,6 +77,7 @@ export default function applyBuff(node, actionContext) {
|
||||
}
|
||||
});
|
||||
applyNodeTriggers(node, 'after', actionContext);
|
||||
applyNodeTriggers(node, 'afterChildren', actionContext);
|
||||
|
||||
// Don't apply the children of the buff, they get copied to the target instead
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { getProperyAncestors, getPropertiesOfType } from '/imports/api/engine/lo
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import { softRemove } from '/imports/api/parenting/softRemove.js';
|
||||
import getEffectivePropTags from '/imports/api/engine/computation/utility/getEffectivePropTags.js';
|
||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js';
|
||||
|
||||
export default function applyBuffRemover(node, actionContext) {
|
||||
// Apply triggers
|
||||
@@ -13,7 +14,7 @@ export default function applyBuffRemover(node, actionContext) {
|
||||
const prop = node.node;
|
||||
|
||||
// Log Name
|
||||
if (prop.name && !prop.silent){
|
||||
if (prop.name && !prop.silent) {
|
||||
actionContext.addLog({ name: prop.name });
|
||||
}
|
||||
|
||||
@@ -53,11 +54,7 @@ export default function applyBuffRemover(node, actionContext) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply triggers
|
||||
applyNodeTriggers(node, 'after', actionContext);
|
||||
// Apply children
|
||||
node.children.forEach(child => applyProperty(child, actionContext));
|
||||
applyChildren(node, actionContext);
|
||||
}
|
||||
|
||||
function removeBuff(buff, actionContext, prop) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { some, intersection, difference, remove, includes } from 'lodash';
|
||||
import applyProperty from '../applyProperty.js';
|
||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js';
|
||||
import { insertCreatureLog } from '/imports/api/creature/log/CreatureLogs.js';
|
||||
import resolve, { Context, toString } from '/imports/parser/resolve.js';
|
||||
import logErrors from './shared/logErrors.js';
|
||||
@@ -13,10 +13,6 @@ import getEffectivePropTags from '/imports/api/engine/computation/utility/getEff
|
||||
|
||||
export default function applyDamage(node, actionContext) {
|
||||
applyNodeTriggers(node, 'before', actionContext);
|
||||
const applyChildren = function () {
|
||||
applyNodeTriggers(node, 'after', actionContext);
|
||||
node.children.forEach(child => applyProperty(child, actionContext));
|
||||
};
|
||||
|
||||
const prop = node.node;
|
||||
const scope = actionContext.scope;
|
||||
@@ -66,7 +62,7 @@ export default function applyDamage(node, actionContext) {
|
||||
|
||||
// If we didn't end up with a constant of finite amount, give up
|
||||
if (reduced?.parseType !== 'constant' || !isFinite(reduced.value)) {
|
||||
return applyChildren();
|
||||
return applyChildren(node, actionContext);
|
||||
}
|
||||
|
||||
// Round the damage to a whole number
|
||||
@@ -133,7 +129,7 @@ export default function applyDamage(node, actionContext) {
|
||||
value: logValue.join('\n'),
|
||||
inline: true,
|
||||
});
|
||||
return applyChildren();
|
||||
return applyChildren(node, actionContext);
|
||||
}
|
||||
|
||||
function applyDamageMultipliers({ target, damage, damageProp, logValue }) {
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import recalculateInlineCalculations from './shared/recalculateInlineCalculations.js';
|
||||
import applyProperty from '../applyProperty.js';
|
||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||
|
||||
export default function applyFolder(node, actionContext) {
|
||||
// Apply triggers
|
||||
applyNodeTriggers(node, 'before', actionContext);
|
||||
applyNodeTriggers(node, 'after', actionContext);
|
||||
// Apply children
|
||||
node.children.forEach(child => applyProperty(child, actionContext));
|
||||
applyChildren(node, actionContext);
|
||||
}
|
||||
|
||||
@@ -1,27 +1,25 @@
|
||||
import recalculateInlineCalculations from './shared/recalculateInlineCalculations.js';
|
||||
import applyProperty from '../applyProperty.js';
|
||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||
|
||||
export default function applyNote(node, actionContext){
|
||||
export default function applyNote(node, actionContext) {
|
||||
applyNodeTriggers(node, 'before', actionContext);
|
||||
const prop = node.node;
|
||||
|
||||
// Log Name, summary
|
||||
let content = { name: prop.name };
|
||||
if (prop.summary?.text){
|
||||
if (prop.summary?.text) {
|
||||
recalculateInlineCalculations(prop.summary, actionContext);
|
||||
content.value = prop.summary.value;
|
||||
}
|
||||
if (content.name || content.value){
|
||||
if (content.name || content.value) {
|
||||
actionContext.addLog(content);
|
||||
}
|
||||
// Log description
|
||||
if (prop.description?.text){
|
||||
if (prop.description?.text) {
|
||||
recalculateInlineCalculations(prop.description, actionContext);
|
||||
actionContext.addLog({value: prop.description.value});
|
||||
actionContext.addLog({ value: prop.description.value });
|
||||
}
|
||||
// Apply triggers
|
||||
applyNodeTriggers(node, 'after', actionContext);
|
||||
// Apply children
|
||||
node.children.forEach(child => applyProperty(child, actionContext));
|
||||
applyChildren(node, actionContext);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import applyProperty from '../applyProperty.js';
|
||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js';
|
||||
import logErrors from './shared/logErrors.js';
|
||||
import applyEffectsToCalculationParseNode from '/imports/api/engine/actions/applyPropertyByType/shared/applyEffectsToCalculationParseNode.js';
|
||||
import resolve, { toString } from '/imports/parser/resolve.js';
|
||||
@@ -8,11 +8,6 @@ export default function applyRoll(node, actionContext) {
|
||||
applyNodeTriggers(node, 'before', actionContext);
|
||||
const prop = node.node;
|
||||
|
||||
const applyChildren = function () {
|
||||
applyNodeTriggers(node, 'after', actionContext);
|
||||
node.children.forEach(child => applyProperty(child, actionContext));
|
||||
};
|
||||
|
||||
if (prop.roll?.calculation) {
|
||||
const logValue = [];
|
||||
|
||||
@@ -42,7 +37,7 @@ export default function applyRoll(node, actionContext) {
|
||||
|
||||
// If we didn't end up with a constant or a number of finite value, give up
|
||||
if (reduced?.parseType !== 'constant' || (reduced.valueType === 'number' && !isFinite(reduced.value))) {
|
||||
return applyChildren();
|
||||
return applyChildren(node, actionContext);
|
||||
}
|
||||
const value = reduced.value;
|
||||
|
||||
@@ -57,5 +52,5 @@ export default function applyRoll(node, actionContext) {
|
||||
});
|
||||
}
|
||||
}
|
||||
return applyChildren();
|
||||
return applyChildren(node, actionContext);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import rollDice from '/imports/parser/rollDice.js';
|
||||
import recalculateCalculation from './shared/recalculateCalculation.js';
|
||||
import applyProperty from '../applyProperty.js';
|
||||
import numberToSignedString from '/imports/api/utility/numberToSignedString.js';
|
||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||
import { applyUnresolvedEffects } from '/imports/api/engine/actions/doCheck.js';
|
||||
|
||||
@@ -34,8 +35,7 @@ export default function applySavingThrow(node, actionContext) {
|
||||
if (!saveTargets?.length) {
|
||||
scope['~saveFailed'] = { value: true };
|
||||
scope['~saveSucceeded'] = { value: true };
|
||||
applyNodeTriggers(node, 'after', actionContext);
|
||||
return node.children.forEach(child => applyProperty(child, actionContext));
|
||||
return applyChildren(node, actionContext);
|
||||
}
|
||||
|
||||
// Each target makes the saving throw
|
||||
@@ -45,10 +45,9 @@ export default function applySavingThrow(node, actionContext) {
|
||||
delete scope['~saveDiceRoll'];
|
||||
delete scope['~saveRoll'];
|
||||
|
||||
const applyChildren = function () {
|
||||
actionContext.targets = [target]
|
||||
applyNodeTriggers(node, 'after', actionContext);
|
||||
node.children.forEach(child => applyProperty(child, actionContext));
|
||||
const applyChildrenToTarget = function () {
|
||||
actionContext.targets = [target];
|
||||
return applyChildren(node, actionContext);
|
||||
};
|
||||
|
||||
const save = target.variables[prop.stat];
|
||||
@@ -58,7 +57,7 @@ export default function applySavingThrow(node, actionContext) {
|
||||
name: 'Saving throw error',
|
||||
value: 'No saving throw found: ' + prop.stat,
|
||||
});
|
||||
return applyChildren();
|
||||
return applyChildrenToTarget();
|
||||
}
|
||||
|
||||
let rollModifierText = numberToSignedString(save.value, true);
|
||||
@@ -105,7 +104,7 @@ export default function applySavingThrow(node, actionContext) {
|
||||
value: resultPrefix + '\n**' + result + '**',
|
||||
inline: true,
|
||||
});
|
||||
return applyChildren();
|
||||
return applyChildrenToTarget();
|
||||
});
|
||||
// reset the targets after the save to each child
|
||||
actionContext.targets = originalTargets;
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import applyProperty from '../applyProperty.js';
|
||||
import recalculateCalculation from './shared/recalculateCalculation.js';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js';
|
||||
|
||||
export default function applyToggle(node, actionContext){
|
||||
export default function applyToggle(node, actionContext) {
|
||||
applyNodeTriggers(node, 'before', actionContext);
|
||||
const prop = node.node;
|
||||
recalculateCalculation(prop.condition, actionContext);
|
||||
if (prop.condition?.value) {
|
||||
applyNodeTriggers(node, 'after', actionContext);
|
||||
return node.children.forEach(child => applyProperty(child, actionContext));
|
||||
return applyChildren(node, actionContext);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||
import applyProperty from '/imports/api/engine/actions/applyProperty.js';
|
||||
|
||||
export default function applyChildren(node, actionContext) {
|
||||
applyNodeTriggers(node, 'after', actionContext);
|
||||
node.children.forEach(child => applyProperty(child, actionContext));
|
||||
applyNodeTriggers(node, 'afterChildren', actionContext);
|
||||
}
|
||||
@@ -2,22 +2,22 @@
|
||||
* 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){
|
||||
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){
|
||||
while (stack.length) {
|
||||
const top = stack[stack.length - 1];
|
||||
const prop = top.node;
|
||||
if (prop._computationDetails.inventoryChildrenVisited){
|
||||
if (prop._computationDetails.inventoryChildrenVisited) {
|
||||
if (prop.type === 'container') containerStack.pop();
|
||||
stack.pop();
|
||||
handleProp(prop, containerStack, dependencyGraph);
|
||||
} else {
|
||||
// Add all containers to the stack when we first visit them
|
||||
if (prop.type === 'container'){
|
||||
if (prop.type === 'container') {
|
||||
containerStack.push(top.node);
|
||||
}
|
||||
// Push children onto the stack and mark this as children are visited
|
||||
@@ -27,18 +27,18 @@ export default function linkInventory(forest, dependencyGraph){
|
||||
}
|
||||
}
|
||||
|
||||
function handleProp(prop, containerStack, dependencyGraph){
|
||||
function handleProp(prop, containerStack, dependencyGraph) {
|
||||
// Skip props that aren't part of the inventory
|
||||
if (prop.type !== 'item' && prop.type !== 'container') return;
|
||||
// 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){
|
||||
if (prop.type === 'item') {
|
||||
if (prop.attuned) {
|
||||
dependencyGraph.addLink('itemsAttuned', prop._id, 'attunedItem');
|
||||
}
|
||||
if (prop.equipped){
|
||||
if (prop.equipped) {
|
||||
dependencyGraph.addLink('weightEquipment', prop._id, 'equippedItem');
|
||||
dependencyGraph.addLink('valueEquipment', prop._id, 'equippedItem');
|
||||
}
|
||||
@@ -47,14 +47,14 @@ function handleProp(prop, containerStack, dependencyGraph){
|
||||
// Get the parent container
|
||||
const container = containerStack[containerStack.length - 1];
|
||||
|
||||
if (container){
|
||||
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){
|
||||
if (carried) {
|
||||
dependencyGraph.addLink('weightCarried', prop._id, 'inventoryStats');
|
||||
dependencyGraph.addLink('valueCarried', prop._id, 'inventoryStats');
|
||||
}
|
||||
|
||||
@@ -80,6 +80,15 @@ function linkAction(dependencyGraph, prop, { propsById }) {
|
||||
key: `resources.attributesConsumed[${index}].quantity`,
|
||||
});
|
||||
});
|
||||
// Link conditions
|
||||
prop.resources.conditions?.forEach((con, index) => {
|
||||
// Link the property to its condition calculation
|
||||
dependOnCalc({
|
||||
dependencyGraph,
|
||||
prop,
|
||||
key: `resources.conditions[${index}].condition`,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function linkAdjustment(dependencyGraph, prop) {
|
||||
|
||||
@@ -8,14 +8,21 @@ export default function computeAction(computation, node) {
|
||||
}
|
||||
computeResources(computation, node);
|
||||
if (!prop.resources) return;
|
||||
prop.resources.itemsConsumed.forEach(itemConsumed => {
|
||||
prop.resources.conditions?.forEach(conObj => {
|
||||
const condition = conObj.condition;
|
||||
if (!condition) return;
|
||||
if (condition.calculation && !condition.value) {
|
||||
prop.insufficientResources = true;
|
||||
}
|
||||
});
|
||||
prop.resources.itemsConsumed?.forEach(itemConsumed => {
|
||||
if (!itemConsumed.itemId || itemConsumed.available < itemConsumed.quantity?.value) {
|
||||
prop.insufficientResources = true;
|
||||
}
|
||||
});
|
||||
prop.resources.attributesConsumed.forEach(attConsumed => {
|
||||
prop.resources.attributesConsumed?.forEach(attConsumed => {
|
||||
if (!attConsumed.variableName) return;
|
||||
if (attConsumed.available < attConsumed.quantity?.value) {
|
||||
if (!(attConsumed.available >= attConsumed.quantity?.value)) {
|
||||
prop.insufficientResources = true;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
import aggregate from './computeVariable/aggregate/index.js';
|
||||
import { safeStrip } from '/imports/api/engine/computation/utility/stripFloatingPointOddities.js';
|
||||
|
||||
export default function computeContainer(computation, node){
|
||||
export default function computeContainer(computation, node) {
|
||||
if (!node.data) node.data = {};
|
||||
aggregateLinks(computation, node);
|
||||
|
||||
// Clean up floating points
|
||||
const prop = node.data;
|
||||
prop.contentsWeight = safeStrip(prop.contentsWeight);
|
||||
prop.carriedWeight = safeStrip(prop.carriedWeight);
|
||||
}
|
||||
|
||||
function aggregateLinks(computation, node){
|
||||
function aggregateLinks(computation, node) {
|
||||
computation.dependencyGraph.forEachLinkedNode(
|
||||
node.id,
|
||||
(linkedNode, link) => {
|
||||
@@ -13,7 +19,7 @@ function aggregateLinks(computation, node){
|
||||
// Ignore inactive props
|
||||
if (linkedNode.data.inactive) return;
|
||||
// Aggregate inventory links
|
||||
aggregate.inventory({node, linkedNode, link});
|
||||
aggregate.inventory({ node, linkedNode, link });
|
||||
},
|
||||
true // enumerate only outbound links
|
||||
);
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
export default function aggregateInventory({node, linkedNode, link}){
|
||||
export default function aggregateInventory({ node, linkedNode, link }) {
|
||||
let linkedProp = linkedNode.data || {};
|
||||
const prop = node.data;
|
||||
|
||||
switch (link.data){
|
||||
switch (link.data) {
|
||||
case 'attunedItem':
|
||||
prop.baseValue = (prop.baseValue || 0) + 1;
|
||||
return;
|
||||
|
||||
case 'equippedItem':
|
||||
if (node.id === 'weightEquipment'){
|
||||
if (node.id === 'weightEquipment') {
|
||||
prop.baseValue = (prop.baseValue || 0) + weight(linkedProp);
|
||||
} else if (node.id === 'valueEquipment'){
|
||||
} 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){
|
||||
if (!prop.weightless) {
|
||||
prop.contentsWeight = (prop.contentsWeight || 0) + weight(linkedProp);
|
||||
if (prop.carried){
|
||||
if (prop.carried && !prop.contentsWeightless) {
|
||||
prop.carriedWeight = (prop.carriedWeight || 0) + carriedWeight(linkedProp);
|
||||
}
|
||||
}
|
||||
@@ -30,39 +30,39 @@ export default function aggregateInventory({node, linkedNode, link}){
|
||||
return;
|
||||
|
||||
case 'inventoryStats':
|
||||
if (node.id === 'weightTotal'){
|
||||
if (node.id === 'weightTotal') {
|
||||
prop.baseValue = (prop.baseValue || 0) + weight(linkedProp);
|
||||
} else if (node.id === 'valueTotal'){
|
||||
} else if (node.id === 'valueTotal') {
|
||||
prop.baseValue = (prop.baseValue || 0) + value(linkedProp);
|
||||
} else if (node.id === 'weightCarried'){
|
||||
} else if (node.id === 'weightCarried') {
|
||||
prop.baseValue = (prop.baseValue || 0) + carriedWeight(linkedProp);
|
||||
} else if (node.id === 'valueCarried'){
|
||||
} else if (node.id === 'valueCarried') {
|
||||
prop.baseValue = (prop.baseValue || 0) + carriedValue(linkedProp);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function quantity(prop){
|
||||
if (typeof prop.quantity === 'number'){
|
||||
function quantity(prop) {
|
||||
if (typeof prop.quantity === 'number') {
|
||||
return prop.quantity;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
function weight(prop){
|
||||
function weight(prop) {
|
||||
return (prop.weight || 0) * quantity(prop) + (prop.contentsWeight || 0);
|
||||
}
|
||||
|
||||
function carriedWeight(prop){
|
||||
function carriedWeight(prop) {
|
||||
return (prop.weight || 0) * quantity(prop) + (prop.carriedWeight || 0);
|
||||
}
|
||||
|
||||
function value (prop){
|
||||
function value(prop) {
|
||||
return (prop.value || 0) * quantity(prop) + (prop.contentsValue || 0);
|
||||
}
|
||||
|
||||
function carriedValue (prop){
|
||||
function carriedValue(prop) {
|
||||
return (prop.value || 0) * quantity(prop) + (prop.carriedValue || 0);
|
||||
}
|
||||
|
||||
@@ -5,8 +5,26 @@ export default function evaluateToggles(computation, node) {
|
||||
if (!toggles) return;
|
||||
toggles.forEach(toggle => {
|
||||
if (
|
||||
(!toggle.enabled && !toggle.disabled && toggle.condition && !toggle.condition.value)
|
||||
|| (toggle.disabled)
|
||||
(
|
||||
// Toggle isn't set to constantly enabled or disabled
|
||||
!toggle.enabled &&
|
||||
!toggle.disabled &&
|
||||
// Toggle is not disabled by another toggle targeting it
|
||||
// Ancestor toggles would've handled this child anyway,
|
||||
// and tag targeted toggles break the link
|
||||
!toggle.deactivatedByToggle &&
|
||||
!toggle.deactivatedByAncestor &&
|
||||
// Toggle has a condition with a falsy value
|
||||
toggle.condition &&
|
||||
!toggle.condition.value
|
||||
)
|
||||
|| (
|
||||
// Toggle is disabled manually
|
||||
toggle.disabled &&
|
||||
// Toggle isn't deactivated by something else
|
||||
!toggle.deactivatedByToggle &&
|
||||
!toggle.deactivatedByAncestor
|
||||
)
|
||||
) {
|
||||
prop.inactive = true;
|
||||
prop.deactivatedByToggle = true;
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
export default function stripFloatingPointOddities(num, precision = 12){
|
||||
export default function stripFloatingPointOddities(num, precision = 12) {
|
||||
return +parseFloat(num.toPrecision(precision))
|
||||
}
|
||||
|
||||
export function safeStrip(num, precision = 12) {
|
||||
if (!Number.isFinite(num)) return num;
|
||||
return stripFloatingPointOddities(num, precision);
|
||||
}
|
||||
|
||||
@@ -77,6 +77,7 @@ let ActionSchema = createPropertySchema({
|
||||
'resources.itemsConsumed': {
|
||||
type: Array,
|
||||
defaultValue: [],
|
||||
max: 32,
|
||||
},
|
||||
'resources.itemsConsumed.$': {
|
||||
type: Object,
|
||||
@@ -104,6 +105,7 @@ let ActionSchema = createPropertySchema({
|
||||
'resources.attributesConsumed': {
|
||||
type: Array,
|
||||
defaultValue: [],
|
||||
max: 32,
|
||||
},
|
||||
'resources.attributesConsumed.$': {
|
||||
type: Object,
|
||||
@@ -124,6 +126,30 @@ let ActionSchema = createPropertySchema({
|
||||
type: 'fieldToCompute',
|
||||
optional: true,
|
||||
},
|
||||
'resources.conditions': {
|
||||
type: Array,
|
||||
defaultValue: [],
|
||||
max: 32,
|
||||
},
|
||||
'resources.conditions.$': {
|
||||
type: Object,
|
||||
},
|
||||
'resources.conditions.$._id': {
|
||||
type: String,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
autoValue() {
|
||||
if (!this.isSet) return Random.id();
|
||||
}
|
||||
},
|
||||
'resources.conditions.$.condition': {
|
||||
type: 'fieldToCompute',
|
||||
optional: true,
|
||||
},
|
||||
'resources.conditions.$.conditionNote': {
|
||||
type: String,
|
||||
optional: true,
|
||||
max: STORAGE_LIMITS.calculation,
|
||||
},
|
||||
// Prevent the property from showing up in the log
|
||||
silent: {
|
||||
type: Boolean,
|
||||
|
||||
@@ -2,6 +2,7 @@ import SimpleSchema from 'simpl-schema';
|
||||
import createPropertySchema from '/imports/api/properties/subSchemas/createPropertySchema.js';
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||
import VARIABLE_NAME_REGEX from '/imports/constants/VARIABLE_NAME_REGEX.js';
|
||||
import { SavingThrowSchema, ComputedOnlySavingThrowSchema } from '/imports/api/properties/SavingThrows.js';
|
||||
|
||||
const DamageSchema = createPropertySchema({
|
||||
// The roll that determines how much to damage the attribute
|
||||
@@ -32,6 +33,15 @@ const DamageSchema = createPropertySchema({
|
||||
type: Boolean,
|
||||
optional: true,
|
||||
},
|
||||
save: {
|
||||
type: SavingThrowSchema,
|
||||
optional: true,
|
||||
},
|
||||
'save.damageFunction': {
|
||||
type: 'fieldToCompute',
|
||||
optional: true,
|
||||
parseLevel: 'compile',
|
||||
},
|
||||
});
|
||||
|
||||
const ComputedOnlyDamageSchema = createPropertySchema({
|
||||
@@ -40,6 +50,15 @@ const ComputedOnlyDamageSchema = createPropertySchema({
|
||||
optional: true,
|
||||
parseLevel: 'compile',
|
||||
},
|
||||
save: {
|
||||
type: ComputedOnlySavingThrowSchema,
|
||||
optional: true,
|
||||
},
|
||||
'save.damageFunction': {
|
||||
type: 'computedOnlyField',
|
||||
optional: true,
|
||||
parseLevel: 'compile',
|
||||
},
|
||||
});
|
||||
|
||||
const ComputedDamageSchema = new SimpleSchema()
|
||||
|
||||
@@ -18,6 +18,7 @@ const eventOptions = {
|
||||
const timingOptions = {
|
||||
before: 'Before',
|
||||
after: 'After',
|
||||
afterChildren: 'After Children',
|
||||
}
|
||||
|
||||
const actionPropertyTypeOptions = {
|
||||
@@ -91,7 +92,7 @@ let TriggerSchema = createPropertySchema({
|
||||
'extraTags.$._id': {
|
||||
type: String,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
autoValue(){
|
||||
autoValue() {
|
||||
if (!this.isSet) return Random.id();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
{{ label }}
|
||||
<v-icon
|
||||
:right="!!label"
|
||||
:color="value"
|
||||
:color="noColorChange ? undefined : value"
|
||||
>
|
||||
mdi-format-paint
|
||||
</v-icon>
|
||||
@@ -148,6 +148,7 @@
|
||||
type: Number,
|
||||
default: undefined,
|
||||
},
|
||||
noColorChange: Boolean,
|
||||
},
|
||||
data(){ return {
|
||||
colors: [
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
<script lang="js">
|
||||
import { marked } from 'marked';
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
@@ -20,7 +21,7 @@ export default {
|
||||
computed: {
|
||||
compiledMarkdown() {
|
||||
if (!this.markdown) return;
|
||||
return marked(this.markdown);
|
||||
return DOMPurify.sanitize(marked(this.markdown));
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
<tree-node-view
|
||||
:model="node"
|
||||
:selected="selected"
|
||||
:show-external-details="showExternalDetails"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -57,6 +58,7 @@
|
||||
:organize="organize"
|
||||
:selected-node="selectedNode"
|
||||
:start-expanded="startExpanded"
|
||||
:show-external-details="showExternalDetails"
|
||||
@reordered="e => $emit('reordered', e)"
|
||||
@reorganized="e => $emit('reorganized', e)"
|
||||
@selected="e => $emit('selected', e)"
|
||||
@@ -115,6 +117,7 @@ export default {
|
||||
},
|
||||
selected: Boolean,
|
||||
startExpanded: Boolean,
|
||||
showExternalDetails: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -214,6 +217,6 @@ export default {
|
||||
|
||||
.tree-node-title,
|
||||
.dummy-node {
|
||||
height: 40px;
|
||||
min-height: 40px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
:organize="organize"
|
||||
:lazy="lazy"
|
||||
:start-expanded="startExpanded"
|
||||
:show-external-details="showExternalDetails"
|
||||
@selected="e => $emit('selected', e)"
|
||||
@reordered="e => $emit('reordered', e)"
|
||||
@reorganized="e => $emit('reorganized', e)"
|
||||
@@ -64,6 +65,7 @@ export default {
|
||||
default: () => [],
|
||||
},
|
||||
startExpanded: Boolean,
|
||||
showExternalDetails: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
<v-spacer />
|
||||
<color-picker
|
||||
:value="model.color"
|
||||
no-color-change
|
||||
@input="value => change({path: ['color'], value})"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -1,39 +1,35 @@
|
||||
<template>
|
||||
<column-layout
|
||||
wide-columns
|
||||
class="slots-to-fill"
|
||||
<v-fade-transition
|
||||
group
|
||||
leave-absolute
|
||||
hide-on-leave
|
||||
class="column-layout wide-columns"
|
||||
>
|
||||
<v-fade-transition
|
||||
group
|
||||
leave-absolute
|
||||
hide-on-leave
|
||||
<div
|
||||
v-for="pointBuy in pointBuys"
|
||||
:key="pointBuy._id"
|
||||
style="transition: all 0.3s !important"
|
||||
>
|
||||
<div
|
||||
v-for="pointBuy in pointBuys"
|
||||
:key="pointBuy._id"
|
||||
style="transition: all 0.3s !important"
|
||||
>
|
||||
<point-buy-card
|
||||
:model="pointBuy"
|
||||
hover
|
||||
@ignore="ignoreProp(pointBuy._id)"
|
||||
@click="editPointBuy(pointBuy._id)"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-for="slot in slots"
|
||||
:key="slot._id"
|
||||
style="transition: all 0.3s !important"
|
||||
>
|
||||
<slot-card
|
||||
:model="slot"
|
||||
hover
|
||||
@ignore="ignoreProp(slot._id)"
|
||||
@click="fillSlot(slot._id)"
|
||||
/>
|
||||
</div>
|
||||
</v-fade-transition>
|
||||
</column-layout>
|
||||
<point-buy-card
|
||||
:model="pointBuy"
|
||||
hover
|
||||
@ignore="ignoreProp(pointBuy._id)"
|
||||
@click="editPointBuy(pointBuy._id)"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-for="slot in slots"
|
||||
:key="slot._id"
|
||||
style="transition: all 0.3s !important"
|
||||
>
|
||||
<slot-card
|
||||
:model="slot"
|
||||
hover
|
||||
@ignore="ignoreProp(slot._id)"
|
||||
@click="fillSlot(slot._id)"
|
||||
/>
|
||||
</div>
|
||||
</v-fade-transition>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
multiple
|
||||
hover
|
||||
>
|
||||
<template v-for="libraryNode in libraryNodes">
|
||||
<template v-for="libraryNode in [...selectedExcludedNodes, ...libraryNodes]">
|
||||
<v-expansion-panel
|
||||
v-if="showDisabled || !libraryNode._disabledBySlotFillerCondition"
|
||||
:key="libraryNode._id"
|
||||
@@ -245,7 +245,7 @@ import Libraries from '/imports/api/library/Libraries.js';
|
||||
import LibraryNodeExpansionContent from '/imports/client/ui/library/LibraryNodeExpansionContent.vue';
|
||||
import PropertyTags from '/imports/client/ui/properties/viewers/shared/PropertyTags.vue';
|
||||
import { getPropertyName } from '/imports/constants/PROPERTIES.js';
|
||||
import { clone } from 'lodash';
|
||||
import { clone, difference } from 'lodash';
|
||||
import getDefaultSlotFiller from '/imports/api/library/methods/getDefaultSlotFiller.js';
|
||||
import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js';
|
||||
import insertProperty from '/imports/api/creature/creatureProperties/methods/insertProperty.js';
|
||||
@@ -400,6 +400,9 @@ export default {
|
||||
'slotFillers'() {
|
||||
return [this.slotId || this.dummySlot?._id, this.searchValue || undefined, !!this.dummySlot]
|
||||
},
|
||||
'selectedFillers'() {
|
||||
return [this.slotId || this.dummySlot?._id, this.selectedNodeIds, !!this.dummySlot]
|
||||
},
|
||||
},
|
||||
searchLoading() {
|
||||
return !!this.searchValue && !this.$subReady.slotFillers;
|
||||
@@ -541,6 +544,11 @@ export default {
|
||||
this.disabledNodeCount = disabledNodeCount;
|
||||
return nodes;
|
||||
},
|
||||
selectedExcludedNodes() {
|
||||
const displayedIds = this.libraryNodes.map(node => node._id);
|
||||
const excludedNodeIds = difference(this.selectedNodeIds, displayedIds);
|
||||
return LibraryNodes.find({ _id: { $in: excludedNodeIds } });
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -4,7 +4,6 @@ import DOMPurify from 'dompurify';
|
||||
marked.setOptions({
|
||||
breaks: true,
|
||||
gfm: true,
|
||||
sanitizer: DOMPurify.sanitize,
|
||||
silent: true,
|
||||
smartLists: true,
|
||||
smartypants: true,
|
||||
|
||||
@@ -71,9 +71,14 @@
|
||||
</div>
|
||||
<div class="px-3 pb-3">
|
||||
<template
|
||||
v-if="model.resources && model.resources.attributesConsumed.length ||
|
||||
model.resources.itemsConsumed.length"
|
||||
v-if="showResources"
|
||||
>
|
||||
<action-condition-view
|
||||
v-for="condition in model.resources.conditions"
|
||||
:key="condition._id"
|
||||
class="action-child"
|
||||
:model="condition"
|
||||
/>
|
||||
<attribute-consumed-view
|
||||
v-for="attributeConsumed in model.resources.attributesConsumed"
|
||||
:key="attributeConsumed._id"
|
||||
@@ -99,6 +104,7 @@
|
||||
<tree-node-list
|
||||
v-if="children && children.length"
|
||||
start-expanded
|
||||
show-external-details
|
||||
:children="children"
|
||||
@selected="e => $emit('sub-click', e)"
|
||||
/>
|
||||
@@ -111,6 +117,7 @@
|
||||
import { getPropertyName } from '/imports/constants/PROPERTIES.js';
|
||||
import numberToSignedString from '../../../../../api/utility/numberToSignedString.js';
|
||||
import doAction from '/imports/api/engine/actions/doAction.js';
|
||||
import ActionConditionView from '/imports/client/ui/properties/components/actions/ActionConditionView.vue';
|
||||
import AttributeConsumedView from '/imports/client/ui/properties/components/actions/AttributeConsumedView.vue';
|
||||
import ItemConsumedView from '/imports/client/ui/properties/components/actions/ItemConsumedView.vue';
|
||||
import PropertyIcon from '/imports/client/ui/properties/shared/PropertyIcon.vue';
|
||||
@@ -125,6 +132,7 @@ import { some } from 'lodash';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ActionConditionView,
|
||||
AttributeConsumedView,
|
||||
ItemConsumedView,
|
||||
MarkdownText,
|
||||
@@ -161,6 +169,11 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
showResources() {
|
||||
if (this.model.resources?.attributesConsumed?.length
|
||||
|| this.model.resources?.itemsConsumed?.length) return true;
|
||||
return some(this.model.resources?.conditions, con => con.condition && !con.condition.value);
|
||||
},
|
||||
rollBonus() {
|
||||
if (!this.model.attackRoll) return;
|
||||
return numberToSignedString(this.model.attackRoll.value);
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<template lang="html">
|
||||
<div
|
||||
v-if="insufficient"
|
||||
class="layout align-center justify-start error--text"
|
||||
>
|
||||
<div
|
||||
class="mr-2 text-no-wrap text-truncate"
|
||||
style="min-width: 24px; text-align: center;"
|
||||
>
|
||||
{{ model.conditionNote || (model.condition && model.condition.calculation) || 'No condition specified' }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
export default {
|
||||
props: {
|
||||
model: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
insufficient(){
|
||||
return !this.model.condition?.value;
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -5,12 +5,13 @@
|
||||
>
|
||||
<div
|
||||
v-if="model.quantity && model.quantity.value !== 1"
|
||||
class="mr-2"
|
||||
style="width: 24px; text-align: center;"
|
||||
class="mr-2 text-no-wrap text-truncate"
|
||||
style="min-width: 24px; text-align: center;"
|
||||
>
|
||||
{{ model.quantity.value }}
|
||||
</div>
|
||||
<div
|
||||
v-if="model.quantity && (typeof model.quantity.value !== 'string')"
|
||||
class="text-no-wrap text-truncate"
|
||||
>
|
||||
{{ model.statName || model.variableName }}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<v-list-item-content>
|
||||
<v-list-item-title v-if="Number.isFinite(model.total)">
|
||||
<div
|
||||
v-if="model.total > 4"
|
||||
v-if="model.total <= 0 || model.total > 5 || model.value > model.total || model.value < 0"
|
||||
class="layout value"
|
||||
style="align-items: baseline;"
|
||||
>
|
||||
@@ -18,7 +18,10 @@
|
||||
>
|
||||
{{ model.value }}
|
||||
</div>
|
||||
<div class="ml-2 max-value">
|
||||
<div
|
||||
v-if="model.total"
|
||||
class="ml-2 max-value"
|
||||
>
|
||||
/{{ model.total }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<template lang="html">
|
||||
<v-row dense>
|
||||
<v-col
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<computed-field
|
||||
label="Condition"
|
||||
hint="This must be true for the action to be taken"
|
||||
:model="model.condition"
|
||||
:error-messages="errors.condition"
|
||||
@change="({path, value, ack}) =>
|
||||
$emit('change', {path: ['condition', ...path], value, ack})"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col
|
||||
cols="12"
|
||||
md="6"
|
||||
>
|
||||
<text-field
|
||||
label="Condition error text"
|
||||
:value="model.conditionNote"
|
||||
:error-messages="errors.conditionNote"
|
||||
@change="(value, ack) => $emit('change', {path: ['conditionNote'], value, ack})"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import propertyFormMixin from '/imports/client/ui/properties/forms/shared/propertyFormMixin.js';
|
||||
|
||||
export default {
|
||||
mixins: [propertyFormMixin],
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,41 @@
|
||||
<template lang="html">
|
||||
<div class="mt-4">
|
||||
<v-slide-x-transition group>
|
||||
<div
|
||||
v-for="(condition, i) in model"
|
||||
:key="condition._id || i"
|
||||
>
|
||||
<div class="layout align-center">
|
||||
<div style="flex-grow: 1;">
|
||||
<action-condition-form
|
||||
:model="condition"
|
||||
@change="({path, value, ack}) => change([i, ...path], value, ack)"
|
||||
/>
|
||||
</div>
|
||||
<v-btn
|
||||
outlined
|
||||
icon
|
||||
large
|
||||
class="ma-3"
|
||||
style="margin-bottom: 30px !important;"
|
||||
@click="$emit('pull', {path: [i]})"
|
||||
>
|
||||
<v-icon>mdi-delete</v-icon>
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
</v-slide-x-transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import ActionConditionForm from '/imports/client/ui/properties/forms/ActionConditionForm.vue';
|
||||
import propertyFormMixin from '/imports/client/ui/properties/forms/shared/propertyFormMixin.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ActionConditionForm,
|
||||
},
|
||||
mixins: [propertyFormMixin],
|
||||
}
|
||||
</script>
|
||||
@@ -1,5 +1,17 @@
|
||||
<template lang="html">
|
||||
<div class="resources-form">
|
||||
<div
|
||||
v-if="model.conditions && model.conditions.length"
|
||||
class="subheading"
|
||||
>
|
||||
Conditions
|
||||
</div>
|
||||
<action-conditions-list-form
|
||||
:model="model.conditions"
|
||||
@change="({path, value, ack}) => $emit('change', {path: ['conditions', ...path], value, ack})"
|
||||
@push="({path, value, ack}) => $emit('push', {path: ['conditions', ...path], value, ack})"
|
||||
@pull="({path, ack}) => $emit('pull', {path: ['conditions', ...path], ack})"
|
||||
/>
|
||||
<div
|
||||
v-if="model.attributesConsumed && model.attributesConsumed.length"
|
||||
class="subheading"
|
||||
@@ -43,6 +55,9 @@
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-list>
|
||||
<v-list-item @click="addCondition">
|
||||
<v-list-item-title>Add Condition</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item @click="addAttributesConsumed">
|
||||
<v-list-item-title>Add Resource</v-list-item-title>
|
||||
</v-list-item>
|
||||
@@ -56,11 +71,13 @@
|
||||
|
||||
<script lang="js">
|
||||
import AttributesConsumedListForm from '/imports/client/ui/properties/forms/AttributesConsumedListForm.vue';
|
||||
import ActionConditionsListForm from '/imports/client/ui/properties/forms/ActionConditionsListForm.vue';
|
||||
import ItemsConsumedListForm from '/imports/client/ui/properties/forms/ItemsConsumedListForm.vue';
|
||||
import propertyFormMixin from '/imports/client/ui/properties/forms/shared/propertyFormMixin.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ActionConditionsListForm,
|
||||
AttributesConsumedListForm,
|
||||
ItemsConsumedListForm,
|
||||
},
|
||||
@@ -102,6 +119,14 @@ export default {
|
||||
ack: this.acknowledgeAddResult,
|
||||
});
|
||||
},
|
||||
addCondition() {
|
||||
this.addResourceLoading = true;
|
||||
this.$emit('push', {
|
||||
path: ['conditions'],
|
||||
value: { _id: Random.id() },
|
||||
ack: this.acknowledgeAddResult,
|
||||
});
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -199,5 +199,7 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="css">
|
||||
|
||||
.property-viewer ol, .property-viewer ul {
|
||||
padding-left: 36px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,17 +1,32 @@
|
||||
<template lang="html">
|
||||
<div class="layout align-center justify-start">
|
||||
<v-icon
|
||||
v-if="!hideIcon"
|
||||
class="mr-2"
|
||||
:color="model.color"
|
||||
:class="selected && 'primary--text'"
|
||||
>
|
||||
{{ icon }}
|
||||
</v-icon>
|
||||
<div class="text-no-wrap text-truncate">
|
||||
{{ model.amount && model.amount.value }}
|
||||
{{ model.damageType }}<span v-if="model.damageType !== 'healing'"> damage</span>
|
||||
<span v-if="model.target === 'self'">to self</span>
|
||||
<div>
|
||||
<div class="layout align-center justify-start" style="height:40px;">
|
||||
<v-icon
|
||||
v-if="!hideIcon"
|
||||
class="mr-2"
|
||||
:color="model.color"
|
||||
:class="selected && 'primary--text'"
|
||||
>
|
||||
{{ icon }}
|
||||
</v-icon>
|
||||
<div class="text-no-wrap text-truncate">
|
||||
{{ model.amount && model.amount.value }}
|
||||
{{ model.damageType }}<span v-if="model.damageType !== 'healing'"> damage</span>
|
||||
<span v-if="model.target === 'self'">to self</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="showExternalDetails"
|
||||
v-for="effect in model.amount.effects">
|
||||
<div v-if="effect.amount.value !== 0"
|
||||
style="position:relative; top:-15px; left:5px; height:25px;">
|
||||
<inline-effect
|
||||
hide-breadcrumbs
|
||||
:key="effect._id"
|
||||
:data-id="effect._id"
|
||||
:model="effect"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -19,9 +34,11 @@
|
||||
<script lang="js">
|
||||
import treeNodeViewMixin from '/imports/client/ui/properties/treeNodeViews/treeNodeViewMixin.js';
|
||||
import { getPropertyIcon } from '/imports/constants/PROPERTIES.js';
|
||||
import InlineEffect from '../components/effects/InlineEffect.vue';
|
||||
|
||||
export default {
|
||||
mixins: [treeNodeViewMixin],
|
||||
components: {InlineEffect},
|
||||
computed: {
|
||||
icon() {
|
||||
if (this.model.damageType === 'healing') {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
:is="treeNodeView"
|
||||
:model="model"
|
||||
:selected="selected"
|
||||
:show-external-details="showExternalDetails"
|
||||
:class="{
|
||||
'inactive': model.inactive,
|
||||
}"
|
||||
@@ -24,6 +25,7 @@ export default {
|
||||
required: true,
|
||||
},
|
||||
selected: Boolean,
|
||||
showExternalDetails: Boolean,
|
||||
},
|
||||
computed: {
|
||||
treeNodeView(){
|
||||
|
||||
@@ -12,6 +12,7 @@ export default {
|
||||
},
|
||||
selected: Boolean,
|
||||
hideIcon: Boolean,
|
||||
showExternalDetails: Boolean,
|
||||
},
|
||||
computed: {
|
||||
title(){
|
||||
|
||||
@@ -64,6 +64,19 @@
|
||||
name="Reset"
|
||||
:value="reset"
|
||||
/>
|
||||
<property-field
|
||||
v-if="model.resources.conditions.length"
|
||||
name="Conditions"
|
||||
>
|
||||
<div style="width: 100%;">
|
||||
<action-condition-view
|
||||
v-for="condition in model.resources.conditions"
|
||||
:key="condition._id"
|
||||
class="action-child"
|
||||
:model="condition"
|
||||
/>
|
||||
</div>
|
||||
</property-field>
|
||||
<property-field
|
||||
v-if="model.resources.attributesConsumed.length"
|
||||
name="Attributes consumed"
|
||||
@@ -107,6 +120,7 @@
|
||||
<script lang="js">
|
||||
import propertyViewerMixin from '/imports/client/ui/properties/viewers/shared/propertyViewerMixin.js';
|
||||
import doAction from '/imports/api/engine/actions/doAction.js';
|
||||
import ActionConditionView from '/imports/client/ui/properties/components/actions/ActionConditionView.vue';
|
||||
import AttributeConsumedView from '/imports/client/ui/properties/components/actions/AttributeConsumedView.vue';
|
||||
import ItemConsumedView from '/imports/client/ui/properties/components/actions/ItemConsumedView.vue';
|
||||
import PropertyIcon from '/imports/client/ui/properties/shared/PropertyIcon.vue';
|
||||
@@ -116,6 +130,7 @@ import { snackbar } from '/imports/client/ui/components/snackbars/SnackbarQueue.
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ActionConditionView,
|
||||
AttributeConsumedView,
|
||||
ItemConsumedView,
|
||||
PropertyIcon,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated automatically by nearley, version 2.20.1
|
||||
// Generated automatically by nearley, version 2.16.0
|
||||
// http://github.com/Hardmath123/nearley
|
||||
function id(x) { return x[0]; }
|
||||
|
||||
@@ -93,7 +93,7 @@ let ParserRules = [
|
||||
d => [d[2], ...d[3]]
|
||||
},
|
||||
{"name": "arguments", "symbols": [{"literal":"("}, "_", {"literal":")"}], "postprocess": d => []},
|
||||
{"name": "indexExpression", "symbols": ["arrayExpression", {"literal":"["}, "_", "expression", "_", {"literal":"]"}], "postprocess": d => node.index.create({array: d[0], index: d[3]})},
|
||||
{"name": "indexExpression", "symbols": ["indexExpression", {"literal":"["}, "_", "expression", "_", {"literal":"]"}], "postprocess": d => node.index.create({array: d[0], index: d[3]})},
|
||||
{"name": "indexExpression", "symbols": ["arrayExpression"], "postprocess": id},
|
||||
{"name": "arrayExpression$subexpression$1", "symbols": ["expression"], "postprocess": d => d[0]},
|
||||
{"name": "arrayExpression$ebnf$1", "symbols": []},
|
||||
|
||||
@@ -125,7 +125,7 @@ arguments ->
|
||||
| "(" _ ")" {% d => [] %}
|
||||
|
||||
indexExpression ->
|
||||
arrayExpression "[" _ expression _ "]" {% d => node.index.create({array: d[0], index: d[3]}) %}
|
||||
indexExpression "[" _ expression _ "]" {% d => node.index.create({array: d[0], index: d[3]}) %}
|
||||
| arrayExpression {% id %}
|
||||
|
||||
arrayExpression ->
|
||||
|
||||
@@ -7,6 +7,58 @@ import getCreatureLibraryIds from '/imports/api/library/getCreatureLibraryIds.js
|
||||
import { LIBRARY_NODE_TREE_FIELDS } from '/imports/server/publications/library.js';
|
||||
import escapeRegex from '/imports/api/utility/escapeRegex.js';
|
||||
|
||||
// Publish docs the user has already selected so they don't disappear when searching
|
||||
Meteor.publish('selectedFillers', function (slotId, nodeIds, isDummySlot) {
|
||||
let autorun = this.autorun;
|
||||
autorun(function () {
|
||||
let userId = this.userId;
|
||||
if (!userId) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Get the slot from the right collection
|
||||
let slot;
|
||||
if (isDummySlot) {
|
||||
slot = LibraryNodes.findOne(slotId);
|
||||
} else {
|
||||
slot = CreatureProperties.findOne(slotId);
|
||||
}
|
||||
|
||||
if (!slot) return [];
|
||||
|
||||
// Get all the ids of libraries the user can access
|
||||
const creatureId = slot.ancestors[0].id;
|
||||
const libraryIds = getCreatureLibraryIds(creatureId, userId);
|
||||
const libraries = Libraries.find({
|
||||
$or: [
|
||||
{ owner: userId },
|
||||
{ writers: userId },
|
||||
{ readers: userId },
|
||||
{ _id: { $in: libraryIds }, public: true },
|
||||
]
|
||||
}, {
|
||||
sort: { name: 1 }
|
||||
});
|
||||
|
||||
let filter = { _id: { $in: nodeIds } };
|
||||
// Get the limit of the documents the user can fetch
|
||||
let options = {
|
||||
sort: {
|
||||
name: 1,
|
||||
order: 1,
|
||||
},
|
||||
limit: 100,
|
||||
fields: LIBRARY_NODE_TREE_FIELDS,
|
||||
};
|
||||
autorun(function () {
|
||||
return [
|
||||
LibraryNodes.find(filter, options),
|
||||
libraries
|
||||
];
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Meteor.publish('slotFillers', function (slotId, searchTerm, isDummySlot) {
|
||||
if (searchTerm) check(searchTerm, String);
|
||||
|
||||
@@ -50,6 +102,7 @@ Meteor.publish('slotFillers', function (slotId, searchTerm, isDummySlot) {
|
||||
|
||||
let options = undefined;
|
||||
if (searchTerm) {
|
||||
if (!filter.$and) filter.$and = [];
|
||||
filter.$and.push({
|
||||
$or: [
|
||||
{ name: { $regex: escapeRegex(searchTerm), '$options': 'i' } },
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "dicecloud",
|
||||
"version": "2.0.55",
|
||||
"version": "2.0.57",
|
||||
"description": "Unofficial Online Realtime D&D 5e App",
|
||||
"license": "GPL-3.0",
|
||||
"repository": {
|
||||
|
||||
Submodule app/packages/redis-oplog deleted from 83e302c154
Reference in New Issue
Block a user