Compare commits
12 Commits
2.0-beta.1
...
2.0-beta.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e1d670fe9f | ||
|
|
1e9f0515e5 | ||
|
|
0404020335 | ||
|
|
c248d8f4a0 | ||
|
|
8d95da8b7a | ||
|
|
e11ab39864 | ||
|
|
331fcef9ad | ||
|
|
7e3bff9677 | ||
|
|
1b650b26b6 | ||
|
|
5925605962 | ||
|
|
dee1265b69 | ||
|
|
3d3ec3bcf2 |
@@ -92,8 +92,32 @@ let CreatureSchema = new SimpleSchema({
|
||||
type: SimpleSchema.Integer,
|
||||
defaultValue: 0,
|
||||
},
|
||||
// Sum of all weights of items and containers that are carried
|
||||
'denormalizedStats.weightCarried': {
|
||||
// Inventory
|
||||
'denormalizedStats.weightTotal': {
|
||||
type: Number,
|
||||
defaultValue: 0,
|
||||
},
|
||||
'denormalizedStats.weightEquipment': {
|
||||
type: Number,
|
||||
defaultValue: 0,
|
||||
},
|
||||
'denormalizedStats.weightCarried': {
|
||||
type: Number,
|
||||
defaultValue: 0,
|
||||
},
|
||||
'denormalizedStats.valueTotal': {
|
||||
type: Number,
|
||||
defaultValue: 0,
|
||||
},
|
||||
'denormalizedStats.valueEquipment': {
|
||||
type: Number,
|
||||
defaultValue: 0,
|
||||
},
|
||||
'denormalizedStats.valueCarried': {
|
||||
type: Number,
|
||||
defaultValue: 0,
|
||||
},
|
||||
'denormalizedStats.itemsAttuned': {
|
||||
type: Number,
|
||||
defaultValue: 0,
|
||||
},
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
} from '/imports/api/parenting/parenting.js';
|
||||
import {setDocToLastOrder} from '/imports/api/parenting/order.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import { recomputeCreatureByDoc } from '/imports/api/creature/computation/methods/recomputeCreature.js';
|
||||
|
||||
export default function applyBuff({
|
||||
prop,
|
||||
@@ -57,5 +58,10 @@ function copyNodeListToTarget(propList, target, oldParent){
|
||||
collection: CreatureProperties,
|
||||
doc: propList[0],
|
||||
});
|
||||
CreatureProperties.batchInsert(propList);
|
||||
|
||||
CreatureProperties.batchInsert(propList, () => {
|
||||
// This insert is racing the main recompute, recmpute again after it's
|
||||
// certainly finished
|
||||
recomputeCreatureByDoc(target);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -52,23 +52,24 @@ function applyProperty(options){
|
||||
return true;
|
||||
}
|
||||
|
||||
function applyPropertyAndWalkChildren({prop, child, targets, ...options}){
|
||||
let shouldKeepWalking = applyProperty({ prop, targets, ...options });
|
||||
function applyPropertyAndWalkChildren({prop, children, targets, ...options}){
|
||||
let shouldKeepWalking = applyProperty({ prop, children, targets, ...options });
|
||||
if (shouldKeepWalking){
|
||||
applyProperties({ forest: child.children, targets, ...options,});
|
||||
applyProperties({ forest: children, targets, ...options,});
|
||||
}
|
||||
}
|
||||
|
||||
export default function applyProperties({ forest, targets, ...options}){
|
||||
forest.forEach(child => {
|
||||
let prop = child.node;
|
||||
forest.forEach(node => {
|
||||
let prop = node.node;
|
||||
let children = node.children;
|
||||
if (shouldSplit(prop) && targets.length){
|
||||
targets.forEach(target => {
|
||||
let targets = [target]
|
||||
applyPropertyAndWalkChildren({ targets, prop, child, ...options});
|
||||
applyPropertyAndWalkChildren({ targets, prop, children, ...options});
|
||||
});
|
||||
} else {
|
||||
applyPropertyAndWalkChildren({prop, child, targets, ...options});
|
||||
applyPropertyAndWalkChildren({prop, children, targets, ...options});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { assertEditPermission } from '/imports/api/creature/creaturePermissions.
|
||||
import { recomputeCreatureByDoc } from '/imports/api/creature/computation/methods/recomputeCreature.js';
|
||||
import { nodesToTree } from '/imports/api/parenting/parenting.js';
|
||||
import applyProperties from '/imports/api/creature/actions/applyProperties.js';
|
||||
import recomputeInventory from '/imports/api/creature/denormalise/recomputeInventory.js';
|
||||
|
||||
const doAction = new ValidatedMethod({
|
||||
name: 'creatureProperties.doAction',
|
||||
@@ -43,6 +44,8 @@ const doAction = new ValidatedMethod({
|
||||
});
|
||||
doActionWork({action, creature, targets, method: this});
|
||||
|
||||
// The acting creature might have used ammo
|
||||
recomputeInventory(creature._id);
|
||||
// recompute creatures
|
||||
recomputeCreatureByDoc(creature);
|
||||
targets.forEach(target => {
|
||||
|
||||
@@ -51,7 +51,7 @@ export default function spendResources({prop, log}){
|
||||
// Now that we have confirmed that there are no errors, do actual work
|
||||
//Items
|
||||
itemQuantityAdjustments.forEach(adjustQuantityWork);
|
||||
|
||||
|
||||
// Use uses
|
||||
if (prop.usesResult){
|
||||
CreatureProperties.update(prop._id, {
|
||||
|
||||
@@ -4,8 +4,8 @@ export default function embedInlineCalculations(string, calculations){
|
||||
if (!string) return '';
|
||||
if (!calculations) return string;
|
||||
let index = 0;
|
||||
return string.replace(INLINE_CALCULATION_REGEX, () => {
|
||||
return string.replace(INLINE_CALCULATION_REGEX, substring => {
|
||||
let comp = calculations && calculations[index++];
|
||||
return comp && comp.result ? comp.result : string;
|
||||
return (comp && 'result' in comp) ? comp.result : substring;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ export default class EffectAggregator{
|
||||
prop: stat,
|
||||
memo
|
||||
});
|
||||
this.statBaseValue = result.value;
|
||||
this.statBaseValue = +result.value;
|
||||
stat.dependencies = union(
|
||||
stat.dependencies,
|
||||
dependencies,
|
||||
|
||||
@@ -122,10 +122,14 @@ function computeSymbols({calc, memo, prop, dependencies}){
|
||||
computeStat(stat, memo);
|
||||
}
|
||||
if (stat){
|
||||
dependencies = union(dependencies, [
|
||||
stat._id || node.name,
|
||||
...stat.dependencies
|
||||
]);
|
||||
if (stat.dependencies){
|
||||
dependencies = union(dependencies, [
|
||||
stat._id || node.name,
|
||||
...stat.dependencies
|
||||
]);
|
||||
} else {
|
||||
dependencies = union(dependencies, [stat._id || node.name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -4,7 +4,8 @@ import SimpleSchema from 'simpl-schema';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
|
||||
import { recomputePropertyDependencies } from '/imports/api/creature/computation/methods/recomputeCreature.js';
|
||||
import recomputeInventory from '/imports/api/creature/denormalise/recomputeInventory.js';
|
||||
import { recomputeCreatureByDoc } from '/imports/api/creature/computation/methods/recomputeCreature.js';
|
||||
|
||||
const adjustQuantity = new ValidatedMethod({
|
||||
name: 'creatureProperties.adjustQuantity',
|
||||
@@ -30,8 +31,10 @@ const adjustQuantity = new ValidatedMethod({
|
||||
// Do work
|
||||
adjustQuantityWork({property, operation, value});
|
||||
|
||||
// Changing quantity does not change dependencies, recompute deps
|
||||
recomputePropertyDependencies(property);
|
||||
// Changing quantity does not change dependencies, but recomputing the
|
||||
// inventory changes many deps at once, so recompute fully
|
||||
recomputeCreatureByDoc(rootCreature);
|
||||
recomputeInventory(rootCreature._id);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
|
||||
import { organizeDoc } from '/imports/api/parenting/organizeMethods.js';
|
||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
|
||||
import { recomputeCreatureByDoc } from '/imports/api/creature/computation/methods/recomputeCreature.js';
|
||||
import recomputeInventory from '/imports/api/creature/denormalise/recomputeInventory.js';
|
||||
import INVENTORY_TAGS from '/imports/constants/INVENTORY_TAGS.js';
|
||||
|
||||
export function getParentRefByTag(creatureId, tag){
|
||||
@@ -49,7 +51,7 @@ const equipItem = new ValidatedMethod({
|
||||
});
|
||||
let tag = equipped ? INVENTORY_TAGS.equipment : INVENTORY_TAGS.carried;
|
||||
let parentRef = getParentRefByTag(creature._id, tag);
|
||||
// organizeDoc handles recompuation
|
||||
|
||||
organizeDoc.call({
|
||||
docRef: {
|
||||
id: _id,
|
||||
@@ -57,7 +59,11 @@ const equipItem = new ValidatedMethod({
|
||||
},
|
||||
parentRef,
|
||||
order: Number.MAX_SAFE_INTEGER,
|
||||
skipRecompute: true,
|
||||
});
|
||||
|
||||
recomputeInventory(creature._id);
|
||||
recomputeCreatureByDoc(creature);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js
|
||||
import { reorderDocs } from '/imports/api/parenting/order.js';
|
||||
import recomputeInactiveProperties from '/imports/api/creature/denormalise/recomputeInactiveProperties.js';
|
||||
import { recomputeCreatureByDoc } from '/imports/api/creature/computation/methods/recomputeCreature.js';
|
||||
import recomputeInventory from '/imports/api/creature/denormalise/recomputeInventory.js';
|
||||
|
||||
const insertProperty = new ValidatedMethod({
|
||||
name: 'creatureProperties.insert',
|
||||
@@ -35,6 +36,11 @@ export function insertPropertyWork({property, creature}){
|
||||
});
|
||||
// Inserting the active status of the property needs to be denormalised
|
||||
recomputeInactiveProperties(creature._id);
|
||||
|
||||
// Recompute the inventory if it has changed
|
||||
if (property.type === 'item' || property.type === 'container'){
|
||||
recomputeInventory(creature._id);
|
||||
}
|
||||
// Inserting a creature property invalidates dependencies: full recompute
|
||||
recomputeCreatureByDoc(creature);
|
||||
return _id;
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
} from '/imports/api/parenting/parenting.js';
|
||||
import { reorderDocs } from '/imports/api/parenting/order.js';
|
||||
import { setDocToLastOrder } from '/imports/api/parenting/order.js';
|
||||
import recomputeInventory from '/imports/api/creature/denormalise/recomputeInventory.js';
|
||||
|
||||
const insertPropertyFromLibraryNode = new ValidatedMethod({
|
||||
name: 'creatureProperties.insertPropertyFromLibraryNode',
|
||||
@@ -97,6 +98,8 @@ const insertPropertyFromLibraryNode = new ValidatedMethod({
|
||||
|
||||
// The library properties need to denormalise which of them are inactive
|
||||
recomputeInactiveProperties(rootId);
|
||||
// Some of the library properties may be items or containers
|
||||
recomputeInventory(rootCreature._id);
|
||||
// Inserting a creature property invalidates dependencies: full recompute
|
||||
recomputeCreatureByDoc(rootCreature);
|
||||
// Return the docId of the last property, the inserted root property
|
||||
|
||||
@@ -7,6 +7,7 @@ import { restore } from '/imports/api/parenting/softRemove.js';
|
||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
|
||||
import recomputeInactiveProperties from '/imports/api/creature/denormalise/recomputeInactiveProperties.js';
|
||||
import { recomputeCreatureByDoc } from '/imports/api/creature/computation/methods/recomputeCreature.js';
|
||||
import recomputeInventory from '/imports/api/creature/denormalise/recomputeInventory.js';
|
||||
|
||||
const restoreProperty = new ValidatedMethod({
|
||||
name: 'creatureProperties.restore',
|
||||
@@ -27,6 +28,8 @@ const restoreProperty = new ValidatedMethod({
|
||||
// Do work
|
||||
restore({_id, collection: CreatureProperties});
|
||||
|
||||
// Items and containers might be restored
|
||||
recomputeInventory(rootCreature._id);
|
||||
// Parents active status may have changed while it was deleted
|
||||
recomputeInactiveProperties(rootCreature._id);
|
||||
// Changes dependency tree by restoring children
|
||||
|
||||
@@ -6,6 +6,7 @@ import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js
|
||||
import { softRemove } from '/imports/api/parenting/softRemove.js';
|
||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
|
||||
import { recomputeCreatureByDoc } from '/imports/api/creature/computation/methods/recomputeCreature.js';
|
||||
import recomputeInventory from '/imports/api/creature/denormalise/recomputeInventory.js';
|
||||
|
||||
const softRemoveProperty = new ValidatedMethod({
|
||||
name: 'creatureProperties.softRemove',
|
||||
@@ -26,6 +27,8 @@ const softRemoveProperty = new ValidatedMethod({
|
||||
// Do work
|
||||
softRemove({_id, collection: CreatureProperties});
|
||||
|
||||
// Potentially changes items and containers
|
||||
recomputeInventory(rootCreature._id);
|
||||
// Changes dependency tree by removing children
|
||||
recomputeCreatureByDoc(rootCreature);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js
|
||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
|
||||
import { recomputeCreatureByDoc } from '/imports/api/creature/computation/methods/recomputeCreature.js';
|
||||
import recomputeInactiveProperties from '/imports/api/creature/denormalise/recomputeInactiveProperties.js';
|
||||
import recomputeInventory from '/imports/api/creature/denormalise/recomputeInventory.js';
|
||||
|
||||
const updateCreatureProperty = new ValidatedMethod({
|
||||
name: 'creatureProperties.update',
|
||||
@@ -52,6 +53,11 @@ const updateCreatureProperty = new ValidatedMethod({
|
||||
].includes(path[0])){
|
||||
recomputeInactiveProperties(rootCreature._id);
|
||||
}
|
||||
|
||||
if (property.type === 'item' || property.type === 'container'){
|
||||
// Potentially changes items and containers
|
||||
recomputeInventory(rootCreature._id);
|
||||
}
|
||||
// Updating a property is likely to change dependencies, do a full recompute
|
||||
recomputeCreatureByDoc(rootCreature);
|
||||
},
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import nodesToTree from '/imports/api/parenting/parenting.js';
|
||||
import Creatures from '/imports/api/creature/Creatures.js';
|
||||
import { nodesToTree } from '/imports/api/parenting/parenting.js';
|
||||
|
||||
export default function recomputeInventory(creatureId){
|
||||
let inventoryForest = nodesToTree({
|
||||
@@ -10,27 +11,27 @@ export default function recomputeInventory(creatureId){
|
||||
},
|
||||
deactivatedByAncestor: {$ne: true},
|
||||
});
|
||||
return getChildrenInventoryData(inventoryForest);
|
||||
}
|
||||
|
||||
function getChildrenInventoryData(forest){
|
||||
let data = {
|
||||
weightTotal: 0,
|
||||
weightEquipment: 0,
|
||||
weightCarried: 0,
|
||||
valueTotal: 0,
|
||||
valueEquipment: 0,
|
||||
valueCarried: 0,
|
||||
}
|
||||
forest.forEach(tree => {
|
||||
let treeData = getInventoryData(tree);
|
||||
for (let key in data){
|
||||
data[key] += treeData[key];
|
||||
}
|
||||
let containersToWrite = [];
|
||||
let data = getChildrenInventoryData(inventoryForest, containersToWrite);
|
||||
containersToWrite.forEach(container => {
|
||||
CreatureProperties.update(container._id, {$set: {
|
||||
contentsWeight: container.contentsWeight,
|
||||
contentsValue: container.contentsValue,
|
||||
}}, {selector: {type: 'container'}});
|
||||
});
|
||||
Creatures.update(creatureId, {$set: {
|
||||
'denormalizedStats.weightTotal': data.weightTotal,
|
||||
'denormalizedStats.weightEquipment': data.weightEquipment,
|
||||
'denormalizedStats.weightCarried': data.weightCarried,
|
||||
'denormalizedStats.valueTotal': data.valueTotal,
|
||||
'denormalizedStats.valueEquipment': data.valueEquipment,
|
||||
'denormalizedStats.valueCarried': data.valueCarried,
|
||||
'denormalizedStats.itemsAttuned': data.itemsAttuned,
|
||||
}});
|
||||
return data;
|
||||
}
|
||||
|
||||
function getInventoryData(tree){
|
||||
function getChildrenInventoryData(forest, containersToWrite){
|
||||
let data = {
|
||||
weightTotal: 0,
|
||||
weightEquipment: 0,
|
||||
@@ -40,24 +41,41 @@ function getInventoryData(tree){
|
||||
valueCarried: 0,
|
||||
itemsAttuned: 0,
|
||||
}
|
||||
let childData = getChildrenInventoryData(tree.children);
|
||||
forest.forEach(tree => {
|
||||
let treeData = getInventoryData(tree, containersToWrite);
|
||||
for (let key in data){
|
||||
data[key] += treeData[key] || 0;
|
||||
}
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
function getInventoryData(tree, containersToWrite){
|
||||
let data = {
|
||||
weightTotal: 0,
|
||||
weightEquipment: 0,
|
||||
weightCarried: 0,
|
||||
valueTotal: 0,
|
||||
valueEquipment: 0,
|
||||
valueCarried: 0,
|
||||
itemsAttuned: 0,
|
||||
}
|
||||
let childData = getChildrenInventoryData(tree.children, containersToWrite);
|
||||
let node = tree.node;
|
||||
if (node.type === 'container'){
|
||||
data.weightTotal += node.weight;
|
||||
data.valueTotal += node.value;
|
||||
if (node.carried){
|
||||
data.weightCarried += node.weight;
|
||||
data.valueCarried += node.valueCarried;
|
||||
}
|
||||
storeContentsData(node, childData);
|
||||
data.weightTotal += node.weight || 0;
|
||||
data.valueTotal += node.value || 0;
|
||||
data.weightCarried += node.weight || 0;
|
||||
data.valueCarried += node.value || 0;
|
||||
storeContentsData(node, childData, containersToWrite);
|
||||
} else if (node.type === 'item'){
|
||||
data.weightTotal += node.weight * node.quantity;
|
||||
data.valueTotal += node.value * node.quantity;
|
||||
data.weightCarried += node.weight * node.quantity;
|
||||
data.valueCarried += node.valueCarried * node.quantity;
|
||||
data.weightTotal += (node.weight * node.quantity) || 0;
|
||||
data.valueTotal += (node.value * node.quantity) || 0;
|
||||
data.weightCarried += (node.weight * node.quantity) || 0;
|
||||
data.valueCarried += (node.value * node.quantity) || 0;
|
||||
if (node.equipped){
|
||||
data.weightEquipment += node.weight * node.quantity;
|
||||
data.valueEquipment += node.valueCarried * node.quantity;
|
||||
data.weightEquipment += (node.weight * node.quantity) || 0;
|
||||
data.valueEquipment += (node.value * node.quantity) || 0;
|
||||
}
|
||||
if (node.attuned){
|
||||
data.itemsAttuned += 1;
|
||||
@@ -66,16 +84,18 @@ function getInventoryData(tree){
|
||||
for (let key in data){
|
||||
data[key] += childData[key];
|
||||
}
|
||||
if (node.carried === false){
|
||||
data.weightCarried = 0;
|
||||
data.valueCarried = 0;
|
||||
}
|
||||
if (node.contentsWeightless){
|
||||
data.weightCarried = node.weight;
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
function storeContentsData(node, childData){
|
||||
let newContentsWeight;
|
||||
if (node.contentsWeightless){
|
||||
newContentsWeight = 0;
|
||||
} else {
|
||||
newContentsWeight = childData.weightCarried
|
||||
}
|
||||
function storeContentsData(node, childData, containersToWrite){
|
||||
let newContentsWeight = childData.weightCarried
|
||||
if (node.contentsWeight !== newContentsWeight){
|
||||
node.contentsWeight = newContentsWeight;
|
||||
node.contentsWeightChanged = true;
|
||||
@@ -85,4 +105,7 @@ function storeContentsData(node, childData){
|
||||
node.contentsValue = newContentsValue;
|
||||
node.contentsValueChanged = true;
|
||||
}
|
||||
if (node.contentsWeightChanged || node.contentsValueChanged){
|
||||
containersToWrite.push(node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js';
|
||||
import getCollectionByName from '/imports/api/parenting/getCollectionByName.js';
|
||||
import { recomputeCreatureById } from '/imports/api/creature/computation/methods/recomputeCreature.js';
|
||||
import recomputeInactiveProperties from '/imports/api/creature/denormalise/recomputeInactiveProperties.js';
|
||||
|
||||
import recomputeInventory from '/imports/api/creature/denormalise/recomputeInventory.js';
|
||||
const organizeDoc = new ValidatedMethod({
|
||||
name: 'organize.organizeDoc',
|
||||
validate: new SimpleSchema({
|
||||
@@ -20,13 +20,17 @@ const organizeDoc = new ValidatedMethod({
|
||||
type: Number,
|
||||
// Should end in 0.5 to place it reliably between two existing documents
|
||||
},
|
||||
skipRecompute: {
|
||||
type: Boolean,
|
||||
optional: true,
|
||||
},
|
||||
}).validator(),
|
||||
mixins: [RateLimiterMixin],
|
||||
rateLimit: {
|
||||
numRequests: 5,
|
||||
timeInterval: 5000,
|
||||
},
|
||||
run({docRef, parentRef, order}) {
|
||||
run({docRef, parentRef, order, skipRecompute}) {
|
||||
let doc = fetchDocByRef(docRef);
|
||||
let collection = getCollectionByName(docRef.collection);
|
||||
// The user must be able to edit both the doc and its parent to move it
|
||||
@@ -52,15 +56,20 @@ const organizeDoc = new ValidatedMethod({
|
||||
// Figure out which creatures need to be recalculated after this move
|
||||
let docCreatures = getCreatureAncestors(doc);
|
||||
let parentCreatures = getCreatureAncestors(parent);
|
||||
let creaturesToRecompute = union(docCreatures, parentCreatures);
|
||||
// Recompute the creatures
|
||||
creaturesToRecompute.forEach(id => {
|
||||
// The active status of some properties might change due to a change in
|
||||
// ancestry
|
||||
recomputeInactiveProperties(id);
|
||||
// Some Dependencies depend on ancestry, so a full recompute is needed
|
||||
recomputeCreatureById(id);
|
||||
});
|
||||
if (!skipRecompute){
|
||||
let creaturesToRecompute = union(docCreatures, parentCreatures);
|
||||
// Recompute the creatures
|
||||
creaturesToRecompute.forEach(id => {
|
||||
// The active status of some properties might change due to a change in
|
||||
// ancestry
|
||||
recomputeInactiveProperties(id);
|
||||
if (doc.type === 'container' || doc.type === 'item'){
|
||||
recomputeInventory(id);
|
||||
}
|
||||
// Some Dependencies depend on ancestry, so a full recompute is needed
|
||||
recomputeCreatureById(id);
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -91,6 +91,10 @@ const SVG_ICONS = Object.freeze({
|
||||
name: 'weight',
|
||||
shape: 'M256 46c-45.074 0-82 36.926-82 82 0 25.812 12.123 48.936 30.938 64H128L32 480h448l-96-288h-76.938C325.877 176.936 338 153.812 338 128c0-45.074-36.926-82-82-82zm0 36c25.618 0 46 20.382 46 46s-20.382 46-46 46-46-20.382-46-46 20.382-46 46-46z',
|
||||
},
|
||||
'weightless': {
|
||||
name: 'weightless',
|
||||
shape: 'M470.72 20L368.186 49.813l41.563-28.094c-26.254 5.922-59.36 17.502-100.97 36.186l-67.874 70.78L264.97 79.25c-23.247 12.958-47.95 29.99-71.814 49.844l-15.78 64.312L174 145.844c-23.55 21.548-45.624 45.6-63.875 70.812-19.25 26.59-34.28 54.506-41.813 82.438L40.19 280.28c6.138 19.613 11.892 39.232 22.906 58.845.032 1.468.1 2.944.187 4.406L29.657 333.19c11.227 18.284 23.577 35.893 43 49.125.45 1.003.953 1.973 1.438 2.968-11.838 33.33-20.568 67.004-26.53 101.69l18.405 3.155c4.952-28.808 11.836-56.842 20.905-84.563.04.053.084.105.125.157 44.277-156.11 142.813-266.846 287.03-324l6.876 17.374c-129.048 51.143-219.303 145.15-265.78 279.062 18.106.102 35.796-2.088 52.218-6.22l4.875-60.967 13.093 55.5c10.84-3.922 20.88-8.762 29.812-14.376l-20.688-43.47 32.782 34.813c7.944-6.468 14.613-13.678 19.624-21.53 30.308-47.507 62.195-94.728 124.75-134.188l-45.72-16.25 70.157 2.124c2.044-1.085 4.087-2.18 6.19-3.25 9.087-4.63 17.916-10.182 26.31-16.375L378.814 150l74.718-17.625c5.788-5.81 11.174-11.836 16.033-17.97 17.384-21.94 29.034-44.784 26.28-65.56-1.376-10.39-7.556-20.154-17.624-25.626-2.333-1.27-4.832-2.337-7.5-3.22zM106.25 406c-.89 3.06-1.778 6.122-2.625 9.22l2.625-9.22z'
|
||||
}
|
||||
});
|
||||
|
||||
export default SVG_ICONS;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import ArrayNode from '/imports/parser/parseTree/ArrayNode.js';
|
||||
|
||||
export default {
|
||||
'abs': {
|
||||
comment: 'Returns the absolute value of a number',
|
||||
@@ -5,7 +7,7 @@ export default {
|
||||
{input: 'abs(9)', result: '9'},
|
||||
{input: 'abs(-3)', result: '3'},
|
||||
],
|
||||
argumentType: 'number',
|
||||
arguments: ['number'],
|
||||
resultType: 'number',
|
||||
fn: Math.abs,
|
||||
},
|
||||
@@ -15,21 +17,21 @@ export default {
|
||||
{input: 'sqrt(16)', result: '4'},
|
||||
{input: 'sqrt(10)', result: '3.1622776601683795'},
|
||||
],
|
||||
argumentType: 'number',
|
||||
arguments: ['number'],
|
||||
resultType: 'number',
|
||||
fn: Math.sqrt,
|
||||
},
|
||||
'max': {
|
||||
comment: 'Returns the largest of the given numbers',
|
||||
examples: [{input: 'min(12, 6, 3, 168)', result: '168'}],
|
||||
argumentType: 'number',
|
||||
examples: [{input: 'max(12, 6, 3, 168)', result: '168'}],
|
||||
arguments: anyNumberOf('number'),
|
||||
resultType: 'number',
|
||||
fn: Math.max,
|
||||
},
|
||||
'min': {
|
||||
comment: 'Returns the smallest of the given numbers',
|
||||
examples: [{input: 'min(12, 6, 3, 168)', result: '3'}],
|
||||
argumentType: 'number',
|
||||
arguments: anyNumberOf('number'),
|
||||
resultType: 'number',
|
||||
fn: Math.min,
|
||||
},
|
||||
@@ -40,7 +42,7 @@ export default {
|
||||
{input: 'round(5.5)', result: '6'},
|
||||
{input: 'round(5.05)', result: '5'},
|
||||
],
|
||||
argumentType: 'number',
|
||||
arguments: ['number'],
|
||||
resultType: 'number',
|
||||
fn: Math.round,
|
||||
},
|
||||
@@ -52,7 +54,7 @@ export default {
|
||||
{input: 'floor(5)', result: '5'},
|
||||
{input: 'floor(-5.5)', result: '-6'},
|
||||
],
|
||||
argumentType: 'number',
|
||||
arguments: ['number'],
|
||||
resultType: 'number',
|
||||
fn: Math.floor,
|
||||
},
|
||||
@@ -64,7 +66,7 @@ export default {
|
||||
{input: 'ceil(5)', result: '5'},
|
||||
{input: 'ceil(-5.5)', result: '-5'},
|
||||
],
|
||||
argumentType: 'number',
|
||||
arguments: ['number'],
|
||||
resultType: 'number',
|
||||
fn: Math.ceil,
|
||||
},
|
||||
@@ -76,7 +78,7 @@ export default {
|
||||
{input: 'trunc(5)', result: '5'},
|
||||
{input: 'trunc(-5.5)', result: '-5'},
|
||||
],
|
||||
argumentType: 'number',
|
||||
arguments:[ 'number'],
|
||||
resultType: 'number',
|
||||
fn: Math.trunc,
|
||||
},
|
||||
@@ -87,8 +89,32 @@ export default {
|
||||
{input: 'sign(3)', result: '1'},
|
||||
{input: 'sign(0)', result: '0'},
|
||||
],
|
||||
argumentType: 'number',
|
||||
arguments: ['number'],
|
||||
resultType: 'number',
|
||||
fn: Math.sign,
|
||||
},
|
||||
'tableLookup': {
|
||||
comment: 'Returns the index of the last value in the array that is less than the specified amount',
|
||||
examples: [
|
||||
{input: 'tableLookup([100, 300, 900], 457)', result: '2'},
|
||||
{input: 'tableLookup([100, 300, 900], 23)', result: '0'},
|
||||
{input: 'tableLookup([100, 300, 900, 1200], 900)', result: '3'},
|
||||
{input: 'tableLookup([100, 300], 594)', result: '2'},
|
||||
],
|
||||
arguments: [ArrayNode, 'number'],
|
||||
resultType: 'number',
|
||||
fn: function tableLookup(arrayNode, number){
|
||||
for(let i in arrayNode.values){
|
||||
let node = arrayNode.values[i];
|
||||
if (node.value > number) return i;
|
||||
}
|
||||
return arrayNode.values.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function anyNumberOf(type){
|
||||
let argumentArray = [type];
|
||||
argumentArray.anyLength = true;
|
||||
return argumentArray;
|
||||
}
|
||||
|
||||
@@ -11,40 +11,64 @@ export default class CallNode extends ParseNode {
|
||||
}
|
||||
resolve(fn, scope, context){
|
||||
let func = functions[this.functionName];
|
||||
// Check that the function exists
|
||||
if (!func) return new ErrorNode({
|
||||
node: this,
|
||||
error: `${this.functionName} is not a function`,
|
||||
error: `${this.functionName} is not a supported function`,
|
||||
context,
|
||||
});
|
||||
let args = castArgsToType({fn, scope, context, args: this.args, type: func.argumentType});
|
||||
if (args.failed){
|
||||
if (fn === 'reduce'){
|
||||
|
||||
// Resolve the arguments
|
||||
let resolvedArgs = this.args.map(node => node[fn](scope, context));
|
||||
|
||||
// Check that the arguments match what is expected
|
||||
let checkFailed = this.checkArugments({
|
||||
fn,
|
||||
context,
|
||||
resolvedArgs,
|
||||
argumentsExpected: func.arguments
|
||||
});
|
||||
|
||||
if (checkFailed){
|
||||
if (fn !== 'reduce'){
|
||||
return new ErrorNode({
|
||||
node: this,
|
||||
error: 'Could not convert all arguments to the correct type',
|
||||
context,
|
||||
error: `Invalid arguments to ${this.functionName} function`,
|
||||
});
|
||||
} else {
|
||||
return new CallNode({
|
||||
functionName: this.functionName,
|
||||
args: args,
|
||||
args: resolvedArgs,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
let value = func.fn.apply(null, args);
|
||||
return new ConstantNode({
|
||||
value,
|
||||
type: 'number',
|
||||
previousNodes: [this],
|
||||
});
|
||||
} catch (error) {
|
||||
return new ErrorNode({
|
||||
node: this,
|
||||
error,
|
||||
context,
|
||||
});
|
||||
}
|
||||
|
||||
// Map contant nodes to constants before attempting to run the function
|
||||
let mappedArgs = resolvedArgs.map(node => {
|
||||
if (node instanceof ConstantNode){
|
||||
return node.value;
|
||||
} else {
|
||||
return node;
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
// Run the function
|
||||
let value = func.fn.apply(null, mappedArgs);
|
||||
|
||||
let type = typeof value;
|
||||
if (type === 'number' || type === 'string' || type === 'boolean'){
|
||||
// Convert constant results into constant nodes
|
||||
return new ConstantNode({ value, type });
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
} catch (error) {
|
||||
return new ErrorNode({
|
||||
node: this,
|
||||
error: error.message || error,
|
||||
context,
|
||||
});
|
||||
}
|
||||
}
|
||||
toString(){
|
||||
@@ -57,20 +81,47 @@ export default class CallNode extends ParseNode {
|
||||
replaceChildren(fn){
|
||||
this.args = this.args.map(arg => arg.replaceNodes(fn));
|
||||
}
|
||||
}
|
||||
checkArugments({fn, context, argumentsExpected, resolvedArgs}){
|
||||
// Check that the number of arguments matches the number expected
|
||||
if (
|
||||
!argumentsExpected.anyLength &&
|
||||
argumentsExpected.length !== resolvedArgs.length
|
||||
){
|
||||
context.storeError({
|
||||
type: 'error',
|
||||
message: 'Incorrect number of arguments ' +
|
||||
`to ${this.functionName} function, ` +
|
||||
`expected ${argumentsExpected.length} got ${resolvedArgs.length}`
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
function castArgsToType({fn, scope, context, args, type}){
|
||||
let resolvedArgs = args.map(node => node[fn](scope, context))
|
||||
let result = [];
|
||||
if (type === 'number'){
|
||||
resolvedArgs.forEach(node => {
|
||||
if (node.isNumber){
|
||||
result.push(node.value);
|
||||
let failed = false;
|
||||
// Check that each argument is of the correct type
|
||||
resolvedArgs.forEach((node, index) => {
|
||||
let type;
|
||||
if (argumentsExpected.anyLength){
|
||||
type = argumentsExpected[0];
|
||||
} else {
|
||||
resolvedArgs.failed = true;
|
||||
type = argumentsExpected[index];
|
||||
}
|
||||
})
|
||||
if (typeof type === 'string'){
|
||||
// Type being a string means a constant node with matching type
|
||||
if (node.type !== type) failed = true;
|
||||
} else {
|
||||
// Otherwise check that the node is an instance of the given type
|
||||
if (!(node instanceof type)) failed = true;
|
||||
}
|
||||
if (failed && fn === 'reduce'){
|
||||
let typeName = typeof type === 'string' ? type : type.constructor.name;
|
||||
let nodeName = node.type || node.constructor.name
|
||||
context.storeError({
|
||||
type: 'error',
|
||||
message: `Incorrect arguments to ${this.functionName} function` +
|
||||
`expected ${typeName} got ${nodeName}`
|
||||
});
|
||||
}
|
||||
});
|
||||
return failed;
|
||||
}
|
||||
if (resolvedArgs.failed) return resolvedArgs;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import Creatures from '/imports/api/creature/Creatures.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import CreatureLogs from '/imports/api/creature/log/CreatureLogs.js';
|
||||
import { assertViewPermission } from '/imports/api/creature/creaturePermissions.js';
|
||||
import recomputeInvetory from '/imports/api/creature/denormalise/recomputeInventory.js';
|
||||
import { recomputeCreatureById } from '/imports/api/creature/computation/methods/recomputeCreature.js';
|
||||
import VERSION from '/imports/constants/VERSION.js';
|
||||
|
||||
@@ -25,7 +26,10 @@ Meteor.publish('singleCharacter', function(creatureId){
|
||||
try { assertViewPermission(creature, userId) }
|
||||
catch(e){ return [] }
|
||||
if (creature.computeVersion !== VERSION){
|
||||
try { recomputeCreatureById(creatureId) }
|
||||
try {
|
||||
recomputeInvetory(creatureId);
|
||||
recomputeCreatureById(creatureId)
|
||||
}
|
||||
catch(e){ console.error(e) }
|
||||
}
|
||||
return [
|
||||
|
||||
@@ -50,7 +50,7 @@ Meteor.publish('slotFillers', function(slotId){
|
||||
}
|
||||
this.autorun(function(){
|
||||
// Get the limit of the documents the user can fetch
|
||||
var limit = self.data('limit') || 16;
|
||||
var limit = self.data('limit') || 20;
|
||||
check(limit, Number);
|
||||
|
||||
// Get the search term
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
<v-layout
|
||||
v-if="editing && model"
|
||||
key="edit-buttons"
|
||||
style="flex-shrink: 0;"
|
||||
>
|
||||
<v-spacer />
|
||||
<color-picker
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
>
|
||||
Level {{ creature.variables.level.value }}
|
||||
</v-card-title>
|
||||
<v-list>
|
||||
<v-list two-line>
|
||||
<v-list-tile>
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title
|
||||
@@ -52,7 +52,14 @@
|
||||
>
|
||||
{{ creature.variables.milestoneLevels.value }} Milestone levels
|
||||
</v-list-tile-title>
|
||||
<v-list-tile-title v-else>
|
||||
<v-list-tile-title
|
||||
v-if="
|
||||
!(creature.variables.milestoneLevels &&
|
||||
creature.variables.milestoneLevels.value) ||
|
||||
(creature.variables.xp &&
|
||||
creature.variables.xp.value)
|
||||
"
|
||||
>
|
||||
{{
|
||||
creature.variables.xp &&
|
||||
creature.variables.xp.value ||
|
||||
|
||||
@@ -1,6 +1,59 @@
|
||||
<template lang="html">
|
||||
<div class="inventory">
|
||||
<column-layout wide-columns>
|
||||
<div>
|
||||
<v-card>
|
||||
<v-list>
|
||||
<v-list-tile>
|
||||
<v-list-tile-avatar>
|
||||
<v-icon>$vuetify.icons.injustice</v-icon>
|
||||
</v-list-tile-avatar>
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title>
|
||||
Weight Carried
|
||||
</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
<v-list-tile-action>
|
||||
<v-list-tile-title>
|
||||
{{ creature.denormalizedStats.weightCarried || 0 }} lb
|
||||
</v-list-tile-title>
|
||||
</v-list-tile-action>
|
||||
</v-list-tile>
|
||||
<v-list-tile>
|
||||
<v-list-tile-avatar>
|
||||
<v-icon>$vuetify.icons.cash</v-icon>
|
||||
</v-list-tile-avatar>
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title>
|
||||
Net worth
|
||||
</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
<v-list-tile-action>
|
||||
<v-list-tile-title>
|
||||
<coin-value
|
||||
:value="creature.denormalizedStats.valueTotal || 0"
|
||||
/>
|
||||
</v-list-tile-title>
|
||||
</v-list-tile-action>
|
||||
</v-list-tile>
|
||||
<v-list-tile v-if="creature.denormalizedStats.itemsAttuned">
|
||||
<v-list-tile-avatar>
|
||||
<v-icon>$vuetify.icons.spell</v-icon>
|
||||
</v-list-tile-avatar>
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title>
|
||||
Items attuned
|
||||
</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
<v-list-tile-action>
|
||||
<v-list-tile-title>
|
||||
{{ creature.denormalizedStats.itemsAttuned }}
|
||||
</v-list-tile-title>
|
||||
</v-list-tile-action>
|
||||
</v-list-tile>
|
||||
</v-list>
|
||||
</v-card>
|
||||
</div>
|
||||
<div>
|
||||
<toolbar-card
|
||||
:color="creature.color"
|
||||
@@ -53,6 +106,7 @@ import ToolbarCard from '/imports/ui/components/ToolbarCard.vue';
|
||||
import ItemList from '/imports/ui/properties/components/inventory/ItemList.vue';
|
||||
import { getParentRefByTag } from '/imports/api/creature/creatureProperties/methods/equipItem.js';
|
||||
import INVENTORY_TAGS from '/imports/constants/INVENTORY_TAGS.js';
|
||||
import CoinValue from '/imports/ui/components/CoinValue.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -60,6 +114,7 @@ export default {
|
||||
ContainerCard,
|
||||
ToolbarCard,
|
||||
ItemList,
|
||||
CoinValue,
|
||||
},
|
||||
props: {
|
||||
creatureId: {
|
||||
@@ -82,7 +137,10 @@ export default {
|
||||
});
|
||||
},
|
||||
creature(){
|
||||
return Creatures.findOne(this.creatureId, {fields: {color: 1}});
|
||||
return Creatures.findOne(this.creatureId, {fields: {
|
||||
color: 1,
|
||||
denormalizedStats: 1,
|
||||
}});
|
||||
},
|
||||
containersWithoutAncestorContainers(){
|
||||
return CreatureProperties.find({
|
||||
|
||||
@@ -196,7 +196,7 @@ export default {
|
||||
},
|
||||
loadMore(){
|
||||
if (this.currentLimit >= this.countAll) return;
|
||||
this._subs['slotFillers'].setData('limit', this.currentLimit + 16);
|
||||
this._subs['slotFillers'].setData('limit', this.currentLimit + 20);
|
||||
},
|
||||
insert(){
|
||||
if (!this.selectedNode) return;
|
||||
|
||||
@@ -9,6 +9,31 @@
|
||||
{{ model.name }}
|
||||
</v-toolbar-title>
|
||||
<v-spacer />
|
||||
<v-toolbar-title>
|
||||
<v-icon
|
||||
small
|
||||
style="width: 16px;"
|
||||
class="mr-1"
|
||||
>
|
||||
$vuetify.icons.weight
|
||||
</v-icon>
|
||||
{{ (model.contentsWeight ? 0 : model.contentsWeight || 0) + (model.weight || 0) }}
|
||||
</v-toolbar-title>
|
||||
<v-toolbar-title
|
||||
class="layout row align-center"
|
||||
style="flex-grow: 0;"
|
||||
>
|
||||
<v-icon
|
||||
small
|
||||
style="width: 16px;"
|
||||
class="mr-1"
|
||||
>
|
||||
$vuetify.icons.two_coins
|
||||
</v-icon>
|
||||
<coin-value
|
||||
:value="(model.contentsValue || 0) + (model.value || 0)"
|
||||
/>
|
||||
</v-toolbar-title>
|
||||
</template>
|
||||
<v-card-text class="px-0">
|
||||
<item-list
|
||||
@@ -23,11 +48,13 @@
|
||||
import ToolbarCard from '/imports/ui/components/ToolbarCard.vue';
|
||||
import ItemList from '/imports/ui/properties/components/inventory/ItemList.vue';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import CoinValue from '/imports/ui/components/CoinValue.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ToolbarCard,
|
||||
ItemList,
|
||||
CoinValue,
|
||||
},
|
||||
props: {
|
||||
model: {
|
||||
|
||||
@@ -15,7 +15,13 @@
|
||||
{{ title }}
|
||||
</v-list-tile-title>
|
||||
</v-list-tile-content>
|
||||
<v-list-tile-action>
|
||||
<v-list-tile-action
|
||||
v-if="model.attuned"
|
||||
style="min-width: 40px;"
|
||||
>
|
||||
<v-icon>$vuetify.icons.spell</v-icon>
|
||||
</v-list-tile-action>
|
||||
<v-list-tile-action style="min-width: 40px;">
|
||||
<increment-button
|
||||
v-if="context.creatureId && model.showIncrement"
|
||||
icon
|
||||
|
||||
@@ -10,7 +10,7 @@ export default {
|
||||
},
|
||||
},
|
||||
mounted(){
|
||||
if (this.$refs.focusFirst){
|
||||
if (this.$refs.focusFirst && this.$refs.focusFirst.focus){
|
||||
setTimeout(() => this.$refs.focusFirst.focus(), 300);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
<property-tags :tags="model.tags" />
|
||||
<div class="layout row wrap justify-space-around">
|
||||
<div
|
||||
v-if="model.value !== undefined"
|
||||
class="mr-3 my-3"
|
||||
>
|
||||
<v-layout
|
||||
v-if="model.value !== undefined"
|
||||
row
|
||||
align-center
|
||||
class="mb-2"
|
||||
>
|
||||
<v-icon
|
||||
class="mr-2"
|
||||
@@ -21,15 +22,34 @@
|
||||
:value="model.value"
|
||||
/>
|
||||
</v-layout>
|
||||
</div>
|
||||
<div
|
||||
v-if="model.weight !== undefined"
|
||||
class="my-3"
|
||||
>
|
||||
<v-layout
|
||||
row
|
||||
align-center
|
||||
>
|
||||
<v-icon
|
||||
class="mr-2"
|
||||
x-large
|
||||
>
|
||||
$vuetify.icons.cash
|
||||
</v-icon>
|
||||
<coin-value
|
||||
class="title mr-2"
|
||||
:value="model.contentsValue"
|
||||
/>
|
||||
<span class="title">
|
||||
contents
|
||||
</span>
|
||||
</v-layout>
|
||||
</div>
|
||||
<div
|
||||
class="my-3"
|
||||
>
|
||||
<v-layout
|
||||
v-if="model.weight !== undefined"
|
||||
row
|
||||
align-center
|
||||
justify-end
|
||||
class="mb-2"
|
||||
>
|
||||
<span class="title mr-2">
|
||||
{{ model.weight }} lb
|
||||
@@ -41,6 +61,45 @@
|
||||
$vuetify.icons.weight
|
||||
</v-icon>
|
||||
</v-layout>
|
||||
<v-layout
|
||||
row
|
||||
align-center
|
||||
justify-end
|
||||
:class="{'mb-2': model.contentsWeightless}"
|
||||
>
|
||||
<span class="title mr-2">
|
||||
{{ model.contentsWeight }} lb
|
||||
</span>
|
||||
<span
|
||||
class="title"
|
||||
>
|
||||
contents
|
||||
</span>
|
||||
<v-icon
|
||||
class="ml-2"
|
||||
x-large
|
||||
>
|
||||
$vuetify.icons.injustice
|
||||
</v-icon>
|
||||
</v-layout>
|
||||
<v-layout
|
||||
v-if="model.contentsWeightless"
|
||||
row
|
||||
align-center
|
||||
justify-end
|
||||
>
|
||||
<span
|
||||
class="title"
|
||||
>
|
||||
Contents weightless
|
||||
</span>
|
||||
<v-icon
|
||||
class="ml-2"
|
||||
x-large
|
||||
>
|
||||
$vuetify.icons.weightless
|
||||
</v-icon>
|
||||
</v-layout>
|
||||
</div>
|
||||
</div>
|
||||
<property-description
|
||||
|
||||
@@ -89,6 +89,7 @@
|
||||
row
|
||||
align-center
|
||||
justify-end
|
||||
:class="{'mb-2': model.attuned}"
|
||||
>
|
||||
<span class="title mr-2">
|
||||
{{ model.weight }} lb
|
||||
@@ -106,6 +107,22 @@
|
||||
$vuetify.icons.weight
|
||||
</v-icon>
|
||||
</v-layout>
|
||||
<v-layout
|
||||
v-if="model.attuned"
|
||||
row
|
||||
align-center
|
||||
justify-end
|
||||
>
|
||||
<span class="title">
|
||||
Attuned
|
||||
</span>
|
||||
<v-icon
|
||||
class="ml-2"
|
||||
x-large
|
||||
>
|
||||
$vuetify.icons.spell
|
||||
</v-icon>
|
||||
</v-layout>
|
||||
</div>
|
||||
</div>
|
||||
<property-description
|
||||
|
||||
Reference in New Issue
Block a user