Merge feature-nested-sets into develop
This commit is contained in:
@@ -50,3 +50,4 @@ littledata:synced-cron
|
||||
typescript@4.9.4
|
||||
seba:minifiers-autoprefixer
|
||||
mixmax:smart-disconnect
|
||||
zodern:types
|
||||
|
||||
@@ -125,3 +125,4 @@ url@1.3.2
|
||||
webapp@1.13.5
|
||||
webapp-hashing@1.1.1
|
||||
zer0th:meteor-vuetify-loader@0.1.41
|
||||
zodern:types@1.0.9
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import '/imports/api/simpleSchemaConfig.js';
|
||||
import '/imports/client/ui/vueSetup.js';
|
||||
import '/imports/client/ui/styles/stylesIndex.js';
|
||||
import '/imports/client/config.js';
|
||||
import '/imports/client/serviceWorker.js';
|
||||
import '/imports/api/simpleSchemaConfig';
|
||||
import '/imports/client/ui/vueSetup';
|
||||
import '/imports/client/ui/styles/stylesIndex';
|
||||
import '/imports/client/config';
|
||||
import '/imports/client/serviceWorker';
|
||||
|
||||
import 'ngraph.graph';
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import { incrementFileStorageUsed } from '/imports/api/users/methods/updateFileStorageUsed.js';
|
||||
import { CreaturePropertySchema } from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import { CreatureSchema } from '/imports/api/creature/creatures/Creatures.js';
|
||||
import { incrementFileStorageUsed } from '/imports/api/users/methods/updateFileStorageUsed';
|
||||
import { CreaturePropertySchema } from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import { CreatureSchema } from '/imports/api/creature/creatures/Creatures';
|
||||
let createS3FilesCollection;
|
||||
if (Meteor.isServer) {
|
||||
createS3FilesCollection = require('/imports/api/files/server/s3FileStorage.js').createS3FilesCollection
|
||||
createS3FilesCollection = require('/imports/api/files/server/s3FileStorage').createS3FilesCollection
|
||||
} else {
|
||||
createS3FilesCollection = require('/imports/api/files/client/s3FileStorage.js').createS3FilesCollection
|
||||
createS3FilesCollection = require('/imports/api/files/client/s3FileStorage').createS3FilesCollection
|
||||
}
|
||||
|
||||
const ArchiveCreatureFiles = createS3FilesCollection({
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
import SCHEMA_VERSION from '/imports/constants/SCHEMA_VERSION.js';
|
||||
import SCHEMA_VERSION from '/imports/constants/SCHEMA_VERSION';
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import { assertOwnership } from '/imports/api/creature/creatures/creaturePermissions.js';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import CreatureLogs from '/imports/api/creature/log/CreatureLogs.js';
|
||||
import Experiences from '/imports/api/creature/experience/Experiences.js';
|
||||
import { removeCreatureWork } from '/imports/api/creature/creatures/methods/removeCreature.js';
|
||||
import ArchiveCreatureFiles from '/imports/api/creature/archive/ArchiveCreatureFiles.js';
|
||||
import { assertOwnership } from '/imports/api/creature/creatures/creaturePermissions';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import CreatureLogs from '/imports/api/creature/log/CreatureLogs';
|
||||
import Experiences from '/imports/api/creature/experience/Experiences';
|
||||
import { removeCreatureWork } from '/imports/api/creature/creatures/methods/removeCreature';
|
||||
import ArchiveCreatureFiles from '/imports/api/creature/archive/ArchiveCreatureFiles';
|
||||
import { getFilter } from '/imports/api/parenting/parentingFunctions';
|
||||
|
||||
export function getArchiveObj(creatureId){
|
||||
export function getArchiveObj(creatureId) {
|
||||
// Build the archive document
|
||||
const creature = Creatures.findOne(creatureId);
|
||||
const properties = CreatureProperties.find({'ancestors.id': creatureId}).fetch();
|
||||
const experiences = Experiences.find({creatureId}).fetch();
|
||||
const logs = CreatureLogs.find({creatureId}).fetch();
|
||||
const properties = CreatureProperties.find({ ...getFilter.descendantsOfRoot(creatureId) }).fetch();
|
||||
const experiences = Experiences.find({ creatureId }).fetch();
|
||||
const logs = CreatureLogs.find({ creatureId }).fetch();
|
||||
let archiveCreature = {
|
||||
meta: {
|
||||
type: 'DiceCloud V2 Creature Archive',
|
||||
@@ -31,7 +32,7 @@ export function getArchiveObj(creatureId){
|
||||
return archiveCreature;
|
||||
}
|
||||
|
||||
export function archiveCreature(creatureId){
|
||||
export function archiveCreature(creatureId) {
|
||||
const archive = getArchiveObj(creatureId);
|
||||
const buffer = Buffer.from(JSON.stringify(archive, null, 2));
|
||||
ArchiveCreatureFiles.write(buffer, {
|
||||
@@ -44,7 +45,7 @@ export function archiveCreature(creatureId){
|
||||
creatureName: archive.creature.name,
|
||||
},
|
||||
}, (error) => {
|
||||
if (error){
|
||||
if (error) {
|
||||
throw error;
|
||||
} else {
|
||||
removeCreatureWork(creatureId);
|
||||
@@ -65,10 +66,10 @@ const archiveCreatureToFile = new ValidatedMethod({
|
||||
numRequests: 10,
|
||||
timeInterval: 5000,
|
||||
},
|
||||
async run({creatureId}) {
|
||||
async run({ creatureId }) {
|
||||
assertOwnership(creatureId, this.userId);
|
||||
if (Meteor.isServer){
|
||||
archiveCreature(creatureId, this.userId);
|
||||
if (Meteor.isServer) {
|
||||
archiveCreature(creatureId);
|
||||
} else {
|
||||
removeCreatureWork(creatureId);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import '/imports/api/creature/archive/methods/archiveCreatureToFile.js';
|
||||
import '/imports/api/creature/archive/methods/restoreCreatureFromFile.js';
|
||||
import '/imports/api/creature/archive/methods/removeArchiveCreature.js';
|
||||
import '/imports/api/creature/archive/methods/archiveCreatureToFile';
|
||||
import '/imports/api/creature/archive/methods/restoreCreatureFromFile';
|
||||
import '/imports/api/creature/archive/methods/removeArchiveCreature';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import ArchiveCreatureFiles from '/imports/api/creature/archive/ArchiveCreatureFiles.js';
|
||||
import { incrementFileStorageUsed } from '/imports/api/users/methods/updateFileStorageUsed.js';
|
||||
import ArchiveCreatureFiles from '/imports/api/creature/archive/ArchiveCreatureFiles';
|
||||
import { incrementFileStorageUsed } from '/imports/api/users/methods/updateFileStorageUsed';
|
||||
|
||||
const removeArchiveCreature = new ValidatedMethod({
|
||||
name: 'ArchiveCreatureFiles.methods.removeArchiveCreature',
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import SCHEMA_VERSION from '/imports/constants/SCHEMA_VERSION.js';
|
||||
import SCHEMA_VERSION from '/imports/constants/SCHEMA_VERSION';
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import CreatureLogs from '/imports/api/creature/log/CreatureLogs.js';
|
||||
import Experiences from '/imports/api/creature/experience/Experiences.js';
|
||||
import { removeCreatureWork } from '/imports/api/creature/creatures/methods/removeCreature.js';
|
||||
import ArchiveCreatureFiles from '/imports/api/creature/archive/ArchiveCreatureFiles.js';
|
||||
import assertHasCharactersSlots from '/imports/api/creature/creatures/methods/assertHasCharacterSlots.js';
|
||||
import { incrementFileStorageUsed } from '/imports/api/users/methods/updateFileStorageUsed.js';
|
||||
import verifyArchiveSafety from '/imports/api/creature/archive/methods/verifyArchiveSafety.js';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import CreatureLogs from '/imports/api/creature/log/CreatureLogs';
|
||||
import Experiences from '/imports/api/creature/experience/Experiences';
|
||||
import { removeCreatureWork } from '/imports/api/creature/creatures/methods/removeCreature';
|
||||
import ArchiveCreatureFiles from '/imports/api/creature/archive/ArchiveCreatureFiles';
|
||||
import assertHasCharactersSlots from '/imports/api/creature/creatures/methods/assertHasCharacterSlots';
|
||||
import { incrementFileStorageUsed } from '/imports/api/users/methods/updateFileStorageUsed';
|
||||
import verifyArchiveSafety from '/imports/api/creature/archive/methods/verifyArchiveSafety';
|
||||
|
||||
let migrateArchive;
|
||||
if (Meteor.isServer) {
|
||||
migrateArchive = require('/imports/migrations/archive/migrateArchive.js').default;
|
||||
migrateArchive = require('/imports/migrations/archive/migrateArchive').default;
|
||||
}
|
||||
|
||||
function restoreCreature(archive, userId) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { slice } from 'lodash';
|
||||
import PER_CREATURE_LOG_LIMIT from '/imports/api/creature/log/CreatureLogs.js';
|
||||
import PER_CREATURE_LOG_LIMIT from '/imports/api/creature/log/CreatureLogs';
|
||||
|
||||
export default function verifyArchiveSafety({ meta, creature, properties, experiences, logs }){
|
||||
export default function verifyArchiveSafety({ meta, creature, properties, experiences, logs }) {
|
||||
const creatureId = creature._id;
|
||||
|
||||
// Check lengths of arrays
|
||||
@@ -21,8 +21,14 @@ export default function verifyArchiveSafety({ meta, creature, properties, experi
|
||||
}
|
||||
});
|
||||
properties.forEach(prop => {
|
||||
if (prop.ancestors[0].id !== creatureId) {
|
||||
throw new Meteor.Error('Malicious prop', 'Properties contains an entry for the wrong creature');
|
||||
if (meta.schemaVersion.schemaVersion >= 3) {
|
||||
if (prop.root?.id !== creatureId) {
|
||||
throw new Meteor.Error('Malicious prop', 'Properties contains an entry for the wrong creature');
|
||||
}
|
||||
} else {
|
||||
if (prop.ancestors?.[0]?.id !== creatureId) {
|
||||
throw new Meteor.Error('Malicious prop', 'Properties contains an entry for the wrong creature');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS';
|
||||
|
||||
let CreatureFolders = new Mongo.Collection('creatureFolders');
|
||||
|
||||
@@ -35,5 +35,5 @@ let creatureFolderSchema = new SimpleSchema({
|
||||
|
||||
CreatureFolders.attachSchema(creatureFolderSchema);
|
||||
|
||||
import '/imports/api/creature/creatureFolders/methods.js/index.js';
|
||||
import '/imports/api/creature/creatureFolders/methods.js/index';
|
||||
export default CreatureFolders;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import '/imports/api/creature/creatureFolders/methods.js/insertCreatureFolder.js';
|
||||
import '/imports/api/creature/creatureFolders/methods.js/updateCreatureFolderName.js';
|
||||
import '/imports/api/creature/creatureFolders/methods.js/removeCreatureFolder.js';
|
||||
import '/imports/api/creature/creatureFolders/methods.js/moveCreatureToFolder.js';
|
||||
import '/imports/api/creature/creatureFolders/methods.js/insertCreatureFolder';
|
||||
import '/imports/api/creature/creatureFolders/methods.js/updateCreatureFolderName';
|
||||
import '/imports/api/creature/creatureFolders/methods.js/removeCreatureFolder';
|
||||
import '/imports/api/creature/creatureFolders/methods.js/moveCreatureToFolder';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import CreatureFolders from '/imports/api/creature/creatureFolders/CreatureFolders.js';
|
||||
import CreatureFolders from '/imports/api/creature/creatureFolders/CreatureFolders';
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
|
||||
@@ -15,23 +15,23 @@ const insertCreatureFolder = new ValidatedMethod({
|
||||
let userId = this.userId;
|
||||
if (!userId) {
|
||||
throw new Meteor.Error('creatureFolders.methods.insert.denied',
|
||||
'You need to be logged in to insert a folder');
|
||||
'You need to be logged in to insert a folder');
|
||||
}
|
||||
// Limit folders to 50 per user
|
||||
let existingFolders = CreatureFolders.find({
|
||||
owner: userId
|
||||
}, {
|
||||
fields: {order: 1},
|
||||
sort: {order :-1}
|
||||
fields: { order: 1 },
|
||||
sort: { order: -1 }
|
||||
});
|
||||
if (existingFolders.count() >= 50){
|
||||
if (existingFolders.count() >= 50) {
|
||||
throw new Meteor.Error('creatureFolders.methods.insert.denied',
|
||||
'You can not have more than 50 folders');
|
||||
'You can not have more than 50 folders');
|
||||
}
|
||||
// Make the new folder the last in the order
|
||||
let order = 0;
|
||||
let lastFolder = existingFolders.fetch()[0];
|
||||
if (lastFolder){
|
||||
if (lastFolder) {
|
||||
order = (lastFolder.order || 0) + 1;
|
||||
}
|
||||
// Insert
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import CreatureFolders from '/imports/api/creature/creatureFolders/CreatureFolders.js';
|
||||
import CreatureFolders from '/imports/api/creature/creatureFolders/CreatureFolders';
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
|
||||
@@ -10,33 +10,33 @@ const moveCreatureToFolder = new ValidatedMethod({
|
||||
numRequests: 5,
|
||||
timeInterval: 5000,
|
||||
},
|
||||
run({creatureId, folderId}) {
|
||||
run({ creatureId, folderId }) {
|
||||
// Ensure logged in
|
||||
let userId = this.userId;
|
||||
if (!userId) {
|
||||
throw new Meteor.Error('creatureFolders.methods.updateName.denied',
|
||||
'You need to be logged in to remove a folder');
|
||||
'You need to be logged in to remove a folder');
|
||||
}
|
||||
// Check that this folder is owned by the user
|
||||
if (folderId){
|
||||
if (folderId) {
|
||||
let existingFolder = CreatureFolders.findOne(folderId);
|
||||
if (existingFolder.owner !== userId){
|
||||
if (existingFolder.owner !== userId) {
|
||||
throw new Meteor.Error('creatureFolders.methods.updateName.denied',
|
||||
'This folder does not belong to you');
|
||||
'This folder does not belong to you');
|
||||
}
|
||||
}
|
||||
// Remove from other folders
|
||||
CreatureFolders.update({
|
||||
owner: userId
|
||||
}, {
|
||||
$pull: {creatures: creatureId},
|
||||
$pull: { creatures: creatureId },
|
||||
}, {
|
||||
multi: true,
|
||||
});
|
||||
if (folderId){
|
||||
if (folderId) {
|
||||
// Add to this folder
|
||||
CreatureFolders.update(folderId, {
|
||||
$addToSet: {creatures: creatureId},
|
||||
$addToSet: { creatures: creatureId },
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import CreatureFolders from '/imports/api/creature/creatureFolders/CreatureFolders.js';
|
||||
import CreatureFolders from '/imports/api/creature/creatureFolders/CreatureFolders';
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
|
||||
@@ -10,18 +10,18 @@ const removeCreatureFolder = new ValidatedMethod({
|
||||
numRequests: 5,
|
||||
timeInterval: 5000,
|
||||
},
|
||||
run({_id}) {
|
||||
run({ _id }) {
|
||||
// Ensure logged in
|
||||
let userId = this.userId;
|
||||
if (!userId) {
|
||||
throw new Meteor.Error('creatureFolders.methods.updateName.denied',
|
||||
'You need to be logged in to remove a folder');
|
||||
'You need to be logged in to remove a folder');
|
||||
}
|
||||
// Check that this folder is owned by the user
|
||||
let existingFolder = CreatureFolders.findOne(_id);
|
||||
if (existingFolder.owner !== userId){
|
||||
if (existingFolder.owner !== userId) {
|
||||
throw new Meteor.Error('creatureFolders.methods.updateName.denied',
|
||||
'This folder does not belong to you');
|
||||
'This folder does not belong to you');
|
||||
}
|
||||
// Remove
|
||||
return CreatureFolders.remove(_id);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import CreatureFolders from '/imports/api/creature/creatureFolders/CreatureFolders.js';
|
||||
import CreatureFolders from '/imports/api/creature/creatureFolders/CreatureFolders';
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
|
||||
@@ -10,31 +10,31 @@ const reorderCreatureFolder = new ValidatedMethod({
|
||||
numRequests: 5,
|
||||
timeInterval: 5000,
|
||||
},
|
||||
run({_id, order}) {
|
||||
run({ _id, order }) {
|
||||
// Ensure logged in
|
||||
let userId = this.userId;
|
||||
if (!userId) {
|
||||
throw new Meteor.Error('creatureFolders.methods.reorder.denied',
|
||||
'You need to be logged in to reorder a folder');
|
||||
'You need to be logged in to reorder a folder');
|
||||
}
|
||||
// Check that this folder is owned by the user
|
||||
let existingFolder = CreatureFolders.findOne(_id);
|
||||
if (existingFolder.owner !== userId){
|
||||
if (existingFolder.owner !== userId) {
|
||||
throw new Meteor.Error('creatureFolders.methods.reorder.denied',
|
||||
'This folder does not belong to you');
|
||||
'This folder does not belong to you');
|
||||
}
|
||||
// First give it the new order, it should end in 0.5 putting it between two other docs
|
||||
CreatureFolders.update(_id, {$set: {order}});
|
||||
CreatureFolders.update(_id, { $set: { order } });
|
||||
this.unblock();
|
||||
// Reorder all the folders with integer numbers in this new order
|
||||
CreatureFolders.find({
|
||||
owner: userId
|
||||
}, {
|
||||
fields: {order: 1,},
|
||||
sort: {order: -1}
|
||||
fields: { order: 1, },
|
||||
sort: { order: -1 }
|
||||
}).forEach((folder, index) => {
|
||||
if (folder.order !== index){
|
||||
CreatureFolders.update(_id, {$set: {order: index}})
|
||||
if (folder.order !== index) {
|
||||
CreatureFolders.update(_id, { $set: { order: index } })
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import CreatureFolders from '/imports/api/creature/creatureFolders/CreatureFolders.js';
|
||||
import CreatureFolders from '/imports/api/creature/creatureFolders/CreatureFolders';
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
|
||||
@@ -10,21 +10,21 @@ const updateCreatureFolderName = new ValidatedMethod({
|
||||
numRequests: 5,
|
||||
timeInterval: 5000,
|
||||
},
|
||||
run({_id, name}) {
|
||||
run({ _id, name }) {
|
||||
// Ensure logged in
|
||||
let userId = this.userId;
|
||||
if (!userId) {
|
||||
throw new Meteor.Error('creatureFolders.methods.updateName.denied',
|
||||
'You need to be logged in to update a folder');
|
||||
'You need to be logged in to update a folder');
|
||||
}
|
||||
// Check that this folder is owned by the user
|
||||
let existingFolder = CreatureFolders.findOne(_id);
|
||||
if (existingFolder.owner !== userId){
|
||||
if (existingFolder.owner !== userId) {
|
||||
throw new Meteor.Error('creatureFolders.methods.updateName.denied',
|
||||
'This folder does not belong to you');
|
||||
'This folder does not belong to you');
|
||||
}
|
||||
// Update
|
||||
return CreatureFolders.update(_id, {$set: {name}});
|
||||
return CreatureFolders.update(_id, { $set: { name } });
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,15 +1,35 @@
|
||||
import { Mongo } from 'meteor/mongo';
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import ColorSchema from '/imports/api/properties/subSchemas/ColorSchema.js';
|
||||
import ChildSchema from '/imports/api/parenting/ChildSchema.js';
|
||||
import SoftRemovableSchema from '/imports/api/parenting/SoftRemovableSchema.js';
|
||||
import propertySchemasIndex from '/imports/api/properties/computedPropertySchemasIndex.js';
|
||||
import { storedIconsSchema } from '/imports/api/icons/Icons.js';
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||
import ColorSchema from '/imports/api/properties/subSchemas/ColorSchema';
|
||||
import ChildSchema, { TreeDoc } from '/imports/api/parenting/ChildSchema';
|
||||
import SoftRemovableSchema from '/imports/api/parenting/SoftRemovableSchema';
|
||||
import propertySchemasIndex from '/imports/api/properties/computedPropertySchemasIndex';
|
||||
import { storedIconsSchema } from '/imports/api/icons/Icons';
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS';
|
||||
|
||||
let CreatureProperties = new Mongo.Collection('creatureProperties');
|
||||
const CreatureProperties: Mongo.Collection<CreatureProperty> = new Mongo.Collection('creatureProperties');
|
||||
|
||||
let CreaturePropertySchema = new SimpleSchema({
|
||||
export interface CreatureProperty extends TreeDoc {
|
||||
_id: string
|
||||
_migrationError?: string
|
||||
type: string
|
||||
tags: string[]
|
||||
disabled?: boolean
|
||||
icon?: {
|
||||
name: string
|
||||
shape: string
|
||||
},
|
||||
libraryNodeId?: string
|
||||
slotQuantityFilled?: number
|
||||
inactive?: boolean
|
||||
deactivatedByAncestor?: boolean
|
||||
deactivatedBySelf?: boolean
|
||||
deactivatedByToggle?: boolean
|
||||
deactivatingToggleId?: boolean
|
||||
dirty?: boolean
|
||||
}
|
||||
|
||||
const CreaturePropertySchema = new SimpleSchema({
|
||||
_id: {
|
||||
type: String,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
@@ -56,7 +76,7 @@ let CreaturePropertySchema = new SimpleSchema({
|
||||
|
||||
const DenormalisedOnlyCreaturePropertySchema = new SimpleSchema({
|
||||
// Denormalised flag if this property is inactive on the sheet for any reason
|
||||
// Including being disabled, or a decendent of a disabled property
|
||||
// Including being disabled, or a descendant of a disabled property
|
||||
inactive: {
|
||||
type: Boolean,
|
||||
optional: true,
|
||||
@@ -135,13 +155,14 @@ const DenormalisedOnlyCreaturePropertySchema = new SimpleSchema({
|
||||
|
||||
CreaturePropertySchema.extend(DenormalisedOnlyCreaturePropertySchema);
|
||||
|
||||
for (let key in propertySchemasIndex) {
|
||||
let schema = new SimpleSchema({});
|
||||
for (const key in propertySchemasIndex) {
|
||||
const schema = new SimpleSchema({});
|
||||
schema.extend(propertySchemasIndex[key]);
|
||||
schema.extend(CreaturePropertySchema);
|
||||
schema.extend(ColorSchema);
|
||||
schema.extend(ChildSchema);
|
||||
schema.extend(SoftRemovableSchema);
|
||||
// @ts-expect-error don't have types for .attachSchema
|
||||
CreatureProperties.attachSchema(schema, {
|
||||
selector: { type: key }
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
import { getCreature } from '/imports/api/engine/loadCreatures';
|
||||
|
||||
export default function getRootCreatureAncestor(property) {
|
||||
return getCreature(property.ancestors[0].id);
|
||||
return getCreature(property.root.id);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
|
||||
|
||||
const adjustQuantity = new ValidatedMethod({
|
||||
name: 'creatureProperties.adjustQuantity',
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import LibraryNodes from '/imports/api/library/LibraryNodes.js';
|
||||
import { RefSchema } from '/imports/api/parenting/ChildSchema.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import LibraryNodes from '/imports/api/library/LibraryNodes';
|
||||
import { RefSchema } from '/imports/api/parenting/ChildSchema';
|
||||
import {
|
||||
assertEditPermission,
|
||||
assertDocEditPermission,
|
||||
assertCopyPermission
|
||||
} from '/imports/api/sharing/sharingPermissions.js';
|
||||
} from '/imports/api/sharing/sharingPermissions';
|
||||
import {
|
||||
setLineageOfDocs,
|
||||
getAncestry,
|
||||
fetchDocByRef,
|
||||
getFilter,
|
||||
renewDocIds
|
||||
} from '/imports/api/parenting/parenting.js';
|
||||
import { reorderDocs } from '/imports/api/parenting/order.js';
|
||||
import { setDocToLastOrder } from '/imports/api/parenting/order.js';
|
||||
import Libraries from '/imports/api/library/Libraries.js';
|
||||
} from '/imports/api/parenting/parentingFunctions';
|
||||
import { rebuildNestedSets } from '/imports/api/parenting/parentingFunctions';
|
||||
import Libraries from '/imports/api/library/Libraries';
|
||||
const DUPLICATE_CHILDREN_LIMIT = 500;
|
||||
|
||||
const copyPropertyToLibrary = new ValidatedMethod({
|
||||
@@ -41,33 +40,30 @@ const copyPropertyToLibrary = new ValidatedMethod({
|
||||
},
|
||||
run({ propId, parentRef, order }) {
|
||||
// get the new ancestry for the properties
|
||||
let { parentDoc, ancestors } = getAncestry({ parentRef });
|
||||
const parentDoc = fetchDocByRef(parentRef);
|
||||
|
||||
// Check permission to edit the destination
|
||||
let rootLibrary;
|
||||
if (parentRef.collection === 'libraries') {
|
||||
rootLibrary = parentDoc;
|
||||
} else if (parentRef.collection === 'libraryNodes') {
|
||||
rootLibrary = Libraries.findOne(parentDoc.ancestors[0].id)
|
||||
rootLibrary = Libraries.findOne(parentDoc.root.id)
|
||||
} else {
|
||||
throw `${parentRef.collection} is not a valid parent collection`
|
||||
}
|
||||
assertEditPermission(rootLibrary, this.userId);
|
||||
|
||||
const insertedRootNode = insertNodeFromProperty(propId, ancestors, order, this);
|
||||
const insertedRootNode = insertNodeFromProperty(propId, order, this);
|
||||
|
||||
// Tree structure changed by inserts, reorder the tree
|
||||
reorderDocs({
|
||||
collection: LibraryNodes,
|
||||
ancestorId: rootLibrary._id,
|
||||
});
|
||||
rebuildNestedSets(LibraryNodes, rootLibrary._id);
|
||||
|
||||
// Return the docId of the inserted root property
|
||||
return insertedRootNode?._id;
|
||||
},
|
||||
});
|
||||
|
||||
function insertNodeFromProperty(propId, ancestors, order, method) {
|
||||
function insertNodeFromProperty(propId, order, method) {
|
||||
// Fetch the property and its descendants, provided they have not been
|
||||
// removed
|
||||
let prop = CreatureProperties.findOne({
|
||||
@@ -87,9 +83,9 @@ function insertNodeFromProperty(propId, ancestors, order, method) {
|
||||
// Make sure we can edit this property
|
||||
assertDocEditPermission(prop, method.userId);
|
||||
|
||||
let oldParent = prop.parent;
|
||||
let oldParentId = prop.parentId;
|
||||
const propCursor = CreatureProperties.find({
|
||||
'ancestors.id': propId,
|
||||
...getFilter.descendants(prop),
|
||||
removed: { $ne: true },
|
||||
});
|
||||
|
||||
@@ -109,13 +105,6 @@ function insertNodeFromProperty(propId, ancestors, order, method) {
|
||||
// properties
|
||||
assertSourceLibraryCopyPermission(props, method);
|
||||
|
||||
// re-map all the ancestors
|
||||
setLineageOfDocs({
|
||||
docArray: props,
|
||||
newAncestry: ancestors,
|
||||
oldParent,
|
||||
});
|
||||
|
||||
// Give the docs new IDs without breaking internal references
|
||||
renewDocIds({
|
||||
docArray: props,
|
||||
@@ -123,14 +112,8 @@ function insertNodeFromProperty(propId, ancestors, order, method) {
|
||||
});
|
||||
|
||||
// Order the root node
|
||||
if (order === undefined) {
|
||||
setDocToLastOrder({
|
||||
collection: LibraryNodes,
|
||||
doc: prop,
|
||||
});
|
||||
} else {
|
||||
prop.order = order;
|
||||
}
|
||||
prop.left = Number.MAX_SAFE_INTEGER - 1;
|
||||
prop.right = Number.MAX_SAFE_INTEGER;
|
||||
|
||||
// Clean the props
|
||||
props = cleanProps(props);
|
||||
@@ -142,8 +125,8 @@ function insertNodeFromProperty(propId, ancestors, order, method) {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {[Property]} props The properties to check
|
||||
* @param {String} userId The userId trying to copy these properties to a library
|
||||
* @param props The properties to check
|
||||
* @param userId The userId trying to copy these properties to a library
|
||||
* Checks that every property can be copied out of the library that originated it by this user
|
||||
*/
|
||||
function assertSourceLibraryCopyPermission(props, method) {
|
||||
@@ -162,9 +145,9 @@ function assertSourceLibraryCopyPermission(props, method) {
|
||||
LibraryNodes.find({
|
||||
_id: { $in: libraryNodeIds }
|
||||
}, {
|
||||
fields: { ancestors: 1 }
|
||||
fields: { root: 1 }
|
||||
}).forEach(node => {
|
||||
sourceLibIds.add(node.ancestors?.[0]?.id);
|
||||
sourceLibIds.add(node.root.id);
|
||||
});
|
||||
|
||||
// Assert copy permission on each of those libraries
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
|
||||
import { applyTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||
import ActionContext from '/imports/api/engine/actions/ActionContext.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
|
||||
import { applyTriggers } from '/imports/api/engine/actions/applyTriggers';
|
||||
import ActionContext from '/imports/api/engine/actions/ActionContext';
|
||||
|
||||
const damageProperty = new ValidatedMethod({
|
||||
name: 'creatureProperties.damage',
|
||||
@@ -28,7 +28,7 @@ const damageProperty = new ValidatedMethod({
|
||||
if (!prop) throw new Meteor.Error(
|
||||
'Damage property failed', 'Property doesn\'t exist'
|
||||
);
|
||||
const creatureId = prop.ancestors[0].id;
|
||||
const creatureId = prop.root.id;
|
||||
const actionContext = new ActionContext(creatureId, [creatureId], this);
|
||||
|
||||
// Check permissions
|
||||
@@ -59,7 +59,7 @@ const damageProperty = new ValidatedMethod({
|
||||
},
|
||||
});
|
||||
|
||||
export function damagePropertyWork({ prop, operation, value, actionContext, logFunction }) {
|
||||
export function damagePropertyWork({ prop, operation, value, actionContext, logFunction = undefined }) {
|
||||
|
||||
// Save the value to the scope before applying the before triggers
|
||||
if (operation === 'increment') {
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
|
||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
|
||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor';
|
||||
import {
|
||||
setLineageOfDocs,
|
||||
getFilter,
|
||||
renewDocIds
|
||||
} from '/imports/api/parenting/parenting.js';
|
||||
import { reorderDocs } from '/imports/api/parenting/order.js';
|
||||
} from '/imports/api/parenting/parentingFunctions';
|
||||
import { rebuildNestedSets } from '/imports/api/parenting/parentingFunctions';
|
||||
var snackbar;
|
||||
if (Meteor.isClient) {
|
||||
snackbar = require(
|
||||
'/imports/client/ui/components/snackbars/SnackbarQueue.js'
|
||||
'/imports/client/ui/components/snackbars/SnackbarQueue'
|
||||
).snackbar
|
||||
}
|
||||
|
||||
@@ -33,6 +33,8 @@ const duplicateProperty = new ValidatedMethod({
|
||||
},
|
||||
run({ _id }) {
|
||||
let property = CreatureProperties.findOne(_id);
|
||||
if (!property) throw new Meteor.Error('not-found', 'The source property was not found');
|
||||
|
||||
let creature = getRootCreatureAncestor(property);
|
||||
|
||||
assertEditPermission(creature, this.userId);
|
||||
@@ -49,7 +51,7 @@ const duplicateProperty = new ValidatedMethod({
|
||||
|
||||
// Get all the descendants
|
||||
let nodes = CreatureProperties.find({
|
||||
'ancestors.id': _id,
|
||||
...getFilter.descendants(property),
|
||||
removed: { $ne: true },
|
||||
}, {
|
||||
limit: DUPLICATE_CHILDREN_LIMIT + 1,
|
||||
@@ -66,22 +68,13 @@ const duplicateProperty = new ValidatedMethod({
|
||||
}
|
||||
}
|
||||
|
||||
// re-map all the ancestors
|
||||
setLineageOfDocs({
|
||||
docArray: nodes,
|
||||
newAncestry: [
|
||||
...property.ancestors,
|
||||
{ id: propertyId, collection: 'creatureProperties' }
|
||||
],
|
||||
oldParent: { id: _id, collection: 'creatureProperties' },
|
||||
});
|
||||
|
||||
// Give the docs new IDs without breaking internal references
|
||||
const allNodes = [property, ...nodes];
|
||||
renewDocIds({ docArray: allNodes });
|
||||
|
||||
// Order the root node
|
||||
property.order += 0.5;
|
||||
property.left = Number.MAX_SAFE_INTEGER - 1;
|
||||
property.right = Number.MAX_SAFE_INTEGER;
|
||||
|
||||
// Mark the sheet as needing recompute
|
||||
property.dirty = true;
|
||||
@@ -90,10 +83,7 @@ const duplicateProperty = new ValidatedMethod({
|
||||
CreatureProperties.batchInsert(allNodes);
|
||||
|
||||
// Tree structure changed by inserts, reorder the tree
|
||||
reorderDocs({
|
||||
collection: CreatureProperties,
|
||||
ancestorId: property.ancestors[0].id,
|
||||
});
|
||||
rebuildNestedSets(CreatureProperties, property.root.id);
|
||||
|
||||
return propertyId;
|
||||
},
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
|
||||
import { organizeDoc } from '/imports/api/parenting/organizeMethods.js';
|
||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
|
||||
import BUILT_IN_TAGS from '/imports/constants/BUILT_IN_TAGS.js';
|
||||
import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/getParentRefByTag.js';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
|
||||
import { organizeDoc } from '/imports/api/parenting/organizeMethods';
|
||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor';
|
||||
import BUILT_IN_TAGS from '/imports/constants/BUILT_IN_TAGS';
|
||||
import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/getParentRefByTag';
|
||||
|
||||
// Equipping or unequipping an item will also change its parent
|
||||
const equipItem = new ValidatedMethod({
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
|
||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
|
||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor';
|
||||
|
||||
const flipToggle = new ValidatedMethod({
|
||||
name: 'creatureProperties.flipToggle',
|
||||
@@ -17,7 +17,7 @@ const flipToggle = new ValidatedMethod({
|
||||
run({ _id }) {
|
||||
// Permission
|
||||
let property = CreatureProperties.findOne(_id, {
|
||||
fields: { type: 1, ancestors: 1, enabled: 1, disabled: 1 }
|
||||
fields: { type: 1, root: 1, enabled: 1, disabled: 1 }
|
||||
});
|
||||
if (property.type !== 'toggle') {
|
||||
throw new Meteor.Error('wrong property',
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import { getFilter } from '/imports/api/parenting/parentingFunctions';
|
||||
|
||||
export default function getParentRefByTag(creatureId, tag){
|
||||
export default function getParentRefByTag(creatureId, tag) {
|
||||
let prop = CreatureProperties.findOne({
|
||||
'ancestors.id': creatureId,
|
||||
removed: {$ne: true},
|
||||
inactive: {$ne: true},
|
||||
...getFilter.descendantsOfRoot(creatureId),
|
||||
removed: { $ne: true },
|
||||
inactive: { $ne: true },
|
||||
tags: tag,
|
||||
}, {
|
||||
sort: {order: 1},
|
||||
sort: { order: 1 },
|
||||
});
|
||||
return prop && {id: prop._id, collection: 'creatureProperties'};
|
||||
return prop && { id: prop._id, collection: 'creatureProperties' };
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { getFilter } from "/imports/api/parenting/parentingFunctions";
|
||||
|
||||
export default function getSlotFillFilter({ slot, libraryIds }) {
|
||||
|
||||
if (!slot) throw 'Slot is required for getSlotFillFilter';
|
||||
@@ -6,9 +8,14 @@ export default function getSlotFillFilter({ slot, libraryIds }) {
|
||||
let filter = {
|
||||
fillSlots: true,
|
||||
removed: { $ne: true },
|
||||
$and: []
|
||||
$and: [],
|
||||
};
|
||||
filter['ancestors.id'] = { $in: libraryIds };
|
||||
if (libraryIds.length) {
|
||||
Object.assign(
|
||||
filter,
|
||||
getFilter.descendantsOfAllRoots(libraryIds)
|
||||
);
|
||||
}
|
||||
if (slot.slotType) {
|
||||
filter.$and.push({
|
||||
$or: [{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { assert } from 'chai';
|
||||
import getSlotFillFilter from '/imports/api/creature/creatureProperties/methods/getSlotFillFilter.js';
|
||||
import getSlotFillFilter from '/imports/api/creature/creatureProperties/methods/getSlotFillFilter';
|
||||
|
||||
describe('Slot fill filter', function () {
|
||||
|
||||
@@ -33,7 +33,7 @@ describe('Slot fill filter', function () {
|
||||
$or: [{
|
||||
libraryTags: { $all: ['tag1', 'tag2'] }
|
||||
}],
|
||||
'ancestors.id': { $in: ['libraryId1', 'libraryId2'] },
|
||||
'root.id': { $in: ['libraryId1', 'libraryId2'] },
|
||||
removed: { $ne: true },
|
||||
fillSlots: true,
|
||||
});
|
||||
@@ -76,7 +76,7 @@ describe('Slot fill filter', function () {
|
||||
$and: [
|
||||
{ libraryTags: { $nin: ['tag5', 'tag6', 'tag7', 'tag8'] } },
|
||||
],
|
||||
'ancestors.id': { $in: ['libraryId1', 'libraryId2'] },
|
||||
'root.id': { $in: ['libraryId1', 'libraryId2'] },
|
||||
removed: { $ne: true },
|
||||
fillSlots: true,
|
||||
});
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import '/imports/api/creature/creatureProperties/methods/adjustQuantity.js';
|
||||
import '/imports/api/creature/creatureProperties/methods/copyPropertyToLibrary.js';
|
||||
import '/imports/api/creature/creatureProperties/methods/damageProperty.js';
|
||||
import '/imports/api/creature/creatureProperties/methods/duplicateProperty.js';
|
||||
import '/imports/api/creature/creatureProperties/methods/equipItem.js';
|
||||
import '/imports/api/creature/creatureProperties/methods/insertProperty.js';
|
||||
import '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js';
|
||||
import '/imports/api/creature/creatureProperties/methods/pullFromProperty.js';
|
||||
import '/imports/api/creature/creatureProperties/methods/pushToProperty.js';
|
||||
import '/imports/api/creature/creatureProperties/methods/restoreProperty.js';
|
||||
import '/imports/api/creature/creatureProperties/methods/selectAmmoItem.js';
|
||||
import '/imports/api/creature/creatureProperties/methods/softRemoveProperty.js';
|
||||
import '/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js';
|
||||
import '/imports/api/creature/creatureProperties/methods/flipToggle.js';
|
||||
import '/imports/api/creature/creatureProperties/methods/adjustQuantity';
|
||||
import '/imports/api/creature/creatureProperties/methods/copyPropertyToLibrary';
|
||||
import '/imports/api/creature/creatureProperties/methods/damageProperty';
|
||||
import '/imports/api/creature/creatureProperties/methods/duplicateProperty';
|
||||
import '/imports/api/creature/creatureProperties/methods/equipItem';
|
||||
import '/imports/api/creature/creatureProperties/methods/insertProperty';
|
||||
import '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode';
|
||||
import '/imports/api/creature/creatureProperties/methods/pullFromProperty';
|
||||
import '/imports/api/creature/creatureProperties/methods/pushToProperty';
|
||||
import '/imports/api/creature/creatureProperties/methods/restoreProperty';
|
||||
import '/imports/api/creature/creatureProperties/methods/selectAmmoItem';
|
||||
import '/imports/api/creature/creatureProperties/methods/softRemoveProperty';
|
||||
import '/imports/api/creature/creatureProperties/methods/updateCreatureProperty';
|
||||
import '/imports/api/creature/creatureProperties/methods/flipToggle';
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor';
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
|
||||
import { reorderDocs } from '/imports/api/parenting/order.js';
|
||||
import { getAncestry } from '/imports/api/parenting/parenting.js';
|
||||
import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/getParentRefByTag.js';
|
||||
import { RefSchema } from '/imports/api/parenting/ChildSchema.js';
|
||||
import { getHighestOrder } from '/imports/api/parenting/order.js';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
|
||||
import { fetchDocByRef, rebuildNestedSets } from '/imports/api/parenting/parentingFunctions';
|
||||
import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/getParentRefByTag';
|
||||
import { RefSchema } from '/imports/api/parenting/ChildSchema';
|
||||
|
||||
const insertProperty = new ValidatedMethod({
|
||||
name: 'creatureProperties.insert',
|
||||
@@ -25,27 +23,23 @@ const insertProperty = new ValidatedMethod({
|
||||
timeInterval: 5000,
|
||||
},
|
||||
run({ creatureProperty, parentRef }) {
|
||||
// get the new ancestry for the properties
|
||||
let { parentDoc, ancestors } = getAncestry({ parentRef });
|
||||
let rootCreature;
|
||||
const parentDoc = fetchDocByRef(parentRef);
|
||||
|
||||
// Check permission to edit
|
||||
let rootCreature;
|
||||
if (parentRef.collection === 'creatures') {
|
||||
rootCreature = parentDoc;
|
||||
} else if (parentRef.collection === 'creatureProperties') {
|
||||
rootCreature = getRootCreatureAncestor(parentDoc);
|
||||
creatureProperty.parentId = parentDoc._id;
|
||||
} else {
|
||||
throw `${parentRef.collection} is not a valid parent collection`
|
||||
}
|
||||
assertEditPermission(rootCreature, this.userId);
|
||||
|
||||
creatureProperty.parent = parentRef;
|
||||
creatureProperty.ancestors = ancestors;
|
||||
creatureProperty.root = { collection: 'creatures', id: rootCreature._id };
|
||||
|
||||
return insertPropertyWork({
|
||||
property: creatureProperty,
|
||||
creature: rootCreature,
|
||||
});
|
||||
return insertPropertyWork(creatureProperty);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -77,18 +71,17 @@ const insertPropertyAsChildOfTag = new ValidatedMethod({
|
||||
},
|
||||
run({ creatureProperty, creatureId, tag, tagDefaultName }) {
|
||||
let parentRef = getParentRefByTag(creatureId, tag);
|
||||
let insertFolderFirst = false;
|
||||
|
||||
if (!parentRef) {
|
||||
// Use the creature as the parent and mark that we need to insert the folder first later
|
||||
var insertFolderFirst = true;
|
||||
insertFolderFirst = true;
|
||||
parentRef = { id: creatureId, collection: 'creatures' };
|
||||
}
|
||||
|
||||
// get the new ancestry for the properties
|
||||
let { parentDoc, ancestors } = getAncestry({ parentRef });
|
||||
|
||||
// Check permission to edit
|
||||
let rootCreature;
|
||||
const parentDoc = fetchDocByRef(parentRef);
|
||||
if (parentRef.collection === 'creatures') {
|
||||
rootCreature = parentDoc;
|
||||
} else if (parentRef.collection === 'creatureProperties') {
|
||||
@@ -98,46 +91,34 @@ const insertPropertyAsChildOfTag = new ValidatedMethod({
|
||||
}
|
||||
assertEditPermission(rootCreature, this.userId);
|
||||
|
||||
const root = { collection: 'creatures', id: rootCreature._id };
|
||||
|
||||
// Add the folder first if we need to
|
||||
if (insertFolderFirst) {
|
||||
let order = getHighestOrder({
|
||||
collection: CreatureProperties,
|
||||
ancestorId: parentRef.id,
|
||||
}) + 1;
|
||||
let id = CreatureProperties.insert({
|
||||
type: 'folder',
|
||||
name: tagDefaultName || (tag.charAt(0).toUpperCase() + tag.slice(1)),
|
||||
tags: [tag],
|
||||
parent: parentRef,
|
||||
ancestors: [parentRef],
|
||||
order,
|
||||
// parentId: undefined,
|
||||
root,
|
||||
});
|
||||
// Make the folder our new parent
|
||||
let newParentRef = { id, collection: 'creatureProperties' };
|
||||
ancestors = [parentRef, newParentRef];
|
||||
parentRef = newParentRef;
|
||||
creatureProperty.order = order + 1;
|
||||
parentRef = { id, collection: 'creatureProperties' };
|
||||
}
|
||||
|
||||
creatureProperty.parent = parentRef;
|
||||
creatureProperty.ancestors = ancestors;
|
||||
creatureProperty.root = root;
|
||||
creatureProperty.parentId = parentRef.id;
|
||||
|
||||
return insertPropertyWork({
|
||||
property: creatureProperty,
|
||||
creature: rootCreature,
|
||||
});
|
||||
return insertPropertyWork(creatureProperty);
|
||||
},
|
||||
});
|
||||
|
||||
export function insertPropertyWork({ property, creature }) {
|
||||
export function insertPropertyWork(property) {
|
||||
delete property._id;
|
||||
property.dirty = true;
|
||||
let _id = CreatureProperties.insert(property);
|
||||
// Tree structure changed by insert, reorder the tree
|
||||
reorderDocs({
|
||||
collection: CreatureProperties,
|
||||
ancestorId: creature._id,
|
||||
});
|
||||
rebuildNestedSets(CreatureProperties, property.root.id);
|
||||
return _id;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import LibraryNodes from '/imports/api/library/LibraryNodes.js';
|
||||
import { RefSchema } from '/imports/api/parenting/ChildSchema.js';
|
||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import LibraryNodes from '/imports/api/library/LibraryNodes';
|
||||
import { RefSchema } from '/imports/api/parenting/ChildSchema';
|
||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
|
||||
import {
|
||||
setLineageOfDocs,
|
||||
getAncestry,
|
||||
renewDocIds
|
||||
} from '/imports/api/parenting/parenting.js';
|
||||
import { reorderDocs } from '/imports/api/parenting/order.js';
|
||||
import { setDocToLastOrder } from '/imports/api/parenting/order.js';
|
||||
import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js';
|
||||
renewDocIds,
|
||||
fetchDocByRef,
|
||||
rebuildNestedSets,
|
||||
getFilter
|
||||
} from '/imports/api/parenting/parentingFunctions';
|
||||
import { union } from 'lodash';
|
||||
|
||||
const insertPropertyFromLibraryNode = new ValidatedMethod({
|
||||
@@ -30,19 +28,15 @@ const insertPropertyFromLibraryNode = new ValidatedMethod({
|
||||
parentRef: {
|
||||
type: RefSchema,
|
||||
},
|
||||
order: {
|
||||
type: Number,
|
||||
optional: true,
|
||||
},
|
||||
}).validator(),
|
||||
mixins: [RateLimiterMixin],
|
||||
rateLimit: {
|
||||
numRequests: 5,
|
||||
timeInterval: 5000,
|
||||
},
|
||||
run({ nodeIds, parentRef, order }) {
|
||||
run({ nodeIds, parentRef }) {
|
||||
// get the new ancestry for the properties
|
||||
let { parentDoc, ancestors } = getAncestry({ parentRef });
|
||||
const parentDoc = fetchDocByRef(parentRef);
|
||||
|
||||
// Check permission to edit
|
||||
let rootCreature;
|
||||
@@ -55,37 +49,32 @@ const insertPropertyFromLibraryNode = new ValidatedMethod({
|
||||
}
|
||||
assertEditPermission(rootCreature, this.userId);
|
||||
|
||||
// {libraryId: hasViewPermission}
|
||||
//let libraryPermissionMemoir = {};
|
||||
const root = { collection: 'creatures', id: rootCreature._id };
|
||||
const parentId = parentRef.id;
|
||||
|
||||
let node;
|
||||
nodeIds.forEach(nodeId => {
|
||||
// TODO: Check library view permission for each node before starting
|
||||
node = insertPropertyFromNode(nodeId, ancestors, order);
|
||||
node = insertPropertyFromNode(nodeId, root, parentId);
|
||||
});
|
||||
|
||||
// get one of the root inserted docs
|
||||
let rootId = node._id;
|
||||
|
||||
// Tree structure changed by inserts, reorder the tree
|
||||
reorderDocs({
|
||||
collection: CreatureProperties,
|
||||
ancestorId: rootCreature._id,
|
||||
});
|
||||
// Return the docId of the last property, the inserted root property
|
||||
return rootId;
|
||||
rebuildNestedSets(CreatureProperties, rootCreature._id);
|
||||
|
||||
// get one of the root inserted docs
|
||||
const lastInsertedId = node?._id;
|
||||
return lastInsertedId;
|
||||
},
|
||||
});
|
||||
|
||||
function insertPropertyFromNode(nodeId, ancestors, order) {
|
||||
// Fetch the library node and its decendents, provided they have not been
|
||||
function insertPropertyFromNode(nodeId, root, parentId) {
|
||||
// Fetch the library node and its descendants, provided they have not been
|
||||
// removed
|
||||
// TODO: Check permission to read the library this node is in
|
||||
let node = LibraryNodes.findOne({
|
||||
_id: nodeId,
|
||||
removed: { $ne: true },
|
||||
});
|
||||
if (!node) {
|
||||
if (Meteor.isClient) return;
|
||||
if (Meteor.isClient) return {};
|
||||
else {
|
||||
throw new Meteor.Error(
|
||||
'Insert property from library failed',
|
||||
@@ -93,13 +82,12 @@ function insertPropertyFromNode(nodeId, ancestors, order) {
|
||||
);
|
||||
}
|
||||
}
|
||||
let oldParent = node.parent;
|
||||
|
||||
let nodes = LibraryNodes.find({
|
||||
'ancestors.id': nodeId,
|
||||
...getFilter.descendants(node),
|
||||
removed: { $ne: true },
|
||||
}).fetch();
|
||||
|
||||
|
||||
// The root node is first in the array of nodes
|
||||
// It must get the first generated ID to prevent flickering
|
||||
nodes = [node, ...nodes];
|
||||
@@ -112,31 +100,17 @@ function insertPropertyFromNode(nodeId, ancestors, order) {
|
||||
// set libraryNodeIds
|
||||
storeLibraryNodeReferences(nodes);
|
||||
|
||||
// re-map all the ancestors
|
||||
setLineageOfDocs({
|
||||
docArray: nodes,
|
||||
newAncestry: ancestors,
|
||||
oldParent,
|
||||
});
|
||||
|
||||
// Give the docs new IDs without breaking internal references
|
||||
renewDocIds({
|
||||
docArray: nodes,
|
||||
collectionMap: { 'libraryNodes': 'creatureProperties' }
|
||||
});
|
||||
|
||||
// Order the root node
|
||||
if (order === undefined) {
|
||||
setDocToLastOrder({
|
||||
collection: CreatureProperties,
|
||||
doc: node,
|
||||
});
|
||||
} else {
|
||||
node.order = order;
|
||||
}
|
||||
// Mark root node as dirty
|
||||
node.dirty = true;
|
||||
|
||||
// Mark all nodes as dirty
|
||||
dirtyNodes(nodes);
|
||||
// Move the root node to the end of the order
|
||||
node.left = Number.MAX_SAFE_INTEGER;
|
||||
|
||||
// Insert the creature properties
|
||||
CreatureProperties.batchInsert(nodes);
|
||||
@@ -150,12 +124,6 @@ function storeLibraryNodeReferences(nodes) {
|
||||
});
|
||||
}
|
||||
|
||||
function dirtyNodes(nodes) {
|
||||
nodes.forEach(node => {
|
||||
node.dirty = true;
|
||||
});
|
||||
}
|
||||
|
||||
// Covert node references into actual nodes
|
||||
// TODO: check permissions for each library a reference node references
|
||||
function reifyNodeReferences(nodes, visitedRefs = new Set(), depth = 0) {
|
||||
@@ -178,7 +146,6 @@ function reifyNodeReferences(nodes, visitedRefs = new Set(), depth = 0) {
|
||||
let referencedNode
|
||||
try {
|
||||
referencedNode = fetchDocByRef(node.ref);
|
||||
referencedNode.order = node.order;
|
||||
referencedNode.tags = union(node.tags, referencedNode.tags);
|
||||
// We are definitely replacing this node, so add it to the list
|
||||
visitedRefs.add(node._id);
|
||||
@@ -188,23 +155,15 @@ function reifyNodeReferences(nodes, visitedRefs = new Set(), depth = 0) {
|
||||
}
|
||||
|
||||
// Get all the descendants of the referenced node
|
||||
let descendents = LibraryNodes.find({
|
||||
'ancestors.id': referencedNode._id,
|
||||
let descendants = LibraryNodes.find({
|
||||
...getFilter.descendants(referencedNode),
|
||||
removed: { $ne: true },
|
||||
}, {
|
||||
sort: { order: 1 },
|
||||
}).fetch();
|
||||
|
||||
// We are adding the referenced node and its descendants
|
||||
let addedNodes = [referencedNode, ...descendents];
|
||||
|
||||
// re-map all the ancestors to parent the new sub-tree into our existing
|
||||
// node tree
|
||||
setLineageOfDocs({
|
||||
docArray: addedNodes,
|
||||
newAncestry: node.ancestors,
|
||||
oldParent: referencedNode.parent,
|
||||
});
|
||||
let addedNodes = [referencedNode, ...descendants];
|
||||
|
||||
// Filter all the looped references
|
||||
addedNodes = addedNodes.filter(addedNode => {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
|
||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
|
||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor';
|
||||
|
||||
const pullFromProperty = new ValidatedMethod({
|
||||
name: 'creatureProperties.pull',
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
|
||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
|
||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor';
|
||||
import { get } from 'lodash';
|
||||
|
||||
const pushToProperty = new ValidatedMethod({
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
|
||||
import { restore } from '/imports/api/parenting/softRemove.js';
|
||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
|
||||
import { restore } from '/imports/api/parenting/softRemove';
|
||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor';
|
||||
|
||||
const restoreProperty = new ValidatedMethod({
|
||||
name: 'creatureProperties.restore',
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
|
||||
|
||||
const selectAmmoItem = new ValidatedMethod({
|
||||
name: 'creatureProperties.selectAmmoItem',
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
|
||||
import { softRemove } from '/imports/api/parenting/softRemove.js';
|
||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
|
||||
import { softRemove } from '/imports/api/parenting/softRemove';
|
||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor';
|
||||
|
||||
const softRemoveProperty = new ValidatedMethod({
|
||||
name: 'creatureProperties.softRemove',
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
|
||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
|
||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor';
|
||||
|
||||
const updateCreatureProperty = new ValidatedMethod({
|
||||
name: 'creatureProperties.update',
|
||||
@@ -14,6 +14,8 @@ const updateCreatureProperty = new ValidatedMethod({
|
||||
case 'order':
|
||||
case 'parent':
|
||||
case 'ancestors':
|
||||
case 'root':
|
||||
case 'parentId':
|
||||
case 'damage':
|
||||
throw new Meteor.Error('Permission denied',
|
||||
'This property can\'t be updated directly');
|
||||
@@ -27,7 +29,7 @@ const updateCreatureProperty = new ValidatedMethod({
|
||||
run({ _id, path, value }) {
|
||||
// Permission
|
||||
let property = CreatureProperties.findOne(_id, {
|
||||
fields: { type: 1, ancestors: 1 }
|
||||
fields: { type: 1, root: 1 }
|
||||
});
|
||||
let rootCreature = getRootCreatureAncestor(property);
|
||||
assertEditPermission(rootCreature, this.userId);
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import computeCreature from '/imports/api/engine/computeCreature.js';
|
||||
import computeCreature from '/imports/api/engine/computeCreature';
|
||||
|
||||
/**
|
||||
* Recomputes all ancestor creatures of this property
|
||||
* @deprecated
|
||||
*/
|
||||
export default function recomputeCreaturesByProperty(property){
|
||||
for (let ref of property.ancestors){
|
||||
if (ref.collection === 'creatures') {
|
||||
computeCreature.call(ref.id);
|
||||
}
|
||||
}
|
||||
export default function recomputeCreaturesByProperty(property) {
|
||||
computeCreature.call(property.root.id);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import deathSaveSchema from '/imports/api/properties/subSchemas/DeathSavesSchema.js'
|
||||
import ColorSchema from '/imports/api/properties/subSchemas/ColorSchema.js';
|
||||
import SharingSchema from '/imports/api/sharing/SharingSchema.js';
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||
import deathSaveSchema from '/imports/api/properties/subSchemas/DeathSavesSchema'
|
||||
import ColorSchema from '/imports/api/properties/subSchemas/ColorSchema';
|
||||
import SharingSchema from '/imports/api/sharing/SharingSchema';
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS';
|
||||
|
||||
//set up the collection for creatures
|
||||
let Creatures = new Mongo.Collection('creatures');
|
||||
@@ -195,4 +195,4 @@ Creatures.attachSchema(CreatureSchema);
|
||||
export default Creatures;
|
||||
export { CreatureSchema };
|
||||
|
||||
import '/imports/api/engine/actions/doAction.js';
|
||||
import '/imports/api/engine/actions/doAction';
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures';
|
||||
import {
|
||||
assertEditPermission as editPermission,
|
||||
assertViewPermission as viewPermission,
|
||||
assertOwnership as ownership
|
||||
} from '/imports/api/sharing/sharingPermissions.js';
|
||||
} from '/imports/api/sharing/sharingPermissions';
|
||||
|
||||
function getCreature(creature, fields){
|
||||
if (typeof creature === 'string'){
|
||||
return Creatures.findOne(creature, {fields});
|
||||
function getCreature(creature, fields) {
|
||||
if (typeof creature === 'string') {
|
||||
return Creatures.findOne(creature, { fields });
|
||||
} else {
|
||||
return creature;
|
||||
}
|
||||
}
|
||||
|
||||
export function assertOwnership(creature, userId){
|
||||
creature = getCreature(creature, {owner: 1});
|
||||
export function assertOwnership(creature, userId) {
|
||||
creature = getCreature(creature, { owner: 1 });
|
||||
ownership(creature, userId);
|
||||
}
|
||||
|
||||
export function assertEditPermission(creature, userId) {
|
||||
creature = getCreature(creature, {owner: 1, writers: 1});
|
||||
creature = getCreature(creature, { owner: 1, writers: 1 });
|
||||
editPermission(creature, userId);
|
||||
}
|
||||
|
||||
export function assertViewPermission(creature, userId) {
|
||||
creature = getCreature(creature, {owner: 1, readers:1, writers: 1, public: 1});
|
||||
creature = getCreature(creature, { owner: 1, readers: 1, writers: 1, public: 1 });
|
||||
viewPermission(creature, userId);
|
||||
}
|
||||
|
||||
@@ -1,47 +1,50 @@
|
||||
import BUILT_IN_TAGS from '/imports/constants/BUILT_IN_TAGS.js';
|
||||
import BUILT_IN_TAGS from '/imports/constants/BUILT_IN_TAGS';
|
||||
|
||||
export default function defaultCharacterProperties(creatureId){
|
||||
export default function defaultCharacterProperties(creatureId) {
|
||||
if (!creatureId) throw 'creatureId is required';
|
||||
const creatureRef = {collection: 'creatures', id: creatureId};
|
||||
const creatureRef = { collection: 'creatures', id: creatureId };
|
||||
let randomSrc = DDP.randomStream('defaultProperties');
|
||||
const inventoryId = randomSrc.id();
|
||||
const inventoryRef = {collection: 'creatureProperties', id: inventoryId};
|
||||
return [
|
||||
{
|
||||
type: 'propertySlot',
|
||||
name: 'Ruleset',
|
||||
description: {text: 'Choose a starting point for your character, this will define the basic setup of your character sheet. Without a base ruleset, your sheet will be empty.'},
|
||||
description: { text: 'Choose a starting point for your character, this will define the basic setup of your character sheet. Without a base ruleset, your sheet will be empty.' },
|
||||
slotTags: ['base'],
|
||||
tags: [],
|
||||
quantityExpected: {calculation: '1'},
|
||||
quantityExpected: { calculation: '1' },
|
||||
hideWhenFull: true,
|
||||
spaceLeft: 1,
|
||||
totalFilled: 0,
|
||||
order: 0,
|
||||
parent: creatureRef,
|
||||
ancestors: [creatureRef],
|
||||
left: 1,
|
||||
right: 2,
|
||||
parentId: creatureId,
|
||||
root: creatureRef,
|
||||
}, {
|
||||
_id: inventoryId,
|
||||
type: 'folder',
|
||||
name: 'Inventory',
|
||||
tags: [BUILT_IN_TAGS.inventory],
|
||||
order: 1,
|
||||
parent: creatureRef,
|
||||
ancestors: [creatureRef],
|
||||
left: 3,
|
||||
right: 8,
|
||||
parentId: creatureId,
|
||||
root: creatureRef,
|
||||
}, {
|
||||
type: 'folder',
|
||||
name: 'Equipment',
|
||||
tags: [BUILT_IN_TAGS.equipment],
|
||||
order: 2,
|
||||
parent: inventoryRef,
|
||||
ancestors: [creatureRef, inventoryRef],
|
||||
left: 4,
|
||||
right: 5,
|
||||
parentId: inventoryId,
|
||||
root: creatureRef,
|
||||
}, {
|
||||
type: 'folder',
|
||||
name: 'Carried',
|
||||
tags: [BUILT_IN_TAGS.carried],
|
||||
order: 3,
|
||||
parent: inventoryRef,
|
||||
ancestors: [creatureRef, inventoryRef],
|
||||
left: 6,
|
||||
right: 7,
|
||||
parent: inventoryId,
|
||||
root: creatureRef,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { getUserTier } from '/imports/api/users/patreon/tiers.js';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||
import { getUserTier } from '/imports/api/users/patreon/tiers';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures';
|
||||
|
||||
export default function assertHasCharactersSlots(userId) {
|
||||
if (characterSlotsRemaining(userId) <= 0) {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import simpleSchemaMixin from '/imports/api/creature/mixins/simpleSchemaMixin.js';
|
||||
import simpleSchemaMixin from '/imports/api/creature/mixins/simpleSchemaMixin';
|
||||
|
||||
const changeAllowedLibraries = new ValidatedMethod({
|
||||
name: 'creatures.changeAllowedLibraries',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import '/imports/api/creature/creatures/methods/insertCreature.js';
|
||||
import '/imports/api/creature/creatures/methods/removeCreature.js';
|
||||
import '/imports/api/creature/creatures/methods/restCreature.js';
|
||||
import '/imports/api/creature/creatures/methods/updateCreature.js';
|
||||
import '/imports/api/creature/creatures/methods/changeAllowedLibraries.js';
|
||||
import '/imports/api/creature/creatures/methods/insertCreature';
|
||||
import '/imports/api/creature/creatures/methods/removeCreature';
|
||||
import '/imports/api/creature/creatures/methods/restCreature';
|
||||
import '/imports/api/creature/creatures/methods/updateCreature';
|
||||
import '/imports/api/creature/creatures/methods/changeAllowedLibraries';
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import simpleSchemaMixin from '/imports/api/creature/mixins/simpleSchemaMixin.js';
|
||||
import Creatures, { CreatureSchema } from '/imports/api/creature/creatures/Creatures.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import defaultCharacterProperties from '/imports/api/creature/creatures/defaultCharacterProperties.js';
|
||||
import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js';
|
||||
import assertHasCharactersSlots from '/imports/api/creature/creatures/methods/assertHasCharacterSlots.js';
|
||||
import getSlotFillFilter from '/imports/api/creature/creatureProperties/methods/getSlotFillFilter.js';
|
||||
import getCreatureLibraryIds from '/imports/api/library/getCreatureLibraryIds.js';
|
||||
import LibraryNodes from '/imports/api/library/LibraryNodes.js';
|
||||
import { insertExperienceForCreature } from '/imports/api/creature/experience/Experiences.js';
|
||||
import simpleSchemaMixin from '/imports/api/creature/mixins/simpleSchemaMixin';
|
||||
import Creatures, { CreatureSchema } from '/imports/api/creature/creatures/Creatures';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import defaultCharacterProperties from '/imports/api/creature/creatures/defaultCharacterProperties';
|
||||
import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode';
|
||||
import assertHasCharactersSlots from '/imports/api/creature/creatures/methods/assertHasCharacterSlots';
|
||||
import getSlotFillFilter from '/imports/api/creature/creatureProperties/methods/getSlotFillFilter';
|
||||
import getCreatureLibraryIds from '/imports/api/library/getCreatureLibraryIds';
|
||||
import LibraryNodes from '/imports/api/library/LibraryNodes';
|
||||
import { insertExperienceForCreature } from '/imports/api/creature/experience/Experiences';
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
|
||||
const insertCreature = new ValidatedMethod({
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import { assertOwnership } from '/imports/api/creature/creatures/creaturePermissions.js';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||
import CreatureVariables from '/imports/api/creature/creatures/CreatureVariables.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import CreatureLogs from '/imports/api/creature/log/CreatureLogs.js';
|
||||
import Experiences from '/imports/api/creature/experience/Experiences.js';
|
||||
import { assertOwnership } from '/imports/api/creature/creatures/creaturePermissions';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures';
|
||||
import CreatureVariables from '/imports/api/creature/creatures/CreatureVariables';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import CreatureLogs from '/imports/api/creature/log/CreatureLogs';
|
||||
import Experiences from '/imports/api/creature/experience/Experiences';
|
||||
import { getFilter } from '/imports/api/parenting/parentingFunctions';
|
||||
|
||||
function removeRelatedDocuments(creatureId){
|
||||
CreatureVariables.remove({_creatureId: creatureId});
|
||||
CreatureProperties.remove({'ancestors.id': creatureId});
|
||||
CreatureLogs.remove({creatureId});
|
||||
Experiences.remove({creatureId});
|
||||
function removeRelatedDocuments(creatureId) {
|
||||
CreatureVariables.remove({ _creatureId: creatureId });
|
||||
CreatureProperties.remove(getFilter.descendantsOfRoot(creatureId));
|
||||
CreatureLogs.remove({ creatureId });
|
||||
Experiences.remove({ creatureId });
|
||||
}
|
||||
|
||||
const removeCreature = new ValidatedMethod({
|
||||
@@ -28,14 +29,14 @@ const removeCreature = new ValidatedMethod({
|
||||
numRequests: 5,
|
||||
timeInterval: 5000,
|
||||
},
|
||||
run({charId}) {
|
||||
run({ charId }) {
|
||||
assertOwnership(charId, this.userId)
|
||||
this.unblock();
|
||||
removeCreatureWork(charId)
|
||||
},
|
||||
});
|
||||
|
||||
export function removeCreatureWork(creatureId){
|
||||
export function removeCreatureWork(creatureId) {
|
||||
Creatures.remove(creatureId);
|
||||
removeRelatedDocuments(creatureId);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions';
|
||||
import { union } from 'lodash';
|
||||
import ActionContext from '/imports/api/engine/actions/ActionContext.js';
|
||||
import { applyTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||
import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js';
|
||||
import ActionContext from '/imports/api/engine/actions/ActionContext';
|
||||
import { applyTriggers } from '/imports/api/engine/actions/applyTriggers';
|
||||
import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty';
|
||||
import { getFilter } from '/imports/api/parenting/parentingFunctions';
|
||||
|
||||
const restCreature = new ValidatedMethod({
|
||||
name: 'creature.methods.rest',
|
||||
@@ -74,7 +75,7 @@ function doRestWork(restType, actionContext) {
|
||||
export function resetProperties(creatureId, resetFilter, actionContext) {
|
||||
// Only apply to active properties
|
||||
const filter = {
|
||||
'ancestors.id': creatureId,
|
||||
...getFilter.descendantsOfRoot(creatureId),
|
||||
reset: resetFilter,
|
||||
removed: { $ne: true },
|
||||
inactive: { $ne: true },
|
||||
@@ -128,7 +129,7 @@ export function resetProperties(creatureId, resetFilter, actionContext) {
|
||||
|
||||
function resetHitDice(creatureId, actionContext) {
|
||||
let hitDice = CreatureProperties.find({
|
||||
'ancestors.id': creatureId,
|
||||
...getFilter.descendantsOfRoot(creatureId),
|
||||
type: 'attribute',
|
||||
attributeType: 'hitDice',
|
||||
removed: { $ne: true },
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
|
||||
|
||||
const updateCreature = new ValidatedMethod({
|
||||
name: 'creatures.update',
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures';
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS';
|
||||
|
||||
let Experiences = new Mongo.Collection('experiences');
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS';
|
||||
|
||||
let ExperienceSchema = new SimpleSchema({
|
||||
title: {
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||
import CreatureVariables from '/imports/api/creature/creatures/CreatureVariables.js';
|
||||
import LogContentSchema from '/imports/api/creature/log/LogContentSchema.js';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures';
|
||||
import CreatureVariables from '/imports/api/creature/creatures/CreatureVariables';
|
||||
import LogContentSchema from '/imports/api/creature/log/LogContentSchema';
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
|
||||
import { parse, prettifyParseError } from '/imports/parser/parser.js';
|
||||
import resolve, { toString } from '/imports/parser/resolve.js';
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions';
|
||||
import { parse, prettifyParseError } from '/imports/parser/parser';
|
||||
import resolve, { toString } from '/imports/parser/resolve';
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS';
|
||||
|
||||
const PER_CREATURE_LOG_LIMIT = 100;
|
||||
|
||||
if (Meteor.isServer) {
|
||||
var sendWebhookAsCreature = require('/imports/server/discord/sendWebhook.js').sendWebhookAsCreature;
|
||||
var sendWebhookAsCreature = require('/imports/server/discord/sendWebhook').sendWebhookAsCreature;
|
||||
}
|
||||
|
||||
let CreatureLogs = new Mongo.Collection('creatureLogs');
|
||||
|
||||
@@ -1,7 +1,18 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
||||
import RollDetailsSchema from '/imports/api/properties/subSchemas/RollDetailsSchema.js';
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema';
|
||||
import RollDetailsSchema from '/imports/api/properties/subSchemas/RollDetailsSchema';
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS';
|
||||
|
||||
export interface LogContent {
|
||||
name?: string
|
||||
value?: string
|
||||
inline?: boolean
|
||||
context?: {
|
||||
errors: any[]
|
||||
rolls: any[]
|
||||
doubleRolls?: boolean
|
||||
}
|
||||
}
|
||||
|
||||
let LogContentSchema = new SimpleSchema({
|
||||
// The name of the field, included in discord webhook message
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
assertEditPermission,
|
||||
assertViewPermission,
|
||||
assertOwnership,
|
||||
} from '/imports/api/creature/creatures/creaturePermissions.js';
|
||||
} from '/imports/api/creature/creatures/creaturePermissions';
|
||||
|
||||
// Checks if the method has permission to run on the document. If the document
|
||||
// has a charId, that creature is checked, otherwise if it has an _id and the
|
||||
@@ -13,36 +13,36 @@ import {
|
||||
// Because this mixin injects the charId into argument objects that don't
|
||||
// already contain it, it should always come last in the mixin list, so that it
|
||||
// the outermost wrapper of the run function
|
||||
export default function creaturePermissionMixin(methodOptions){
|
||||
export default function creaturePermissionMixin(methodOptions) {
|
||||
let assertPermission;
|
||||
if (methodOptions.permission === 'owner'){
|
||||
if (methodOptions.permission === 'owner') {
|
||||
assertPermission = assertOwnership;
|
||||
} else if (methodOptions.permission === 'edit'){
|
||||
} else if (methodOptions.permission === 'edit') {
|
||||
assertPermission = assertEditPermission;
|
||||
} else if (methodOptions.permission === 'view'){
|
||||
} else if (methodOptions.permission === 'view') {
|
||||
assertPermission = assertViewPermission;
|
||||
} else {
|
||||
throw "`permission` missing in method options";
|
||||
}
|
||||
|
||||
let getCharId;
|
||||
if (methodOptions.getCharId){
|
||||
if (methodOptions.getCharId) {
|
||||
getCharId = methodOptions.getCharId;
|
||||
} else if (methodOptions.collection) {
|
||||
getCharId = function({_id}){
|
||||
getCharId = function ({ _id }) {
|
||||
return methodOptions.collection.findOne(_id, {
|
||||
fields: {charId: 1}
|
||||
fields: { charId: 1 }
|
||||
}).charId;
|
||||
};
|
||||
} else {
|
||||
getCharId = function(){
|
||||
getCharId = function () {
|
||||
throw "`getCharId` or `collection` missing in method options," +
|
||||
" or {charId} missing in call";
|
||||
};
|
||||
}
|
||||
|
||||
let runFunc = methodOptions.run;
|
||||
methodOptions.run = function(doc, ...rest){
|
||||
methodOptions.run = function (doc, ...rest) {
|
||||
// Store the charId on the doc for other mixins if it had to be fetched
|
||||
doc.charId = doc.charId || getCharId.apply(this, arguments);
|
||||
assertPermission(doc.charId, this.userId);
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
import {
|
||||
updateChildren,
|
||||
updateDescendants,
|
||||
} from '/imports/api/parenting/parenting.js';
|
||||
import { inheritedFields } from '/imports/api/parenting/ChildSchema.js';
|
||||
import MONGO_OPERATORS from '/imports/constants/MONGO_OPERATORS.js';
|
||||
|
||||
// This mixin can be safely applied to all update methods which are validated
|
||||
// with the updateSchemaMixin. It will propagate updates to fields which
|
||||
// are inherited and normalised on the parent or ancestor docs
|
||||
// It should have neglible performance impact for updates that aren't inherited
|
||||
function propagateInheritanceUpdate({_id, update}){
|
||||
let childModifier = {};
|
||||
let descendantModifier = {};
|
||||
// For each operator
|
||||
for (let operator of MONGO_OPERATORS){
|
||||
// If the operator is in the update, for each field
|
||||
if (update[operator]) for (let field in update[operator]){
|
||||
// Get the top level field that was actually modified
|
||||
const indexOfDot = field.indexOf('.');
|
||||
let modifiedField;
|
||||
if (indexOfDot !== -1) {
|
||||
modifiedField = field.substring(0, indexOfDot);
|
||||
} else {
|
||||
modifiedField = field;
|
||||
}
|
||||
// If that field is updated and inherited
|
||||
if (inheritedFields.has(modifiedField)){
|
||||
// Perform the same update on the descendants
|
||||
if (!childModifier[operator]) childModifier[operator] = {};
|
||||
if (!descendantModifier[operator]) descendantModifier[operator] = {};
|
||||
childModifier[operator][`parent.${field}`] = update[operator][field];
|
||||
descendantModifier[operator][`ancestors.$.${field}`] = update[operator][field];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the parent object of its children
|
||||
updateChildren({
|
||||
parentId: _id,
|
||||
modifier: childModifier,
|
||||
});
|
||||
|
||||
// Update the ancestors object of its descendants
|
||||
updateDescendants({
|
||||
ancestorId: _id,
|
||||
modifier: descendantModifier,
|
||||
});
|
||||
}
|
||||
|
||||
export default function propagateInheritanceUpdateMixin(methodOptions){
|
||||
let runFunc = methodOptions.run;
|
||||
methodOptions.run = function({_id, update}){
|
||||
const result = runFunc.apply(this, arguments);
|
||||
propagateInheritanceUpdate({_id, update});
|
||||
return result;
|
||||
};
|
||||
return methodOptions;
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import computeCreature from '/imports/api/engine/computeCreature.js';
|
||||
import computeCreature from '/imports/api/engine/computeCreature';
|
||||
|
||||
export default function recomputeCreatureMixin(methodOptions){
|
||||
export default function recomputeCreatureMixin(methodOptions) {
|
||||
let runFunc = methodOptions.run;
|
||||
methodOptions.run = function({charId}){
|
||||
methodOptions.run = function ({ charId }) {
|
||||
const result = runFunc.apply(this, arguments);
|
||||
if (
|
||||
methodOptions.skipRecompute &&
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import { setDocToLastOrder } from '/imports/api/parenting/order.js';
|
||||
|
||||
export function setDocToLastMixin(methodOptions){
|
||||
// Make sure the doc has a charId
|
||||
// This mixin should come before simpleSchemaMixin so that it can extend the
|
||||
// schema before it is turned into a validate function
|
||||
if (methodOptions.validate){
|
||||
throw new Meteor.Error(`setDocToLastMixin should come before simpleSchemaMixin`);
|
||||
}
|
||||
methodOptions.schema = new SimpleSchema({
|
||||
charId: {
|
||||
type: String,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
},
|
||||
}).extend(methodOptions.schema);
|
||||
let collection = methodOptions.collection;
|
||||
if (!collection){
|
||||
throw new Meteor.Error("`collection` required in method options for setDocToLastMixin");
|
||||
}
|
||||
let runFunc = methodOptions.run;
|
||||
methodOptions.run = function(doc){
|
||||
setDocToLastOrder({collection, doc});
|
||||
return runFunc.apply(this, arguments);
|
||||
};
|
||||
return methodOptions;
|
||||
}
|
||||
@@ -3,60 +3,17 @@ import { Mongo } from 'meteor/mongo';
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import { softRemove } from '/imports/api/parenting/softRemove.js';
|
||||
import SoftRemovableSchema from '/imports/api/parenting/SoftRemovableSchema.js';
|
||||
import { storedIconsSchema } from '/imports/api/icons/Icons.js';
|
||||
import '/imports/api/library/methods/index.js';
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||
import { restore } from '/imports/api/parenting/softRemove.js';
|
||||
import { getAncestry, updateParent } from '/imports/api/parenting/parenting.js';
|
||||
import { nodeArrayToTree } from '/imports/api/parenting/nodesToTree';
|
||||
import { getDocsInDepthFirstOrder } from '/imports/api/parenting/getDescendantsInDepthFirstOrder';
|
||||
import { softRemove } from '/imports/api/parenting/softRemove';
|
||||
import SoftRemovableSchema from '/imports/api/parenting/SoftRemovableSchema';
|
||||
import { storedIconsSchema } from '/imports/api/icons/Icons';
|
||||
import '/imports/api/library/methods/index';
|
||||
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS';
|
||||
import { restore } from '/imports/api/parenting/softRemove';
|
||||
import { getFilter, rebuildNestedSets, changeParent } from '/imports/api/parenting/parentingFunctions';
|
||||
import ChildSchema from '/imports/api/parenting/ChildSchema';
|
||||
|
||||
const Docs = new Mongo.Collection('docs');
|
||||
|
||||
const RefSchema = new SimpleSchema({
|
||||
id: {
|
||||
type: String,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
index: 1
|
||||
},
|
||||
collection: {
|
||||
type: String,
|
||||
max: STORAGE_LIMITS.collectionName,
|
||||
},
|
||||
urlName: {
|
||||
type: String,
|
||||
regEx: /[a-z]+(?:[a-z]|-)+/,
|
||||
min: 2,
|
||||
max: STORAGE_LIMITS.variableName,
|
||||
optional: true,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
max: STORAGE_LIMITS.description,
|
||||
optional: true,
|
||||
},
|
||||
});
|
||||
|
||||
let ChildSchema = new SimpleSchema({
|
||||
order: {
|
||||
type: Number,
|
||||
},
|
||||
parent: {
|
||||
type: RefSchema,
|
||||
optional: true,
|
||||
},
|
||||
ancestors: {
|
||||
type: Array,
|
||||
defaultValue: [],
|
||||
maxCount: STORAGE_LIMITS.ancestorCount,
|
||||
},
|
||||
'ancestors.$': {
|
||||
type: RefSchema,
|
||||
},
|
||||
});
|
||||
|
||||
let DocSchema = new SimpleSchema({
|
||||
_id: {
|
||||
type: String,
|
||||
@@ -106,38 +63,14 @@ function assertDocsEditPermission(userId) {
|
||||
function getDocLink(doc, urlName) {
|
||||
if (!urlName) urlName = doc.urlName;
|
||||
const address = ['/docs'];
|
||||
doc.ancestors?.forEach(a => {
|
||||
const ancestorDocs = Docs.find(getFilter.ancestors(doc));
|
||||
ancestorDocs?.forEach(a => {
|
||||
address.push(a.urlName);
|
||||
});
|
||||
address.push(urlName);
|
||||
return address.join('/');
|
||||
}
|
||||
|
||||
function rebuildDocAncestors(docId) {
|
||||
const newDoc = Docs.findOne(docId);
|
||||
Docs.find({ 'ancestors.id': docId }).forEach(doc => {
|
||||
doc.ancestors.forEach((a, i) => {
|
||||
if (a.id === docId) {
|
||||
Docs.update(doc._id, {
|
||||
$set: {
|
||||
[`ancestors.${i}`]: {
|
||||
id: newDoc._id,
|
||||
collection: 'docs',
|
||||
urlName: newDoc.urlName,
|
||||
name: newDoc.name,
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
doc = Docs.findOne(doc._id);
|
||||
const newLink = getDocLink(doc);
|
||||
if (doc.href !== newLink) {
|
||||
Docs.update(doc._id, { $set: { href: newLink } })
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Add a means of seeding new servers with documentation
|
||||
if (Meteor.isClient) {
|
||||
Docs.getJsonDocs = function () {
|
||||
@@ -162,18 +95,11 @@ const insertDoc = new ValidatedMethod({
|
||||
numRequests: 5,
|
||||
timeInterval: 5000,
|
||||
},
|
||||
run({ doc, parentRef }) {
|
||||
run({ doc, parentId }) {
|
||||
delete doc._id;
|
||||
assertDocsEditPermission(this.userId);
|
||||
// get the new ancestry for the properties
|
||||
if (parentRef) {
|
||||
var { ancestors } = getAncestry({
|
||||
parentRef,
|
||||
inheritedFields: { name: 1, urlName: 1 },
|
||||
});
|
||||
}
|
||||
doc.parent = parentRef;
|
||||
doc.ancestors = ancestors;
|
||||
|
||||
doc.parentId = parentId;
|
||||
|
||||
const lastOrder = Docs.find({}, { sort: { order: -1 } }).fetch()[0]?.order || 0;
|
||||
doc.order = lastOrder + 1;
|
||||
@@ -185,7 +111,7 @@ const insertDoc = new ValidatedMethod({
|
||||
}
|
||||
|
||||
const docId = Docs.insert(doc);
|
||||
reorderDocs();
|
||||
rebuildNestedSets(Docs);
|
||||
return docId;
|
||||
},
|
||||
});
|
||||
@@ -223,13 +149,9 @@ const updateDoc = new ValidatedMethod({
|
||||
}
|
||||
modifier.$set = modifier.$set || {};
|
||||
modifier.$set.href = newLink;
|
||||
rebuildDocAncestors(_id);
|
||||
}
|
||||
const updates = Docs.update(_id, modifier);
|
||||
if (pathString === 'name' || pathString === 'urlName') {
|
||||
rebuildDocAncestors(_id);
|
||||
}
|
||||
reorderDocs();
|
||||
rebuildNestedSets(Docs);
|
||||
return updates;
|
||||
},
|
||||
});
|
||||
@@ -279,7 +201,7 @@ const softRemoveDoc = new ValidatedMethod({
|
||||
run({ _id }) {
|
||||
assertDocsEditPermission(this.userId);
|
||||
softRemove({ _id, collection: Docs });
|
||||
reorderDocs();
|
||||
rebuildNestedSets(Docs);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -295,8 +217,8 @@ const restoreDoc = new ValidatedMethod({
|
||||
},
|
||||
run({ _id }) {
|
||||
assertDocsEditPermission(this.userId);
|
||||
restore({ _id, collection: Docs });
|
||||
reorderDocs();
|
||||
restore('docs', _id);
|
||||
rebuildNestedSets(Docs);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -316,17 +238,18 @@ const organizeDoc = new ValidatedMethod({
|
||||
timeInterval: 5000,
|
||||
},
|
||||
run({ docId, parentId, order }) {
|
||||
let doc = Docs.findOne(docId);
|
||||
const doc = Docs.findOne(docId);
|
||||
const parent = Docs.findOne(parentId);
|
||||
// The user must be able to edit both the doc and its parent to move it
|
||||
// successfully
|
||||
assertDocsEditPermission(this.userId);
|
||||
|
||||
// Change the doc's parent
|
||||
updateParent({ docRef: { id: docId, collection: 'docs' }, parentRef: { id: parentId, collection: 'docs' } });
|
||||
changeParent(doc, parent, Docs);
|
||||
// Change the doc's order to be a half step ahead of its target location
|
||||
Docs.update(doc._id, { $set: { order } });
|
||||
|
||||
reorderDocs();
|
||||
rebuildNestedSets(Docs);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -349,52 +272,10 @@ const reorderDoc = new ValidatedMethod({
|
||||
Docs.update(docId, {
|
||||
$set: { order }
|
||||
});
|
||||
reorderDocs();
|
||||
rebuildNestedSets(Docs);
|
||||
},
|
||||
});
|
||||
|
||||
function reorderDocs() {
|
||||
const docs = Docs.find({ removed: { $ne: true } }, { sort: { order: 1 } }).fetch();
|
||||
const forest = nodeArrayToTree(docs);
|
||||
const orderedDocs = getDocsInDepthFirstOrder(forest);
|
||||
const bulkWrite = [];
|
||||
orderedDocs.forEach((doc, index) => {
|
||||
if (doc.order !== index) {
|
||||
bulkWrite.push({
|
||||
updateOne: {
|
||||
filter: { _id: doc._id },
|
||||
update: { $set: { order: index } },
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
if (Meteor.isServer && bulkWrite.length) {
|
||||
Docs.rawCollection().bulkWrite(
|
||||
bulkWrite,
|
||||
{ ordered: false },
|
||||
function (e) {
|
||||
if (e) {
|
||||
console.error('Bulk write failed: ');
|
||||
console.error(e);
|
||||
}
|
||||
// Rebuild the ancestors of all the docs
|
||||
// This is a pretty slow way to do anything, but docs hardly ever get rearranged
|
||||
docs.forEach(doc => {
|
||||
rebuildDocAncestors(doc._id);
|
||||
});
|
||||
}
|
||||
);
|
||||
} else {
|
||||
bulkWrite.forEach(op => {
|
||||
Docs.update(
|
||||
op.updateOne.filter,
|
||||
op.updateOne.update,
|
||||
{ selector: { type: 'any' } }
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
DocSchema,
|
||||
insertDoc,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { CreatureLogSchema, insertCreatureLogWork } from '/imports/api/creature/log/CreatureLogs.js';
|
||||
import { CreatureLogSchema, insertCreatureLogWork } from '/imports/api/creature/log/CreatureLogs';
|
||||
import {
|
||||
getCreature, getVariables, getPropertiesOfType, replaceLinkedVariablesWithProps
|
||||
} from '/imports/api/engine/loadCreatures.js';
|
||||
} from '/imports/api/engine/loadCreatures';
|
||||
import { groupBy, remove } from 'lodash';
|
||||
|
||||
export default class ActionContext {
|
||||
@@ -56,10 +56,10 @@ export default class ActionContext {
|
||||
// Group the triggers into triggers.<event>.<timing> or
|
||||
// triggers.doActionProperty.<propertyType>.<timing>
|
||||
this.triggers = groupBy(this.triggers, 'event');
|
||||
for (let event in this.triggers) {
|
||||
for (const event in this.triggers) {
|
||||
if (event === 'doActionProperty') {
|
||||
this.triggers[event] = groupBy(this.triggers[event], 'actionPropertyType');
|
||||
for (let propertyType in this.triggers[event]) {
|
||||
for (const propertyType in this.triggers[event]) {
|
||||
this.triggers[event][propertyType] = groupBy(this.triggers[event][propertyType], 'timing');
|
||||
}
|
||||
} else {
|
||||
@@ -1,37 +0,0 @@
|
||||
import action from './applyPropertyByType/applyAction.js';
|
||||
import ammo from './applyPropertyByType/applyItemAsAmmo.js'
|
||||
import adjustment from './applyPropertyByType/applyAdjustment.js';
|
||||
import branch from './applyPropertyByType/applyBranch.js';
|
||||
import buff from './applyPropertyByType/applyBuff.js';
|
||||
import buffRemover from './applyPropertyByType/applyBuffRemover.js';
|
||||
import damage from './applyPropertyByType/applyDamage.js';
|
||||
import folder from './applyPropertyByType/applyFolder.js';
|
||||
import note from './applyPropertyByType/applyNote.js';
|
||||
import roll from './applyPropertyByType/applyRoll.js';
|
||||
import savingThrow from './applyPropertyByType/applySavingThrow.js';
|
||||
import toggle from './applyPropertyByType/applyToggle.js';
|
||||
|
||||
const applyPropertyByType = {
|
||||
action,
|
||||
ammo,
|
||||
adjustment,
|
||||
branch,
|
||||
buff,
|
||||
buffRemover,
|
||||
damage,
|
||||
folder,
|
||||
note,
|
||||
propertySlot: folder,
|
||||
roll,
|
||||
savingThrow,
|
||||
spell: action,
|
||||
toggle,
|
||||
};
|
||||
|
||||
export default async function applyProperty(node, actionContext, ...rest) {
|
||||
if (node.node.deactivatedByToggle) return;
|
||||
actionContext.scope[`#${node.node.type}`] = node.node;
|
||||
console.log('start apply props by type', node.node.type)
|
||||
await applyPropertyByType[node.node.type]?.(node, actionContext, ...rest);
|
||||
console.log('end apply prop by type', node.node.type)
|
||||
}
|
||||
38
app/imports/api/engine/actions/applyProperty.ts
Normal file
38
app/imports/api/engine/actions/applyProperty.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import action from './applyPropertyByType/applyAction';
|
||||
import ammo from './applyPropertyByType/applyItemAsAmmo'
|
||||
import adjustment from './applyPropertyByType/applyAdjustment';
|
||||
import branch from './applyPropertyByType/applyBranch';
|
||||
import buff from './applyPropertyByType/applyBuff';
|
||||
import buffRemover from './applyPropertyByType/applyBuffRemover';
|
||||
import damage from './applyPropertyByType/applyDamage';
|
||||
import folder from './applyPropertyByType/applyFolder';
|
||||
import note from './applyPropertyByType/applyNote';
|
||||
import roll from './applyPropertyByType/applyRoll';
|
||||
import savingThrow from './applyPropertyByType/applySavingThrow';
|
||||
import toggle from './applyPropertyByType/applyToggle';
|
||||
import ActionContext from '/imports/api/engine/actions/ActionContext';
|
||||
import { TreeNode } from '/imports/api/parenting/parentingFunctions';
|
||||
import { CreatureProperty } from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
|
||||
const applyPropertyByType = {
|
||||
action,
|
||||
ammo,
|
||||
adjustment,
|
||||
branch,
|
||||
buff,
|
||||
buffRemover,
|
||||
damage,
|
||||
folder,
|
||||
note,
|
||||
propertySlot: folder,
|
||||
roll,
|
||||
savingThrow,
|
||||
spell: action,
|
||||
toggle,
|
||||
};
|
||||
|
||||
export default function applyProperty(node: TreeNode<CreatureProperty>, actionContext: ActionContext, ...rest) {
|
||||
if (node.doc.deactivatedByToggle) return;
|
||||
actionContext.scope[`#${node.doc.type}`] = node.doc;
|
||||
applyPropertyByType[node.doc.type]?.(node, actionContext, ...rest);
|
||||
}
|
||||
@@ -1,22 +1,31 @@
|
||||
import recalculateInlineCalculations from './shared/recalculateInlineCalculations.js';
|
||||
import recalculateCalculation from './shared/recalculateCalculation.js';
|
||||
import rollDice from '/imports/parser/rollDice.js';
|
||||
import applyProperty from '../applyProperty.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js';
|
||||
import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js';
|
||||
import numberToSignedString from '/imports/api/utility/numberToSignedString.js';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||
import { resetProperties } from '/imports/api/creature/creatures/methods/restCreature.js';
|
||||
import recalculateInlineCalculations from './shared/recalculateInlineCalculations';
|
||||
import recalculateCalculation from './shared/recalculateCalculation';
|
||||
import rollDice from '/imports/parser/rollDice';
|
||||
import applyProperty from '../applyProperty';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren';
|
||||
import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty';
|
||||
import numberToSignedString from '/imports/api/utility/numberToSignedString';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers';
|
||||
import { resetProperties } from '/imports/api/creature/creatures/methods/restCreature';
|
||||
import { TreeNode, hasAncestorRelationship } from '/imports/api/parenting/parentingFunctions';
|
||||
import { Action } from '/imports/api/properties/Actions';
|
||||
import { LogContent } from '/imports/api/creature/log/LogContentSchema';
|
||||
import { Item } from '/imports/api/properties/Items';
|
||||
|
||||
export default async function applyAction(node, actionContext) {
|
||||
await applyNodeTriggers(node, 'before', actionContext);
|
||||
const prop = node.node;
|
||||
interface Ammo extends Item {
|
||||
type: 'ammo'
|
||||
adjustment: number
|
||||
}
|
||||
|
||||
export default function applyAction(node: TreeNode<Action>, actionContext) {
|
||||
applyNodeTriggers(node, 'before', actionContext);
|
||||
const prop = node.doc;
|
||||
if (prop.target === 'self') actionContext.targets = [actionContext.creature];
|
||||
const targets = actionContext.targets;
|
||||
|
||||
// Log the name and summary
|
||||
let content = { name: prop.name };
|
||||
const content: LogContent = { name: prop.name, };
|
||||
if (prop.summary?.text) {
|
||||
recalculateInlineCalculations(prop.summary, actionContext);
|
||||
content.value = prop.summary.value;
|
||||
@@ -27,7 +36,7 @@ export default async function applyAction(node, actionContext) {
|
||||
const failed = await spendResources(prop, actionContext);
|
||||
if (failed) return;
|
||||
|
||||
const attack = prop.attackRoll || prop.attackRollBonus;
|
||||
const attack = prop.attackRoll;
|
||||
|
||||
// Attack if there is an attack roll
|
||||
if (attack && attack.calculation) {
|
||||
@@ -59,7 +68,7 @@ function applyAttackWithoutTarget({ attack, actionContext }) {
|
||||
|
||||
recalculateCalculation(attack, actionContext);
|
||||
const scope = actionContext.scope;
|
||||
let {
|
||||
const {
|
||||
resultPrefix,
|
||||
result,
|
||||
criticalHit,
|
||||
@@ -96,7 +105,7 @@ function applyAttackToTarget({ attack, target, actionContext }) {
|
||||
|
||||
recalculateCalculation(attack, actionContext);
|
||||
|
||||
let {
|
||||
const {
|
||||
resultPrefix,
|
||||
result,
|
||||
criticalHit,
|
||||
@@ -176,7 +185,7 @@ function applyCrits(value, scope) {
|
||||
scopeCrit = scopeCrit.value;
|
||||
}
|
||||
const criticalHitTarget = scopeCrit || 20;
|
||||
let criticalHit = value >= criticalHitTarget;
|
||||
const criticalHit = value >= criticalHitTarget;
|
||||
let criticalMiss;
|
||||
if (criticalHit) {
|
||||
scope['~criticalHit'] = { value: true };
|
||||
@@ -189,9 +198,9 @@ function applyCrits(value, scope) {
|
||||
return { criticalHit, criticalMiss };
|
||||
}
|
||||
|
||||
async function spendResources(prop, actionContext) {
|
||||
function spendResources(prop: Action, actionContext) {
|
||||
// Check Uses
|
||||
if (prop.usesLeft <= 0) {
|
||||
if (!prop.usesLeft || prop.usesLeft <= 0) {
|
||||
if (!prop.silent) actionContext.addLog({
|
||||
name: 'Error',
|
||||
value: `${prop.name || 'action'} does not have enough uses left`,
|
||||
@@ -207,42 +216,45 @@ async function spendResources(prop, actionContext) {
|
||||
return true;
|
||||
}
|
||||
// Items
|
||||
let spendLog = [];
|
||||
let gainLog = [];
|
||||
const ammoToApply = [];
|
||||
const spendLog: string[] = [];
|
||||
const gainLog: string[] = [];
|
||||
const ammoToApply: TreeNode<Ammo>[] = [];
|
||||
try {
|
||||
prop.resources.itemsConsumed.forEach(itemConsumed => {
|
||||
recalculateCalculation(itemConsumed.quantity, actionContext);
|
||||
if (!itemConsumed.itemId) {
|
||||
throw 'No ammo was selected for this prop';
|
||||
}
|
||||
let item = CreatureProperties.findOne(itemConsumed.itemId);
|
||||
if (!item || item.ancestors[0].id !== prop.ancestors[0].id) {
|
||||
const item = CreatureProperties.findOne(itemConsumed.itemId) as Item;
|
||||
if (!item || item.root.id !== prop.root.id) {
|
||||
throw 'The prop\'s ammo was not found on the creature';
|
||||
}
|
||||
|
||||
if (
|
||||
!itemConsumed?.quantity?.value ||
|
||||
!isFinite(itemConsumed.quantity.value)
|
||||
!isFinite(+itemConsumed.quantity.value)
|
||||
) return;
|
||||
const quantityConsumed = +itemConsumed.quantity.value;
|
||||
|
||||
let logName = item.name;
|
||||
if (itemConsumed.quantity.value > 1 || itemConsumed.quantity.value < -1) {
|
||||
if (quantityConsumed > 1 || quantityConsumed < -1) {
|
||||
logName = item.plural || logName;
|
||||
}
|
||||
if (itemConsumed.quantity.value > 0) {
|
||||
spendLog.push(logName + ': ' + itemConsumed.quantity.value);
|
||||
} else if (itemConsumed.quantity.value < 0) {
|
||||
gainLog.push(logName + ': ' + -itemConsumed.quantity.value);
|
||||
if (quantityConsumed > 0) {
|
||||
spendLog.push(logName + ': ' + quantityConsumed);
|
||||
} else if (quantityConsumed < 0) {
|
||||
gainLog.push(logName + ': ' + -quantityConsumed);
|
||||
}
|
||||
// So long as the item isn't an ancestor of the current prop apply it
|
||||
// If it was an ancestor this would be an infinite loop
|
||||
if (!hasAncestorRelationship(item, prop)) {
|
||||
ammoToApply.push({
|
||||
node: {
|
||||
doc: {
|
||||
...item,
|
||||
// Use ammo pseudo-type
|
||||
type: 'ammo',
|
||||
// Store the adjustment to be applied
|
||||
adjustment: itemConsumed.quantity.value,
|
||||
adjustment: quantityConsumed,
|
||||
},
|
||||
children: []
|
||||
});
|
||||
@@ -263,6 +275,7 @@ async function spendResources(prop, actionContext) {
|
||||
CreatureProperties.update(prop._id, {
|
||||
$inc: { usesUsed: 1 }
|
||||
}, {
|
||||
//@ts-expect-error no typings for collection 2 selector
|
||||
selector: prop
|
||||
});
|
||||
if (!prop.silent) actionContext.addLog({
|
||||
@@ -277,8 +290,9 @@ async function spendResources(prop, actionContext) {
|
||||
recalculateCalculation(attConsumed.quantity, actionContext);
|
||||
|
||||
if (!attConsumed.quantity?.value) return;
|
||||
const quantityConsumed = +attConsumed.quantity.value;
|
||||
if (!attConsumed.variableName) return;
|
||||
let stat = actionContext.scope[attConsumed.variableName];
|
||||
const stat = actionContext.scope[attConsumed.variableName];
|
||||
if (!stat) {
|
||||
spendLog.push(attConsumed.variableName + ': ' + ' not found');
|
||||
return;
|
||||
@@ -289,10 +303,10 @@ async function spendResources(prop, actionContext) {
|
||||
value: attConsumed.quantity.value,
|
||||
actionContext,
|
||||
});
|
||||
if (attConsumed.quantity.value > 0) {
|
||||
spendLog.push(stat.name + ': ' + attConsumed.quantity.value);
|
||||
} else if (attConsumed.quantity.value < 0) {
|
||||
gainLog.push(stat.name + ': ' + -attConsumed.quantity.value);
|
||||
if (quantityConsumed > 0) {
|
||||
spendLog.push(stat.name + ': ' + quantityConsumed);
|
||||
} else if (quantityConsumed < 0) {
|
||||
gainLog.push(stat.name + ': ' + -quantityConsumed);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -313,20 +327,3 @@ async function spendResources(prop, actionContext) {
|
||||
inline: true,
|
||||
});
|
||||
}
|
||||
|
||||
function hasAncestorRelationship(a, b) {
|
||||
let top, bottom;
|
||||
if (a.ancestors.length === b.ancestors.length) {
|
||||
// Can't be ancestors of one another if they have the same number of ancestors
|
||||
return false;
|
||||
} else if (a.ancestors.length > b.ancestors.length) {
|
||||
// longer ancestor list goes on the bottom
|
||||
top = b;
|
||||
bottom = a;
|
||||
} else {
|
||||
top = a;
|
||||
bottom = b;
|
||||
}
|
||||
const expectedAncestorPosition = top.ancestors.length;
|
||||
return bottom.ancestors[expectedAncestorPosition]?.id === top._id;
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js';
|
||||
import recalculateCalculation from './shared/recalculateCalculation.js';
|
||||
import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren';
|
||||
import recalculateCalculation from './shared/recalculateCalculation';
|
||||
import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers';
|
||||
|
||||
export default function applyAdjustment(node, actionContext) {
|
||||
applyNodeTriggers(node, 'before', actionContext);
|
||||
const prop = node.node;
|
||||
const prop = node.doc
|
||||
const damageTargets = prop.target === 'self' ? [actionContext.creature] : actionContext.targets;
|
||||
|
||||
if (!prop.amount) {
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import applyProperty from '../applyProperty.js';
|
||||
import recalculateCalculation from './shared/recalculateCalculation.js';
|
||||
import rollDice from '/imports/parser/rollDice.js';
|
||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||
import getUserInput from '/imports/api/engine/actions/getUserInput';
|
||||
import applyProperty from '../applyProperty';
|
||||
import recalculateCalculation from './shared/recalculateCalculation';
|
||||
import rollDice from '/imports/parser/rollDice';
|
||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers';
|
||||
|
||||
export default async function applyBranch(node, actionContext) {
|
||||
applyNodeTriggers(node, 'before', actionContext);
|
||||
const scope = actionContext.scope;
|
||||
const targets = actionContext.targets;
|
||||
const prop = node.node;
|
||||
const prop = node.doc
|
||||
switch (prop.branchType) {
|
||||
case 'if':
|
||||
recalculateCalculation(prop.condition, actionContext);
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
import {
|
||||
setLineageOfDocs,
|
||||
renewDocIds
|
||||
} from '/imports/api/parenting/parenting.js';
|
||||
import { setDocToLastOrder } from '/imports/api/parenting/order.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import computedSchemas from '/imports/api/properties/computedPropertySchemasIndex.js';
|
||||
import applyFnToKey from '/imports/api/engine/computation/utility/applyFnToKey.js';
|
||||
renewDocIds,
|
||||
} from '/imports/api/parenting/parentingFunctions';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import computedSchemas from '/imports/api/properties/computedPropertySchemasIndex';
|
||||
import applyFnToKey from '/imports/api/engine/computation/utility/applyFnToKey';
|
||||
import { get } from 'lodash';
|
||||
import resolve, { map, toString } from '/imports/parser/resolve.js';
|
||||
import accessor from '/imports/parser/parseTree/accessor.js';
|
||||
import logErrors from './shared/logErrors.js';
|
||||
import { insertCreatureLog } from '/imports/api/creature/log/CreatureLogs.js';
|
||||
import cyrb53 from '/imports/api/engine/computation/utility/cyrb53.js';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||
import INLINE_CALCULATION_REGEX from '/imports/constants/INLINE_CALCULTION_REGEX.js';
|
||||
import recalculateInlineCalculations from './shared/recalculateInlineCalculations.js';
|
||||
import resolve, { map, toString } from '/imports/parser/resolve';
|
||||
import accessor from '/imports/parser/parseTree/accessor';
|
||||
import logErrors from './shared/logErrors';
|
||||
import { insertCreatureLog } from '/imports/api/creature/log/CreatureLogs';
|
||||
import cyrb53 from '/imports/api/engine/computation/utility/cyrb53';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers';
|
||||
import INLINE_CALCULATION_REGEX from '/imports/constants/INLINE_CALCULTION_REGEX';
|
||||
import recalculateInlineCalculations from './shared/recalculateInlineCalculations';
|
||||
|
||||
export default function applyBuff(node, actionContext) {
|
||||
applyNodeTriggers(node, 'before', actionContext);
|
||||
const prop = node.node;
|
||||
const prop = node.doc
|
||||
let buffTargets = prop.target === 'self' ? [actionContext.creature] : actionContext.targets;
|
||||
|
||||
// Mark the buff as dirty for recalculation
|
||||
@@ -26,7 +24,7 @@ export default function applyBuff(node, actionContext) {
|
||||
|
||||
// Then copy the descendants of the buff to the targets
|
||||
let propList = [prop];
|
||||
function addChildrenToPropList(children, { skipCrystalize } = {}) {
|
||||
function addChildrenToPropList(children, { skipCrystalize } = { skipCrystalize: false }) {
|
||||
children.forEach(child => {
|
||||
if (skipCrystalize) child.node._skipCrystalize = true;
|
||||
propList.push(child.node);
|
||||
@@ -41,13 +39,20 @@ export default function applyBuff(node, actionContext) {
|
||||
crystalizeVariables({ propList, actionContext });
|
||||
}
|
||||
|
||||
let oldParent = {
|
||||
id: prop.parent.id,
|
||||
collection: prop.parent.collection,
|
||||
};
|
||||
buffTargets.forEach(target => {
|
||||
const targetPropList = EJSON.clone(propList);
|
||||
// Move the properties to the target by replacing the old subtree parent and root with the '
|
||||
// target id
|
||||
renewDocIds({
|
||||
docArray: targetPropList,
|
||||
idMap: {
|
||||
[prop.parentId]: target._id,
|
||||
[prop.root.id]: target._id,
|
||||
},
|
||||
collectionMap: { [prop.root.collection]: 'creatures' }
|
||||
});
|
||||
// Apply the buff
|
||||
copyNodeListToTarget(propList, target, oldParent);
|
||||
CreatureProperties.batchInsert(targetPropList);
|
||||
|
||||
//Log the buff
|
||||
let logValue = prop.description?.value
|
||||
@@ -82,23 +87,6 @@ export default function applyBuff(node, actionContext) {
|
||||
// Don't apply the children of the buff, they get copied to the target instead
|
||||
}
|
||||
|
||||
function copyNodeListToTarget(propList, target, oldParent) {
|
||||
let ancestry = [{ collection: 'creatures', id: target._id }];
|
||||
setLineageOfDocs({
|
||||
docArray: propList,
|
||||
newAncestry: ancestry,
|
||||
oldParent,
|
||||
});
|
||||
renewDocIds({
|
||||
docArray: propList,
|
||||
});
|
||||
setDocToLastOrder({
|
||||
collection: CreatureProperties,
|
||||
doc: propList[0],
|
||||
});
|
||||
CreatureProperties.batchInsert(propList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces all variables with their resolved values
|
||||
* except variables of the form `~target.thing.total` become `thing.total`
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { findLast, difference, intersection, filter } from 'lodash';
|
||||
import applyProperty from '../applyProperty.js';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||
import { getProperyAncestors, getPropertiesOfType } from '/imports/api/engine/loadCreatures.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import { softRemove } from '/imports/api/parenting/softRemove.js';
|
||||
import getEffectivePropTags from '/imports/api/engine/computation/utility/getEffectivePropTags.js';
|
||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js';
|
||||
import applyProperty from '../applyProperty';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers';
|
||||
import { getPropertyAncestors, getPropertiesOfType } from '/imports/api/engine/loadCreatures';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import { softRemove } from '/imports/api/parenting/softRemove';
|
||||
import getEffectivePropTags from '/imports/api/engine/computation/utility/getEffectivePropTags';
|
||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren';
|
||||
|
||||
export default function applyBuffRemover(node, actionContext) {
|
||||
// Apply triggers
|
||||
applyNodeTriggers(node, 'before', actionContext);
|
||||
|
||||
const prop = node.node;
|
||||
const prop = node.doc
|
||||
|
||||
// Log Name
|
||||
if (prop.name && !prop.silent) {
|
||||
@@ -21,7 +21,7 @@ export default function applyBuffRemover(node, actionContext) {
|
||||
// Remove buffs
|
||||
if (prop.targetParentBuff) {
|
||||
// Remove nearest ancestor buff
|
||||
const ancestors = getProperyAncestors(actionContext.creature._id, prop._id);
|
||||
const ancestors = getPropertyAncestors(actionContext.creature._id, prop._id);
|
||||
const nearestBuff = findLast(ancestors, ancestor => ancestor.type === 'buff');
|
||||
if (!nearestBuff) {
|
||||
actionContext.addLog({
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import { some, intersection, difference, remove, includes } from 'lodash';
|
||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js';
|
||||
import { insertCreatureLog } from '/imports/api/creature/log/CreatureLogs.js';
|
||||
import resolve, { Context, toString } from '/imports/parser/resolve.js';
|
||||
import logErrors from './shared/logErrors.js';
|
||||
import recalculateCalculation from '/imports/api/engine/actions/applyPropertyByType/shared/recalculateCalculation.js'
|
||||
import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js';
|
||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren';
|
||||
import { insertCreatureLog } from '/imports/api/creature/log/CreatureLogs';
|
||||
import resolve, { Context, toString } from '/imports/parser/resolve';
|
||||
import logErrors from './shared/logErrors';
|
||||
import recalculateCalculation from '/imports/api/engine/actions/applyPropertyByType/shared/recalculateCalculation'
|
||||
import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty';
|
||||
import {
|
||||
getPropertiesOfType
|
||||
} from '/imports/api/engine/loadCreatures.js';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||
import getEffectivePropTags from '/imports/api/engine/computation/utility/getEffectivePropTags.js';
|
||||
import applySavingThrow from '/imports/api/engine/actions/applyPropertyByType/applySavingThrow.js';
|
||||
} from '/imports/api/engine/loadCreatures';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers';
|
||||
import getEffectivePropTags from '/imports/api/engine/computation/utility/getEffectivePropTags';
|
||||
import applySavingThrow from '/imports/api/engine/actions/applyPropertyByType/applySavingThrow';
|
||||
|
||||
export default function applyDamage(node, actionContext) {
|
||||
applyNodeTriggers(node, 'before', actionContext);
|
||||
|
||||
const prop = node.node;
|
||||
const prop = node.doc
|
||||
const scope = actionContext.scope;
|
||||
|
||||
// Skip if there is no parse node to work with
|
||||
@@ -167,7 +167,7 @@ export default function applyDamage(node, actionContext) {
|
||||
creatureId: target._id,
|
||||
content: [{
|
||||
name,
|
||||
value: `Recieved **${damageDealt}** ${suffix}`,
|
||||
value: `Received **${damageDealt}** ${suffix}`,
|
||||
}],
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers';
|
||||
|
||||
export default function applyFolder(node, actionContext) {
|
||||
// Apply triggers
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { getPropertyDecendants } from '/imports/api/engine/loadCreatures.js';
|
||||
import applyProperty from '../applyProperty.js';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||
import { nodeArrayToTree } from '/imports/api/parenting/nodesToTree.js';
|
||||
import { adjustQuantityWork } from '/imports/api/creature/creatureProperties/methods/adjustQuantity.js';
|
||||
import { getPropertyDescendants } from '/imports/api/engine/loadCreatures';
|
||||
import applyProperty from '../applyProperty';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers';
|
||||
import { docsToForest as nodeArrayToTree } from '/imports/api/parenting/parentingFunctions';
|
||||
import { adjustQuantityWork } from '/imports/api/creature/creatureProperties/methods/adjustQuantity';
|
||||
|
||||
export default function applyItemAsAmmo(node, actionContext) {
|
||||
// The item node should come without children, since it is not part of the original action tree
|
||||
const prop = node.node;
|
||||
const prop = node.doc
|
||||
// Get all the item's descendant properties
|
||||
const properties = getPropertyDecendants(actionContext.creature._id, prop._id);
|
||||
const properties = getPropertyDescendants(actionContext.creature._id, prop._id);
|
||||
properties.sort((a, b) => a.order - b.order);
|
||||
const propertyForest = nodeArrayToTree(properties);
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import recalculateInlineCalculations from './shared/recalculateInlineCalculations.js';
|
||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||
import recalculateInlineCalculations from './shared/recalculateInlineCalculations';
|
||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers';
|
||||
|
||||
export default async function applyNote(node, actionContext) {
|
||||
await applyNodeTriggers(node, 'before', actionContext);
|
||||
const prop = node.node;
|
||||
export default function applyNote(node, actionContext) {
|
||||
applyNodeTriggers(node, 'before', actionContext);
|
||||
const prop = node.doc
|
||||
|
||||
// Log Name, summary
|
||||
let content = { name: prop.name };
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js';
|
||||
import logErrors from './shared/logErrors.js';
|
||||
import recalculateCalculation from '/imports/api/engine/actions/applyPropertyByType/shared/recalculateCalculation.js';
|
||||
import resolve, { toString } from '/imports/parser/resolve.js';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren';
|
||||
import logErrors from './shared/logErrors';
|
||||
import recalculateCalculation from '/imports/api/engine/actions/applyPropertyByType/shared/recalculateCalculation';
|
||||
import resolve, { toString } from '/imports/parser/resolve';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers';
|
||||
|
||||
export default function applyRoll(node, actionContext) {
|
||||
applyNodeTriggers(node, 'before', actionContext);
|
||||
const prop = node.node;
|
||||
const prop = node.doc
|
||||
|
||||
if (prop.roll?.calculation) {
|
||||
const logValue = [];
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import rollDice from '/imports/parser/rollDice.js';
|
||||
import recalculateCalculation from './shared/recalculateCalculation.js';
|
||||
import applyProperty from '../applyProperty.js';
|
||||
import numberToSignedString from '/imports/api/utility/numberToSignedString.js';
|
||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||
import { applyUnresolvedEffects } from '/imports/api/engine/actions/doCheck.js';
|
||||
import rollDice from '/imports/parser/rollDice';
|
||||
import recalculateCalculation from './shared/recalculateCalculation';
|
||||
import applyProperty from '../applyProperty';
|
||||
import numberToSignedString from '/imports/api/utility/numberToSignedString';
|
||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers';
|
||||
import { applyUnresolvedEffects } from '/imports/api/engine/actions/doCheck';
|
||||
|
||||
export default function applySavingThrow(node, actionContext) {
|
||||
applyNodeTriggers(node, 'before', actionContext);
|
||||
const prop = node.node;
|
||||
const prop = node.doc
|
||||
const originalTargets = actionContext.targets;
|
||||
|
||||
let saveTargets = prop.target === 'self' ? [actionContext.creature] : actionContext.targets;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import recalculateCalculation from './shared/recalculateCalculation.js';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js';
|
||||
import recalculateCalculation from './shared/recalculateCalculation';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers';
|
||||
import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren';
|
||||
|
||||
export default function applyToggle(node, actionContext) {
|
||||
applyNodeTriggers(node, 'before', actionContext);
|
||||
const prop = node.node;
|
||||
const prop = node.doc
|
||||
recalculateCalculation(prop.condition, actionContext);
|
||||
if (prop.condition?.value) {
|
||||
return applyChildren(node, actionContext);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||
import applyProperty from '/imports/api/engine/actions/applyProperty.js';
|
||||
import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers';
|
||||
import applyProperty from '/imports/api/engine/actions/applyProperty';
|
||||
|
||||
export default async function applyChildren(node, actionContext) {
|
||||
applyNodeTriggers(node, 'after', actionContext);
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import logErrors from './logErrors.js';
|
||||
import { Context, toPrimitiveOrString } from '/imports/parser/resolve.js';
|
||||
import logErrors from './logErrors';
|
||||
import { toPrimitiveOrString } from '/imports/parser/resolve';
|
||||
import {
|
||||
aggregateCalculationEffects,
|
||||
aggregateCalculationProficiencies,
|
||||
resolveCalculationNode,
|
||||
} from '/imports/api/engine/computation/computeComputation/computeByType/computeCalculation.js';
|
||||
} from '/imports/api/engine/computation/computeComputation/computeByType/computeCalculation';
|
||||
import { getSingleProperty } from '/imports/api/engine/loadCreatures';
|
||||
import resolve from '/imports/parser/resolve.js';
|
||||
import { getEffectiveActionScope } from '/imports/api/engine/actions/Actions';
|
||||
import resolve from '/imports/parser/resolve';
|
||||
|
||||
// TODO move this whole file to Actions.ts
|
||||
// Redo the work of imports/api/engine/computation/computeComputation/computeByType/computeCalculation.js
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import embedInlineCalculations from '/imports/api/engine/computation/utility/embedInlineCalculations.js';
|
||||
import recalculateCalculation from './recalculateCalculation.js'
|
||||
import embedInlineCalculations from '/imports/api/engine/computation/utility/embedInlineCalculations';
|
||||
import recalculateCalculation from './recalculateCalculation'
|
||||
|
||||
export default function recalculateInlineCalculations(inlineCalcObj, action) {
|
||||
export default function recalculateInlineCalculations(inlineCalcObj, actionContext) {
|
||||
// Skip if there are no calculations
|
||||
if (!inlineCalcObj?.inlineCalculations?.length) return;
|
||||
// Recalculate each calculation with the current scope
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { triggerMatchTags } from '/imports/api/engine/actions/applyTriggers.js';
|
||||
import clean from '/imports/api/engine/computation/utility/cleanProp.testFn.js';
|
||||
import { triggerMatchTags } from '/imports/api/engine/actions/applyTriggers';
|
||||
import clean from '/imports/api/engine/computation/utility/cleanProp.testFn';
|
||||
import { assert } from 'chai';
|
||||
|
||||
export default function () {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import recalculateCalculation from '/imports/api/engine/actions/applyPropertyByType/shared/recalculateCalculation.js';
|
||||
import recalculateInlineCalculations from '/imports/api/engine/actions/applyPropertyByType/shared/recalculateInlineCalculations.js';
|
||||
import { getPropertyDecendants } from '/imports/api/engine/loadCreatures.js';
|
||||
import { nodeArrayToTree } from '/imports/api/parenting/nodesToTree.js';
|
||||
import applyProperty from '/imports/api/engine/actions/applyProperty.js';
|
||||
import recalculateCalculation from '/imports/api/engine/actions/applyPropertyByType/shared/recalculateCalculation';
|
||||
import recalculateInlineCalculations from '/imports/api/engine/actions/applyPropertyByType/shared/recalculateInlineCalculations';
|
||||
import { getPropertyDescendants } from '/imports/api/engine/loadCreatures';
|
||||
import { TreeNode, docsToForest as nodeArrayToTree } from '/imports/api/parenting/parentingFunctions';
|
||||
import applyProperty from '/imports/api/engine/actions/applyProperty';
|
||||
import { difference, intersection } from 'lodash';
|
||||
import getEffectivePropTags from '/imports/api/engine/computation/utility/getEffectivePropTags.js';
|
||||
import getEffectivePropTags from '/imports/api/engine/computation/utility/getEffectivePropTags';
|
||||
|
||||
export async function applyNodeTriggers(node, timing, actionContext) {
|
||||
const prop = node.node;
|
||||
export function applyNodeTriggers(node: TreeNode<any>, timing, actionContext) {
|
||||
const prop = node.doc;
|
||||
const type = prop.type;
|
||||
const triggers = actionContext.triggers?.doActionProperty?.[type]?.[timing];
|
||||
if (triggers) {
|
||||
@@ -68,7 +68,7 @@ export async function applyTrigger(trigger, prop, actionContext) {
|
||||
if (!trigger.silent) actionContext.addLog(content);
|
||||
|
||||
// Get all the trigger's properties and apply them
|
||||
const properties = getPropertyDecendants(actionContext.creature._id, trigger._id);
|
||||
const properties = getPropertyDescendants(actionContext.creature._id, trigger._id);
|
||||
properties.sort((a, b) => a.order - b.order);
|
||||
const propertyForest = nodeArrayToTree(properties);
|
||||
for (const node of propertyForest) {
|
||||
@@ -1,15 +1,15 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
|
||||
import { nodeArrayToTree } from '/imports/api/parenting/nodesToTree.js';
|
||||
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions';
|
||||
import { docsToForest } from '/imports/api/parenting/parentingFunctions';
|
||||
import {
|
||||
getProperyAncestors, getPropertyDecendants
|
||||
} from '/imports/api/engine/loadCreatures.js';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import applyProperty from './applyProperty.js';
|
||||
import ActionContext from '/imports/api/engine/actions/ActionContext.js';
|
||||
getPropertyAncestors, getPropertyDescendants
|
||||
} from '/imports/api/engine/loadCreatures';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import applyProperty from './applyProperty';
|
||||
import ActionContext from '/imports/api/engine/actions/ActionContext';
|
||||
|
||||
const doAction = new ValidatedMethod({
|
||||
name: 'creatureProperties.doAction',
|
||||
@@ -47,10 +47,10 @@ const doAction = new ValidatedMethod({
|
||||
async run({ actionId, targetIds = [], scope, invocationId }) {
|
||||
console.log('do Action running');
|
||||
// Get action context
|
||||
let action = CreatureProperties.findOne(actionId);
|
||||
const creatureId = action.ancestors[0].id;
|
||||
|
||||
const actionContext = new ActionContext(creatureId, targetIds, this, invocationId);
|
||||
const action = CreatureProperties.findOne(actionId);
|
||||
if (!action) throw new Meteor.Error('not-found', 'The action was not found');
|
||||
const creatureId = action.root.id;
|
||||
const actionContext = new ActionContext(creatureId, targetIds, this);
|
||||
|
||||
// Check permissions
|
||||
assertEditPermission(actionContext.creature, this.userId);
|
||||
@@ -58,10 +58,10 @@ const doAction = new ValidatedMethod({
|
||||
assertEditPermission(target, this.userId);
|
||||
});
|
||||
|
||||
const ancestors = getProperyAncestors(creatureId, action._id);
|
||||
const ancestors = getPropertyAncestors(creatureId, action._id);
|
||||
ancestors.sort((a, b) => a.order - b.order);
|
||||
|
||||
const properties = getPropertyDecendants(creatureId, action._id);
|
||||
const properties = getPropertyDescendants(creatureId, action._id);
|
||||
properties.push(action);
|
||||
properties.sort((a, b) => a.order - b.order);
|
||||
|
||||
@@ -86,7 +86,7 @@ export async function doActionWork({
|
||||
}) {
|
||||
// get the docs
|
||||
const ancestorScope = getAncestorScope(ancestors);
|
||||
const propertyForest = nodeArrayToTree(properties);
|
||||
const propertyForest = docsToForest(properties);
|
||||
if (propertyForest.length !== 1) {
|
||||
throw new Meteor.Error(`The action has ${propertyForest.length} top level properties, expected 1`);
|
||||
}
|
||||
@@ -106,7 +106,7 @@ export async function doActionWork({
|
||||
|
||||
// Assumes ancestors are in tree order already
|
||||
function getAncestorScope(ancestors) {
|
||||
let scope = {};
|
||||
const scope = {};
|
||||
ancestors.forEach(prop => {
|
||||
scope[`#${prop.type}`] = prop;
|
||||
});
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import '/imports/api/simpleSchemaConfig.js';
|
||||
//import testTypes from './testTypes/index.js';
|
||||
import applyTriggers from '/imports/api/engine/actions/applyTriggers.testFn.js';
|
||||
import { doActionWork } from './doAction.js';
|
||||
import { CreatureLogSchema } from '/imports/api/creature/log/CreatureLogs.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||
import '/imports/api/simpleSchemaConfig';
|
||||
//import testTypes from './testTypes/index';
|
||||
import applyTriggers from '/imports/api/engine/actions/applyTriggers.testFn';
|
||||
import { doActionWork } from './doAction';
|
||||
import { CreatureLogSchema } from '/imports/api/creature/log/CreatureLogs';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures';
|
||||
|
||||
function cleanProp(prop) {
|
||||
let schema = CreatureProperties.simpleSchema(prop);
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures';
|
||||
import {
|
||||
getProperyAncestors, getPropertyDecendants
|
||||
} from '/imports/api/engine/loadCreatures.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
|
||||
import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js';
|
||||
import { doActionWork } from '/imports/api/engine/actions/doAction.js';
|
||||
import ActionContext from '/imports/api/engine/actions/ActionContext.js';
|
||||
getPropertyAncestors, getPropertyDescendants
|
||||
} from '/imports/api/engine/loadCreatures';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions';
|
||||
import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty';
|
||||
import { doActionWork } from '/imports/api/engine/actions/doAction';
|
||||
import ActionContext from '/imports/api/engine/actions/ActionContext';
|
||||
|
||||
const doAction = new ValidatedMethod({
|
||||
name: 'creatureProperties.doCastSpell',
|
||||
@@ -48,7 +48,7 @@ const doAction = new ValidatedMethod({
|
||||
run({ spellId, slotId, ritual, targetIds = [], scope = {} }) {
|
||||
// Get action context
|
||||
let spell = CreatureProperties.findOne(spellId);
|
||||
const creatureId = spell.ancestors[0].id;
|
||||
const creatureId = spell.root.id;
|
||||
const actionContext = new ActionContext(creatureId, targetIds, this);
|
||||
|
||||
// Check permissions
|
||||
@@ -57,10 +57,10 @@ const doAction = new ValidatedMethod({
|
||||
assertEditPermission(target, this.userId);
|
||||
});
|
||||
|
||||
const ancestors = getProperyAncestors(creatureId, spell._id);
|
||||
const ancestors = getPropertyAncestors(creatureId, spell._id);
|
||||
ancestors.sort((a, b) => a.order - b.order);
|
||||
|
||||
const properties = getPropertyDecendants(creatureId, spell._id);
|
||||
const properties = getPropertyDescendants(creatureId, spell._id);
|
||||
properties.push(spell);
|
||||
properties.sort((a, b) => a.order - b.order);
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
|
||||
import rollDice from '/imports/parser/rollDice.js';
|
||||
import numberToSignedString from '/imports/api/utility/numberToSignedString.js';
|
||||
import { applyTriggers } from '/imports/api/engine/actions/applyTriggers.js';
|
||||
import ActionContext from '/imports/api/engine/actions/ActionContext.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions';
|
||||
import rollDice from '/imports/parser/rollDice';
|
||||
import numberToSignedString from '/imports/api/utility/numberToSignedString';
|
||||
import { applyTriggers } from '/imports/api/engine/actions/applyTriggers';
|
||||
import ActionContext from '/imports/api/engine/actions/ActionContext';
|
||||
import recalculateCalculation from '/imports/api/engine/actions/applyPropertyByType/shared/recalculateCalculation';
|
||||
import { getSingleProperty } from '/imports/api/engine/loadCreatures';
|
||||
|
||||
@@ -26,7 +26,8 @@ const doCheck = new ValidatedMethod({
|
||||
},
|
||||
run({ propId, scope }) {
|
||||
const prop = CreatureProperties.findOne(propId);
|
||||
const creatureId = prop.ancestors[0].id;
|
||||
if (!prop) throw new Meteor.Error('not-found', 'The property was not found');
|
||||
const creatureId = prop.root.id;
|
||||
const actionContext = new ActionContext(creatureId, [creatureId], this);
|
||||
Object.assign(actionContext.scope, scope);
|
||||
actionContext.scope[`#${prop.type}`] = prop;
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
import './doCastSpell.js';
|
||||
import './doCheck.js';
|
||||
import './doCastSpell';
|
||||
import './doCheck';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { EJSON } from 'meteor/ejson';
|
||||
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';
|
||||
|
||||
interface CreatureProperty {
|
||||
_id: string;
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import walkDown from '/imports/api/engine/computation/utility/walkdown.js';
|
||||
import { CreatureProperty } from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import walkDown from '/imports/api/engine/computation/utility/walkdown';
|
||||
import { TreeNode } from '/imports/api/parenting/parentingFunctions';
|
||||
import { isSpell } from '/imports/api/properties/Spells';
|
||||
|
||||
export default function computeInactiveStatus(node) {
|
||||
const prop = node.node;
|
||||
export default function computeInactiveStatus(node: TreeNode<CreatureProperty>): void {
|
||||
const prop = node.doc;
|
||||
if (!isActive(prop)) {
|
||||
// Mark prop inactive due to self
|
||||
prop.inactive = true;
|
||||
@@ -10,22 +13,21 @@ export default function computeInactiveStatus(node) {
|
||||
if (!childrenActive(prop)) {
|
||||
// Mark children as inactive due to ancestor
|
||||
walkDown(node.children, child => {
|
||||
child.node.inactive = true;
|
||||
child.node.deactivatedByAncestor = true;
|
||||
child.doc.inactive = true;
|
||||
child.doc.deactivatedByAncestor = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function isActive(prop) {
|
||||
function isActive(prop: CreatureProperty): boolean {
|
||||
if (prop.disabled) return false;
|
||||
switch (prop.type) {
|
||||
// Unprepared spells are inactive
|
||||
case 'spell': return !!prop.prepared || !!prop.alwaysPrepared;
|
||||
default: return true;
|
||||
if (isSpell(prop)) {
|
||||
return !!prop.prepared || !!prop.alwaysPrepared;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function childrenActive(prop) {
|
||||
function childrenActive(prop): boolean {
|
||||
// Children of disabled properties are always inactive
|
||||
if (prop.disabled) return false;
|
||||
switch (prop.type) {
|
||||
@@ -3,11 +3,11 @@
|
||||
* before `spacesLeft` can be computed
|
||||
*/
|
||||
export default function computeSlotQuantityFilled(node, dependencyGraph) {
|
||||
let slot = node.node;
|
||||
let slot = node.doc;
|
||||
if (slot.type !== 'propertySlot') return;
|
||||
slot.totalFilled = 0;
|
||||
node.children.forEach(child => {
|
||||
let childProp = child.node;
|
||||
let childProp = child.doc;
|
||||
dependencyGraph.addLink(slot._id, childProp._id, 'slotFill');
|
||||
if (
|
||||
Number.isFinite(childProp.slotQuantityFilled)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import walkDown from '/imports/api/engine/computation/utility/walkdown.js';
|
||||
import { getEffectTagTargets } from '/imports/api/engine/computation/buildComputation/linkTypeDependencies.js';
|
||||
import walkDown from '/imports/api/engine/computation/utility/walkdown';
|
||||
import { getEffectTagTargets } from '/imports/api/engine/computation/buildComputation/linkTypeDependencies';
|
||||
|
||||
export default function computeToggleDependencies(node, dependencyGraph, computation, forest) {
|
||||
const prop = node.node;
|
||||
const prop = node.doc
|
||||
// Only for toggles
|
||||
if (prop.type !== 'toggle') return;
|
||||
|
||||
@@ -11,12 +11,12 @@ export default function computeToggleDependencies(node, dependencyGraph, computa
|
||||
getEffectTagTargets(prop, computation).forEach(targetId => {
|
||||
const target = forest.nodeIndex[targetId];
|
||||
if (!target) return;
|
||||
target.node._computationDetails.toggleAncestors.push(prop);
|
||||
dependencyGraph.addLink(target.node._id, prop._id, 'toggle');
|
||||
target.doc._computationDetails.toggleAncestors.push(prop);
|
||||
dependencyGraph.addLink(target.doc._id, prop._id, 'toggle');
|
||||
walkDown(target.children, child => {
|
||||
// The child nodes depend on the toggle
|
||||
child.node._computationDetails.toggleAncestors.push(prop);
|
||||
dependencyGraph.addLink(child.node._id, prop._id, 'toggle');
|
||||
child.doc._computationDetails.toggleAncestors.push(prop);
|
||||
dependencyGraph.addLink(child.doc._id, prop._id, 'toggle');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -26,7 +26,7 @@ export default function computeToggleDependencies(node, dependencyGraph, computa
|
||||
|
||||
walkDown(node.children, child => {
|
||||
// The child nodes depend on the toggle
|
||||
child.node._computationDetails.toggleAncestors.push(prop);
|
||||
dependencyGraph.addLink(child.node._id, prop._id, 'toggle');
|
||||
child.doc._computationDetails.toggleAncestors.push(prop);
|
||||
dependencyGraph.addLink(child.doc._id, prop._id, 'toggle');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import findAncestorByType from '/imports/api/engine/computation/utility/findAncestorByType.js';
|
||||
import { traverse } from '/imports/parser/resolve.js';
|
||||
import { traverse } from '/imports/parser/resolve';
|
||||
|
||||
export default function linkCalculationDependencies(dependencyGraph, prop, { propsById }) {
|
||||
prop._computationDetails.calculations.forEach(calcObj => {
|
||||
@@ -57,3 +56,14 @@ function getAncestorProp(type, memo, prop, propsById) {
|
||||
return ancestorProp;
|
||||
}
|
||||
}
|
||||
|
||||
function findAncestorByType(prop, type, propsById) {
|
||||
if (!prop || !prop.parentId) return;
|
||||
let parentProp = prop;
|
||||
while (parentProp) {
|
||||
parentProp = propsById[parentProp.parentId];
|
||||
if (parentProp?.type === type) {
|
||||
return parentProp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ export default function linkInventory(forest, dependencyGraph) {
|
||||
|
||||
while (stack.length) {
|
||||
const top = stack[stack.length - 1];
|
||||
const prop = top.node;
|
||||
const prop = top.doc;
|
||||
if (prop._computationDetails.inventoryChildrenVisited) {
|
||||
if (prop.type === 'container') containerStack.pop();
|
||||
stack.pop();
|
||||
@@ -18,7 +18,7 @@ export default function linkInventory(forest, dependencyGraph) {
|
||||
} else {
|
||||
// Add all containers to the stack when we first visit them
|
||||
if (prop.type === 'container') {
|
||||
containerStack.push(top.node);
|
||||
containerStack.push(top.doc);
|
||||
}
|
||||
// Push children onto the stack and mark this as children are visited
|
||||
stack.push(...top.children);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import INLINE_CALCULATION_REGEX from '/imports/constants/INLINE_CALCULTION_REGEX.js';
|
||||
import { prettifyParseError, parse } from '/imports/parser/parser.js';
|
||||
import applyFnToKey from '/imports/api/engine/computation/utility/applyFnToKey.js';
|
||||
import INLINE_CALCULATION_REGEX from '/imports/constants/INLINE_CALCULTION_REGEX';
|
||||
import { prettifyParseError, parse } from '/imports/parser/parser';
|
||||
import applyFnToKey from '/imports/api/engine/computation/utility/applyFnToKey';
|
||||
import { get, set, unset } from 'lodash';
|
||||
import errorNode from '/imports/parser/parseTree/error.js';
|
||||
import cyrb53 from '/imports/api/engine/computation/utility/cyrb53.js';
|
||||
import errorNode from '/imports/parser/parseTree/error';
|
||||
import cyrb53 from '/imports/api/engine/computation/utility/cyrb53';
|
||||
|
||||
export default function parseCalculationFields(prop, schemas) {
|
||||
discoverInlineCalculationFields(prop, schemas);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import applyFnToKey from '../utility/applyFnToKey.js';
|
||||
import applyFnToKey from '../utility/applyFnToKey';
|
||||
import { unset } from 'lodash';
|
||||
|
||||
export default function removeSchemaFields(schemas, prop) {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { buildComputationFromProps } from '/imports/api/engine/computation/buildCreatureComputation.js';
|
||||
import { buildComputationFromProps } from '/imports/api/engine/computation/buildCreatureComputation';
|
||||
import { assert } from 'chai';
|
||||
import clean from '../../utility/cleanProp.testFn.js';
|
||||
import clean from '../../utility/cleanProp.testFn';
|
||||
import { applyNestedSetProperties } from '/imports/api/parenting/parentingFunctions';
|
||||
|
||||
export default function () {
|
||||
let computation = buildComputationFromProps(testProperties);
|
||||
|
||||
const bySelf = (propId, note) => assertDeactivatedBySelf(computation, propId, note);
|
||||
const byAncestor = (propId, note) => assertDeactivatedByAncestor(computation, propId, note);
|
||||
const active = (propId, note) => assertActive(computation, propId, note);
|
||||
@@ -51,66 +53,68 @@ var testProperties = [
|
||||
clean({
|
||||
_id: 'itemUnequippedId',
|
||||
type: 'item',
|
||||
ancestors: [{ id: 'charId' }],
|
||||
parentId: 'charId',
|
||||
}),
|
||||
clean({
|
||||
_id: 'itemUnequippedChildId',
|
||||
type: 'folder',
|
||||
ancestors: [{ id: 'charId' }, { id: 'itemUnequippedId' }],
|
||||
parentId: 'itemUnequippedId',
|
||||
}),
|
||||
clean({
|
||||
_id: 'itemEquippedId',
|
||||
type: 'item',
|
||||
equipped: true,
|
||||
ancestors: [{ id: 'charId' }],
|
||||
parentId: 'charId',
|
||||
}),
|
||||
clean({
|
||||
_id: 'itemEquippedChildId',
|
||||
type: 'folder',
|
||||
ancestors: [{ id: 'charId' }, { id: 'itemEquippedId' }],
|
||||
parentId: 'itemEquippedId',
|
||||
}),
|
||||
// Spells
|
||||
clean({
|
||||
_id: 'spellPreparedId',
|
||||
type: 'spell',
|
||||
ancestors: [{ id: 'charId' }],
|
||||
parentId: 'charId',
|
||||
prepared: true,
|
||||
}),
|
||||
clean({
|
||||
_id: 'spellPreparedChildId',
|
||||
type: 'folder',
|
||||
ancestors: [{ id: 'charId' }, { id: 'spellPreparedId' }],
|
||||
parentId: 'spellPreparedId',
|
||||
}),
|
||||
clean({
|
||||
_id: 'spellAlwaysPreparedId',
|
||||
type: 'spell',
|
||||
ancestors: [{ id: 'charId' }],
|
||||
parentId: 'charId',
|
||||
alwaysPrepared: true,
|
||||
}),
|
||||
clean({
|
||||
_id: 'spellAlwaysPreparedChildId',
|
||||
type: 'folder',
|
||||
ancestors: [{ id: 'charId' }, { id: 'spellAlwaysPreparedId' }],
|
||||
parentId: 'spellAlwaysPreparedId',
|
||||
}),
|
||||
clean({
|
||||
_id: 'spellUnpreparedId',
|
||||
type: 'spell',
|
||||
ancestors: [{ id: 'charId' }],
|
||||
parentId: 'charId',
|
||||
}),
|
||||
clean({
|
||||
_id: 'spellUnpreparedChildId',
|
||||
type: 'folder',
|
||||
ancestors: [{ id: 'charId' }, { id: 'spellUnpreparedId' }],
|
||||
parentId: 'spellUnpreparedId',
|
||||
}),
|
||||
// Notes
|
||||
clean({
|
||||
_id: 'NoteId',
|
||||
type: 'note',
|
||||
ancestors: [{ id: 'charId' }],
|
||||
parentId: 'charId',
|
||||
}),
|
||||
clean({
|
||||
_id: 'NoteChildId',
|
||||
type: 'folder',
|
||||
ancestors: [{ id: 'charId' }, { id: 'NoteId' }],
|
||||
parentId: 'NoteId',
|
||||
}),
|
||||
];
|
||||
|
||||
applyNestedSetProperties(testProperties);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { buildComputationFromProps } from '/imports/api/engine/computation/buildCreatureComputation.js';
|
||||
import { buildComputationFromProps } from '/imports/api/engine/computation/buildCreatureComputation';
|
||||
import { assert } from 'chai';
|
||||
import clean from '../../utility/cleanProp.testFn.js';
|
||||
import clean from '../../utility/cleanProp.testFn';
|
||||
import { applyNestedSetProperties } from '/imports/api/parenting/parentingFunctions';
|
||||
|
||||
export default function () {
|
||||
const computation = buildComputationFromProps(testProperties);
|
||||
@@ -13,7 +14,6 @@ var testProperties = [
|
||||
clean({
|
||||
_id: 'slotId',
|
||||
type: 'propertySlot',
|
||||
ancestors: [{ id: 'charId' }],
|
||||
}),
|
||||
// Children
|
||||
clean({
|
||||
@@ -21,16 +21,18 @@ var testProperties = [
|
||||
type: 'folder',
|
||||
slotQuantityFilled: 3,
|
||||
slotFillerType: 'item',
|
||||
ancestors: [{ id: 'charId' }, { id: 'slotId' }],
|
||||
parentId: 'slotId',
|
||||
}),
|
||||
clean({
|
||||
_id: 'slotChildId',
|
||||
type: 'item',
|
||||
ancestors: [{ id: 'charId' }, { id: 'slotId' }],
|
||||
parentId: 'slotId',
|
||||
}),
|
||||
clean({
|
||||
_id: 'slotGrandchildId',
|
||||
type: 'effect',
|
||||
ancestors: [{ id: 'charId' }, { id: 'slotId' }, { id: 'slotChildId' }],
|
||||
parentId: 'slotChildId',
|
||||
}),
|
||||
];
|
||||
|
||||
applyNestedSetProperties(testProperties);
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { buildComputationFromProps } from '/imports/api/engine/computation/buildCreatureComputation.js';
|
||||
import { buildComputationFromProps } from '/imports/api/engine/computation/buildCreatureComputation';
|
||||
import { assert } from 'chai';
|
||||
import clean from '../../utility/cleanProp.testFn.js';
|
||||
import clean from '../../utility/cleanProp.testFn';
|
||||
import { applyNestedSetProperties } from '/imports/api/parenting/parentingFunctions';
|
||||
|
||||
export default function(){
|
||||
export default function () {
|
||||
const computation = buildComputationFromProps(testProperties);
|
||||
const hasLink = computation.dependencyGraph.hasLink;
|
||||
assert.include(
|
||||
@@ -37,38 +38,37 @@ var testProperties = [
|
||||
_id: 'enabledToggleId',
|
||||
type: 'toggle',
|
||||
enabled: true,
|
||||
ancestors: [{id: 'charId'}],
|
||||
}),
|
||||
clean({
|
||||
_id: 'disabledToggleId',
|
||||
type: 'toggle',
|
||||
disabled: true,
|
||||
ancestors: [{id: 'charId'}],
|
||||
}),
|
||||
clean({
|
||||
_id: 'conditionToggleId',
|
||||
type: 'toggle',
|
||||
ancestors: [{id: 'charId'}],
|
||||
}),
|
||||
// Children
|
||||
clean({
|
||||
_id: 'enabledToggleChildId',
|
||||
type: 'folder',
|
||||
ancestors: [{id: 'charId'}, {id: 'enabledToggleId'}],
|
||||
parentId: 'enabledToggleId',
|
||||
}),
|
||||
clean({
|
||||
_id: 'disabledToggleChildId',
|
||||
type: 'folder',
|
||||
ancestors: [{id: 'charId'}, {id: 'disabledToggleId'}],
|
||||
parentId: 'disabledToggleId',
|
||||
}),
|
||||
clean({
|
||||
_id: 'conditionToggleChildId',
|
||||
type: 'folder',
|
||||
ancestors: [{id: 'charId'}, {id: 'conditionToggleId'}],
|
||||
parentId: 'conditionToggleId',
|
||||
}),
|
||||
clean({
|
||||
_id: 'conditionToggleGrandChildId',
|
||||
type: 'folder',
|
||||
ancestors: [{id: 'charId'}, {id: 'conditionToggleId'}, {id: 'conditionToggleChildId'}],
|
||||
parentId: 'conditionToggleChildId',
|
||||
}),
|
||||
];
|
||||
|
||||
applyNestedSetProperties(testProperties);
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { buildComputationFromProps } from '/imports/api/engine/computation/buildCreatureComputation.js';
|
||||
import { buildComputationFromProps } from '/imports/api/engine/computation/buildCreatureComputation';
|
||||
import { assert } from 'chai';
|
||||
import clean from '../../utility/cleanProp.testFn.js';
|
||||
import clean from '../../utility/cleanProp.testFn';
|
||||
import { applyNestedSetProperties } from '/imports/api/parenting/parentingFunctions';
|
||||
|
||||
export default function(){
|
||||
export default function () {
|
||||
const computation = buildComputationFromProps(testProperties);
|
||||
const hasLink = computation.dependencyGraph.hasLink;
|
||||
const prop = (id) => computation.propsById[id];
|
||||
@@ -32,7 +33,6 @@ var testProperties = [
|
||||
clean({
|
||||
_id: 'spellListId',
|
||||
type: 'spellList',
|
||||
ancestors: [{id: 'charId'}],
|
||||
}),
|
||||
clean({
|
||||
_id: 'childId',
|
||||
@@ -40,7 +40,7 @@ var testProperties = [
|
||||
description: {
|
||||
text: 'DC {#spellList.dc} save or suck'
|
||||
},
|
||||
ancestors: [{id: 'charId'}, {id: 'spellListId'}],
|
||||
parentId: 'spellListId',
|
||||
}),
|
||||
clean({
|
||||
_id: 'grandchildId',
|
||||
@@ -48,7 +48,7 @@ var testProperties = [
|
||||
dc: {
|
||||
calculation: '#spellList.dc + strength + wisdom.modifier'
|
||||
},
|
||||
ancestors: [{id: 'charId'}, {id: 'spellListId'}, {id: 'childId'}],
|
||||
parentId: 'childId',
|
||||
}),
|
||||
clean({
|
||||
_id: 'strengthId',
|
||||
@@ -57,6 +57,7 @@ var testProperties = [
|
||||
baseValue: {
|
||||
calculation: '15 + ',
|
||||
},
|
||||
ancestors: [{id: 'charId'}],
|
||||
}),
|
||||
];
|
||||
|
||||
applyNestedSetProperties(testProperties);
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { buildComputationFromProps } from '/imports/api/engine/computation/buildCreatureComputation.js';
|
||||
import { buildComputationFromProps } from '/imports/api/engine/computation/buildCreatureComputation';
|
||||
import { assert } from 'chai';
|
||||
import clean from '../../utility/cleanProp.testFn.js';
|
||||
import clean from '../../utility/cleanProp.testFn';
|
||||
import { applyNestedSetProperties } from '/imports/api/parenting/parentingFunctions';
|
||||
|
||||
export default function(){
|
||||
export default function () {
|
||||
const computation = buildComputationFromProps(testProperties);
|
||||
const hasLink = computation.dependencyGraph.hasLink;
|
||||
|
||||
@@ -62,28 +63,28 @@ var testProperties = [
|
||||
type: 'item',
|
||||
equipped: true,
|
||||
attuned: true,
|
||||
ancestors: [{id: 'charId'}],
|
||||
}),
|
||||
clean({
|
||||
_id: 'containerId',
|
||||
type: 'container',
|
||||
carried: true,
|
||||
ancestors: [{id: 'charId'}],
|
||||
}),
|
||||
clean({
|
||||
_id: 'childContainerId',
|
||||
type: 'container',
|
||||
carried: true,
|
||||
ancestors: [{id: 'charId'}, {id: 'containerId'}],
|
||||
}),
|
||||
clean({
|
||||
_id: 'childItemId',
|
||||
type: 'item',
|
||||
ancestors: [{id: 'charId'}, {id: 'containerId'}],
|
||||
parentId: 'containerId',
|
||||
}),
|
||||
clean({
|
||||
_id: 'grandchildItemId',
|
||||
type: 'item',
|
||||
ancestors: [{id: 'charId'}, {id: 'containerId'}, {id: 'childContainerId'}],
|
||||
parentId: 'childContainerId',
|
||||
}),
|
||||
clean({
|
||||
_id: 'childItemId',
|
||||
type: 'item',
|
||||
parentId: 'containerId',
|
||||
}),
|
||||
];
|
||||
|
||||
applyNestedSetProperties(testProperties);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { buildComputationFromProps } from '/imports/api/engine/computation/buildCreatureComputation.js';
|
||||
import { buildComputationFromProps } from '/imports/api/engine/computation/buildCreatureComputation';
|
||||
import { assert } from 'chai';
|
||||
import clean from '../../utility/cleanProp.testFn.js';
|
||||
import clean from '../../utility/cleanProp.testFn';
|
||||
|
||||
export default function(){
|
||||
export default function () {
|
||||
const computation = buildComputationFromProps(testProperties);
|
||||
const getLink = computation.dependencyGraph.hasLink;
|
||||
const getNode = computation.dependencyGraph.getNode;
|
||||
@@ -22,6 +22,5 @@ var testProperties = [
|
||||
_id: 'strengthId',
|
||||
type: 'attribute',
|
||||
variableName: 'strength',
|
||||
ancestors: [{id: 'charId'}],
|
||||
}),
|
||||
];
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { nodeArrayToTree } from '/imports/api/parenting/nodesToTree.js';
|
||||
import { docsToForest } from '/imports/api/parenting/parentingFunctions';
|
||||
import { DenormalisedOnlyCreaturePropertySchema as denormSchema }
|
||||
from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import { getProperties, getCreature, getVariables } from '/imports/api/engine/loadCreatures.js';
|
||||
import computedOnlySchemas from '/imports/api/properties/computedOnlyPropertySchemasIndex.js';
|
||||
import computedSchemas from '/imports/api/properties/computedPropertySchemasIndex.js';
|
||||
import linkInventory from './buildComputation/linkInventory.js';
|
||||
import walkDown from './utility/walkdown.js';
|
||||
import parseCalculationFields from './buildComputation/parseCalculationFields.js';
|
||||
import computeInactiveStatus from './buildComputation/computeInactiveStatus.js';
|
||||
import computeToggleDependencies from './buildComputation/computeToggleDependencies.js';
|
||||
import linkCalculationDependencies from './buildComputation/linkCalculationDependencies.js';
|
||||
import linkTypeDependencies from './buildComputation/linkTypeDependencies.js';
|
||||
import computeSlotQuantityFilled from './buildComputation/computeSlotQuantityFilled.js';
|
||||
import CreatureComputation from './CreatureComputation.ts';
|
||||
import removeSchemaFields from './buildComputation/removeSchemaFields.js';
|
||||
from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import { getProperties, getCreature, getVariables } from '/imports/api/engine/loadCreatures';
|
||||
import computedOnlySchemas from '/imports/api/properties/computedOnlyPropertySchemasIndex';
|
||||
import computedSchemas from '/imports/api/properties/computedPropertySchemasIndex';
|
||||
import linkInventory from './buildComputation/linkInventory';
|
||||
import walkDown from './utility/walkdown';
|
||||
import parseCalculationFields from './buildComputation/parseCalculationFields';
|
||||
import computeInactiveStatus from './buildComputation/computeInactiveStatus';
|
||||
import computeToggleDependencies from './buildComputation/computeToggleDependencies';
|
||||
import linkCalculationDependencies from './buildComputation/linkCalculationDependencies';
|
||||
import linkTypeDependencies from './buildComputation/linkTypeDependencies';
|
||||
import computeSlotQuantityFilled from './buildComputation/computeSlotQuantityFilled';
|
||||
import CreatureComputation from './CreatureComputation';
|
||||
import removeSchemaFields from './buildComputation/removeSchemaFields';
|
||||
|
||||
/**
|
||||
* Store index of properties
|
||||
@@ -86,7 +86,7 @@ export function buildComputationFromProps(properties, creature, variables) {
|
||||
});
|
||||
|
||||
// Get all the properties as trees based on their ancestors
|
||||
let forest = nodeArrayToTree(properties);
|
||||
let forest = docsToForest(properties);
|
||||
// Walk the property trees computing things that need to be inherited
|
||||
walkDown(forest, node => {
|
||||
computeInactiveStatus(node);
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import '/imports/api/simpleSchemaConfig.js';
|
||||
import { buildComputationFromProps } from './buildCreatureComputation.js';
|
||||
import '/imports/api/simpleSchemaConfig';
|
||||
import { buildComputationFromProps } from './buildCreatureComputation';
|
||||
import { assert } from 'chai';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import computeInactiveStatus from './buildComputation/tests/computeInactiveStatus.testFn.js';
|
||||
import computeSlotQuantityFilled from './buildComputation/tests/computeSlotQuantityFilled.testFn.js';
|
||||
import computeToggleDependencies from './buildComputation/tests/computeToggleDependencies.testFn.js';
|
||||
import linkCalculationDependencies from './buildComputation/tests/linkCalculationDependencies.testFn.js';
|
||||
import linkInventory from './buildComputation/tests/linkInventory.testFn.js';
|
||||
import linkTypeDependencies from './buildComputation/tests/linkTypeDependencies.testFn.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import computeInactiveStatus from './buildComputation/tests/computeInactiveStatus.testFn';
|
||||
import computeSlotQuantityFilled from './buildComputation/tests/computeSlotQuantityFilled.testFn';
|
||||
import computeToggleDependencies from './buildComputation/tests/computeToggleDependencies.testFn';
|
||||
import linkCalculationDependencies from './buildComputation/tests/linkCalculationDependencies.testFn';
|
||||
import linkInventory from './buildComputation/tests/linkInventory.testFn';
|
||||
import linkTypeDependencies from './buildComputation/tests/linkTypeDependencies.testFn';
|
||||
|
||||
describe('buildComputation', function(){
|
||||
it('Builds something at all', function(){
|
||||
describe('buildComputation', function () {
|
||||
it('Builds something at all', function () {
|
||||
let computation = buildComputationFromProps(testProperties);
|
||||
assert.exists(computation);
|
||||
});
|
||||
@@ -37,7 +37,7 @@ var testProperties = [
|
||||
}),
|
||||
];
|
||||
|
||||
function clean(prop){
|
||||
function clean(prop) {
|
||||
let schema = CreatureProperties.simpleSchema(prop);
|
||||
return schema.clean(prop);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import _variable from './computeByType/computeVariable.js';
|
||||
import action from './computeByType/computeAction.js';
|
||||
import attribute from './computeByType/computeAttribute.js';
|
||||
import skill from './computeByType/computeSkill.js';
|
||||
import pointBuy from './computeByType/computePointBuy.js';
|
||||
import propertySlot from './computeByType/computeSlot.js';
|
||||
import container from './computeByType/computeContainer.js';
|
||||
import spellList from './computeByType/computeSpellList.js';
|
||||
import toggle from './computeByType/computeToggle.js';
|
||||
import trigger from './computeByType/computeTrigger.js'
|
||||
import _calculation from './computeByType/computeCalculation.js';
|
||||
import _variable from './computeByType/computeVariable';
|
||||
import action from './computeByType/computeAction';
|
||||
import attribute from './computeByType/computeAttribute';
|
||||
import skill from './computeByType/computeSkill';
|
||||
import pointBuy from './computeByType/computePointBuy';
|
||||
import propertySlot from './computeByType/computeSlot';
|
||||
import container from './computeByType/computeContainer';
|
||||
import spellList from './computeByType/computeSpellList';
|
||||
import toggle from './computeByType/computeToggle';
|
||||
import _calculation from './computeByType/computeCalculation';
|
||||
|
||||
export default Object.freeze({
|
||||
_variable,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import call from '/imports/parser/parseTree/call.js';
|
||||
import constant from '/imports/parser/parseTree/constant.js';
|
||||
import operator from '/imports/parser/parseTree/operator.js';
|
||||
import parenthesis from '/imports/parser/parseTree/parenthesis.js';
|
||||
import resolve, { toPrimitiveOrString } from '/imports/parser/resolve.js';
|
||||
import call from '/imports/parser/parseTree/call';
|
||||
import constant from '/imports/parser/parseTree/constant';
|
||||
import operator from '/imports/parser/parseTree/operator';
|
||||
import parenthesis from '/imports/parser/parseTree/parenthesis';
|
||||
import resolve, { toPrimitiveOrString } from '/imports/parser/resolve';
|
||||
|
||||
export default function computeCalculation(computation, node) {
|
||||
const calcObj = node.data;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import aggregate from './computeVariable/aggregate/index.js';
|
||||
import { safeStrip } from '/imports/api/engine/computation/utility/stripFloatingPointOddities.js';
|
||||
import aggregate from './computeVariable/aggregate/index';
|
||||
import { safeStrip } from '/imports/api/engine/computation/utility/stripFloatingPointOddities';
|
||||
|
||||
export default function computeContainer(computation, node) {
|
||||
if (!node.data) node.data = {};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user