diff --git a/rpg-docs/Model/Inventory/Items.js b/rpg-docs/Model/Inventory/Items.js index b3ecc740..419677e4 100644 --- a/rpg-docs/Model/Inventory/Items.js +++ b/rpg-docs/Model/Inventory/Items.js @@ -19,6 +19,159 @@ Schemas.Item = new SimpleSchema({ Items.attachSchema(Schemas.Item); +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"); + //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"); + } + 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"); + } + } else { + var parentCollectionObject = global[parent.collection]; + var parentObject = null; + if (parentCollectionObject) + parentObject = parentCollectionObject.findOne( + parent.id, {fields: {_id: 1, charId: 1}} + ); + if (!parentObject) throw new Meteor.Error( + "Invalid parent", + "The destination parent " + parent.id + + " does not exist in the collection " + parent.collection + ); + if (parentObject.charId){ + permission = Meteor.call("canWriteCharacter", parentObject.charId); + if (!permission){ + throw new Meteor.Error("Access denied", + "Not permitted to move items to this character"); + } + } + } +}; + +var moveItem = function(itemId, enable, parentCollection, parentId) { + var item = Items.findOne(itemId); + if (!item) return; + parentCollection = parentCollection || item.parent.collection; + parentId = parentId || item.parent.id; + + if (Meteor.isServer) { + checkMovePermission(itemId, {collection: parentCollection, id: parentId}); + } + + //update the item provided the update will actually change something + if ( + item.parent.collection !== parentCollection || + item.parent.id !== parentId || + item.enabled !== enable + ){ + var setter = {$set: { + "parent.collection": parentCollection, + "parent.id": parentId, + enabled: enable, + }}; + if (parentCollection === "Characters"){ + setter.$set.charId = parentId; + } + Items.update(itemId, setter); + } +}; + +Meteor.methods({ + moveItemToParent: function(itemId, parent) { + check(itemId, String); + check(parent, {collection: String, id: String}); + moveItem(itemId, false, parent.collection, parent.id); + }, + moveItemToCharacter: function(itemId, charId) { + check(itemId, String); + check(charId, String); + moveItem(itemId, false, "Characters", charId); + }, + moveItemToContainer: function(itemId, containerId) { + check(itemId, String); + check(containerId, String); + moveItem(itemId, false, "Containers", containerId); + }, + equipItem: function(itemId, charId){ + check(itemId, String); + check(charId, String); + moveItem(itemId, true, "Characters", charId); + }, + unequipItem: function(itemId, charId){ + check(itemId, String); + check(charId, String); + moveItem(itemId, false, "Characters", charId); + }, + splitItemToParent: function(itemId, moveQuantity, parent){ + check(itemId, String); + check(moveQuantity, Number); + check(parent, {id: String, collection: String}); + + //get the item + var item = Items.findOne(itemId); + if (!item) return; + + //don't bother moving nothing + if (moveQuantity <= 0 || item.quantity <= 0){ + return; + } + //ensure we are only moving up to the current stack size + if (item.quantity < moveQuantity){ + moveQuantity = this.quantity; + } + + if (Meteor.isServer) { + checkMovePermission(itemId, parent); + } + + //create a new item stack + var newStack = _.omit(EJSON.clone(item), "_id"); + newStack.parent = parent; + if (newStack.parent.collection === "Characters"){ + newStack.charId = newStack.parent.id; + } + newStack.quantity = moveQuantity; + + //find out if we have an exact replica in the destination + var query = _.omit(newStack, ["parent", "quantity"]); + query["parent.collection"] = newStack.parent.collection; + query["parent.id"] = newStack.parent.id; + var existingStack = Items.findOne(query); + if (existingStack){ + //increase the existing stack's size + Items.update( + existingStack._id, + {$inc: {quantity: moveQuantity}} + ); + } else { + //insert the new stack + Items.insert(newStack, function(err, id){ + if (err) throw err; + //copy the children also + Meteor.call("cloneChildren", item._id, {collection: "Items", id: id}); + }); + } + + //reduce the old stack's size + var oldQuantity = item.quantity - moveQuantity; + if (oldQuantity === 0){ + Items.remove(itemId); + } else { + Items.update(itemId, {$set: {quantity: oldQuantity}}); + } + }, +}); + Items.helpers({ totalValue: function(){ return this.value * this.quantity; @@ -33,103 +186,6 @@ Items.helpers({ return this.name; } }, - equip: function(characterId){ - var charId = characterId || this.charId; - if (!charId || !Characters.findOne(charId)) throw "Invalid character"; - if (this.parent.collection === "Characters" && - this.parent.id === charId && - this.enabled) { - return; - } - Items.update( - this._id, - {$set: { - "parent.collection": "Characters", - "parent.id": charId, - enabled: true, - }} - ); - }, - unequip: function(){ - if (!this.enabled) return; - Items.update(this._id, {$set: {enabled: false}}); - }, - moveToContainer: function(containerId){ - if (!containerId || !Containers.findOne(containerId)){ - throw "Invalid container"; - } - if (this.parent.collection === "Containers" && - this.parent.id === containerId && - !this.enabled) return; - Items.update( - this._id, - {$set: { - "parent.collection": "Containers", - "parent.id": containerId, - enabled: false, - }} - ); - }, - moveToCharacter: function(characterId){ - if (!characterId || !Characters.findOne(characterId)) { - throw "Invalid character"; - } - if (this.parent.collection === "Characters" && - this.parent.id === characterId && - !this.enabled) return; - Items.update( - this._id, - {$set: { - "parent.collection": "Characters", - "parent.id": characterId, - charId: characterId, - enabled: false, - }} - ); - }, - splitToParent: function(parent, moveQuantity){ - check(parent, {id: String, collection: String}); - check(moveQuantity, Number); - var parentCollection = Meteor.isClient ? - window[parent.collection] : global[parent.collection]; - if (!parent.id || !parentCollection.findOne(parent.id)){ - throw "Invalid parent"; - } - var oldStack = this; - //we can only move as much as we have, leaving 0 behind at worst - if (oldStack.quantity < moveQuantity) moveQuantity = oldStack.quantity; - var oldQuantity = oldStack.quantity - moveQuantity; - - var newStack = _.pick(oldStack, Schemas.Item.objectKeys()); - newStack.parent = parent; - newStack.quantity = moveQuantity; - - var existingStack = Items.findOne(_.omit(newStack, "quantity")); - var updateStackSize = function(){ - if (oldQuantity > 0){ - Items.update(oldStack._id, {$set: {quantity: oldQuantity}}); - } else { - Items.remove(oldStack._id); - } - }; - if (existingStack){ - Items.update( - existingStack._id, - {$inc: {quantity: moveQuantity}}, - {}, - function(){ - updateStackSize(); - } - ); - } else { - Items.insert(newStack, function(err, id){ - if (err) throw err; - updateStackSize(); - //copy the children also - Meteor.call("cloneChildren", oldStack._id, {collection: "Items", id: id}); - }); - } - }, }); Items.before.update(function(userId, doc, fieldNames, modifier, options){ diff --git a/rpg-docs/client/views/character/inventory/inventory.js b/rpg-docs/client/views/character/inventory/inventory.js index 19604963..ffecf135 100644 --- a/rpg-docs/client/views/character/inventory/inventory.js +++ b/rpg-docs/client/views/character/inventory/inventory.js @@ -174,71 +174,93 @@ Template.inventoryItem.helpers({ Template.layout.events({ "dragstart .inventoryItem": function(event, instance){ + event.originalEvent.dataTransfer.setData("dicecloud-id/items", this._id); Session.set("inventory.dragItemId", this._id); - Session.set("inventory.dragItemOriginalContainer", this.container); - Session.set("inventory.dragItemOriginalCharacter", this.charId); + }, + "dragover .itemContainer, dragenter .itemContainer": + function(event, instance){ + if (_.contains(event.originalEvent.dataTransfer.types, "dicecloud-id/items")){ + event.preventDefault(); + } + }, + "dragover .equipmentContainer, dragenter .equipmentContainer": + function(event, instance){ + if (_.contains(event.originalEvent.dataTransfer.types, "dicecloud-id/items")){ + event.preventDefault(); + } + }, + "dragover .carriedContainer, dragenter .carriedContainer": + function(event, instance){ + if (_.contains(event.originalEvent.dataTransfer.types, "dicecloud-id/items")){ + event.preventDefault(); + } + }, + "dragover .characterRepresentative, dragenter .characterRepresentative": + function(event, instance){ + if (_.contains(event.originalEvent.dataTransfer.types, "dicecloud-id/items")){ + event.preventDefault(); + } }, "dragend .inventoryItem": function(event, instance){ - resetInvetorySession(); //this is a valid drop zone + Session.set("inventory.dragItemId", null); }, - "dragover .itemContainer": function(event, instance){ - event.preventDefault(); - }, - "dragover .equipmentContainer": function(event, instance){ - event.preventDefault(); - }, - "dragover .carriedContainer": function(event, instance){ - event.preventDefault(); - }, - "drop .itemContainer": function(event, instacne){ - var item = Items.findOne(Session.get("inventory.dragItemId")); + "drop .itemContainer": function(event, instance){ + var itemId = event.originalEvent.dataTransfer.getData("dicecloud-id/items"); if (event.ctrlKey){ //split the stack to the container GlobalUI.showDialog({ template: "splitStackDialog", data: { - id: item._id, + id: itemId, parentCollection: "Containers", parentId: this._id, }, }); } else { //move item to the container - item.moveToContainer(this._id); + Meteor.call("moveItemToContainer", itemId, this._id); } - resetInvetorySession(); + Session.set("inventory.dragItemId", null); }, "drop .equipmentContainer": function(event, instance){ - var charId = Session.get("inventory.dragItemOriginalCharacter"); - var item = Items.findOne(Session.get("inventory.dragItemId")); - item.equip(charId); - resetInvetorySession(); + var itemId = event.originalEvent.dataTransfer.getData("dicecloud-id/items"); + Meteor.call("equipItem", itemId, this._id); + Session.set("inventory.dragItemId", null); }, "drop .carriedContainer": function(event, instance){ - var charId = Session.get("inventory.dragItemOriginalCharacter"); - var item = Items.findOne(Session.get("inventory.dragItemId")); + var itemId = event.originalEvent.dataTransfer.getData("dicecloud-id/items"); if (event.ctrlKey){ //split the stack to the container GlobalUI.showDialog({ template: "splitStackDialog", data: { - id: item._id, + id: itemId, parentCollection: "Characters", parentId: this._id, }, }); } else { //move item to the character - item.moveToCharacter(this._id); + Meteor.call("moveItemToCharacter", itemId, this._id); } - resetInvetorySession(); + Session.set("inventory.dragItemId", null); + }, + "drop .characterRepresentative": function(event, instance) { + var itemId = event.originalEvent.dataTransfer.getData("dicecloud-id/items"); + if (event.ctrlKey){ + //split the stack to the container + GlobalUI.showDialog({ + template: "splitStackDialog", + data: { + id: itemId, + parentCollection: "Characters", + parentId: this._id, + }, + }); + } else { + //move item to the character + Meteor.call("moveItemToCharacter", itemId, this._id); + } + Session.set("inventory.dragItemId", null); }, }); - -var resetInvetorySession = function(){ - _.defer(function(){ - Session.set("inventory.dragItemId", null); - Session.set("inventory.dragItemOriginalContainer", null); - Session.set("inventory.dragItemOriginalCharacter", null); - }); -}; diff --git a/rpg-docs/client/views/character/inventory/itemDialog/itemDialog.js b/rpg-docs/client/views/character/inventory/itemDialog/itemDialog.js index 26136daa..705ce351 100644 --- a/rpg-docs/client/views/character/inventory/itemDialog/itemDialog.js +++ b/rpg-docs/client/views/character/inventory/itemDialog/itemDialog.js @@ -81,13 +81,10 @@ Template.itemEdit.events({ }, "change #equippedInput": function(event){ var equipped = Template.instance().find("#equippedInput").checked; - var item = Items.findOne(this._id); - if (item){ - if (equipped){ - item.equip(); - } else { - item.unequip(); - } + if (equipped){ + Meteor.call("equipItem", this._id, this.charId); + } else { + Meteor.call("unequipItem", this._id, this.charId); } }, "change #attunementCheckbox": function(event){ @@ -107,7 +104,6 @@ Template.containerDropdown.events({ var detail = event.originalEvent.detail; if (!detail.isSelected) return; var containerId = detail.item.getAttribute("name"); - var item = Items.findOne(Template.currentData()._id); - item.moveToContainer(containerId); + Meteor.call("moveItemToContainer", Template.currentData()._id, containerId); } }); diff --git a/rpg-docs/client/views/character/inventory/splitStackDialog/splitStackDialog.js b/rpg-docs/client/views/character/inventory/splitStackDialog/splitStackDialog.js index e384e939..dabe398a 100644 --- a/rpg-docs/client/views/character/inventory/splitStackDialog/splitStackDialog.js +++ b/rpg-docs/client/views/character/inventory/splitStackDialog/splitStackDialog.js @@ -7,13 +7,12 @@ Template.splitStackDialog.helpers({ Template.splitStackDialog.events({ "tap #moveButton": function(event, instance){ - var item = Items.findOne(this.id); - if (item){ - item.splitToParent( - {collection: this.parentCollection , id: this.parentId}, - +instance.find("#quantityInput").value - ); - } + Meteor.call( + "splitItemToParent", + this.id, + +instance.find("#quantityInput").value, + {collection: this.parentCollection , id: this.parentId} + ); }, "tap #oneButton":function(event, instance){ instance.find("#quantityInput").value = 1; diff --git a/rpg-docs/client/views/characterList/characterSideList.html b/rpg-docs/client/views/characterList/characterSideList.html index b4b098af..ede5791e 100644 --- a/rpg-docs/client/views/characterList/characterSideList.html +++ b/rpg-docs/client/views/characterList/characterSideList.html @@ -3,7 +3,7 @@ {{#if characters.count}}