Finished implementing basic item library
This commit is contained in:
@@ -10,9 +10,6 @@ Schemas.Item = new SimpleSchema({
|
||||
value: {type: Number, min: 0, defaultValue: 0, decimal: true},
|
||||
enabled: {type: Boolean, defaultValue: false},
|
||||
requiresAttunement: {type: Boolean, defaultValue: false},
|
||||
category: {type: String, optional: true, allowedValues: [
|
||||
"adventuringGear", "armor", "weapons", "tools",
|
||||
],},
|
||||
"settings.showIncrement": {type: Boolean, defaultValue: false},
|
||||
color: {
|
||||
type: String,
|
||||
@@ -27,18 +24,18 @@ var checkMovePermission = function(itemId, parent) {
|
||||
var item = Items.findOne(itemId);
|
||||
if (!item)
|
||||
throw new Meteor.Error("No such item",
|
||||
"An item could not be found to move");
|
||||
"An item could not be found to move");
|
||||
//handle permissions
|
||||
var permission = Meteor.call("canWriteCharacter", item.charId);
|
||||
if (!permission){
|
||||
throw new Meteor.Error("Access denied",
|
||||
"Not permitted to move items from this character");
|
||||
"Not permitted to move items from this character");
|
||||
}
|
||||
if (parent.collection === "Characters"){
|
||||
permission = Meteor.call("canWriteCharacter", parent.id);
|
||||
if (!permission){
|
||||
throw new Meteor.Error("Access denied",
|
||||
"Not permitted to move items to this character");
|
||||
"Not permitted to move items to this character");
|
||||
}
|
||||
} else {
|
||||
var parentCollectionObject = global[parent.collection];
|
||||
@@ -56,7 +53,7 @@ var checkMovePermission = function(itemId, parent) {
|
||||
permission = Meteor.call("canWriteCharacter", parentObject.charId);
|
||||
if (!permission){
|
||||
throw new Meteor.Error("Access denied",
|
||||
"Not permitted to move items to this character");
|
||||
"Not permitted to move items to this character");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
47
rpg-docs/Model/Library/Library.js
Normal file
47
rpg-docs/Model/Library/Library.js
Normal file
@@ -0,0 +1,47 @@
|
||||
Libraries = new Mongo.Collection("library");
|
||||
|
||||
Schemas.Library = new SimpleSchema({
|
||||
name: {type: String},
|
||||
owner: {type: String, regEx: SimpleSchema.RegEx.Id},
|
||||
readers: {type: [String], regEx: SimpleSchema.RegEx.Id, defaultValue: []},
|
||||
writers: {type: [String], regEx: SimpleSchema.RegEx.Id, defaultValue: []},
|
||||
public: {type: Boolean, defaultValue: false},
|
||||
});
|
||||
|
||||
Libraries.attachSchema(Schemas.Library);
|
||||
|
||||
Libraries.allow({
|
||||
insert(userId, doc) {
|
||||
return userId && doc.owner === userId;
|
||||
},
|
||||
update(userId, doc, fields, modifier) {
|
||||
return canEdit(userId, doc);
|
||||
},
|
||||
remove(userId, doc) {
|
||||
return canEdit(userId, doc);
|
||||
},
|
||||
fetch: ["owner", "writers"],
|
||||
});
|
||||
|
||||
Libraries.deny({
|
||||
// For now, only admins can manage libraries
|
||||
insert(userId, doc){
|
||||
var user = Meteor.users.findOne(userId);
|
||||
return !user || !_.contains(user.roles, "admin");
|
||||
},
|
||||
update(userId, doc, fields, modifier) {
|
||||
// Can't change owners
|
||||
return _.contains(fields, "owner")
|
||||
},
|
||||
fetch: [],
|
||||
});
|
||||
|
||||
const canEdit = function(userId, library){
|
||||
if (!userId || !library) return;
|
||||
return library.owner === userId || _.contains(library.writers, userId);
|
||||
};
|
||||
|
||||
Libraries.canEdit = function(userId, libraryId){
|
||||
const library = Libraries.findOne(libraryId);
|
||||
return canEdit(userId, library);
|
||||
};
|
||||
43
rpg-docs/Model/Library/LibraryAttacks.js
Normal file
43
rpg-docs/Model/Library/LibraryAttacks.js
Normal file
@@ -0,0 +1,43 @@
|
||||
Schemas.LibraryAttacks = new SimpleSchema({
|
||||
name: {
|
||||
type: String,
|
||||
defaultValue: "New Attack",
|
||||
trim: false,
|
||||
},
|
||||
details: {
|
||||
type: String,
|
||||
optional: true,
|
||||
trim: false,
|
||||
},
|
||||
attackBonus: {
|
||||
type: String,
|
||||
defaultValue: "strengthMod + proficiencyBonus",
|
||||
optional: true,
|
||||
trim: false,
|
||||
},
|
||||
damage: {
|
||||
type: String,
|
||||
defaultValue: "1d8 + {strengthMod}",
|
||||
optional: true,
|
||||
trim: false,
|
||||
},
|
||||
damageType: {
|
||||
type: String,
|
||||
allowedValues: [
|
||||
"bludgeoning",
|
||||
"piercing",
|
||||
"slashing",
|
||||
"acid",
|
||||
"cold",
|
||||
"fire",
|
||||
"force",
|
||||
"lightning",
|
||||
"necrotic",
|
||||
"poison",
|
||||
"psychic",
|
||||
"radiant",
|
||||
"thunder",
|
||||
],
|
||||
defaultValue: "slashing",
|
||||
},
|
||||
});
|
||||
40
rpg-docs/Model/Library/LibraryEffects.js
Normal file
40
rpg-docs/Model/Library/LibraryEffects.js
Normal file
@@ -0,0 +1,40 @@
|
||||
Schemas.LibraryEffects = new SimpleSchema({
|
||||
name: {
|
||||
type: String,
|
||||
optional: true, //TODO make necessary if there is no owner
|
||||
trim: false,
|
||||
},
|
||||
operation: {
|
||||
type: String,
|
||||
defaultValue: "add",
|
||||
allowedValues: [
|
||||
"base",
|
||||
"proficiency",
|
||||
"add",
|
||||
"mul",
|
||||
"min",
|
||||
"max",
|
||||
"advantage",
|
||||
"disadvantage",
|
||||
"passiveAdd",
|
||||
"fail",
|
||||
"conditional",
|
||||
],
|
||||
},
|
||||
// Effects either have a value OR a calculation
|
||||
value: {
|
||||
type: Number,
|
||||
decimal: true,
|
||||
optional: true,
|
||||
},
|
||||
calculation: {
|
||||
type: String,
|
||||
optional: true,
|
||||
trim: false,
|
||||
},
|
||||
//which stat the effect is applied to
|
||||
stat: {
|
||||
type: String,
|
||||
optional: true,
|
||||
},
|
||||
});
|
||||
43
rpg-docs/Model/Library/LibraryItems.js
Normal file
43
rpg-docs/Model/Library/LibraryItems.js
Normal file
@@ -0,0 +1,43 @@
|
||||
LibraryItems = new Mongo.Collection("libraryItems");
|
||||
|
||||
Schemas.LibraryItems = new SimpleSchema({
|
||||
name: {type: String, defaultValue: "New Item", trim: false},
|
||||
plural: {type: String, optional: true, trim: false},
|
||||
description:{type: String, optional: true, trim: false},
|
||||
quantity: {type: Number, min: 0, defaultValue: 1},
|
||||
weight: {type: Number, min: 0, defaultValue: 0, decimal: true},
|
||||
value: {type: Number, min: 0, defaultValue: 0, decimal: true},
|
||||
requiresAttunement: {type: Boolean, defaultValue: false},
|
||||
|
||||
library: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
|
||||
|
||||
"settings.category": {
|
||||
type: String,
|
||||
optional: true,
|
||||
allowedValues: [
|
||||
"adventuringGear", "armor", "weapons", "tools",
|
||||
],
|
||||
},
|
||||
"settings.showIncrement": {
|
||||
type: Boolean,
|
||||
defaultValue: false,
|
||||
},
|
||||
|
||||
effects: {type: [Schemas.LibraryEffects], defaultValue: []},
|
||||
attacks: {type: [Schemas.LibraryAttacks], defaultValue: []},
|
||||
});
|
||||
|
||||
LibraryItems.attachSchema(Schemas.LibraryItems);
|
||||
|
||||
LibraryItems.allow({
|
||||
insert(userId, doc) {
|
||||
return Libraries.canEdit(userId, doc.library);
|
||||
},
|
||||
update(userId, doc, fields, modifier) {
|
||||
return Libraries.canEdit(userId, doc.library);
|
||||
},
|
||||
remove(userId, doc) {
|
||||
return Libraries.canEdit(userId, doc.library);
|
||||
},
|
||||
fetch: ["library"],
|
||||
});
|
||||
@@ -138,6 +138,13 @@
|
||||
</paper-fab>
|
||||
<paper-tooltip position="left"> New container </paper-tooltip>
|
||||
</div>
|
||||
<div>
|
||||
<paper-fab icon="av:library-books"
|
||||
class="libraryItem"
|
||||
mini>
|
||||
</paper-fab>
|
||||
<paper-tooltip position="left"> Library item </paper-tooltip>
|
||||
</div>
|
||||
<div>
|
||||
<paper-fab icon="note-add"
|
||||
class="addItem"
|
||||
|
||||
@@ -139,6 +139,43 @@ Template.inventory.events({
|
||||
returnElement: () => $(`[data-id='${itemId}']`).get(0),
|
||||
});
|
||||
},
|
||||
"click .libraryItem": function(event, instance){
|
||||
var charId = this._id;
|
||||
var itemId = Items.insert({
|
||||
charId: charId,
|
||||
parent:{
|
||||
id: charId,
|
||||
collection: "Characters",
|
||||
},
|
||||
});
|
||||
pushDialogStack({
|
||||
template: "itemLibraryDialog",
|
||||
element: event.currentTarget,
|
||||
callback: (result) => {
|
||||
if (!result) {
|
||||
Items.remove(itemId);
|
||||
return;
|
||||
}
|
||||
// Make the library item into a regular item
|
||||
let item = _.omit(result, "library", "attacks", "effects");
|
||||
delete item.settings.category;
|
||||
// Update the item to match library item
|
||||
Items.update(itemId, {$set: item});
|
||||
// Copy over attacks and effects
|
||||
_.each(result.attacks, (attack) => {
|
||||
attack.charId = charId;
|
||||
attack.parent = {id: itemId, collection: "Items"};
|
||||
Attacks.insert(attack);
|
||||
});
|
||||
_.each(result.effects, (effect) => {
|
||||
effect.charId = charId;
|
||||
effect.parent = {id: itemId, collection: "Items"};
|
||||
Effects.insert(effect);
|
||||
});
|
||||
},
|
||||
returnElement: () => $(`[data-id='${itemId}']`).get(0),
|
||||
})
|
||||
},
|
||||
"click .addContainer": function(event, instance){
|
||||
var containerId = Containers.insert({
|
||||
name: "New Container",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template name="itemDialog">
|
||||
{{#with item}}
|
||||
{{#baseDialog title=itemHeading class=colorClass startEditing=../startEditing showLibrary=true}}
|
||||
{{#baseDialog title=itemHeading class=colorClass startEditing=../startEditing}}
|
||||
{{> itemDetails}}
|
||||
{{else}}
|
||||
{{> itemEdit}}
|
||||
|
||||
@@ -33,21 +33,6 @@ Template.itemDialog.events({
|
||||
"click #doneEditingButton": function(event, instance){
|
||||
instance.editing.set(false);
|
||||
},
|
||||
"click #libraryButton": function(event, instance){
|
||||
pushDialogStack({
|
||||
template: "itemLibraryDialog",
|
||||
element: event.currentTarget,
|
||||
callback: function(result){
|
||||
if (!result) return;
|
||||
delete result.parent;
|
||||
delete result._id;
|
||||
delete result.charId;
|
||||
result.quantity = 1;
|
||||
Items.update(instance.data.itemId, {$set: result});
|
||||
//TODO Replace the effects with the effects of the library item
|
||||
},
|
||||
})
|
||||
},
|
||||
"color-change": function(event, instance){
|
||||
Items.update(instance.data.itemId, {$set: {color: event.color}});
|
||||
},
|
||||
|
||||
@@ -10,26 +10,32 @@
|
||||
</paper-input>
|
||||
</app-toolbar>
|
||||
<div class="flex scroll-y">
|
||||
<div class="items" style="padding:8px">
|
||||
{{#if searchTerm}}
|
||||
{{#if searchItems.count}}
|
||||
{{#each item in searchItems}}
|
||||
{{>libraryItem item=item selected=(isSelected item)}}
|
||||
{{/each}}
|
||||
{{#if ready}}
|
||||
<div class="items" style="padding:8px">
|
||||
{{#if searchTerm}}
|
||||
{{#if searchItems.count}}
|
||||
{{#each item in searchItems}}
|
||||
{{>libraryItem item=item selected=(isSelected item)}}
|
||||
{{/each}}
|
||||
{{else}}
|
||||
No items match "{{searchTerm}}"
|
||||
{{/if}}
|
||||
{{else}}
|
||||
No items match "{{searchTerm}}"
|
||||
{{/if}}
|
||||
{{else}}
|
||||
{{#each category in categories}}
|
||||
<div class="paper-font-subhead">
|
||||
{{category.name}}
|
||||
</div>
|
||||
{{#each item in itemsInCategory category.value}}
|
||||
{{>libraryItem item=item selected=(isSelected item)}}
|
||||
{{#each category in categories}}
|
||||
<div class="paper-font-subhead">
|
||||
{{category.name}}
|
||||
</div>
|
||||
{{#each item in (itemsInCategory category.key)}}
|
||||
{{>libraryItem item=item selected=(isSelected item)}}
|
||||
{{/each}}
|
||||
{{/each}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="fit layout vertical center center-justified">
|
||||
<paper-spinner active></paper-spinner>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="layout horizontal end-justified">
|
||||
<paper-button class="cancelButton">Cancel</paper-button>
|
||||
@@ -44,11 +50,5 @@
|
||||
<div class="itemName flex">
|
||||
{{item.name}}
|
||||
</div>
|
||||
<div style="margin: 0 8px">
|
||||
{{item.weight}}
|
||||
</div>
|
||||
<div style="margin: 0 8px">
|
||||
{{item.value}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,24 +1,30 @@
|
||||
const librarySubs = new SubsManager();
|
||||
|
||||
Template.itemLibraryDialog.onCreated(function(){
|
||||
this.selectedItem = new ReactiveVar();
|
||||
this.searchTerm = new ReactiveVar();
|
||||
this.ready = new ReactiveVar();
|
||||
this.autorun(() => {
|
||||
var handle = librarySubs.subscribe("standardLibraries");
|
||||
this.ready.set(handle.ready());
|
||||
});
|
||||
});
|
||||
|
||||
Template.itemLibraryDialog.helpers({
|
||||
ready(){
|
||||
return Template.instance().ready.get();
|
||||
},
|
||||
categories(){
|
||||
return [
|
||||
{name: "Weapons", key: "weapons"},
|
||||
{name: "Armor", key: "armor"},
|
||||
{name: "Adventuring Equipment", key: "adventuringEquipment"},
|
||||
{name: "Tools", key: "tools"},
|
||||
{name: "Adventuring Gear", key: "adventuringGear"},
|
||||
];
|
||||
},
|
||||
itemsInCategory(category){
|
||||
//TODO return a cursor of all library items in the category
|
||||
// As a dummy function returns a random 2 items
|
||||
let count = Items.find({}).count();
|
||||
return Items.find({}, {
|
||||
limit: 5,
|
||||
skip: Math.floor(Math.random() * (count - 5)),
|
||||
itemsInCategory(categoryKey){
|
||||
return LibraryItems.find({
|
||||
library: "SRDLibraryGA3XWsd",
|
||||
"settings.category": categoryKey,
|
||||
});
|
||||
},
|
||||
isSelected(item){
|
||||
@@ -30,10 +36,12 @@ Template.itemLibraryDialog.helpers({
|
||||
},
|
||||
searchItems(){
|
||||
const searchTerm = Template.instance().searchTerm.get();
|
||||
//TODO return something relevant to the search terms
|
||||
return Items.find({name: {
|
||||
$regex: new RegExp(".*" + searchTerm + ".*", "gi")
|
||||
}});
|
||||
return LibraryItems.find({
|
||||
library: "SRDLibraryGA3XWsd",
|
||||
name: {
|
||||
$regex: new RegExp(".*" + searchTerm + ".*", "gi")
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
<iron-icon icon="bug-report" item-icon></iron-icon>
|
||||
Send Feedback
|
||||
</paper-icon-item>
|
||||
<a href="changeLog" tabindex="-1">
|
||||
<a href="/changeLog" tabindex="-1">
|
||||
<paper-icon-item id="changeLog">
|
||||
<iron-icon icon="list" item-icon></iron-icon>
|
||||
Change Log
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<div class="flex" style="position: relative;">
|
||||
<iron-pages id="tabPages" class="fit" selected={{selectedTab}}>
|
||||
<div name="items" class="tab-page fit">{{> itemLibrary}}</div>
|
||||
<div name="spells" class="tab-page fit">{{> spellLibrary}}</div>
|
||||
<div name="spells" class="tab-page fit">{{! {{> spellLibrary}} }}</div>
|
||||
</iron-pages>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -16,12 +16,6 @@
|
||||
{{#unless hideColor}}
|
||||
{{> colorDropdown}}
|
||||
{{/unless}}
|
||||
{{#if showLibrary}}
|
||||
<paper-icon-button id="libraryButton"
|
||||
tabindex="0"
|
||||
icon="av:library-books">
|
||||
</paper-icon-button>
|
||||
{{/if}}
|
||||
<paper-icon-button id="doneEditingButton"
|
||||
icon="done">
|
||||
</paper-icon-button>
|
||||
|
||||
@@ -28,7 +28,7 @@ function CacheObject(func, address, args, cache, context){
|
||||
return;
|
||||
}
|
||||
//if we haven't run this before this flush, reset the counter after the flush
|
||||
if(self.numRun === 0){
|
||||
if (self.numRun === 0){
|
||||
Tracker.afterFlush(function(){
|
||||
self.numRun = 0;
|
||||
});
|
||||
@@ -38,10 +38,10 @@ function CacheObject(func, address, args, cache, context){
|
||||
//even if we don't use its value, we need to track its dependencies
|
||||
var newValue = func.apply(context, args);
|
||||
//prevent dependency loops, the memoized function shouldn't re-run
|
||||
//more than once per flush
|
||||
if (self.numRun > 1){
|
||||
//more than 10 times per flush
|
||||
if (self.numRun > 10){
|
||||
newValue = NaN;
|
||||
if(_.isNaN(self.currentValue)) return;
|
||||
if (_.isNaN(self.currentValue)) return;
|
||||
}
|
||||
//if the value changed, store the new value
|
||||
if (self.currentValue !== newValue){
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"dependencies": {
|
||||
"@polymer/polymer": "^1.2.5-npm-test.2",
|
||||
"babel-runtime": "^6.23.0",
|
||||
"bcrypt": "^1.0.1",
|
||||
"bcrypt": "^1.0.2",
|
||||
"bower": "^1.7.9"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
Meteor.publish("standardLibraries", function(){
|
||||
return Items.find({charId: {$in: [
|
||||
"SRDLibrary",
|
||||
]}});
|
||||
const standardLibraryIds = [
|
||||
"SRDLibraryGA3XWsd",
|
||||
];
|
||||
return [
|
||||
LibraryItems.find({library: {$in: standardLibraryIds}}),
|
||||
Libraries.find({_id: {$in: standardLibraryIds}}),
|
||||
];
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user