Implemented Feature editing UI
This commit is contained in:
2
.codio
2
.codio
@@ -3,7 +3,7 @@
|
||||
|
||||
// Run button configuration
|
||||
"commands": {
|
||||
"Run Meteor": "cd rpg-docs \n METEOR_OFFLINE_CATALOG=1 meteor run"
|
||||
"Run Meteor": "cd rpg-docs \n meteor run"
|
||||
},
|
||||
|
||||
// Preview button configuration
|
||||
|
||||
@@ -14,5 +14,6 @@ reactive-var
|
||||
cw4gn3r:jquery-event-drag
|
||||
underscore
|
||||
aldeed:collection2
|
||||
aldeed:autoform
|
||||
differential:vulcanize
|
||||
aldeed:autoform
|
||||
conielo:autoform-polymer-paper
|
||||
|
||||
@@ -3,7 +3,7 @@ accounts-password@1.0.5
|
||||
accounts-ui@1.1.4
|
||||
accounts-ui-unstyled@1.1.5
|
||||
aldeed:autoform@4.2.2
|
||||
aldeed:collection2@2.3.0
|
||||
aldeed:collection2@2.3.1
|
||||
aldeed:simple-schema@1.1.0
|
||||
application-configuration@1.0.4
|
||||
autoupdate@1.1.4
|
||||
@@ -14,6 +14,7 @@ blaze-tools@1.0.2
|
||||
boilerplate-generator@1.0.2
|
||||
callback-hook@1.0.2
|
||||
check@1.0.3
|
||||
conielo:autoform-polymer-paper@0.1.1
|
||||
cw4gn3r:jquery-event-drag@2.2.0
|
||||
dburles:collection-helpers@1.0.2
|
||||
ddp@1.0.13
|
||||
|
||||
@@ -213,8 +213,6 @@ Schemas.Character = new SimpleSchema({
|
||||
},
|
||||
|
||||
//mechanics
|
||||
features: { type: [String], defaultValue: [], regEx: SimpleSchema.RegEx.Id,},
|
||||
customFeatures: { type: [Schemas.Feature], defaultValue: []},
|
||||
actions: { type: [Schemas.Action], defaultValue: []},
|
||||
deathSave: { type: Schemas.DeathSave },
|
||||
time: { type: Number, min: 0, decimal: true, defaultValue: 0},
|
||||
|
||||
@@ -1,28 +1,9 @@
|
||||
//Features are features that can be selected but not edited
|
||||
//they are the things that come in the player's handbook and
|
||||
//facilitate the quick building of characters
|
||||
//They are the primary means of collecting cease and desist letters :(
|
||||
//
|
||||
//Should only be edited by admins
|
||||
//
|
||||
//TODO add a Meteor Method that lets users add a feature to their character
|
||||
//and pushes the effects and actions accordingly
|
||||
//
|
||||
//TODO add a Method that updates every character with a given feature if that feature should change
|
||||
|
||||
Features = new Meteor.Collection("features");
|
||||
|
||||
Schemas.Feature = new SimpleSchema({
|
||||
_id: {
|
||||
type: String,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
autoValue: function(){
|
||||
if(!this.isSet) return Random.id();
|
||||
}
|
||||
},
|
||||
charId: {type: String, regEx: SimpleSchema.RegEx.Id, optional: true},
|
||||
name: {type: String},
|
||||
description:{type: String, optional: true},
|
||||
source: {type: String, optional: true},
|
||||
effects: {type: [Schemas.Effect], defaultValue: []},
|
||||
actions: {type: [Schemas.Action], defaultValue: []},
|
||||
attacks: {type: [Schemas.Attack], defaultValue: []},
|
||||
@@ -31,12 +12,24 @@ Schemas.Feature = new SimpleSchema({
|
||||
|
||||
Features.attachSchema(Schemas.Feature);
|
||||
|
||||
//observe standard features for changes and update characters using them
|
||||
Features.find().observe({
|
||||
//update the features of the items as needed
|
||||
Features.find({}, {fields: {name: 0, description: 0}}).observe({
|
||||
added: function(newFeature){
|
||||
if(newFeature.charId){
|
||||
//make sure existing versions of this feature's effects aren't duplicated
|
||||
removeFeatureEffects(newFeature.charId, newFeature);
|
||||
//add the new feature's effects
|
||||
addFeatureEffects(newFeature.charId, newFeature);
|
||||
}
|
||||
},
|
||||
changed: function(newFeature, oldFeature){
|
||||
//TODO
|
||||
if(oldFeature.charId)
|
||||
removeFeatureEffects(oldFeature.charId, oldFeature);
|
||||
if(newFeature.charId)
|
||||
addFeatureEffects(newFeature.charId, newFeature);
|
||||
},
|
||||
removed: function(oldFeature){
|
||||
//TODO
|
||||
if(oldFeature.charId)
|
||||
removeFeatureEffects(oldFeature.charId, oldFeature);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,11 +7,11 @@ Schemas.Attribute = new SimpleSchema({
|
||||
},
|
||||
//effect arrays
|
||||
effects: { type: [Schemas.Effect], defaultValue: [] },
|
||||
reset: {
|
||||
type: String,
|
||||
defaultValue: "longRest",
|
||||
allowedValues: ["longRest", "shortRest"]
|
||||
}
|
||||
reset: {
|
||||
type: String,
|
||||
defaultValue: "longRest",
|
||||
allowedValues: ["longRest", "shortRest"]
|
||||
}
|
||||
});
|
||||
|
||||
//note that to make an invulnerability add a new max of zero value
|
||||
@@ -22,12 +22,16 @@ Schemas.Vulnerability = new SimpleSchema({
|
||||
defaultValue: 0
|
||||
},
|
||||
//effect arrays
|
||||
mul: { type: [Schemas.Effect], defaultValue: [] },
|
||||
min: { type: [Schemas.Effect], defaultValue: [{name: "Resistance doesn't stack", value: 0.5}] },
|
||||
max: { type: [Schemas.Effect], defaultValue: [{name: "Vulnerability doesn't stack", value: 2}] },
|
||||
reset: {
|
||||
type: String,
|
||||
defaultValue: "longRest",
|
||||
allowedValues: ["longRest", "shortRest"]
|
||||
}
|
||||
effects: {
|
||||
type: [Schemas.Effect],
|
||||
defaultValue: [
|
||||
{type: "inate", name: "Resistance doesn't stack", operation: "min", value: 0.5},
|
||||
{type: "inate", name: "Vulnerability doesn't stack", operation: "max", value: 2}
|
||||
]
|
||||
},
|
||||
reset: {
|
||||
type: String,
|
||||
defaultValue: "longRest",
|
||||
allowedValues: ["longRest", "shortRest"]
|
||||
}
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@ Schemas.Effect = new SimpleSchema({
|
||||
operation: {
|
||||
type: String,
|
||||
defaultValue: "add",
|
||||
allowedValues: ["base", "proficiency","add","mul","min","max","advantage","disadvantage","passiveAdd","fail","conditional","passiveAdd"]
|
||||
allowedValues: ["base", "proficiency","add","mul","min","max","advantage","disadvantage","passiveAdd","fail","conditional"]
|
||||
},
|
||||
value: {
|
||||
type: Number,
|
||||
@@ -31,7 +31,7 @@ Schemas.Effect = new SimpleSchema({
|
||||
type: {
|
||||
type: String,
|
||||
defaultValue: "editable",
|
||||
allowedValues: ["editable", "feat", "buff", "equipment", "inate"]
|
||||
allowedValues: ["editable", "feature", "buff", "equipment", "inate"]
|
||||
},
|
||||
//which stat the effect is applied to
|
||||
stat: {
|
||||
@@ -1,9 +1,10 @@
|
||||
//set up the collection for containers
|
||||
Containers = new Meteor.Collection("containers");
|
||||
|
||||
|
||||
Schemas.Container = new SimpleSchema({
|
||||
name: { type: String },
|
||||
owner: { type: String, regEx: SimpleSchema.RegEx.Id},
|
||||
charId: { type: String, regEx: SimpleSchema.RegEx.Id},
|
||||
isCarried: { type: Boolean }
|
||||
});
|
||||
|
||||
|
||||
@@ -4,14 +4,20 @@ Schemas.Item = new SimpleSchema({
|
||||
name: {type: String, defaultValue: "New Item"},
|
||||
plural: {type: String, optional: true},
|
||||
description:{type: String, defaultValue: ""},
|
||||
container: {type: String}, //id of container it normally is stowed in
|
||||
character: {type: String, regEx: SimpleSchema.RegEx.Id}, //id of owner
|
||||
container: {type: String, regEx: SimpleSchema.RegEx.Id, optional: true}, //id of container it normally is stowed in
|
||||
charId: {type: String, regEx: SimpleSchema.RegEx.Id, optional: true}, //id of owner
|
||||
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},
|
||||
tradeGood: {type: Boolean, defaultValue: false},
|
||||
stackable: {type: Boolean, defaultValue: false},
|
||||
feature: {type: Schemas.Feature},
|
||||
"feature.name": {type: String, autoValue: function(){return this.field("name").value}},
|
||||
"feature.description": {type: String, autoValue: function(){return this.field("description").value}},
|
||||
"feature.source": {type: String, autoValue: function(){return this.field("name").value}},
|
||||
"feature.effects.$.name": {type: String, autoValue: function(){return this.field("name").value}},
|
||||
"feature.effects.$.type": {type: String, autoValue: function(){return "equipment"}},
|
||||
"feature.attacks.$.name": {type: String, autoValue: function(){return this.field("name").value}},
|
||||
equipmentSlot: {
|
||||
type: String,
|
||||
defaultValue: "none",
|
||||
@@ -23,20 +29,20 @@ Schemas.Item = new SimpleSchema({
|
||||
Items.attachSchema(Schemas.Item);
|
||||
|
||||
//update the features of the items as needed
|
||||
Items.find({}, {fields: {feature: 1, character: 1, equipped: 1}}).observe({
|
||||
Items.find({}, {fields: {feature: 1, charId: 1, equipped: 1}}).observe({
|
||||
added: function(newItem){
|
||||
if(newItem.feature && newItem.character)
|
||||
addFeatureEffects(newItem.character, newItem.feature);
|
||||
if(newItem.feature && newItem.charId)
|
||||
addFeatureEffects(newItem.charId, newItem.feature);
|
||||
},
|
||||
changed: function(newItem, oldItem){
|
||||
if(oldItem.feature && oldItem.character)
|
||||
removeFeatureEffects(oldItem.character, oldItem.feature);
|
||||
if(newItem.feature && newItem.character)
|
||||
addFeatureEffects(newItem.character, newItem.feature);
|
||||
if(oldItem.feature && oldItem.charId)
|
||||
removeFeatureEffects(oldItem.charId, oldItem.feature);
|
||||
if(newItem.feature && newItem.charId)
|
||||
addFeatureEffects(newItem.charId, newItem.feature);
|
||||
},
|
||||
removed: function(oldItem){
|
||||
if(oldItem.feature && oldItem.character)
|
||||
removeFeatureEffects(oldItem.character, oldItem.feature);
|
||||
if(oldItem.feature && oldItem.charId)
|
||||
removeFeatureEffects(oldItem.charId, oldItem.feature);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -20,7 +20,12 @@ Router.map( function () {
|
||||
this.route('characterSheet', {
|
||||
path: '/character/:_id',
|
||||
waitOn: function(){
|
||||
return Meteor.subscribe("singleCharacter", this.params._id, Meteor.userId());
|
||||
return [
|
||||
Meteor.subscribe("singleCharacter", this.params._id, Meteor.userId()),
|
||||
Meteor.subscribe("characterContainers", this.params._id, Meteor.userId()),
|
||||
Meteor.subscribe("characterItems", this.params._id, Meteor.userId()),
|
||||
Meteor.subscribe("characterFeatures", this.params._id, Meteor.userId()),
|
||||
];
|
||||
},
|
||||
data: function() {
|
||||
var data = Characters.findOne({_id: this.params._id}, {fields: {_id: 1}});
|
||||
@@ -50,4 +55,4 @@ Router.map( function () {
|
||||
this.route('loading', {
|
||||
path: '/loading'
|
||||
});
|
||||
});
|
||||
});
|
||||
55
rpg-docs/client/globalHelpers/GlobalUI.js
Normal file
55
rpg-docs/client/globalHelpers/GlobalUI.js
Normal file
@@ -0,0 +1,55 @@
|
||||
this.GlobalUI = (function() {
|
||||
function GlobalUI() {}
|
||||
|
||||
GlobalUI.dialog = {};
|
||||
|
||||
GlobalUI.toast = function(text, className) {
|
||||
var toast;
|
||||
toast = $("[global-toast]")[0];
|
||||
toast.text = text;
|
||||
return toast.show();
|
||||
};
|
||||
|
||||
GlobalUI.showDialog = function(opts) {
|
||||
this.dialog = $("[global-dialog]")[0];
|
||||
Session.set("global.ui.dialogHeader", opts.heading);
|
||||
Session.set("global.ui.dialogData", opts.data);
|
||||
Session.set("global.ui.dialogTemplate", opts.template);
|
||||
Session.set("global.ui.dialogFullOnMobile", opts.fullOnMobile != null);
|
||||
return Tracker.afterFlush((function(_this) {
|
||||
return function() {
|
||||
return _this.dialog.open();
|
||||
};
|
||||
})(this));
|
||||
};
|
||||
|
||||
GlobalUI.closeDialog = function() {
|
||||
return this.dialog.close();
|
||||
};
|
||||
|
||||
return GlobalUI;
|
||||
|
||||
})();
|
||||
|
||||
Template.layout.helpers({
|
||||
globalDialogTemplate: function() {
|
||||
return Session.get("global.ui.dialogTemplate");
|
||||
},
|
||||
globalDialogData: function() {
|
||||
return Session.get("global.ui.dialogData");
|
||||
},
|
||||
globalDialogFullOnMobile: function() {
|
||||
return Session.get("global.ui.dialogFullOnMobile");
|
||||
},
|
||||
globalDialogHeader: function(){
|
||||
return Session.get("global.ui.dialogHeader");
|
||||
}
|
||||
});
|
||||
|
||||
Template.layout.events({
|
||||
"core-overlay-close-completed [global-dialog]": function(e) {
|
||||
Session.set("global.ui.dialogTemplate", null);
|
||||
Session.set("global.ui.dialogData", null);
|
||||
return Session.set("global.ui.dialogFullOnMobile", null);
|
||||
},
|
||||
});
|
||||
1
rpg-docs/client/globalHelpers/setupAutoform.js
Normal file
1
rpg-docs/client/globalHelpers/setupAutoform.js
Normal file
@@ -0,0 +1 @@
|
||||
AutoForm.setDefaultTemplate('paper');
|
||||
@@ -1,5 +1,5 @@
|
||||
<template name="stats">
|
||||
<core-animated-pages selected={{selectedSection}} transitions="hero-transition cross-fade">
|
||||
<core-animated-pages selected={{selectedSection}} transitions="hero-transition cross-fade" fit style="overflow-y: scroll">
|
||||
<section id="stats">
|
||||
{{> abilityCards}}
|
||||
</section>
|
||||
|
||||
@@ -17,14 +17,14 @@
|
||||
</paper-tabs>
|
||||
</div>
|
||||
</core-toolbar>
|
||||
<div>
|
||||
<core-animated-pages id="tabPages" selected={{selectedTab}} transitions="slide-from-right">
|
||||
<swipe-detect touch-action="pan-y">{{> stats}}</swipe-detect>
|
||||
<swipe-detect touch-action="pan-y">{{> features}}</swipe-detect>
|
||||
<swipe-detect touch-action="pan-y">inventory</swipe-detect>
|
||||
<swipe-detect touch-action="pan-y">spellBook</swipe-detect>
|
||||
<swipe-detect touch-action="pan-y">persona</swipe-detect>
|
||||
<swipe-detect touch-action="pan-y">journal</swipe-detect>
|
||||
<div fit>
|
||||
<core-animated-pages id="tabPages" selected={{selectedTab}} transitions="slide-from-right" fit>
|
||||
<swipe-detect touch-action="pan-y" flex>{{> stats}}</swipe-detect>
|
||||
<swipe-detect touch-action="pan-y" flex>{{> features}}</swipe-detect>
|
||||
<swipe-detect touch-action="pan-y" flex>{{> inventory}}</swipe-detect>
|
||||
<swipe-detect touch-action="pan-y" flex>spellBook</swipe-detect>
|
||||
<swipe-detect touch-action="pan-y" flex>persona</swipe-detect>
|
||||
<swipe-detect touch-action="pan-y" flex>journal</swipe-detect>
|
||||
</core-animated-pages>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
24
rpg-docs/client/views/character/features/featureDialog.html
Normal file
24
rpg-docs/client/views/character/features/featureDialog.html
Normal file
@@ -0,0 +1,24 @@
|
||||
<template name="autoFeatureDialog">
|
||||
{{> quickForm schema="Schemas.Feature" id="insertFeatureForm" type="insert"}}
|
||||
<paper-button affirmative>Cancel</paper-button>
|
||||
<paper-button affirmative>Save Item</paper-button>
|
||||
</template>
|
||||
|
||||
<template name="featureDialog">
|
||||
{{#with feature}}
|
||||
<div class="featureDialogWidth"></div>
|
||||
<paper-input id="featureNameInput" label="Name" floatinglabel value={{name}}></paper-input>
|
||||
<paper-input-decorator label="Description" floatinglabel layout vertical>
|
||||
<paper-autogrow-textarea>
|
||||
<textarea id="featureDescriptionInput" placeholder aria-label="Description" value={{description}}></textarea>
|
||||
</paper-autogrow-textarea>
|
||||
</paper-input-decorator>
|
||||
<h3>Effects</h3>
|
||||
{{#each effects}}
|
||||
{{>featureEffect}}
|
||||
{{/each}}
|
||||
<br>
|
||||
<paper-icon-button id="addEffectButton" role="button" tabindex="0" icon="add" aria-label="addEffect"></paper-icon-button>
|
||||
{{/with}}
|
||||
<paper-button affirmative>Done</paper-button>
|
||||
</template>
|
||||
36
rpg-docs/client/views/character/features/featureDialog.js
Normal file
36
rpg-docs/client/views/character/features/featureDialog.js
Normal file
@@ -0,0 +1,36 @@
|
||||
Template.featureDialog.rendered = function(){
|
||||
var self = this;
|
||||
this.autorun(function(){
|
||||
var feature = Features.findOne(Template.currentData().featureId, {fields: {name: 1}});
|
||||
if(feature && feature.name) Session.set("global.ui.dialogHeader", feature.name);
|
||||
})
|
||||
}
|
||||
|
||||
Template.featureDialog.events({
|
||||
"tap #addEffectButton": function(){
|
||||
var numUpdated = Features.update(this._id, {
|
||||
$push: {
|
||||
"effects": {
|
||||
name: "fe",
|
||||
operation: "add",
|
||||
type: "feature"
|
||||
}
|
||||
}
|
||||
});
|
||||
console.log("pushed add button ", numUpdated, " updated");
|
||||
},
|
||||
"change #featureNameInput": function(event){
|
||||
var name = Template.instance().find("#featureNameInput").value;
|
||||
Features.update(this._id, {$set: {name: name}});
|
||||
},
|
||||
"change #featureDescriptionInput": function(event){
|
||||
var description = Template.instance().find("#featureDescriptionInput").value;
|
||||
Features.update(this._id, {$set: {description: description}});
|
||||
}
|
||||
});
|
||||
|
||||
Template.featureDialog.helpers({
|
||||
feature: function(){
|
||||
return Features.findOne(this.featureId);
|
||||
}
|
||||
});
|
||||
69
rpg-docs/client/views/character/features/featureEffect.html
Normal file
69
rpg-docs/client/views/character/features/featureEffect.html
Normal file
@@ -0,0 +1,69 @@
|
||||
<template name="featureEffect">
|
||||
<paper-dropdown-menu id="statGroupDropDown" label="Stat Group">
|
||||
<paper-dropdown class="dropdown">
|
||||
<core-menu id="statGroupMenu" class="menu" selected={{selectedStatGroup}}>
|
||||
{{#each statGroups}}
|
||||
<paper-item>{{this}}</paper-item>
|
||||
{{/each}}
|
||||
</core-menu>
|
||||
</paper-dropdown>
|
||||
</paper-dropdown-menu>
|
||||
{{#if stats}}
|
||||
<paper-dropdown-menu id="statDropDown" label="Stat">
|
||||
<paper-dropdown class="dropdown">
|
||||
<core-menu id="statMenu" class="menu" selected={{selectedStat}}>
|
||||
{{#each stats}}
|
||||
<paper-item>{{name}}</paper-item>
|
||||
{{/each}}
|
||||
</core-menu>
|
||||
</paper-dropdown>
|
||||
</paper-dropdown-menu>
|
||||
{{/if}}
|
||||
{{#if operations}}
|
||||
<paper-dropdown-menu id="operationDropDown" label="Operation">
|
||||
<paper-dropdown class="dropdown">
|
||||
<core-menu id="operationMenu" class="menu" selected={{selectedOperation}}>
|
||||
{{#each operations}}
|
||||
<paper-item>{{name}}</paper-item>
|
||||
{{/each}}
|
||||
</core-menu>
|
||||
</paper-dropdown>
|
||||
</paper-dropdown-menu>
|
||||
{{/if}}
|
||||
{{> Template.dynamic template=effectValueTemplate data=valueTemplateData}}
|
||||
{{#if needsCommit}}
|
||||
<paper-icon-button id="commitChanges" role="button" tabindex="0" icon="check" aria-label="Commit Changes"></paper-icon-button>
|
||||
<paper-icon-button id="clearChanges" role="button" tabindex="0" icon="clear" aria-label="Clear Changes"></paper-icon-button>
|
||||
{{else}}
|
||||
<paper-icon-button id="deleteEffect" role="button" tabindex="0" icon="delete" aria-label="Delete"></paper-icon-button>
|
||||
{{/if}}
|
||||
<br>
|
||||
</template>
|
||||
|
||||
<template name="regularEffectValue">
|
||||
<paper-input id="effectValueInput" label="Value" floatinglabel value={{effectValue}}></paper-input>
|
||||
</template>
|
||||
|
||||
<template name="multiplierEffectValue">
|
||||
<paper-dropdown-menu id="damageMultiplierDropDown" label="Damage Multiplier">
|
||||
<paper-dropdown class="dropdown">
|
||||
<core-menu id="multiplierMenu" class="menu" selected={{selectedDamageMultiplier}}>
|
||||
<paper-item>Resistance</paper-item>
|
||||
<paper-item>Vulnerability</paper-item>
|
||||
<paper-item>Immunity</paper-item>
|
||||
</core-menu>
|
||||
</paper-dropdown>
|
||||
</paper-dropdown-menu>
|
||||
</template>
|
||||
|
||||
<template name="proficiencyEffectValue">
|
||||
<paper-dropdown-menu id="proficiencyDropDown" label="Proficiency">
|
||||
<paper-dropdown class="dropdown">
|
||||
<core-menu id="proficiencyMenu" class="menu" selected={{selectedProfiencyMultiplier}}>
|
||||
<paper-item>Proficient</paper-item>
|
||||
<paper-item>Half Prof. Bonus</paper-item>
|
||||
<paper-item>Double Prof. Bonus</paper-item>
|
||||
</core-menu>
|
||||
</paper-dropdown>
|
||||
</paper-dropdown-menu>
|
||||
</template>
|
||||
329
rpg-docs/client/views/character/features/featureEffect.js
Normal file
329
rpg-docs/client/views/character/features/featureEffect.js
Normal file
@@ -0,0 +1,329 @@
|
||||
var stats = [
|
||||
{stat: "strength", name: "Strength", group: "Ability Scores"},
|
||||
{stat: "dexterity", name: "Dexterity", group: "Ability Scores"},
|
||||
{stat: "constitution", name: "Constitution", group: "Ability Scores"},
|
||||
{stat: "intelligence", name: "Intelligence", group: "Ability Scores"},
|
||||
{stat: "wisdom", name: "Wisdom", group: "Ability Scores"},
|
||||
{stat: "charisma", name: "Charisma", group: "Ability Scores"},
|
||||
{name: "Strength Save", stat: "strengthSave", group: "Saving Throws"},
|
||||
{name: "Dexterity Save", stat: "dexteritySave", group: "Saving Throws"},
|
||||
{name: "Constitution Save", stat: "constitutionSave", group: "Saving Throws"},
|
||||
{name: "Intelligence Save", stat: "intelligenceSave", group: "Saving Throws"},
|
||||
{name: "Wisdom Save", stat: "wisdomSave", group: "Saving Throws"},
|
||||
{name: "Charisma Save", stat: "charismaSave", group: "Saving Throws"},
|
||||
{name: "Acrobatics", stat: "acrobatics", group: "Skills"},
|
||||
{name: "Animal Handling", stat: "animalHandling", group: "Skills"},
|
||||
{name: "Arcana", stat: "arcana", group: "Skills"},
|
||||
{name: "Athletics", stat: "athletics", group: "Skills"},
|
||||
{name: "Deception", stat: "deception", group: "Skills"},
|
||||
{name: "History", stat: "history", group: "Skills"},
|
||||
{name: "Insight", stat: "insight", group: "Skills"},
|
||||
{name: "Intimidation", stat: "intimidation", group: "Skills"},
|
||||
{name: "Investigation", stat: "investigation", group: "Skills"},
|
||||
{name: "Medicine", stat: "medicine", group: "Skills"},
|
||||
{name: "Nature", stat: "nature", group: "Skills"},
|
||||
{name: "Perception", stat: "perception", group: "Skills"},
|
||||
{name: "Performance", stat: "performance", group: "Skills"},
|
||||
{name: "Persuasion", stat: "persuasion", group: "Skills"},
|
||||
{name: "Religion", stat: "religion", group: "Skills"},
|
||||
{name: "Sleight of Hand", stat: "sleightOfHand", group: "Skills"},
|
||||
{name: "Stealth", stat: "stealth", group: "Skills"},
|
||||
{name: "Survival", stat: "survival", group: "Skills"},
|
||||
{stat: "hitPoints", name: "Hit Points", group: "Stats"},
|
||||
{stat: "armor", name: "Armor", group: "Stats"},
|
||||
{stat: "speed", name: "Speed", group: "Stats"},
|
||||
{stat: "ki", name: "Ki Points", group: "Stats"},
|
||||
{stat: "sorceryPoints", name: "Sorcery Points", group: "Stats"},
|
||||
{stat: "rages", name: "Rages", group: "Stats"},
|
||||
{stat: "rageDamage", name: "Rage Damage", group: "Stats"},
|
||||
{stat: "expertiseDice", name: "Expertise Dice", group: "Stats"},
|
||||
{stat: "superiorityDice", name: "Superiority Dice", group: "Stats"},
|
||||
{stat: "level1SpellSlots", name: "level 1", group: "Spell Slots"},
|
||||
{stat: "level2SpellSlots", name: "level 2", group: "Spell Slots"},
|
||||
{stat: "level3SpellSlots", name: "level 3", group: "Spell Slots"},
|
||||
{stat: "level4SpellSlots", name: "level 4", group: "Spell Slots"},
|
||||
{stat: "level5SpellSlots", name: "level 5", group: "Spell Slots"},
|
||||
{stat: "level6SpellSlots", name: "level 6", group: "Spell Slots"},
|
||||
{stat: "level7SpellSlots", name: "level 7", group: "Spell Slots"},
|
||||
{stat: "level8SpellSlots", name: "level 8", group: "Spell Slots"},
|
||||
{stat: "level9SpellSlots", name: "level 9", group: "Spell Slots"},
|
||||
{stat: "d6HitDice", name: "d6", group: "Hit Dice"},
|
||||
{stat: "d8HitDice", name: "d8", group: "Hit Dice"},
|
||||
{stat: "d10HitDice", name: "d10", group: "Hit Dice"},
|
||||
{stat: "d12HitDice", name: "d12", group: "Hit Dice"},
|
||||
{stat: "acidMultiplier", name: "Acid", group: "Weakness/Resistance"},
|
||||
{stat: "bludgeoningMultiplier", name: "Bludgeoning", group: "Weakness/Resistance"},
|
||||
{stat: "coldMultiplier", name: "Cold", group: "Weakness/Resistance"},
|
||||
{stat: "fireMultiplier", name: "Fire", group: "Weakness/Resistance"},
|
||||
{stat: "forceMultiplier", name: "Force", group: "Weakness/Resistance"},
|
||||
{stat: "lightningMultiplier", name: "Lightning", group: "Weakness/Resistance"},
|
||||
{stat: "necroticMultiplier", name: "Necrotic", group: "Weakness/Resistance"},
|
||||
{stat: "piercingMultiplier", name: "Piercing", group: "Weakness/Resistance"},
|
||||
{stat: "poisonMultiplier", name: "Poison", group: "Weakness/Resistance"},
|
||||
{stat: "psychicMultiplier", name: "Psychic", group: "Weakness/Resistance"},
|
||||
{stat: "radiantMultiplier", name: "Radiant", group: "Weakness/Resistance"},
|
||||
{stat: "slashingMultiplier", name: "Slashing", group: "Weakness/Resistance"},
|
||||
{stat: "thunderMultiplier", name: "Thunder", group: "Weakness/Resistance"}
|
||||
];
|
||||
|
||||
var statsDict = _.indexBy(stats, "stat")
|
||||
var statGroups = _.groupBy(stats, "group");
|
||||
var statGroupNames = _.keys(statGroups);
|
||||
|
||||
var statGroupIndex = function(statName){
|
||||
if(!_.isString(statName)) return;
|
||||
var stat = statsDict[statName];
|
||||
if(stat){
|
||||
return _.indexOf(statGroupNames, stat.group)
|
||||
}
|
||||
}
|
||||
|
||||
var statIndex = function(statName){
|
||||
if(!_.isString(statName)) return;
|
||||
var stat = statsDict[statName];
|
||||
if(!stat) return;
|
||||
var group = statGroups[stat.group];
|
||||
if(!group) return;
|
||||
return _.indexOf(_.pluck(group, "stat"), statName);
|
||||
}
|
||||
|
||||
var attributeOperations = [
|
||||
{name: "Base Value", operation: "base"},
|
||||
{name: "Add", operation: "add"},
|
||||
{name: "Multiply", operation: "mul"},
|
||||
{name: "Min", operation: "min"},
|
||||
{name: "Max", operation: "max"}
|
||||
];
|
||||
var skillOperations = [
|
||||
{name: "Proficiency", operation: "proficiency"},
|
||||
{name: "Add", operation: "add"},
|
||||
{name: "Multiply", operation: "mul"},
|
||||
{name: "Min", operation: "min"},
|
||||
{name: "Max", operation: "max"},
|
||||
{name: "Advantage", operation: "advantage"},
|
||||
{name: "Disadvantage", operation: "disadvantage"},
|
||||
{name: "Passive Bonus", operation: "passiveAdd"},
|
||||
{name: "Automatically Fail", operation: "fail"},
|
||||
{name: "Conditional Benefit", operation: "conditional"}
|
||||
];
|
||||
|
||||
var operationIndex = function(statName, operation){
|
||||
if(!_.isString(statName)) return;
|
||||
if(!_.isString(operation)) return;
|
||||
var group = statsDict[statName].group;
|
||||
var opGroup;
|
||||
if(group === "Saving Throws" || group === "Skills"){
|
||||
opGroup = skillOperations;
|
||||
} else {
|
||||
opGroup = attributeOperations;
|
||||
}
|
||||
return _.indexOf(_.pluck(opGroup, "operation"), operation);
|
||||
}
|
||||
|
||||
Template.featureEffect.created = function(){
|
||||
this.selectedStatGroup = new ReactiveVar();
|
||||
this.selectedStat = new ReactiveVar();
|
||||
this.selectedOperation = new ReactiveVar();
|
||||
this.value = new ReactiveVar();
|
||||
};
|
||||
|
||||
Template.featureEffect.rendered = function(){
|
||||
var self = this;
|
||||
self.autorun(function(){
|
||||
var data = Template.currentData();
|
||||
if(!data) return;
|
||||
if(data.stat){
|
||||
if(statsDict[data.stat]){
|
||||
self.selectedStatGroup.set(statsDict[data.stat].group);
|
||||
}
|
||||
self.selectedStat.set(data.stat);
|
||||
}
|
||||
if(data.operation){
|
||||
self.selectedOperation.set(data.operation);
|
||||
}
|
||||
var value = undefined;
|
||||
if(_.isNumber(data.value)){
|
||||
value = data.value;
|
||||
} else if (_.isString(data.calculation)){
|
||||
value = data.calculation;
|
||||
}
|
||||
if(value){
|
||||
self.value.set(value);
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
Template.featureEffect.helpers({
|
||||
selectedStatGroup: function(){
|
||||
var groupName = Template.instance().selectedStatGroup.get();
|
||||
return _.indexOf(statGroupNames, groupName);
|
||||
},
|
||||
selectedStat: function(){
|
||||
var statName = Template.instance().selectedStat.get();
|
||||
return statIndex(statName);
|
||||
},
|
||||
selectedOperation: function(){
|
||||
var opName = Template.instance().selectedOperation.get();
|
||||
var statName = Template.instance().selectedStat.get();
|
||||
return operationIndex(statName, opName);
|
||||
},
|
||||
statGroups: function(){
|
||||
return statGroupNames;
|
||||
},
|
||||
stats: function(){
|
||||
var group = Template.instance().selectedStatGroup.get();
|
||||
return statGroups[group];
|
||||
},
|
||||
operations: function(){
|
||||
var group = Template.instance().selectedStatGroup.get();
|
||||
if(group === "Weakness/Resistance") return null;
|
||||
if(group === "Saving Throws" || group === "Skills"){
|
||||
return skillOperations;
|
||||
} else {
|
||||
return attributeOperations;
|
||||
}
|
||||
},
|
||||
effectValueTemplate: function(){
|
||||
//resistance/vulnerability template
|
||||
var group = Template.instance().selectedStatGroup.get();
|
||||
if(group === "Weakness/Resistance") return "multiplierEffectValue";
|
||||
|
||||
var op = Template.instance().selectedOperation.get();
|
||||
if(!op) return null;
|
||||
//operations that don't need templates
|
||||
if(op === "advantage" || op === "disadvantage" || op === "fail") return null;
|
||||
//proficiency template
|
||||
if(op === "proficiency") return "proficiencyEffectValue";
|
||||
|
||||
//default template
|
||||
return "regularEffectValue";
|
||||
},
|
||||
needsCommit: function(){
|
||||
var inst = Template.instance();
|
||||
if(
|
||||
inst.selectedStat.get() !== this.stat ||
|
||||
inst.selectedOperation.get() !== this.operation ||
|
||||
(inst.value.get() !== this.value && inst.value.get() !== this.calculation)
|
||||
){
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
valueTemplateData: function(){
|
||||
var value = Template.instance().value.get()
|
||||
var effectValue = value;
|
||||
var selectedDamageMultiplier = null;
|
||||
if(value === 0.5) selectedDamageMultiplier = 0;
|
||||
if(value === 2) selectedDamageMultiplier = 1;
|
||||
if(value === 0) selectedDamageMultiplier = 2;
|
||||
var selectedProfiencyMultiplier = null;
|
||||
if(value === 1) selectedProfiencyMultiplier = 0;
|
||||
if(value === 0.5) selectedProfiencyMultiplier = 1;
|
||||
if(value === 2) selectedProfiencyMultiplier = 2;
|
||||
var data = {
|
||||
effectValue: effectValue,
|
||||
selectedDamageMultiplier: selectedDamageMultiplier,
|
||||
selectedProfiencyMultiplier: selectedProfiencyMultiplier
|
||||
};
|
||||
return data;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Template.featureEffect.events({
|
||||
"tap #commitChanges": function(event){
|
||||
var newEffect = this;
|
||||
var inst = Template.instance();
|
||||
newEffect.operation = inst.selectedOperation.get();
|
||||
newEffect.stat = inst.selectedStat.get();
|
||||
var val = inst.value.get();
|
||||
if(_.isNumber(val)){
|
||||
newEffect.value = val;
|
||||
newEffect.calculation = null;
|
||||
} else if(_.isString(val)) {
|
||||
newEffect.calculation = val;
|
||||
newEffect.value = null;
|
||||
}
|
||||
Meteor.call("updateFeatureEffect", Template.parentData()._id, newEffect);
|
||||
},
|
||||
"tap #clearChanges": function(event){
|
||||
//essentially re-render
|
||||
var inst = Template.instance();
|
||||
if(this.operation) inst.selectedOperation.set(this.operation);
|
||||
if(this.stat) inst.selectedStat.set(this.stat);
|
||||
if(this.stat) inst.selectedStatGroup.set(statsDict[this.stat].group)
|
||||
var value = undefined;
|
||||
if(_.isNumber(this.value)){
|
||||
value = this.value;
|
||||
} else if (_.isString(this.calculation)){
|
||||
value = this.calculation;
|
||||
}
|
||||
inst.value.set(value);
|
||||
},
|
||||
"tap #deleteEffect": function(event){
|
||||
Features.update(Template.parentData()._id, { $pull: { "effects": {_id: this._id} } });
|
||||
},
|
||||
"core-select #statGroupMenu": function(event){
|
||||
var groupIndex = Template.instance().find("#statGroupMenu").selected;
|
||||
var groupName = statGroupNames[groupIndex]
|
||||
var oldName = Template.instance().selectedStatGroup.get();
|
||||
if(oldName != groupName){
|
||||
Template.instance().selectedStatGroup.set(groupName);
|
||||
var oldIndex = statGroupIndex(Template.instance().selectedStat.get())
|
||||
if(oldIndex != groupIndex){
|
||||
Template.instance().selectedStat.set(null);
|
||||
}
|
||||
}
|
||||
},
|
||||
"core-select #statMenu": function(event){
|
||||
var statIndex = Template.instance().find("#statMenu").selected;
|
||||
var groupIndex = Template.instance().find("#statGroupMenu").selected;
|
||||
var groupName = statGroupNames[groupIndex]
|
||||
var group = statGroups[groupName];
|
||||
var statObj = group[statIndex];
|
||||
if(!statObj) return;
|
||||
var statName = statObj.stat;
|
||||
Template.instance().selectedStat.set(statName);
|
||||
},
|
||||
"core-select #operationMenu": function(event){
|
||||
var groupName = Template.instance().selectedStatGroup.get();
|
||||
var opGroup = (groupName === "Saving Throws" || groupName === "Skills")? skillOperations : attributeOperations;
|
||||
var opIndex = Template.instance().find("#operationMenu").selected;
|
||||
var op = opGroup[opIndex];
|
||||
if(!op) return;
|
||||
var opName = op.operation;
|
||||
Template.instance().selectedOperation.set(opName);
|
||||
},
|
||||
"core-select #multiplierMenu": function(event){
|
||||
var inst = Template.instance();
|
||||
var selected = Template.instance().find("#multiplierMenu").selected;
|
||||
if(selected === 0){
|
||||
inst.value.set(0.5);
|
||||
inst.selectedOperation.set("mul");
|
||||
} else if (selected === 1){
|
||||
inst.value.set(2);
|
||||
inst.selectedOperation.set("mul");
|
||||
} else if (selected === 2){
|
||||
inst.value.set(0);
|
||||
inst.selectedOperation.set("max");
|
||||
}
|
||||
},
|
||||
"core-select #proficiencyMenu": function(event){
|
||||
var inst = Template.instance();
|
||||
var selected = inst.find("#proficiencyMenu").selected;
|
||||
var value;
|
||||
if(selected === 0){
|
||||
inst.value.set(1);
|
||||
} else if (selected === 1){
|
||||
inst.value.set(0.5);
|
||||
} else if (selected === 2){
|
||||
inst.value.set(2);
|
||||
}
|
||||
},
|
||||
"change #effectValueInput": function(event){
|
||||
var inst = Template.instance();
|
||||
var value = inst.find("#effectValueInput").value;
|
||||
inst.value.set(value);
|
||||
}
|
||||
});
|
||||
5
rpg-docs/client/views/character/features/features.css
Normal file
5
rpg-docs/client/views/character/features/features.css
Normal file
@@ -0,0 +1,5 @@
|
||||
paper-shadow.featureCard {
|
||||
padding: 16px;
|
||||
margin: 8px;
|
||||
background: white;
|
||||
}
|
||||
@@ -1,32 +1,22 @@
|
||||
<template name="features">
|
||||
<div class="statsFlex"><!--resources-->
|
||||
{{#if attributeBase "rages"}}
|
||||
{{#statCard id="rages" type="attribute" title="Rages"}}
|
||||
{{attributeValue "rages"}}
|
||||
{{/statCard}}
|
||||
{{/if}}
|
||||
{{#if canCast}}
|
||||
{{#statCard id="spellSlots" type="attribute" title="Spell Slots"}}
|
||||
<h1>{{> spellSlots}}</h1>
|
||||
{{/statCard}}
|
||||
{{/if}}
|
||||
{{#if attributeBase "sorceryPoints"}}
|
||||
{{#statCard id="sorceryPoints" type="attribute" title="Sorcery Points"}}
|
||||
{{attributeValue "sorceryPoints"}}
|
||||
{{/statCard}}
|
||||
{{/if}}
|
||||
{{#if attributeBase "expertiseDice"}}
|
||||
{{#statCard id="expertiseDice" type="attribute" title="Expertise Dice"}}
|
||||
{{attributeValue "expertiseDice"}}
|
||||
{{/statCard}}
|
||||
{{/if}}
|
||||
{{#if attributeBase "superiorityDice"}}
|
||||
{{#statCard id="superiorityDice" type="attribute" title="Superiority Dice"}}
|
||||
{{attributeValue "superiorityDice"}}
|
||||
{{/statCard}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="actionsFlex">
|
||||
<div class="resources"><!--resources-->
|
||||
|
||||
</div>
|
||||
<div class="actions">
|
||||
|
||||
</div>
|
||||
<div class="features">
|
||||
{{#each features}}
|
||||
<paper-shadow class="featureCard">
|
||||
<div class="featureCardTop">
|
||||
<h1>{{name}}</h1>
|
||||
</div>
|
||||
<div class="featureCardBottom">
|
||||
<p>{{description}}</p>
|
||||
</div>
|
||||
<paper-ripple fit></paper-ripple>
|
||||
</paper-shadow>
|
||||
{{/each}}
|
||||
</div>
|
||||
<paper-fab id="addFeature" icon="add" title="Add" role="button" tabindex="0" aria-label="Add"></paper-fab>
|
||||
</template>
|
||||
@@ -1,19 +1,27 @@
|
||||
Template.features.helpers({
|
||||
features: function(){
|
||||
var features = Features.find({character: this._id});
|
||||
var features = Features.find({charId: this._id});
|
||||
return features;
|
||||
}
|
||||
});
|
||||
|
||||
Template.features.events({
|
||||
// Fires when any element is clicked
|
||||
'change .enabled': function (event) {
|
||||
var enable = event.target.checked
|
||||
Features.update(this._id, {$set: {enabled: enable}});
|
||||
if(enable){
|
||||
Template.parentData(1).pushEffects(this.name, this.effects);
|
||||
} else {
|
||||
Template.parentData(1).pullEffects(this.effects);
|
||||
}
|
||||
"tap #addFeature": function(event){
|
||||
var featureId = Features.insert({name: "New Feature", charId: this._id});
|
||||
GlobalUI.showDialog({
|
||||
heading: "New Feature",
|
||||
template: "featureDialog",
|
||||
data: {featureId: featureId},
|
||||
fullOnMobile: true
|
||||
})
|
||||
},
|
||||
"tap .featureCard": function(event){
|
||||
var featureId = this._id;
|
||||
GlobalUI.showDialog({
|
||||
heading: this.name,
|
||||
template: "featureDialog",
|
||||
data: {featureId: featureId},
|
||||
fullOnMobile: true
|
||||
})
|
||||
}
|
||||
});
|
||||
23
rpg-docs/client/views/character/features/featuresDialog.css
Normal file
23
rpg-docs/client/views/character/features/featuresDialog.css
Normal file
@@ -0,0 +1,23 @@
|
||||
body /deep/ .featureDialogWidth {
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
body /deep/ #statGroupDropDown {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
body /deep/ #statDropDown {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
body /deep/ #operationDropDown {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
body /deep/ #damageMultiplierDropDown {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
body /deep/ #proficiencyDropDown {
|
||||
width: 120px;
|
||||
}
|
||||
@@ -1,20 +1,27 @@
|
||||
<template name="inventory">
|
||||
<paper-shadow class="equipment">
|
||||
Armor: {{#if equippedArmor}}{{equippedArmor}}{{else}}None{{/if}}
|
||||
<paper-shadow class="equipment card double">
|
||||
Armor:<br>
|
||||
<paper-item>{{#if armor}}{{armor.name}}{{else}}none{{/if}}</paper-item>
|
||||
Equipment:<br>
|
||||
{{#each equipment}}
|
||||
<paper-item>
|
||||
{{name}}
|
||||
</paper-item>
|
||||
{{/each}}
|
||||
</paper-shadow>
|
||||
<div class="containers">
|
||||
{{#each containers}}
|
||||
<paper-shadow>
|
||||
<h3>{{name}}</h3>
|
||||
<table>
|
||||
{{#each items}}
|
||||
<tr class={{#if isSelected}}selected{{/if}}>
|
||||
<td>{{#if stackable}}{{quantity}}{{/if}}</td>
|
||||
<td>{{pluralName}}</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</table>
|
||||
</paper-shadow>
|
||||
<paper-shadow class="card">
|
||||
<h3>{{name}}</h3>
|
||||
{{#each items ../_id _id}}
|
||||
<paper-item>
|
||||
{{#if stackable}}{{quantity}}{{/if}} {{pluralName}}
|
||||
</paper-item>
|
||||
{{/each}}
|
||||
</paper-shadow>
|
||||
{{/each}}
|
||||
</div>
|
||||
<paper-fab id="add" icon="add" title="Add" role="button" tabindex="0" aria-label="Add"></paper-fab>
|
||||
<paper-fab id="addItem" icon="note-add" title="Add Item" role="button" tabindex="0" aria-label="Add Item"></paper-fab>
|
||||
<paper-fab id="addContainer" icon="work" title="Add Container" role="button" tabindex="0" aria-label="Add Container"></paper-fab>
|
||||
</template>
|
||||
27
rpg-docs/client/views/character/inventory/inventory.js
Normal file
27
rpg-docs/client/views/character/inventory/inventory.js
Normal file
@@ -0,0 +1,27 @@
|
||||
Template.inventory.helpers({
|
||||
containers: function(){
|
||||
return Containers.find({charId: this._id})
|
||||
},
|
||||
items: function(charId, containerId){
|
||||
return Items.find({charId: charId, equipped: false, container: containerId })
|
||||
},
|
||||
armor: function(){
|
||||
return Items.findOne({ charId: this._id, equipped: true, equipmentSlot: "armor" })
|
||||
},
|
||||
equipment: function(){
|
||||
return Items.find({ charId: this._id, equipped: true, equipmentSlot: {$ne: "armor"} })
|
||||
}
|
||||
});
|
||||
|
||||
Template.inventory.events({
|
||||
"tap #addItem": function(){
|
||||
GlobalUI.showDialog({
|
||||
template: "itemDialog",
|
||||
data: null,
|
||||
fullOnMobile: true
|
||||
})
|
||||
},
|
||||
"tap #addContainer": function(){
|
||||
Containers.insert({name: "New Container", isCarried: true, charId: this._id});
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,5 @@
|
||||
<template name="itemDialog">
|
||||
{{> quickForm collection="Items" id="insertItemForm" type="insert"}}
|
||||
<paper-button affirmative>Cancel</paper-button>
|
||||
<paper-button affirmative>Save Item</paper-button>
|
||||
</template>
|
||||
@@ -1,6 +1,6 @@
|
||||
<head>
|
||||
<title>RPG Docs</title>
|
||||
<meta name="viewport" content="width=device-width initial-scale=1.0, user-scalable=no">
|
||||
<link href='http://fonts.googleapis.com/css?family=Roboto:400,300,300italic,400italic,500,500italic,700,700italic,900,900italic,100italic,100&subset=latin,latin-ext' rel='stylesheet' type='text/css'>
|
||||
<link href='https://fonts.googleapis.com/css?family=Roboto:400,300,300italic,400italic,500,500italic,700,700italic,900,900italic,100italic,100&subset=latin,latin-ext' rel='stylesheet' type='text/css'>
|
||||
<script src="/components/webcomponentsjs/webcomponents.js"></script>
|
||||
</head>
|
||||
|
||||
@@ -8,11 +8,15 @@
|
||||
<link rel="import" href="/components/core-scaffold/core-scaffold.html">
|
||||
|
||||
<!--paper components-->
|
||||
<link rel="import" href="/components/paper-dialog/paper-action-dialog.html">
|
||||
<link rel="import" href="/components/paper-dialog/paper-dialog.html">
|
||||
<link rel="import" href="/components/paper-dropdown/paper-dropdown.html">
|
||||
<link rel="import" href="/components/paper-dropdown-menu/paper-dropdown-menu.html">
|
||||
<link rel="import" href="/components/paper-fab/paper-fab.html">
|
||||
<link rel="import" href="/components/paper-icon-button/paper-icon-button.html">
|
||||
<link rel="import" href="/components/paper-input/paper-autogrow-textarea.html">
|
||||
<link rel="import" href="/components/paper-input/paper-input.html">
|
||||
<link rel="import" href="/components/paper-input/paper-input-decorator.html">
|
||||
<link rel="import" href="/components/paper-item/paper-item.html">
|
||||
<link rel="import" href="/components/paper-shadow/paper-shadow.html">
|
||||
<link rel="import" href="/components/paper-spinner/paper-spinner.html">
|
||||
|
||||
@@ -13,11 +13,14 @@
|
||||
</core-header-panel>
|
||||
</core-drawer-panel>
|
||||
|
||||
<paper-action-dialog global-dialog layered backdrop
|
||||
<paper-action-dialog global-dialog backdrop
|
||||
transition="paper-dialog-transition-bottom"
|
||||
full-on-mobile>
|
||||
class={{#if globalDialogFullOnMobile}}full-on-mobile{{/if}}
|
||||
autoclosedisabled
|
||||
heading={{globalDialogHeader}}
|
||||
layered>
|
||||
{{#if globalDialogTemplate}}
|
||||
{{> UI.dynamic template=globalDialogTemplate data=globalDialogData}}
|
||||
{{> UI.dynamic template=globalDialogTemplate data=globalDialogData}}
|
||||
{{/if}}
|
||||
</paper-action-dialog>
|
||||
|
||||
|
||||
316
rpg-docs/lib/constants/standardItems.js
Normal file
316
rpg-docs/lib/constants/standardItems.js
Normal file
@@ -0,0 +1,316 @@
|
||||
standardItems = [
|
||||
//armor
|
||||
{
|
||||
name: "Padded Armor",
|
||||
plural: "Padded Armor",
|
||||
description: "Padded armor consists of quilted layers of cloth and batting.",
|
||||
equipmentSlot: "armor",
|
||||
weight: 8,
|
||||
value: 5,
|
||||
feature: {
|
||||
effects: [
|
||||
{
|
||||
stat: "armor",
|
||||
operation: "base",
|
||||
value: 11,
|
||||
},
|
||||
{
|
||||
stat: "stealth",
|
||||
operation: "disadvantage",
|
||||
value: 1,
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "Leather Armor",
|
||||
plural: "Leather Armor",
|
||||
description:
|
||||
"The breastplate and shoulder protectors of this armor are made of leather that has been stiffened by being boiled in oil. The rest of the armor is made of softer and more flexible materials.",
|
||||
equipmentSlot: "armor",
|
||||
weight: 10,
|
||||
value: 10,
|
||||
feature: {
|
||||
effects: [
|
||||
{
|
||||
stat: "armor",
|
||||
operation: "base",
|
||||
value: 11,
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "Studded leather Armor",
|
||||
plural: "Studded leather Armor",
|
||||
description:
|
||||
"Made from tough but flexible leather, studded leather is reinforced with close-set rivets or spikes.",
|
||||
equipmentSlot: "armor",
|
||||
weight: 13,
|
||||
value: 45,
|
||||
feature: {
|
||||
effects: [
|
||||
{
|
||||
stat: "armor",
|
||||
operation: "base",
|
||||
value: 12,
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "Hide Armor",
|
||||
plural: "Hide Armor",
|
||||
description:
|
||||
"This crude armor consists of thick furs and pelts. It is commonly worn by barbarian tribes, evil humanoids, and other folk who lack access to the tools and materials needed to create better armor.",
|
||||
equipmentSlot: "armor",
|
||||
weight: 12,
|
||||
value: 10,
|
||||
feature: {
|
||||
effects: [
|
||||
{
|
||||
stat: "armor",
|
||||
operation: "base",
|
||||
value: 12,
|
||||
},
|
||||
{
|
||||
stat: "dexterityArmor",
|
||||
operation: "max",
|
||||
value: 2,
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "Chain Shirt",
|
||||
plural: "Chain Shirts",
|
||||
description:
|
||||
"Made of interlocking metal rings, a chain shirt is worn between layers of clothing or leather. This armor offers modest protection to the wearer’s upper body and allows the sound of the rings rubbing against one another to be muffled by outer layers.",
|
||||
equipmentSlot: "armor",
|
||||
weight: 20,
|
||||
value: 50,
|
||||
feature: {
|
||||
effects: [
|
||||
{
|
||||
stat: "armor",
|
||||
operation: "base",
|
||||
value: 13,
|
||||
},
|
||||
{
|
||||
stat: "dexterityArmor",
|
||||
operation: "max",
|
||||
value: 2,
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "Scale Mail",
|
||||
plural: "Scale Mail",
|
||||
description:
|
||||
"This armor consists of a coat and leggings (and perhaps a separate skirt) of leather covered with overlapping pieces of metal, much like the scales of a fish. The suit includes gauntlets.",
|
||||
equipmentSlot: "armor",
|
||||
weight: 45,
|
||||
value: 50,
|
||||
feature: {
|
||||
effects: [
|
||||
{
|
||||
stat: "armor",
|
||||
operation: "base",
|
||||
value: 14,
|
||||
},
|
||||
{
|
||||
stat: "dexterityArmor",
|
||||
operation: "max",
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
stat: "stealth",
|
||||
operation: "disadvantage",
|
||||
value: 1,
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "Breastplate",
|
||||
plural: "Breastplates",
|
||||
description:
|
||||
"This armor consists of a fitted metal chest piece worn with supple leather. Although it leaves the legs and arms relatively unprotected, this armor provides good protection for the wearer’s vital organs while leaving the wearer relatively unencumbered.",
|
||||
equipmentSlot: "armor",
|
||||
weight: 20,
|
||||
value: 400,
|
||||
feature: {
|
||||
effects: [
|
||||
{
|
||||
stat: "armor",
|
||||
operation: "base",
|
||||
value: 14,
|
||||
},
|
||||
{
|
||||
stat: "dexterityArmor",
|
||||
operation: "max",
|
||||
value: 2,
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "Half Plate",
|
||||
plural: "Half Plate",
|
||||
description:
|
||||
"Half plate consists of shaped metal plates that cover most of the wearer’s body. It does not include leg protection beyond simple greaves that are attached with leather straps.",
|
||||
equipmentSlot: "armor",
|
||||
weight: 40,
|
||||
value: 750,
|
||||
feature: {
|
||||
effects: [
|
||||
{
|
||||
stat: "armor",
|
||||
operation: "base",
|
||||
value: 15,
|
||||
},
|
||||
{
|
||||
stat: "dexterityArmor",
|
||||
operation: "max",
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
stat: "stealth",
|
||||
operation: "disadvantage",
|
||||
value: 1,
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "Ring Mail",
|
||||
plural: "Ring Mail",
|
||||
description:
|
||||
"This armor is leather armor with heavy rings sewn into it. The rings help reinforce the armor against blows from swords and axes. Ring mail is inferior to chain mail, and it’s usually worn only by those who can’t afford better armor.",
|
||||
equipmentSlot: "armor",
|
||||
weight: 40,
|
||||
value: 30,
|
||||
feature: {
|
||||
effects: [
|
||||
{
|
||||
stat: "armor",
|
||||
operation: "base",
|
||||
value: 14,
|
||||
},
|
||||
{
|
||||
stat: "dexterityArmor",
|
||||
operation: "max",
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
stat: "stealth",
|
||||
operation: "disadvantage",
|
||||
value: 1,
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "Chain Mail",
|
||||
plural: "Chain Mail",
|
||||
description:
|
||||
"Made of interlocking metal rings, chain mail includes a layer of quilted fabric worn underneath the mail to prevent chafing and to cushion the impact of blows. The suit includes gauntlets.",
|
||||
equipmentSlot: "armor",
|
||||
weight: 55,
|
||||
value: 75,
|
||||
feature: {
|
||||
effects: [
|
||||
{
|
||||
stat: "armor",
|
||||
operation: "base",
|
||||
value: 16,
|
||||
},
|
||||
{
|
||||
stat: "dexterityArmor",
|
||||
operation: "max",
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
stat: "stealth",
|
||||
operation: "disadvantage",
|
||||
value: 1,
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "Splint Armor",
|
||||
plural: "Splint Armor",
|
||||
description:
|
||||
"This armor is made of narrow vertical strips of metal riveted to a backing of leather that is worn over cloth padding. Flexible chain mail protects the joints.",
|
||||
equipmentSlot: "armor",
|
||||
weight: 60,
|
||||
value: 200,
|
||||
feature: {
|
||||
effects: [
|
||||
{
|
||||
stat: "armor",
|
||||
operation: "base",
|
||||
value: 17,
|
||||
},
|
||||
{
|
||||
stat: "dexterityArmor",
|
||||
operation: "max",
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
stat: "stealth",
|
||||
operation: "disadvantage",
|
||||
value: 1,
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "Plate Armor",
|
||||
plural: "Plate Armor",
|
||||
description:
|
||||
"Plate consists of shaped, interlocking metal plates to cover the entire body. A suit of plate includes gauntlets, heavy leather boots, a visored helmet, and thick layers of padding underneath the armor. Buckles and straps distribute the weight over the body.",
|
||||
equipmentSlot: "armor",
|
||||
weight: 65,
|
||||
value: 1500,
|
||||
feature: {
|
||||
effects: [
|
||||
{
|
||||
stat: "armor",
|
||||
operation: "base",
|
||||
value: 18,
|
||||
},
|
||||
{
|
||||
stat: "dexterityArmor",
|
||||
operation: "max",
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
stat: "stealth",
|
||||
operation: "disadvantage",
|
||||
value: 1,
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "Shield",
|
||||
plural: "Shields",
|
||||
description:
|
||||
"A shield is made from wood or metal and is carried in one hand. Wielding a shield increases your Armor Class by 2. You can benefit from only one shield at a time.",
|
||||
equipmentSlot: "held",
|
||||
weight: 6,
|
||||
value: 10,
|
||||
feature: {
|
||||
effects: [
|
||||
{
|
||||
stat: "armor",
|
||||
operation: "add",
|
||||
value: 2,
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
]
|
||||
@@ -8,20 +8,34 @@ Meteor.methods({
|
||||
selector,
|
||||
{ $set: setter }
|
||||
)
|
||||
},
|
||||
updateFeatureEffect: function (featureId, newEffect) {
|
||||
var selector = {_id: featureId};
|
||||
selector["effects._id"] = newEffect._id;
|
||||
var setter = {};
|
||||
setter["effects.$"] = newEffect
|
||||
Features.update(
|
||||
selector,
|
||||
{ $set: setter }
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
//pull a single effect by stat and id
|
||||
pullEffect = function(id, effect){
|
||||
var pullObject = {};
|
||||
pullObject[effect.stat + ".effects"] = {_id: effect._id};
|
||||
Characters.update(id, {$pull: pullObject });
|
||||
}
|
||||
if(effect.stat){
|
||||
var pullObject = {};
|
||||
pullObject[effect.stat + ".effects"] = {_id: effect._id};
|
||||
Characters.update(id, {$pull: pullObject });
|
||||
}
|
||||
},
|
||||
|
||||
pushEffect = function(id, effect){
|
||||
var pushObject = {};
|
||||
pushObject[effect.stat + ".effects"] = effect;
|
||||
Characters.update(id, {$push: pushObject});
|
||||
if(effect.stat){
|
||||
var pushObject = {};
|
||||
pushObject[effect.stat + ".effects"] = effect;
|
||||
Characters.update(id, {$push: pushObject});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,32 +1,6 @@
|
||||
Meteor.methods({
|
||||
addFeature: function(charId, newFeature){
|
||||
Characters.update(
|
||||
charId,
|
||||
{ $push: {"customFeatures": newFeature} }
|
||||
);
|
||||
addFeatureEffects(charId, newFeature);
|
||||
},
|
||||
removeFeature: function(charId, oldFeature){
|
||||
Characters.update(
|
||||
charId,
|
||||
{ $pull: { "customFeatures": {"_id": oldFeature._id} } }
|
||||
);
|
||||
removeFeatureEffects(charId, oldFeature);
|
||||
},
|
||||
updateFeature: function (charId, oldFeature, newFeature) {
|
||||
var selector = {_id: charId, "customFeatures._id": oldFeature._id};
|
||||
var setter = {"customFeatures.$": newFeature};
|
||||
Characters.update(
|
||||
selector,
|
||||
{ $set: setter }
|
||||
);
|
||||
removeFeatureEffects(charId, oldFeature);
|
||||
addFeatureEffects(charId, newFeature);
|
||||
}
|
||||
});
|
||||
|
||||
addFeatureEffects = function(charId, newFeature){
|
||||
_.each(newFeature.effects, function(effect){
|
||||
if(newFeature.name) effect.name = newFeature.name;
|
||||
pushEffect(charId, effect);
|
||||
});
|
||||
_.each(newFeature.actions, function(action){
|
||||
@@ -47,10 +21,10 @@ removeFeatureEffects = function(charId, oldFeature){
|
||||
_.each(oldFeature.actions, function(action){
|
||||
pullAction(charId, action);
|
||||
});
|
||||
_.each(newFeature.attacks, function(attack){
|
||||
pushAttack(charId, attack);
|
||||
_.each(oldFeature.attacks, function(attack){
|
||||
pullAttack(charId, attack);
|
||||
});
|
||||
_.each(newFeature.spells, function(spell){
|
||||
pushSpell(charId, spell);
|
||||
_.each(oldFeature.spells, function(spell){
|
||||
pullSpell(charId, spell);
|
||||
});
|
||||
};
|
||||
@@ -1,4 +1,16 @@
|
||||
Meteor.publish("singleCharacter", function(characterId, userId){
|
||||
//TODO check if this characer can be viewed by this user
|
||||
return Characters.find({_id: characterId});
|
||||
});
|
||||
|
||||
Meteor.publish("characterContainers", function(characterId, userId){
|
||||
return Containers.find({charId: characterId});
|
||||
});
|
||||
|
||||
Meteor.publish("characterItems", function(characterId, userId){
|
||||
return Items.find({charId: characterId});
|
||||
});
|
||||
|
||||
Meteor.publish("characterFeatures", function(characterId, userId){
|
||||
return Features.find({charId: characterId});
|
||||
});
|
||||
Reference in New Issue
Block a user