Compare commits
15 Commits
2.0-beta.3
...
2.0-beta.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d66c06107 | ||
|
|
21629138f0 | ||
|
|
59a488256b | ||
|
|
766519b4a3 | ||
|
|
e7f73d0e54 | ||
|
|
193d5eec50 | ||
|
|
9284c9ad76 | ||
|
|
f86152675f | ||
|
|
cbac5264cd | ||
|
|
34e3325464 | ||
|
|
79c9e67ce2 | ||
|
|
4c2aabf90d | ||
|
|
48331d3806 | ||
|
|
45f05d0d34 | ||
|
|
58629c92f4 |
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
build
|
||||||
@@ -2,6 +2,7 @@ import action from './applyPropertyByType/applyAction.js';
|
|||||||
import adjustment from './applyPropertyByType/applyAdjustment.js';
|
import adjustment from './applyPropertyByType/applyAdjustment.js';
|
||||||
import branch from './applyPropertyByType/applyBranch.js';
|
import branch from './applyPropertyByType/applyBranch.js';
|
||||||
import buff from './applyPropertyByType/applyBuff.js';
|
import buff from './applyPropertyByType/applyBuff.js';
|
||||||
|
import buffRemover from './applyPropertyByType/applyBuffRemover.js';
|
||||||
import damage from './applyPropertyByType/applyDamage.js';
|
import damage from './applyPropertyByType/applyDamage.js';
|
||||||
import note from './applyPropertyByType/applyNote.js';
|
import note from './applyPropertyByType/applyNote.js';
|
||||||
import roll from './applyPropertyByType/applyRoll.js';
|
import roll from './applyPropertyByType/applyRoll.js';
|
||||||
@@ -13,6 +14,7 @@ const applyPropertyByType = {
|
|||||||
adjustment,
|
adjustment,
|
||||||
branch,
|
branch,
|
||||||
buff,
|
buff,
|
||||||
|
buffRemover,
|
||||||
damage,
|
damage,
|
||||||
note,
|
note,
|
||||||
roll,
|
roll,
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js'
|
|||||||
export default function applyAction(node, actionContext) {
|
export default function applyAction(node, actionContext) {
|
||||||
applyNodeTriggers(node, 'before', actionContext);
|
applyNodeTriggers(node, 'before', actionContext);
|
||||||
const prop = node.node;
|
const prop = node.node;
|
||||||
let targets = actionContext.targets;
|
if (prop.target === 'self') actionContext.targets = [actionContext.creature];
|
||||||
if (prop.target === 'self') targets = [actionContext.creature];
|
const targets = actionContext.targets;
|
||||||
|
|
||||||
// Log the name and summary
|
// Log the name and summary
|
||||||
let content = { name: prop.name };
|
let content = { name: prop.name };
|
||||||
|
|||||||
@@ -21,10 +21,14 @@ export default function applyBuff(node, actionContext){
|
|||||||
|
|
||||||
// Then copy the decendants of the buff to the targets
|
// Then copy the decendants of the buff to the targets
|
||||||
let propList = [prop];
|
let propList = [prop];
|
||||||
function addChildrenToPropList(children){
|
function addChildrenToPropList(children, { skipCrystalize } = {}){
|
||||||
children.forEach(child => {
|
children.forEach(child => {
|
||||||
|
if (skipCrystalize) child.node._skipCrystalize = true;
|
||||||
propList.push(child.node);
|
propList.push(child.node);
|
||||||
addChildrenToPropList(child.children);
|
// recursively add the child's children, but don't crystalize nested buffs
|
||||||
|
addChildrenToPropList(child.children, {
|
||||||
|
skipCrystalize: skipCrystalize || child.node.type === 'buff'
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
addChildrenToPropList(node.children);
|
addChildrenToPropList(node.children);
|
||||||
@@ -88,6 +92,10 @@ function copyNodeListToTarget(propList, target, oldParent){
|
|||||||
*/
|
*/
|
||||||
function crystalizeVariables({propList, actionContext}){
|
function crystalizeVariables({propList, actionContext}){
|
||||||
propList.forEach(prop => {
|
propList.forEach(prop => {
|
||||||
|
if (prop._skipCrystalize) {
|
||||||
|
delete prop._skipCrystalize;
|
||||||
|
return;
|
||||||
|
}
|
||||||
computedSchemas[prop.type].computedFields().forEach( calcKey => {
|
computedSchemas[prop.type].computedFields().forEach( calcKey => {
|
||||||
applyFnToKey(prop, calcKey, (prop, key) => {
|
applyFnToKey(prop, calcKey, (prop, key) => {
|
||||||
const calcObj = get(prop, key);
|
const calcObj = get(prop, key);
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
import { findLast, difference, intersection, filter } from 'lodash';
|
||||||
|
import applyProperty from '../applyProperty.js';
|
||||||
|
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||||
|
import { getProperyAncestors, getPropertiesOfType } from '/imports/api/engine/loadCreatures.js';
|
||||||
|
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';
|
||||||
|
|
||||||
|
export default function applyBuffRemover(node, actionContext) {
|
||||||
|
// Apply triggers
|
||||||
|
applyNodeTriggers(node, 'before', actionContext);
|
||||||
|
|
||||||
|
const prop = node.node;
|
||||||
|
|
||||||
|
// Log Name
|
||||||
|
if (prop.name){
|
||||||
|
actionContext.addLog({ name: prop.name });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove buffs
|
||||||
|
if (prop.targetParentBuff) {
|
||||||
|
// Remove nearest ancestor buff
|
||||||
|
const ancestors = getProperyAncestors(actionContext.creature._id, prop._id);
|
||||||
|
const nearestBuff = findLast(ancestors, ancestor => ancestor.type === 'buff');
|
||||||
|
if (!nearestBuff) {
|
||||||
|
actionContext.addLog({
|
||||||
|
name: 'Error',
|
||||||
|
value: 'Buff remover does not have a parent buff to remove',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
removeBuff(nearestBuff, actionContext);
|
||||||
|
} else {
|
||||||
|
// Get all the buffs targeted by tags
|
||||||
|
const allBuffs = getPropertiesOfType(actionContext.creature._id, 'buff');
|
||||||
|
const targetedBuffs = filter(allBuffs, buff => {
|
||||||
|
if (buff.inactive) return false;
|
||||||
|
if (buffRemoverMatchTags(prop, buff)) return true;
|
||||||
|
});
|
||||||
|
// Remove the buffs
|
||||||
|
if (prop.removeAll) {
|
||||||
|
// Remove all matching buffs
|
||||||
|
targetedBuffs.forEach(buff => {
|
||||||
|
removeBuff(buff, actionContext);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Sort in reverse order
|
||||||
|
targetedBuffs.sort((a, b) => b.order - a.order);
|
||||||
|
// Remove the one with the highest order
|
||||||
|
const buff = targetedBuffs[0];
|
||||||
|
if (buff) {
|
||||||
|
removeBuff(buff, actionContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply triggers
|
||||||
|
applyNodeTriggers(node, 'after', actionContext);
|
||||||
|
// Apply children
|
||||||
|
node.children.forEach(child => applyProperty(child, actionContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeBuff(buff, actionContext) {
|
||||||
|
actionContext.addLog({
|
||||||
|
name: 'Removed',
|
||||||
|
value: `${buff.name || 'Buff'}`
|
||||||
|
});
|
||||||
|
softRemove({ _id: buff._id, collection: CreatureProperties });
|
||||||
|
}
|
||||||
|
|
||||||
|
function buffRemoverMatchTags(buffRemover, prop) {
|
||||||
|
let matched = false;
|
||||||
|
const propTags = getEffectivePropTags(prop);
|
||||||
|
// Check the target tags
|
||||||
|
if (
|
||||||
|
!buffRemover.targetTags?.length ||
|
||||||
|
difference(buffRemover.targetTags, propTags).length === 0
|
||||||
|
) {
|
||||||
|
matched = true;
|
||||||
|
}
|
||||||
|
// Check the extra tags
|
||||||
|
buffRemover.extraTags?.forEach(extra => {
|
||||||
|
if (extra.operation === 'OR') {
|
||||||
|
if (matched) return;
|
||||||
|
if (
|
||||||
|
!extra.tags.length ||
|
||||||
|
difference(extra.tags, propTags).length === 0
|
||||||
|
) {
|
||||||
|
matched = true;
|
||||||
|
}
|
||||||
|
} else if (extra.operation === 'NOT') {
|
||||||
|
if (
|
||||||
|
extra.tags.length &&
|
||||||
|
intersection(extra.tags, propTags)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return matched;
|
||||||
|
}
|
||||||
@@ -10,8 +10,10 @@ export default function computeToggleDependencies(node, dependencyGraph){
|
|||||||
prop.enabled
|
prop.enabled
|
||||||
) return;
|
) return;
|
||||||
walkDown(node.children, child => {
|
walkDown(node.children, child => {
|
||||||
child.node._computationDetails.toggleAncestors.push(prop);
|
// Only for children that aren't inactive
|
||||||
|
if (child.node.inactive) return;
|
||||||
// The child nodes depend on the toggle condition compuation
|
// The child nodes depend on the toggle condition compuation
|
||||||
|
child.node._computationDetails.toggleAncestors.push(prop);
|
||||||
dependencyGraph.addLink(child.node._id, prop._id, 'toggle');
|
dependencyGraph.addLink(child.node._id, prop._id, 'toggle');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,7 +105,8 @@ function linkBuff(dependencyGraph, prop){
|
|||||||
dependOnCalc({dependencyGraph, prop, key: 'duration'});
|
dependOnCalc({dependencyGraph, prop, key: 'duration'});
|
||||||
}
|
}
|
||||||
|
|
||||||
function linkClassLevel(dependencyGraph, prop){
|
function linkClassLevel(dependencyGraph, prop) {
|
||||||
|
if (prop.inactive) return;
|
||||||
// The variableName of the prop depends on the prop
|
// The variableName of the prop depends on the prop
|
||||||
if (prop.variableName && prop.level){
|
if (prop.variableName && prop.level){
|
||||||
dependencyGraph.addLink(prop.variableName, prop._id, 'classLevel');
|
dependencyGraph.addLink(prop.variableName, prop._id, 'classLevel');
|
||||||
@@ -121,11 +122,16 @@ function linkDamage(dependencyGraph, prop){
|
|||||||
dependOnCalc({dependencyGraph, prop, key: 'amount'});
|
dependOnCalc({dependencyGraph, prop, key: 'amount'});
|
||||||
}
|
}
|
||||||
|
|
||||||
function linkEffects(dependencyGraph, prop, computation){
|
function linkEffects(dependencyGraph, prop, computation) {
|
||||||
// The effect depends on its amount calculation
|
// The effect depends on its amount calculation
|
||||||
dependOnCalc({dependencyGraph, prop, key: 'amount'});
|
dependOnCalc({ dependencyGraph, prop, key: 'amount' });
|
||||||
|
// Inactive effects aren't going to impact their targeted stats
|
||||||
|
if (prop.inactive) return;
|
||||||
// The stats depend on the effect
|
// The stats depend on the effect
|
||||||
if (prop.targetByTags){
|
if (prop.inactive) {
|
||||||
|
// Inactive effects apply to no stats
|
||||||
|
return;
|
||||||
|
} else if (prop.targetByTags){
|
||||||
getEffectTagTargets(prop, computation).forEach(targetId => {
|
getEffectTagTargets(prop, computation).forEach(targetId => {
|
||||||
const targetProp = computation.propsById[targetId];
|
const targetProp = computation.propsById[targetId];
|
||||||
if (
|
if (
|
||||||
@@ -221,13 +227,14 @@ function linkRoll(dependencyGraph, prop){
|
|||||||
}
|
}
|
||||||
|
|
||||||
function linkVariableName(dependencyGraph, prop){
|
function linkVariableName(dependencyGraph, prop){
|
||||||
// The variableName of the prop depends on the prop
|
// The variableName of the prop depends on the prop if the prop is active
|
||||||
if (prop.variableName){
|
if (prop.variableName && !prop.inactive){
|
||||||
dependencyGraph.addLink(prop.variableName, prop._id, 'definition');
|
dependencyGraph.addLink(prop.variableName, prop._id, 'definition');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function linkDamageMultiplier(dependencyGraph, prop){
|
function linkDamageMultiplier(dependencyGraph, prop) {
|
||||||
|
if (prop.inactive) return;
|
||||||
prop.damageTypes.forEach(damageType => {
|
prop.damageTypes.forEach(damageType => {
|
||||||
// Remove all non-letter characters from the damage name
|
// Remove all non-letter characters from the damage name
|
||||||
const damageName = damageType.replace(/[^a-z]/gi, '')
|
const damageName = damageType.replace(/[^a-z]/gi, '')
|
||||||
@@ -237,6 +244,7 @@ function linkDamageMultiplier(dependencyGraph, prop){
|
|||||||
|
|
||||||
function linkProficiencies(dependencyGraph, prop){
|
function linkProficiencies(dependencyGraph, prop){
|
||||||
// The stats depend on the proficiency
|
// The stats depend on the proficiency
|
||||||
|
if (prop.inactive) return;
|
||||||
prop.stats.forEach(statName => {
|
prop.stats.forEach(statName => {
|
||||||
if (!statName) return;
|
if (!statName) return;
|
||||||
dependencyGraph.addLink(statName, prop._id, prop.type);
|
dependencyGraph.addLink(statName, prop._id, prop.type);
|
||||||
@@ -248,6 +256,10 @@ function linkSavingThrow(dependencyGraph, prop){
|
|||||||
}
|
}
|
||||||
|
|
||||||
function linkSkill(dependencyGraph, prop){
|
function linkSkill(dependencyGraph, prop){
|
||||||
|
// Depends on base value
|
||||||
|
dependOnCalc({ dependencyGraph, prop, key: 'baseValue' });
|
||||||
|
// Link dependents
|
||||||
|
if (prop.inactive) return;
|
||||||
linkVariableName(dependencyGraph, prop);
|
linkVariableName(dependencyGraph, prop);
|
||||||
// The prop depends on the variable references as the ability
|
// The prop depends on the variable references as the ability
|
||||||
if (prop.ability){
|
if (prop.ability){
|
||||||
@@ -255,9 +267,6 @@ function linkSkill(dependencyGraph, prop){
|
|||||||
}
|
}
|
||||||
// Skills depend on the creature's proficiencyBonus
|
// Skills depend on the creature's proficiencyBonus
|
||||||
dependencyGraph.addLink(prop._id, 'proficiencyBonus', 'skillProficiencyBonus');
|
dependencyGraph.addLink(prop._id, 'proficiencyBonus', 'skillProficiencyBonus');
|
||||||
|
|
||||||
// Depends on base value
|
|
||||||
dependOnCalc({dependencyGraph, prop, key: 'baseValue'});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function linkSlot(dependencyGraph, prop){
|
function linkSlot(dependencyGraph, prop){
|
||||||
|
|||||||
@@ -89,6 +89,10 @@ export function buildComputationFromProps(properties, creature, variables){
|
|||||||
// Walk the property trees computing things that need to be inherited
|
// Walk the property trees computing things that need to be inherited
|
||||||
walkDown(forest, node => {
|
walkDown(forest, node => {
|
||||||
computeInactiveStatus(node);
|
computeInactiveStatus(node);
|
||||||
|
});
|
||||||
|
// Inactive status must be complete for the whole tree before toggle deps
|
||||||
|
// are calculated
|
||||||
|
walkDown(forest, node => {
|
||||||
computeToggleDependencies(node, dependencyGraph);
|
computeToggleDependencies(node, dependencyGraph);
|
||||||
computeSlotQuantityFilled(node, dependencyGraph);
|
computeSlotQuantityFilled(node, dependencyGraph);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export default function getAggregatorResult(node){
|
|||||||
if (aggregator.set !== undefined) {
|
if (aggregator.set !== undefined) {
|
||||||
result = aggregator.set;
|
result = aggregator.set;
|
||||||
}
|
}
|
||||||
if (!node.definingProp?.decimal && Number.isFinite(result)){
|
if (!node.data.definingProp?.decimal && Number.isFinite(result)){
|
||||||
result = Math.floor(result);
|
result = Math.floor(result);
|
||||||
} else if (Number.isFinite(result)){
|
} else if (Number.isFinite(result)){
|
||||||
result = stripFloatingPointOddities(result);
|
result = stripFloatingPointOddities(result);
|
||||||
|
|||||||
@@ -51,11 +51,22 @@ function compute(computation, node){
|
|||||||
|
|
||||||
function pushDependenciesToStack(nodeId, graph, stack, computation){
|
function pushDependenciesToStack(nodeId, graph, stack, computation){
|
||||||
graph.forEachLinkedNode(nodeId, linkedNode => {
|
graph.forEachLinkedNode(nodeId, linkedNode => {
|
||||||
if (linkedNode._visitedChildren && !linkedNode._visited){
|
if (linkedNode._visitedChildren && !linkedNode._visited) {
|
||||||
const pather = path.nba(graph, {
|
// This is a dependency loop, find a path from the node to itself
|
||||||
oriented: true
|
// and store that path as a dependency loop error
|
||||||
});
|
const pather = path.nba(graph, { oriented: true });
|
||||||
const loop = pather.find(nodeId, nodeId);
|
let loop = [];
|
||||||
|
// Pather doesn't like going from a node to iteself, so find all the
|
||||||
|
// paths going from the next node back to the original node
|
||||||
|
// and return the shortest one
|
||||||
|
graph.forEachLinkedNode(nodeId, nextNode => {
|
||||||
|
const newLoop = pather.find(nextNode.id, nodeId);
|
||||||
|
if (!newLoop.length) return;
|
||||||
|
if (!loop.length || newLoop.length < loop.length - 1) {
|
||||||
|
loop = [linkedNode, ...newLoop];
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
|
||||||
if (loop.length) {
|
if (loop.length) {
|
||||||
computation.errors.push({
|
computation.errors.push({
|
||||||
type: 'dependencyLoop',
|
type: 'dependencyLoop',
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ export function getSingleProperty(creatureId, propertyId) {
|
|||||||
'removed': {$ne: true},
|
'removed': {$ne: true},
|
||||||
}, {
|
}, {
|
||||||
sort: { order: 1 },
|
sort: { order: 1 },
|
||||||
fields: { icon: 0 },
|
|
||||||
});
|
});
|
||||||
// console.timeEnd(`Cache miss on creature properties: ${creatureId}`);
|
// console.timeEnd(`Cache miss on creature properties: ${creatureId}`);
|
||||||
return prop;
|
return prop;
|
||||||
@@ -65,7 +64,6 @@ export function getProperties(creatureId) {
|
|||||||
'removed': {$ne: true},
|
'removed': {$ne: true},
|
||||||
}, {
|
}, {
|
||||||
sort: { order: 1 },
|
sort: { order: 1 },
|
||||||
fields: { icon: 0 },
|
|
||||||
}).fetch();
|
}).fetch();
|
||||||
// console.timeEnd(`Cache miss on creature properties: ${creatureId}`);
|
// console.timeEnd(`Cache miss on creature properties: ${creatureId}`);
|
||||||
return props;
|
return props;
|
||||||
@@ -90,7 +88,6 @@ export function getPropertiesOfType(creatureId, propType) {
|
|||||||
'type': propType,
|
'type': propType,
|
||||||
}, {
|
}, {
|
||||||
sort: { order: 1 },
|
sort: { order: 1 },
|
||||||
fields: { icon: 0 },
|
|
||||||
}).fetch();
|
}).fetch();
|
||||||
// console.timeEnd(`Cache miss on creature properties: ${creatureId}`);
|
// console.timeEnd(`Cache miss on creature properties: ${creatureId}`);
|
||||||
return props;
|
return props;
|
||||||
@@ -103,11 +100,7 @@ export function getCreature(creatureId) {
|
|||||||
if (creature) return creature;
|
if (creature) return creature;
|
||||||
}
|
}
|
||||||
// console.time(`Cache miss on Creature: ${creatureId}`);
|
// console.time(`Cache miss on Creature: ${creatureId}`);
|
||||||
const creature = Creatures.findOne(creatureId, {
|
const creature = Creatures.findOne(creatureId);
|
||||||
denormalizedStats: 1,
|
|
||||||
variables: 1,
|
|
||||||
dirty: 1,
|
|
||||||
});
|
|
||||||
// console.timeEnd(`Cache miss on Creature: ${creatureId}`);
|
// console.timeEnd(`Cache miss on Creature: ${creatureId}`);
|
||||||
return creature;
|
return creature;
|
||||||
}
|
}
|
||||||
@@ -149,6 +142,7 @@ export function getProperyAncestors(creatureId, propertyId) {
|
|||||||
// Fetch from database
|
// Fetch from database
|
||||||
return CreatureProperties.find({
|
return CreatureProperties.find({
|
||||||
_id: { $in: ancestorIds },
|
_id: { $in: ancestorIds },
|
||||||
|
removed: {$ne: true},
|
||||||
}, {
|
}, {
|
||||||
sort: { order: 1 },
|
sort: { order: 1 },
|
||||||
}).fetch();
|
}).fetch();
|
||||||
@@ -175,6 +169,8 @@ export function getPropertyDecendants(creatureId, propertyId) {
|
|||||||
return CreatureProperties.find({
|
return CreatureProperties.find({
|
||||||
'ancestors.id': propertyId,
|
'ancestors.id': propertyId,
|
||||||
removed: { $ne: true },
|
removed: { $ne: true },
|
||||||
|
}, {
|
||||||
|
sort: { order: 1 },
|
||||||
}).fetch();
|
}).fetch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -199,7 +195,6 @@ class LoadedCreature {
|
|||||||
removed: { $ne: true },
|
removed: { $ne: true },
|
||||||
}, {
|
}, {
|
||||||
sort: { order: 1 },
|
sort: { order: 1 },
|
||||||
fields: { icon: 0 },
|
|
||||||
}).observeChanges({
|
}).observeChanges({
|
||||||
added(id, fields) {
|
added(id, fields) {
|
||||||
fields._id = id;
|
fields._id = id;
|
||||||
|
|||||||
79
app/imports/api/properties/BuffRemovers.js
Normal file
79
app/imports/api/properties/BuffRemovers.js
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import SimpleSchema from 'simpl-schema';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
import createPropertySchema from '/imports/api/properties/subSchemas/createPropertySchema.js';
|
||||||
|
|
||||||
|
let BuffRemoverSchema = createPropertySchema({
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
|
},
|
||||||
|
// This will remove just the nearest ancestor buff
|
||||||
|
targetParentBuff: {
|
||||||
|
type: Boolean,
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
|
// The following only applies when not targeting the parent buff
|
||||||
|
// Which character to remove buffs from
|
||||||
|
target: {
|
||||||
|
type: String,
|
||||||
|
allowedValues: [
|
||||||
|
'self',
|
||||||
|
'target',
|
||||||
|
],
|
||||||
|
defaultValue: 'target',
|
||||||
|
},
|
||||||
|
// remove 1 or remove all
|
||||||
|
removeAll: {
|
||||||
|
type: Boolean,
|
||||||
|
optional: true,
|
||||||
|
defaultValue: true,
|
||||||
|
},
|
||||||
|
// Buffs to remove based on tags:
|
||||||
|
targetTags: {
|
||||||
|
type: Array,
|
||||||
|
optional: true,
|
||||||
|
maxCount: STORAGE_LIMITS.tagCount,
|
||||||
|
},
|
||||||
|
'targetTags.$': {
|
||||||
|
type: String,
|
||||||
|
max: STORAGE_LIMITS.tagLength,
|
||||||
|
},
|
||||||
|
extraTags: {
|
||||||
|
type: Array,
|
||||||
|
optional: true,
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let ComputedOnlyBuffRemoverSchema = createPropertySchema({});
|
||||||
|
|
||||||
|
const ComputedBuffRemoverSchema = new SimpleSchema()
|
||||||
|
.extend(BuffRemoverSchema)
|
||||||
|
.extend(ComputedOnlyBuffRemoverSchema);
|
||||||
|
|
||||||
|
export { BuffRemoverSchema, ComputedOnlyBuffRemoverSchema, ComputedBuffRemoverSchema };
|
||||||
@@ -12,6 +12,10 @@ let BuffSchema = createPropertySchema({
|
|||||||
type: 'inlineCalculationFieldToCompute',
|
type: 'inlineCalculationFieldToCompute',
|
||||||
optional: true,
|
optional: true,
|
||||||
},
|
},
|
||||||
|
hideRemoveButton: {
|
||||||
|
type: Boolean,
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
// How many rounds this buff lasts
|
// How many rounds this buff lasts
|
||||||
duration: {
|
duration: {
|
||||||
type: 'fieldToCompute',
|
type: 'fieldToCompute',
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
|
||||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
import createPropertySchema from '/imports/api/properties/subSchemas/createPropertySchema.js';
|
||||||
|
|
||||||
// Folders organize a character sheet into a tree, particularly to group things
|
// Folders organize a character sheet into a tree, particularly to group things
|
||||||
// like 'race' and 'background'
|
// like 'race' and 'background'
|
||||||
let FolderSchema = new SimpleSchema({
|
let FolderSchema = new createPropertySchema({
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
max: STORAGE_LIMITS.name,
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const ComputedOnlyFolderSchema = new SimpleSchema({});
|
const ComputedOnlyFolderSchema = new createPropertySchema({});
|
||||||
|
|
||||||
export { FolderSchema, ComputedOnlyFolderSchema };
|
export { FolderSchema, ComputedOnlyFolderSchema };
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ const actionPropertyTypeOptions = {
|
|||||||
adjustment: 'Attribute damage',
|
adjustment: 'Attribute damage',
|
||||||
branch: 'Branch',
|
branch: 'Branch',
|
||||||
buff: 'Buff',
|
buff: 'Buff',
|
||||||
|
buffRemover: 'Buff Removed',
|
||||||
damage: 'Damage',
|
damage: 'Damage',
|
||||||
note: 'Note',
|
note: 'Note',
|
||||||
roll: 'Roll',
|
roll: 'Roll',
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { ComputedOnlyActionSchema } from '/imports/api/properties/Actions.js';
|
|||||||
import { ComputedOnlyAdjustmentSchema } from '/imports/api/properties/Adjustments.js';
|
import { ComputedOnlyAdjustmentSchema } from '/imports/api/properties/Adjustments.js';
|
||||||
import { ComputedOnlyAttributeSchema } from '/imports/api/properties/Attributes.js';
|
import { ComputedOnlyAttributeSchema } from '/imports/api/properties/Attributes.js';
|
||||||
import { ComputedOnlyBuffSchema } from '/imports/api/properties/Buffs.js';
|
import { ComputedOnlyBuffSchema } from '/imports/api/properties/Buffs.js';
|
||||||
|
import { ComputedOnlyBuffRemoverSchema } from '/imports/api/properties/BuffRemovers.js';
|
||||||
import { ComputedOnlyBranchSchema } from '/imports/api/properties/Branches.js';
|
import { ComputedOnlyBranchSchema } from '/imports/api/properties/Branches.js';
|
||||||
import { ComputedOnlyClassSchema } from '/imports/api/properties/Classes.js';
|
import { ComputedOnlyClassSchema } from '/imports/api/properties/Classes.js';
|
||||||
import { ComputedOnlyClassLevelSchema } from '/imports/api/properties/ClassLevels.js';
|
import { ComputedOnlyClassLevelSchema } from '/imports/api/properties/ClassLevels.js';
|
||||||
@@ -32,6 +33,7 @@ const propertySchemasIndex = {
|
|||||||
adjustment: ComputedOnlyAdjustmentSchema,
|
adjustment: ComputedOnlyAdjustmentSchema,
|
||||||
attribute: ComputedOnlyAttributeSchema,
|
attribute: ComputedOnlyAttributeSchema,
|
||||||
buff: ComputedOnlyBuffSchema,
|
buff: ComputedOnlyBuffSchema,
|
||||||
|
buffRemover: ComputedOnlyBuffRemoverSchema,
|
||||||
branch: ComputedOnlyBranchSchema,
|
branch: ComputedOnlyBranchSchema,
|
||||||
class: ComputedOnlyClassSchema,
|
class: ComputedOnlyClassSchema,
|
||||||
classLevel: ComputedOnlyClassLevelSchema,
|
classLevel: ComputedOnlyClassLevelSchema,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { ComputedActionSchema } from '/imports/api/properties/Actions.js';
|
|||||||
import { ComputedAdjustmentSchema } from '/imports/api/properties/Adjustments.js';
|
import { ComputedAdjustmentSchema } from '/imports/api/properties/Adjustments.js';
|
||||||
import { ComputedAttributeSchema } from '/imports/api/properties/Attributes.js';
|
import { ComputedAttributeSchema } from '/imports/api/properties/Attributes.js';
|
||||||
import { ComputedBuffSchema } from '/imports/api/properties/Buffs.js';
|
import { ComputedBuffSchema } from '/imports/api/properties/Buffs.js';
|
||||||
|
import { ComputedBuffRemoverSchema } from '/imports/api/properties/BuffRemovers.js';
|
||||||
import { ComputedBranchSchema } from '/imports/api/properties/Branches.js';
|
import { ComputedBranchSchema } from '/imports/api/properties/Branches.js';
|
||||||
import { ComputedClassSchema } from '/imports/api/properties/Classes.js';
|
import { ComputedClassSchema } from '/imports/api/properties/Classes.js';
|
||||||
import { ComputedClassLevelSchema } from '/imports/api/properties/ClassLevels.js';
|
import { ComputedClassLevelSchema } from '/imports/api/properties/ClassLevels.js';
|
||||||
@@ -32,6 +33,7 @@ const propertySchemasIndex = {
|
|||||||
adjustment: ComputedAdjustmentSchema,
|
adjustment: ComputedAdjustmentSchema,
|
||||||
attribute: ComputedAttributeSchema,
|
attribute: ComputedAttributeSchema,
|
||||||
buff: ComputedBuffSchema,
|
buff: ComputedBuffSchema,
|
||||||
|
buffRemover: ComputedBuffRemoverSchema,
|
||||||
branch: ComputedBranchSchema,
|
branch: ComputedBranchSchema,
|
||||||
class: ComputedClassSchema,
|
class: ComputedClassSchema,
|
||||||
classLevel: ComputedClassLevelSchema,
|
classLevel: ComputedClassLevelSchema,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { ActionSchema } from '/imports/api/properties/Actions.js';
|
|||||||
import { AdjustmentSchema } from '/imports/api/properties/Adjustments.js';
|
import { AdjustmentSchema } from '/imports/api/properties/Adjustments.js';
|
||||||
import { AttributeSchema } from '/imports/api/properties/Attributes.js';
|
import { AttributeSchema } from '/imports/api/properties/Attributes.js';
|
||||||
import { BuffSchema } from '/imports/api/properties/Buffs.js';
|
import { BuffSchema } from '/imports/api/properties/Buffs.js';
|
||||||
|
import { BuffRemoverSchema } from '/imports/api/properties/BuffRemovers.js';
|
||||||
import { BranchSchema } from '/imports/api/properties/Branches.js';
|
import { BranchSchema } from '/imports/api/properties/Branches.js';
|
||||||
import { ClassSchema } from '/imports/api/properties/Classes.js';
|
import { ClassSchema } from '/imports/api/properties/Classes.js';
|
||||||
import { ClassLevelSchema } from '/imports/api/properties/ClassLevels.js';
|
import { ClassLevelSchema } from '/imports/api/properties/ClassLevels.js';
|
||||||
@@ -32,6 +33,7 @@ const propertySchemasIndex = {
|
|||||||
adjustment: AdjustmentSchema,
|
adjustment: AdjustmentSchema,
|
||||||
attribute: AttributeSchema,
|
attribute: AttributeSchema,
|
||||||
buff: BuffSchema,
|
buff: BuffSchema,
|
||||||
|
buffRemover: BuffRemoverSchema,
|
||||||
branch: BranchSchema,
|
branch: BranchSchema,
|
||||||
class: ClassSchema,
|
class: ClassSchema,
|
||||||
classLevel: ClassLevelSchema,
|
classLevel: ClassLevelSchema,
|
||||||
|
|||||||
@@ -24,6 +24,12 @@ 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.',
|
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', 'branch'],
|
suggestedParents: ['action', 'attack', 'savingThrow', 'spell', 'branch'],
|
||||||
},
|
},
|
||||||
|
buffRemover: {
|
||||||
|
icon: '$vuetify.icons.buffRemover',
|
||||||
|
name: 'Remove Buff',
|
||||||
|
helpText: 'Removes a buff from the target character',
|
||||||
|
suggestedParents: ['action', 'attack', 'savingThrow', 'spell', 'branch'],
|
||||||
|
},
|
||||||
branch: {
|
branch: {
|
||||||
icon: 'mdi-file-tree',
|
icon: 'mdi-file-tree',
|
||||||
name: 'Branch',
|
name: 'Branch',
|
||||||
|
|||||||
@@ -59,6 +59,10 @@ const SVG_ICONS = Object.freeze({
|
|||||||
name: 'buff',
|
name: 'buff',
|
||||||
shape: 'M331.924 20.385c-36.708.887-82.53 60.972-116.063 147.972h.003c30.564-65.57 71.17-106.39 97.348-99.378 28.058 7.516 37.11 69.42 24.847 148.405-.895-.32-1.773-.642-2.672-.96.893.367 1.765.738 2.65 1.106-2.988 19.215-7.22 39.424-12.767 60.12-2.77 10.332-5.763 20.39-8.936 30.14-24.996-3.82-52.374-9.537-80.82-17.16-105.856-28.36-186.115-72.12-179.307-97.53 4.257-15.884 42.167-23.775 95.908-20.29-74.427-8.7-128.912-2.044-135.035 20.803-9.038 33.73 89.168 89.372 219.147 124.2 24.436 6.55 48.267 11.897 70.918 16.042-28.965 75.878-68.293 126.078-96.653 118.48-21.817-5.85-35.995-45.443-36.316-100.206-4.79 75.476 9.278 131.945 40.66 140.356 38.836 10.407 91.394-54.998 127.896-152.98 80.12 10.74 138.958 4.278 145.38-19.682 6.384-23.82-41.025-58.44-115.102-89.03 20.713-109.022 8.483-198.5-31.96-209.34-2.968-.796-6.013-1.144-9.124-1.07zm40.568 213.086c44.65 22.992 71.146 47.135 67.07 62.348-4.055 15.13-38.104 20.457-87.333 16.303 3.415-10.604 6.64-21.502 9.63-32.663 4.176-15.588 7.713-30.965 10.632-45.986z',
|
shape: 'M331.924 20.385c-36.708.887-82.53 60.972-116.063 147.972h.003c30.564-65.57 71.17-106.39 97.348-99.378 28.058 7.516 37.11 69.42 24.847 148.405-.895-.32-1.773-.642-2.672-.96.893.367 1.765.738 2.65 1.106-2.988 19.215-7.22 39.424-12.767 60.12-2.77 10.332-5.763 20.39-8.936 30.14-24.996-3.82-52.374-9.537-80.82-17.16-105.856-28.36-186.115-72.12-179.307-97.53 4.257-15.884 42.167-23.775 95.908-20.29-74.427-8.7-128.912-2.044-135.035 20.803-9.038 33.73 89.168 89.372 219.147 124.2 24.436 6.55 48.267 11.897 70.918 16.042-28.965 75.878-68.293 126.078-96.653 118.48-21.817-5.85-35.995-45.443-36.316-100.206-4.79 75.476 9.278 131.945 40.66 140.356 38.836 10.407 91.394-54.998 127.896-152.98 80.12 10.74 138.958 4.278 145.38-19.682 6.384-23.82-41.025-58.44-115.102-89.03 20.713-109.022 8.483-198.5-31.96-209.34-2.968-.796-6.013-1.144-9.124-1.07zm40.568 213.086c44.65 22.992 71.146 47.135 67.07 62.348-4.055 15.13-38.104 20.457-87.333 16.303 3.415-10.604 6.64-21.502 9.63-32.663 4.176-15.588 7.713-30.965 10.632-45.986z',
|
||||||
},
|
},
|
||||||
|
'perpendicular-rings-crossed-out': {
|
||||||
|
name: 'buffRemover',
|
||||||
|
shape: 'm 342.85706,29.520771 -10e-4,0.0017 C 311.09371,29.014264 281.87055,64.906082 247.60595,132.05856 L 172.54472,18.060082 142.22614,39.094548 433.13139,479.99983 459.83281,455.56605 385.30374,345.07055 c 60.47709,6.76324 95.40448,9.77027 101.5894,-10.08043 7.33528,-23.5444 -38.64515,-60.03954 -111.4339,-93.57921 25.07382,-108.10207 16.44748,-197.999126 -23.52751,-210.454283 -2.93364,-0.914533 -5.96321,-1.384872 -9.07467,-1.435856 z M 318.01727,76.44599 c 1.43939,0.165657 2.83703,0.458385 4.19071,0.879905 27.7335,8.636553 34.29121,70.855545 18.86666,149.284215 -0.88143,-0.35568 -1.74564,-0.7136 -2.63114,-1.06745 0.87755,0.40257 1.73435,0.80776 2.60387,1.21101 -3.29527,16.73399 -2.43292,17.48734 -7.73765,35.32955 L 249.50725,134.94386 C 277.16641,91.939481 296.57992,73.978789 318.01727,76.44599 Z M 21.309482,189.96942 c -10.385032,33.3398 85.507398,92.87964 213.982728,132.89854 18.67653,5.81939 37.00521,10.88752 54.75098,15.23025 -7.08963,-10.99585 -14.12857,-21.92647 -21.25144,-32.93262 -2.34002,-0.62085 -4.72317,-1.30046 -7.07998,-1.94408 -8.56911,-2.33994 -17.21776,-4.79347 -26.04044,-7.54193 C 131.03978,263.09208 52.602729,216.14328 60.425516,191.02722 65.316857,175.32699 103.51348,168.9655 157.07104,174.60556 121.54273,171.56302 37.239273,147.38028 21.309482,189.96942 Z M 374.83456,244.0657 c 43.69065,24.76623 69.19628,49.95346 64.51274,64.99048 -4.48269,14.38828 -26.92387,13.08335 -72.99123,7.92393 L 362.747,311.5007 c -5.21026,-7.91039 -3.28718,-12.58648 -0.38327,-21.91074 4.79853,-15.40771 8.94901,-30.63076 12.46879,-45.52239 z m -74.68362,109.69514 c -31.13564,67.76298 -69.48034,110.7402 -95.97392,102.48874 -21.56445,-6.72129 -34.14164,-46.85287 -32.26347,-101.58445 -7.81672,75.22256 3.97274,132.21064 34.99162,141.87493 32.83746,10.2293 77.9122,-34.67 115.55909,-108.20499 -7.40453,-11.47654 -14.88401,-23.0444 -22.31332,-34.57423 z'
|
||||||
|
},
|
||||||
'roll': {
|
'roll': {
|
||||||
name: 'roll',
|
name: 'roll',
|
||||||
shape: 'M 339.33314,69.985523 152.23146,95.161367 297.9199,159.07076 Z m 13.13639,6.106743 -41.41324,89.085234 142.77019,70.18694 z M 286.72755,169.97878 132.07338,102.13811 116.91912,287.72473 Z m 23.20215,10.78603 19.43763,205.72115 132.11738,-131.21375 z m -14.47505,0.7893 -172.49845,119.61061 192.24446,89.36907 z M 115.16567,131.2299 49.03503,273.48548 l 52.96523,18.92787 z m 334.97786,155.72184 -114.74252,113.9631 48.61189,28.29247 z m -329.82146,28.96669 45.63657,144.20053 139.66241,-58.06123 z m -17.69094,-7.89455 -46.061724,-16.46047 81.264174,127.69506 z m 220.42858,102.47108 -107.73294,44.78793 150.00722,-20.18447 z',
|
shape: 'M 339.33314,69.985523 152.23146,95.161367 297.9199,159.07076 Z m 13.13639,6.106743 -41.41324,89.085234 142.77019,70.18694 z M 286.72755,169.97878 132.07338,102.13811 116.91912,287.72473 Z m 23.20215,10.78603 19.43763,205.72115 132.11738,-131.21375 z m -14.47505,0.7893 -172.49845,119.61061 192.24446,89.36907 z M 115.16567,131.2299 49.03503,273.48548 l 52.96523,18.92787 z m 334.97786,155.72184 -114.74252,113.9631 48.61189,28.29247 z m -329.82146,28.96669 45.63657,144.20053 139.66241,-58.06123 z m -17.69094,-7.89455 -46.061724,-16.46047 81.264174,127.69506 z m 220.42858,102.47108 -107.73294,44.78793 150.00722,-20.18447 z',
|
||||||
|
|||||||
@@ -100,21 +100,18 @@ Meteor.publish('classFillers', function(classId){
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get all the ids of libraries the user can access
|
// Get all the ids of libraries the user can access
|
||||||
const user = Meteor.users.findOne(userId, {
|
const creatureId = classProp.ancestors[0].id;
|
||||||
fields: {subscribedLibraries: 1}
|
const libraryIds = getCreatureLibraryIds(creatureId, userId);
|
||||||
});
|
const libraries = Libraries.find({
|
||||||
const subs = user && user.subscribedLibraries || [];
|
|
||||||
let libraries = Libraries.find({
|
|
||||||
$or: [
|
$or: [
|
||||||
{owner: userId},
|
{ owner: userId },
|
||||||
{writers: userId},
|
{ writers: userId },
|
||||||
{readers: userId},
|
{ readers: userId },
|
||||||
{_id: {$in: subs}},
|
{ _id: { $in: libraryIds }, public: true },
|
||||||
]
|
]
|
||||||
}, {
|
}, {
|
||||||
fields: {_id: 1, name: 1},
|
sort: { name: 1 }
|
||||||
});
|
});
|
||||||
let libraryIds = libraries.map(lib => lib._id);
|
|
||||||
|
|
||||||
// Build a filter for nodes in those libraries that match the slot
|
// Build a filter for nodes in those libraries that match the slot
|
||||||
let filter = getSlotFillFilter({slot: classProp, libraryIds});
|
let filter = getSlotFillFilter({slot: classProp, libraryIds});
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export default {
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css">
|
||||||
.column-layout {
|
.column-layout {
|
||||||
column-count: 12;
|
column-count: 12;
|
||||||
column-fill: balance;
|
column-fill: balance;
|
||||||
@@ -35,7 +35,7 @@ export default {
|
|||||||
transform: translateZ(0);
|
transform: translateZ(0);
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
}
|
}
|
||||||
.column-layout >>> > div {
|
.column-layout > div, .column-layout > span > div {
|
||||||
/*
|
/*
|
||||||
Table and width set because firefox does not support break-inside: avoid
|
Table and width set because firefox does not support break-inside: avoid
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -28,11 +28,11 @@
|
|||||||
</v-btn>
|
</v-btn>
|
||||||
<div
|
<div
|
||||||
class="layout align-center justify-start pr-1"
|
class="layout align-center justify-start pr-1"
|
||||||
style="flex-grow: 0;"
|
|
||||||
>
|
>
|
||||||
<!--{{node && node.order}}-->
|
<!--{{node && node.order}}-->
|
||||||
<div
|
<div
|
||||||
v-if="isSlot"
|
v-if="isSlot"
|
||||||
|
class="text-truncate"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
:class="{
|
:class="{
|
||||||
@@ -47,15 +47,38 @@
|
|||||||
:model="node"
|
:model="node"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<tree-node-view
|
<template
|
||||||
v-else
|
v-else
|
||||||
:model="node"
|
>
|
||||||
/>
|
<tree-node-view
|
||||||
|
:model="node"
|
||||||
|
/>
|
||||||
|
<v-spacer />
|
||||||
|
<v-btn
|
||||||
|
icon
|
||||||
|
:disabled="context.editPermission === false"
|
||||||
|
@click.stop="remove(node)"
|
||||||
|
>
|
||||||
|
<v-icon>
|
||||||
|
mdi-delete
|
||||||
|
</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
<template v-if="condenseChild">
|
<template v-if="condenseChild">
|
||||||
<span class="mr-4">:</span>
|
<span class="mr-4">:</span>
|
||||||
<tree-node-view
|
<tree-node-view
|
||||||
:model="children[0].node"
|
:model="children[0].node"
|
||||||
/>
|
/>
|
||||||
|
<v-spacer />
|
||||||
|
<v-btn
|
||||||
|
icon
|
||||||
|
:disabled="context.editPermission === false"
|
||||||
|
@click.stop="remove(children[0].node)"
|
||||||
|
>
|
||||||
|
<v-icon>
|
||||||
|
mdi-delete
|
||||||
|
</v-icon>
|
||||||
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -92,98 +115,118 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="js">
|
<script lang="js">
|
||||||
/**
|
/**
|
||||||
* TreeNode's are list item views of character properties. Every property which
|
* TreeNode's are list item views of character properties. Every property which
|
||||||
* can belong to the character is shown in the tree view of the character
|
* can belong to the character is shown in the tree view of the character
|
||||||
* the tree view shows off the full character structure, and where each part of
|
* the tree view shows off the full character structure, and where each part of
|
||||||
* character comes from.
|
* character comes from.
|
||||||
**/
|
**/
|
||||||
import TreeNodeView from '/imports/ui/properties/treeNodeViews/TreeNodeView.vue';
|
import TreeNodeView from '/imports/ui/properties/treeNodeViews/TreeNodeView.vue';
|
||||||
import FillSlotButton from '/imports/ui/creature/buildTree/FillSlotButton.vue';
|
import FillSlotButton from '/imports/ui/creature/buildTree/FillSlotButton.vue';
|
||||||
import { some } from 'lodash';
|
import { some } from 'lodash';
|
||||||
|
import { snackbar } from '/imports/ui/components/snackbars/SnackbarQueue.js';
|
||||||
|
import softRemoveProperty from '/imports/api/creature/creatureProperties/methods/softRemoveProperty.js';
|
||||||
|
import restoreProperty from '/imports/api/creature/creatureProperties/methods/restoreProperty.js';
|
||||||
|
import getPropertyTitle from '/imports/ui/properties/shared/getPropertyTitle.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'BuildTreeNode',
|
name: 'BuildTreeNode',
|
||||||
components: {
|
components: {
|
||||||
TreeNodeView,
|
TreeNodeView,
|
||||||
FillSlotButton,
|
FillSlotButton,
|
||||||
|
},
|
||||||
|
inject: {
|
||||||
|
context: { default: {} }
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
node: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
props: {
|
children: {
|
||||||
node: {
|
type: Array,
|
||||||
type: Object,
|
default: () => [],
|
||||||
required: true,
|
},
|
||||||
},
|
},
|
||||||
children: {
|
data(){return {
|
||||||
type: Array,
|
expanded: false,
|
||||||
default: () => [],
|
/* expand if there's a slot needing attention:
|
||||||
},
|
this.node._descendantCanFill || (
|
||||||
},
|
this.node.type === 'propertySlot' &&
|
||||||
data(){return {
|
this. node.quantityExpected?.value === 0 ||
|
||||||
expanded: false,
|
(this.node.quantityExpected?.value > 1 && this.node.spaceLeft > 0)
|
||||||
/* expand if there's a slot needing attention:
|
)
|
||||||
this.node._descendantCanFill || (
|
*/
|
||||||
this.node.type === 'propertySlot' &&
|
}},
|
||||||
this. node.quantityExpected?.value === 0 ||
|
computed: {
|
||||||
(this.node.quantityExpected?.value > 1 && this.node.spaceLeft > 0)
|
condenseChild(){
|
||||||
)
|
return this.node.type === 'propertySlot' &&
|
||||||
*/
|
this.children.length === 1 &&
|
||||||
}},
|
this.children[0].node.type !== 'propertySlot' &&
|
||||||
computed: {
|
this.node.quantityExpected &&
|
||||||
condenseChild(){
|
this.node.quantityExpected.value === 1;
|
||||||
return this.node.type === 'propertySlot' &&
|
},
|
||||||
this.children.length === 1 &&
|
isSlot(){
|
||||||
this.children[0].node.type !== 'propertySlot' &&
|
return this.node.type === 'propertySlot';
|
||||||
|
},
|
||||||
|
canFill(){
|
||||||
|
return !!this.node._canFill;
|
||||||
|
},
|
||||||
|
canFillWithOne(){
|
||||||
|
return this.isSlot &&
|
||||||
this.node.quantityExpected &&
|
this.node.quantityExpected &&
|
||||||
this.node.quantityExpected.value === 1;
|
this.node.quantityExpected.value === 1 &&
|
||||||
},
|
this.node.spaceLeft === 1
|
||||||
isSlot(){
|
|
||||||
return this.node.type === 'propertySlot';
|
|
||||||
},
|
|
||||||
canFill(){
|
|
||||||
return !!this.node._canFill;
|
|
||||||
},
|
|
||||||
canFillWithOne(){
|
|
||||||
return this.isSlot &&
|
|
||||||
this.node.quantityExpected &&
|
|
||||||
this.node.quantityExpected.value === 1 &&
|
|
||||||
this.node.spaceLeft === 1
|
|
||||||
},
|
|
||||||
canFillWithMany(){
|
|
||||||
return this.isSlot && (
|
|
||||||
!this.node.quantityExpected ||
|
|
||||||
this.node.quantityExpected.value === 0 ||
|
|
||||||
(this.node.quantityExpected.value > 1 && this.node.spaceLeft > 0)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
hasChildren(){
|
|
||||||
return !!this.children && !!this.computedChildren.length || this.lazy && !this.expanded;
|
|
||||||
},
|
|
||||||
showExpanded(){
|
|
||||||
return this.canExpand && this.expanded;
|
|
||||||
},
|
|
||||||
computedChildren(){
|
|
||||||
if (this.condenseChild){
|
|
||||||
return this.children[0].children;
|
|
||||||
}
|
|
||||||
return this.children;
|
|
||||||
},
|
|
||||||
canExpand(){
|
|
||||||
return !!this.computedChildren.length || this.canFillWithMany;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
'node._ancestorOfMatchedDocument'(value){
|
|
||||||
this.expanded = !!value ||
|
|
||||||
some(this.selectedNode?.ancestors, ref => ref.id === this.node._id);
|
|
||||||
},
|
|
||||||
'selectedNode.ancestors'(value){
|
|
||||||
this.expanded = !!some(value, ref => ref.id === this.node._id) || this.expanded;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
beforeCreate() {
|
canFillWithMany(){
|
||||||
this.$options.components.BuildTreeNodeList = require('./BuildTreeNodeList.vue').default
|
return this.isSlot && (
|
||||||
},
|
!this.node.quantityExpected ||
|
||||||
};
|
this.node.quantityExpected.value === 0 ||
|
||||||
|
(this.node.quantityExpected.value > 1 && this.node.spaceLeft > 0)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
hasChildren(){
|
||||||
|
return !!this.children && !!this.computedChildren.length || this.lazy && !this.expanded;
|
||||||
|
},
|
||||||
|
showExpanded(){
|
||||||
|
return this.canExpand && this.expanded;
|
||||||
|
},
|
||||||
|
computedChildren(){
|
||||||
|
if (this.condenseChild){
|
||||||
|
return this.children[0].children;
|
||||||
|
}
|
||||||
|
return this.children;
|
||||||
|
},
|
||||||
|
canExpand(){
|
||||||
|
return !!this.computedChildren.length || this.canFillWithMany;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'node._ancestorOfMatchedDocument'(value){
|
||||||
|
this.expanded = !!value ||
|
||||||
|
some(this.selectedNode?.ancestors, ref => ref.id === this.node._id);
|
||||||
|
},
|
||||||
|
'selectedNode.ancestors'(value){
|
||||||
|
this.expanded = !!some(value, ref => ref.id === this.node._id) || this.expanded;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
beforeCreate() {
|
||||||
|
this.$options.components.BuildTreeNodeList = require('./BuildTreeNodeList.vue').default
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
remove(model) {
|
||||||
|
const _id = model._id;
|
||||||
|
softRemoveProperty.call({_id});
|
||||||
|
snackbar({
|
||||||
|
text: `Deleted ${getPropertyTitle(model)}`,
|
||||||
|
callbackName: 'undo',
|
||||||
|
callback(){
|
||||||
|
restoreProperty.call({_id});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css" scoped>
|
||||||
|
|||||||
@@ -9,9 +9,9 @@
|
|||||||
v-model="fab"
|
v-model="fab"
|
||||||
color="primary"
|
color="primary"
|
||||||
fab
|
fab
|
||||||
|
small
|
||||||
data-id="insert-creature-property-fab"
|
data-id="insert-creature-property-fab"
|
||||||
class="insert-creature-property-fab"
|
class="insert-creature-property-fab"
|
||||||
small
|
|
||||||
>
|
>
|
||||||
<transition
|
<transition
|
||||||
name="fab-rotate"
|
name="fab-rotate"
|
||||||
@@ -48,12 +48,13 @@
|
|||||||
import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js';
|
import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js';
|
||||||
import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js';
|
import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js';
|
||||||
|
|
||||||
function getParentAndOrderFromSelectedTreeNode(creatureId){
|
function getParentAndOrderFromSelectedTreeNode(creatureId, $store){
|
||||||
// find the parent based on the currently selected property
|
// find the parent based on the currently selected property
|
||||||
let el = document.querySelector('.tree-tab .tree-node-title.primary--text');
|
let el = document.querySelector('.tree-tab .tree-node-title.primary--text');
|
||||||
let selectedComponent = el && el.parentElement.__vue__.$parent;
|
let selectedComponent = el && el.parentElement.__vue__.$parent;
|
||||||
let parentRef, order;
|
let parentRef, order;
|
||||||
if (selectedComponent){
|
const onTreeTab = $store.getters.tabNameById(creatureId) === 'tree';
|
||||||
|
if (onTreeTab && selectedComponent){
|
||||||
if (selectedComponent.showExpanded){
|
if (selectedComponent.showExpanded){
|
||||||
parentRef = {
|
parentRef = {
|
||||||
id: selectedComponent.node._id,
|
id: selectedComponent.node._id,
|
||||||
@@ -156,7 +157,7 @@
|
|||||||
let creatureId = this.creatureId;
|
let creatureId = this.creatureId;
|
||||||
let fab = hideFab();
|
let fab = hideFab();
|
||||||
|
|
||||||
let {parentRef, order } = getParentAndOrderFromSelectedTreeNode(creatureId);
|
let {parentRef, order } = getParentAndOrderFromSelectedTreeNode(creatureId, this.$store);
|
||||||
let parent;
|
let parent;
|
||||||
try {
|
try {
|
||||||
parent = fetchDocByRef(parentRef);
|
parent = fetchDocByRef(parentRef);
|
||||||
|
|||||||
@@ -14,17 +14,19 @@
|
|||||||
<v-fade-transition
|
<v-fade-transition
|
||||||
mode="out-in"
|
mode="out-in"
|
||||||
>
|
>
|
||||||
<v-app-bar-title :key="$store.state.pageTitle">
|
<v-toolbar-title :key="$store.state.pageTitle">
|
||||||
<div>
|
{{ $store.state.pageTitle }}
|
||||||
{{ $store.state.pageTitle }}
|
</v-toolbar-title>
|
||||||
</div>
|
|
||||||
</v-app-bar-title>
|
|
||||||
</v-fade-transition>
|
</v-fade-transition>
|
||||||
<v-spacer />
|
<v-spacer />
|
||||||
<v-fade-transition
|
<v-fade-transition
|
||||||
mode="out-in"
|
mode="out-in"
|
||||||
>
|
>
|
||||||
<div :key="$route.meta.title">
|
<v-layout
|
||||||
|
:key="$route.meta.title"
|
||||||
|
class="flex-shrink-0 flex-grow-0"
|
||||||
|
justify-end
|
||||||
|
>
|
||||||
<template v-if="creature">
|
<template v-if="creature">
|
||||||
<shared-icon :model="creature" />
|
<shared-icon :model="creature" />
|
||||||
<v-menu
|
<v-menu
|
||||||
@@ -68,7 +70,7 @@
|
|||||||
</v-menu>
|
</v-menu>
|
||||||
<v-app-bar-nav-icon @click="toggleRightDrawer" />
|
<v-app-bar-nav-icon @click="toggleRightDrawer" />
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</v-layout>
|
||||||
</v-fade-transition>
|
</v-fade-transition>
|
||||||
<v-fade-transition
|
<v-fade-transition
|
||||||
slot="extension"
|
slot="extension"
|
||||||
|
|||||||
@@ -9,9 +9,7 @@
|
|||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
<v-row dense>
|
<v-row dense>
|
||||||
<v-col cols="12">
|
<slot-cards-to-fill :creature-id="creatureId" />
|
||||||
<slot-cards-to-fill :creature-id="creatureId" />
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
</v-row>
|
||||||
<v-row dense>
|
<v-row dense>
|
||||||
<v-col
|
<v-col
|
||||||
@@ -20,7 +18,51 @@
|
|||||||
lg="6"
|
lg="6"
|
||||||
>
|
>
|
||||||
<v-card class="pb-4">
|
<v-card class="pb-4">
|
||||||
<v-card-title>Slots</v-card-title>
|
<v-card-title style="height: 68px;">
|
||||||
|
Slots
|
||||||
|
<v-spacer />
|
||||||
|
<v-scale-transition>
|
||||||
|
<v-menu
|
||||||
|
bottom
|
||||||
|
left
|
||||||
|
>
|
||||||
|
<template #activator="{ on }">
|
||||||
|
<v-badge
|
||||||
|
v-show="hiddenCount"
|
||||||
|
slot="activator"
|
||||||
|
color="primary"
|
||||||
|
overlap
|
||||||
|
:value="hiddenCount"
|
||||||
|
:content="hiddenCount"
|
||||||
|
>
|
||||||
|
<v-btn
|
||||||
|
icon
|
||||||
|
v-on="on"
|
||||||
|
>
|
||||||
|
<v-icon>mdi-file-hidden</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</v-badge>
|
||||||
|
</template>
|
||||||
|
<v-list>
|
||||||
|
<v-subheader>
|
||||||
|
<v-icon class="mr-2">
|
||||||
|
mdi-file-hidden
|
||||||
|
</v-icon>
|
||||||
|
{{ hiddenCount }} hidden {{ hiddenCount > 1 ? 'slots' : 'slot' }}
|
||||||
|
</v-subheader>
|
||||||
|
<v-list-item
|
||||||
|
v-for="slot in hiddenSlots"
|
||||||
|
:key="slot._id"
|
||||||
|
@click="unhideSlot(slot._id)"
|
||||||
|
>
|
||||||
|
<v-list-item-title>
|
||||||
|
{{ getPropertyTitle(slot) }}
|
||||||
|
</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
|
</v-list>
|
||||||
|
</v-menu>
|
||||||
|
</v-scale-transition>
|
||||||
|
</v-card-title>
|
||||||
<build-tree-node-list
|
<build-tree-node-list
|
||||||
:children="slotBuildTree"
|
:children="slotBuildTree"
|
||||||
class="mx-2"
|
class="mx-2"
|
||||||
@@ -135,6 +177,9 @@ import SlotCardsToFill from '/imports/ui/creature/slots/SlotCardsToFill.vue';
|
|||||||
import CreatureVariables from '../../../../api/creature/creatures/CreatureVariables';
|
import CreatureVariables from '../../../../api/creature/creatures/CreatureVariables';
|
||||||
import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js';
|
import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js';
|
||||||
import CharacterErrors from '/imports/ui/creature/character/errors/CharacterErrors.vue';
|
import CharacterErrors from '/imports/ui/creature/character/errors/CharacterErrors.vue';
|
||||||
|
import { snackbar } from '/imports/ui/components/snackbars/SnackbarQueue.js';
|
||||||
|
import updateCreatureProperty from '/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js';
|
||||||
|
import getPropertyTitle from '/imports/ui/properties/shared/getPropertyTitle.js';
|
||||||
|
|
||||||
function traverse(tree, callback, parents = []){
|
function traverse(tree, callback, parents = []){
|
||||||
tree.forEach(node => {
|
tree.forEach(node => {
|
||||||
@@ -179,7 +224,10 @@ export default {
|
|||||||
...this.highestLevels,
|
...this.highestLevels,
|
||||||
...this.classProperties
|
...this.classProperties
|
||||||
].sort((a, b) => a.order - b.order);
|
].sort((a, b) => a.order - b.order);
|
||||||
}
|
},
|
||||||
|
hiddenCount() {
|
||||||
|
return this.hiddenSlots.length;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
meteor: {
|
meteor: {
|
||||||
creature(){
|
creature(){
|
||||||
@@ -188,6 +236,29 @@ export default {
|
|||||||
variables() {
|
variables() {
|
||||||
return CreatureVariables.findOne({ _creatureId: this.creatureId }) || {};
|
return CreatureVariables.findOne({ _creatureId: this.creatureId }) || {};
|
||||||
},
|
},
|
||||||
|
hiddenSlots(){
|
||||||
|
return CreatureProperties.find({
|
||||||
|
type: 'propertySlot',
|
||||||
|
'ancestors.id': this.creatureId,
|
||||||
|
ignored: true,
|
||||||
|
$and: [
|
||||||
|
{
|
||||||
|
$or: [
|
||||||
|
{'slotCondition.value': {$nin: [false, 0, '']}},
|
||||||
|
{'slotCondition.value': {$exists: false}},
|
||||||
|
]
|
||||||
|
},{
|
||||||
|
$or: [
|
||||||
|
{ 'quantityExpected.value': {$in: [false, 0, '', undefined]} },
|
||||||
|
{ 'quantityExpected.value': {exists: false} },
|
||||||
|
{spaceLeft: {$gt: 0}},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
],
|
||||||
|
removed: {$ne: true},
|
||||||
|
inactive: {$ne: true},
|
||||||
|
}).fetch();
|
||||||
|
},
|
||||||
classProperties(){
|
classProperties(){
|
||||||
return CreatureProperties.find({
|
return CreatureProperties.find({
|
||||||
'ancestors.id': this.creatureId,
|
'ancestors.id': this.creatureId,
|
||||||
@@ -313,6 +384,19 @@ export default {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
getPropertyTitle,
|
||||||
|
unhideSlot(_id) {
|
||||||
|
updateCreatureProperty.call({
|
||||||
|
_id,
|
||||||
|
path: ['ignored'],
|
||||||
|
value: false,
|
||||||
|
}, error => {
|
||||||
|
if (error){
|
||||||
|
console.error(error);
|
||||||
|
snackbar({text: error.reason || error.message || error.toString()});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -46,7 +46,7 @@
|
|||||||
{{ buff.name }}
|
{{ buff.name }}
|
||||||
</v-list-item-title>
|
</v-list-item-title>
|
||||||
</v-list-item-content>
|
</v-list-item-content>
|
||||||
<v-list-item-action>
|
<v-list-item-action v-if="!buff.hideRemoveButton">
|
||||||
<v-btn
|
<v-btn
|
||||||
icon
|
icon
|
||||||
@click.stop="softRemove(buff._id)"
|
@click.stop="softRemove(buff._id)"
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
:style="`border: solid 1px ${accentColor};`"
|
:style="`border: solid 1px ${accentColor};`"
|
||||||
hover
|
hover
|
||||||
class="slot-card d-flex flex-column"
|
class="slot-card d-flex flex-column"
|
||||||
style="max-width: 400px;"
|
|
||||||
@mouseover="hover = true"
|
@mouseover="hover = true"
|
||||||
@mouseleave="hover = false"
|
@mouseleave="hover = false"
|
||||||
@click="$emit('click')"
|
@click="$emit('click')"
|
||||||
@@ -17,7 +16,7 @@
|
|||||||
<v-card-title>
|
<v-card-title>
|
||||||
{{ model.name }}
|
{{ model.name }}
|
||||||
</v-card-title>
|
</v-card-title>
|
||||||
<v-card-text>
|
<v-card-text v-if="model.description">
|
||||||
<property-description
|
<property-description
|
||||||
text
|
text
|
||||||
:model="model.description"
|
:model="model.description"
|
||||||
|
|||||||
@@ -1,26 +1,30 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="slots-to-fill">
|
<column-layout wide-columns class="slots-to-fill">
|
||||||
<v-slide-y-transition
|
<v-fade-transition
|
||||||
group
|
group
|
||||||
leave-absolute
|
leave-absolute
|
||||||
|
hide-on-leave
|
||||||
>
|
>
|
||||||
<slot-card
|
<div
|
||||||
v-for="slot in slots"
|
v-for="slot in slots"
|
||||||
:key="slot._id"
|
:key="slot._id"
|
||||||
:model="slot"
|
style="transition: all 0.3s !important"
|
||||||
class="ma-1"
|
>
|
||||||
hover
|
<slot-card
|
||||||
style="display: inline-block !important; transition: all 0.3s !important;"
|
:model="slot"
|
||||||
@ignore="ignoreSlot(slot._id)"
|
hover
|
||||||
@click="fillSlot(slot._id)"
|
@ignore="ignoreSlot(slot._id)"
|
||||||
/>
|
@click="fillSlot(slot._id)"
|
||||||
</v-slide-y-transition>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</v-fade-transition>
|
||||||
|
</column-layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="js">
|
<script lang="js">
|
||||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||||
import SlotCard from '/imports/ui/creature/slots/SlotCard.vue';
|
import SlotCard from '/imports/ui/creature/slots/SlotCard.vue';
|
||||||
|
import ColumnLayout from '/imports/ui/components/ColumnLayout.vue';
|
||||||
import updateCreatureProperty from '/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js';
|
import updateCreatureProperty from '/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js';
|
||||||
import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js';
|
import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js';
|
||||||
import { snackbar } from '/imports/ui/components/snackbars/SnackbarQueue.js';
|
import { snackbar } from '/imports/ui/components/snackbars/SnackbarQueue.js';
|
||||||
@@ -28,6 +32,7 @@ import { snackbar } from '/imports/ui/components/snackbars/SnackbarQueue.js';
|
|||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
SlotCard,
|
SlotCard,
|
||||||
|
ColumnLayout,
|
||||||
},
|
},
|
||||||
inject: {
|
inject: {
|
||||||
context: { default: {} }
|
context: { default: {} }
|
||||||
|
|||||||
@@ -36,22 +36,34 @@
|
|||||||
@change="change('target', ...arguments)"
|
@change="change('target', ...arguments)"
|
||||||
/>
|
/>
|
||||||
</v-expand-transition>
|
</v-expand-transition>
|
||||||
<smart-combobox
|
<form-sections>
|
||||||
label="Tags"
|
<form-section
|
||||||
multiple
|
v-if="$slots.children"
|
||||||
chips
|
name="Children"
|
||||||
deletable-chips
|
standalone
|
||||||
hint="Used to let slots find this property in a library, should otherwise be left blank"
|
>
|
||||||
:value="model.tags"
|
<slot name="children" />
|
||||||
@change="change('tags', ...arguments)"
|
</form-section>
|
||||||
/>
|
<form-section
|
||||||
<form-section
|
name="Advanced"
|
||||||
v-if="$slots.children"
|
>
|
||||||
name="Children"
|
<smart-switch
|
||||||
standalone
|
label="Hide remove button"
|
||||||
>
|
:value="model.hideRemoveButton"
|
||||||
<slot name="children" />
|
:error-messages="errors.hideRemoveButton"
|
||||||
</form-section>
|
@change="change('hideRemoveButton', ...arguments)"
|
||||||
|
/>
|
||||||
|
<smart-combobox
|
||||||
|
label="Tags"
|
||||||
|
multiple
|
||||||
|
chips
|
||||||
|
deletable-chips
|
||||||
|
hint="Used to let slots find this property in a library, should otherwise be left blank"
|
||||||
|
:value="model.tags"
|
||||||
|
@change="change('tags', ...arguments)"
|
||||||
|
/>
|
||||||
|
</form-section>
|
||||||
|
</form-sections>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -60,12 +72,6 @@
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [propertyFormMixin],
|
mixins: [propertyFormMixin],
|
||||||
props: {
|
|
||||||
parentTarget: {
|
|
||||||
type: String,
|
|
||||||
default: undefined,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data(){return {
|
data(){return {
|
||||||
targetOptions: [
|
targetOptions: [
|
||||||
{
|
{
|
||||||
|
|||||||
172
app/imports/ui/properties/forms/BuffRemoverForm.vue
Normal file
172
app/imports/ui/properties/forms/BuffRemoverForm.vue
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
<template lang="html">
|
||||||
|
<div class="buff-remover-form">
|
||||||
|
<text-field
|
||||||
|
ref="focusFirst"
|
||||||
|
label="Name"
|
||||||
|
:value="model.name"
|
||||||
|
:error-messages="errors.name"
|
||||||
|
@change="change('name', ...arguments)"
|
||||||
|
/>
|
||||||
|
<smart-switch
|
||||||
|
label="Remove parent buff"
|
||||||
|
:value="model.targetParentBuff"
|
||||||
|
:error-messages="errors.targetParentBuff"
|
||||||
|
@change="change('targetParentBuff', ...arguments)"
|
||||||
|
/>
|
||||||
|
<v-expand-transition>
|
||||||
|
<div v-if="!model.targetParentBuff">
|
||||||
|
<smart-switch
|
||||||
|
:label="model.removeAll ? 'Remove All. All matching buffs will be removed' : 'Remove All. Only 1 matching buff will be removed'"
|
||||||
|
:value="model.removeAll"
|
||||||
|
:error-messages="errors.removeAll"
|
||||||
|
@change="change('removeAll', ...arguments)"
|
||||||
|
/>
|
||||||
|
<smart-select
|
||||||
|
label="Target"
|
||||||
|
:items="targetOptions"
|
||||||
|
:value="model.target"
|
||||||
|
:error-messages="errors.target"
|
||||||
|
:menu-props="{auto: true, lazy: true}"
|
||||||
|
@change="change('target', ...arguments)"
|
||||||
|
/>
|
||||||
|
<v-layout
|
||||||
|
align-center
|
||||||
|
>
|
||||||
|
<v-btn
|
||||||
|
icon
|
||||||
|
style="margin-top: -30px;"
|
||||||
|
class="mr-2"
|
||||||
|
:loading="addExtraTagsLoading"
|
||||||
|
:disabled="extraTagsFull"
|
||||||
|
@click="addExtraTags"
|
||||||
|
>
|
||||||
|
<v-icon>
|
||||||
|
mdi-plus
|
||||||
|
</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
<smart-combobox
|
||||||
|
label="Tags Required"
|
||||||
|
hint="The effect will apply to properties that have all the listed tags"
|
||||||
|
multiple
|
||||||
|
chips
|
||||||
|
deletable-chips
|
||||||
|
persistent-hint
|
||||||
|
:value="model.targetTags"
|
||||||
|
:error-messages="errors.targetTags"
|
||||||
|
@change="change('targetTags', ...arguments)"
|
||||||
|
/>
|
||||||
|
</v-layout>
|
||||||
|
<v-slide-x-transition
|
||||||
|
v-if="!model.targetParentBuff"
|
||||||
|
group
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="(extras, i) in model.extraTags"
|
||||||
|
:key="extras._id"
|
||||||
|
class="target-tags layout align-center justify-space-between"
|
||||||
|
>
|
||||||
|
<smart-select
|
||||||
|
label="Operation"
|
||||||
|
style="width: 90px; flex-grow: 0;"
|
||||||
|
:items="['OR', 'NOT']"
|
||||||
|
:value="extras.operation"
|
||||||
|
:error-messages="errors.extraTags && errors.extraTags[i]"
|
||||||
|
@change="change(['extraTags', i, 'operation'], ...arguments)"
|
||||||
|
/>
|
||||||
|
<smart-combobox
|
||||||
|
label="Tags"
|
||||||
|
:hint="extras.operation === 'OR' ? 'The effect will also target properties that have all of these tags instead' : 'The effect will ignore properties that have any of these tags'"
|
||||||
|
class="mx-2"
|
||||||
|
multiple
|
||||||
|
chips
|
||||||
|
deletable-chips
|
||||||
|
persistent-hint
|
||||||
|
:value="extras.tags"
|
||||||
|
@change="change(['extraTags', i, 'tags'], ...arguments)"
|
||||||
|
/>
|
||||||
|
<v-btn
|
||||||
|
icon
|
||||||
|
style="margin-top: -30px;"
|
||||||
|
@click="$emit('pull', {path: ['extraTags', i]})"
|
||||||
|
>
|
||||||
|
<v-icon>mdi-delete</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</div>
|
||||||
|
</v-slide-x-transition>
|
||||||
|
</div>
|
||||||
|
</v-expand-transition>
|
||||||
|
<form-sections>
|
||||||
|
<form-section
|
||||||
|
v-if="$slots.children"
|
||||||
|
name="Children"
|
||||||
|
standalone
|
||||||
|
>
|
||||||
|
<slot name="children" />
|
||||||
|
</form-section>
|
||||||
|
<form-section
|
||||||
|
name="Advanced"
|
||||||
|
>
|
||||||
|
<smart-combobox
|
||||||
|
label="Tags"
|
||||||
|
multiple
|
||||||
|
chips
|
||||||
|
deletable-chips
|
||||||
|
hint="Used to let slots find this property in a library, should otherwise be left blank"
|
||||||
|
:value="model.tags"
|
||||||
|
@change="change('tags', ...arguments)"
|
||||||
|
/>
|
||||||
|
</form-section>
|
||||||
|
</form-sections>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="js">
|
||||||
|
import propertyFormMixin from '/imports/ui/properties/forms/shared/propertyFormMixin.js';
|
||||||
|
import {
|
||||||
|
BuffRemoverSchema
|
||||||
|
} from '/imports/api/properties/BuffRemovers.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [propertyFormMixin],
|
||||||
|
data(){return {
|
||||||
|
addExtraTagsLoading: false,
|
||||||
|
extraTagOperations: ['OR', 'NOT'],
|
||||||
|
targetOptions: [
|
||||||
|
{
|
||||||
|
text: 'Self',
|
||||||
|
value: 'self',
|
||||||
|
}, {
|
||||||
|
text: 'Target',
|
||||||
|
value: 'target',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}},
|
||||||
|
computed: {
|
||||||
|
extraTagsFull(){
|
||||||
|
if (!this.model.extraTags) return false;
|
||||||
|
let maxCount = BuffRemoverSchema.get('extraTags', 'maxCount');
|
||||||
|
return this.model.extraTags.length >= maxCount;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
acknowledgeAddResult(){
|
||||||
|
this.addExtraTagsLoading = false;
|
||||||
|
},
|
||||||
|
addExtraTags(){
|
||||||
|
this.addExtraTagsLoading = true;
|
||||||
|
this.$emit('push', {
|
||||||
|
path: ['extraTags'],
|
||||||
|
value: {
|
||||||
|
_id: Random.id(),
|
||||||
|
operation: 'OR',
|
||||||
|
tags: [],
|
||||||
|
},
|
||||||
|
ack: this.acknowledgeAddResult,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="css" scoped>
|
||||||
|
</style>
|
||||||
@@ -2,6 +2,7 @@ const ActionForm = () => import('/imports/ui/properties/forms/ActionForm.vue');
|
|||||||
const AdjustmentForm = () => import('/imports/ui/properties/forms/AdjustmentForm.vue');
|
const AdjustmentForm = () => import('/imports/ui/properties/forms/AdjustmentForm.vue');
|
||||||
const AttributeForm = () => import('/imports/ui/properties/forms/AttributeForm.vue');
|
const AttributeForm = () => import('/imports/ui/properties/forms/AttributeForm.vue');
|
||||||
const BuffForm = () => import('/imports/ui/properties/forms/BuffForm.vue');
|
const BuffForm = () => import('/imports/ui/properties/forms/BuffForm.vue');
|
||||||
|
const BuffRemoverForm = () => import('/imports/ui/properties/forms/BuffRemoverForm.vue');
|
||||||
const BranchForm = () => import('/imports/ui/properties/forms/BranchForm.vue');
|
const BranchForm = () => import('/imports/ui/properties/forms/BranchForm.vue');
|
||||||
const ClassForm = () => import('/imports/ui/properties/forms/ClassForm.vue');
|
const ClassForm = () => import('/imports/ui/properties/forms/ClassForm.vue');
|
||||||
const ClassLevelForm = () => import('/imports/ui/properties/forms/ClassLevelForm.vue');
|
const ClassLevelForm = () => import('/imports/ui/properties/forms/ClassLevelForm.vue');
|
||||||
@@ -31,6 +32,7 @@ export default {
|
|||||||
adjustment: AdjustmentForm,
|
adjustment: AdjustmentForm,
|
||||||
attribute: AttributeForm,
|
attribute: AttributeForm,
|
||||||
buff: BuffForm,
|
buff: BuffForm,
|
||||||
|
buffRemover: BuffRemoverForm,
|
||||||
branch: BranchForm,
|
branch: BranchForm,
|
||||||
constant: ConstantForm,
|
constant: ConstantForm,
|
||||||
container: ContainerForm,
|
container: ContainerForm,
|
||||||
|
|||||||
97
app/imports/ui/properties/viewers/BuffRemoverViewer.vue
Normal file
97
app/imports/ui/properties/viewers/BuffRemoverViewer.vue
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
<template lang="html">
|
||||||
|
<div class="buff-remover-viewer">
|
||||||
|
<v-row dense>
|
||||||
|
<property-field
|
||||||
|
v-if="model.target === 'self'"
|
||||||
|
name="Target"
|
||||||
|
value="Self"
|
||||||
|
/>
|
||||||
|
<template v-if="!model.targetParentBuff">
|
||||||
|
<property-field
|
||||||
|
v-if="model.target === 'self'"
|
||||||
|
name="Target"
|
||||||
|
value="Self"
|
||||||
|
/>
|
||||||
|
<property-field
|
||||||
|
name="When applied"
|
||||||
|
:value="model.removeAll ? 'Remove all matching buffs' : 'Remove 1 matching buff'"
|
||||||
|
/>
|
||||||
|
<property-field
|
||||||
|
name="Targeted tags"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div class="d-flex flex-wrap">
|
||||||
|
<v-chip
|
||||||
|
v-for="(tag, index) in model.targetTags"
|
||||||
|
:key="index"
|
||||||
|
class="ma-1"
|
||||||
|
>
|
||||||
|
{{ tag }}
|
||||||
|
</v-chip>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="ex in model.extraTags"
|
||||||
|
:key="ex._id"
|
||||||
|
>
|
||||||
|
<span class="ma-2">
|
||||||
|
{{ ex.operation }}
|
||||||
|
</span>
|
||||||
|
<div class="d-flex flex-wrap">
|
||||||
|
<v-chip
|
||||||
|
v-for="(extraTag, index) in ex.tags"
|
||||||
|
:key="index"
|
||||||
|
class="ma-1"
|
||||||
|
>
|
||||||
|
{{ extraTag }}
|
||||||
|
</v-chip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</property-field>
|
||||||
|
</template>
|
||||||
|
</v-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="js">
|
||||||
|
import propertyViewerMixin from '/imports/ui/properties/viewers/shared/propertyViewerMixin.js'
|
||||||
|
import numberToSignedString from '/imports/ui/utility/numberToSignedString.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [propertyViewerMixin],
|
||||||
|
computed: {
|
||||||
|
reset(){
|
||||||
|
let reset = this.model.reset
|
||||||
|
if (reset === 'shortRest'){
|
||||||
|
return `Reset${
|
||||||
|
this.model.resetMultiplier && ' x' + this.model.resetMultiplier
|
||||||
|
} on a short rest`;
|
||||||
|
} else if (reset === 'longRest'){
|
||||||
|
return `Reset${
|
||||||
|
this.model.resetMultiplier && ' x' + this.model.resetMultiplier
|
||||||
|
} on a long rest`;
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
numberToSignedString,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="css" scoped>
|
||||||
|
.ability-value {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 24px !important;
|
||||||
|
color: rgba(0, 0, 0, 0.54);
|
||||||
|
}
|
||||||
|
.mod, .ability-value {
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.attribute-value {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -2,6 +2,7 @@ const ActionViewer = () => import ('/imports/ui/properties/viewers/ActionViewer.
|
|||||||
const AdjustmentViewer = () => import ('/imports/ui/properties/viewers/AdjustmentViewer.vue');
|
const AdjustmentViewer = () => import ('/imports/ui/properties/viewers/AdjustmentViewer.vue');
|
||||||
const AttributeViewer = () => import ('/imports/ui/properties/viewers/AttributeViewer.vue');
|
const AttributeViewer = () => import ('/imports/ui/properties/viewers/AttributeViewer.vue');
|
||||||
const BuffViewer = () => import ('/imports/ui/properties/viewers/BuffViewer.vue');
|
const BuffViewer = () => import ('/imports/ui/properties/viewers/BuffViewer.vue');
|
||||||
|
const BuffRemoverViewer = () => import ('/imports/ui/properties/viewers/BuffRemoverViewer.vue');
|
||||||
const BranchViewer = () => import ('/imports/ui/properties/viewers/BranchViewer.vue');
|
const BranchViewer = () => import ('/imports/ui/properties/viewers/BranchViewer.vue');
|
||||||
const ContainerViewer = () => import ('/imports/ui/properties/viewers/ContainerViewer.vue');
|
const ContainerViewer = () => import ('/imports/ui/properties/viewers/ContainerViewer.vue');
|
||||||
const ClassViewer = () => import ('/imports/ui/properties/viewers/ClassViewer.vue');
|
const ClassViewer = () => import ('/imports/ui/properties/viewers/ClassViewer.vue');
|
||||||
@@ -31,6 +32,7 @@ export default {
|
|||||||
adjustment: AdjustmentViewer,
|
adjustment: AdjustmentViewer,
|
||||||
attribute: AttributeViewer,
|
attribute: AttributeViewer,
|
||||||
buff: BuffViewer,
|
buff: BuffViewer,
|
||||||
|
buffRemover: BuffRemoverViewer,
|
||||||
branch: BranchViewer,
|
branch: BranchViewer,
|
||||||
container: ContainerViewer,
|
container: ContainerViewer,
|
||||||
class: ClassViewer,
|
class: ClassViewer,
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import Vuex from 'vuex';
|
import Vuex from 'vuex';
|
||||||
import dialogStackStore from '/imports/ui/dialogStack/dialogStackStore.js';
|
import dialogStackStore from '/imports/ui/dialogStack/dialogStackStore.js';
|
||||||
|
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||||
|
const tabs = ['stats', 'features', 'inventory', 'spells', 'journal', 'build', 'tree'];
|
||||||
|
const tabsWithoutSpells = ['stats', 'features', 'inventory', 'journal', 'build', 'tree'];
|
||||||
|
|
||||||
Vue.use(Vuex);
|
Vue.use(Vuex);
|
||||||
const store = new Vuex.Store({
|
const store = new Vuex.Store({
|
||||||
@@ -16,13 +19,21 @@ const store = new Vuex.Store({
|
|||||||
showDetailsDialog: false,
|
showDetailsDialog: false,
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
// ...
|
|
||||||
tabById: (state) => (id) => {
|
tabById: (state) => (id) => {
|
||||||
if (id in state.characterSheetTabs){
|
if (id in state.characterSheetTabs){
|
||||||
return state.characterSheetTabs[id];
|
return state.characterSheetTabs[id];
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
tabNameById: (state) => (id) => {
|
||||||
|
const tabNumber = state.characterSheetTabs[id];
|
||||||
|
const creature = Creatures.findOne(id);
|
||||||
|
if (creature?.settings?.hideSpellsTab) {
|
||||||
|
return tabsWithoutSpells[tabNumber];
|
||||||
|
} else {
|
||||||
|
return tabs[tabNumber]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "dicecloud",
|
"name": "dicecloud",
|
||||||
"version": "2.0.33",
|
"version": "2.0.38",
|
||||||
"description": "Unofficial Online Realtime D&D 5e App",
|
"description": "Unofficial Online Realtime D&D 5e App",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -11,7 +11,8 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"run": "meteor",
|
"run": "meteor",
|
||||||
"debug": "meteor --inspect",
|
"debug": "meteor --inspect",
|
||||||
"test": "meteor test --driver-package meteortesting:mocha --port 3001"
|
"test": "meteor test --driver-package meteortesting:mocha --port 3001",
|
||||||
|
"build": "meteor build ../build --architecture os.linux.x86_64"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "14.0.x",
|
"node": "14.0.x",
|
||||||
|
|||||||
Reference in New Issue
Block a user