Fixed bugs with item display, equipment will now automatically move to the first property with the 'equipment' tag, carried items will move to the first property with the 'carried' tag

This commit is contained in:
Stefan Zermatten
2021-01-12 12:54:02 +02:00
parent 28c042343e
commit 403f2663c2
11 changed files with 244 additions and 99 deletions

View File

@@ -22,6 +22,7 @@ import { storedIconsSchema } from '/imports/api/icons/Icons.js';
import { reorderDocs } from '/imports/api/parenting/order.js';
import '/imports/api/creature/actions/doAction.js';
import '/imports/api/creature/creatureProperties/manageEquipment.js';
let CreatureProperties = new Mongo.Collection('creatureProperties');
@@ -50,6 +51,15 @@ let CreaturePropertySchema = new SimpleSchema({
inactive: {
type: Boolean,
optional: true,
index: 1,
},
// Denormalised flag if this property was made inactive by an inactive
// ancestor. True if this property has an inactive ancestor even if this
// property is itself inactive
deactivatedByAncestor: {
type: Boolean,
optional: true,
index: 1,
},
});
@@ -205,7 +215,7 @@ const insertPropertyFromLibraryNode = new ValidatedMethod({
const updateProperty = new ValidatedMethod({
name: 'creatureProperties.update',
validate({_id, path}){
if (!_id) return false;
if (!_id) throw new Meteor.Error('No _id', '_id is required');
// We cannot change these fields with a simple update
switch (path[0]){
case 'type':

View File

@@ -0,0 +1,7 @@
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import getClosestPropertyAncestorCreature from '/imports/api/creature/creatureProperties/getClosestPropertyAncestorCreature.js';
export default function assertPropertyEditPermission(prop, userId){
let creature = getClosestPropertyAncestorCreature(prop);
assertEditPermission(creature, userId);
}

View File

@@ -0,0 +1,7 @@
import Creatures from '/imports/api/creature/Creatures.js';
import getClosestPropertyAncestorCreatureId from '/imports/api/creature/creatureProperties/getClosestPropertyAncestorCreatureId.js';
export default function getClosestPropertyAncestorCreature(prop){
let creatureId = getClosestPropertyAncestorCreatureId(prop);
return Creatures.findOne(creatureId);
}

View File

@@ -0,0 +1,13 @@
export default function getClosestPropertyAncestorCreatureId(prop){
if (!prop.ancestors) throw 'Property has no ancestors';
let creatureId;
// Find the last ancestor in the creature collection
for (let i = prop.ancestors.length - 1; i >= 0; i--){
if (prop.ancestors[i].collection === 'creatures'){
creatureId = prop.ancestors[i].id;
break;
}
}
if (!creatureId) throw 'This property has no creature ancestors';
return creatureId;
}

View File

@@ -0,0 +1,64 @@
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import CreatureProperties from '/imports/api/creature/CreatureProperties.js';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
import { organizeDoc } from '/imports/api/parenting/organizeMethods.js';
import getClosestPropertyAncestorCreature from '/imports/api/creature/creatureProperties/getClosestPropertyAncestorCreature.js';
import INVENTORY_TAGS from '/imports/constants/INVENTORY_TAGS.js';
function getParentRefByTag(creatureId, tag){
let prop = CreatureProperties.findOne({
'ancestors.id': creatureId,
removed: {$ne: true},
inactive: {$ne: true},
tags: tag,
}, {
sort: {order: 1},
});
if (prop){
return {id: prop._id, collection: 'creatureProperties'};
} else {
return {id: creatureId, collection: 'creatures'};
}
}
// Equipping or unequipping an item will also change its parent
const equipItem = new ValidatedMethod({
name: 'creatureProperties.equip',
validate({_id, equipped}){
if (!_id) throw new Meteor.Error('No _id', '_id is required');
if (equipped !== true && equipped !== false) {
throw new Meteor.Error('No equipped', 'equipped is required to be true or false');
}
},
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
run({_id, equipped}) {
let item = CreatureProperties.findOne(_id);
if (item.type !== 'item') throw new Meteor.Error('wrong type',
'Equip and unequip can only be performed on items');
let creature = getClosestPropertyAncestorCreature(item);
assertEditPermission(creature, this.userId);
CreatureProperties.update(_id, {
$set: {equipped},
}, {
selector: {type: 'item'},
});
let tag = equipped ? INVENTORY_TAGS.equipment : INVENTORY_TAGS.carried;
let parentRef = getParentRefByTag(creature._id, tag);
// organizeDoc handles recompuation
organizeDoc.call({
docRef: {
id: _id,
collection: 'creatureProperties',
},
parentRef,
order: Number.MAX_SAFE_INTEGER,
});
},
});
export { equipItem, getParentRefByTag }

View File

@@ -15,28 +15,43 @@ export default function recomputeInactiveProperties(ancestorId){
fields: {_id: 1},
}).map(prop => prop._id);
// Set all the properties inactive that aren't already inactive but should be
// Deactivate relevant properties
// Inactive properties
CreatureProperties.update({
'ancestors.id': ancestorId,
$or: [{
'_id': {$in: disabledIds}
}, {
'ancestors.id': {$in: disabledIds}
}],
inactive: {$ne: true},
'_id': {$in: disabledIds},
$or: [{inactive: {$ne: true}}, {deactivatedByAncestor: true}],
}, {
$set: {inactive: true},
$unset: {deactivatedByAncestor: 1},
}, {
multi: true,
selector: {type: 'any'},
});
// Decendants of inactive properties
CreatureProperties.update({
'ancestors.id': {$eq: ancestorId, $in: disabledIds},
$or: [{inactive: {$ne: true}}, {deactivatedByAncestor: {$ne: true}}],
}, {
$set: {
inactive: true,
deactivatedByAncestor: true,
},
}, {
multi: true,
selector: {type: 'any'},
});
// Remove inactive from all the properties that are inactive but shouldn't be
CreatureProperties.update({
'ancestors.id': {$eq: ancestorId, $nin: disabledIds},
'_id': {$nin: disabledIds},
inactive: true,
$or: [{inactive: true}, {deactivatedByAncestor: true}],
}, {
$unset: {inactive: 1},
$unset: {
inactive: 1,
deactivatedByAncestor: 1,
},
}, {
multi: true,
selector: {type: 'any'},