Compare commits
27 Commits
2.0-beta.4
...
2.0-beta.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
93ac9215c2 | ||
|
|
a6b501a62c | ||
|
|
e956bacf07 | ||
|
|
60b6b283b1 | ||
|
|
1c9b390551 | ||
|
|
21a487635d | ||
|
|
c92a26d5e6 | ||
|
|
49b514b8f3 | ||
|
|
5cb835c536 | ||
|
|
aa8f2d230d | ||
|
|
2fa913b09a | ||
|
|
de598c70a7 | ||
|
|
baecdeff24 | ||
|
|
d4b7d22b5f | ||
|
|
87f79737e8 | ||
|
|
9f0ffe13f8 | ||
|
|
adaa31d76c | ||
|
|
b051d764f8 | ||
|
|
ffb5b4a4f3 | ||
|
|
fd87b7fb75 | ||
|
|
f035902842 | ||
|
|
dbc5f7253f | ||
|
|
f0e7253374 | ||
|
|
ffe37bf907 | ||
|
|
a63e2099d3 | ||
|
|
0308e4e7a7 | ||
|
|
43f8df09f0 |
@@ -11,14 +11,14 @@ accounts-google@1.4.0
|
|||||||
email@2.2.1
|
email@2.2.1
|
||||||
meteor-base@1.5.1
|
meteor-base@1.5.1
|
||||||
mobile-experience@1.1.0
|
mobile-experience@1.1.0
|
||||||
mongo@1.15.0
|
mongo@1.16.0-beta280.7
|
||||||
session@1.2.0
|
session@1.2.0
|
||||||
tracker@1.2.0
|
tracker@1.2.0
|
||||||
logging@1.3.1
|
logging@1.3.1
|
||||||
reload@1.3.1
|
reload@1.3.1
|
||||||
ejson@1.1.2
|
ejson@1.1.2
|
||||||
check@1.3.1
|
check@1.3.1
|
||||||
standard-minifier-js@2.8.0
|
standard-minifier-js@2.8.1
|
||||||
shell-server@0.5.0
|
shell-server@0.5.0
|
||||||
ecmascript@0.16.2
|
ecmascript@0.16.2
|
||||||
es5-shim@4.8.0
|
es5-shim@4.8.0
|
||||||
@@ -48,3 +48,4 @@ simple:rest-bearer-token-parser
|
|||||||
simple:rest-json-error-handler
|
simple:rest-json-error-handler
|
||||||
littledata:synced-cron
|
littledata:synced-cron
|
||||||
mdg:meteor-apm-agent
|
mdg:meteor-apm-agent
|
||||||
|
typescript
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
METEOR@2.7.3
|
METEOR@2.8-beta.7
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
accounts-base@2.2.3
|
accounts-base@2.2.4
|
||||||
accounts-google@1.4.0
|
accounts-google@1.4.0
|
||||||
accounts-oauth@1.4.1
|
accounts-oauth@1.4.1
|
||||||
accounts-password@2.3.1
|
accounts-password@2.3.1
|
||||||
@@ -12,7 +12,7 @@ aldeed:collection2@3.5.0
|
|||||||
aldeed:schema-index@3.0.0
|
aldeed:schema-index@3.0.0
|
||||||
allow-deny@1.1.1
|
allow-deny@1.1.1
|
||||||
autoupdate@1.8.0
|
autoupdate@1.8.0
|
||||||
babel-compiler@7.9.0
|
babel-compiler@7.9.2
|
||||||
babel-runtime@1.5.1
|
babel-runtime@1.5.1
|
||||||
base64@1.0.12
|
base64@1.0.12
|
||||||
binary-heap@1.0.11
|
binary-heap@1.0.11
|
||||||
@@ -55,33 +55,33 @@ littledata:synced-cron@1.5.1
|
|||||||
livedata@1.0.18
|
livedata@1.0.18
|
||||||
localstorage@1.2.0
|
localstorage@1.2.0
|
||||||
logging@1.3.1
|
logging@1.3.1
|
||||||
mdg:meteor-apm-agent@3.5.0
|
mdg:meteor-apm-agent@3.5.1
|
||||||
mdg:validated-method@1.2.0
|
mdg:validated-method@1.2.0
|
||||||
meteor@1.10.0
|
meteor@1.10.1-beta280.7
|
||||||
meteor-base@1.5.1
|
meteor-base@1.5.1
|
||||||
meteortesting:browser-tests@1.3.5
|
meteortesting:browser-tests@1.3.5
|
||||||
meteortesting:mocha@2.0.3
|
meteortesting:mocha@2.0.3
|
||||||
meteortesting:mocha-core@8.1.2
|
meteortesting:mocha-core@8.1.2
|
||||||
mikowals:batch-insert@1.3.0
|
mikowals:batch-insert@1.3.0
|
||||||
minifier-css@1.6.0
|
minifier-css@1.6.1
|
||||||
minifier-js@2.7.4
|
minifier-js@2.7.5
|
||||||
minimongo@1.8.0
|
minimongo@1.9.0-beta280.7
|
||||||
mobile-experience@1.1.0
|
mobile-experience@1.1.0
|
||||||
mobile-status-bar@1.1.0
|
mobile-status-bar@1.1.0
|
||||||
modern-browsers@0.1.8
|
modern-browsers@0.1.8
|
||||||
modules@0.18.0
|
modules@0.19.0-beta280.7
|
||||||
modules-runtime@0.13.0
|
modules-runtime@0.13.0
|
||||||
mongo@1.15.0
|
mongo@1.16.0-beta280.7
|
||||||
mongo-decimal@0.1.3
|
mongo-decimal@0.1.3
|
||||||
mongo-dev-server@1.1.0
|
mongo-dev-server@1.1.0
|
||||||
mongo-id@1.0.8
|
mongo-id@1.0.8
|
||||||
mongo-livedata@1.0.12
|
mongo-livedata@1.0.12
|
||||||
npm-mongo@4.3.1
|
npm-mongo@4.9.0-beta280.7
|
||||||
oauth@2.1.2
|
oauth@2.1.2
|
||||||
oauth2@1.3.1
|
oauth2@1.3.1
|
||||||
ordered-dict@1.1.0
|
ordered-dict@1.1.0
|
||||||
ostrio:cookies@2.7.2
|
ostrio:cookies@2.7.2
|
||||||
ostrio:files@2.0.1
|
ostrio:files@2.3.0
|
||||||
patreon-oauth@0.1.0
|
patreon-oauth@0.1.0
|
||||||
peerlibrary:assert@0.3.0
|
peerlibrary:assert@0.3.0
|
||||||
peerlibrary:check-extension@0.7.0
|
peerlibrary:check-extension@0.7.0
|
||||||
@@ -116,7 +116,7 @@ simple:rest-json-error-handler@1.1.1
|
|||||||
simple:rest-method-mixin@1.1.0
|
simple:rest-method-mixin@1.1.0
|
||||||
socket-stream-client@0.5.0
|
socket-stream-client@0.5.0
|
||||||
spacebars-compiler@1.3.1
|
spacebars-compiler@1.3.1
|
||||||
standard-minifier-js@2.8.0
|
standard-minifier-js@2.8.1
|
||||||
static-html@1.3.2
|
static-html@1.3.2
|
||||||
templating-tools@1.2.2
|
templating-tools@1.2.2
|
||||||
tmeasday:check-npm-versions@1.0.2
|
tmeasday:check-npm-versions@1.0.2
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ const DenormalisedOnlyCreaturePropertySchema = new SimpleSchema({
|
|||||||
|
|
||||||
CreaturePropertySchema.extend(DenormalisedOnlyCreaturePropertySchema);
|
CreaturePropertySchema.extend(DenormalisedOnlyCreaturePropertySchema);
|
||||||
|
|
||||||
for (let key in propertySchemasIndex){
|
for (let key in propertySchemasIndex) {
|
||||||
let schema = new SimpleSchema({});
|
let schema = new SimpleSchema({});
|
||||||
schema.extend(propertySchemasIndex[key]);
|
schema.extend(propertySchemasIndex[key]);
|
||||||
schema.extend(CreaturePropertySchema);
|
schema.extend(CreaturePropertySchema);
|
||||||
@@ -101,7 +101,7 @@ for (let key in propertySchemasIndex){
|
|||||||
schema.extend(ChildSchema);
|
schema.extend(ChildSchema);
|
||||||
schema.extend(SoftRemovableSchema);
|
schema.extend(SoftRemovableSchema);
|
||||||
CreatureProperties.attachSchema(schema, {
|
CreatureProperties.attachSchema(schema, {
|
||||||
selector: {type: key}
|
selector: { type: key }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,33 +20,33 @@ const adjustQuantity = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({_id, operation, value}) {
|
run({ _id, operation, value }) {
|
||||||
// Permissions
|
// Permissions
|
||||||
let property = CreatureProperties.findOne(_id);
|
let property = CreatureProperties.findOne(_id);
|
||||||
let rootCreature = getRootCreatureAncestor(property);
|
let rootCreature = getRootCreatureAncestor(property);
|
||||||
assertEditPermission(rootCreature, this.userId);
|
assertEditPermission(rootCreature, this.userId);
|
||||||
|
|
||||||
// Do work
|
// Do work
|
||||||
adjustQuantityWork({property, operation, value});
|
adjustQuantityWork({ property, operation, value });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export function adjustQuantityWork({property, operation, value}){
|
export function adjustQuantityWork({ property, operation, value }) {
|
||||||
// Check if property has quantity
|
// Check if property has quantity
|
||||||
let schema = CreatureProperties.simpleSchema(property);
|
let schema = CreatureProperties.simpleSchema(property);
|
||||||
if (!schema.allowsKey('quantity')){
|
if (!schema.allowsKey('quantity')) {
|
||||||
throw new Meteor.Error(
|
throw new Meteor.Error(
|
||||||
'Adjust quantity failed',
|
'Adjust quantity failed',
|
||||||
`Property of type "${property.type}" doesn't have a quantity`
|
`Property of type "${property.type}" doesn't have a quantity`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (operation === 'set'){
|
if (operation === 'set') {
|
||||||
CreatureProperties.update(property._id, {
|
CreatureProperties.update(property._id, {
|
||||||
$set: {quantity: value, dirty: true}
|
$set: { quantity: value, dirty: true }
|
||||||
}, {
|
}, {
|
||||||
selector: property
|
selector: property
|
||||||
});
|
});
|
||||||
} else if (operation === 'increment'){
|
} else if (operation === 'increment') {
|
||||||
// value here is 'damage'
|
// value here is 'damage'
|
||||||
value = -value;
|
value = -value;
|
||||||
let currentQuantity = property.quantity;
|
let currentQuantity = property.quantity;
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ const damageProperty = new ValidatedMethod({
|
|||||||
|
|
||||||
// Check if property can take damage
|
// Check if property can take damage
|
||||||
let schema = CreatureProperties.simpleSchema(prop);
|
let schema = CreatureProperties.simpleSchema(prop);
|
||||||
if (!schema.allowsKey('damage')){
|
if (!schema.allowsKey('damage')) {
|
||||||
throw new Meteor.Error(
|
throw new Meteor.Error(
|
||||||
'Damage property failed',
|
'Damage property failed',
|
||||||
`Property of type "${prop.type}" can't be damaged`
|
`Property of type "${prop.type}" can't be damaged`
|
||||||
@@ -86,7 +86,7 @@ export function damagePropertyWork({ prop, operation, value, actionContext }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let damage, newValue, increment;
|
let damage, newValue, increment;
|
||||||
if (operation === 'set'){
|
if (operation === 'set') {
|
||||||
const total = prop.total || 0;
|
const total = prop.total || 0;
|
||||||
// Set represents what we want the value to be after damage
|
// Set represents what we want the value to be after damage
|
||||||
// So we need the actual damage to get to that value
|
// So we need the actual damage to get to that value
|
||||||
@@ -105,7 +105,7 @@ export function damagePropertyWork({ prop, operation, value, actionContext }) {
|
|||||||
// Also write it straight to the prop so that it is updated in the actionContext
|
// Also write it straight to the prop so that it is updated in the actionContext
|
||||||
prop.damage = damage;
|
prop.damage = damage;
|
||||||
prop.value = newValue;
|
prop.value = newValue;
|
||||||
} else if (operation === 'increment'){
|
} else if (operation === 'increment') {
|
||||||
let currentValue = prop.value || 0;
|
let currentValue = prop.value || 0;
|
||||||
let currentDamage = prop.damage || 0;
|
let currentDamage = prop.damage || 0;
|
||||||
increment = value;
|
increment = value;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
} from '/imports/api/parenting/parenting.js';
|
} from '/imports/api/parenting/parenting.js';
|
||||||
import { reorderDocs } from '/imports/api/parenting/order.js';
|
import { reorderDocs } from '/imports/api/parenting/order.js';
|
||||||
var snackbar;
|
var snackbar;
|
||||||
if (Meteor.isClient){
|
if (Meteor.isClient) {
|
||||||
snackbar = require(
|
snackbar = require(
|
||||||
'/imports/ui/components/snackbars/SnackbarQueue.js'
|
'/imports/ui/components/snackbars/SnackbarQueue.js'
|
||||||
).snackbar
|
).snackbar
|
||||||
@@ -31,7 +31,7 @@ const duplicateProperty = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({_id}) {
|
run({ _id }) {
|
||||||
let property = CreatureProperties.findOne(_id);
|
let property = CreatureProperties.findOne(_id);
|
||||||
let creature = getRootCreatureAncestor(property);
|
let creature = getRootCreatureAncestor(property);
|
||||||
|
|
||||||
@@ -45,16 +45,16 @@ const duplicateProperty = new ValidatedMethod({
|
|||||||
// Get all the descendants
|
// Get all the descendants
|
||||||
let nodes = CreatureProperties.find({
|
let nodes = CreatureProperties.find({
|
||||||
'ancestors.id': _id,
|
'ancestors.id': _id,
|
||||||
removed: {$ne: true},
|
removed: { $ne: true },
|
||||||
}, {
|
}, {
|
||||||
limit: DUPLICATE_CHILDREN_LIMIT + 1,
|
limit: DUPLICATE_CHILDREN_LIMIT + 1,
|
||||||
sort: {order: 1},
|
sort: { order: 1 },
|
||||||
}).fetch();
|
}).fetch();
|
||||||
|
|
||||||
// Alert the user if the limit was hit
|
// Alert the user if the limit was hit
|
||||||
if (nodes.length > DUPLICATE_CHILDREN_LIMIT){
|
if (nodes.length > DUPLICATE_CHILDREN_LIMIT) {
|
||||||
nodes.pop();
|
nodes.pop();
|
||||||
if (Meteor.isClient){
|
if (Meteor.isClient) {
|
||||||
snackbar({
|
snackbar({
|
||||||
text: `Only the first ${DUPLICATE_CHILDREN_LIMIT} children were duplicated`,
|
text: `Only the first ${DUPLICATE_CHILDREN_LIMIT} children were duplicated`,
|
||||||
});
|
});
|
||||||
@@ -64,15 +64,15 @@ const duplicateProperty = new ValidatedMethod({
|
|||||||
// re-map all the ancestors
|
// re-map all the ancestors
|
||||||
setLineageOfDocs({
|
setLineageOfDocs({
|
||||||
docArray: nodes,
|
docArray: nodes,
|
||||||
newAncestry : [
|
newAncestry: [
|
||||||
...property.ancestors,
|
...property.ancestors,
|
||||||
{id: propertyId, collection: 'creatureProperties'}
|
{ id: propertyId, collection: 'creatureProperties' }
|
||||||
],
|
],
|
||||||
oldParent : {id: _id, collection: 'creatureProperties'},
|
oldParent: { id: _id, collection: 'creatureProperties' },
|
||||||
});
|
});
|
||||||
|
|
||||||
// Give the docs new IDs without breaking internal references
|
// Give the docs new IDs without breaking internal references
|
||||||
renewDocIds({docArray: nodes});
|
renewDocIds({ docArray: nodes });
|
||||||
|
|
||||||
// Order the root node
|
// Order the root node
|
||||||
property.order += 0.5;
|
property.order += 0.5;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/
|
|||||||
// Equipping or unequipping an item will also change its parent
|
// Equipping or unequipping an item will also change its parent
|
||||||
const equipItem = new ValidatedMethod({
|
const equipItem = new ValidatedMethod({
|
||||||
name: 'creatureProperties.equip',
|
name: 'creatureProperties.equip',
|
||||||
validate({_id, equipped}){
|
validate({ _id, equipped }) {
|
||||||
if (!_id) throw new Meteor.Error('No _id', '_id is required');
|
if (!_id) throw new Meteor.Error('No _id', '_id is required');
|
||||||
if (equipped !== true && equipped !== false) {
|
if (equipped !== true && equipped !== false) {
|
||||||
throw new Meteor.Error('No equipped', 'equipped is required to be true or false');
|
throw new Meteor.Error('No equipped', 'equipped is required to be true or false');
|
||||||
@@ -21,7 +21,7 @@ const equipItem = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({_id, equipped}) {
|
run({ _id, equipped }) {
|
||||||
let item = CreatureProperties.findOne(_id);
|
let item = CreatureProperties.findOne(_id);
|
||||||
if (item.type !== 'item') throw new Meteor.Error('wrong type',
|
if (item.type !== 'item') throw new Meteor.Error('wrong type',
|
||||||
'Equip and unequip can only be performed on items');
|
'Equip and unequip can only be performed on items');
|
||||||
@@ -30,11 +30,11 @@ const equipItem = new ValidatedMethod({
|
|||||||
CreatureProperties.update(_id, {
|
CreatureProperties.update(_id, {
|
||||||
$set: { equipped, dirty: true },
|
$set: { equipped, dirty: true },
|
||||||
}, {
|
}, {
|
||||||
selector: {type: 'item'},
|
selector: { type: 'item' },
|
||||||
});
|
});
|
||||||
let tag = equipped ? BUILT_IN_TAGS.equipment : BUILT_IN_TAGS.carried;
|
let tag = equipped ? BUILT_IN_TAGS.equipment : BUILT_IN_TAGS.carried;
|
||||||
let parentRef = getParentRefByTag(creature._id, tag);
|
let parentRef = getParentRefByTag(creature._id, tag);
|
||||||
if (!parentRef) parentRef = {id: creature._id, collection: 'creatures'};
|
if (!parentRef) parentRef = { id: creature._id, collection: 'creatures' };
|
||||||
|
|
||||||
organizeDoc.call({
|
organizeDoc.call({
|
||||||
docRef: {
|
docRef: {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/ge
|
|||||||
|
|
||||||
const flipToggle = new ValidatedMethod({
|
const flipToggle = new ValidatedMethod({
|
||||||
name: 'creatureProperties.flipToggle',
|
name: 'creatureProperties.flipToggle',
|
||||||
validate({_id}){
|
validate({ _id }) {
|
||||||
if (!_id) throw new Meteor.Error('No _id', '_id is required');
|
if (!_id) throw new Meteor.Error('No _id', '_id is required');
|
||||||
},
|
},
|
||||||
mixins: [RateLimiterMixin],
|
mixins: [RateLimiterMixin],
|
||||||
@@ -14,16 +14,16 @@ const flipToggle = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({_id}) {
|
run({ _id }) {
|
||||||
// Permission
|
// Permission
|
||||||
let property = CreatureProperties.findOne(_id, {
|
let property = CreatureProperties.findOne(_id, {
|
||||||
fields: {type: 1, ancestors: 1, enabled: 1, disabled: 1}
|
fields: { type: 1, ancestors: 1, enabled: 1, disabled: 1 }
|
||||||
});
|
});
|
||||||
if (property.type !== 'toggle'){
|
if (property.type !== 'toggle') {
|
||||||
throw new Meteor.Error('wrong property',
|
throw new Meteor.Error('wrong property',
|
||||||
'This method can only be applied to toggles');
|
'This method can only be applied to toggles');
|
||||||
}
|
}
|
||||||
if (!property.enabled && !property.disabled){
|
if (!property.enabled && !property.disabled) {
|
||||||
throw new Meteor.Error('Computed toggle',
|
throw new Meteor.Error('Computed toggle',
|
||||||
'Can\'t flip a toggle that is computed')
|
'Can\'t flip a toggle that is computed')
|
||||||
}
|
}
|
||||||
@@ -32,12 +32,14 @@ const flipToggle = new ValidatedMethod({
|
|||||||
|
|
||||||
// Invert the current value, disabled is the canonical store of value
|
// Invert the current value, disabled is the canonical store of value
|
||||||
const currentValue = !property.disabled;
|
const currentValue = !property.disabled;
|
||||||
CreatureProperties.update(_id, {$set: {
|
CreatureProperties.update(_id, {
|
||||||
|
$set: {
|
||||||
enabled: !currentValue,
|
enabled: !currentValue,
|
||||||
disabled: currentValue,
|
disabled: currentValue,
|
||||||
dirty: true,
|
dirty: true,
|
||||||
}}, {
|
}
|
||||||
selector: {type: 'toggle'},
|
}, {
|
||||||
|
selector: { type: 'toggle' },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -24,15 +24,15 @@ const insertProperty = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({creatureProperty, parentRef}) {
|
run({ creatureProperty, parentRef }) {
|
||||||
// get the new ancestry for the properties
|
// get the new ancestry for the properties
|
||||||
let {parentDoc, ancestors} = getAncestry({parentRef});
|
let { parentDoc, ancestors } = getAncestry({ parentRef });
|
||||||
|
|
||||||
// Check permission to edit
|
// Check permission to edit
|
||||||
let rootCreature;
|
let rootCreature;
|
||||||
if (parentRef.collection === 'creatures'){
|
if (parentRef.collection === 'creatures') {
|
||||||
rootCreature = parentDoc;
|
rootCreature = parentDoc;
|
||||||
} else if (parentRef.collection === 'creatureProperties'){
|
} else if (parentRef.collection === 'creatureProperties') {
|
||||||
rootCreature = getRootCreatureAncestor(parentDoc);
|
rootCreature = getRootCreatureAncestor(parentDoc);
|
||||||
} else {
|
} else {
|
||||||
throw `${parentRef.collection} is not a valid parent collection`
|
throw `${parentRef.collection} is not a valid parent collection`
|
||||||
@@ -75,23 +75,23 @@ const insertPropertyAsChildOfTag = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({creatureProperty, creatureId, tag, tagDefaultName}) {
|
run({ creatureProperty, creatureId, tag, tagDefaultName }) {
|
||||||
let parentRef = getParentRefByTag(creatureId, tag);
|
let parentRef = getParentRefByTag(creatureId, tag);
|
||||||
|
|
||||||
if (!parentRef){
|
if (!parentRef) {
|
||||||
// Use the creature as the parent and mark that we need to insert the folder first later
|
// Use the creature as the parent and mark that we need to insert the folder first later
|
||||||
var insertFolderFirst = true;
|
var insertFolderFirst = true;
|
||||||
parentRef = {id: creatureId, collection: 'creatures'};
|
parentRef = { id: creatureId, collection: 'creatures' };
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the new ancestry for the properties
|
// get the new ancestry for the properties
|
||||||
let {parentDoc, ancestors} = getAncestry({parentRef});
|
let { parentDoc, ancestors } = getAncestry({ parentRef });
|
||||||
|
|
||||||
// Check permission to edit
|
// Check permission to edit
|
||||||
let rootCreature;
|
let rootCreature;
|
||||||
if (parentRef.collection === 'creatures'){
|
if (parentRef.collection === 'creatures') {
|
||||||
rootCreature = parentDoc;
|
rootCreature = parentDoc;
|
||||||
} else if (parentRef.collection === 'creatureProperties'){
|
} else if (parentRef.collection === 'creatureProperties') {
|
||||||
rootCreature = getRootCreatureAncestor(parentDoc);
|
rootCreature = getRootCreatureAncestor(parentDoc);
|
||||||
} else {
|
} else {
|
||||||
throw `${parentRef.collection} is not a valid parent collection`
|
throw `${parentRef.collection} is not a valid parent collection`
|
||||||
@@ -99,7 +99,7 @@ const insertPropertyAsChildOfTag = new ValidatedMethod({
|
|||||||
assertEditPermission(rootCreature, this.userId);
|
assertEditPermission(rootCreature, this.userId);
|
||||||
|
|
||||||
// Add the folder first if we need to
|
// Add the folder first if we need to
|
||||||
if (insertFolderFirst){
|
if (insertFolderFirst) {
|
||||||
let order = getHighestOrder({
|
let order = getHighestOrder({
|
||||||
collection: CreatureProperties,
|
collection: CreatureProperties,
|
||||||
ancestorId: parentRef.id,
|
ancestorId: parentRef.id,
|
||||||
@@ -113,7 +113,7 @@ const insertPropertyAsChildOfTag = new ValidatedMethod({
|
|||||||
order,
|
order,
|
||||||
});
|
});
|
||||||
// Make the folder our new parent
|
// Make the folder our new parent
|
||||||
let newParentRef = {id, collection: 'creatureProperties'};
|
let newParentRef = { id, collection: 'creatureProperties' };
|
||||||
ancestors = [parentRef, newParentRef];
|
ancestors = [parentRef, newParentRef];
|
||||||
parentRef = newParentRef;
|
parentRef = newParentRef;
|
||||||
creatureProperty.order = order + 1;
|
creatureProperty.order = order + 1;
|
||||||
@@ -129,7 +129,7 @@ const insertPropertyAsChildOfTag = new ValidatedMethod({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export function insertPropertyWork({property, creature}){
|
export function insertPropertyWork({ property, creature }) {
|
||||||
delete property._id;
|
delete property._id;
|
||||||
property.dirty = true;
|
property.dirty = true;
|
||||||
let _id = CreatureProperties.insert(property);
|
let _id = CreatureProperties.insert(property);
|
||||||
|
|||||||
@@ -39,15 +39,15 @@ const insertPropertyFromLibraryNode = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({nodeIds, parentRef, order}) {
|
run({ nodeIds, parentRef, order }) {
|
||||||
// get the new ancestry for the properties
|
// get the new ancestry for the properties
|
||||||
let {parentDoc, ancestors} = getAncestry({parentRef});
|
let { parentDoc, ancestors } = getAncestry({ parentRef });
|
||||||
|
|
||||||
// Check permission to edit
|
// Check permission to edit
|
||||||
let rootCreature;
|
let rootCreature;
|
||||||
if (parentRef.collection === 'creatures'){
|
if (parentRef.collection === 'creatures') {
|
||||||
rootCreature = parentDoc;
|
rootCreature = parentDoc;
|
||||||
} else if (parentRef.collection === 'creatureProperties'){
|
} else if (parentRef.collection === 'creatureProperties') {
|
||||||
rootCreature = getRootCreatureAncestor(parentDoc);
|
rootCreature = getRootCreatureAncestor(parentDoc);
|
||||||
} else {
|
} else {
|
||||||
throw `${parentRef.collection} is not a valid parent collection`
|
throw `${parentRef.collection} is not a valid parent collection`
|
||||||
@@ -75,13 +75,13 @@ const insertPropertyFromLibraryNode = new ValidatedMethod({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function insertPropertyFromNode(nodeId, ancestors, order){
|
function insertPropertyFromNode(nodeId, ancestors, order) {
|
||||||
// Fetch the library node and its decendents, provided they have not been
|
// Fetch the library node and its decendents, provided they have not been
|
||||||
// removed
|
// removed
|
||||||
// TODO: Check permission to read the library this node is in
|
// TODO: Check permission to read the library this node is in
|
||||||
let node = LibraryNodes.findOne({
|
let node = LibraryNodes.findOne({
|
||||||
_id: nodeId,
|
_id: nodeId,
|
||||||
removed: {$ne: true},
|
removed: { $ne: true },
|
||||||
});
|
});
|
||||||
if (!node) {
|
if (!node) {
|
||||||
if (Meteor.isClient) return;
|
if (Meteor.isClient) return;
|
||||||
@@ -95,7 +95,7 @@ function insertPropertyFromNode(nodeId, ancestors, order){
|
|||||||
let oldParent = node.parent;
|
let oldParent = node.parent;
|
||||||
let nodes = LibraryNodes.find({
|
let nodes = LibraryNodes.find({
|
||||||
'ancestors.id': nodeId,
|
'ancestors.id': nodeId,
|
||||||
removed: {$ne: true},
|
removed: { $ne: true },
|
||||||
}).fetch();
|
}).fetch();
|
||||||
|
|
||||||
// Convert all references into actual nodes
|
// Convert all references into actual nodes
|
||||||
@@ -118,11 +118,11 @@ function insertPropertyFromNode(nodeId, ancestors, order){
|
|||||||
// Give the docs new IDs without breaking internal references
|
// Give the docs new IDs without breaking internal references
|
||||||
renewDocIds({
|
renewDocIds({
|
||||||
docArray: nodes,
|
docArray: nodes,
|
||||||
collectionMap: {'libraryNodes': 'creatureProperties'}
|
collectionMap: { 'libraryNodes': 'creatureProperties' }
|
||||||
});
|
});
|
||||||
|
|
||||||
// Order the root node
|
// Order the root node
|
||||||
if (order === undefined){
|
if (order === undefined) {
|
||||||
setDocToLastOrder({
|
setDocToLastOrder({
|
||||||
collection: CreatureProperties,
|
collection: CreatureProperties,
|
||||||
doc: node,
|
doc: node,
|
||||||
@@ -139,7 +139,7 @@ function insertPropertyFromNode(nodeId, ancestors, order){
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
function storeLibraryNodeReferences(nodes){
|
function storeLibraryNodeReferences(nodes) {
|
||||||
nodes.forEach(node => {
|
nodes.forEach(node => {
|
||||||
if (node.libraryNodeId) return;
|
if (node.libraryNodeId) return;
|
||||||
node.libraryNodeId = node._id;
|
node.libraryNodeId = node._id;
|
||||||
@@ -154,7 +154,7 @@ function dirtyNodes(nodes) {
|
|||||||
|
|
||||||
// Covert node references into actual nodes
|
// Covert node references into actual nodes
|
||||||
// TODO: check permissions for each library a reference node references
|
// TODO: check permissions for each library a reference node references
|
||||||
function reifyNodeReferences(nodes, visitedRefs = new Set(), depth = 0){
|
function reifyNodeReferences(nodes, visitedRefs = new Set(), depth = 0) {
|
||||||
depth += 1;
|
depth += 1;
|
||||||
// New nodes added this function
|
// New nodes added this function
|
||||||
let newNodes = [];
|
let newNodes = [];
|
||||||
@@ -165,9 +165,9 @@ function reifyNodeReferences(nodes, visitedRefs = new Set(), depth = 0){
|
|||||||
if (node.type !== 'reference') return true;
|
if (node.type !== 'reference') return true;
|
||||||
|
|
||||||
// We have gone too deep, keep the reference node as an error
|
// We have gone too deep, keep the reference node as an error
|
||||||
if (depth >= 10){
|
if (depth >= 10) {
|
||||||
if (Meteor.isClient) console.warn('Reference depth limit exceeded');
|
if (Meteor.isClient) console.warn('Reference depth limit exceeded');
|
||||||
node.cache = {error: 'Reference depth limit exceeded'};
|
node.cache = { error: 'Reference depth limit exceeded' };
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,17 +177,17 @@ function reifyNodeReferences(nodes, visitedRefs = new Set(), depth = 0){
|
|||||||
referencedNode.order = node.order;
|
referencedNode.order = node.order;
|
||||||
// We are definitely replacing this node, so add it to the list
|
// We are definitely replacing this node, so add it to the list
|
||||||
visitedRefs.add(node._id);
|
visitedRefs.add(node._id);
|
||||||
} catch (e){
|
} catch (e) {
|
||||||
node.cache = {error: e.reason || e.message || e.toString()};
|
node.cache = { error: e.reason || e.message || e.toString() };
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all the descendants of the referenced node
|
// Get all the descendants of the referenced node
|
||||||
let descendents = LibraryNodes.find({
|
let descendents = LibraryNodes.find({
|
||||||
'ancestors.id': referencedNode._id,
|
'ancestors.id': referencedNode._id,
|
||||||
removed: {$ne: true},
|
removed: { $ne: true },
|
||||||
}, {
|
}, {
|
||||||
sort: {order: 1},
|
sort: { order: 1 },
|
||||||
}).fetch();
|
}).fetch();
|
||||||
|
|
||||||
// We are adding the referenced node and its descendants
|
// We are adding the referenced node and its descendants
|
||||||
@@ -204,11 +204,11 @@ function reifyNodeReferences(nodes, visitedRefs = new Set(), depth = 0){
|
|||||||
// Filter all the looped references
|
// Filter all the looped references
|
||||||
addedNodes = addedNodes.filter(addedNode => {
|
addedNodes = addedNodes.filter(addedNode => {
|
||||||
// Add all non-reference nodes
|
// Add all non-reference nodes
|
||||||
if (addedNode.type !== 'reference'){
|
if (addedNode.type !== 'reference') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// If this exact reference has already been resolved before, filter it out
|
// If this exact reference has already been resolved before, filter it out
|
||||||
if (visitedRefs.has(addedNode._id)){
|
if (visitedRefs.has(addedNode._id)) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
// Otherwise mark it as visited, and keep it
|
// Otherwise mark it as visited, and keep it
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const pullFromProperty = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({_id, path, itemId}){
|
run({ _id, path, itemId }) {
|
||||||
// Permissions
|
// Permissions
|
||||||
let property = CreatureProperties.findOne(_id);
|
let property = CreatureProperties.findOne(_id);
|
||||||
let rootCreature = getRootCreatureAncestor(property);
|
let rootCreature = getRootCreatureAncestor(property);
|
||||||
@@ -23,7 +23,7 @@ const pullFromProperty = new ValidatedMethod({
|
|||||||
$pull: { [path.join('.')]: { _id: itemId } },
|
$pull: { [path.join('.')]: { _id: itemId } },
|
||||||
$set: { dirty: true }
|
$set: { dirty: true }
|
||||||
}, {
|
}, {
|
||||||
selector: {type: property.type},
|
selector: { type: property.type },
|
||||||
getAutoValues: false,
|
getAutoValues: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const pushToProperty = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({_id, path, value}){
|
run({ _id, path, value }) {
|
||||||
// Permissions
|
// Permissions
|
||||||
let property = CreatureProperties.findOne(_id);
|
let property = CreatureProperties.findOne(_id);
|
||||||
let rootCreature = getRootCreatureAncestor(property);
|
let rootCreature = getRootCreatureAncestor(property);
|
||||||
@@ -25,10 +25,10 @@ const pushToProperty = new ValidatedMethod({
|
|||||||
let schema = CreatureProperties.simpleSchema(property);
|
let schema = CreatureProperties.simpleSchema(property);
|
||||||
let maxCount = schema.get(joinedPath, 'maxCount');
|
let maxCount = schema.get(joinedPath, 'maxCount');
|
||||||
|
|
||||||
if (Number.isFinite(maxCount)){
|
if (Number.isFinite(maxCount)) {
|
||||||
let array = get(property, path);
|
let array = get(property, path);
|
||||||
let currentCount = array ? array.length : 0;
|
let currentCount = array ? array.length : 0;
|
||||||
if (currentCount >= maxCount){
|
if (currentCount >= maxCount) {
|
||||||
throw new Meteor.Error(
|
throw new Meteor.Error(
|
||||||
'Array is full',
|
'Array is full',
|
||||||
`Cannot have more than ${maxCount} values`
|
`Cannot have more than ${maxCount} values`
|
||||||
@@ -41,7 +41,7 @@ const pushToProperty = new ValidatedMethod({
|
|||||||
$push: { [joinedPath]: value },
|
$push: { [joinedPath]: value },
|
||||||
$set: { dirty: true },
|
$set: { dirty: true },
|
||||||
}, {
|
}, {
|
||||||
selector: {type: property.type},
|
selector: { type: property.type },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ const restoreProperty = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({_id}){
|
run({ _id }) {
|
||||||
// Permissions
|
// Permissions
|
||||||
let property = CreatureProperties.findOne(_id);
|
let property = CreatureProperties.findOne(_id);
|
||||||
let rootCreature = getRootCreatureAncestor(property);
|
let rootCreature = getRootCreatureAncestor(property);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const selectAmmoItem = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({actionId, itemId, itemConsumedIndex}) {
|
run({ actionId, itemId, itemConsumedIndex }) {
|
||||||
// Permissions
|
// Permissions
|
||||||
let action = CreatureProperties.findOne(actionId);
|
let action = CreatureProperties.findOne(actionId);
|
||||||
let rootCreature = getRootCreatureAncestor(action);
|
let rootCreature = getRootCreatureAncestor(action);
|
||||||
@@ -25,12 +25,12 @@ const selectAmmoItem = new ValidatedMethod({
|
|||||||
|
|
||||||
// Check that this index has a document to edit
|
// Check that this index has a document to edit
|
||||||
let itemConsumed = action.resources.itemsConsumed[itemConsumedIndex];
|
let itemConsumed = action.resources.itemsConsumed[itemConsumedIndex];
|
||||||
if (!itemConsumed){
|
if (!itemConsumed) {
|
||||||
throw new Meteor.Error('Resouce not found',
|
throw new Meteor.Error('Resouce not found',
|
||||||
'Could not set ammo, because the ammo document was not found');
|
'Could not set ammo, because the ammo document was not found');
|
||||||
}
|
}
|
||||||
let itemToLink = CreatureProperties.findOne(itemId);
|
let itemToLink = CreatureProperties.findOne(itemId);
|
||||||
if (!itemToLink){
|
if (!itemToLink) {
|
||||||
throw new Meteor.Error('Item not found',
|
throw new Meteor.Error('Item not found',
|
||||||
'Could not set ammo: the item was not found');
|
'Could not set ammo: the item was not found');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ const softRemoveProperty = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({_id}){
|
run({ _id }) {
|
||||||
// Permissions
|
// Permissions
|
||||||
let property = CreatureProperties.findOne(_id);
|
let property = CreatureProperties.findOne(_id);
|
||||||
let rootCreature = getRootCreatureAncestor(property);
|
let rootCreature = getRootCreatureAncestor(property);
|
||||||
assertEditPermission(rootCreature, this.userId);
|
assertEditPermission(rootCreature, this.userId);
|
||||||
|
|
||||||
// Do work
|
// Do work
|
||||||
softRemove({_id, collection: CreatureProperties});
|
softRemove({ _id, collection: CreatureProperties });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/ge
|
|||||||
|
|
||||||
const updateCreatureProperty = new ValidatedMethod({
|
const updateCreatureProperty = new ValidatedMethod({
|
||||||
name: 'creatureProperties.update',
|
name: 'creatureProperties.update',
|
||||||
validate({_id, path}){
|
validate({ _id, path }) {
|
||||||
if (!_id) throw new Meteor.Error('No _id', '_id is required');
|
if (!_id) throw new Meteor.Error('No _id', '_id is required');
|
||||||
// We cannot change these fields with a simple update
|
// We cannot change these fields with a simple update
|
||||||
switch (path[0]){
|
switch (path[0]) {
|
||||||
case 'type':
|
case 'type':
|
||||||
case 'order':
|
case 'order':
|
||||||
case 'parent':
|
case 'parent':
|
||||||
@@ -24,10 +24,10 @@ const updateCreatureProperty = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({_id, path, value}) {
|
run({ _id, path, value }) {
|
||||||
// Permission
|
// Permission
|
||||||
let property = CreatureProperties.findOne(_id, {
|
let property = CreatureProperties.findOne(_id, {
|
||||||
fields: {type: 1, ancestors: 1}
|
fields: { type: 1, ancestors: 1 }
|
||||||
});
|
});
|
||||||
let rootCreature = getRootCreatureAncestor(property);
|
let rootCreature = getRootCreatureAncestor(property);
|
||||||
assertEditPermission(rootCreature, this.userId);
|
assertEditPermission(rootCreature, this.userId);
|
||||||
@@ -35,13 +35,13 @@ const updateCreatureProperty = new ValidatedMethod({
|
|||||||
let pathString = path.join('.');
|
let pathString = path.join('.');
|
||||||
let modifier;
|
let modifier;
|
||||||
// unset empty values
|
// unset empty values
|
||||||
if (value === null || value === undefined){
|
if (value === null || value === undefined) {
|
||||||
modifier = { $unset: {[pathString]: 1}, $set: { dirty: true } };
|
modifier = { $unset: { [pathString]: 1 }, $set: { dirty: true } };
|
||||||
} else {
|
} else {
|
||||||
modifier = { $set: {[pathString]: value, dirty: true } };
|
modifier = { $set: { [pathString]: value, dirty: true } };
|
||||||
}
|
}
|
||||||
CreatureProperties.update(_id, modifier, {
|
CreatureProperties.update(_id, modifier, {
|
||||||
selector: {type: property.type},
|
selector: { type: property.type },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ let CreatureSchema = new SimpleSchema({
|
|||||||
'computeErrors.$.type': {
|
'computeErrors.$.type': {
|
||||||
type: String,
|
type: String,
|
||||||
},
|
},
|
||||||
'computeErrors.$.details' : {
|
'computeErrors.$.details': {
|
||||||
type: Object,
|
type: Object,
|
||||||
blackbox: true,
|
blackbox: true,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||||
import {assertEditPermission} from '/imports/api/sharing/sharingPermissions.js';
|
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
|
||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
import simpleSchemaMixin from '/imports/api/creature/mixins/simpleSchemaMixin.js';
|
import simpleSchemaMixin from '/imports/api/creature/mixins/simpleSchemaMixin.js';
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ const changeAllowedLibraries = new ValidatedMethod({
|
|||||||
numRequests: 10,
|
numRequests: 10,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({_id, allowedLibraries, allowedLibraryCollections}) {
|
run({ _id, allowedLibraries, allowedLibraryCollections }) {
|
||||||
let creature = Creatures.findOne(_id);
|
let creature = Creatures.findOne(_id);
|
||||||
assertEditPermission(creature, this.userId);
|
assertEditPermission(creature, this.userId);
|
||||||
let $set;
|
let $set;
|
||||||
@@ -48,7 +48,7 @@ const changeAllowedLibraries = new ValidatedMethod({
|
|||||||
$set.allowedLibraryCollections = allowedLibraryCollections;
|
$set.allowedLibraryCollections = allowedLibraryCollections;
|
||||||
}
|
}
|
||||||
if (!$set) return;
|
if (!$set) return;
|
||||||
Creatures.update(_id, {$set});
|
Creatures.update(_id, { $set });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ const toggleAllUserLibraries = new ValidatedMethod({
|
|||||||
numRequests: 10,
|
numRequests: 10,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({_id, value}) {
|
run({ _id, value }) {
|
||||||
if (value) {
|
if (value) {
|
||||||
Creatures.update(_id, {
|
Creatures.update(_id, {
|
||||||
$unset: {
|
$unset: {
|
||||||
@@ -87,4 +87,4 @@ const toggleAllUserLibraries = new ValidatedMethod({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export {changeAllowedLibraries, toggleAllUserLibraries};
|
export { changeAllowedLibraries, toggleAllUserLibraries };
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ const insertCreature = new ValidatedMethod({
|
|||||||
let baseId, rulesetSlot;
|
let baseId, rulesetSlot;
|
||||||
defaultCharacterProperties(creatureId).forEach(prop => {
|
defaultCharacterProperties(creatureId).forEach(prop => {
|
||||||
let id = CreatureProperties.insert(prop);
|
let id = CreatureProperties.insert(prop);
|
||||||
if (prop.name === 'Ruleset'){
|
if (prop.name === 'Ruleset') {
|
||||||
baseId = id;
|
baseId = id;
|
||||||
rulesetSlot = prop;
|
rulesetSlot = prop;
|
||||||
}
|
}
|
||||||
@@ -95,7 +95,7 @@ function insertDefaultRuleset(creatureId, baseId, userId, slot) {
|
|||||||
const ruleset = fillCursor.fetch()[0]
|
const ruleset = fillCursor.fetch()[0]
|
||||||
insertPropertyFromLibraryNode.call({
|
insertPropertyFromLibraryNode.call({
|
||||||
nodeIds: [ruleset._id],
|
nodeIds: [ruleset._id],
|
||||||
parentRef: {id: baseId, collection: 'creatureProperties'},
|
parentRef: { id: baseId, collection: 'creatureProperties' },
|
||||||
order: 0.5,
|
order: 0.5,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||||
import {assertEditPermission} from '/imports/api/sharing/sharingPermissions.js';
|
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
|
||||||
|
|
||||||
const updateCreature = new ValidatedMethod({
|
const updateCreature = new ValidatedMethod({
|
||||||
name: 'creatures.update',
|
name: 'creatures.update',
|
||||||
validate({_id, path}){
|
validate({ _id, path }) {
|
||||||
if (!_id) return false;
|
if (!_id) return false;
|
||||||
// Allowed fields
|
// Allowed fields
|
||||||
let allowedFields = [
|
let allowedFields = [
|
||||||
@@ -17,7 +17,7 @@ const updateCreature = new ValidatedMethod({
|
|||||||
'color',
|
'color',
|
||||||
'settings',
|
'settings',
|
||||||
];
|
];
|
||||||
if (!allowedFields.includes(path[0])){
|
if (!allowedFields.includes(path[0])) {
|
||||||
throw new Meteor.Error('Creatures.methods.update.denied',
|
throw new Meteor.Error('Creatures.methods.update.denied',
|
||||||
'This field can\'t be updated using this method');
|
'This field can\'t be updated using this method');
|
||||||
}
|
}
|
||||||
@@ -27,16 +27,16 @@ const updateCreature = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({_id, path, value}) {
|
run({ _id, path, value }) {
|
||||||
let creature = Creatures.findOne(_id);
|
let creature = Creatures.findOne(_id);
|
||||||
assertEditPermission(creature, this.userId);
|
assertEditPermission(creature, this.userId);
|
||||||
if (value === undefined || value === null){
|
if (value === undefined || value === null) {
|
||||||
Creatures.update(_id, {
|
Creatures.update(_id, {
|
||||||
$unset: {[path.join('.')]: 1},
|
$unset: { [path.join('.')]: 1 },
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Creatures.update(_id, {
|
Creatures.update(_id, {
|
||||||
$set: {[path.join('.')]: value},
|
$set: { [path.join('.')]: value },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ let ExperienceSchema = new SimpleSchema({
|
|||||||
// The real-world date that it occured, usually sorted by date
|
// The real-world date that it occured, usually sorted by date
|
||||||
date: {
|
date: {
|
||||||
type: Date,
|
type: Date,
|
||||||
autoValue: function() {
|
autoValue: function () {
|
||||||
// If the date isn't set, set it to now
|
// If the date isn't set, set it to now
|
||||||
if (!this.isSet) {
|
if (!this.isSet) {
|
||||||
return new Date();
|
return new Date();
|
||||||
@@ -46,8 +46,8 @@ let ExperienceSchema = new SimpleSchema({
|
|||||||
|
|
||||||
Experiences.attachSchema(ExperienceSchema);
|
Experiences.attachSchema(ExperienceSchema);
|
||||||
|
|
||||||
const insertExperienceForCreature = function({experience, creatureId, userId}){
|
const insertExperienceForCreature = function ({ experience, creatureId }) {
|
||||||
if (experience.xp){
|
if (experience.xp) {
|
||||||
Creatures.update(creatureId, {
|
Creatures.update(creatureId, {
|
||||||
$inc: { 'denormalizedStats.xp': experience.xp },
|
$inc: { 'denormalizedStats.xp': experience.xp },
|
||||||
$set: { dirty: true },
|
$set: { dirty: true },
|
||||||
@@ -84,7 +84,7 @@ const insertExperience = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({experience, creatureIds}) {
|
run({ experience, creatureIds }) {
|
||||||
let userId = this.userId;
|
let userId = this.userId;
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
throw new Meteor.Error('Experiences.methods.insert.denied',
|
throw new Meteor.Error('Experiences.methods.insert.denied',
|
||||||
@@ -93,7 +93,7 @@ const insertExperience = new ValidatedMethod({
|
|||||||
let insertedIds = [];
|
let insertedIds = [];
|
||||||
creatureIds.forEach(creatureId => {
|
creatureIds.forEach(creatureId => {
|
||||||
assertEditPermission(creatureId, userId);
|
assertEditPermission(creatureId, userId);
|
||||||
let id = insertExperienceForCreature({experience, creatureId, userId});
|
let id = insertExperienceForCreature({ experience, creatureId, userId });
|
||||||
insertedIds.push(id);
|
insertedIds.push(id);
|
||||||
});
|
});
|
||||||
return insertedIds;
|
return insertedIds;
|
||||||
@@ -113,7 +113,7 @@ const removeExperience = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({experienceId}) {
|
run({ experienceId }) {
|
||||||
let userId = this.userId;
|
let userId = this.userId;
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
throw new Meteor.Error('Experiences.methods.remove.denied',
|
throw new Meteor.Error('Experiences.methods.remove.denied',
|
||||||
@@ -123,7 +123,7 @@ const removeExperience = new ValidatedMethod({
|
|||||||
if (!experience) return;
|
if (!experience) return;
|
||||||
let creatureId = experience.creatureId
|
let creatureId = experience.creatureId
|
||||||
assertEditPermission(creatureId, userId);
|
assertEditPermission(creatureId, userId);
|
||||||
if (experience.xp){
|
if (experience.xp) {
|
||||||
Creatures.update(creatureId, {
|
Creatures.update(creatureId, {
|
||||||
$inc: { 'denormalizedStats.xp': -experience.xp },
|
$inc: { 'denormalizedStats.xp': -experience.xp },
|
||||||
$set: { dirty: true },
|
$set: { dirty: true },
|
||||||
@@ -154,7 +154,7 @@ const recomputeExperiences = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({creatureId}) {
|
run({ creatureId }) {
|
||||||
let userId = this.userId;
|
let userId = this.userId;
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
throw new Meteor.Error('Experiences.methods.recompute.denied',
|
throw new Meteor.Error('Experiences.methods.recompute.denied',
|
||||||
@@ -167,16 +167,18 @@ const recomputeExperiences = new ValidatedMethod({
|
|||||||
Experiences.find({
|
Experiences.find({
|
||||||
creatureId
|
creatureId
|
||||||
}, {
|
}, {
|
||||||
fields: {xp: 1, levels: 1}
|
fields: { xp: 1, levels: 1 }
|
||||||
}).forEach(experience => {
|
}).forEach(experience => {
|
||||||
xp += experience.xp || 0;
|
xp += experience.xp || 0;
|
||||||
milestoneLevels += experience.levels || 0;
|
milestoneLevels += experience.levels || 0;
|
||||||
});
|
});
|
||||||
Creatures.update(creatureId, {$set: {
|
Creatures.update(creatureId, {
|
||||||
|
$set: {
|
||||||
'denormalizedStats.xp': xp,
|
'denormalizedStats.xp': xp,
|
||||||
'denormalizedStats.milestoneLevels': milestoneLevels,
|
'denormalizedStats.milestoneLevels': milestoneLevels,
|
||||||
dirty: true,
|
dirty: true,
|
||||||
}});
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ let ExperienceSchema = new SimpleSchema({
|
|||||||
// The real-world date that it occured
|
// The real-world date that it occured
|
||||||
date: {
|
date: {
|
||||||
type: Date,
|
type: Date,
|
||||||
autoValue: function() {
|
autoValue: function () {
|
||||||
// If the date isn't set, set it to now
|
// If the date isn't set, set it to now
|
||||||
if (!this.isSet) {
|
if (!this.isSet) {
|
||||||
return new Date();
|
return new Date();
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ import CreatureVariables from '/imports/api/creature/creatures/CreatureVariables
|
|||||||
import LogContentSchema from '/imports/api/creature/log/LogContentSchema.js';
|
import LogContentSchema from '/imports/api/creature/log/LogContentSchema.js';
|
||||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||||
import {assertEditPermission} from '/imports/api/creature/creatures/creaturePermissions.js';
|
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
|
||||||
import {parse, prettifyParseError} from '/imports/parser/parser.js';
|
import { parse, prettifyParseError } from '/imports/parser/parser.js';
|
||||||
import resolve, { toString } from '/imports/parser/resolve.js';
|
import resolve, { toString } from '/imports/parser/resolve.js';
|
||||||
const PER_CREATURE_LOG_LIMIT = 100;
|
const PER_CREATURE_LOG_LIMIT = 100;
|
||||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
if (Meteor.isServer){
|
if (Meteor.isServer) {
|
||||||
var sendWebhookAsCreature = require('/imports/server/discord/sendWebhook.js').sendWebhookAsCreature;
|
var sendWebhookAsCreature = require('/imports/server/discord/sendWebhook.js').sendWebhookAsCreature;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ let CreatureLogSchema = new SimpleSchema({
|
|||||||
// The real-world date that it occured, usually sorted by date
|
// The real-world date that it occured, usually sorted by date
|
||||||
date: {
|
date: {
|
||||||
type: Date,
|
type: Date,
|
||||||
autoValue: function() {
|
autoValue: function () {
|
||||||
// If the date isn't set, set it to now
|
// If the date isn't set, set it to now
|
||||||
if (!this.isSet) {
|
if (!this.isSet) {
|
||||||
return new Date();
|
return new Date();
|
||||||
@@ -50,23 +50,23 @@ let CreatureLogSchema = new SimpleSchema({
|
|||||||
|
|
||||||
CreatureLogs.attachSchema(CreatureLogSchema);
|
CreatureLogs.attachSchema(CreatureLogSchema);
|
||||||
|
|
||||||
function removeOldLogs(creatureId){
|
function removeOldLogs(creatureId) {
|
||||||
// Find the first log that is over the limit
|
// Find the first log that is over the limit
|
||||||
let firstExpiredLog = CreatureLogs.find({
|
let firstExpiredLog = CreatureLogs.find({
|
||||||
creatureId
|
creatureId
|
||||||
}, {
|
}, {
|
||||||
sort: {date: -1},
|
sort: { date: -1 },
|
||||||
skip: PER_CREATURE_LOG_LIMIT,
|
skip: PER_CREATURE_LOG_LIMIT,
|
||||||
});
|
});
|
||||||
if (!firstExpiredLog) return;
|
if (!firstExpiredLog) return;
|
||||||
// Remove all logs older than the one over the limit
|
// Remove all logs older than the one over the limit
|
||||||
CreatureLogs.remove({
|
CreatureLogs.remove({
|
||||||
creatureId,
|
creatureId,
|
||||||
date: {$lte: firstExpiredLog.date},
|
date: { $lte: firstExpiredLog.date },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function logToMessageData(log){
|
function logToMessageData(log) {
|
||||||
let embed = {
|
let embed = {
|
||||||
fields: [],
|
fields: [],
|
||||||
};
|
};
|
||||||
@@ -78,8 +78,8 @@ function logToMessageData(log){
|
|||||||
return { embeds: [embed] };
|
return { embeds: [embed] };
|
||||||
}
|
}
|
||||||
|
|
||||||
function logWebhook({log, creature}){
|
function logWebhook({ log, creature }) {
|
||||||
if (Meteor.isServer){
|
if (Meteor.isServer) {
|
||||||
sendWebhookAsCreature({
|
sendWebhookAsCreature({
|
||||||
creature,
|
creature,
|
||||||
data: logToMessageData(log),
|
data: logToMessageData(log),
|
||||||
@@ -97,44 +97,46 @@ const insertCreatureLog = new ValidatedMethod({
|
|||||||
validate: new SimpleSchema({
|
validate: new SimpleSchema({
|
||||||
log: CreatureLogSchema.omit('date'),
|
log: CreatureLogSchema.omit('date'),
|
||||||
}).validator(),
|
}).validator(),
|
||||||
run({log}){
|
run({ log }) {
|
||||||
const creatureId = log.creatureId;
|
const creatureId = log.creatureId;
|
||||||
const creature = Creatures.findOne(creatureId, {fields: {
|
const creature = Creatures.findOne(creatureId, {
|
||||||
|
fields: {
|
||||||
readers: 1,
|
readers: 1,
|
||||||
writers: 1,
|
writers: 1,
|
||||||
owner: 1,
|
owner: 1,
|
||||||
'settings.discordWebhook': 1,
|
'settings.discordWebhook': 1,
|
||||||
name: 1,
|
name: 1,
|
||||||
avatarPicture: 1,
|
avatarPicture: 1,
|
||||||
}});
|
}
|
||||||
|
});
|
||||||
assertEditPermission(creature, this.userId);
|
assertEditPermission(creature, this.userId);
|
||||||
// Build the new log
|
// Build the new log
|
||||||
let id = insertCreatureLogWork({log, creature, method: this})
|
let id = insertCreatureLogWork({ log, creature, method: this })
|
||||||
return id;
|
return id;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export function insertCreatureLogWork({log, creature, method}){
|
export function insertCreatureLogWork({ log, creature, method }) {
|
||||||
// Build the new log
|
// Build the new log
|
||||||
if (typeof log === 'string'){
|
if (typeof log === 'string') {
|
||||||
log = {content: [{value: log}]};
|
log = { content: [{ value: log }] };
|
||||||
}
|
}
|
||||||
if (!log.content?.length) return;
|
if (!log.content?.length) return;
|
||||||
log.date = new Date();
|
log.date = new Date();
|
||||||
// Insert it
|
// Insert it
|
||||||
let id = CreatureLogs.insert(log);
|
let id = CreatureLogs.insert(log);
|
||||||
if (Meteor.isServer){
|
if (Meteor.isServer) {
|
||||||
method?.unblock();
|
method?.unblock();
|
||||||
removeOldLogs(creature._id);
|
removeOldLogs(creature._id);
|
||||||
logWebhook({log, creature});
|
logWebhook({ log, creature });
|
||||||
}
|
}
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function equalIgnoringWhitespace(a, b){
|
function equalIgnoringWhitespace(a, b) {
|
||||||
if (typeof a !== 'string' || typeof b !== 'string') return a === b;
|
if (typeof a !== 'string' || typeof b !== 'string') return a === b;
|
||||||
return a.replace(/\s/g,'') === b.replace(/\s/g, '');
|
return a.replace(/\s/g, '') === b.replace(/\s/g, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
const logRoll = new ValidatedMethod({
|
const logRoll = new ValidatedMethod({
|
||||||
@@ -153,24 +155,26 @@ const logRoll = new ValidatedMethod({
|
|||||||
regEx: SimpleSchema.RegEx.Id,
|
regEx: SimpleSchema.RegEx.Id,
|
||||||
},
|
},
|
||||||
}).validator(),
|
}).validator(),
|
||||||
run({roll, creatureId}){
|
run({ roll, creatureId }) {
|
||||||
const creature = Creatures.findOne(creatureId, {fields: {
|
const creature = Creatures.findOne(creatureId, {
|
||||||
|
fields: {
|
||||||
readers: 1,
|
readers: 1,
|
||||||
writers: 1,
|
writers: 1,
|
||||||
owner: 1,
|
owner: 1,
|
||||||
'settings.discordWebhook': 1,
|
'settings.discordWebhook': 1,
|
||||||
name: 1,
|
name: 1,
|
||||||
avatarPicture: 1,
|
avatarPicture: 1,
|
||||||
}});
|
}
|
||||||
|
});
|
||||||
assertEditPermission(creature, this.userId);
|
assertEditPermission(creature, this.userId);
|
||||||
const variables = CreatureVariables.findOne({ _creatureId: creatureId });
|
const variables = CreatureVariables.findOne({ _creatureId: creatureId });
|
||||||
let logContent = []
|
let logContent = []
|
||||||
let parsedResult = undefined;
|
let parsedResult = undefined;
|
||||||
try {
|
try {
|
||||||
parsedResult = parse(roll);
|
parsedResult = parse(roll);
|
||||||
} catch (e){
|
} catch (e) {
|
||||||
let error = prettifyParseError(e);
|
let error = prettifyParseError(e);
|
||||||
logContent.push({name: 'Parse Error', value: error});
|
logContent.push({ name: 'Parse Error', value: error });
|
||||||
}
|
}
|
||||||
if (parsedResult) try {
|
if (parsedResult) try {
|
||||||
let {
|
let {
|
||||||
@@ -184,19 +188,19 @@ const logRoll = new ValidatedMethod({
|
|||||||
logContent.push({
|
logContent.push({
|
||||||
value: compiledString
|
value: compiledString
|
||||||
});
|
});
|
||||||
let {result: rolled} = resolve('roll', compiled, variables, context);
|
let { result: rolled } = resolve('roll', compiled, variables, context);
|
||||||
let rolledString = toString(rolled);
|
let rolledString = toString(rolled);
|
||||||
if (rolledString !== compiledString) logContent.push({
|
if (rolledString !== compiledString) logContent.push({
|
||||||
value: rolledString
|
value: rolledString
|
||||||
});
|
});
|
||||||
let {result} = resolve('reduce', rolled, variables, context);
|
let { result } = resolve('reduce', rolled, variables, context);
|
||||||
let resultString = toString(result);
|
let resultString = toString(result);
|
||||||
if (resultString !== rolledString) logContent.push({
|
if (resultString !== rolledString) logContent.push({
|
||||||
value: resultString
|
value: resultString
|
||||||
});
|
});
|
||||||
} catch (e){
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
logContent = [{name: 'Calculation error'}];
|
logContent = [{ name: 'Calculation error' }];
|
||||||
}
|
}
|
||||||
const log = {
|
const log = {
|
||||||
content: logContent,
|
content: logContent,
|
||||||
@@ -204,11 +208,11 @@ const logRoll = new ValidatedMethod({
|
|||||||
date: new Date(),
|
date: new Date(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let id = insertCreatureLogWork({log, creature, method: this});
|
let id = insertCreatureLogWork({ log, creature, method: this });
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default CreatureLogs;
|
export default CreatureLogs;
|
||||||
export { CreatureLogSchema, insertCreatureLog, logRoll, PER_CREATURE_LOG_LIMIT};
|
export { CreatureLogSchema, insertCreatureLog, logRoll, PER_CREATURE_LOG_LIMIT };
|
||||||
|
|||||||
3
app/imports/api/docs/Docs.js
Normal file
3
app/imports/api/docs/Docs.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
if (Meteor.isServer) throw 'Client side only collection, don\'t import on server';
|
||||||
|
const Docs = new Mongo.Collection('docs');
|
||||||
|
export default Docs;
|
||||||
@@ -4,6 +4,7 @@ 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 buffRemover from './applyPropertyByType/applyBuffRemover.js';
|
||||||
import damage from './applyPropertyByType/applyDamage.js';
|
import damage from './applyPropertyByType/applyDamage.js';
|
||||||
|
import folder from './applyPropertyByType/applyFolder.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';
|
||||||
import savingThrow from './applyPropertyByType/applySavingThrow.js';
|
import savingThrow from './applyPropertyByType/applySavingThrow.js';
|
||||||
@@ -16,6 +17,7 @@ const applyPropertyByType = {
|
|||||||
buff,
|
buff,
|
||||||
buffRemover,
|
buffRemover,
|
||||||
damage,
|
damage,
|
||||||
|
folder,
|
||||||
note,
|
note,
|
||||||
roll,
|
roll,
|
||||||
savingThrow,
|
savingThrow,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import {
|
|||||||
setLineageOfDocs,
|
setLineageOfDocs,
|
||||||
renewDocIds
|
renewDocIds
|
||||||
} from '/imports/api/parenting/parenting.js';
|
} from '/imports/api/parenting/parenting.js';
|
||||||
import {setDocToLastOrder} from '/imports/api/parenting/order.js';
|
import { setDocToLastOrder } from '/imports/api/parenting/order.js';
|
||||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||||
import computedSchemas from '/imports/api/properties/computedPropertySchemasIndex.js';
|
import computedSchemas from '/imports/api/properties/computedPropertySchemasIndex.js';
|
||||||
import applyFnToKey from '/imports/api/engine/computation/utility/applyFnToKey.js';
|
import applyFnToKey from '/imports/api/engine/computation/utility/applyFnToKey.js';
|
||||||
@@ -15,14 +15,14 @@ import cyrb53 from '/imports/api/engine/computation/utility/cyrb53.js';
|
|||||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||||
import INLINE_CALCULATION_REGEX from '/imports/constants/INLINE_CALCULTION_REGEX.js';
|
import INLINE_CALCULATION_REGEX from '/imports/constants/INLINE_CALCULTION_REGEX.js';
|
||||||
|
|
||||||
export default function applyBuff(node, actionContext){
|
export default function applyBuff(node, actionContext) {
|
||||||
applyNodeTriggers(node, 'before', actionContext);
|
applyNodeTriggers(node, 'before', actionContext);
|
||||||
const prop = node.node;
|
const prop = node.node;
|
||||||
let buffTargets = prop.target === 'self' ? [actionContext.creature] : actionContext.targets;
|
let buffTargets = prop.target === 'self' ? [actionContext.creature] : actionContext.targets;
|
||||||
|
|
||||||
// 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, { skipCrystalize } = {}){
|
function addChildrenToPropList(children, { skipCrystalize } = {}) {
|
||||||
children.forEach(child => {
|
children.forEach(child => {
|
||||||
if (skipCrystalize) child.node._skipCrystalize = true;
|
if (skipCrystalize) child.node._skipCrystalize = true;
|
||||||
propList.push(child.node);
|
propList.push(child.node);
|
||||||
@@ -34,7 +34,7 @@ export default function applyBuff(node, actionContext){
|
|||||||
}
|
}
|
||||||
addChildrenToPropList(node.children);
|
addChildrenToPropList(node.children);
|
||||||
if (!prop.skipCrystalization) {
|
if (!prop.skipCrystalization) {
|
||||||
crystalizeVariables({propList, actionContext});
|
crystalizeVariables({ propList, actionContext });
|
||||||
}
|
}
|
||||||
|
|
||||||
let oldParent = {
|
let oldParent = {
|
||||||
@@ -46,8 +46,8 @@ export default function applyBuff(node, actionContext){
|
|||||||
copyNodeListToTarget(propList, target, oldParent);
|
copyNodeListToTarget(propList, target, oldParent);
|
||||||
|
|
||||||
//Log the buff
|
//Log the buff
|
||||||
if ((prop.name || prop.description?.value) && !prop.silent){
|
if ((prop.name || prop.description?.value) && !prop.silent) {
|
||||||
if (target._id === actionContext.creature._id){
|
if (target._id === actionContext.creature._id) {
|
||||||
// Targeting self
|
// Targeting self
|
||||||
actionContext.addLog({
|
actionContext.addLog({
|
||||||
name: prop.name,
|
name: prop.name,
|
||||||
@@ -72,8 +72,8 @@ export default function applyBuff(node, actionContext){
|
|||||||
// Don't apply the children of the buff, they get copied to the target instead
|
// Don't apply the children of the buff, they get copied to the target instead
|
||||||
}
|
}
|
||||||
|
|
||||||
function copyNodeListToTarget(propList, target, oldParent){
|
function copyNodeListToTarget(propList, target, oldParent) {
|
||||||
let ancestry = [{collection: 'creatures', id: target._id}];
|
let ancestry = [{ collection: 'creatures', id: target._id }];
|
||||||
setLineageOfDocs({
|
setLineageOfDocs({
|
||||||
docArray: propList,
|
docArray: propList,
|
||||||
newAncestry: ancestry,
|
newAncestry: ancestry,
|
||||||
@@ -93,14 +93,14 @@ function copyNodeListToTarget(propList, target, oldParent){
|
|||||||
* Replaces all variables with their resolved values
|
* Replaces all variables with their resolved values
|
||||||
* except variables of the form `$target.thing.total` become `thing.total`
|
* except variables of the form `$target.thing.total` become `thing.total`
|
||||||
*/
|
*/
|
||||||
function crystalizeVariables({propList, actionContext}){
|
function crystalizeVariables({ propList, actionContext }) {
|
||||||
propList.forEach(prop => {
|
propList.forEach(prop => {
|
||||||
if (prop._skipCrystalize) {
|
if (prop._skipCrystalize) {
|
||||||
delete prop._skipCrystalize;
|
delete prop._skipCrystalize;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Iterate through all the calculations and crystalize them
|
// Iterate through all the calculations and crystalize them
|
||||||
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);
|
||||||
if (!calcObj?.parseNode) return;
|
if (!calcObj?.parseNode) return;
|
||||||
@@ -110,12 +110,12 @@ function crystalizeVariables({propList, actionContext}){
|
|||||||
node.parseType !== 'accessor' && node.parseType !== 'symbol'
|
node.parseType !== 'accessor' && node.parseType !== 'symbol'
|
||||||
) return node;
|
) return node;
|
||||||
// Handle variables
|
// Handle variables
|
||||||
if (node.name === '$target'){
|
if (node.name === '$target') {
|
||||||
// strip $target
|
// strip $target
|
||||||
if (node.parseType === 'accessor'){
|
if (node.parseType === 'accessor') {
|
||||||
node.name = node.path.shift();
|
node.name = node.path.shift();
|
||||||
if (!node.path.length){
|
if (!node.path.length) {
|
||||||
return symbol.create({name: node.name})
|
return symbol.create({ name: node.name })
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Can't strip symbols
|
// Can't strip symbols
|
||||||
@@ -127,7 +127,7 @@ function crystalizeVariables({propList, actionContext}){
|
|||||||
return node;
|
return node;
|
||||||
} else {
|
} else {
|
||||||
// Resolve all other variables
|
// Resolve all other variables
|
||||||
const {result, context} = resolve('reduce', node, actionContext.scope);
|
const { result, context } = resolve('reduce', node, actionContext.scope);
|
||||||
logErrors(context.errors, actionContext);
|
logErrors(context.errors, actionContext);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -137,14 +137,14 @@ function crystalizeVariables({propList, actionContext}){
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
// For each key in the schema
|
// For each key in the schema
|
||||||
computedSchemas[prop.type].inlineCalculationFields().forEach( calcKey => {
|
computedSchemas[prop.type].inlineCalculationFields().forEach(calcKey => {
|
||||||
// That ends in .inlineCalculations
|
// That ends in .inlineCalculations
|
||||||
applyFnToKey(prop, calcKey, (prop, key) => {
|
applyFnToKey(prop, calcKey, (prop, key) => {
|
||||||
const inlineCalcObj = get(prop, key);
|
const inlineCalcObj = get(prop, key);
|
||||||
if (!inlineCalcObj) return;
|
if (!inlineCalcObj) return;
|
||||||
|
|
||||||
// If there is no text, skip
|
// If there is no text, skip
|
||||||
if (!inlineCalcObj.text){
|
if (!inlineCalcObj.text) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { some, intersection, difference, remove } from 'lodash';
|
import { some, intersection, difference, remove, includes } from 'lodash';
|
||||||
import applyProperty from '../applyProperty.js';
|
import applyProperty from '../applyProperty.js';
|
||||||
import {insertCreatureLog} from '/imports/api/creature/log/CreatureLogs.js';
|
import {insertCreatureLog} from '/imports/api/creature/log/CreatureLogs.js';
|
||||||
import resolve, { Context, toString } from '/imports/parser/resolve.js';
|
import resolve, { Context, toString } from '/imports/parser/resolve.js';
|
||||||
@@ -147,21 +147,21 @@ function applyDamageMultipliers({target, damage, damageProp, logValue}){
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
multiplier.immunity &&
|
multiplier.immunity &&
|
||||||
some(multiplier.immunities, multiplierAppliesTo(damageProp))
|
some(multiplier.immunities, multiplierAppliesTo(damageProp, 'immunity'))
|
||||||
){
|
){
|
||||||
logValue.push(`Immune to ${damageTypeText}`);
|
logValue.push(`Immune to ${damageTypeText}`);
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
if (
|
if (
|
||||||
multiplier.resistance &&
|
multiplier.resistance &&
|
||||||
some(multiplier.resistances, multiplierAppliesTo(damageProp))
|
some(multiplier.resistances, multiplierAppliesTo(damageProp, 'resistance'))
|
||||||
){
|
){
|
||||||
logValue.push(`Resistant to ${damageTypeText}`);
|
logValue.push(`Resistant to ${damageTypeText}`);
|
||||||
damage = Math.floor(damage / 2);
|
damage = Math.floor(damage / 2);
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
multiplier.vulnerability &&
|
multiplier.vulnerability &&
|
||||||
some(multiplier.vulnerabilities, multiplierAppliesTo(damageProp))
|
some(multiplier.vulnerabilities, multiplierAppliesTo(damageProp, 'vulnerability'))
|
||||||
){
|
){
|
||||||
logValue.push(`Vulnerable to ${damageTypeText}`);
|
logValue.push(`Vulnerable to ${damageTypeText}`);
|
||||||
damage = Math.floor(damage * 2);
|
damage = Math.floor(damage * 2);
|
||||||
@@ -170,8 +170,11 @@ function applyDamageMultipliers({target, damage, damageProp, logValue}){
|
|||||||
return damage;
|
return damage;
|
||||||
}
|
}
|
||||||
|
|
||||||
function multiplierAppliesTo(damageProp){
|
function multiplierAppliesTo(damageProp, multiplierType){
|
||||||
return multiplier => {
|
return multiplier => {
|
||||||
|
// Apply the default 'ignore x' tags
|
||||||
|
if (includes(damageProp.tags, `ignore ${multiplierType}`)) return false;
|
||||||
|
|
||||||
const hasRequiredTags = difference(
|
const hasRequiredTags = difference(
|
||||||
multiplier.includeTags, damageProp.tags
|
multiplier.includeTags, damageProp.tags
|
||||||
).length === 0;
|
).length === 0;
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import recalculateInlineCalculations from './shared/recalculateInlineCalculations.js';
|
||||||
|
import applyProperty from '../applyProperty.js';
|
||||||
|
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||||
|
|
||||||
|
export default function applyFolder(node, actionContext) {
|
||||||
|
// Apply triggers
|
||||||
|
applyNodeTriggers(node, 'before', actionContext);
|
||||||
|
applyNodeTriggers(node, 'after', actionContext);
|
||||||
|
// Apply children
|
||||||
|
node.children.forEach(child => applyProperty(child, actionContext));
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ import logErrors from './logErrors.js';
|
|||||||
export default function recalculateCalculation(calc, actionContext, context){
|
export default function recalculateCalculation(calc, actionContext, context){
|
||||||
if (!calc?.parseNode) return;
|
if (!calc?.parseNode) return;
|
||||||
calc._parseLevel = 'reduce';
|
calc._parseLevel = 'reduce';
|
||||||
applyEffectsToCalculationParseNode(calc, actionContext.log);
|
applyEffectsToCalculationParseNode(calc, actionContext);
|
||||||
evaluateCalculation(calc, actionContext.scope, context);
|
evaluateCalculation(calc, actionContext.scope, context);
|
||||||
logErrors(calc.errors, actionContext.log);
|
logErrors(calc.errors, actionContext);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,13 +56,13 @@ const doAction = new ValidatedMethod({
|
|||||||
properties.sort((a, b) => a.order - b.order);
|
properties.sort((a, b) => a.order - b.order);
|
||||||
|
|
||||||
// Do the action
|
// Do the action
|
||||||
doActionWork({properties, ancestors, actionContext, methodScope: scope});
|
doActionWork({ properties, ancestors, actionContext, methodScope: scope });
|
||||||
|
|
||||||
// Recompute all involved creatures
|
// Recompute all involved creatures
|
||||||
Creatures.update({
|
Creatures.update({
|
||||||
_id: { $in: [creatureId, ...targetIds] }
|
_id: { $in: [creatureId, ...targetIds] }
|
||||||
}, {
|
}, {
|
||||||
$set: {dirty: true},
|
$set: { dirty: true },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -71,11 +71,11 @@ export default doAction;
|
|||||||
|
|
||||||
export function doActionWork({
|
export function doActionWork({
|
||||||
properties, ancestors, actionContext, methodScope = {},
|
properties, ancestors, actionContext, methodScope = {},
|
||||||
}){
|
}) {
|
||||||
// get the docs
|
// get the docs
|
||||||
const ancestorScope = getAncestorScope(ancestors);
|
const ancestorScope = getAncestorScope(ancestors);
|
||||||
const propertyForest = nodeArrayToTree(properties);
|
const propertyForest = nodeArrayToTree(properties);
|
||||||
if (propertyForest.length !== 1){
|
if (propertyForest.length !== 1) {
|
||||||
throw new Meteor.Error(`The action has ${propertyForest.length} top level properties, expected 1`);
|
throw new Meteor.Error(`The action has ${propertyForest.length} top level properties, expected 1`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ export function doActionWork({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Assumes ancestors are in tree order already
|
// Assumes ancestors are in tree order already
|
||||||
function getAncestorScope(ancestors){
|
function getAncestorScope(ancestors) {
|
||||||
let scope = {};
|
let scope = {};
|
||||||
ancestors.forEach(prop => {
|
ancestors.forEach(prop => {
|
||||||
scope[`#${prop.type}`] = prop;
|
scope[`#${prop.type}`] = prop;
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ const doAction = new ValidatedMethod({
|
|||||||
regEx: SimpleSchema.RegEx.Id,
|
regEx: SimpleSchema.RegEx.Id,
|
||||||
optional: true,
|
optional: true,
|
||||||
},
|
},
|
||||||
|
ritual: {
|
||||||
|
type: Boolean,
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
targetIds: {
|
targetIds: {
|
||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
@@ -41,7 +45,7 @@ const doAction = new ValidatedMethod({
|
|||||||
numRequests: 10,
|
numRequests: 10,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({ spellId, slotId, targetIds = [], scope = {} }) {
|
run({ spellId, slotId, ritual, targetIds = [], scope = {} }) {
|
||||||
// Get action context
|
// Get action context
|
||||||
let spell = CreatureProperties.findOne(spellId);
|
let spell = CreatureProperties.findOne(spellId);
|
||||||
const creatureId = spell.ancestors[0].id;
|
const creatureId = spell.ancestors[0].id;
|
||||||
@@ -64,25 +68,26 @@ const doAction = new ValidatedMethod({
|
|||||||
let slotLevel = spell.level || 0;
|
let slotLevel = spell.level || 0;
|
||||||
let slot;
|
let slot;
|
||||||
|
|
||||||
if (slotId && !spell.castWithoutSpellSlots){
|
// If a spell requires a slot, make sure a slot is spent
|
||||||
|
if (!spell.castWithoutSpellSlots && !(ritual && spell.ritual)) {
|
||||||
slot = CreatureProperties.findOne(slotId);
|
slot = CreatureProperties.findOne(slotId);
|
||||||
if (!slot){
|
if (!slot) {
|
||||||
throw new Meteor.Error('No slot',
|
throw new Meteor.Error('No slot',
|
||||||
'Slot not found to cast spell');
|
'Slot not found to cast spell');
|
||||||
}
|
}
|
||||||
if (!slot.value){
|
if (!slot.value) {
|
||||||
throw new Meteor.Error('No slot',
|
throw new Meteor.Error('No slot',
|
||||||
'Slot depleted');
|
'Slot depleted');
|
||||||
}
|
}
|
||||||
if (slot.attributeType !== 'spellSlot'){
|
if (slot.attributeType !== 'spellSlot') {
|
||||||
throw new Meteor.Error('Not a slot',
|
throw new Meteor.Error('Not a slot',
|
||||||
'The given property is not a valid spell slot');
|
'The given property is not a valid spell slot');
|
||||||
}
|
}
|
||||||
if (!slot.spellSlotLevel?.value){
|
if (!slot.spellSlotLevel?.value) {
|
||||||
throw new Meteor.Error('No slot level',
|
throw new Meteor.Error('No slot level',
|
||||||
'Slot does not have a spell slot level');
|
'Slot does not have a spell slot level');
|
||||||
}
|
}
|
||||||
if (slot.spellSlotLevel.value < spell.level){
|
if (slot.spellSlotLevel.value < spell.level) {
|
||||||
throw new Meteor.Error('Slot too small',
|
throw new Meteor.Error('Slot too small',
|
||||||
'Slot is not large enough to cast spell');
|
'Slot is not large enough to cast spell');
|
||||||
}
|
}
|
||||||
@@ -96,15 +101,21 @@ const doAction = new ValidatedMethod({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Post the slot level spent to the log
|
// Post the slot level spent to the log
|
||||||
if (slot?.spellSlotLevel?.value){
|
if (slot?.spellSlotLevel?.value) {
|
||||||
actionContext.addLog({
|
actionContext.addLog({
|
||||||
name: `Casting using a level ${slotLevel} spell slot`
|
name: `Casting using a level ${slotLevel} spell slot`
|
||||||
});
|
});
|
||||||
} else if (slotLevel) {
|
} else if (slotLevel) {
|
||||||
|
if (ritual) {
|
||||||
|
actionContext.addLog({
|
||||||
|
name: `Ritual casting at level ${slotLevel}`
|
||||||
|
});
|
||||||
|
} else {
|
||||||
actionContext.addLog({
|
actionContext.addLog({
|
||||||
name: `Casting at level ${slotLevel}`
|
name: `Casting at level ${slotLevel}`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
actionContext.scope['slotLevel'] = slotLevel;
|
actionContext.scope['slotLevel'] = slotLevel;
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import rollDice from '/imports/parser/rollDice.js';
|
|||||||
import numberToSignedString from '/imports/ui/utility/numberToSignedString.js';
|
import numberToSignedString from '/imports/ui/utility/numberToSignedString.js';
|
||||||
import { applyTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
import { applyTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||||
import ActionContext from '/imports/api/engine/actions/ActionContext.js';
|
import ActionContext from '/imports/api/engine/actions/ActionContext.js';
|
||||||
|
import evaluateCalculation from '/imports/api/engine/computation/utility/evaluateCalculation.js';
|
||||||
|
|
||||||
const doCheck = new ValidatedMethod({
|
const doCheck = new ValidatedMethod({
|
||||||
name: 'creatureProperties.doCheck',
|
name: 'creatureProperties.doCheck',
|
||||||
@@ -72,7 +73,11 @@ function rollCheck(prop, actionContext) {
|
|||||||
throw (`${prop.type} not supported for checks`);
|
throw (`${prop.type} not supported for checks`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const rollModifierText = numberToSignedString(rollModifier, true);
|
let rollModifierText = numberToSignedString(rollModifier, true);
|
||||||
|
|
||||||
|
const { effectBonus, effectString } = applyUnresolvedEffects(prop, scope)
|
||||||
|
rollModifierText += effectString;
|
||||||
|
rollModifier += effectBonus;
|
||||||
|
|
||||||
let value, values, resultPrefix;
|
let value, values, resultPrefix;
|
||||||
if (scope['$checkAdvantage'] === 1){
|
if (scope['$checkAdvantage'] === 1){
|
||||||
@@ -106,3 +111,21 @@ function rollCheck(prop, actionContext) {
|
|||||||
value: `${resultPrefix} **${result}**`,
|
value: `${resultPrefix} **${result}**`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function applyUnresolvedEffects(prop, scope) {
|
||||||
|
let effectBonus = 0;
|
||||||
|
let effectString = '';
|
||||||
|
if (!prop.effects) {
|
||||||
|
return { effectBonus, effectString};
|
||||||
|
}
|
||||||
|
prop.effects.forEach(effect => {
|
||||||
|
if (!effect.amount?.parseNode) return;
|
||||||
|
if (effect.operation !== 'add') return;
|
||||||
|
effect.amount._parseLevel = 'reduce';
|
||||||
|
evaluateCalculation(effect.amount, scope);
|
||||||
|
if (typeof effect.amount?.value !== 'number') return;
|
||||||
|
effectBonus += effect.amount.value;
|
||||||
|
effectString += ` ${effect.amount.value < 0 ? '-' : '+'} [${effect.amount.calculation}] ${Math.abs(effect.amount.value)}`
|
||||||
|
});
|
||||||
|
return { effectBonus, effectString};
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,24 @@
|
|||||||
import { EJSON } from 'meteor/ejson';
|
import { EJSON } from 'meteor/ejson';
|
||||||
import createGraph from 'ngraph.graph';
|
import createGraph, { Graph } from 'ngraph.graph';
|
||||||
import getEffectivePropTags from '/imports/api/engine/computation/utility/getEffectivePropTags.js';
|
import getEffectivePropTags from '/imports/api/engine/computation/utility/getEffectivePropTags.js';
|
||||||
|
|
||||||
|
interface CreatureProperty {
|
||||||
|
_id: string;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
export default class CreatureComputation {
|
export default class CreatureComputation {
|
||||||
constructor(properties, creature, variables){
|
originalPropsById: object;
|
||||||
|
propsById: object;
|
||||||
|
propsWithTag: object;
|
||||||
|
scope: object;
|
||||||
|
props: Array<CreatureProperty>;
|
||||||
|
dependencyGraph: Graph;
|
||||||
|
errors: Array<object>;
|
||||||
|
creature: object;
|
||||||
|
variables: object;
|
||||||
|
|
||||||
|
constructor(properties: Array<CreatureProperty>, creature: object, variables: object) {
|
||||||
// Set up fields
|
// Set up fields
|
||||||
this.originalPropsById = {};
|
this.originalPropsById = {};
|
||||||
this.propsById = {};
|
this.propsById = {};
|
||||||
@@ -29,8 +29,8 @@ function childrenActive(prop){
|
|||||||
// Children of disabled properties are always inactive
|
// Children of disabled properties are always inactive
|
||||||
if (prop.disabled) return false;
|
if (prop.disabled) return false;
|
||||||
switch (prop.type){
|
switch (prop.type){
|
||||||
// Only equipped items have active children
|
// Only equipped items with non-zero quantity have active children
|
||||||
case 'item': return !!prop.equipped;
|
case 'item': return !!prop.equipped && prop.quantity !== 0;
|
||||||
// The children of actions, spells, and triggers are always inactive
|
// The children of actions, spells, and triggers are always inactive
|
||||||
case 'action': return false;
|
case 'action': return false;
|
||||||
case 'spell': return false;
|
case 'spell': return false;
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ export default function computeToggleDependencies(node, dependencyGraph){
|
|||||||
prop.enabled
|
prop.enabled
|
||||||
) return;
|
) return;
|
||||||
walkDown(node.children, child => {
|
walkDown(node.children, child => {
|
||||||
// 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);
|
child.node._computationDetails.toggleAncestors.push(prop);
|
||||||
dependencyGraph.addLink(child.node._id, prop._id, 'toggle');
|
dependencyGraph.addLink(child.node._id, prop._id, 'toggle');
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import computeToggleDependencies from './buildComputation/computeToggleDependenc
|
|||||||
import linkCalculationDependencies from './buildComputation/linkCalculationDependencies.js';
|
import linkCalculationDependencies from './buildComputation/linkCalculationDependencies.js';
|
||||||
import linkTypeDependencies from './buildComputation/linkTypeDependencies.js';
|
import linkTypeDependencies from './buildComputation/linkTypeDependencies.js';
|
||||||
import computeSlotQuantityFilled from './buildComputation/computeSlotQuantityFilled.js';
|
import computeSlotQuantityFilled from './buildComputation/computeSlotQuantityFilled.js';
|
||||||
import CreatureComputation from './CreatureComputation.js';
|
import CreatureComputation from './CreatureComputation.ts';
|
||||||
import removeSchemaFields from './buildComputation/removeSchemaFields.js';
|
import removeSchemaFields from './buildComputation/removeSchemaFields.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { pick } from 'lodash';
|
||||||
|
|
||||||
export default function aggregateEffect({node, linkedNode, link}){
|
export default function aggregateEffect({node, linkedNode, link}){
|
||||||
if (link.data !== 'effect') return;
|
if (link.data !== 'effect') return;
|
||||||
// store the effect aggregator, its presence indicates that the variable is
|
// store the effect aggregator, its presence indicates that the variable is
|
||||||
@@ -19,11 +21,22 @@ export default function aggregateEffect({node, linkedNode, link}){
|
|||||||
|
|
||||||
// Store a summary of the effect itself
|
// Store a summary of the effect itself
|
||||||
node.data.effects = node.data.effects || [];
|
node.data.effects = node.data.effects || [];
|
||||||
|
// Store either just
|
||||||
|
let effectAmount;
|
||||||
|
if (!linkedNode.data.amount) {
|
||||||
|
effectAmount = undefined;
|
||||||
|
} else if (typeof linkedNode.data.amount.value === 'string') {
|
||||||
|
effectAmount = pick(linkedNode.data.amount, [
|
||||||
|
'calculation', 'parseNode', 'parseError', 'value'
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
effectAmount = pick(linkedNode.data.amount, ['value']);
|
||||||
|
}
|
||||||
node.data.effects.push({
|
node.data.effects.push({
|
||||||
_id: linkedNode.data._id,
|
_id: linkedNode.data._id,
|
||||||
name: linkedNode.data.name,
|
name: linkedNode.data.name,
|
||||||
operation: linkedNode.data.operation,
|
operation: linkedNode.data.operation,
|
||||||
amount: linkedNode.data.amount && {value: linkedNode.data.amount.value},
|
amount: effectAmount,
|
||||||
type: linkedNode.data.type,
|
type: linkedNode.data.type,
|
||||||
// ancestors: linkedNode.data.ancestors,
|
// ancestors: linkedNode.data.ancestors,
|
||||||
});
|
});
|
||||||
@@ -33,7 +46,7 @@ export default function aggregateEffect({node, linkedNode, link}){
|
|||||||
// Get the result of the effect
|
// Get the result of the effect
|
||||||
const result = linkedNode.data.amount?.value;
|
const result = linkedNode.data.amount?.value;
|
||||||
// Skip aggregating if the result is not resolved completely
|
// Skip aggregating if the result is not resolved completely
|
||||||
if (typeof result === 'string') return;
|
if (typeof result === 'string' || result === undefined) return;
|
||||||
// Aggregate the effect based on its operation
|
// Aggregate the effect based on its operation
|
||||||
switch(linkedNode.data.operation){
|
switch(linkedNode.data.operation){
|
||||||
case 'base':
|
case 'base':
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ export default function evaluateToggles(computation, node){
|
|||||||
let toggles = prop._computationDetails?.toggleAncestors;
|
let toggles = prop._computationDetails?.toggleAncestors;
|
||||||
if (!toggles) return;
|
if (!toggles) return;
|
||||||
toggles.forEach(toggle => {
|
toggles.forEach(toggle => {
|
||||||
if (prop.inactive || !toggle.condition) return;
|
if (!toggle.condition) return;
|
||||||
if (!toggle.condition.value){
|
if (!toggle.condition.value){
|
||||||
prop.inactive = true;
|
prop.inactive = true;
|
||||||
prop.deactivatedByToggle = true;
|
prop.deactivatedByToggle = true;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export default function getEffectivePropTags(prop) {
|
|||||||
if (prop.variableName) tags.push(prop.variableName);
|
if (prop.variableName) tags.push(prop.variableName);
|
||||||
if (prop.damageType) tags.push(prop.damageType);
|
if (prop.damageType) tags.push(prop.damageType);
|
||||||
if (prop.skillType) tags.push(prop.skillType);
|
if (prop.skillType) tags.push(prop.skillType);
|
||||||
|
if (prop.actionType) tags.push(prop.actionType);
|
||||||
if (prop.attributeType) tags.push(prop.attributeType);
|
if (prop.attributeType) tags.push(prop.attributeType);
|
||||||
if (prop.reset) tags.push(prop.reset);
|
if (prop.reset) tags.push(prop.reset);
|
||||||
return tags;
|
return tags;
|
||||||
|
|||||||
@@ -57,11 +57,11 @@ Icons.attachSchema(iconsSchema);
|
|||||||
const writeIcons = new ValidatedMethod({
|
const writeIcons = new ValidatedMethod({
|
||||||
name: 'icons.write',
|
name: 'icons.write',
|
||||||
validate: null,
|
validate: null,
|
||||||
run(icons){
|
run(icons) {
|
||||||
assertAdmin(this.userId);
|
assertAdmin(this.userId);
|
||||||
if (Meteor.isServer){
|
if (Meteor.isServer) {
|
||||||
this.unblock();
|
this.unblock();
|
||||||
Icons.rawCollection().insert(icons, {ordered: false});
|
Icons.rawCollection().insert(icons, { ordered: false });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -80,11 +80,11 @@ const findIcons = new ValidatedMethod({
|
|||||||
numRequests: 20,
|
numRequests: 20,
|
||||||
timeInterval: 10000,
|
timeInterval: 10000,
|
||||||
},
|
},
|
||||||
run({search}){
|
run({ search }) {
|
||||||
if (!search) return [];
|
if (!search) return [];
|
||||||
if (!Meteor.isServer) return;
|
if (!Meteor.isServer) return;
|
||||||
return Icons.find(
|
return Icons.find(
|
||||||
{ $text: {$search: search} },
|
{ $text: { $search: search } },
|
||||||
{
|
{
|
||||||
// relevant documents have a higher score.
|
// relevant documents have a higher score.
|
||||||
fields: {
|
fields: {
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ const insertLibrary = new ValidatedMethod({
|
|||||||
'You need to be logged in to insert a library');
|
'You need to be logged in to insert a library');
|
||||||
}
|
}
|
||||||
let tier = getUserTier(this.userId);
|
let tier = getUserTier(this.userId);
|
||||||
if (!tier.paidBenefits){
|
if (!tier.paidBenefits) {
|
||||||
throw new Meteor.Error('Libraries.methods.insert.denied',
|
throw new Meteor.Error('Libraries.methods.insert.denied',
|
||||||
`The ${tier.name} tier does not allow you to insert a library`);
|
`The ${tier.name} tier does not allow you to insert a library`);
|
||||||
}
|
}
|
||||||
@@ -74,10 +74,10 @@ const updateLibraryName = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({_id, name}){
|
run({ _id, name }) {
|
||||||
let library = Libraries.findOne(_id);
|
let library = Libraries.findOne(_id);
|
||||||
assertEditPermission(library, this.userId);
|
assertEditPermission(library, this.userId);
|
||||||
Libraries.update(_id, {$set: {name}});
|
Libraries.update(_id, { $set: { name } });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -97,10 +97,10 @@ const updateLibraryDescription = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({_id, description}){
|
run({ _id, description }) {
|
||||||
let library = Libraries.findOne(_id);
|
let library = Libraries.findOne(_id);
|
||||||
assertEditPermission(library, this.userId);
|
assertEditPermission(library, this.userId);
|
||||||
Libraries.update(_id, {$set: {description}});
|
Libraries.update(_id, { $set: { description } });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -117,7 +117,7 @@ const removeLibrary = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({_id}){
|
run({ _id }) {
|
||||||
let library = Libraries.findOne(_id);
|
let library = Libraries.findOne(_id);
|
||||||
assertOwnership(library, this.userId);
|
assertOwnership(library, this.userId);
|
||||||
this.unblock();
|
this.unblock();
|
||||||
@@ -125,9 +125,9 @@ const removeLibrary = new ValidatedMethod({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export function removeLibaryWork(libraryId){
|
export function removeLibaryWork(libraryId) {
|
||||||
Libraries.remove(libraryId);
|
Libraries.remove(libraryId);
|
||||||
LibraryNodes.remove({'ancestors.id': libraryId});
|
LibraryNodes.remove({ 'ancestors.id': libraryId });
|
||||||
}
|
}
|
||||||
|
|
||||||
export { LibrarySchema, insertLibrary, updateLibraryName, updateLibraryDescription, removeLibrary };
|
export { LibrarySchema, insertLibrary, updateLibraryName, updateLibraryDescription, removeLibrary };
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ if (Meteor.isServer) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let key in propertySchemasIndex){
|
for (let key in propertySchemasIndex) {
|
||||||
let schema = new SimpleSchema({});
|
let schema = new SimpleSchema({});
|
||||||
schema.extend(LibraryNodeSchema);
|
schema.extend(LibraryNodeSchema);
|
||||||
schema.extend(ColorSchema);
|
schema.extend(ColorSchema);
|
||||||
@@ -68,18 +68,18 @@ for (let key in propertySchemasIndex){
|
|||||||
schema.extend(ChildSchema);
|
schema.extend(ChildSchema);
|
||||||
schema.extend(SoftRemovableSchema);
|
schema.extend(SoftRemovableSchema);
|
||||||
LibraryNodes.attachSchema(schema, {
|
LibraryNodes.attachSchema(schema, {
|
||||||
selector: {type: key}
|
selector: { type: key }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLibrary(node){
|
function getLibrary(node) {
|
||||||
if (!node) throw new Meteor.Error('No node provided');
|
if (!node) throw new Meteor.Error('No node provided');
|
||||||
let library = Libraries.findOne(node.ancestors[0].id);
|
let library = Libraries.findOne(node.ancestors[0].id);
|
||||||
if (!library) throw new Meteor.Error('Library does not exist');
|
if (!library) throw new Meteor.Error('Library does not exist');
|
||||||
return library;
|
return library;
|
||||||
}
|
}
|
||||||
|
|
||||||
function assertNodeEditPermission(node, userId){
|
function assertNodeEditPermission(node, userId) {
|
||||||
let lib = getLibrary(node);
|
let lib = getLibrary(node);
|
||||||
return assertEditPermission(lib, userId);
|
return assertEditPermission(lib, userId);
|
||||||
}
|
}
|
||||||
@@ -96,7 +96,7 @@ const insertNode = new ValidatedMethod({
|
|||||||
delete libraryNode._id;
|
delete libraryNode._id;
|
||||||
assertNodeEditPermission(libraryNode, this.userId);
|
assertNodeEditPermission(libraryNode, this.userId);
|
||||||
let nodeId = LibraryNodes.insert(libraryNode);
|
let nodeId = LibraryNodes.insert(libraryNode);
|
||||||
if (libraryNode.type == 'reference'){
|
if (libraryNode.type == 'reference') {
|
||||||
libraryNode._id = nodeId;
|
libraryNode._id = nodeId;
|
||||||
updateReferenceNodeWork(libraryNode, this.userId);
|
updateReferenceNodeWork(libraryNode, this.userId);
|
||||||
}
|
}
|
||||||
@@ -106,10 +106,10 @@ const insertNode = new ValidatedMethod({
|
|||||||
|
|
||||||
const updateLibraryNode = new ValidatedMethod({
|
const updateLibraryNode = new ValidatedMethod({
|
||||||
name: 'libraryNodes.update',
|
name: 'libraryNodes.update',
|
||||||
validate({_id, path}){
|
validate({ _id, path }) {
|
||||||
if (!_id) return false;
|
if (!_id) return false;
|
||||||
// We cannot change these fields with a simple update
|
// We cannot change these fields with a simple update
|
||||||
switch (path[0]){
|
switch (path[0]) {
|
||||||
case 'type':
|
case 'type':
|
||||||
case 'order':
|
case 'order':
|
||||||
case 'parent':
|
case 'parent':
|
||||||
@@ -122,21 +122,21 @@ const updateLibraryNode = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({_id, path, value}) {
|
run({ _id, path, value }) {
|
||||||
let node = LibraryNodes.findOne(_id);
|
let node = LibraryNodes.findOne(_id);
|
||||||
assertNodeEditPermission(node, this.userId);
|
assertNodeEditPermission(node, this.userId);
|
||||||
let pathString = path.join('.');
|
let pathString = path.join('.');
|
||||||
let modifier;
|
let modifier;
|
||||||
// unset empty values
|
// unset empty values
|
||||||
if (value === null || value === undefined){
|
if (value === null || value === undefined) {
|
||||||
modifier = {$unset: {[pathString]: 1}};
|
modifier = { $unset: { [pathString]: 1 } };
|
||||||
} else {
|
} else {
|
||||||
modifier = {$set: {[pathString]: value}};
|
modifier = { $set: { [pathString]: value } };
|
||||||
}
|
}
|
||||||
let numUpdated = LibraryNodes.update(_id, modifier, {
|
let numUpdated = LibraryNodes.update(_id, modifier, {
|
||||||
selector: {type: node.type},
|
selector: { type: node.type },
|
||||||
});
|
});
|
||||||
if (node.type == 'reference'){
|
if (node.type == 'reference') {
|
||||||
node = LibraryNodes.findOne(_id);
|
node = LibraryNodes.findOne(_id);
|
||||||
updateReferenceNodeWork(node, this.userId);
|
updateReferenceNodeWork(node, this.userId);
|
||||||
}
|
}
|
||||||
@@ -152,13 +152,13 @@ const pushToLibraryNode = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({_id, path, value}){
|
run({ _id, path, value }) {
|
||||||
let node = LibraryNodes.findOne(_id);
|
let node = LibraryNodes.findOne(_id);
|
||||||
assertNodeEditPermission(node, this.userId);
|
assertNodeEditPermission(node, this.userId);
|
||||||
return LibraryNodes.update(_id, {
|
return LibraryNodes.update(_id, {
|
||||||
$push: {[path.join('.')]: value},
|
$push: { [path.join('.')]: value },
|
||||||
}, {
|
}, {
|
||||||
selector: {type: node.type},
|
selector: { type: node.type },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -171,13 +171,13 @@ const pullFromLibraryNode = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({_id, path, itemId}){
|
run({ _id, path, itemId }) {
|
||||||
let node = LibraryNodes.findOne(_id);
|
let node = LibraryNodes.findOne(_id);
|
||||||
assertNodeEditPermission(node, this.userId);
|
assertNodeEditPermission(node, this.userId);
|
||||||
return LibraryNodes.update(_id, {
|
return LibraryNodes.update(_id, {
|
||||||
$pull: {[path.join('.')]: {_id: itemId}},
|
$pull: { [path.join('.')]: { _id: itemId } },
|
||||||
}, {
|
}, {
|
||||||
selector: {type: node.type},
|
selector: { type: node.type },
|
||||||
getAutoValues: false,
|
getAutoValues: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -193,10 +193,10 @@ const softRemoveLibraryNode = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({_id}){
|
run({ _id }) {
|
||||||
let node = LibraryNodes.findOne(_id);
|
let node = LibraryNodes.findOne(_id);
|
||||||
assertNodeEditPermission(node, this.userId);
|
assertNodeEditPermission(node, this.userId);
|
||||||
softRemove({_id, collection: LibraryNodes});
|
softRemove({ _id, collection: LibraryNodes });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -210,12 +210,12 @@ const restoreLibraryNode = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({_id}){
|
run({ _id }) {
|
||||||
// Permissions
|
// Permissions
|
||||||
let node = LibraryNodes.findOne(_id);
|
let node = LibraryNodes.findOne(_id);
|
||||||
assertNodeEditPermission(node, this.userId);
|
assertNodeEditPermission(node, this.userId);
|
||||||
// Do work
|
// Do work
|
||||||
restore({_id, collection: LibraryNodes});
|
restore({ _id, collection: LibraryNodes });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
import { reorderDocs } from '/imports/api/parenting/order.js';
|
import { reorderDocs } from '/imports/api/parenting/order.js';
|
||||||
|
|
||||||
var snackbar;
|
var snackbar;
|
||||||
if (Meteor.isClient){
|
if (Meteor.isClient) {
|
||||||
snackbar = require(
|
snackbar = require(
|
||||||
'/imports/ui/components/snackbars/SnackbarQueue.js'
|
'/imports/ui/components/snackbars/SnackbarQueue.js'
|
||||||
).snackbar
|
).snackbar
|
||||||
@@ -31,7 +31,7 @@ const duplicateLibraryNode = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({_id}) {
|
run({ _id }) {
|
||||||
let libraryNode = LibraryNodes.findOne(_id);
|
let libraryNode = LibraryNodes.findOne(_id);
|
||||||
assertDocEditPermission(libraryNode, this.userId);
|
assertDocEditPermission(libraryNode, this.userId);
|
||||||
|
|
||||||
@@ -41,15 +41,15 @@ const duplicateLibraryNode = new ValidatedMethod({
|
|||||||
|
|
||||||
let nodes = LibraryNodes.find({
|
let nodes = LibraryNodes.find({
|
||||||
'ancestors.id': _id,
|
'ancestors.id': _id,
|
||||||
removed: {$ne: true},
|
removed: { $ne: true },
|
||||||
}, {
|
}, {
|
||||||
limit: DUPLICATE_CHILDREN_LIMIT + 1,
|
limit: DUPLICATE_CHILDREN_LIMIT + 1,
|
||||||
sort: {order: 1},
|
sort: { order: 1 },
|
||||||
}).fetch();
|
}).fetch();
|
||||||
|
|
||||||
if (nodes.length > DUPLICATE_CHILDREN_LIMIT){
|
if (nodes.length > DUPLICATE_CHILDREN_LIMIT) {
|
||||||
nodes.pop();
|
nodes.pop();
|
||||||
if (Meteor.isClient){
|
if (Meteor.isClient) {
|
||||||
snackbar({
|
snackbar({
|
||||||
text: `Only the first ${DUPLICATE_CHILDREN_LIMIT} children were duplicated`,
|
text: `Only the first ${DUPLICATE_CHILDREN_LIMIT} children were duplicated`,
|
||||||
});
|
});
|
||||||
@@ -59,15 +59,15 @@ const duplicateLibraryNode = new ValidatedMethod({
|
|||||||
// re-map all the ancestors
|
// re-map all the ancestors
|
||||||
setLineageOfDocs({
|
setLineageOfDocs({
|
||||||
docArray: nodes,
|
docArray: nodes,
|
||||||
newAncestry : [
|
newAncestry: [
|
||||||
...libraryNode.ancestors,
|
...libraryNode.ancestors,
|
||||||
{id: libraryNodeId, collection: 'libraryNodes'}
|
{ id: libraryNodeId, collection: 'libraryNodes' }
|
||||||
],
|
],
|
||||||
oldParent : {id: _id, collection: 'libraryNodes'},
|
oldParent: { id: _id, collection: 'libraryNodes' },
|
||||||
});
|
});
|
||||||
|
|
||||||
// Give the docs new IDs without breaking internal references
|
// Give the docs new IDs without breaking internal references
|
||||||
renewDocIds({docArray: nodes});
|
renewDocIds({ docArray: nodes });
|
||||||
|
|
||||||
// Order the root node
|
// Order the root node
|
||||||
libraryNode.order += 0.5;
|
libraryNode.order += 0.5;
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
|
|
||||||
let SoftRemovableSchema = new SimpleSchema({
|
let SoftRemovableSchema = new SimpleSchema({
|
||||||
"removed": {
|
'removed': {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
optional: true,
|
optional: true,
|
||||||
index: 1,
|
index: 1,
|
||||||
},
|
},
|
||||||
"removedAt": {
|
'removedAt': {
|
||||||
type: Date,
|
type: Date,
|
||||||
optional: true,
|
optional: true,
|
||||||
index: 1,
|
index: 1,
|
||||||
},
|
},
|
||||||
"removedWith": {
|
'removedWith': {
|
||||||
optional: true,
|
optional: true,
|
||||||
type: String,
|
type: String,
|
||||||
regEx: SimpleSchema.RegEx.Id,
|
regEx: SimpleSchema.RegEx.Id,
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ let BuffRemoverSchema = createPropertySchema({
|
|||||||
'extraTags.$._id': {
|
'extraTags.$._id': {
|
||||||
type: String,
|
type: String,
|
||||||
regEx: SimpleSchema.RegEx.Id,
|
regEx: SimpleSchema.RegEx.Id,
|
||||||
autoValue(){
|
autoValue() {
|
||||||
if (!this.isSet) return Random.id();
|
if (!this.isSet) return Random.id();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ let ConstantSchema = new SimpleSchema({
|
|||||||
errors: {
|
errors: {
|
||||||
type: Array,
|
type: Array,
|
||||||
maxCount: STORAGE_LIMITS.errorCount,
|
maxCount: STORAGE_LIMITS.errorCount,
|
||||||
autoValue(){
|
autoValue() {
|
||||||
let calc = this.field('calculation');
|
let calc = this.field('calculation');
|
||||||
if (!calc.isSet && this.isModifier) {
|
if (!calc.isSet && this.isModifier) {
|
||||||
this.unset()
|
this.unset()
|
||||||
@@ -44,27 +44,27 @@ let ConstantSchema = new SimpleSchema({
|
|||||||
let string = calc.value;
|
let string = calc.value;
|
||||||
if (!string) return [];
|
if (!string) return [];
|
||||||
// Evaluate the calculation with no scope
|
// Evaluate the calculation with no scope
|
||||||
let {result, context} = parseString(string);
|
let { result, context } = parseString(string);
|
||||||
// Any existing errors will result in an early failure
|
// Any existing errors will result in an early failure
|
||||||
if (context && context.errors.length) return context.errors;
|
if (context && context.errors.length) return context.errors;
|
||||||
// Ban variables in constants if necessary
|
// Ban variables in constants if necessary
|
||||||
result && traverse(result, node => {
|
result && traverse(result, node => {
|
||||||
if (node.parseType === 'symbol' || node.parseType === 'accessor'){
|
if (node.parseType === 'symbol' || node.parseType === 'accessor') {
|
||||||
context.error('Variables can\'t be used to define a constant');
|
context.error('Variables can\'t be used to define a constant');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return context && context.errors || [];
|
return context && context.errors || [];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'errors.$':{
|
'errors.$': {
|
||||||
type: ErrorSchema,
|
type: ErrorSchema,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function parseString(string, fn = 'compile'){
|
function parseString(string, fn = 'compile') {
|
||||||
let context = new Context();
|
let context = new Context();
|
||||||
if (!string){
|
if (!string) {
|
||||||
return {result: string, context};
|
return { result: string, context };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the string using mathjs
|
// Parse the string using mathjs
|
||||||
@@ -74,11 +74,11 @@ function parseString(string, fn = 'compile'){
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
let message = prettifyParseError(e);
|
let message = prettifyParseError(e);
|
||||||
context.error(message);
|
context.error(message);
|
||||||
return {context};
|
return { context };
|
||||||
}
|
}
|
||||||
if (!node) return {context};
|
if (!node) return { context };
|
||||||
let {result} = resolve(fn, node, {/*empty scope*/}, context);
|
let { result } = resolve(fn, node, {/*empty scope*/ }, context);
|
||||||
return {result, context}
|
return { result, context }
|
||||||
}
|
}
|
||||||
|
|
||||||
const ComputedOnlyConstantSchema = new SimpleSchema({});
|
const ComputedOnlyConstantSchema = new SimpleSchema({});
|
||||||
|
|||||||
@@ -40,24 +40,24 @@ const ComputedOnlyContainerSchema = createPropertySchema({
|
|||||||
optional: true,
|
optional: true,
|
||||||
},
|
},
|
||||||
// Weight of all the contents, zero if `contentsWeightless` is true
|
// Weight of all the contents, zero if `contentsWeightless` is true
|
||||||
contentsWeight:{
|
contentsWeight: {
|
||||||
type: Number,
|
type: Number,
|
||||||
optional: true,
|
optional: true,
|
||||||
removeBeforeCompute: true,
|
removeBeforeCompute: true,
|
||||||
},
|
},
|
||||||
// Weight of all the carried contents (some sub-containers might not be carried)
|
// Weight of all the carried contents (some sub-containers might not be carried)
|
||||||
// zero if `contentsWeightless` is true
|
// zero if `contentsWeightless` is true
|
||||||
carriedWeight:{
|
carriedWeight: {
|
||||||
type: Number,
|
type: Number,
|
||||||
optional: true,
|
optional: true,
|
||||||
removeBeforeCompute: true,
|
removeBeforeCompute: true,
|
||||||
},
|
},
|
||||||
contentsValue:{
|
contentsValue: {
|
||||||
type: Number,
|
type: Number,
|
||||||
optional: true,
|
optional: true,
|
||||||
removeBeforeCompute: true,
|
removeBeforeCompute: true,
|
||||||
},
|
},
|
||||||
carriedValue:{
|
carriedValue: {
|
||||||
type: Number,
|
type: Number,
|
||||||
optional: true,
|
optional: true,
|
||||||
removeBeforeCompute: true,
|
removeBeforeCompute: true,
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ const magicSchools = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
let SpellSchema = new SimpleSchema({})
|
let SpellSchema = new SimpleSchema({})
|
||||||
.extend(ActionSchema)
|
.extend(ActionSchema)
|
||||||
.extend({
|
.extend({
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
@@ -89,7 +89,7 @@ let SpellSchema = new SimpleSchema({})
|
|||||||
defaultValue: 'abjuration',
|
defaultValue: 'abjuration',
|
||||||
allowedValues: magicSchools,
|
allowedValues: magicSchools,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const ComputedOnlySpellSchema = new SimpleSchema()
|
const ComputedOnlySpellSchema = new SimpleSchema()
|
||||||
.extend(ComputedOnlyActionSchema);
|
.extend(ComputedOnlyActionSchema);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ const AdjustmentSchema = new SimpleSchema({
|
|||||||
_id: {
|
_id: {
|
||||||
type: String,
|
type: String,
|
||||||
regEx: SimpleSchema.RegEx.Id,
|
regEx: SimpleSchema.RegEx.Id,
|
||||||
autoValue(){
|
autoValue() {
|
||||||
if (!this.isSet) return Random.id();
|
if (!this.isSet) return Random.id();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
import ResultsSchema from '/imports/api/properties/subSchemas/ResultsSchema.js';
|
import ResultsSchema from '/imports/api/properties/subSchemas/ResultsSchema.js';
|
||||||
|
|
||||||
let RollResultsSchema = new SimpleSchema ({
|
let RollResultsSchema = new SimpleSchema({
|
||||||
_id: {
|
_id: {
|
||||||
type: String,
|
type: String,
|
||||||
regEx: SimpleSchema.RegEx.Id,
|
regEx: SimpleSchema.RegEx.Id,
|
||||||
autoValue(){
|
autoValue() {
|
||||||
if (!this.isSet) return Random.id();
|
if (!this.isSet) return Random.id();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -22,4 +22,4 @@ let RollResultsSchema = new SimpleSchema ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default RollResultsSchema ;
|
export default RollResultsSchema;
|
||||||
|
|||||||
@@ -18,11 +18,11 @@ const setPublic = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({docRef, isPublic}){
|
run({ docRef, isPublic }) {
|
||||||
let doc = fetchDocByRef(docRef);
|
let doc = fetchDocByRef(docRef);
|
||||||
assertOwnership(doc, this.userId);
|
assertOwnership(doc, this.userId);
|
||||||
return getCollectionByName(docRef.collection).update(docRef.id, {
|
return getCollectionByName(docRef.collection).update(docRef.id, {
|
||||||
$set: {public: isPublic},
|
$set: { public: isPublic },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -45,28 +45,28 @@ const updateUserSharePermissions = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({docRef, userId, role}){
|
run({ docRef, userId, role }) {
|
||||||
let doc = fetchDocByRef(docRef);
|
let doc = fetchDocByRef(docRef);
|
||||||
if (role === 'none'){
|
if (role === 'none') {
|
||||||
// only assert ownership if you aren't removing yourself
|
// only assert ownership if you aren't removing yourself
|
||||||
if (this.userId !== userId){
|
if (this.userId !== userId) {
|
||||||
assertOwnership(doc, this.userId);
|
assertOwnership(doc, this.userId);
|
||||||
}
|
}
|
||||||
return getCollectionByName(docRef.collection).update(docRef.id, {
|
return getCollectionByName(docRef.collection).update(docRef.id, {
|
||||||
$pullAll: { readers: userId, writers: userId },
|
$pullAll: { readers: userId, writers: userId },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (doc.owner === userId){
|
if (doc.owner === userId) {
|
||||||
throw new Meteor.Error('Sharing update failed',
|
throw new Meteor.Error('Sharing update failed',
|
||||||
'User is already the owner of this document');
|
'User is already the owner of this document');
|
||||||
}
|
}
|
||||||
assertOwnership(doc, this.userId);
|
assertOwnership(doc, this.userId);
|
||||||
if (role === 'reader'){
|
if (role === 'reader') {
|
||||||
return getCollectionByName(docRef.collection).update(docRef.id, {
|
return getCollectionByName(docRef.collection).update(docRef.id, {
|
||||||
$addToSet: { readers: userId },
|
$addToSet: { readers: userId },
|
||||||
$pullAll: { writers: userId },
|
$pullAll: { writers: userId },
|
||||||
});
|
});
|
||||||
} else if (role === 'writer'){
|
} else if (role === 'writer') {
|
||||||
return getCollectionByName(docRef.collection).update(docRef.id, {
|
return getCollectionByName(docRef.collection).update(docRef.id, {
|
||||||
$addToSet: { writers: userId },
|
$addToSet: { writers: userId },
|
||||||
$pullAll: { readers: userId },
|
$pullAll: { readers: userId },
|
||||||
@@ -89,29 +89,29 @@ const transferOwnership = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({docRef, userId}){
|
run({ docRef, userId }) {
|
||||||
let doc = fetchDocByRef(docRef);
|
let doc = fetchDocByRef(docRef);
|
||||||
assertOwnership(doc, this.userId);
|
assertOwnership(doc, this.userId);
|
||||||
|
|
||||||
let collection = getCollectionByName(docRef.collection);
|
let collection = getCollectionByName(docRef.collection);
|
||||||
|
|
||||||
let tier = getUserTier(userId);
|
let tier = getUserTier(userId);
|
||||||
if (docRef.collection === 'creatures'){
|
if (docRef.collection === 'creatures') {
|
||||||
let currentCharacterCount = collection.find({
|
let currentCharacterCount = collection.find({
|
||||||
owner: userId,
|
owner: userId,
|
||||||
}, {
|
}, {
|
||||||
fields: {_id: 1},
|
fields: { _id: 1 },
|
||||||
}).count();
|
}).count();
|
||||||
|
|
||||||
if (
|
if (
|
||||||
tier.characterSlots !== -1 &&
|
tier.characterSlots !== -1 &&
|
||||||
currentCharacterCount >= tier.characterSlots
|
currentCharacterCount >= tier.characterSlots
|
||||||
){
|
) {
|
||||||
throw new Meteor.Error('Sharing.methods.transferOwnership.denied',
|
throw new Meteor.Error('Sharing.methods.transferOwnership.denied',
|
||||||
'The new owner is already at their character limit')
|
'The new owner is already at their character limit')
|
||||||
}
|
}
|
||||||
} else if (docRef.collection === 'libraries'){
|
} else if (docRef.collection === 'libraries') {
|
||||||
if (!tier.paidBenefits){
|
if (!tier.paidBenefits) {
|
||||||
throw new Meteor.Error('Sharing.methods.transferOwnership.denied',
|
throw new Meteor.Error('Sharing.methods.transferOwnership.denied',
|
||||||
'The new owner\'s Patreon tier does not have access to library ownership');
|
'The new owner\'s Patreon tier does not have access to library ownership');
|
||||||
}
|
}
|
||||||
@@ -123,7 +123,7 @@ const transferOwnership = new ValidatedMethod({
|
|||||||
});
|
});
|
||||||
// Then make the user the owner and the current owner a writer
|
// Then make the user the owner and the current owner a writer
|
||||||
return collection.update(docRef.id, {
|
return collection.update(docRef.id, {
|
||||||
$set: {owner: userId},
|
$set: { owner: userId },
|
||||||
$addToSet: { writers: this.userId },
|
$addToSet: { writers: this.userId },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ const sendMessage = new ValidatedMethod({
|
|||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
|
|
||||||
run({content, tabletopId}) {
|
run({ content, tabletopId }) {
|
||||||
let user = Meteor.user();
|
let user = Meteor.user();
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new Meteor.Error('messages.send.denied',
|
throw new Meteor.Error('messages.send.denied',
|
||||||
@@ -87,14 +87,14 @@ const removeMessages = new ValidatedMethod({
|
|||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
|
|
||||||
run({messageId, tabletopId}) {
|
run({ messageId, tabletopId }) {
|
||||||
if (!this.userId) {
|
if (!this.userId) {
|
||||||
throw new Meteor.Error('messages.remove.denied',
|
throw new Meteor.Error('messages.remove.denied',
|
||||||
'You need to be logged in to remove a tabletop');
|
'You need to be logged in to remove a tabletop');
|
||||||
}
|
}
|
||||||
let message = Messages.findOne(messageId);
|
let message = Messages.findOne(messageId);
|
||||||
let tabletop = Tabletops.findOne(message.tabletopId);
|
let tabletop = Tabletops.findOne(message.tabletopId);
|
||||||
if (this.userId !== message.userId && this.userId !== tabletop.gameMaster){
|
if (this.userId !== message.userId && this.userId !== tabletop.gameMaster) {
|
||||||
throw new Meteor.Error('messages.remove.denied',
|
throw new Meteor.Error('messages.remove.denied',
|
||||||
'You don\'t have permission to remove this message');
|
'You don\'t have permission to remove this message');
|
||||||
}
|
}
|
||||||
@@ -104,7 +104,7 @@ const removeMessages = new ValidatedMethod({
|
|||||||
Creatures.update({
|
Creatures.update({
|
||||||
tabletop: tabletopId,
|
tabletop: tabletopId,
|
||||||
}, {
|
}, {
|
||||||
$unset: {tabletop: 1},
|
$unset: { tabletop: 1 },
|
||||||
});
|
});
|
||||||
return removed;
|
return removed;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const removeTabletop = new ValidatedMethod({
|
|||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
|
|
||||||
run({tabletopId}) {
|
run({ tabletopId }) {
|
||||||
if (!this.userId) {
|
if (!this.userId) {
|
||||||
throw new Meteor.Error('tabletops.remove.denied',
|
throw new Meteor.Error('tabletops.remove.denied',
|
||||||
'You need to be logged in to remove a tabletop');
|
'You need to be logged in to remove a tabletop');
|
||||||
@@ -39,7 +39,7 @@ const removeTabletop = new ValidatedMethod({
|
|||||||
Creatures.update({
|
Creatures.update({
|
||||||
tabletop: tabletopId,
|
tabletop: tabletopId,
|
||||||
}, {
|
}, {
|
||||||
$unset: {tabletop: 1},
|
$unset: { tabletop: 1 },
|
||||||
});
|
});
|
||||||
return removed;
|
return removed;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -129,13 +129,13 @@ Meteor.users.generateApiKey = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run(){
|
run() {
|
||||||
if(Meteor.isClient) return;
|
if (Meteor.isClient) return;
|
||||||
var user = Meteor.users.findOne(this.userId);
|
var user = Meteor.users.findOne(this.userId);
|
||||||
if (!user) return;
|
if (!user) return;
|
||||||
if (user && user.apiKey) return;
|
if (user && user.apiKey) return;
|
||||||
var apiKey = Random.id(30);
|
var apiKey = Random.id(30);
|
||||||
Meteor.users.update(this.userId, {$set: {apiKey}});
|
Meteor.users.update(this.userId, { $set: { apiKey } });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -149,16 +149,16 @@ Meteor.users.setDarkMode = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({darkMode}){
|
run({ darkMode }) {
|
||||||
if (!this.userId) return;
|
if (!this.userId) return;
|
||||||
Meteor.users.update(this.userId, {$set: {darkMode}});
|
Meteor.users.update(this.userId, { $set: { darkMode } });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Meteor.users.sendVerificationEmail = new ValidatedMethod({
|
Meteor.users.sendVerificationEmail = new ValidatedMethod({
|
||||||
name: 'users.sendVerificationEmail',
|
name: 'users.sendVerificationEmail',
|
||||||
validate: new SimpleSchema({
|
validate: new SimpleSchema({
|
||||||
userId:{
|
userId: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
},
|
},
|
||||||
@@ -171,7 +171,7 @@ Meteor.users.sendVerificationEmail = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({userId, address}){
|
run({ userId, address }) {
|
||||||
userId = this.userId || userId;
|
userId = this.userId || userId;
|
||||||
let user = Meteor.users.findOne(userId);
|
let user = Meteor.users.findOne(userId);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
@@ -194,11 +194,11 @@ Meteor.users.canPickUsername = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({username}){
|
run({ username }) {
|
||||||
if (Meteor.isClient) return;
|
if (Meteor.isClient) return;
|
||||||
let user = Accounts.findUserByUsername(username);
|
let user = Accounts.findUserByUsername(username);
|
||||||
// You can pick your own username
|
// You can pick your own username
|
||||||
if (user && user._id === this.userId){
|
if (user && user._id === this.userId) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return !!user;
|
return !!user;
|
||||||
@@ -213,7 +213,7 @@ Meteor.users.setUsername = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({username}){
|
run({ username }) {
|
||||||
if (!this.userId) throw 'Can only set your username if logged in';
|
if (!this.userId) throw 'Can only set your username if logged in';
|
||||||
if (Meteor.isClient) return;
|
if (Meteor.isClient) return;
|
||||||
return Accounts.setUsername(this.userId, username)
|
return Accounts.setUsername(this.userId, username)
|
||||||
@@ -223,7 +223,7 @@ Meteor.users.setUsername = new ValidatedMethod({
|
|||||||
Meteor.users.setPreference = new ValidatedMethod({
|
Meteor.users.setPreference = new ValidatedMethod({
|
||||||
name: 'users.setPreference',
|
name: 'users.setPreference',
|
||||||
validate: new SimpleSchema({
|
validate: new SimpleSchema({
|
||||||
preference:{
|
preference: {
|
||||||
type: String,
|
type: String,
|
||||||
},
|
},
|
||||||
value: {
|
value: {
|
||||||
@@ -235,16 +235,16 @@ Meteor.users.setPreference = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({preference, value}){
|
run({ preference, value }) {
|
||||||
if (!this.userId) throw 'You can only set preferences once logged in';
|
if (!this.userId) throw 'You can only set preferences once logged in';
|
||||||
let prefPath = `preferences.${preference}`
|
let prefPath = `preferences.${preference}`
|
||||||
if (value == true){
|
if (value == true) {
|
||||||
return Meteor.users.update(this.userId, {
|
return Meteor.users.update(this.userId, {
|
||||||
$set: {[prefPath]: true},
|
$set: { [prefPath]: true },
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return Meteor.users.update(this.userId, {
|
return Meteor.users.update(this.userId, {
|
||||||
$unset: {[prefPath]: 1},
|
$unset: { [prefPath]: 1 },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -253,7 +253,7 @@ Meteor.users.setPreference = new ValidatedMethod({
|
|||||||
Meteor.users.subscribeToLibrary = new ValidatedMethod({
|
Meteor.users.subscribeToLibrary = new ValidatedMethod({
|
||||||
name: 'users.subscribeToLibrary',
|
name: 'users.subscribeToLibrary',
|
||||||
validate: new SimpleSchema({
|
validate: new SimpleSchema({
|
||||||
libraryId:{
|
libraryId: {
|
||||||
type: String,
|
type: String,
|
||||||
regEx: SimpleSchema.RegEx.Id,
|
regEx: SimpleSchema.RegEx.Id,
|
||||||
},
|
},
|
||||||
@@ -266,15 +266,15 @@ Meteor.users.subscribeToLibrary = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({libraryId, subscribe}){
|
run({ libraryId, subscribe }) {
|
||||||
if (!this.userId) throw 'Can only subscribe if logged in';
|
if (!this.userId) throw 'Can only subscribe if logged in';
|
||||||
if (subscribe){
|
if (subscribe) {
|
||||||
return Meteor.users.update(this.userId, {
|
return Meteor.users.update(this.userId, {
|
||||||
$addToSet: {subscribedLibraries: libraryId},
|
$addToSet: { subscribedLibraries: libraryId },
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return Meteor.users.update(this.userId, {
|
return Meteor.users.update(this.userId, {
|
||||||
$pullAll: {subscribedLibraries: libraryId},
|
$pullAll: { subscribedLibraries: libraryId },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -283,7 +283,7 @@ Meteor.users.subscribeToLibrary = new ValidatedMethod({
|
|||||||
Meteor.users.subscribeToLibraryCollection = new ValidatedMethod({
|
Meteor.users.subscribeToLibraryCollection = new ValidatedMethod({
|
||||||
name: 'users.subscribeToLibraryCollection',
|
name: 'users.subscribeToLibraryCollection',
|
||||||
validate: new SimpleSchema({
|
validate: new SimpleSchema({
|
||||||
libraryCollectionId:{
|
libraryCollectionId: {
|
||||||
type: String,
|
type: String,
|
||||||
regEx: SimpleSchema.RegEx.Id,
|
regEx: SimpleSchema.RegEx.Id,
|
||||||
},
|
},
|
||||||
@@ -296,15 +296,15 @@ Meteor.users.subscribeToLibraryCollection = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({libraryCollectionId, subscribe}){
|
run({ libraryCollectionId, subscribe }) {
|
||||||
if (!this.userId) throw 'Can only subscribe if logged in';
|
if (!this.userId) throw 'Can only subscribe if logged in';
|
||||||
if (subscribe){
|
if (subscribe) {
|
||||||
return Meteor.users.update(this.userId, {
|
return Meteor.users.update(this.userId, {
|
||||||
$addToSet: {subscribedLibraryCollections: libraryCollectionId},
|
$addToSet: { subscribedLibraryCollections: libraryCollectionId },
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return Meteor.users.update(this.userId, {
|
return Meteor.users.update(this.userId, {
|
||||||
$pullAll: {subscribedLibraryCollections: libraryCollectionId},
|
$pullAll: { subscribedLibraryCollections: libraryCollectionId },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -313,7 +313,7 @@ Meteor.users.subscribeToLibraryCollection = new ValidatedMethod({
|
|||||||
Meteor.users.findUserByUsernameOrEmail = new ValidatedMethod({
|
Meteor.users.findUserByUsernameOrEmail = new ValidatedMethod({
|
||||||
name: 'users.findUserByUsernameOrEmail',
|
name: 'users.findUserByUsernameOrEmail',
|
||||||
validate: new SimpleSchema({
|
validate: new SimpleSchema({
|
||||||
usernameOrEmail:{
|
usernameOrEmail: {
|
||||||
type: String,
|
type: String,
|
||||||
},
|
},
|
||||||
}).validator(),
|
}).validator(),
|
||||||
@@ -322,7 +322,7 @@ Meteor.users.findUserByUsernameOrEmail = new ValidatedMethod({
|
|||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({usernameOrEmail}){
|
run({ usernameOrEmail }) {
|
||||||
if (Meteor.isClient) return;
|
if (Meteor.isClient) return;
|
||||||
let user = Accounts.findUserByUsername(usernameOrEmail) ||
|
let user = Accounts.findUserByUsername(usernameOrEmail) ||
|
||||||
Accounts.findUserByEmail(usernameOrEmail);
|
Accounts.findUserByEmail(usernameOrEmail);
|
||||||
|
|||||||
@@ -15,16 +15,16 @@ const addEmail = new ValidatedMethod({
|
|||||||
numRequests: 1,
|
numRequests: 1,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({email}){
|
run({ email }) {
|
||||||
const userId = Meteor.userId();
|
const userId = Meteor.userId();
|
||||||
const user = Meteor.users.findOne(userId);
|
const user = Meteor.users.findOne(userId);
|
||||||
if (!user) throw new Meteor.Error('No user',
|
if (!user) throw new Meteor.Error('No user',
|
||||||
'You must be logged in to add an email address');
|
'You must be logged in to add an email address');
|
||||||
if (user.emails && user.emails.length >= 2){
|
if (user.emails && user.emails.length >= 2) {
|
||||||
throw new Meteor.Error('Emails full',
|
throw new Meteor.Error('Emails full',
|
||||||
'You may only have up to 2 email addresses per account');
|
'You may only have up to 2 email addresses per account');
|
||||||
}
|
}
|
||||||
if (Meteor.isServer){
|
if (Meteor.isServer) {
|
||||||
Accounts.addEmail(userId, email);
|
Accounts.addEmail(userId, email);
|
||||||
Accounts.sendVerificationEmail(userId, email);
|
Accounts.sendVerificationEmail(userId, email);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||||
import Libraries, {removeLibaryWork} from '/imports/api/library/Libraries.js';
|
import Libraries, { removeLibaryWork } from '/imports/api/library/Libraries.js';
|
||||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||||
import {removeCreatureWork} from '/imports/api/creature/creatures/methods/removeCreature.js';
|
import { removeCreatureWork } from '/imports/api/creature/creatures/methods/removeCreature.js';
|
||||||
|
|
||||||
Meteor.users.deleteMyAccount = new ValidatedMethod({
|
Meteor.users.deleteMyAccount = new ValidatedMethod({
|
||||||
name: 'users.deleteMyAccount',
|
name: 'users.deleteMyAccount',
|
||||||
@@ -12,20 +12,20 @@ Meteor.users.deleteMyAccount = new ValidatedMethod({
|
|||||||
numRequests: 1,
|
numRequests: 1,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run(){
|
run() {
|
||||||
let userId = Meteor.userId();
|
let userId = Meteor.userId();
|
||||||
if (!userId) throw new Meteor.Error('No user',
|
if (!userId) throw new Meteor.Error('No user',
|
||||||
'You must be logged in to delete your account');
|
'You must be logged in to delete your account');
|
||||||
|
|
||||||
// Delete all creatures
|
// Delete all creatures
|
||||||
let creatures = Creatures.find({owner: userId}, {fields: {_id: 1}}).fetch();
|
let creatures = Creatures.find({ owner: userId }, { fields: { _id: 1 } }).fetch();
|
||||||
creatures.forEach(creature => removeCreatureWork(creature._id));
|
creatures.forEach(creature => removeCreatureWork(creature._id));
|
||||||
|
|
||||||
// Remove permissions from all creatures
|
// Remove permissions from all creatures
|
||||||
Creatures.update({
|
Creatures.update({
|
||||||
$or: [
|
$or: [
|
||||||
{writers: userId},
|
{ writers: userId },
|
||||||
{readers: userId},
|
{ readers: userId },
|
||||||
],
|
],
|
||||||
}, {
|
}, {
|
||||||
$pull: {
|
$pull: {
|
||||||
@@ -37,14 +37,14 @@ Meteor.users.deleteMyAccount = new ValidatedMethod({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Delete all libraries
|
// Delete all libraries
|
||||||
let libraries = Libraries.find({owner: userId}, {fields: {_id: 1}}).fetch();
|
let libraries = Libraries.find({ owner: userId }, { fields: { _id: 1 } }).fetch();
|
||||||
libraries.forEach(library => removeLibaryWork(library._id));
|
libraries.forEach(library => removeLibaryWork(library._id));
|
||||||
|
|
||||||
// Remove permissions from all creatures
|
// Remove permissions from all creatures
|
||||||
Libraries.update({
|
Libraries.update({
|
||||||
$or: [
|
$or: [
|
||||||
{writers: userId},
|
{ writers: userId },
|
||||||
{readers: userId},
|
{ readers: userId },
|
||||||
],
|
],
|
||||||
}, {
|
}, {
|
||||||
$pull: {
|
$pull: {
|
||||||
|
|||||||
@@ -15,20 +15,20 @@ const removeEmail = new ValidatedMethod({
|
|||||||
numRequests: 1,
|
numRequests: 1,
|
||||||
timeInterval: 5000,
|
timeInterval: 5000,
|
||||||
},
|
},
|
||||||
run({email}){
|
run({ email }) {
|
||||||
const userId = Meteor.userId();
|
const userId = Meteor.userId();
|
||||||
const user = Meteor.users.findOne(userId);
|
const user = Meteor.users.findOne(userId);
|
||||||
if (!user) throw new Meteor.Error('No user',
|
if (!user) throw new Meteor.Error('No user',
|
||||||
'You must be logged in to remove an email address');
|
'You must be logged in to remove an email address');
|
||||||
if (!user.emails){
|
if (!user.emails) {
|
||||||
throw new Meteor.Error('No email to remove',
|
throw new Meteor.Error('No email to remove',
|
||||||
'No email addresses are associated with this account');
|
'No email addresses are associated with this account');
|
||||||
}
|
}
|
||||||
if (user.emails.length == 1){
|
if (user.emails.length == 1) {
|
||||||
throw new Meteor.Error('Can\'t remove last email',
|
throw new Meteor.Error('Can\'t remove last email',
|
||||||
'You may not remove the last email address from your account');
|
'You may not remove the last email address from your account');
|
||||||
}
|
}
|
||||||
if (Meteor.isServer){
|
if (Meteor.isServer) {
|
||||||
Accounts.removeEmail(userId, email);
|
Accounts.removeEmail(userId, email);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,14 @@ const PROPERTIES = Object.freeze({
|
|||||||
action: {
|
action: {
|
||||||
icon: '$vuetify.icons.action',
|
icon: '$vuetify.icons.action',
|
||||||
name: 'Action',
|
name: 'Action',
|
||||||
|
docsPath: 'property/action',
|
||||||
helpText: 'Actions are things your character can do. When an action is taken, all the properties under it are activated.',
|
helpText: 'Actions are things your character can do. When an action is taken, all the properties under it are activated.',
|
||||||
suggestedParents: ['classLevel', 'feature', 'item'],
|
suggestedParents: ['classLevel', 'feature', 'item'],
|
||||||
},
|
},
|
||||||
attribute: {
|
attribute: {
|
||||||
icon: '$vuetify.icons.attribute',
|
icon: '$vuetify.icons.attribute',
|
||||||
name: 'Attribute',
|
name: 'Attribute',
|
||||||
|
docsPath: 'property/attribute',
|
||||||
helpText: 'Attributes are the numbered statistics of your character, excluding rolls you might add proficiency bonus to, those are skills.',
|
helpText: 'Attributes are the numbered statistics of your character, excluding rolls you might add proficiency bonus to, those are skills.',
|
||||||
examples: 'Ability scores, speed, hit points, ki',
|
examples: 'Ability scores, speed, hit points, ki',
|
||||||
suggestedParents: ['classLevel', 'buff'],
|
suggestedParents: ['classLevel', 'buff'],
|
||||||
@@ -15,48 +17,56 @@ const PROPERTIES = Object.freeze({
|
|||||||
adjustment: {
|
adjustment: {
|
||||||
icon: '$vuetify.icons.attribute_damage',
|
icon: '$vuetify.icons.attribute_damage',
|
||||||
name: 'Attribute damage',
|
name: 'Attribute damage',
|
||||||
|
docsPath: 'property/attribute-damage',
|
||||||
helpText: 'Attribute damage reduces the current value of an attribute when it is applied by an action. A negative value causes the attribute to increase instead, up to its normal maximum.',
|
helpText: 'Attribute damage reduces the current value of an attribute when it is applied by an action. A negative value causes the attribute to increase instead, up to its normal maximum.',
|
||||||
suggestedParents: ['action', 'attack', 'savingThrow', 'spell', 'branch'],
|
suggestedParents: ['action', 'attack', 'savingThrow', 'spell', 'branch'],
|
||||||
},
|
},
|
||||||
buff: {
|
buff: {
|
||||||
icon: '$vuetify.icons.buff',
|
icon: '$vuetify.icons.buff',
|
||||||
name: 'Buff',
|
name: 'Buff',
|
||||||
|
docsPath: 'property/buff',
|
||||||
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: {
|
buffRemover: {
|
||||||
icon: '$vuetify.icons.buffRemover',
|
icon: '$vuetify.icons.buffRemover',
|
||||||
name: 'Remove Buff',
|
name: 'Remove Buff',
|
||||||
|
docsPath: 'property/remove-buff',
|
||||||
helpText: 'Removes a buff from the target character',
|
helpText: 'Removes a buff from the target character',
|
||||||
suggestedParents: ['action', 'attack', 'savingThrow', 'spell', 'branch'],
|
suggestedParents: ['action', 'attack', 'savingThrow', 'spell', 'branch'],
|
||||||
},
|
},
|
||||||
branch: {
|
branch: {
|
||||||
icon: 'mdi-file-tree',
|
icon: 'mdi-file-tree',
|
||||||
name: 'Branch',
|
name: 'Branch',
|
||||||
|
docsPath: 'property/branch',
|
||||||
helpText: 'When a branch is activated as a child of an action, it can control which of its children get activated.',
|
helpText: 'When a branch is activated as a child of an action, it can control which of its children get activated.',
|
||||||
suggestedParents: ['action', 'attack', 'savingThrow', 'spell'],
|
suggestedParents: ['action', 'attack', 'savingThrow', 'spell'],
|
||||||
},
|
},
|
||||||
class: {
|
class: {
|
||||||
icon: 'mdi-card-account-details',
|
icon: 'mdi-card-account-details',
|
||||||
name: 'Class',
|
name: 'Class',
|
||||||
|
docsPath: 'property/class',
|
||||||
helpText: 'Your character should ideally have one starting class. Classes hold class levels',
|
helpText: 'Your character should ideally have one starting class. Classes hold class levels',
|
||||||
suggestedParents: [],
|
suggestedParents: [],
|
||||||
},
|
},
|
||||||
classLevel: {
|
classLevel: {
|
||||||
icon: '$vuetify.icons.class_level',
|
icon: '$vuetify.icons.class_level',
|
||||||
name: 'Class level',
|
name: 'Class level',
|
||||||
|
docsPath: 'property/class-level',
|
||||||
helpText: 'Class levels represent a single level gained in a class',
|
helpText: 'Class levels represent a single level gained in a class',
|
||||||
suggestedParents: ['class'],
|
suggestedParents: ['class'],
|
||||||
},
|
},
|
||||||
constant: {
|
constant: {
|
||||||
icon: 'mdi-anchor',
|
icon: 'mdi-anchor',
|
||||||
name: 'Constant',
|
name: 'Constant',
|
||||||
|
docsPath: 'property/constant',
|
||||||
helpText: 'A constant can define a static value that can be used in calculations elsewhere in the sheet',
|
helpText: 'A constant can define a static value that can be used in calculations elsewhere in the sheet',
|
||||||
suggestedParents: [],
|
suggestedParents: [],
|
||||||
},
|
},
|
||||||
container: {
|
container: {
|
||||||
icon: 'mdi-bag-personal-outline',
|
icon: 'mdi-bag-personal-outline',
|
||||||
name: 'Container',
|
name: 'Container',
|
||||||
|
docsPath: 'property/container',
|
||||||
helpText: 'A container holds items in the inventory',
|
helpText: 'A container holds items in the inventory',
|
||||||
examples: 'Coin pouch, backpack',
|
examples: 'Coin pouch, backpack',
|
||||||
suggestedParents: ['folder'],
|
suggestedParents: ['folder'],
|
||||||
@@ -64,18 +74,21 @@ const PROPERTIES = Object.freeze({
|
|||||||
damage: {
|
damage: {
|
||||||
icon: '$vuetify.icons.damage',
|
icon: '$vuetify.icons.damage',
|
||||||
name: 'Damage',
|
name: 'Damage',
|
||||||
|
docsPath: 'property/damage',
|
||||||
helpText: 'When damage is activated by an action it reduces the hit points of the target creature by the calculated amount.',
|
helpText: 'When damage is activated by an action it reduces the hit points of the target creature by the calculated amount.',
|
||||||
suggestedParents: ['action', 'attack', 'savingThrow', 'spell', 'branch'],
|
suggestedParents: ['action', 'attack', 'savingThrow', 'spell', 'branch'],
|
||||||
},
|
},
|
||||||
damageMultiplier: {
|
damageMultiplier: {
|
||||||
icon: '$vuetify.icons.damage_multiplier',
|
icon: '$vuetify.icons.damage_multiplier',
|
||||||
name: 'Damage multiplier',
|
name: 'Damage multiplier',
|
||||||
|
docsPath: 'property/damage-multiplier',
|
||||||
helpText: 'Resistance, vulnerability, and immunity.',
|
helpText: 'Resistance, vulnerability, and immunity.',
|
||||||
suggestedParents: ['classLevel', 'feature', 'item'],
|
suggestedParents: ['classLevel', 'feature', 'item'],
|
||||||
},
|
},
|
||||||
effect: {
|
effect: {
|
||||||
icon: '$vuetify.icons.effect',
|
icon: '$vuetify.icons.effect',
|
||||||
name: 'Effect',
|
name: 'Effect',
|
||||||
|
docsPath: 'property/effect',
|
||||||
helpText: 'Effects change the value or state of attributes and skills.',
|
helpText: 'Effects change the value or state of attributes and skills.',
|
||||||
examples: '+2 Strength, Advantage on dexterity saving throws',
|
examples: '+2 Strength, Advantage on dexterity saving throws',
|
||||||
suggestedParents: ['buff', 'classLevel', 'feature', 'folder', 'item'],
|
suggestedParents: ['buff', 'classLevel', 'feature', 'folder', 'item'],
|
||||||
@@ -83,42 +96,49 @@ const PROPERTIES = Object.freeze({
|
|||||||
feature: {
|
feature: {
|
||||||
icon: 'mdi-text-subject',
|
icon: 'mdi-text-subject',
|
||||||
name: 'Feature',
|
name: 'Feature',
|
||||||
|
docsPath: 'property/feature',
|
||||||
helpText: 'Descriptive or narrative features your character has access to',
|
helpText: 'Descriptive or narrative features your character has access to',
|
||||||
suggestedParents: ['classLevel', 'folder'],
|
suggestedParents: ['classLevel', 'folder'],
|
||||||
},
|
},
|
||||||
folder: {
|
folder: {
|
||||||
icon: 'mdi-folder-outline',
|
icon: 'mdi-folder-outline',
|
||||||
name: 'Folder',
|
name: 'Folder',
|
||||||
|
docsPath: 'property/feature',
|
||||||
helpText: 'A way to organise other properties on the character',
|
helpText: 'A way to organise other properties on the character',
|
||||||
suggestedParents: ['folder'],
|
suggestedParents: ['action', 'folder'],
|
||||||
},
|
},
|
||||||
item: {
|
item: {
|
||||||
icon: 'mdi-cube-outline',
|
icon: 'mdi-cube-outline',
|
||||||
name: 'Item',
|
name: 'Item',
|
||||||
|
docsPath: 'property/item',
|
||||||
helpText: 'Objects and equipment your charcter finds on their adventures',
|
helpText: 'Objects and equipment your charcter finds on their adventures',
|
||||||
suggestedParents: ['container'],
|
suggestedParents: ['container'],
|
||||||
},
|
},
|
||||||
note: {
|
note: {
|
||||||
icon: 'mdi-note-outline',
|
icon: 'mdi-note-outline',
|
||||||
name: 'Note',
|
name: 'Note',
|
||||||
|
docsPath: 'property/note',
|
||||||
helpText: 'Notes about your character and their adventures',
|
helpText: 'Notes about your character and their adventures',
|
||||||
suggestedParents: ['note', 'folder'],
|
suggestedParents: ['note', 'folder'],
|
||||||
},
|
},
|
||||||
pointBuy: {
|
pointBuy: {
|
||||||
icon: 'mdi-table',
|
icon: 'mdi-table',
|
||||||
name: 'Point Buy',
|
name: 'Point Buy',
|
||||||
|
docsPath: 'property/point-buy',
|
||||||
helpText: 'A point buy table that allows the user to select an array of values that match a given cost',
|
helpText: 'A point buy table that allows the user to select an array of values that match a given cost',
|
||||||
suggestedParents: [],
|
suggestedParents: [],
|
||||||
},
|
},
|
||||||
proficiency: {
|
proficiency: {
|
||||||
icon: 'mdi-brightness-1',
|
icon: 'mdi-brightness-1',
|
||||||
name: 'Proficiency',
|
name: 'Proficiency',
|
||||||
|
docsPath: 'property/proficiency',
|
||||||
helpText: 'Proficiencies apply your proficiency bonus to skills already on your character sheet.',
|
helpText: 'Proficiencies apply your proficiency bonus to skills already on your character sheet.',
|
||||||
suggestedParents: ['buff', 'classLevel', 'feature', 'folder'],
|
suggestedParents: ['buff', 'classLevel', 'feature', 'folder'],
|
||||||
},
|
},
|
||||||
roll: {
|
roll: {
|
||||||
icon: '$vuetify.icons.roll',
|
icon: '$vuetify.icons.roll',
|
||||||
name: 'Roll',
|
name: 'Roll',
|
||||||
|
docsPath: 'property/roll',
|
||||||
helpText: 'When activated by an action, rolls perform a calculation and temporarily store the result for other properties under the same action to use',
|
helpText: 'When activated by an action, rolls perform a calculation and temporarily store the result for other properties under the same action to use',
|
||||||
suggestedParents: ['action', 'attack', 'savingThrow', 'spell', 'branch'],
|
suggestedParents: ['action', 'attack', 'savingThrow', 'spell', 'branch'],
|
||||||
},
|
},
|
||||||
@@ -132,48 +152,56 @@ const PROPERTIES = Object.freeze({
|
|||||||
savingThrow: {
|
savingThrow: {
|
||||||
icon: '$vuetify.icons.saving_throw',
|
icon: '$vuetify.icons.saving_throw',
|
||||||
name: 'Saving throw',
|
name: 'Saving throw',
|
||||||
|
docsPath: 'property/saving-throw',
|
||||||
helpText: 'When a saving throw is activated by an action, it causes the target to make a saving throw, if the saving throw fails, the children properties of the saving throw are activated.',
|
helpText: 'When a saving throw is activated by an action, it causes the target to make a saving throw, if the saving throw fails, the children properties of the saving throw are activated.',
|
||||||
suggestedParents: ['action', 'attack', 'spell'],
|
suggestedParents: ['action', 'attack', 'spell'],
|
||||||
},
|
},
|
||||||
skill: {
|
skill: {
|
||||||
icon: '$vuetify.icons.skill',
|
icon: '$vuetify.icons.skill',
|
||||||
name: 'Skill',
|
name: 'Skill',
|
||||||
|
docsPath: 'property/skill',
|
||||||
helpText: 'Skills, saves, languages, and weapon and tool proficiencies are all skills. Skills can have a default proficiency set. Proficiencies and effects can change the value and state of skills.',
|
helpText: 'Skills, saves, languages, and weapon and tool proficiencies are all skills. Skills can have a default proficiency set. Proficiencies and effects can change the value and state of skills.',
|
||||||
suggestedParents: ['classLevel', 'folder'],
|
suggestedParents: ['classLevel', 'folder'],
|
||||||
},
|
},
|
||||||
propertySlot: {
|
propertySlot: {
|
||||||
icon: 'mdi-power-socket-eu',
|
icon: 'mdi-power-socket-eu',
|
||||||
name: 'Slot',
|
name: 'Slot',
|
||||||
|
docsPath: 'property/slot',
|
||||||
helpText: 'A slot in the character sheet is used to specify that a property needs to be selected from a library to fill the slot. The slot can determine what tags it is looking for, and any subscribed library property with matching tags can fill the slot',
|
helpText: 'A slot in the character sheet is used to specify that a property needs to be selected from a library to fill the slot. The slot can determine what tags it is looking for, and any subscribed library property with matching tags can fill the slot',
|
||||||
suggestedParents: [],
|
suggestedParents: [],
|
||||||
},
|
},
|
||||||
slotFiller: {
|
slotFiller: {
|
||||||
icon: 'mdi-power-plug-outline',
|
icon: 'mdi-power-plug-outline',
|
||||||
name: 'Slot filler',
|
name: 'Slot filler',
|
||||||
|
docsPath: 'property/slot-filler',
|
||||||
helpText: 'A slot filler allows for more advanced logic when it attempts to fill a slot. It can masquarade as any property type, and calculate whether it should fill a slot or not.',
|
helpText: 'A slot filler allows for more advanced logic when it attempts to fill a slot. It can masquarade as any property type, and calculate whether it should fill a slot or not.',
|
||||||
suggestedParents: ['propertySlot'],
|
suggestedParents: ['propertySlot'],
|
||||||
},
|
},
|
||||||
spellList: {
|
spellList: {
|
||||||
icon: '$vuetify.icons.spell_list',
|
icon: '$vuetify.icons.spell_list',
|
||||||
name: 'Spell list',
|
name: 'Spell list',
|
||||||
|
docsPath: 'property/spell-list',
|
||||||
helpText: 'A list of spells on your character sheet. It can provide a DC and spell attack bonus to the spells within',
|
helpText: 'A list of spells on your character sheet. It can provide a DC and spell attack bonus to the spells within',
|
||||||
suggestedParents: [],
|
suggestedParents: [],
|
||||||
},
|
},
|
||||||
spell: {
|
spell: {
|
||||||
icon: '$vuetify.icons.spell',
|
icon: '$vuetify.icons.spell',
|
||||||
name: 'Spell',
|
name: 'Spell',
|
||||||
|
docsPath: 'property/spell',
|
||||||
helpText: 'A spell your character can potentially cast',
|
helpText: 'A spell your character can potentially cast',
|
||||||
suggestedParents: ['spellList'],
|
suggestedParents: ['spellList'],
|
||||||
},
|
},
|
||||||
toggle: {
|
toggle: {
|
||||||
icon: '$vuetify.icons.toggle',
|
icon: '$vuetify.icons.toggle',
|
||||||
name: 'Toggle',
|
name: 'Toggle',
|
||||||
|
docsPath: 'property/toggle',
|
||||||
helpText: 'Togggles allow parts of the character sheet to be turned on and off, either manually or as the result of a calculation.',
|
helpText: 'Togggles allow parts of the character sheet to be turned on and off, either manually or as the result of a calculation.',
|
||||||
suggestedParents: [],
|
suggestedParents: [],
|
||||||
},
|
},
|
||||||
trigger: {
|
trigger: {
|
||||||
icon: 'mdi-electric-switch',
|
icon: 'mdi-electric-switch',
|
||||||
name: 'Trigger',
|
name: 'Trigger',
|
||||||
|
docsPath: 'property/trigger',
|
||||||
helpText: 'Triggers apply their children in response to events on the character sheet, such as taking an action or receiving damage',
|
helpText: 'Triggers apply their children in response to events on the character sheet, such as taking an action or receiving damage',
|
||||||
suggestedParents: [],
|
suggestedParents: [],
|
||||||
},
|
},
|
||||||
@@ -188,3 +216,17 @@ export function getPropertyName(type){
|
|||||||
export function getPropertyIcon(type){
|
export function getPropertyIcon(type){
|
||||||
return type && PROPERTIES[type] && PROPERTIES[type].icon;
|
return type && PROPERTIES[type] && PROPERTIES[type].icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const propsByDocsPath = new Map();
|
||||||
|
|
||||||
|
for (const key in PROPERTIES) {
|
||||||
|
const prop = PROPERTIES[key];
|
||||||
|
if (prop.docsPath) {
|
||||||
|
propsByDocsPath.set(prop.docsPath, {
|
||||||
|
...prop,
|
||||||
|
type: key,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { propsByDocsPath };
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ const migrateTo = new ValidatedMethod({
|
|||||||
numRequests: 1,
|
numRequests: 1,
|
||||||
timeInterval: 10000,
|
timeInterval: 10000,
|
||||||
},
|
},
|
||||||
run({version}) {
|
run({ version }) {
|
||||||
if (Meteor.isClient) return;
|
if (Meteor.isClient) return;
|
||||||
assertAdmin(this.userId);
|
assertAdmin(this.userId);
|
||||||
Migrations.migrateTo(version);
|
Migrations.migrateTo(version);
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ const validateDatabase = new ValidatedMethod({
|
|||||||
const schema = collection.instance.simpleSchema(doc);
|
const schema = collection.instance.simpleSchema(doc);
|
||||||
let cleanDoc = schema.clean(doc);
|
let cleanDoc = schema.clean(doc);
|
||||||
try {
|
try {
|
||||||
schema.validate(cleanDoc, {modifier: false});
|
schema.validate(cleanDoc, { modifier: false });
|
||||||
} catch (e){
|
} catch (e) {
|
||||||
console.log(collection.name, doc._id, e.message || e.reason || e.toString());
|
console.log(collection.name, doc._id, e.message || e.reason || e.toString());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -112,10 +112,10 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
'resolve': {
|
'resolve': {
|
||||||
comment: 'Forces the given calcultion to resolve into a number',
|
comment: 'Forces the given calcultion to resolve into a number, even in calculations where it would usually keep the unknown values as is',
|
||||||
examples: [
|
examples: [
|
||||||
{input: 'resolve(someUndefinedVariable + 3 + 4)', result: '7'},
|
{input: 'resolve(someUndefinedVariable + 3 + 4)', result: '7'},
|
||||||
{input: 'resolve(3d6)', result: '2'},
|
{input: 'resolve(1d6)', result: '4'},
|
||||||
],
|
],
|
||||||
arguments: ['parseNode'],
|
arguments: ['parseNode'],
|
||||||
fn: function resolveFn(node){
|
fn: function resolveFn(node){
|
||||||
|
|||||||
@@ -2,23 +2,23 @@ import resolve, { traverse, toString, map } from '../resolve';
|
|||||||
import error from './error';
|
import error from './error';
|
||||||
|
|
||||||
const indexNode = {
|
const indexNode = {
|
||||||
create({array, index}) {
|
create({ array, index }) {
|
||||||
return {
|
return {
|
||||||
parseType: 'index',
|
parseType: 'index',
|
||||||
array,
|
array,
|
||||||
index,
|
index,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
resolve(fn, node, scope, context){
|
resolve(fn, node, scope, context) {
|
||||||
let {result: index} = resolve(fn, node.index, scope, context);
|
let { result: index } = resolve(fn, node.index, scope, context);
|
||||||
let {result: array} = resolve(fn, node.array, scope, context);
|
let { result: array } = resolve(fn, node.array, scope, context);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
index.valueType === 'number' &&
|
index.valueType === 'number' &&
|
||||||
Number.isInteger(index.value) &&
|
Number.isInteger(index.value) &&
|
||||||
array.parseType === 'array'
|
array.parseType === 'array'
|
||||||
){
|
) {
|
||||||
if (index.value < 1 || index.value > array.values.length){
|
if (index.value < 1 || index.value > array.values.length) {
|
||||||
context.error({
|
context.error({
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
message: `Index of ${index.value} is out of range for an array` +
|
message: `Index of ${index.value} is out of range for an array` +
|
||||||
@@ -26,11 +26,11 @@ const indexNode = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
let selection = array.values[index.value - 1];
|
let selection = array.values[index.value - 1];
|
||||||
if (selection){
|
if (selection) {
|
||||||
return resolve(fn, selection, scope, context);
|
return resolve(fn, selection, scope, context);
|
||||||
}
|
}
|
||||||
} else if (fn === 'reduce'){
|
} else if (fn === 'reduce') {
|
||||||
if (array.parseType !== 'array'){
|
if (array.parseType !== 'array') {
|
||||||
const message = `Can not get the index of a non-array node: ${toString(node.array)} = ${toString(array)}`
|
const message = `Can not get the index of a non-array node: ${toString(node.array)} = ${toString(array)}`
|
||||||
context.error(message);
|
context.error(message);
|
||||||
return {
|
return {
|
||||||
@@ -40,7 +40,7 @@ const indexNode = {
|
|||||||
}),
|
}),
|
||||||
context,
|
context,
|
||||||
};
|
};
|
||||||
} else if (!index.isInteger){
|
} else if (!index.isInteger) {
|
||||||
const message = `${toString(array)} is not an integer index of the array`
|
const message = `${toString(array)} is not an integer index of the array`
|
||||||
context.error(message);
|
context.error(message);
|
||||||
return {
|
return {
|
||||||
@@ -60,17 +60,17 @@ const indexNode = {
|
|||||||
context,
|
context,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
toString(node){
|
toString(node) {
|
||||||
return `${toString(node.array)}[${toString(node.index)}]`;
|
return `${toString(node.array)}[${toString(node.index)}]`;
|
||||||
},
|
},
|
||||||
traverse(node, fn){
|
traverse(node, fn) {
|
||||||
fn(node);
|
fn(node);
|
||||||
traverse(node.array, fn);
|
traverse(node.array, fn);
|
||||||
traverse(node.index, fn);
|
traverse(node.index, fn);
|
||||||
},
|
},
|
||||||
map(node, fn){
|
map(node, fn) {
|
||||||
const resultingNode = fn(node);
|
const resultingNode = fn(node);
|
||||||
if (resultingNode === node){
|
if (resultingNode === node) {
|
||||||
node.array = map(node.array, fn);
|
node.array = map(node.array, fn);
|
||||||
node.index = map(node.index, fn);
|
node.index = map(node.index, fn);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import constant from './constant.js';
|
import constant from './constant.js';
|
||||||
|
|
||||||
const rollArray = {
|
const rollArray = {
|
||||||
create({values, diceSize, diceNum}) {
|
create({ values, diceSize, diceNum }) {
|
||||||
return {
|
return {
|
||||||
parseType: 'rollArray',
|
parseType: 'rollArray',
|
||||||
values,
|
values,
|
||||||
@@ -9,16 +9,16 @@ const rollArray = {
|
|||||||
diceNum,
|
diceNum,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
compile(node, scope, context){
|
compile(node, scope, context) {
|
||||||
return {
|
return {
|
||||||
result: node,
|
result: node,
|
||||||
context
|
context
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
toString(node){
|
toString(node) {
|
||||||
return `${node.diceNum || ''}d${node.diceSize} [ ${node.values.join(', ')} ]`;
|
return `${node.diceNum || ''}d${node.diceSize} [ ${node.values.join(', ')} ]`;
|
||||||
},
|
},
|
||||||
reduce(node, scope, context){
|
reduce(node, scope, context) {
|
||||||
const total = node.values.reduce((a, b) => a + b, 0);
|
const total = node.values.reduce((a, b) => a + b, 0);
|
||||||
return {
|
return {
|
||||||
result: constant.create({
|
result: constant.create({
|
||||||
|
|||||||
0
app/imports/server/action.js
Normal file
0
app/imports/server/action.js
Normal file
@@ -14,15 +14,15 @@ Meteor.startup(() => {
|
|||||||
* and were not restored
|
* and were not restored
|
||||||
* @return {Number} Number of documents removed
|
* @return {Number} Number of documents removed
|
||||||
*/
|
*/
|
||||||
const deleteOldSoftRemovedDocs = function(){
|
const deleteOldSoftRemovedDocs = function () {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const yesterday = new Date(now.getTime() - (24 * 60 * 60 * 1000));
|
const yesterday = new Date(now.getTime() - (24 * 60 * 60 * 1000));
|
||||||
collections.forEach(collection => {
|
collections.forEach(collection => {
|
||||||
collection.remove({
|
collection.remove({
|
||||||
removed: true,
|
removed: true,
|
||||||
removedAt: {$lt: yesterday} // dates *before* yesterday
|
removedAt: { $lt: yesterday } // dates *before* yesterday
|
||||||
}, function(error){
|
}, function (error) {
|
||||||
if (error){
|
if (error) {
|
||||||
console.error(JSON.stringify(error, null, 2));
|
console.error(JSON.stringify(error, null, 2));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -31,7 +31,7 @@ Meteor.startup(() => {
|
|||||||
|
|
||||||
SyncedCron.add({
|
SyncedCron.add({
|
||||||
name: 'deleteSoftRemovedDocs',
|
name: 'deleteSoftRemovedDocs',
|
||||||
schedule: function(parser) {
|
schedule: function (parser) {
|
||||||
return parser.text('every 10 minutes');
|
return parser.text('every 10 minutes');
|
||||||
},
|
},
|
||||||
job: deleteOldSoftRemovedDocs,
|
job: deleteOldSoftRemovedDocs,
|
||||||
|
|||||||
34
app/imports/server/publications/docs.js
Normal file
34
app/imports/server/publications/docs.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { propsByDocsPath } from '/imports/constants/PROPERTIES.js';
|
||||||
|
|
||||||
|
|
||||||
|
// Manual doc paths
|
||||||
|
const docPaths = [
|
||||||
|
'computed-fields',
|
||||||
|
'inline-calculations',
|
||||||
|
'dependency-loops',
|
||||||
|
'docs',
|
||||||
|
'tags',
|
||||||
|
];
|
||||||
|
const docs = new Map();
|
||||||
|
docPaths.forEach(path => {
|
||||||
|
docs.set(path, Assets.getText(`docs/${path}.md`))
|
||||||
|
});
|
||||||
|
|
||||||
|
// Doc paths for properties
|
||||||
|
propsByDocsPath.forEach(prop => {
|
||||||
|
docs.set(prop.docsPath, Assets.getText(`docs/${prop.docsPath}.md`));
|
||||||
|
});
|
||||||
|
|
||||||
|
Meteor.publish('docs', function (path) {
|
||||||
|
if (!path) {
|
||||||
|
docs.forEach((text, path) => {
|
||||||
|
this.added('docs', path, { text });
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const text = docs.get(path);
|
||||||
|
if (text) {
|
||||||
|
this.added('docs', path, { text });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.ready();
|
||||||
|
});
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
import Icons from '/imports/api/icons/Icons.js';
|
import Icons from '/imports/api/icons/Icons.js';
|
||||||
|
|
||||||
Meteor.publish('sampleIcons', function(){
|
Meteor.publish('sampleIcons', function () {
|
||||||
return Icons.find({}, {limit: 50});
|
return Icons.find({}, { limit: 50 });
|
||||||
});
|
});
|
||||||
|
|
||||||
Meteor.publish('searchIcons', function(searchValue) {
|
Meteor.publish('searchIcons', function (searchValue) {
|
||||||
// Don't publish anything if there's no search value
|
// Don't publish anything if there's no search value
|
||||||
if (!searchValue) {
|
if (!searchValue) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
return Icons.find(
|
return Icons.find(
|
||||||
{ $text: {$search: searchValue} },
|
{ $text: { $search: searchValue } },
|
||||||
{
|
{
|
||||||
// relevant documents have a higher score.
|
// relevant documents have a higher score.
|
||||||
fields: {
|
fields: {
|
||||||
|
|||||||
@@ -11,3 +11,4 @@ import '/imports/server/publications/ownedDocuments.js';
|
|||||||
import '/imports/server/publications/searchLibraryNodes.js';
|
import '/imports/server/publications/searchLibraryNodes.js';
|
||||||
import '/imports/server/publications/archiveFiles.js';
|
import '/imports/server/publications/archiveFiles.js';
|
||||||
import '/imports/server/publications/userImages.js';
|
import '/imports/server/publications/userImages.js';
|
||||||
|
import '/imports/server/publications/docs.js';
|
||||||
|
|||||||
@@ -2,9 +2,10 @@ import SimpleSchema from 'simpl-schema';
|
|||||||
import '/imports/api/users/Users.js';
|
import '/imports/api/users/Users.js';
|
||||||
import Invites from '/imports/api/users/Invites.js';
|
import Invites from '/imports/api/users/Invites.js';
|
||||||
|
|
||||||
Meteor.publish('user', function(){
|
Meteor.publish('user', function () {
|
||||||
return [
|
return [
|
||||||
Meteor.users.find(this.userId, {fields: {
|
Meteor.users.find(this.userId, {
|
||||||
|
fields: {
|
||||||
roles: 1,
|
roles: 1,
|
||||||
username: 1,
|
username: 1,
|
||||||
apiKey: 1,
|
apiKey: 1,
|
||||||
@@ -22,11 +23,12 @@ Meteor.publish('user', function(){
|
|||||||
'services.google.name': 1,
|
'services.google.name': 1,
|
||||||
'services.google.email': 1,
|
'services.google.email': 1,
|
||||||
'services.google.locale': 1,
|
'services.google.locale': 1,
|
||||||
}}),
|
}
|
||||||
|
}),
|
||||||
Invites.find({
|
Invites.find({
|
||||||
$or: [
|
$or: [
|
||||||
{inviter: this.userId},
|
{ inviter: this.userId },
|
||||||
{invitee: this.userId}
|
{ invitee: this.userId }
|
||||||
],
|
],
|
||||||
}, {
|
}, {
|
||||||
fields: {
|
fields: {
|
||||||
@@ -41,19 +43,19 @@ let userIdsSchema = new SimpleSchema({
|
|||||||
type: Array,
|
type: Array,
|
||||||
optional: true,
|
optional: true,
|
||||||
},
|
},
|
||||||
'ids.$':{
|
'ids.$': {
|
||||||
type: String,
|
type: String,
|
||||||
regEx: SimpleSchema.RegEx.Id,
|
regEx: SimpleSchema.RegEx.Id,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
Meteor.publish('userPublicProfiles', function(ids){
|
Meteor.publish('userPublicProfiles', function (ids) {
|
||||||
userIdsSchema.validate({ids});
|
userIdsSchema.validate({ ids });
|
||||||
if (!this.userId || !ids) return this.ready();
|
if (!this.userId || !ids) return this.ready();
|
||||||
return Meteor.users.find({
|
return Meteor.users.find({
|
||||||
_id: {$in: ids}
|
_id: { $in: ids }
|
||||||
},{
|
}, {
|
||||||
fields: {username: 1},
|
fields: { username: 1 },
|
||||||
sort: {username: 1},
|
sort: { username: 1 },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -19,34 +19,39 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css">
|
<style lang="css">
|
||||||
.column-layout {
|
.column-layout {
|
||||||
column-count: 12;
|
column-count: 12;
|
||||||
column-fill: balance;
|
column-fill: balance;
|
||||||
column-gap: 0;
|
column-gap: 0;
|
||||||
column-width: 240px;
|
column-width: 240px;
|
||||||
transform: translateZ(0);
|
transform: translateZ(0);
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
}
|
}
|
||||||
.column-layout.wide-columns {
|
|
||||||
|
.column-layout.wide-columns {
|
||||||
column-count: 12;
|
column-count: 12;
|
||||||
column-fill: balance;
|
column-fill: balance;
|
||||||
column-gap: 0;
|
column-gap: 0;
|
||||||
column-width: 320px;
|
column-width: 320px;
|
||||||
transform: translateZ(0);
|
transform: translateZ(0);
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
}
|
}
|
||||||
.column-layout > div, .column-layout > span > 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
|
||||||
*/
|
*/
|
||||||
display: table;
|
display: table;
|
||||||
table-layout: fixed;
|
table-layout: fixed;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
backface-visibility: hidden;
|
||||||
-webkit-backface-visibility: hidden;
|
-webkit-backface-visibility: hidden;
|
||||||
|
transform: translateX(0);
|
||||||
-webkit-transform: translateX(0);
|
-webkit-transform: translateX(0);
|
||||||
-webkit-column-break-inside: avoid;
|
-webkit-column-break-inside: avoid;
|
||||||
page-break-inside: avoid;
|
page-break-inside: avoid;
|
||||||
break-inside: avoid;
|
break-inside: avoid;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="js">
|
<script lang="js">
|
||||||
export default {
|
export default {
|
||||||
inject: {
|
inject: {
|
||||||
context: { default: {} }
|
context: { default: {} }
|
||||||
},
|
},
|
||||||
@@ -93,12 +93,12 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
resetData(){
|
resetData() {
|
||||||
this.editValue = this.value;
|
this.editValue = this.value;
|
||||||
this.operation = 'set';
|
this.operation = 'set';
|
||||||
// this.$nextTick didn't work, using timeout instead did
|
// this.$nextTick didn't work, using timeout instead did
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (this.$refs.editInput){
|
if (this.$refs.editInput) {
|
||||||
this.$refs.editInput.focus();
|
this.$refs.editInput.focus();
|
||||||
}
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
@@ -125,11 +125,11 @@
|
|||||||
return 'mdi-minus';
|
return 'mdi-minus';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
toggleAdd(){
|
toggleAdd() {
|
||||||
this.operation = (this.operation === 'add') ? 'set': 'add';
|
this.operation = (this.operation === 'add') ? 'set' : 'add';
|
||||||
},
|
},
|
||||||
toggleSubtract(){
|
toggleSubtract() {
|
||||||
this.operation = (this.operation === 'subtract') ? 'set': 'subtract';
|
this.operation = (this.operation === 'subtract') ? 'set' : 'subtract';
|
||||||
},
|
},
|
||||||
keypress(event) {
|
keypress(event) {
|
||||||
let digitsOnly = /[0-9]/;
|
let digitsOnly = /[0-9]/;
|
||||||
@@ -142,25 +142,26 @@
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
} else if (key === 'Enter') {
|
} else if (key === 'Enter') {
|
||||||
this.commitEdit();
|
this.commitEdit();
|
||||||
} else if (!digitsOnly.test(key)){
|
} else if (!digitsOnly.test(key)) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
input(value){
|
input(value) {
|
||||||
if (+value < 0){
|
if (+value < 0) {
|
||||||
this.editValue = -value;
|
this.editValue = -value;
|
||||||
this.operation = 'subtract';
|
this.operation = 'subtract';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.filled.theme--light {
|
.filled.theme--light {
|
||||||
background: #fff !important;
|
background: #fff !important;
|
||||||
}
|
}
|
||||||
.filled.theme--dark {
|
|
||||||
|
.filled.theme--dark {
|
||||||
background: #424242 !important;
|
background: #424242 !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
<template lang="html">
|
<template lang="html">
|
||||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
<!-- eslint-disable vue/no-v-html -->
|
||||||
<div
|
<div
|
||||||
class="markdown"
|
class="markdown"
|
||||||
|
@click="e => $emit('click', e)"
|
||||||
v-html="compiledMarkdown"
|
v-html="compiledMarkdown"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="js">
|
<script lang="js">
|
||||||
import { marked } from 'marked';
|
import { marked } from 'marked';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
markdown: {
|
markdown: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -22,5 +23,5 @@
|
|||||||
return marked(this.markdown);
|
return marked(this.markdown);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -27,54 +27,58 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="js">
|
<script lang="js">
|
||||||
import isDarkColor from '/imports/ui/utility/isDarkColor.js';
|
import isDarkColor from '/imports/ui/utility/isDarkColor.js';
|
||||||
import getThemeColor from '/imports/ui/utility/getThemeColor.js';
|
import getThemeColor from '/imports/ui/utility/getThemeColor.js';
|
||||||
import CardHighlight from '/imports/ui/components/CardHighlight.vue';
|
import CardHighlight from '/imports/ui/components/CardHighlight.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
CardHighlight,
|
CardHighlight,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
color: {
|
color: {
|
||||||
type: String,
|
type: String,
|
||||||
default(){
|
default() {
|
||||||
return getThemeColor('secondary');
|
return getThemeColor('secondary');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
transparentToolbar: Boolean,
|
transparentToolbar: Boolean,
|
||||||
},
|
},
|
||||||
data(){ return {
|
data() {
|
||||||
|
return {
|
||||||
hovering: false,
|
hovering: false,
|
||||||
}},
|
}
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isDark(){
|
isDark() {
|
||||||
return isDarkColor(this.color);
|
return isDarkColor(this.color);
|
||||||
},
|
},
|
||||||
hasClickListener(){
|
hasClickListener() {
|
||||||
return this.$listeners && !!this.$listeners.click;
|
return this.$listeners && !!this.$listeners.click;
|
||||||
},
|
},
|
||||||
hasToolbarClickListener(){
|
hasToolbarClickListener() {
|
||||||
return this.$listeners && !!this.$listeners.toolbarclick;
|
return this.$listeners && !!this.$listeners.toolbarclick;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
hoverToolbar(val){
|
hoverToolbar(val) {
|
||||||
this.hovering = this.$listeners &&
|
this.hovering = this.$listeners &&
|
||||||
!!this.$listeners.toolbarclick &&
|
!!this.$listeners.toolbarclick &&
|
||||||
val;
|
val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css">
|
<style lang="css">
|
||||||
.toolbar-card .v-toolbar__title {
|
.toolbar-card .v-toolbar__title {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toolbar-card {
|
.toolbar-card {
|
||||||
transition: box-shadow .4s cubic-bezier(0.25, 0.8, 0.25, 1);
|
transition: box-shadow .4s cubic-bezier(0.25, 0.8, 0.25, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.toolbar-card.transparent-toolbar .theme--dark.v-toolbar.v-sheet {
|
.toolbar-card.transparent-toolbar .theme--dark.v-toolbar.v-sheet {
|
||||||
background-color: #303030;
|
background-color: #303030;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,16 +35,18 @@ import { format } from 'date-fns';
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [SmartInput],
|
mixins: [SmartInput],
|
||||||
data(){return {
|
data() {
|
||||||
|
return {
|
||||||
menu: false,
|
menu: false,
|
||||||
};},
|
};
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
formattedSafeValue(){
|
formattedSafeValue() {
|
||||||
return format(this.safeValue, 'YYYY-MM-DD')
|
return format(this.safeValue, 'YYYY-MM-DD')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
dateInput(e){
|
dateInput(e) {
|
||||||
this.menu = false;
|
this.menu = false;
|
||||||
this.input(e);
|
this.input(e);
|
||||||
},
|
},
|
||||||
@@ -53,4 +55,5 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css" scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -97,16 +97,18 @@ export default {
|
|||||||
default: undefined,
|
default: undefined,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data(){return {
|
data() {
|
||||||
|
return {
|
||||||
menu: false,
|
menu: false,
|
||||||
searchString: '',
|
searchString: '',
|
||||||
icons: [],
|
icons: [],
|
||||||
};},
|
};
|
||||||
|
},
|
||||||
watch: {
|
watch: {
|
||||||
menu(value){
|
menu(value) {
|
||||||
if (value){
|
if (value) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (this.$refs.iconSearchField){
|
if (this.$refs.iconSearchField) {
|
||||||
this.$refs.iconSearchField.$children[0].focus();
|
this.$refs.iconSearchField.$children[0].focus();
|
||||||
}
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
@@ -114,15 +116,15 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
search(value, ack){
|
search(value, ack) {
|
||||||
this.searchString = value;
|
this.searchString = value;
|
||||||
this.icons = [];
|
this.icons = [];
|
||||||
findIcons.call({search: value}, (error, result) => {
|
findIcons.call({ search: value }, (error, result) => {
|
||||||
ack(error);
|
ack(error);
|
||||||
this.icons = result;
|
this.icons = result;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
select(icon){
|
select(icon) {
|
||||||
this.menu = false;
|
this.menu = false;
|
||||||
this.change(icon);
|
this.change(icon);
|
||||||
},
|
},
|
||||||
@@ -131,4 +133,5 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css" scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -21,23 +21,25 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="js">
|
<script lang="js">
|
||||||
import SmartInput from '/imports/ui/components/global/SmartInputMixin.js';
|
import SmartInput from '/imports/ui/components/global/SmartInputMixin.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [SmartInput],
|
mixins: [SmartInput],
|
||||||
props: {
|
props: {
|
||||||
multiple: Boolean,
|
multiple: Boolean,
|
||||||
},
|
},
|
||||||
data(){ return {
|
data() {
|
||||||
|
return {
|
||||||
searchInput: '',
|
searchInput: '',
|
||||||
}},
|
}
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
// Multiple combobox gets a long default debounce time while single
|
// Multiple combobox gets a long default debounce time while single
|
||||||
// value gets a shorter one
|
// value gets a shorter one
|
||||||
debounceTime() {
|
debounceTime() {
|
||||||
if (Number.isFinite(this.debounce)){
|
if (Number.isFinite(this.debounce)) {
|
||||||
return this.debounce;
|
return this.debounce;
|
||||||
} else if (Number.isFinite(this.context.debounceTime)){
|
} else if (Number.isFinite(this.context.debounceTime)) {
|
||||||
return this.context.debounceTime;
|
return this.context.debounceTime;
|
||||||
} else {
|
} else {
|
||||||
return this.multiple ? 1000 : 100;
|
return this.multiple ? 1000 : 100;
|
||||||
@@ -45,10 +47,10 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
customChange(val){
|
customChange(val) {
|
||||||
this.input(val);
|
this.input(val);
|
||||||
this.searchInput = '';
|
this.searchInput = '';
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ export default {
|
|||||||
context: { default: {} }
|
context: { default: {} }
|
||||||
},
|
},
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
data(){ return {
|
data() {
|
||||||
|
return {
|
||||||
error: false,
|
error: false,
|
||||||
ackErrors: null,
|
ackErrors: null,
|
||||||
rulesErrors: null,
|
rulesErrors: null,
|
||||||
@@ -22,7 +23,8 @@ export default {
|
|||||||
dirty: false,
|
dirty: false,
|
||||||
safeValue: this.value,
|
safeValue: this.value,
|
||||||
inputValue: this.value,
|
inputValue: this.value,
|
||||||
};},
|
};
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
value: [String, Number, Date, Array, Object, Boolean],
|
value: [String, Number, Date, Array, Object, Boolean],
|
||||||
errorMessages: [String, Array],
|
errorMessages: [String, Array],
|
||||||
@@ -34,11 +36,11 @@ export default {
|
|||||||
rules: Array,
|
rules: Array,
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
focused(newFocus){
|
focused(newFocus) {
|
||||||
// If the value updated while we were focused, show it now on defocus
|
// If the value updated while we were focused, show it now on defocus
|
||||||
// but not if we are waiting for our own writes to get persisted
|
// but not if we are waiting for our own writes to get persisted
|
||||||
// and not if there is an error in our input
|
// and not if there is an error in our input
|
||||||
if (!newFocus && !this.dirty && !this.error){
|
if (!newFocus && !this.dirty && !this.error) {
|
||||||
this.forceSafeValueUpdate();
|
this.forceSafeValueUpdate();
|
||||||
}
|
}
|
||||||
// Start the loading bar on defocus if the input is dirty
|
// Start the loading bar on defocus if the input is dirty
|
||||||
@@ -48,118 +50,118 @@ export default {
|
|||||||
!newFocus &&
|
!newFocus &&
|
||||||
this.dirty &&
|
this.dirty &&
|
||||||
!(this.rulesErrors && this.rulesErrors.length)
|
!(this.rulesErrors && this.rulesErrors.length)
|
||||||
){
|
) {
|
||||||
if (this.hasChangeListener) this.loading = true;
|
if (this.hasChangeListener) this.loading = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dirty(newDirty){
|
dirty(newDirty) {
|
||||||
// Our changes were acknowledged, weren't in error, and we aren't focused,
|
// Our changes were acknowledged, weren't in error, and we aren't focused,
|
||||||
// make sure the internal value matches the database value
|
// make sure the internal value matches the database value
|
||||||
if (!newDirty && !this.focused && !this.error){
|
if (!newDirty && !this.focused && !this.error) {
|
||||||
this.forceSafeValueUpdate();
|
this.forceSafeValueUpdate();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
value(newValue){
|
value(newValue) {
|
||||||
if (
|
if (
|
||||||
!this.focused &&
|
!this.focused &&
|
||||||
!(this.rulesErrors && this.rulesErrors.length)
|
!(this.rulesErrors && this.rulesErrors.length)
|
||||||
){
|
) {
|
||||||
this.safeValue = newValue;
|
this.safeValue = newValue;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
safeValue(){
|
safeValue() {
|
||||||
// The safe value only gets updated from the parent, so it must be valid
|
// The safe value only gets updated from the parent, so it must be valid
|
||||||
this.error = false;
|
this.error = false;
|
||||||
this.ackErrors = null;
|
this.ackErrors = null;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
input(val){
|
input(val) {
|
||||||
this.$emit('input', val);
|
this.$emit('input', val);
|
||||||
this.inputValue = val;
|
this.inputValue = val;
|
||||||
this.dirty = true;
|
this.dirty = true;
|
||||||
|
|
||||||
// Apply the rules if there are any
|
// Apply the rules if there are any
|
||||||
this.rulesErrors = null;
|
this.rulesErrors = null;
|
||||||
if (this.rules && this.rules.length){
|
if (this.rules && this.rules.length) {
|
||||||
this.rules.forEach(rule => {
|
this.rules.forEach(rule => {
|
||||||
const result = rule(val);
|
const result = rule(val);
|
||||||
if (typeof result === 'string'){
|
if (typeof result === 'string') {
|
||||||
if (!this.rulesErrors) this.rulesErrors = [];
|
if (!this.rulesErrors) this.rulesErrors = [];
|
||||||
this.rulesErrors.push(result);
|
this.rulesErrors.push(result);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (this.rulesErrors){
|
if (this.rulesErrors) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.debouncedChange(val);
|
this.debouncedChange(val);
|
||||||
},
|
},
|
||||||
acknowledgeChange(error){
|
acknowledgeChange(error) {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
this.dirty = false;
|
this.dirty = false;
|
||||||
this.error = !!error;
|
this.error = !!error;
|
||||||
if (!error){
|
if (!error) {
|
||||||
this.ackErrors = null;
|
this.ackErrors = null;
|
||||||
} else if (typeof error === 'string'){
|
} else if (typeof error === 'string') {
|
||||||
this.ackErrors = error;
|
this.ackErrors = error;
|
||||||
} else if (error.reason){
|
} else if (error.reason) {
|
||||||
this.ackErrors = error.reason;
|
this.ackErrors = error.reason;
|
||||||
} else if (error.message){
|
} else if (error.message) {
|
||||||
this.ackErrors = error.message;
|
this.ackErrors = error.message;
|
||||||
} else {
|
} else {
|
||||||
this.ackErrors = 'Something went wrong'
|
this.ackErrors = 'Something went wrong'
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
change(val){
|
change(val) {
|
||||||
this.dirty = true;
|
this.dirty = true;
|
||||||
if (this.hasChangeListener()) this.loading = true;
|
if (this.hasChangeListener()) this.loading = true;
|
||||||
this.$emit('change', val, this.acknowledgeChange);
|
this.$emit('change', val, this.acknowledgeChange);
|
||||||
},
|
},
|
||||||
hasChangeListener(){
|
hasChangeListener() {
|
||||||
return this.$listeners && this.$listeners.change;
|
return this.$listeners && this.$listeners.change;
|
||||||
},
|
},
|
||||||
forceSafeValueUpdate(){
|
forceSafeValueUpdate() {
|
||||||
// hack to force the value to update on the child component
|
// hack to force the value to update on the child component
|
||||||
this.safeValue = null;
|
this.safeValue = null;
|
||||||
this.$nextTick(() => this.safeValue = this.value);
|
this.$nextTick(() => this.safeValue = this.value);
|
||||||
},
|
},
|
||||||
focus(){
|
focus() {
|
||||||
this.$refs.input.focus();
|
this.$refs.input.focus();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
errors(){
|
errors() {
|
||||||
let errors = this.ackErrors ? [this.ackErrors] : [];
|
let errors = this.ackErrors ? [this.ackErrors] : [];
|
||||||
if (Array.isArray(this.rulesErrors)){
|
if (Array.isArray(this.rulesErrors)) {
|
||||||
errors.push(...this.rulesErrors)
|
errors.push(...this.rulesErrors)
|
||||||
}
|
}
|
||||||
if (Array.isArray(this.errorMessages)){
|
if (Array.isArray(this.errorMessages)) {
|
||||||
errors.push(...this.errorMessages);
|
errors.push(...this.errorMessages);
|
||||||
} else if (typeof this.errorMessages === 'string' && this.errorMessages){
|
} else if (typeof this.errorMessages === 'string' && this.errorMessages) {
|
||||||
errors.push(this.errorMessages);
|
errors.push(this.errorMessages);
|
||||||
}
|
}
|
||||||
return errors;
|
return errors;
|
||||||
},
|
},
|
||||||
isDisabled(){
|
isDisabled() {
|
||||||
return this.context.editPermission === false || this.disabled;
|
return this.context.editPermission === false || this.disabled;
|
||||||
},
|
},
|
||||||
debounceTime() {
|
debounceTime() {
|
||||||
if (Number.isFinite(this.debounce)){
|
if (Number.isFinite(this.debounce)) {
|
||||||
return this.debounce;
|
return this.debounce;
|
||||||
} else if (Number.isFinite(this.context.debounceTime)){
|
} else if (Number.isFinite(this.context.debounceTime)) {
|
||||||
return this.context.debounceTime;
|
return this.context.debounceTime;
|
||||||
} else {
|
} else {
|
||||||
return 750;
|
return 750;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
created(){
|
created() {
|
||||||
this.debouncedChange = debounce(this.change, this.debounceTime);
|
this.debouncedChange = debounce(this.change, this.debounceTime);
|
||||||
},
|
},
|
||||||
beforeDestroy(){
|
beforeDestroy() {
|
||||||
this.debouncedChange.flush();
|
this.debouncedChange.flush();
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,9 +23,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="js">
|
<script lang="js">
|
||||||
import SmartInput from '/imports/ui/components/global/SmartInputMixin.js';
|
import SmartInput from '/imports/ui/components/global/SmartInputMixin.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [SmartInput],
|
mixins: [SmartInput],
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -14,9 +14,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="js">
|
<script lang="js">
|
||||||
import SmartInput from '/imports/ui/components/global/SmartInputMixin.js';
|
import SmartInput from '/imports/ui/components/global/SmartInputMixin.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [SmartInput],
|
mixins: [SmartInput],
|
||||||
props: {
|
props: {
|
||||||
autoGrow: {
|
autoGrow: {
|
||||||
@@ -24,5 +24,5 @@
|
|||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -54,6 +54,19 @@
|
|||||||
</v-btn>
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
<v-list>
|
<v-list>
|
||||||
|
<v-list-item
|
||||||
|
v-if="docsPath"
|
||||||
|
@click="helpDialog"
|
||||||
|
>
|
||||||
|
<v-list-item-content>
|
||||||
|
<v-list-item-title>
|
||||||
|
Help
|
||||||
|
</v-list-item-title>
|
||||||
|
</v-list-item-content>
|
||||||
|
<v-list-item-action>
|
||||||
|
<v-icon>mdi-help</v-icon>
|
||||||
|
</v-list-item-action>
|
||||||
|
</v-list-item>
|
||||||
<v-list-item
|
<v-list-item
|
||||||
v-if="$listeners && $listeners.duplicate"
|
v-if="$listeners && $listeners.duplicate"
|
||||||
@click="$emit('duplicate')"
|
@click="$emit('duplicate')"
|
||||||
@@ -137,6 +150,7 @@ import PropertyIcon from '/imports/ui/properties/shared/PropertyIcon.vue';
|
|||||||
import { getPropertyName } from '/imports/constants/PROPERTIES.js';
|
import { getPropertyName } from '/imports/constants/PROPERTIES.js';
|
||||||
import ColorPicker from '/imports/ui/components/ColorPicker.vue';
|
import ColorPicker from '/imports/ui/components/ColorPicker.vue';
|
||||||
import getThemeColor from '/imports/ui/utility/getThemeColor.js';
|
import getThemeColor from '/imports/ui/utility/getThemeColor.js';
|
||||||
|
import PROPERTIES from '/imports/constants/PROPERTIES.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -171,7 +185,11 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return model.name || getPropertyName(model.type);
|
return model.name || getPropertyName(model.type);
|
||||||
}
|
},
|
||||||
|
docsPath() {
|
||||||
|
const propDef = PROPERTIES[this.model.type];
|
||||||
|
return propDef && propDef.docsPath;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
colorChanged(value){
|
colorChanged(value){
|
||||||
@@ -180,6 +198,15 @@ export default {
|
|||||||
back(){
|
back(){
|
||||||
this.$store.dispatch('popDialogStack');
|
this.$store.dispatch('popDialogStack');
|
||||||
},
|
},
|
||||||
|
helpDialog() {
|
||||||
|
this.$store.commit('pushDialogStack', {
|
||||||
|
component: 'help-dialog',
|
||||||
|
elementId: 'property-toolbar-menu-button',
|
||||||
|
data: {
|
||||||
|
path: this.docsPath,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -77,18 +77,18 @@
|
|||||||
</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 { canBeParent } from '/imports/api/parenting/parenting.js';
|
import { canBeParent } from '/imports/api/parenting/parenting.js';
|
||||||
import { getPropertyIcon } from '/imports/constants/PROPERTIES.js';
|
import { getPropertyIcon } from '/imports/constants/PROPERTIES.js';
|
||||||
import TreeNodeView from '/imports/ui/properties/treeNodeViews/TreeNodeView.vue';
|
import TreeNodeView from '/imports/ui/properties/treeNodeViews/TreeNodeView.vue';
|
||||||
import { some } from 'lodash';
|
import { some } from 'lodash';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'TreeNode',
|
name: 'TreeNode',
|
||||||
components: {
|
components: {
|
||||||
TreeNodeView,
|
TreeNodeView,
|
||||||
@@ -100,7 +100,7 @@
|
|||||||
},
|
},
|
||||||
group: {
|
group: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
default: undefined,
|
||||||
},
|
},
|
||||||
organize: Boolean,
|
organize: Boolean,
|
||||||
children: {
|
children: {
|
||||||
@@ -118,38 +118,40 @@
|
|||||||
selected: Boolean,
|
selected: Boolean,
|
||||||
startExpanded: Boolean,
|
startExpanded: Boolean,
|
||||||
},
|
},
|
||||||
data(){return {
|
data() {
|
||||||
|
return {
|
||||||
expanded: this.startExpanded || this.node._ancestorOfMatchedDocument ||
|
expanded: this.startExpanded || this.node._ancestorOfMatchedDocument ||
|
||||||
some(this.selectedNode?.ancestors, ref => ref.id === this.node._id) ||
|
some(this.selectedNode?.ancestors, ref => ref.id === this.node._id) ||
|
||||||
false,
|
false,
|
||||||
}},
|
}
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
hasChildren(){
|
hasChildren() {
|
||||||
return this.children && !!this.children.length || this.lazy && !this.expanded;
|
return this.children && !!this.children.length || this.lazy && !this.expanded;
|
||||||
},
|
},
|
||||||
showExpanded(){
|
showExpanded() {
|
||||||
return this.expanded && (this.organize || this.hasChildren)
|
return this.expanded && (this.organize || this.hasChildren)
|
||||||
},
|
},
|
||||||
computedChildren(){
|
computedChildren() {
|
||||||
let children = [];
|
let children = [];
|
||||||
if (this.children){
|
if (this.children) {
|
||||||
children.push(...this.children)
|
children.push(...this.children)
|
||||||
}
|
}
|
||||||
if (this.getChildren){
|
if (this.getChildren) {
|
||||||
children.push(...this.getChildren())
|
children.push(...this.getChildren())
|
||||||
}
|
}
|
||||||
return children;
|
return children;
|
||||||
},
|
},
|
||||||
canExpand(){
|
canExpand() {
|
||||||
return canBeParent(this.node.type);
|
return canBeParent(this.node.type);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
'node._ancestorOfMatchedDocument'(value){
|
'node._ancestorOfMatchedDocument'(value) {
|
||||||
this.expanded = !!value ||
|
this.expanded = !!value ||
|
||||||
some(this.selectedNode?.ancestors, ref => ref.id === this.node._id);
|
some(this.selectedNode?.ancestors, ref => ref.id === this.node._id);
|
||||||
},
|
},
|
||||||
'selectedNode.ancestors'(value){
|
'selectedNode.ancestors'(value) {
|
||||||
this.expanded = !!some(value, ref => ref.id === this.node._id) || this.expanded;
|
this.expanded = !!some(value, ref => ref.id === this.node._id) || this.expanded;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -157,54 +159,67 @@
|
|||||||
this.$options.components.TreeNodeList = require('./TreeNodeList.vue').default
|
this.$options.components.TreeNodeList = require('./TreeNodeList.vue').default
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
icon(type){
|
icon(type) {
|
||||||
return getPropertyIcon(type);
|
return getPropertyIcon(type);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css" scoped>
|
||||||
.rotate-90 {
|
.rotate-90 {
|
||||||
transform: rotate(90deg) translateZ(0);
|
transform: rotate(90deg) translateZ(0);
|
||||||
}
|
}
|
||||||
.drag-area {
|
|
||||||
|
.drag-area {
|
||||||
box-shadow: -2px 0px 0px 0px #808080;
|
box-shadow: -2px 0px 0px 0px #808080;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
min-height: 32px;
|
min-height: 32px;
|
||||||
}
|
}
|
||||||
.handle {
|
|
||||||
|
.handle {
|
||||||
cursor: move;
|
cursor: move;
|
||||||
}
|
}
|
||||||
.empty .drag-area {
|
|
||||||
|
.empty .drag-area {
|
||||||
box-shadow: -2px 0px 0px 0px rgb(128, 128, 128, 0.4);
|
box-shadow: -2px 0px 0px 0px rgb(128, 128, 128, 0.4);
|
||||||
}
|
}
|
||||||
.empty .v-btn {
|
|
||||||
|
.empty .v-btn {
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
}
|
}
|
||||||
.found {
|
|
||||||
|
.found {
|
||||||
background: rgba(200, 0, 0, 0.1) !important;
|
background: rgba(200, 0, 0, 0.1) !important;
|
||||||
}
|
}
|
||||||
.ghost {
|
|
||||||
|
.ghost {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
background: rgba(251, 0, 0, 0.3);
|
background: rgba(251, 0, 0, 0.3);
|
||||||
}
|
}
|
||||||
.v-icon.v-icon--disabled {
|
|
||||||
|
.v-icon.v-icon--disabled {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
.v-icon {
|
|
||||||
|
.v-icon {
|
||||||
transition: none !important;
|
transition: none !important;
|
||||||
}
|
}
|
||||||
.theme--light .tree-node-title:hover {
|
|
||||||
background-color: rgba(0,0,0,.04);
|
.theme--light .tree-node-title:hover {
|
||||||
}
|
background-color: rgba(0, 0, 0, .04);
|
||||||
.theme--dark .tree-node-title:hover {
|
}
|
||||||
background-color: rgba(255,255,255,.04);
|
|
||||||
}
|
.theme--dark .tree-node-title:hover {
|
||||||
.tree-node-title{
|
background-color: rgba(255, 255, 255, .04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-node-title {
|
||||||
transition: background ease 0.3s, color ease 0.15s;
|
transition: background ease 0.3s, color ease 0.15s;
|
||||||
}
|
}
|
||||||
.tree-node-title, .dummy-node {
|
|
||||||
|
.tree-node-title,
|
||||||
|
.dummy-node {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -33,18 +33,24 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="js">
|
<script lang="js">
|
||||||
import draggable from 'vuedraggable';
|
import draggable from 'vuedraggable';
|
||||||
import TreeNode from '/imports/ui/components/tree/TreeNode.vue';
|
import TreeNode from '/imports/ui/components/tree/TreeNode.vue';
|
||||||
import { isParentAllowed } from '/imports/api/parenting/parenting.js';
|
import { isParentAllowed } from '/imports/api/parenting/parenting.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
draggable,
|
draggable,
|
||||||
TreeNode,
|
TreeNode,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
node: Object,
|
node: {
|
||||||
group: String,
|
type: Object,
|
||||||
|
default: undefined,
|
||||||
|
},
|
||||||
|
group: {
|
||||||
|
type: String,
|
||||||
|
default: undefined,
|
||||||
|
},
|
||||||
organize: Boolean,
|
organize: Boolean,
|
||||||
lazy: Boolean,
|
lazy: Boolean,
|
||||||
children: {
|
children: {
|
||||||
@@ -61,39 +67,41 @@
|
|||||||
},
|
},
|
||||||
startExpanded: Boolean,
|
startExpanded: Boolean,
|
||||||
},
|
},
|
||||||
data(){ return {
|
data() {
|
||||||
|
return {
|
||||||
expanded: this.startExpanded || false,
|
expanded: this.startExpanded || false,
|
||||||
displayedChildren: [],
|
displayedChildren: [],
|
||||||
}},
|
}
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
hasChildren(){
|
hasChildren() {
|
||||||
return this.children && this.children.length;
|
return this.children && this.children.length;
|
||||||
},
|
},
|
||||||
showExpanded(){
|
showExpanded() {
|
||||||
return this.expanded && (this.organize || this.hasChildren)
|
return this.expanded && (this.organize || this.hasChildren)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
children(value){
|
children(value) {
|
||||||
this.displayedChildren = value;
|
this.displayedChildren = value;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted(){
|
mounted() {
|
||||||
this.displayedChildren = this.children;
|
this.displayedChildren = this.children;
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
change({added, moved}){
|
change({ added, moved }) {
|
||||||
let event = moved || added;
|
let event = moved || added;
|
||||||
if (event){
|
if (event) {
|
||||||
let doc = event.element.node;
|
let doc = event.element.node;
|
||||||
let newIndex;
|
let newIndex;
|
||||||
if (event.newIndex === 0){
|
if (event.newIndex === 0) {
|
||||||
newIndex = -0.5;
|
newIndex = -0.5;
|
||||||
} else {
|
} else {
|
||||||
if (event.newIndex < this.children.length){
|
if (event.newIndex < this.children.length) {
|
||||||
let childAtNewIndex = this.children[event.newIndex];
|
let childAtNewIndex = this.children[event.newIndex];
|
||||||
let indexOrder = childAtNewIndex.node.order;
|
let indexOrder = childAtNewIndex.node.order;
|
||||||
if (event.newIndex > event.oldIndex){
|
if (event.newIndex > event.oldIndex) {
|
||||||
newIndex = indexOrder + 0.5;
|
newIndex = indexOrder + 0.5;
|
||||||
} else {
|
} else {
|
||||||
newIndex = indexOrder - 0.5;
|
newIndex = indexOrder - 0.5;
|
||||||
@@ -103,32 +111,34 @@
|
|||||||
newIndex = childBeforeNewIndex.node.order + 0.5;
|
newIndex = childBeforeNewIndex.node.order + 0.5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (moved){
|
if (moved) {
|
||||||
this.$emit('reordered', {doc, newIndex});
|
this.$emit('reordered', { doc, newIndex });
|
||||||
} else if (added){
|
} else if (added) {
|
||||||
this.$emit('reorganized', {doc, parent: this.node, newIndex});
|
this.$emit('reorganized', { doc, parent: this.node, newIndex });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
move(evt){
|
move(evt) {
|
||||||
let parentNode = evt.relatedContext.component.$parent.node
|
let parentNode = evt.relatedContext.component.$parent.node
|
||||||
let parentType = parentNode && parentNode.type || 'root';
|
let parentType = parentNode && parentNode.type || 'root';
|
||||||
let childType = evt.draggedContext.element.node.type;
|
let childType = evt.draggedContext.element.node.type;
|
||||||
let allowed = isParentAllowed({parentType, childType});
|
let allowed = isParentAllowed({ parentType, childType });
|
||||||
return allowed;
|
return allowed;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css" scoped>
|
||||||
.flip-list-leave-active {
|
.flip-list-leave-active {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
.flip-list-move {
|
|
||||||
|
.flip-list-move {
|
||||||
transition: transform 0.5s;
|
transition: transform 0.5s;
|
||||||
}
|
}
|
||||||
.no-move {
|
|
||||||
|
.no-move {
|
||||||
transition: transform 0s;
|
transition: transform 0s;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -121,10 +121,10 @@
|
|||||||
|
|
||||||
<script lang="js">
|
<script lang="js">
|
||||||
import { union, without, debounce } from 'lodash';
|
import { union, without, debounce } from 'lodash';
|
||||||
import FormSection, {FormSections} from '/imports/ui/properties/forms/shared/FormSection.vue';
|
import FormSection, { FormSections } from '/imports/ui/properties/forms/shared/FormSection.vue';
|
||||||
import LibraryList from '/imports/ui/library/LibraryList.vue';
|
import LibraryList from '/imports/ui/library/LibraryList.vue';
|
||||||
import LibraryCollections from '/imports/api/library/LibraryCollections.js';
|
import LibraryCollections from '/imports/api/library/LibraryCollections.js';
|
||||||
import {changeAllowedLibraries, toggleAllUserLibraries} from '/imports/api/creature/creatures/methods/changeAllowedLibraries.js';
|
import { changeAllowedLibraries, toggleAllUserLibraries } from '/imports/api/creature/creatures/methods/changeAllowedLibraries.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -149,7 +149,8 @@ export default {
|
|||||||
},
|
},
|
||||||
disabled: Boolean,
|
disabled: Boolean,
|
||||||
},
|
},
|
||||||
data() { return {
|
data() {
|
||||||
|
return {
|
||||||
libraryCollections: this.model.allowedLibraryCollections,
|
libraryCollections: this.model.allowedLibraryCollections,
|
||||||
libraries: this.model.allowedLibraries,
|
libraries: this.model.allowedLibraries,
|
||||||
libraryWriteLoading: false,
|
libraryWriteLoading: false,
|
||||||
@@ -211,29 +212,29 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
changeShowTreeTab(value){
|
changeShowTreeTab(value) {
|
||||||
this.$emit('change', {
|
this.$emit('change', {
|
||||||
path: ['settings','showTreeTab'],
|
path: ['settings', 'showTreeTab'],
|
||||||
value: !!value
|
value: !!value
|
||||||
});
|
});
|
||||||
let currentTab = this.$store.getters.tabById(this.model._id);
|
let currentTab = this.$store.getters.tabById(this.model._id);
|
||||||
if (!value && currentTab === 5){
|
if (!value && currentTab === 5) {
|
||||||
this.$store.commit(
|
this.$store.commit(
|
||||||
'setTabForCharacterSheet',
|
'setTabForCharacterSheet',
|
||||||
{id: this.model._id, tab: 4}
|
{ id: this.model._id, tab: 4 }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
changeHideSpellsTab(value){
|
changeHideSpellsTab(value) {
|
||||||
this.$emit('change', {
|
this.$emit('change', {
|
||||||
path: ['settings','hideSpellsTab'],
|
path: ['settings', 'hideSpellsTab'],
|
||||||
value: !value
|
value: !value
|
||||||
});
|
});
|
||||||
let currentTab = this.$store.getters.tabById(this.model._id);
|
let currentTab = this.$store.getters.tabById(this.model._id);
|
||||||
if (!value && currentTab === 3){
|
if (!value && currentTab === 3) {
|
||||||
this.$store.commit(
|
this.$store.commit(
|
||||||
'setTabForCharacterSheet',
|
'setTabForCharacterSheet',
|
||||||
{id: this.model._id, tab: 4}
|
{ id: this.model._id, tab: 4 }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -266,4 +267,5 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css" scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
<template lang="html">
|
<template lang="html">
|
||||||
<dialog-base v-if="model" :color="model.color">
|
<dialog-base
|
||||||
|
v-if="model"
|
||||||
|
:color="model.color"
|
||||||
|
>
|
||||||
<template slot="toolbar">
|
<template slot="toolbar">
|
||||||
<v-toolbar-title>
|
<v-toolbar-title>
|
||||||
Character Details
|
Character Details
|
||||||
@@ -50,10 +53,10 @@ export default {
|
|||||||
startInEditTab: Boolean,
|
startInEditTab: Boolean,
|
||||||
},
|
},
|
||||||
meteor: {
|
meteor: {
|
||||||
model(){
|
model() {
|
||||||
return Creatures.findOne(this._id);
|
return Creatures.findOne(this._id);
|
||||||
},
|
},
|
||||||
editPermission(){
|
editPermission() {
|
||||||
try {
|
try {
|
||||||
assertEditPermission(this.model, Meteor.userId());
|
assertEditPermission(this.model, Meteor.userId());
|
||||||
return true;
|
return true;
|
||||||
@@ -63,10 +66,10 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
change({path, value, ack}){
|
change({ path, value, ack }) {
|
||||||
updateCreature.call({_id: this._id, path, value}, (error) =>{
|
updateCreature.call({ _id: this._id, path, value }, (error) => {
|
||||||
if (error){
|
if (error) {
|
||||||
if(ack){
|
if (ack) {
|
||||||
ack(error && error.reason || error)
|
ack(error && error.reason || error)
|
||||||
} else {
|
} else {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
@@ -81,4 +84,5 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css" scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -12,9 +12,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="js">
|
<script lang="js">
|
||||||
import BuildTreeNode from '/imports/ui/creature/buildTree/BuildTreeNode.vue';
|
import BuildTreeNode from '/imports/ui/creature/buildTree/BuildTreeNode.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
BuildTreeNode,
|
BuildTreeNode,
|
||||||
},
|
},
|
||||||
@@ -28,8 +28,10 @@
|
|||||||
default: undefined,
|
default: undefined,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data(){ return {
|
data() {
|
||||||
|
return {
|
||||||
expanded: false,
|
expanded: false,
|
||||||
}},
|
}
|
||||||
};
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -43,11 +43,13 @@ export default {
|
|||||||
props: {
|
props: {
|
||||||
id: String,
|
id: String,
|
||||||
},
|
},
|
||||||
data(){return {
|
data() {
|
||||||
|
return {
|
||||||
inputName: undefined,
|
inputName: undefined,
|
||||||
}},
|
}
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
nameMatch(){
|
nameMatch() {
|
||||||
if (!this.name) return true;
|
if (!this.name) return true;
|
||||||
let uppername = this.name.toUpperCase();
|
let uppername = this.name.toUpperCase();
|
||||||
let upperInputName = this.inputName && this.inputName.toUpperCase();
|
let upperInputName = this.inputName && this.inputName.toUpperCase();
|
||||||
@@ -55,19 +57,19 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
meteor: {
|
meteor: {
|
||||||
name(){
|
name() {
|
||||||
let creature = Creatures.findOne(this.id, {fields: {name: 1}});
|
let creature = Creatures.findOne(this.id, { fields: { name: 1 } });
|
||||||
return creature && creature.name;
|
return creature && creature.name;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
remove(){
|
remove() {
|
||||||
this.$router.push('/characterList');
|
this.$router.push('/characterList');
|
||||||
this.$store.dispatch('popDialogStack');
|
this.$store.dispatch('popDialogStack');
|
||||||
removeCreature.call({charId: this.id}, (error) => {
|
removeCreature.call({ charId: this.id }, (error) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
snackbar({text: error.message || error.toString()});
|
snackbar({ text: error.message || error.toString() });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -76,4 +78,5 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css" scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -12,9 +12,7 @@
|
|||||||
size="64"
|
size="64"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div v-else-if="!creature">
|
||||||
v-else-if="!creature"
|
|
||||||
>
|
|
||||||
<v-layout
|
<v-layout
|
||||||
column
|
column
|
||||||
align-center
|
align-center
|
||||||
@@ -55,9 +53,7 @@
|
|||||||
<v-tab-item>
|
<v-tab-item>
|
||||||
<inventory-tab :creature-id="creatureId" />
|
<inventory-tab :creature-id="creatureId" />
|
||||||
</v-tab-item>
|
</v-tab-item>
|
||||||
<v-tab-item
|
<v-tab-item v-if="!creature.settings.hideSpellsTab">
|
||||||
v-if="!creature.settings.hideSpellsTab"
|
|
||||||
>
|
|
||||||
<spells-tab :creature-id="creatureId" />
|
<spells-tab :creature-id="creatureId" />
|
||||||
</v-tab-item>
|
</v-tab-item>
|
||||||
<v-tab-item>
|
<v-tab-item>
|
||||||
@@ -66,9 +62,7 @@
|
|||||||
<v-tab-item>
|
<v-tab-item>
|
||||||
<build-tab :creature-id="creatureId" />
|
<build-tab :creature-id="creatureId" />
|
||||||
</v-tab-item>
|
</v-tab-item>
|
||||||
<v-tab-item
|
<v-tab-item v-if="creature.settings.showTreeTab">
|
||||||
v-if="creature.settings.showTreeTab"
|
|
||||||
>
|
|
||||||
<tree-tab :creature-id="creatureId" />
|
<tree-tab :creature-id="creatureId" />
|
||||||
</v-tab-item>
|
</v-tab-item>
|
||||||
</v-tabs-items>
|
</v-tabs-items>
|
||||||
@@ -78,21 +72,21 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="js">
|
<script lang="js">
|
||||||
//TODO add a "no character found" screen if shown on a false address
|
//TODO add a "no character found" screen if shown on a false address
|
||||||
// or on a character the user does not have permission to view
|
// or on a character the user does not have permission to view
|
||||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||||
import StatsTab from '/imports/ui/creature/character/characterSheetTabs/StatsTab.vue';
|
import StatsTab from '/imports/ui/creature/character/characterSheetTabs/StatsTab.vue';
|
||||||
import FeaturesTab from '/imports/ui/creature/character/characterSheetTabs/FeaturesTab.vue';
|
import FeaturesTab from '/imports/ui/creature/character/characterSheetTabs/FeaturesTab.vue';
|
||||||
import InventoryTab from '/imports/ui/creature/character/characterSheetTabs/InventoryTab.vue';
|
import InventoryTab from '/imports/ui/creature/character/characterSheetTabs/InventoryTab.vue';
|
||||||
import SpellsTab from '/imports/ui/creature/character/characterSheetTabs/SpellsTab.vue';
|
import SpellsTab from '/imports/ui/creature/character/characterSheetTabs/SpellsTab.vue';
|
||||||
import CharacterTab from '/imports/ui/creature/character/characterSheetTabs/JournalTab.vue';
|
import CharacterTab from '/imports/ui/creature/character/characterSheetTabs/JournalTab.vue';
|
||||||
import BuildTab from '/imports/ui/creature/character/characterSheetTabs/BuildTab.vue';
|
import BuildTab from '/imports/ui/creature/character/characterSheetTabs/BuildTab.vue';
|
||||||
import TreeTab from '/imports/ui/creature/character/characterSheetTabs/TreeTab.vue';
|
import TreeTab from '/imports/ui/creature/character/characterSheetTabs/TreeTab.vue';
|
||||||
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
|
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
|
||||||
import CreatureLogs from '/imports/api/creature/log/CreatureLogs.js';
|
import CreatureLogs from '/imports/api/creature/log/CreatureLogs.js';
|
||||||
import { snackbar } from '/imports/ui/components/snackbars/SnackbarQueue.js';
|
import { snackbar } from '/imports/ui/components/snackbars/SnackbarQueue.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
StatsTab,
|
StatsTab,
|
||||||
FeaturesTab,
|
FeaturesTab,
|
||||||
@@ -114,27 +108,27 @@
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
activeTab: {
|
activeTab: {
|
||||||
get(){
|
get() {
|
||||||
return this.tabs;
|
return this.tabs;
|
||||||
},
|
},
|
||||||
set(newTab){
|
set(newTab) {
|
||||||
this.$emit('update:tabs', newTab);
|
this.$emit('update:tabs', newTab);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
'creature.name'(value){
|
'creature.name'(value) {
|
||||||
this.$store.commit('setPageTitle', value || 'Character Sheet');
|
this.$store.commit('setPageTitle', value || 'Character Sheet');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted(){
|
mounted() {
|
||||||
this.$store.commit('setPageTitle', this.creature && this.creature.name || 'Character Sheet');
|
this.$store.commit('setPageTitle', this.creature && this.creature.name || 'Character Sheet');
|
||||||
this.nameObserver = Creatures.find({
|
this.nameObserver = Creatures.find({
|
||||||
creatureId: this.creatureId,
|
creatureId: this.creatureId,
|
||||||
}, {
|
}, {
|
||||||
fields: {name: 1},
|
fields: { name: 1 },
|
||||||
}).observe({
|
}).observe({
|
||||||
added: ({name}) =>
|
added: ({ name }) =>
|
||||||
this.$store.commit('setPageTitle', name || 'Character Sheet'),
|
this.$store.commit('setPageTitle', name || 'Character Sheet'),
|
||||||
changed: ({ name }) =>
|
changed: ({ name }) =>
|
||||||
this.$store.commit('setPageTitle', name || 'Character Sheet'),
|
this.$store.commit('setPageTitle', name || 'Character Sheet'),
|
||||||
@@ -143,29 +137,29 @@
|
|||||||
this.logObserver = CreatureLogs.find({
|
this.logObserver = CreatureLogs.find({
|
||||||
creatureId: this.creatureId,
|
creatureId: this.creatureId,
|
||||||
}).observe({
|
}).observe({
|
||||||
added({content}){
|
added({ content }) {
|
||||||
if (!that.$subReady.singleCharacter) return;
|
if (!that.$subReady.singleCharacter) return;
|
||||||
if (that.$store.state.rightDrawer) return;
|
if (that.$store.state.rightDrawer) return;
|
||||||
snackbar({content});
|
snackbar({ content });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
beforeDestroy(){
|
beforeDestroy() {
|
||||||
this.nameObserver.stop();
|
this.nameObserver.stop();
|
||||||
this.logObserver.stop();
|
this.logObserver.stop();
|
||||||
},
|
},
|
||||||
meteor: {
|
meteor: {
|
||||||
$subscribe: {
|
$subscribe: {
|
||||||
'singleCharacter'(){
|
'singleCharacter'() {
|
||||||
return [this.creatureId];
|
return [this.creatureId];
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
creature(){
|
creature() {
|
||||||
return Creatures.findOne(this.creatureId, {
|
return Creatures.findOne(this.creatureId, {
|
||||||
fields: {variables: 0}
|
fields: { variables: 0 }
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
editPermission(){
|
editPermission() {
|
||||||
try {
|
try {
|
||||||
assertEditPermission(this.creature, Meteor.userId());
|
assertEditPermission(this.creature, Meteor.userId());
|
||||||
return true;
|
return true;
|
||||||
@@ -174,12 +168,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.character-sheet .v-window-item {
|
.character-sheet .v-window-item {
|
||||||
min-height: calc(100vh - 96px);
|
min-height: calc(100vh - 96px);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -11,17 +11,13 @@
|
|||||||
dense
|
dense
|
||||||
>
|
>
|
||||||
<v-app-bar-nav-icon @click="toggleDrawer" />
|
<v-app-bar-nav-icon @click="toggleDrawer" />
|
||||||
<v-fade-transition
|
<v-fade-transition mode="out-in">
|
||||||
mode="out-in"
|
|
||||||
>
|
|
||||||
<v-toolbar-title :key="$store.state.pageTitle">
|
<v-toolbar-title :key="$store.state.pageTitle">
|
||||||
{{ $store.state.pageTitle }}
|
{{ $store.state.pageTitle }}
|
||||||
</v-toolbar-title>
|
</v-toolbar-title>
|
||||||
</v-fade-transition>
|
</v-fade-transition>
|
||||||
<v-spacer />
|
<v-spacer />
|
||||||
<v-fade-transition
|
<v-fade-transition mode="out-in">
|
||||||
mode="out-in"
|
|
||||||
>
|
|
||||||
<v-layout
|
<v-layout
|
||||||
:key="$route.meta.title"
|
:key="$route.meta.title"
|
||||||
class="flex-shrink-0 flex-grow-0"
|
class="flex-shrink-0 flex-grow-0"
|
||||||
@@ -151,17 +147,17 @@ export default {
|
|||||||
context: { default: {} }
|
context: { default: {} }
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
creatureId(){
|
creatureId() {
|
||||||
return this.$route.params.id;
|
return this.$route.params.id;
|
||||||
},
|
},
|
||||||
toolbarColor(){
|
toolbarColor() {
|
||||||
if (this.creature && this.creature.color){
|
if (this.creature && this.creature.color) {
|
||||||
return this.creature.color;
|
return this.creature.color;
|
||||||
} else {
|
} else {
|
||||||
return getThemeColor('secondary');
|
return getThemeColor('secondary');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isDark(){
|
isDark() {
|
||||||
return isDarkColor(this.toolbarColor);
|
return isDarkColor(this.toolbarColor);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -170,7 +166,7 @@ export default {
|
|||||||
'toggleDrawer',
|
'toggleDrawer',
|
||||||
'toggleRightDrawer',
|
'toggleRightDrawer',
|
||||||
]),
|
]),
|
||||||
showCharacterForm(){
|
showCharacterForm() {
|
||||||
this.$store.commit('pushDialogStack', {
|
this.$store.commit('pushDialogStack', {
|
||||||
component: 'creature-form-dialog',
|
component: 'creature-form-dialog',
|
||||||
elementId: 'creature-menu',
|
elementId: 'creature-menu',
|
||||||
@@ -179,7 +175,7 @@ export default {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
showShareDialog(){
|
showShareDialog() {
|
||||||
this.$store.commit('pushDialogStack', {
|
this.$store.commit('pushDialogStack', {
|
||||||
component: 'share-dialog',
|
component: 'share-dialog',
|
||||||
elementId: 'creature-menu',
|
elementId: 'creature-menu',
|
||||||
@@ -191,7 +187,7 @@ export default {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
deleteCharacter(){
|
deleteCharacter() {
|
||||||
let that = this;
|
let that = this;
|
||||||
this.$store.commit('pushDialogStack', {
|
this.$store.commit('pushDialogStack', {
|
||||||
component: 'delete-confirmation-dialog',
|
component: 'delete-confirmation-dialog',
|
||||||
@@ -200,9 +196,9 @@ export default {
|
|||||||
name: this.creature.name,
|
name: this.creature.name,
|
||||||
typeName: 'Character'
|
typeName: 'Character'
|
||||||
},
|
},
|
||||||
callback(confirmation){
|
callback(confirmation) {
|
||||||
if(!confirmation) return;
|
if (!confirmation) return;
|
||||||
removeCreature.call({charId: that.creatureId}, (error) => {
|
removeCreature.call({ charId: that.creatureId }, (error) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
} else {
|
} else {
|
||||||
@@ -212,7 +208,7 @@ export default {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
unshareWithMe(){
|
unshareWithMe() {
|
||||||
updateUserSharePermissions.call({
|
updateUserSharePermissions.call({
|
||||||
docRef: {
|
docRef: {
|
||||||
collection: 'creatures',
|
collection: 'creatures',
|
||||||
@@ -230,10 +226,10 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
meteor: {
|
meteor: {
|
||||||
creature(){
|
creature() {
|
||||||
return Creatures.findOne(this.creatureId);
|
return Creatures.findOne(this.creatureId);
|
||||||
},
|
},
|
||||||
editPermission(){
|
editPermission() {
|
||||||
try {
|
try {
|
||||||
assertEditPermission(this.creature, Meteor.userId());
|
assertEditPermission(this.creature, Meteor.userId());
|
||||||
return true;
|
return true;
|
||||||
@@ -249,9 +245,11 @@ export default {
|
|||||||
.character-sheet-toolbar .v-tabs__container--grow .v-tabs__div {
|
.character-sheet-toolbar .v-tabs__container--grow .v-tabs__div {
|
||||||
max-width: 120px !important;
|
max-width: 120px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.character-sheet-toolbar .v-tabs__bar {
|
.character-sheet-toolbar .v-tabs__bar {
|
||||||
background: none !important;
|
background: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.character-sheet-fab {
|
.character-sheet-fab {
|
||||||
bottom: -24px;
|
bottom: -24px;
|
||||||
right: 8px;
|
right: 8px;
|
||||||
|
|||||||
@@ -16,11 +16,11 @@
|
|||||||
</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 ColumnLayout from '/imports/ui/components/ColumnLayout.vue';
|
import ColumnLayout from '/imports/ui/components/ColumnLayout.vue';
|
||||||
import FeatureCard from '/imports/ui/properties/components/features/FeatureCard.vue';
|
import FeatureCard from '/imports/ui/properties/components/features/FeatureCard.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
ColumnLayout,
|
ColumnLayout,
|
||||||
FeatureCard,
|
FeatureCard,
|
||||||
@@ -32,28 +32,29 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
meteor: {
|
meteor: {
|
||||||
features(){
|
features() {
|
||||||
return CreatureProperties.find({
|
return CreatureProperties.find({
|
||||||
'ancestors.id': this.creatureId,
|
'ancestors.id': this.creatureId,
|
||||||
type: 'feature',
|
type: 'feature',
|
||||||
removed: {$ne: true},
|
removed: { $ne: true },
|
||||||
inactive: {$ne: true},
|
inactive: { $ne: true },
|
||||||
}, {
|
}, {
|
||||||
sort: {order: 1}
|
sort: { order: 1 }
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
featureClicked({_id}){
|
featureClicked({ _id }) {
|
||||||
this.$store.commit('pushDialogStack', {
|
this.$store.commit('pushDialogStack', {
|
||||||
component: 'creature-property-dialog',
|
component: 'creature-property-dialog',
|
||||||
elementId: `${_id}`,
|
elementId: `${_id}`,
|
||||||
data: {_id},
|
data: { _id },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css" scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -30,9 +30,7 @@
|
|||||||
</v-list-item-content>
|
</v-list-item-content>
|
||||||
<v-list-item-action>
|
<v-list-item-action>
|
||||||
<v-list-item-title>
|
<v-list-item-title>
|
||||||
<coin-value
|
<coin-value :value="variables && variables.valueTotal && variables.valueTotal.value|| 0" />
|
||||||
:value="variables && variables.valueTotal && variables.valueTotal.value|| 0"
|
|
||||||
/>
|
|
||||||
</v-list-item-title>
|
</v-list-item-title>
|
||||||
</v-list-item-action>
|
</v-list-item-action>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
@@ -85,9 +83,7 @@
|
|||||||
v-for="container in containersWithoutAncestorContainers"
|
v-for="container in containersWithoutAncestorContainers"
|
||||||
:key="container._id"
|
:key="container._id"
|
||||||
>
|
>
|
||||||
<container-card
|
<container-card :model="container" />
|
||||||
:model="container"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</column-layout>
|
</column-layout>
|
||||||
</div>
|
</div>
|
||||||
@@ -120,70 +116,74 @@ export default {
|
|||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data(){ return {
|
data() {
|
||||||
|
return {
|
||||||
organize: false,
|
organize: false,
|
||||||
}},
|
}
|
||||||
|
},
|
||||||
meteor: {
|
meteor: {
|
||||||
containers(){
|
containers() {
|
||||||
return CreatureProperties.find({
|
return CreatureProperties.find({
|
||||||
'ancestors.id': this.creatureId,
|
'ancestors.id': this.creatureId,
|
||||||
type: 'container',
|
type: 'container',
|
||||||
removed: {$ne: true},
|
removed: { $ne: true },
|
||||||
inactive: {$ne: true},
|
inactive: { $ne: true },
|
||||||
}, {
|
}, {
|
||||||
sort: {order: 1},
|
sort: { order: 1 },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
creature(){
|
creature() {
|
||||||
return Creatures.findOne(this.creatureId, {fields: {
|
return Creatures.findOne(this.creatureId, {
|
||||||
|
fields: {
|
||||||
color: 1,
|
color: 1,
|
||||||
variables: 1,
|
variables: 1,
|
||||||
}});
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
variables() {
|
variables() {
|
||||||
return CreatureVariables.findOne({ _creatureId: this.creatureId }) || {};
|
return CreatureVariables.findOne({ _creatureId: this.creatureId }) || {};
|
||||||
},
|
},
|
||||||
containersWithoutAncestorContainers(){
|
containersWithoutAncestorContainers() {
|
||||||
return CreatureProperties.find({
|
return CreatureProperties.find({
|
||||||
'ancestors.id': {
|
'ancestors.id': {
|
||||||
$eq: this.creatureId,
|
$eq: this.creatureId,
|
||||||
$nin: this.containerIds
|
$nin: this.containerIds
|
||||||
},
|
},
|
||||||
type: 'container',
|
type: 'container',
|
||||||
removed: {$ne: true},
|
removed: { $ne: true },
|
||||||
inactive: {$ne: true},
|
inactive: { $ne: true },
|
||||||
}, {
|
}, {
|
||||||
sort: {order: 1},
|
sort: { order: 1 },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
carriedItems(){
|
carriedItems() {
|
||||||
return CreatureProperties.find({
|
return CreatureProperties.find({
|
||||||
'ancestors.id': {
|
'ancestors.id': {
|
||||||
$eq: this.creatureId,
|
$eq: this.creatureId,
|
||||||
$nin: this.containerIds
|
$nin: this.containerIds
|
||||||
},
|
},
|
||||||
type: 'item',
|
type: 'item',
|
||||||
equipped: {$ne: true},
|
equipped: { $ne: true },
|
||||||
removed: {$ne: true},
|
removed: { $ne: true },
|
||||||
deactivatedByAncestor: {$ne: true},
|
deactivatedByAncestor: { $ne: true },
|
||||||
}, {
|
}, {
|
||||||
sort: {order: 1},
|
sort: { order: 1 },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
equippedItems(){
|
equippedItems() {
|
||||||
return CreatureProperties.find({
|
return CreatureProperties.find({
|
||||||
'ancestors.id': {
|
'ancestors.id': {
|
||||||
$eq: this.creatureId,
|
$eq: this.creatureId,
|
||||||
},
|
},
|
||||||
type: 'item',
|
type: 'item',
|
||||||
equipped: true,
|
equipped: true,
|
||||||
removed: {$ne: true},
|
removed: { $ne: true },
|
||||||
inactive: {$ne: true},
|
inactive: { $ne: true },
|
||||||
}, {
|
}, {
|
||||||
sort: {order: 1},
|
sort: { order: 1 },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
equipmentParentRef(){
|
equipmentParentRef() {
|
||||||
return getParentRefByTag(
|
return getParentRefByTag(
|
||||||
this.creatureId, BUILT_IN_TAGS.equipment
|
this.creatureId, BUILT_IN_TAGS.equipment
|
||||||
) || getParentRefByTag(
|
) || getParentRefByTag(
|
||||||
@@ -193,7 +193,7 @@ export default {
|
|||||||
collection: 'creatures'
|
collection: 'creatures'
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
carriedParentRef(){
|
carriedParentRef() {
|
||||||
return getParentRefByTag(
|
return getParentRefByTag(
|
||||||
this.creatureId, BUILT_IN_TAGS.carried
|
this.creatureId, BUILT_IN_TAGS.carried
|
||||||
) || getParentRefByTag(
|
) || getParentRefByTag(
|
||||||
@@ -205,10 +205,10 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
containerIds(){
|
containerIds() {
|
||||||
return this.containers.map(container => container._id);
|
return this.containers.map(container => container._id);
|
||||||
},
|
},
|
||||||
weightCarried(){
|
weightCarried() {
|
||||||
return stripFloatingPointOddities(
|
return stripFloatingPointOddities(
|
||||||
this.variables &&
|
this.variables &&
|
||||||
this.variables.weightCarried &&
|
this.variables.weightCarried &&
|
||||||
@@ -217,11 +217,11 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
clickProperty(_id){
|
clickProperty(_id) {
|
||||||
this.$store.commit('pushDialogStack', {
|
this.$store.commit('pushDialogStack', {
|
||||||
component: 'creature-property-dialog',
|
component: 'creature-property-dialog',
|
||||||
elementId: `tree-node-${_id}`,
|
elementId: `tree-node-${_id}`,
|
||||||
data: {_id},
|
data: { _id },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -229,4 +229,5 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css" scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -40,30 +40,32 @@ export default {
|
|||||||
required: true,
|
required: true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data(){ return {
|
data() {
|
||||||
|
return {
|
||||||
organize: false,
|
organize: false,
|
||||||
}},
|
}
|
||||||
|
},
|
||||||
meteor: {
|
meteor: {
|
||||||
spellLists(){
|
spellLists() {
|
||||||
return CreatureProperties.find({
|
return CreatureProperties.find({
|
||||||
'ancestors.id': this.creatureId,
|
'ancestors.id': this.creatureId,
|
||||||
type: 'spellList',
|
type: 'spellList',
|
||||||
removed: {$ne: true},
|
removed: { $ne: true },
|
||||||
inactive: {$ne: true},
|
inactive: { $ne: true },
|
||||||
}, {
|
}, {
|
||||||
sort: {order: 1}
|
sort: { order: 1 }
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
spellsWithoutList(){
|
spellsWithoutList() {
|
||||||
return CreatureProperties.find({
|
return CreatureProperties.find({
|
||||||
'ancestors.id': {
|
'ancestors.id': {
|
||||||
$eq: this.creatureId,
|
$eq: this.creatureId,
|
||||||
$nin: this.spellListIds,
|
$nin: this.spellListIds,
|
||||||
},
|
},
|
||||||
type: 'spell',
|
type: 'spell',
|
||||||
removed: {$ne: true},
|
removed: { $ne: true },
|
||||||
deactivatedByAncestor: {$ne: true},
|
deactivatedByAncestor: { $ne: true },
|
||||||
deactivatedByToggle: {$ne: true},
|
deactivatedByToggle: { $ne: true },
|
||||||
}, {
|
}, {
|
||||||
sort: {
|
sort: {
|
||||||
level: 1,
|
level: 1,
|
||||||
@@ -71,31 +73,31 @@ export default {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
spellListsWithoutAncestorSpellLists(){
|
spellListsWithoutAncestorSpellLists() {
|
||||||
return CreatureProperties.find({
|
return CreatureProperties.find({
|
||||||
'ancestors.id': {
|
'ancestors.id': {
|
||||||
$eq: this.creatureId,
|
$eq: this.creatureId,
|
||||||
$nin: this.spellListIds,
|
$nin: this.spellListIds,
|
||||||
},
|
},
|
||||||
type: 'spellList',
|
type: 'spellList',
|
||||||
removed: {$ne: true},
|
removed: { $ne: true },
|
||||||
inactive: {$ne: true},
|
inactive: { $ne: true },
|
||||||
}, {
|
}, {
|
||||||
sort: {order: 1}
|
sort: { order: 1 }
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
spellListIds(){
|
spellListIds() {
|
||||||
return this.spellLists.map(spellList => spellList._id);
|
return this.spellLists.map(spellList => spellList._id);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
clickProperty(_id){
|
clickProperty(_id) {
|
||||||
this.$store.commit('pushDialogStack', {
|
this.$store.commit('pushDialogStack', {
|
||||||
component: 'creature-property-dialog',
|
component: 'creature-property-dialog',
|
||||||
elementId: `spell-list-tile-${_id}`,
|
elementId: `spell-list-tile-${_id}`,
|
||||||
data: {_id},
|
data: { _id },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -103,4 +105,5 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css" scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
<template lang="html">
|
<template lang="html">
|
||||||
<div
|
<div class="stats-tab ma-2">
|
||||||
class="stats-tab ma-2"
|
|
||||||
>
|
|
||||||
<health-bar-card-container :creature-id="creatureId" />
|
<health-bar-card-container :creature-id="creatureId" />
|
||||||
|
|
||||||
<column-layout>
|
<column-layout>
|
||||||
@@ -171,9 +169,7 @@
|
|||||||
v-if="spellSlots && spellSlots.length || hasSpells"
|
v-if="spellSlots && spellSlots.length || hasSpells"
|
||||||
class="spell-slots"
|
class="spell-slots"
|
||||||
>
|
>
|
||||||
<v-card
|
<v-card data-id="spell-slot-card">
|
||||||
data-id="spell-slot-card"
|
|
||||||
>
|
|
||||||
<v-list
|
<v-list
|
||||||
v-if="spellSlots && spellSlots.length"
|
v-if="spellSlots && spellSlots.length"
|
||||||
two-line
|
two-line
|
||||||
@@ -253,18 +249,6 @@
|
|||||||
@sub-click="_id => clickTreeProperty({_id})"
|
@sub-click="_id => clickTreeProperty({_id})"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
v-for="attack in attacks"
|
|
||||||
:key="attack._id"
|
|
||||||
class="attack"
|
|
||||||
>
|
|
||||||
<action-card
|
|
||||||
attack
|
|
||||||
:model="attack"
|
|
||||||
:data-id="attack._id"
|
|
||||||
@click="clickProperty({_id: attack._id})"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="weapons && weapons.length"
|
v-if="weapons && weapons.length"
|
||||||
@@ -351,55 +335,55 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="js">
|
<script lang="js">
|
||||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||||
import softRemoveProperty from '/imports/api/creature/creatureProperties/methods/softRemoveProperty.js';
|
import softRemoveProperty from '/imports/api/creature/creatureProperties/methods/softRemoveProperty.js';
|
||||||
import damageProperty from '/imports/api/creature/creatureProperties/methods/damageProperty.js';
|
import damageProperty from '/imports/api/creature/creatureProperties/methods/damageProperty.js';
|
||||||
import AttributeCard from '/imports/ui/properties/components/attributes/AttributeCard.vue';
|
import AttributeCard from '/imports/ui/properties/components/attributes/AttributeCard.vue';
|
||||||
import AbilityListTile from '/imports/ui/properties/components/attributes/AbilityListTile.vue';
|
import AbilityListTile from '/imports/ui/properties/components/attributes/AbilityListTile.vue';
|
||||||
import ColumnLayout from '/imports/ui/components/ColumnLayout.vue';
|
import ColumnLayout from '/imports/ui/components/ColumnLayout.vue';
|
||||||
import DamageMultiplierCard from '/imports/ui/properties/components/damageMultipliers/DamageMultiplierCard.vue';
|
import DamageMultiplierCard from '/imports/ui/properties/components/damageMultipliers/DamageMultiplierCard.vue';
|
||||||
import HealthBarCardContainer from '/imports/ui/properties/components/attributes/HealthBarCardContainer.vue';
|
import HealthBarCardContainer from '/imports/ui/properties/components/attributes/HealthBarCardContainer.vue';
|
||||||
import HitDiceListTile from '/imports/ui/properties/components/attributes/HitDiceListTile.vue';
|
import HitDiceListTile from '/imports/ui/properties/components/attributes/HitDiceListTile.vue';
|
||||||
import SkillListTile from '/imports/ui/properties/components/skills/SkillListTile.vue';
|
import SkillListTile from '/imports/ui/properties/components/skills/SkillListTile.vue';
|
||||||
import ResourceCard from '/imports/ui/properties/components/attributes/ResourceCard.vue';
|
import ResourceCard from '/imports/ui/properties/components/attributes/ResourceCard.vue';
|
||||||
import SpellSlotListTile from '/imports/ui/properties/components/attributes/SpellSlotListTile.vue';
|
import SpellSlotListTile from '/imports/ui/properties/components/attributes/SpellSlotListTile.vue';
|
||||||
import ActionCard from '/imports/ui/properties/components/actions/ActionCard.vue';
|
import ActionCard from '/imports/ui/properties/components/actions/ActionCard.vue';
|
||||||
import RestButton from '/imports/ui/creature/RestButton.vue';
|
import RestButton from '/imports/ui/creature/RestButton.vue';
|
||||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||||
import ToggleCard from '/imports/ui/properties/components/toggles/ToggleCard.vue';
|
import ToggleCard from '/imports/ui/properties/components/toggles/ToggleCard.vue';
|
||||||
import doCastSpell from '/imports/api/engine/actions/doCastSpell.js';
|
import doCastSpell from '/imports/api/engine/actions/doCastSpell.js';
|
||||||
import {snackbar} from '/imports/ui/components/snackbars/SnackbarQueue.js';
|
import { snackbar } from '/imports/ui/components/snackbars/SnackbarQueue.js';
|
||||||
|
|
||||||
const getProperties = function(creature, filter, options = {
|
const getProperties = function (creature, filter, options = {
|
||||||
sort: {order: 1}
|
sort: { order: 1 }
|
||||||
}){
|
}) {
|
||||||
if (!creature) return;
|
if (!creature) return;
|
||||||
if (creature.settings.hideUnusedStats){
|
if (creature.settings.hideUnusedStats) {
|
||||||
filter.hide = {$ne: true};
|
filter.hide = { $ne: true };
|
||||||
}
|
}
|
||||||
filter['ancestors.id'] = creature._id;
|
filter['ancestors.id'] = creature._id;
|
||||||
filter.removed = {$ne: true};
|
filter.removed = { $ne: true };
|
||||||
filter.inactive = {$ne: true};
|
filter.inactive = { $ne: true };
|
||||||
filter.overridden = {$ne: true};
|
filter.overridden = { $ne: true };
|
||||||
|
|
||||||
return CreatureProperties.find(filter, options);
|
return CreatureProperties.find(filter, options);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getAttributeOfType = function(creature, type){
|
const getAttributeOfType = function (creature, type) {
|
||||||
return getProperties(creature, {
|
return getProperties(creature, {
|
||||||
type: 'attribute',
|
type: 'attribute',
|
||||||
attributeType: type,
|
attributeType: type,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const getSkillOfType = function(creature, type){
|
const getSkillOfType = function (creature, type) {
|
||||||
return getProperties(creature, {
|
return getProperties(creature, {
|
||||||
type: 'skill',
|
type: 'skill',
|
||||||
skillType: type,
|
skillType: type,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
RestButton,
|
RestButton,
|
||||||
AbilityListTile,
|
AbilityListTile,
|
||||||
@@ -420,141 +404,138 @@
|
|||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data(){return {
|
data() {
|
||||||
|
return {
|
||||||
doCheckLoading: false,
|
doCheckLoading: false,
|
||||||
}},
|
}
|
||||||
meteor: {
|
|
||||||
creature(){
|
|
||||||
return Creatures.findOne(this.creatureId, {fields: {settings: 1}});
|
|
||||||
},
|
},
|
||||||
abilities(){
|
meteor: {
|
||||||
|
creature() {
|
||||||
|
return Creatures.findOne(this.creatureId, { fields: { settings: 1 } });
|
||||||
|
},
|
||||||
|
abilities() {
|
||||||
return getAttributeOfType(this.creature, 'ability');
|
return getAttributeOfType(this.creature, 'ability');
|
||||||
},
|
},
|
||||||
stats(){
|
stats() {
|
||||||
return getAttributeOfType(this.creature, 'stat');
|
return getAttributeOfType(this.creature, 'stat');
|
||||||
},
|
},
|
||||||
toggles(){
|
toggles() {
|
||||||
return CreatureProperties.find({
|
return CreatureProperties.find({
|
||||||
'ancestors.id': this.creatureId,
|
'ancestors.id': this.creatureId,
|
||||||
type: 'toggle',
|
type: 'toggle',
|
||||||
removed: {$ne: true},
|
removed: { $ne: true },
|
||||||
deactivatedByAncestor: {$ne: true},
|
deactivatedByAncestor: { $ne: true },
|
||||||
showUI: true,
|
showUI: true,
|
||||||
}, {
|
}, {
|
||||||
sort: {order: 1}
|
sort: { order: 1 }
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
modifiers(){
|
modifiers() {
|
||||||
return getAttributeOfType(this.creature, 'modifier');
|
return getAttributeOfType(this.creature, 'modifier');
|
||||||
},
|
},
|
||||||
resources(){
|
resources() {
|
||||||
return getAttributeOfType(this.creature, 'resource');
|
return getAttributeOfType(this.creature, 'resource');
|
||||||
},
|
},
|
||||||
spellSlots(){
|
spellSlots() {
|
||||||
return getAttributeOfType(this.creature, 'spellSlot');
|
return getAttributeOfType(this.creature, 'spellSlot');
|
||||||
},
|
},
|
||||||
hasSpells(){
|
hasSpells() {
|
||||||
const cursor = getProperties(this.creature, {
|
const cursor = getProperties(this.creature, {
|
||||||
type: 'spell',
|
type: 'spell',
|
||||||
})
|
})
|
||||||
return cursor && cursor.count();
|
return cursor && cursor.count();
|
||||||
},
|
},
|
||||||
hitDice(){
|
hitDice() {
|
||||||
return getAttributeOfType(this.creature, 'hitDice');
|
return getAttributeOfType(this.creature, 'hitDice');
|
||||||
},
|
},
|
||||||
checks(){
|
checks() {
|
||||||
return getSkillOfType(this.creature, 'check');
|
return getSkillOfType(this.creature, 'check');
|
||||||
},
|
},
|
||||||
savingThrows(){
|
savingThrows() {
|
||||||
return getSkillOfType(this.creature, 'save');
|
return getSkillOfType(this.creature, 'save');
|
||||||
},
|
},
|
||||||
skills(){
|
skills() {
|
||||||
return getSkillOfType(this.creature, 'skill');
|
return getSkillOfType(this.creature, 'skill');
|
||||||
},
|
},
|
||||||
tools(){
|
tools() {
|
||||||
return getSkillOfType(this.creature, 'tool');
|
return getSkillOfType(this.creature, 'tool');
|
||||||
},
|
},
|
||||||
weapons(){
|
weapons() {
|
||||||
return getSkillOfType(this.creature, 'weapon');
|
return getSkillOfType(this.creature, 'weapon');
|
||||||
},
|
},
|
||||||
armors(){
|
armors() {
|
||||||
return getSkillOfType(this.creature, 'armor');
|
return getSkillOfType(this.creature, 'armor');
|
||||||
},
|
},
|
||||||
languages(){
|
languages() {
|
||||||
return getSkillOfType(this.creature, 'language');
|
return getSkillOfType(this.creature, 'language');
|
||||||
},
|
},
|
||||||
actions(){
|
actions() {
|
||||||
return getProperties(this.creature, {type: 'action'});
|
return getProperties(this.creature, { type: 'action' });
|
||||||
},
|
},
|
||||||
appliedBuffs(){
|
appliedBuffs() {
|
||||||
return getProperties(this.creature, {type: 'buff'});
|
return getProperties(this.creature, { type: 'buff' });
|
||||||
},
|
},
|
||||||
multipliers(){
|
multipliers() {
|
||||||
return getProperties(this.creature, {
|
return getProperties(this.creature, {
|
||||||
type: 'damageMultiplier'
|
type: 'damageMultiplier'
|
||||||
}, {
|
}, {
|
||||||
sort: {value: 1, order: 1}
|
sort: { value: 1, order: 1 }
|
||||||
});
|
|
||||||
},
|
|
||||||
attacks(){
|
|
||||||
let props = getProperties(this.creature, {type: 'attack'})
|
|
||||||
return props && props.map(attack => {
|
|
||||||
attack.children = CreatureProperties.find({
|
|
||||||
'ancestors.id': attack._id,
|
|
||||||
removed: {$ne: true},
|
|
||||||
inactive: {$ne: true},
|
|
||||||
}, {
|
|
||||||
sort: {order: 1}
|
|
||||||
});
|
|
||||||
return attack;
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
clickProperty({_id}){
|
clickProperty({ _id }) {
|
||||||
this.$store.commit('pushDialogStack', {
|
this.$store.commit('pushDialogStack', {
|
||||||
component: 'creature-property-dialog',
|
component: 'creature-property-dialog',
|
||||||
elementId: `${_id}`,
|
elementId: `${_id}`,
|
||||||
data: {_id},
|
data: { _id },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
clickTreeProperty({_id}){
|
clickTreeProperty({ _id }) {
|
||||||
this.$store.commit('pushDialogStack', {
|
this.$store.commit('pushDialogStack', {
|
||||||
component: 'creature-property-dialog',
|
component: 'creature-property-dialog',
|
||||||
elementId: `tree-node-${_id}`,
|
elementId: `tree-node-${_id}`,
|
||||||
data: {_id},
|
data: { _id },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
incrementChange(_id, {type, value}){
|
incrementChange(_id, { type, value }) {
|
||||||
if (type === 'increment'){
|
if (type === 'increment') {
|
||||||
damageProperty.call({_id, operation: 'increment' ,value: -value});
|
damageProperty.call({ _id, operation: 'increment', value: -value });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
softRemove(_id){
|
softRemove(_id) {
|
||||||
softRemoveProperty.call({_id}, error => {
|
softRemoveProperty.call({ _id }, error => {
|
||||||
if (error) console.error(error);
|
if (error) console.error(error);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
castSpell(){
|
castSpell() {
|
||||||
this.$store.commit('pushDialogStack', {
|
this.$store.commit('pushDialogStack', {
|
||||||
component: 'cast-spell-with-slot-dialog',
|
component: 'cast-spell-with-slot-dialog',
|
||||||
elementId: 'spell-slot-card',
|
elementId: 'spell-slot-card',
|
||||||
data: {
|
data: {
|
||||||
creatureId: this.creatureId,
|
creatureId: this.creatureId,
|
||||||
},
|
},
|
||||||
callback({spellId, slotId} = {}){
|
callback({ spellId, slotId, advantage, ritual } = {}) {
|
||||||
if (!spellId) return;
|
if (!spellId) return;
|
||||||
doCastSpell.call({spellId, slotId}, error => {
|
doCastSpell.call({
|
||||||
|
spellId,
|
||||||
|
slotId,
|
||||||
|
ritual,
|
||||||
|
scope: {
|
||||||
|
$attackAdvantage: advantage,
|
||||||
|
},
|
||||||
|
}, error => {
|
||||||
if (!error) return;
|
if (!error) return;
|
||||||
snackbar({text: error.reason || error.message || error.toString()});
|
snackbar({ text: error.reason || error.message || error.toString() });
|
||||||
console.error(error);
|
console.error(error);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css" scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -13,12 +13,12 @@
|
|||||||
</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 nodesToTree from '/imports/api/parenting/nodesToTree.js'
|
import nodesToTree from '/imports/api/parenting/nodesToTree.js'
|
||||||
import TreeNodeList from '/imports/ui/components/tree/TreeNodeList.vue';
|
import TreeNodeList from '/imports/ui/components/tree/TreeNodeList.vue';
|
||||||
import { organizeDoc, reorderDoc } from '/imports/api/parenting/organizeMethods.js';
|
import { organizeDoc, reorderDoc } from '/imports/api/parenting/organizeMethods.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
TreeNodeList,
|
TreeNodeList,
|
||||||
},
|
},
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
expanded: Boolean,
|
expanded: Boolean,
|
||||||
},
|
},
|
||||||
meteor: {
|
meteor: {
|
||||||
children(){
|
children() {
|
||||||
const children = nodesToTree({
|
const children = nodesToTree({
|
||||||
collection: CreatureProperties,
|
collection: CreatureProperties,
|
||||||
ancestorId: this.root.id,
|
ancestorId: this.root.id,
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
reordered({doc, newIndex}){
|
reordered({ doc, newIndex }) {
|
||||||
reorderDoc.call({
|
reorderDoc.call({
|
||||||
docRef: {
|
docRef: {
|
||||||
id: doc._id,
|
id: doc._id,
|
||||||
@@ -65,9 +65,9 @@
|
|||||||
order: newIndex,
|
order: newIndex,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
reorganized({doc, parent, newIndex}){
|
reorganized({ doc, parent, newIndex }) {
|
||||||
let parentRef;
|
let parentRef;
|
||||||
if (parent){
|
if (parent) {
|
||||||
parentRef = {
|
parentRef = {
|
||||||
id: parent._id,
|
id: parent._id,
|
||||||
collection: 'creatureProperties',
|
collection: 'creatureProperties',
|
||||||
@@ -85,8 +85,9 @@
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css" scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -28,13 +28,15 @@ export default {
|
|||||||
default: undefined,
|
default: undefined,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data() { return {
|
data() {
|
||||||
|
return {
|
||||||
type: undefined,
|
type: undefined,
|
||||||
};},
|
};
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getPropertyName,
|
getPropertyName,
|
||||||
back(){
|
back() {
|
||||||
if (this.forcedType){
|
if (this.forcedType) {
|
||||||
this.$store.dispatch('popDialogStack');
|
this.$store.dispatch('popDialogStack');
|
||||||
} else {
|
} else {
|
||||||
this.type = undefined;
|
this.type = undefined;
|
||||||
@@ -45,4 +47,5 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css" scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -23,18 +23,21 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="js">
|
<script lang="js">
|
||||||
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
|
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
|
||||||
import LibraryAndNode from '/imports/ui/library/LibraryAndNode.vue';
|
import LibraryAndNode from '/imports/ui/library/LibraryAndNode.vue';
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
DialogBase,
|
DialogBase,
|
||||||
LibraryAndNode,
|
LibraryAndNode,
|
||||||
},
|
},
|
||||||
data(){return {
|
data() {
|
||||||
|
return {
|
||||||
node: undefined,
|
node: undefined,
|
||||||
};},
|
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css" scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -52,10 +52,10 @@ export default {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data(){
|
data() {
|
||||||
let schema = ExperienceSchema.omit('creatureId');
|
let schema = ExperienceSchema.omit('creatureId');
|
||||||
let startingModel = {};
|
let startingModel = {};
|
||||||
if (this.startAsMilestone){
|
if (this.startAsMilestone) {
|
||||||
startingModel.levels = 1;
|
startingModel.levels = 1;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
@@ -65,14 +65,14 @@ export default {
|
|||||||
debounceTime: 0,
|
debounceTime: 0,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods:{
|
methods: {
|
||||||
insertExperience(){
|
insertExperience() {
|
||||||
let experience = this.schema.clean(this.model);
|
let experience = this.schema.clean(this.model);
|
||||||
let id = insertExperience.call({
|
let id = insertExperience.call({
|
||||||
experience,
|
experience,
|
||||||
creatureIds: this.creatureIds,
|
creatureIds: this.creatureIds,
|
||||||
}, (error) => {
|
}, (error) => {
|
||||||
if (error){
|
if (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -83,4 +83,5 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css" scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -192,7 +192,6 @@ import getSlotFillFilter from '/imports/api/creature/creatureProperties/methods/
|
|||||||
import Libraries from '/imports/api/library/Libraries.js';
|
import Libraries from '/imports/api/library/Libraries.js';
|
||||||
import LibraryNodeExpansionContent from '/imports/ui/library/LibraryNodeExpansionContent.vue';
|
import LibraryNodeExpansionContent from '/imports/ui/library/LibraryNodeExpansionContent.vue';
|
||||||
import PropertyTags from '/imports/ui/properties/viewers/shared/PropertyTags.vue';
|
import PropertyTags from '/imports/ui/properties/viewers/shared/PropertyTags.vue';
|
||||||
import { getPropertyName } from '/imports/constants/PROPERTIES.js';
|
|
||||||
import { clone } from 'lodash';
|
import { clone } from 'lodash';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -203,7 +202,7 @@ export default {
|
|||||||
LibraryNodeExpansionContent,
|
LibraryNodeExpansionContent,
|
||||||
PropertyTags,
|
PropertyTags,
|
||||||
},
|
},
|
||||||
props:{
|
props: {
|
||||||
classId: {
|
classId: {
|
||||||
type: String,
|
type: String,
|
||||||
default: undefined,
|
default: undefined,
|
||||||
@@ -217,42 +216,44 @@ export default {
|
|||||||
default: undefined,
|
default: undefined,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data(){return {
|
data() {
|
||||||
|
return {
|
||||||
selectedNodeIds: [],
|
selectedNodeIds: [],
|
||||||
searchInput: undefined,
|
searchInput: undefined,
|
||||||
searchValue: undefined,
|
searchValue: undefined,
|
||||||
showDisabled: false,
|
showDisabled: false,
|
||||||
disabledNodeCount: undefined,
|
disabledNodeCount: undefined,
|
||||||
}},
|
}
|
||||||
|
},
|
||||||
reactiveProvide: {
|
reactiveProvide: {
|
||||||
name: 'context',
|
name: 'context',
|
||||||
include: ['creatureId'],
|
include: ['creatureId'],
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
tagsSearched(){
|
tagsSearched() {
|
||||||
let or = [];
|
let or = [];
|
||||||
let not = [];
|
let not = [];
|
||||||
if (this.model.slotTags && this.model.slotTags.length){
|
if (this.model.slotTags && this.model.slotTags.length) {
|
||||||
or.push(this.model.slotTags);
|
or.push(this.model.slotTags);
|
||||||
}
|
}
|
||||||
this.model.extraTags?.forEach(extras => {
|
this.model.extraTags?.forEach(extras => {
|
||||||
if (extras.tags?.length){
|
if (extras.tags?.length) {
|
||||||
if(extras.operation === 'OR'){
|
if (extras.operation === 'OR') {
|
||||||
or.push(extras.tags);
|
or.push(extras.tags);
|
||||||
} else if (extras.operation === 'NOT'){
|
} else if (extras.operation === 'NOT') {
|
||||||
not.push(extras.tags);
|
not.push(extras.tags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return {or, not};
|
return { or, not };
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
loadMore(){
|
loadMore() {
|
||||||
if (this.currentLimit >= this.countAll) return;
|
if (this.currentLimit >= this.countAll) return;
|
||||||
this._subs['classFillers'].setData('limit', this.currentLimit + 50);
|
this._subs['classFillers'].setData('limit', this.currentLimit + 50);
|
||||||
},
|
},
|
||||||
openPropertyDetails(id){
|
openPropertyDetails(id) {
|
||||||
this.$store.commit('pushDialogStack', {
|
this.$store.commit('pushDialogStack', {
|
||||||
component: 'library-node-dialog',
|
component: 'library-node-dialog',
|
||||||
elementId: id,
|
elementId: id,
|
||||||
@@ -261,7 +262,7 @@ export default {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
isDisabled(node){
|
isDisabled(node) {
|
||||||
return node._disabledBySlotFillerCondition ||
|
return node._disabledBySlotFillerCondition ||
|
||||||
node._disabledByAlreadyAdded ||
|
node._disabledByAlreadyAdded ||
|
||||||
(
|
(
|
||||||
@@ -272,15 +273,15 @@ export default {
|
|||||||
},
|
},
|
||||||
meteor: {
|
meteor: {
|
||||||
$subscribe: {
|
$subscribe: {
|
||||||
'classFillers'(){
|
'classFillers'() {
|
||||||
return [this.classId, this.searchValue || undefined]
|
return [this.classId, this.searchValue || undefined]
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
searchLoading(){
|
searchLoading() {
|
||||||
return !!this.searchValue && !this.$subReady.classFillers;
|
return !!this.searchValue && !this.$subReady.classFillers;
|
||||||
},
|
},
|
||||||
model(){
|
model() {
|
||||||
if (this.classId){
|
if (this.classId) {
|
||||||
return CreatureProperties.findOne(this.classId);
|
return CreatureProperties.findOne(this.classId);
|
||||||
} else if (this.dummySlot) {
|
} else if (this.dummySlot) {
|
||||||
let model = clone(this.dummySlot)
|
let model = clone(this.dummySlot)
|
||||||
@@ -294,40 +295,40 @@ export default {
|
|||||||
if (!this.creatureId) return {};
|
if (!this.creatureId) return {};
|
||||||
return CreatureVariables.findOne({ _creatureId: this.creatureId }) || {};
|
return CreatureVariables.findOne({ _creatureId: this.creatureId }) || {};
|
||||||
},
|
},
|
||||||
currentLimit(){
|
currentLimit() {
|
||||||
return this._subs['classFillers'].data('limit') || 50;
|
return this._subs['classFillers'].data('limit') || 50;
|
||||||
},
|
},
|
||||||
countAll(){
|
countAll() {
|
||||||
return this._subs['classFillers'].data('countAll');
|
return this._subs['classFillers'].data('countAll');
|
||||||
},
|
},
|
||||||
alreadyAdded(){
|
alreadyAdded() {
|
||||||
let added = new Set();
|
let added = new Set();
|
||||||
if (!this.model.unique) return added;
|
if (!this.model.unique) return added;
|
||||||
let ancestorId;
|
let ancestorId;
|
||||||
if (this.model.unique === 'uniqueInSlot'){
|
if (this.model.unique === 'uniqueInSlot') {
|
||||||
ancestorId = this.model._id;
|
ancestorId = this.model._id;
|
||||||
} else if (this.model.unique === 'uniqueInCreature'){
|
} else if (this.model.unique === 'uniqueInCreature') {
|
||||||
ancestorId = this.creatureId;
|
ancestorId = this.creatureId;
|
||||||
}
|
}
|
||||||
CreatureProperties.find({
|
CreatureProperties.find({
|
||||||
'ancestors.id': ancestorId,
|
'ancestors.id': ancestorId,
|
||||||
libraryNodeId: {$exists: true},
|
libraryNodeId: { $exists: true },
|
||||||
removed: {$ne: true},
|
removed: { $ne: true },
|
||||||
}, {
|
}, {
|
||||||
fields: {libraryNodeId: 1},
|
fields: { libraryNodeId: 1 },
|
||||||
}).forEach(prop => {
|
}).forEach(prop => {
|
||||||
added.add(prop.libraryNodeId);
|
added.add(prop.libraryNodeId);
|
||||||
});
|
});
|
||||||
return added;
|
return added;
|
||||||
},
|
},
|
||||||
totalQuantitySelected(){
|
totalQuantitySelected() {
|
||||||
let quantitySelected = 0;
|
let quantitySelected = 0;
|
||||||
LibraryNodes.find({
|
LibraryNodes.find({
|
||||||
_id: {$in: this.selectedNodeIds}
|
_id: { $in: this.selectedNodeIds }
|
||||||
}, {
|
}, {
|
||||||
fields: {slotQuantityFilled: 1},
|
fields: { slotQuantityFilled: 1 },
|
||||||
}).forEach(node => {
|
}).forEach(node => {
|
||||||
if (Number.isFinite(node.slotQuantityFilled)){
|
if (Number.isFinite(node.slotQuantityFilled)) {
|
||||||
quantitySelected += node.slotQuantityFilled;
|
quantitySelected += node.slotQuantityFilled;
|
||||||
} else {
|
} else {
|
||||||
quantitySelected += 1;
|
quantitySelected += 1;
|
||||||
@@ -335,30 +336,30 @@ export default {
|
|||||||
});
|
});
|
||||||
return quantitySelected;
|
return quantitySelected;
|
||||||
},
|
},
|
||||||
spaceLeft(){
|
spaceLeft() {
|
||||||
if (!this.model.quantityExpected || this.model.quantityExpected.value === 0) return undefined;
|
if (!this.model.quantityExpected || this.model.quantityExpected.value === 0) return undefined;
|
||||||
return this.model.spaceLeft - this.totalQuantitySelected;
|
return this.model.spaceLeft - this.totalQuantitySelected;
|
||||||
},
|
},
|
||||||
libraryNames(){
|
libraryNames() {
|
||||||
let names = {};
|
let names = {};
|
||||||
Libraries.find().forEach(lib => names[lib._id] = lib.name)
|
Libraries.find().forEach(lib => names[lib._id] = lib.name)
|
||||||
return names;
|
return names;
|
||||||
},
|
},
|
||||||
libraryNodes(){
|
libraryNodes() {
|
||||||
let filter = getSlotFillFilter({ slot: this.model });
|
let filter = getSlotFillFilter({ slot: this.model });
|
||||||
let nodes = LibraryNodes.find(filter, {
|
let nodes = LibraryNodes.find(filter, {
|
||||||
sort: {name: 1, order: 1}
|
sort: { name: 1, order: 1 }
|
||||||
}).fetch();
|
}).fetch();
|
||||||
let disabledNodeCount = 0;
|
let disabledNodeCount = 0;
|
||||||
// Mark classFillers whose condition isn't met or are too big to fit
|
// Mark classFillers whose condition isn't met or are too big to fit
|
||||||
// the quantity to fill
|
// the quantity to fill
|
||||||
nodes.forEach(node => {
|
nodes.forEach(node => {
|
||||||
if (node.slotFillerCondition){
|
if (node.slotFillerCondition) {
|
||||||
try {
|
try {
|
||||||
let parseNode = parse(node.slotFillerCondition);
|
let parseNode = parse(node.slotFillerCondition);
|
||||||
const {result: resultNode} = resolve('reduce', parseNode, this.variables);
|
const { result: resultNode } = resolve('reduce', parseNode, this.variables);
|
||||||
if (resultNode?.parseType === 'constant'){
|
if (resultNode?.parseType === 'constant') {
|
||||||
if (!resultNode.value){
|
if (!resultNode.value) {
|
||||||
node._disabledBySlotFillerCondition = true;
|
node._disabledBySlotFillerCondition = true;
|
||||||
disabledNodeCount += 1;
|
disabledNodeCount += 1;
|
||||||
}
|
}
|
||||||
@@ -367,7 +368,7 @@ export default {
|
|||||||
node._conditionError = toString(resultNode);
|
node._conditionError = toString(resultNode);
|
||||||
disabledNodeCount += 1;
|
disabledNodeCount += 1;
|
||||||
}
|
}
|
||||||
} catch (e){
|
} catch (e) {
|
||||||
console.warn(e);
|
console.warn(e);
|
||||||
let error = prettifyParseError(e);
|
let error = prettifyParseError(e);
|
||||||
node._disabledBySlotFillerCondition = true;
|
node._disabledBySlotFillerCondition = true;
|
||||||
@@ -378,10 +379,10 @@ export default {
|
|||||||
let quantityToFill = node.type === 'slotFiller' ? node.slotQuantityFilled : 1;
|
let quantityToFill = node.type === 'slotFiller' ? node.slotQuantityFilled : 1;
|
||||||
if (
|
if (
|
||||||
quantityToFill > this.spaceLeft
|
quantityToFill > this.spaceLeft
|
||||||
){
|
) {
|
||||||
node._disabledByQuantityFilled = true;
|
node._disabledByQuantityFilled = true;
|
||||||
}
|
}
|
||||||
if (this.alreadyAdded.has(node._id)){
|
if (this.alreadyAdded.has(node._id)) {
|
||||||
node._disabledByAlreadyAdded = true;
|
node._disabledByAlreadyAdded = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -393,7 +394,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css" scoped>
|
||||||
.disabled {
|
.disabled {
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -62,8 +62,10 @@ export default {
|
|||||||
hover: false,
|
hover: false,
|
||||||
}},
|
}},
|
||||||
computed: {
|
computed: {
|
||||||
accentColor(){
|
accentColor() {
|
||||||
if (this.theme.isDark){
|
if (this.model.color) {
|
||||||
|
return this.model.color
|
||||||
|
} else if (this.theme.isDark){
|
||||||
return this.$vuetify.theme.themes.dark.primary;
|
return this.$vuetify.theme.themes.dark.primary;
|
||||||
} else {
|
} else {
|
||||||
return this.$vuetify.theme.themes.light.primary;
|
return this.$vuetify.theme.themes.light.primary;
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ export default {
|
|||||||
LibraryNodeExpansionContent,
|
LibraryNodeExpansionContent,
|
||||||
PropertyTags,
|
PropertyTags,
|
||||||
},
|
},
|
||||||
props:{
|
props: {
|
||||||
slotId: {
|
slotId: {
|
||||||
type: String,
|
type: String,
|
||||||
default: undefined,
|
default: undefined,
|
||||||
@@ -218,36 +218,38 @@ export default {
|
|||||||
default: undefined,
|
default: undefined,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data(){return {
|
data() {
|
||||||
|
return {
|
||||||
selectedNodeIds: [],
|
selectedNodeIds: [],
|
||||||
searchInput: undefined,
|
searchInput: undefined,
|
||||||
searchValue: undefined,
|
searchValue: undefined,
|
||||||
showDisabled: false,
|
showDisabled: false,
|
||||||
disabledNodeCount: undefined,
|
disabledNodeCount: undefined,
|
||||||
}},
|
}
|
||||||
|
},
|
||||||
reactiveProvide: {
|
reactiveProvide: {
|
||||||
name: 'context',
|
name: 'context',
|
||||||
include: ['creatureId'],
|
include: ['creatureId'],
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
tagsSearched(){
|
tagsSearched() {
|
||||||
let or = [];
|
let or = [];
|
||||||
let not = [];
|
let not = [];
|
||||||
if (this.model.slotTags && this.model.slotTags.length){
|
if (this.model.slotTags && this.model.slotTags.length) {
|
||||||
or.push(this.model.slotTags);
|
or.push(this.model.slotTags);
|
||||||
}
|
}
|
||||||
this.model.extraTags?.forEach(extras => {
|
this.model.extraTags?.forEach(extras => {
|
||||||
if (extras.tags?.length){
|
if (extras.tags?.length) {
|
||||||
if(extras.operation === 'OR'){
|
if (extras.operation === 'OR') {
|
||||||
or.push(extras.tags);
|
or.push(extras.tags);
|
||||||
} else if (extras.operation === 'NOT'){
|
} else if (extras.operation === 'NOT') {
|
||||||
not.push(extras.tags);
|
not.push(extras.tags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return {or, not};
|
return { or, not };
|
||||||
},
|
},
|
||||||
slotPropertyTypeName(){
|
slotPropertyTypeName() {
|
||||||
if (!this.model) return;
|
if (!this.model) return;
|
||||||
if (!this.model.slotType) return 'Property';
|
if (!this.model.slotType) return 'Property';
|
||||||
let propName = getPropertyName(this.model.slotType);
|
let propName = getPropertyName(this.model.slotType);
|
||||||
@@ -255,11 +257,11 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
loadMore(){
|
loadMore() {
|
||||||
if (this.currentLimit >= this.countAll) return;
|
if (this.currentLimit >= this.countAll) return;
|
||||||
this._subs['slotFillers'].setData('limit', this.currentLimit + 50);
|
this._subs['slotFillers'].setData('limit', this.currentLimit + 50);
|
||||||
},
|
},
|
||||||
openPropertyDetails(id){
|
openPropertyDetails(id) {
|
||||||
this.$store.commit('pushDialogStack', {
|
this.$store.commit('pushDialogStack', {
|
||||||
component: 'library-node-dialog',
|
component: 'library-node-dialog',
|
||||||
elementId: id,
|
elementId: id,
|
||||||
@@ -268,7 +270,7 @@ export default {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
isDisabled(node){
|
isDisabled(node) {
|
||||||
return node._disabledBySlotFillerCondition ||
|
return node._disabledBySlotFillerCondition ||
|
||||||
node._disabledByAlreadyAdded ||
|
node._disabledByAlreadyAdded ||
|
||||||
(
|
(
|
||||||
@@ -279,15 +281,15 @@ export default {
|
|||||||
},
|
},
|
||||||
meteor: {
|
meteor: {
|
||||||
$subscribe: {
|
$subscribe: {
|
||||||
'slotFillers'(){
|
'slotFillers'() {
|
||||||
return [this.slotId, this.searchValue || undefined]
|
return [this.slotId, this.searchValue || undefined]
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
searchLoading(){
|
searchLoading() {
|
||||||
return !!this.searchValue && !this.$subReady.slotFillers;
|
return !!this.searchValue && !this.$subReady.slotFillers;
|
||||||
},
|
},
|
||||||
model(){
|
model() {
|
||||||
if (this.slotId){
|
if (this.slotId) {
|
||||||
return CreatureProperties.findOne(this.slotId);
|
return CreatureProperties.findOne(this.slotId);
|
||||||
} else if (this.dummySlot) {
|
} else if (this.dummySlot) {
|
||||||
let model = clone(this.dummySlot)
|
let model = clone(this.dummySlot)
|
||||||
@@ -301,40 +303,40 @@ export default {
|
|||||||
if (!this.creatureId) return {};
|
if (!this.creatureId) return {};
|
||||||
return CreatureVariables.findOne({ _creatureId: this.creatureId }) || {};
|
return CreatureVariables.findOne({ _creatureId: this.creatureId }) || {};
|
||||||
},
|
},
|
||||||
currentLimit(){
|
currentLimit() {
|
||||||
return this._subs['slotFillers'].data('limit') || 50;
|
return this._subs['slotFillers'].data('limit') || 50;
|
||||||
},
|
},
|
||||||
countAll(){
|
countAll() {
|
||||||
return this._subs['slotFillers'].data('countAll');
|
return this._subs['slotFillers'].data('countAll');
|
||||||
},
|
},
|
||||||
alreadyAdded(){
|
alreadyAdded() {
|
||||||
let added = new Set();
|
let added = new Set();
|
||||||
if (!this.model.unique) return added;
|
if (!this.model.unique) return added;
|
||||||
let ancestorId;
|
let ancestorId;
|
||||||
if (this.model.unique === 'uniqueInSlot'){
|
if (this.model.unique === 'uniqueInSlot') {
|
||||||
ancestorId = this.model._id;
|
ancestorId = this.model._id;
|
||||||
} else if (this.model.unique === 'uniqueInCreature'){
|
} else if (this.model.unique === 'uniqueInCreature') {
|
||||||
ancestorId = this.creatureId;
|
ancestorId = this.creatureId;
|
||||||
}
|
}
|
||||||
CreatureProperties.find({
|
CreatureProperties.find({
|
||||||
'ancestors.id': ancestorId,
|
'ancestors.id': ancestorId,
|
||||||
libraryNodeId: {$exists: true},
|
libraryNodeId: { $exists: true },
|
||||||
removed: {$ne: true},
|
removed: { $ne: true },
|
||||||
}, {
|
}, {
|
||||||
fields: {libraryNodeId: 1},
|
fields: { libraryNodeId: 1 },
|
||||||
}).forEach(prop => {
|
}).forEach(prop => {
|
||||||
added.add(prop.libraryNodeId);
|
added.add(prop.libraryNodeId);
|
||||||
});
|
});
|
||||||
return added;
|
return added;
|
||||||
},
|
},
|
||||||
totalQuantitySelected(){
|
totalQuantitySelected() {
|
||||||
let quantitySelected = 0;
|
let quantitySelected = 0;
|
||||||
LibraryNodes.find({
|
LibraryNodes.find({
|
||||||
_id: {$in: this.selectedNodeIds}
|
_id: { $in: this.selectedNodeIds }
|
||||||
}, {
|
}, {
|
||||||
fields: {slotQuantityFilled: 1},
|
fields: { slotQuantityFilled: 1 },
|
||||||
}).forEach(node => {
|
}).forEach(node => {
|
||||||
if (Number.isFinite(node.slotQuantityFilled)){
|
if (Number.isFinite(node.slotQuantityFilled)) {
|
||||||
quantitySelected += node.slotQuantityFilled;
|
quantitySelected += node.slotQuantityFilled;
|
||||||
} else {
|
} else {
|
||||||
quantitySelected += 1;
|
quantitySelected += 1;
|
||||||
@@ -342,30 +344,30 @@ export default {
|
|||||||
});
|
});
|
||||||
return quantitySelected;
|
return quantitySelected;
|
||||||
},
|
},
|
||||||
spaceLeft(){
|
spaceLeft() {
|
||||||
if (!this.model.quantityExpected || this.model.quantityExpected.value === 0) return undefined;
|
if (!this.model.quantityExpected || this.model.quantityExpected.value === 0) return undefined;
|
||||||
return this.model.spaceLeft - this.totalQuantitySelected;
|
return this.model.spaceLeft - this.totalQuantitySelected;
|
||||||
},
|
},
|
||||||
libraryNames(){
|
libraryNames() {
|
||||||
let names = {};
|
let names = {};
|
||||||
Libraries.find().forEach(lib => names[lib._id] = lib.name)
|
Libraries.find().forEach(lib => names[lib._id] = lib.name)
|
||||||
return names;
|
return names;
|
||||||
},
|
},
|
||||||
libraryNodes(){
|
libraryNodes() {
|
||||||
let filter = getSlotFillFilter({slot: this.model});
|
let filter = getSlotFillFilter({ slot: this.model });
|
||||||
let nodes = LibraryNodes.find(filter, {
|
let nodes = LibraryNodes.find(filter, {
|
||||||
sort: {name: 1, order: 1}
|
sort: { name: 1, order: 1 }
|
||||||
}).fetch();
|
}).fetch();
|
||||||
let disabledNodeCount = 0;
|
let disabledNodeCount = 0;
|
||||||
// Mark slotFillers whose condition isn't met or are too big to fit
|
// Mark slotFillers whose condition isn't met or are too big to fit
|
||||||
// the quantity to fill
|
// the quantity to fill
|
||||||
nodes.forEach(node => {
|
nodes.forEach(node => {
|
||||||
if (node.slotFillerCondition){
|
if (node.slotFillerCondition) {
|
||||||
try {
|
try {
|
||||||
let parseNode = parse(node.slotFillerCondition);
|
let parseNode = parse(node.slotFillerCondition);
|
||||||
const {result: resultNode} = resolve('reduce', parseNode, this.variables);
|
const { result: resultNode } = resolve('reduce', parseNode, this.variables);
|
||||||
if (resultNode?.parseType === 'constant'){
|
if (resultNode?.parseType === 'constant') {
|
||||||
if (!resultNode.value){
|
if (!resultNode.value) {
|
||||||
node._disabledBySlotFillerCondition = true;
|
node._disabledBySlotFillerCondition = true;
|
||||||
disabledNodeCount += 1;
|
disabledNodeCount += 1;
|
||||||
}
|
}
|
||||||
@@ -374,7 +376,7 @@ export default {
|
|||||||
node._conditionError = toString(resultNode);
|
node._conditionError = toString(resultNode);
|
||||||
disabledNodeCount += 1;
|
disabledNodeCount += 1;
|
||||||
}
|
}
|
||||||
} catch (e){
|
} catch (e) {
|
||||||
console.warn(e);
|
console.warn(e);
|
||||||
let error = prettifyParseError(e);
|
let error = prettifyParseError(e);
|
||||||
node._disabledBySlotFillerCondition = true;
|
node._disabledBySlotFillerCondition = true;
|
||||||
@@ -385,10 +387,10 @@ export default {
|
|||||||
let quantityToFill = node.type === 'slotFiller' ? node.slotQuantityFilled : 1;
|
let quantityToFill = node.type === 'slotFiller' ? node.slotQuantityFilled : 1;
|
||||||
if (
|
if (
|
||||||
quantityToFill > this.spaceLeft
|
quantityToFill > this.spaceLeft
|
||||||
){
|
) {
|
||||||
node._disabledByQuantityFilled = true;
|
node._disabledByQuantityFilled = true;
|
||||||
}
|
}
|
||||||
if (this.alreadyAdded.has(node._id)){
|
if (this.alreadyAdded.has(node._id)) {
|
||||||
node._disabledByAlreadyAdded = true;
|
node._disabledByAlreadyAdded = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -400,7 +402,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css" scoped>
|
||||||
.disabled {
|
.disabled {
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user