Implemented remaining core features

This commit is contained in:
Thaum
2015-02-25 12:48:45 +00:00
parent 52b8c8b8d6
commit 56f8e95d81
38 changed files with 748 additions and 100 deletions

View File

@@ -9,7 +9,8 @@ Schemas.Attack = new SimpleSchema({
regEx: SimpleSchema.RegEx.Id
},
name: {
type: String
type: String,
defaultValue: "New Attack"
},
range: {
type: String,
@@ -17,15 +18,24 @@ Schemas.Attack = new SimpleSchema({
},
attackBonus: {
type: String,
optional: true
optional: true,
defaultValue: "strengthMod + proficiencyBonus"
},
damage: {
type: String
type: String,
optional: true,
defaultValue: "1d8 + {strengthMod}"
},
damageType: {
type: String,
allowedValues: ["acid", "bludgeoning", "cold", "fire", "force", "lightning", "necrotic",
"piercing", "poison", "psychic", "radiant", "slashing", "thunder"]
allowedValues: ["bludgeoning", "piercing", "slashing", "acid", "cold", "fire", "force", "lightning", "necrotic",
"poison", "psychic", "radiant", "thunder"],
defaultValue: "slashing"
},
color: {
type: String,
allowedValues: _.pluck(colorOptions, "key"),
defaultValue: "q"
}
});

View File

@@ -3,16 +3,18 @@ Characters = new Meteor.Collection("characters");
Schemas.Character = new SimpleSchema({
//strings
name: { type: String, defaultValue: "", trim: false},
alignment: { type: String, defaultValue: "", trim: false},
gender: { type: String, defaultValue: "", trim: false},
race: { type: String, defaultValue: "", trim: false},
description:{ type: String, defaultValue: "", trim: false},
personality:{ type: String, defaultValue: "", trim: false},
ideals: { type: String, defaultValue: "", trim: false},
bonds: { type: String, defaultValue: "", trim: false},
flaws: { type: String, defaultValue: "", trim: false},
backstory: { type: String, defaultValue: "", trim: false},
name: { type: String, defaultValue: "", trim: false},
alignment: { type: String, defaultValue: "", trim: false},
gender: { type: String, defaultValue: "", trim: false},
race: { type: String, defaultValue: "", trim: false},
description: { type: String, defaultValue: "", trim: false},
personality: { type: String, defaultValue: "", trim: false},
ideals: { type: String, defaultValue: "", trim: false},
bonds: { type: String, defaultValue: "", trim: false},
flaws: { type: String, defaultValue: "", trim: false},
backstory: { type: String, defaultValue: "", trim: false},
proficiencies:{ type: String, defaultValue: "", trim: false},
languages: { type: String, defaultValue: "", trim: false},
//attributes
//ability scores

View File

@@ -5,7 +5,7 @@ Effects = new Meteor.Collection("effects");
* that modify their final value or presentation in some way
*/
Schemas.Effect = new SimpleSchema({
charId: {
charId: {
type: String,
regEx: SimpleSchema.RegEx.Id
},
@@ -31,7 +31,7 @@ Schemas.Effect = new SimpleSchema({
type: {
type: String,
defaultValue: "editable",
allowedValues: ["editable", "feature", "buff", "equipment", "inate"]
allowedValues: ["editable", "feature", "level", "buff", "equipment", "racial", "inate"]
},
//the id of the feature, buff or item that created this effect
sourceId: {

View File

@@ -0,0 +1,20 @@
Classes = new Meteor.Collection("classes");
Schemas.Class = new SimpleSchema({
charId: {type: String, regEx: SimpleSchema.RegEx.Id},
name: {type: String},
createdAt: {
type: Date,
autoValue: function() {
if (this.isInsert) {
return new Date;
} else if (this.isUpsert) {
return {$setOnInsert: new Date};
} else {
this.unset();
}
}
},
});
Classes.attachSchema(Schemas.Class);

View File

@@ -0,0 +1,9 @@
Levels = new Meteor.Collection("levels");
Schemas.Level = new SimpleSchema({
charId: {type: String, regEx: SimpleSchema.RegEx.Id},
classId: {type: String, regEx: SimpleSchema.RegEx.Id},
value: {type: Number}
});
Levels.attachSchema(Schemas.Level);

View File

@@ -14,5 +14,9 @@ Schemas.DeathSave = new SimpleSchema({
canDeathSave: {
type: Boolean,
defaultValue: true
},
stable: {
type: Boolean,
defaultValue: false
}
});

View File

@@ -1,7 +1,6 @@
//set up the collection for containers
Containers = new Meteor.Collection("containers");
Schemas.Container = new SimpleSchema({
name: { type: String },
charId: { type: String, regEx: SimpleSchema.RegEx.Id},

View File

@@ -1,5 +1,6 @@
Template.registerHelper("detailHero", function(){
if ( Session.equals("global.ui.detailHeroId", this._id) ) {
Template.registerHelper("detailHero", function(suffix){
var id = suffix? this._id + suffix : this._id;
if ( Session.equals("global.ui.detailHeroId", id) ) {
return "hero";
}
});

View File

@@ -4,5 +4,13 @@ Template.registerHelper("evaluate", function(charId, string){
Template.registerHelper("evaluateSigned", function(charId, string){
var number = evaluate(charId, string);
return number > 0? "+" + number : "" + number;
if(_.isFinite(number)){
return number > 0? "+" + number : "" + number;
} else{
return number;
}
});
Template.registerHelper("evaluateString", function(charId, string){
return evaluateString(charId, string);
});

View File

@@ -1,14 +1,18 @@
Session.set("Mongol", {
'collections': [
"Characters",
"Actions",
"Attacks",
"Classes",
"Containers",
"Items",
"Features",
"Effects",
"Spells",
"SpellLists",
"Experiences",
"Features",
"Items",
"Levels",
"Notes",
"Experiences"
"Spells",
"SpellLists"
],
'display': true,
'opacity_normal': ".7",

View File

@@ -161,6 +161,10 @@ paper-fab-menu /deep/ .container {
padding: 24px !important;
}
paper-slider {
margin-left: -8px;
}
.list-subhead {
color: rgba(0,0,0,0.54);
font-size: 14px;
@@ -169,6 +173,18 @@ paper-fab-menu /deep/ .container {
font-weight: 500;
}
.whiteTop {
border-bottom: rgba(0,0,0,0.12) solid 1px;
background: white;
padding: 16px;
position: relative;
border-radius: 2px 2px 0 0;
}
.whiteTop paper-icon-button {
margin: -8px;
}
.fullwidth {
width: 100%;
}
@@ -177,6 +193,26 @@ paper-fab-menu /deep/ .container {
padding: 16px;
}
.listPadded {
padding: 0 0 16px 0;
}
.rightPadded {
padding-right: 16px;
}
.bottomPadded {
padding-bottom: 16px;
}
.s {
padding: 0 0 16px 0;
}
.leftRound{
border-radius: 2px 0 0 2px;
}
.preline {
white-space: pre-line;
}

View File

@@ -101,3 +101,8 @@ p, .body1, body {
html /deep/ .white-text{
color: #fff;
}
.black54 {
color: #444;
color: rgba(0,0,0,0.54);
}

View File

@@ -0,0 +1,40 @@
<template name="attackDialog">
{{#with attack}}
<core-header-panel fit>
<core-toolbar class={{colorClass}} hero-id="toolbar" hero>
<paper-icon-button id="backButton" role="button" tabindex="0" icon="arrow-back" aria-label="close"></paper-icon-button>
<div flex>{{name}}</div>
<paper-icon-button id="deleteAttack"
role="button"
tabindex="0"
icon="delete"
aria-label="Delete Attack"
noink></paper-icon-button>
</core-toolbar>
<div class="detailContent">
<div layout horizontal>
<!--Name-->
<paper-input id="attackNameInput" label="Name" floatinglabel value={{name}}></paper-input>
<!--color-->
{{> colorDropdown}}
</div>
<!--attackBonus-->
<paper-input id="attackBonusInput" label="Attack Bonus" floatinglabel value={{attackBonus}}></paper-input>
<!--damage-->
<paper-input id="damageInput" label="Damage" floatinglabel value={{damage}}></paper-input>
<!--range-->
<paper-input id="rangeInput" label="Range" floatinglabel value={{range}}></paper-input>
<!--DamageType-->
<paper-dropdown-menu id="damageTypeDropdown" label="DamageType">
<paper-dropdown layered class="dropdown">
<core-menu class="menu" selected={{damageType}}>
{{#each damageTypes}}
<paper-item name={{this}} class="containerMenuItem">{{this}}</paper-item>
{{/each}}
</core-menu>
</paper-dropdown>
</paper-dropdown-menu>
</div>
</core-header-panel>
{{/with}}
</template>

View File

@@ -0,0 +1,78 @@
var damageTypes = ["bludgeoning", "piercing", "slashing", "acid", "cold", "fire", "force", "lightning", "necrotic",
"poison", "psychic", "radiant", "thunder"];
Template.attackDialog.rendered = function(){
var self = this;
//update all autogrows after they've been filled
var pata = this.$("paper-autogrow-textarea");
pata.each(function(index, el){
el.update($(el).children().get(0));
})
//update all input fields as well
var input = this.$("paper-input");
input.each(function(index, el){
el.valueChanged();
})
//after the dialog is built, open it
if (!this.alreadyRendered){
Session.set("global.ui.detailShow", true);
this.alreadyRendered = true;
}
}
Template.attackDialog.events({
"tap #backButton": function(){
GlobalUI.closeDetail()
},
"tap #deleteAttack": function(){
Attacks.remove(this._id);
GlobalUI.closeDetail()
},
"tap #addEffectButton": function(){
Effects.insert({
charId: this.charId,
sourceId: this._id,
operation: "add",
type: "attack"
});
},
"change #attackNameInput": function(event){
var value = event.currentTarget.value;
Attacks.update(this._id, {$set: {name: value}});
},
"change #attackBonusInput": function(event){
var value = event.currentTarget.value;
Attacks.update(this._id, {$set: {attackBonus: value}});
},
"change #damageInput": function(event){
var value = event.currentTarget.value;
Attacks.update(this._id, {$set: {damage: value}});
},
"change #rangeInput": function(event){
var value = event.currentTarget.value;
Attacks.update(this._id, {$set: {range: value}});
},
"core-select #damageTypeDropdown": function(event){
var detail = event.originalEvent.detail;
if(!detail.isSelected) return;
var value = detail.item.getAttribute("name");
if(value == this.damageType) return;
Attacks.update(this._id, {$set: {damageType: value}});
},
"core-select .colorDropdown": function(event){
var detail = event.originalEvent.detail;
if(!detail.isSelected) return;
var value = detail.item.getAttribute("name");
if(value == this.color) return;
Attacks.update(this._id, {$set: {color: value}});
},
});
Template.attackDialog.helpers({
attack: function(){
return Attacks.findOne(this.attackId);
},
damageTypes: function(){
return damageTypes;
}
});

View File

@@ -20,5 +20,4 @@
.resourceCards paper-shadow.healthCard {
width: 100%;
padding: 0 16px 0 0;
}

View File

@@ -12,6 +12,48 @@
{{>resource name="sorceryPoints" title="Sorcery Points" color="teal" char=this}}
<!--superiorityDice-->
{{>resource name="superiorityDice" title="Superiority Dice" color="teal" char=this}}
<!--Attacks-->
<paper-shadow class="card container" hero-id="main" {{detailHero}}>
<div class="whiteTop" hero-id="toolbar" layout horizontal center {{detailHero}}>
<div flex>
<div class="containerName subhead">Attacks</div>
</div>
<paper-icon-button class="black54" id="addAttackButton" icon="add"></paper-icon-button>
</div>
<div class="containerMain listPadded">
{{#each attacks}}
<div class="itemSlot">
<paper-item class="white attack" hero-id="main" {{detailHero}}>
<div layout horizontal class="fullwidth">
<div class="headline rightPadded" layout horizontal center>
{{evaluateSigned ../_id attackBonus}}
</div>
<div layout vertical flex>
<div>{{name}}</div>
<div class="caption">
{{{evaluateString ../_id damage}}} {{damageType}} {{range}}
</div>
</div>
</div>
</paper-item>
</div>
{{/each}}
</div>
</paper-shadow>
<!--Proficiencies-->
<paper-shadow class="card container" hero-id="main" {{detailHero "proficiencies"}}>
<div id="proficiencies"
class="containerTop grey white-text"
hero-id="toolbar"
layout horizontal center
{{detailHero "proficiencies"}}>
<div class="containerName subhead">Proficiencies</div>
</div>
<div flex class="containerMain padded preline">{{characterProficiencies}}</div>
</paper-shadow>
<!--features-->
{{#each features}}
<paper-shadow class="card container featureCard" hero-id="main" {{detailHero}}>

View File

@@ -11,6 +11,13 @@ Template.features.helpers({
},
featureOrder: function(){
return _.indexOf(_.keys(colorOptions), this.color);
},
attacks: function(){
return Attacks.find({charId: this._id}, {sort: {color: 1, name: 1}});
},
characterProficiencies: function(){
var char = Characters.findOne(this._id);
return char && char.proficiencies;
}
});
@@ -23,7 +30,21 @@ Template.features.events({
heroId: featureId
})
},
"tap .containerTop": function(event){
"tap #addAttackButton": function(event){
var charId = this._id;
Attacks.insert({
charId: charId
}, function(error, id){
if(!error){
GlobalUI.setDetail({
template: "attackDialog",
data: {attackId: id, charId: charId},
heroId: id
});
}
});
},
"tap .featureCard .containerTop": function(event){
var featureId = this._id;
var charId = Template.parentData()._id;
GlobalUI.setDetail({
@@ -32,6 +53,15 @@ Template.features.events({
heroId: featureId
});
},
"tap .attack": function(event){
var attackId = this._id;
var charId = Template.parentData()._id;
GlobalUI.setDetail({
template: "attackDialog",
data: {attackId: attackId, charId: charId},
heroId: attackId
});
},
"tap .useFeature": function(event){
var featureId = this._id;
Features.update(featureId, {$inc: {used: 1}});
@@ -39,6 +69,14 @@ Template.features.events({
"tap .resetFeature": function(event){
var featureId = this._id;
Features.update(featureId, {$set: {used: 0}});
},
"tap #proficiencies": function(event){
var charId = this._id;
GlobalUI.setDetail({
template: "textDialog",
data: {charId: charId, field: "proficiencies", title: "Proficiencies", color: "q"},
heroId: this._id + "proficiencies"
});
}
});

View File

@@ -1,15 +0,0 @@
<template name="healthCard">
<paper-shadow class="card container healthCard" hero-id="main" {{detailHero}} layout horizontal>
<div class="green white-text title padded leftRound" layout horizontal center>
Hit Points
</div>
<div flex layout vertical around-justified>
<paper-slider id="hitPointSlider"
value={{attributeValue "hitPoints"}}
max={{attributeBase "hitPoints"}}
editable pin
role="slider"
></paper-slider>
</div>
</paper-shadow>
</template>

View File

@@ -1,7 +0,0 @@
Template.healthCard.events({
"change #hitPointSlider": function(event){
var value = event.currentTarget.value;
var adjustment = value - this.attributeBase("hitPoints");
Characters.update(this._id, {$set: {"hitPoints.adjustment": adjustment}});
}
});

View File

@@ -2,11 +2,32 @@
<div fit>
<div id="inventory" class="scroll-y" fit>
<div class="containers">
<!--Net Worth-->
<paper-shadow class="card container" hero-id="main" {{detailHero}} layout horizontal>
<div class="indigo white-text subhead padded leftRound" layout horizontal center>
Net Worth
</div>
<div class="padded" layout horizontal center>
{{valueString netWorth}}
</div>
</paper-shadow>
<!--Weight Carried-->
<paper-shadow class="card container" hero-id="main" {{detailHero}} layout horizontal>
<div class="green white-text subhead padded leftRound" layout horizontal center>
Weight Carried
</div>
<div class="padded" layout horizontal center>
{{round weightCarried}}lbs
</div>
</paper-shadow>
<!--Equipment-->
<paper-shadow class="card container">
<div class="equipmentTop" layout horizontal center>
<div class="containerName subhead" flex>Equipment</div>
<div class="caption" style="margin-right: 8px">(net Worth)</div>
<div class="caption">(weightCarried)lbs</div>
<div class="containerName subhead" flex>
Equipment
</div>
<div class="caption" style="margin-right: 8px">{{valueString equipmentValue}}</div>
<div class="caption">{{round equipmentWeight}}lbs</div>
</div>
<div flex class="equipmentMain">
{{#if armor}}

View File

@@ -24,8 +24,36 @@ Template.inventory.helpers({
colorClass: function(){
return getColorClass(this.color)
},
containerOrder: function(){
return _.indexOf(_.keys(colorOptions), this.color);
netWorth: function(){
var worth = 0;
Items.find({charId: this._id}, {fields: {value : 1, quantity: 1}}).forEach(function(item){
worth += item.totalValue();
});
return worth;
},
weightCarried: function(){
var weight = 0;
Containers.find({charId: this._id, isCarried: true}).forEach(function(container){
weight += container.totalWeight();
});
Items.find({charId: this._id, equipped: false}, {fields: {weight : 1, quantity: 1}}).forEach(function(item){
weight += item.totalWeight();
});
return weight;
},
equipmentValue: function(){
var value = 0;
Items.find({charId: this._id, equipped: true}, {fields: {value : 1, quantity: 1}}).forEach(function(item){
value += item.totalValue();
});
return value;
},
equipmentWeight: function(){
var weight = 0;
Items.find({charId: this._id, equipped: true}, {fields: {weight : 1, quantity: 1}}).forEach(function(item){
weight += item.totalWeight();
});
return weight;
}
});

View File

@@ -5,7 +5,7 @@
<paper-shadow class="card container experiencesCard" hero-id="main" {{detailHero}}>
<div class="containerTop {{colorClass}}" hero-id="toolbar" layout horizontal center {{detailHero}}>
<div class="containerName subhead" flex>Experience</div>
<div class="subhead">{{experience}}XP</div>
<div class="subhead">{{experience}} XP</div>
</div>
<div class="containerMain experiences">
{{#each experiences}}
@@ -23,6 +23,35 @@
</div>
{{/if}}
</paper-shadow>
<paper-shadow class="card container" hero-id="main" {{detailHero}}>
<div class="containerTop {{colorClass}}" hero-id="toolbar" layout horizontal center {{detailHero}}>
<div flex>
<div class="containerName subhead">Levels</div>
</div>
</div>
<div class="containerMain experiences">
<div class="list-subhead" layout horizontal center>
Race
</div>
<div class="itemSlot">
<paper-item class="inventoryItem race" hero-id="main" {{detailHero "race"}} layout horizontal>
{{race}}
</paper-item>
</div>
{{#each classes}}
<div class="list-subhead" layout horizontal center>
{{name}}
</div>
{{#each levels ../_id}}
<div class="itemSlot">
<paper-item class="inventoryItem level" hero-id="main" {{detailHero}} layout horizontal>
Level {{value}}
</paper-item>
</div>
{{/each}}
{{/each}}
</div>
</paper-shadow>
{{#each notes}}
<paper-shadow class="card container" hero-id="main" {{detailHero}}>
<div class="containerTop {{colorClass}} noteTop" hero-id="toolbar" layout horizontal center {{detailHero}}>
@@ -30,9 +59,7 @@
<div class="containerName subhead">{{name}}</div>
</div>
</div>
<div class="containerMain">
{{description}}
</div>
<div class="containerMain preline">{{description}}</div>
</paper-shadow>
{{/each}}
</div>

View File

@@ -19,6 +19,16 @@ Template.journal.helpers({
moreExperiencesOrCollapse: function(){
return (!(Experiences.find({charId: this._id}).count() < Template.instance().experiencesLimit.get())) ||
Template.instance().experiencesLimit.get() > (this.settings && this.settings.experiencesInc || 10);
},
classes: function(){
return Classes.find({charId: this._id}, {sort: {createdAt: 1}});
},
levels: function(charId){
return Levels.find({charId: charId, classId: this._id}, {sort: {value: 1}});
},
race: function(){
var char = Characters.findOne(this._id, {fields: {race: 1}});
return char && char.race;
}
});
@@ -37,6 +47,20 @@ Template.journal.events({
heroId: this._id
});
},
"tap .level": function(event){
GlobalUI.setDetail({
template: "levelDialog",
data: {levelId: this._id, charId: this.charId},
heroId: this._id
});
},
"tap .race": function(event){
GlobalUI.setDetail({
template: "raceDialog",
data: {charId: this._id},
heroId: this._id + "race"
});
},
"tap #addNote": function(event){
var charId = this._id;
Notes.insert({

View File

@@ -0,0 +1,32 @@
<template name="levelDialog">
{{#with level}}
<core-header-panel fit>
<core-toolbar class="grey white-text" hero-id="toolbar" hero>
<paper-icon-button id="backButton" role="button" tabindex="0" icon="arrow-back" aria-label="close"></paper-icon-button>
<div flex>{{name}}</div>
<paper-icon-button id="deleteLevel"
role="button"
tabindex="0"
icon="delete"
aria-label="Delete Level"
noink></paper-icon-button>
</core-toolbar>
<div class="detailContent">
<!--Level-->
<paper-input id="levelValueInput" label="Level" floatinglabel value={{value}}></paper-input>
{{#if effects}}
<hr style="margin: 16px 0 16px 0;">
<div id="effects">
<h2>Effects</h2>
{{#each effects}}
{{>effectEdit}}
{{/each}}
</div>
{{/if}}
<div layout horizontal end-justified>
<paper-button id="addEffectButton" raised>Add Effect</paper-button>
</div>
</div>
</core-header-panel>
{{/with}}
</template>

View File

@@ -0,0 +1,49 @@
Template.levelDialog.rendered = function(){
var self = this;
//update all autogrows after they've been filled
var pata = this.$("paper-autogrow-textarea");
pata.each(function(index, el){
el.update($(el).children().get(0));
})
//update all input fields as well
var input = this.$("paper-input");
input.each(function(index, el){
el.valueChanged();
})
//after the dialog is built, open it
if (!this.alreadyRendered){
Session.set("global.ui.detailShow", true);
this.alreadyRendered = true;
}
}
Template.levelDialog.events({
"tap #backButton": function(){
GlobalUI.closeDetail()
},
"tap #deleteLevel": function(){
Levels.remove(this._id);
GlobalUI.closeDetail()
},
"tap #addEffectButton": function(){
Effects.insert({
charId: this.charId,
sourceId: this._id,
operation: "add",
type: "level"
});
},
"change #levelValueInput": function(event){
var value = event.currentTarget.value;
Levels.update(this._id, {$set: {value: value}});
}
});
Template.levelDialog.helpers({
level: function(){
return Levels.findOne(this.levelId);
},
effects: function(){
return Effects.find({sourceId: this._id, type: "level"});
}
});

View File

@@ -0,0 +1,23 @@
<template name="raceDialog">
<core-header-panel fit>
<core-toolbar class="grey white-text" hero-id="toolbar" hero>
<paper-icon-button id="backButton" role="button" tabindex="0" icon="arrow-back" aria-label="close"></paper-icon-button>
<div flex>{{race}}</div>
</core-toolbar>
<div class="detailContent">
<paper-input id="raceInput" label="Race" floatinglabel value={{race}}></paper-input>
{{#if effects}}
<hr style="margin: 16px 0 16px 0;">
<div id="effects">
<h2>Effects</h2>
{{#each effects}}
{{>effectEdit}}
{{/each}}
</div>
{{/if}}
<div layout horizontal end-justified>
<paper-button id="addEffectButton" raised>Add Effect</paper-button>
</div>
</div>
</core-header-panel>
</template>

View File

@@ -0,0 +1,45 @@
Template.raceDialog.rendered = function(){
var self = this;
//update all autogrows after they've been filled
var pata = this.$("paper-autogrow-textarea");
pata.each(function(index, el){
el.update($(el).children().get(0));
})
//update all input fields as well
var input = this.$("paper-input");
input.each(function(index, el){
el.valueChanged();
})
//after the dialog is built, open it
if (!this.alreadyRendered){
Session.set("global.ui.detailShow", true);
this.alreadyRendered = true;
}
}
Template.raceDialog.events({
"tap #backButton": function(){
GlobalUI.closeDetail()
},
"tap #addEffectButton": function(){
Effects.insert({
charId: this.charId,
operation: "add",
type: "racial"
});
},
"change #raceInput": function(event){
var value = event.currentTarget.value;
Characters.update(this.charId, {$set: {race: value}});
}
});
Template.raceDialog.helpers({
effects: function(){
return Effects.find({charId: this.charId, type: "racial"});
},
race: function(){
var char = Characters.findOne(this.charId, {fields: {race: 1}});
return char && char.race;
}
});

View File

@@ -11,6 +11,7 @@
{{> containerCard characterField "bonds" "Bonds"}}
{{> containerCard characterField "flaws" "Flaws"}}
{{> containerCard characterField "backstory" "Background"}}
{{> containerCard characterField "languages" "Languages"}}
</div>
</div>
</div>

View File

@@ -4,7 +4,8 @@ var colorMap = {
ideals: "g",
bonds: "h",
flaws: "i",
backstory: "j"
backstory: "j",
languages: "k"
}
Template.persona.helpers({

View File

@@ -1,5 +1,5 @@
var spellLevels = [
{ name: "Cantrips", level: 0 },
{ name: "Cantrip", level: 0 },
{ name: "Level 1", level: 1 },
{ name: "Level 2", level: 2 },
{ name: "Level 3", level: 3 },

View File

@@ -31,15 +31,15 @@ Template.spellListDialog.events({
var value = event.currentTarget.value
SpellLists.update(this._id, {$set: {name: value}});
},
"change #spellListSaveDCInput, input #spellListNameInput": function(event){
"change #spellListSaveDCInput, input #spellListSaveDCInput": function(event){
var value = event.currentTarget.value
SpellLists.update(this._id, {$set: {saveDC: value}});
},
"change #spellListAttackBonusInput, input #spellListNameInput": function(event){
"change #spellListAttackBonusInput, input #spellListAttackBonusInput": function(event){
var value = event.currentTarget.value
SpellLists.update(this._id, {$set: {attackBonus: value}});
},
"change #spellListMaxPreparedInput, input #spellListNameInput": function(event){
"change #spellListMaxPreparedInput, input #spellListMaxPreparedInput": function(event){
var value = event.currentTarget.value
SpellLists.update(this._id, {$set: {maxPrepared: value}});
},

View File

@@ -2,27 +2,29 @@
<div fit>
<div id="spells" class="scroll-y" fit>
<div class="containers">
<paper-shadow class="card container" hero-id="main" {{detailHero}} style="order: {{containerOrder}};">
<div class="containerTop {{colorClass}}" layout horizontal center>
<div class="containerName subhead" hero-id="title" flex>Spell Slots</div>
</div>
<div flex class="containerMain">
{{#each levels}}{{#if showSlots ..}}
<div class="itemSlot">
<paper-item class="inventoryItem spellSlot" hero-id="main" {{detailHero}} layout horizontal>
<div class="slotName">
{{name}}
</div>
<div flex layout horizontal center>
{{#each slotBubbles ..}}
<paper-icon-button class="slotBubble" icon={{icon}} disabled={{disabled}}></paper-icon-button>
{{/each}}
</div>
</paper-item>
</div>
{{/if}}{{/each}}
</div>
</paper-shadow>
{{#if hasSlots}}
<paper-shadow class="card container" hero-id="main" {{detailHero}} style="order: {{containerOrder}};">
<div class="containerTop {{colorClass}}" layout horizontal center>
<div class="containerName subhead" hero-id="title" flex>Spell Slots</div>
</div>
<div flex class="containerMain">
{{#each levels}}{{#if showSlots ..}}
<div class="itemSlot">
<paper-item class="inventoryItem spellSlot" hero-id="main" {{detailHero}} layout horizontal>
<div class="slotName">
{{name}}
</div>
<div flex layout horizontal center>
{{#each slotBubbles ..}}
<paper-icon-button class="slotBubble" icon={{icon}} disabled={{disabled}}></paper-icon-button>
{{/each}}
</div>
</paper-item>
</div>
{{/if}}{{/each}}
</div>
</paper-shadow>
{{/if}}
{{#each spellLists}}
<paper-shadow class="card container" hero-id="main" {{detailHero}} style="order: {{order}};">
<div class="containerTop {{colorClass}}" hero-id="toolbar" layout horizontal center {{detailHero}}>

View File

@@ -46,7 +46,7 @@ Template.spells.helpers({
}
if(this.components.material){
components += components? ", M" : "M";
components += "("+this.components.material+")";
components += " ("+this.components.material+")";
}
if(this.components.concentration){
components += " - Requires concentration"
@@ -83,6 +83,14 @@ Template.spells.helpers({
showSlots: function(char){
return this.level && char.attributeBase("level" + this.level +"SpellSlots");
},
hasSlots: function(){
for(var i = 1; i <= 9; i += 1){
if(this.attributeBase("level"+i+"SpellSlots")){
return true;
}
}
return false;
},
slotBubbles: function(char){
var baseSlots = char.attributeBase("level" + this.level +"SpellSlots"),
currentSlots = char.attributeValue("level" + this.level +"SpellSlots"),
@@ -165,7 +173,8 @@ Template.spells.events({
Spells.insert({
name: "New Spell",
charId: this._id,
listId: SpellLists.findOne({charId: this._id})._id
listId: SpellLists.findOne({charId: this._id})._id,
prepared: "prepared"
}, function(error, id){
if(!error){
GlobalUI.setDetail({

View File

@@ -0,0 +1,53 @@
<template name="healthCard">
<paper-shadow class="card container healthCard" hero-id="main" {{detailHero}} layout horizontal wrap>
<div class="green white-text subhead padded leftRound" layout horizontal center>
Hit Points
</div>
<div class="padded" flex layout vertical center-justified style="min-width: 180px;">
<paper-slider id="hitPointSlider"
value={{attributeValue "hitPoints"}}
max={{attributeBase "hitPoints"}}
editable pin
role="slider"
></paper-slider>
{{#if showDeathSave}}
{{#with deathSaveObject}}
<div class="padded" layout vertical center>
<div class="subhead">
Death Saves
</div>
<div layout horizontal>
<div layout vertical center>
<div class="caption">Fail</div>
<div layout horizontal center-justified wrap reverse>
<paper-icon-button class="slotBubble failBubble" icon={{failIcon 1}} disabled={{failDisabled 1}}></paper-icon-button>
<paper-icon-button class="slotBubble failBubble" icon={{failIcon 2}} disabled={{failDisabled 2}}></paper-icon-button>
<paper-icon-button class="slotBubble failBubble" icon={{failIcon 3}} disabled={{failDisabled 3}}></paper-icon-button>
</div>
</div>
<div layout horizontal center center-justified style="min-width: 94px">
{{#if dead}}
DEAD
{{else}}
{{#if stable}}
<paper-button id="stableButton">Stable</paper-button>
{{else}}
<paper-button id="unstableButton">Unstable</paper-button>
{{/if}}
{{/if}}
</div>
<div layout vertical center>
<div class="caption">Pass</div>
<div layout horizontal center-justified wrap>
<paper-icon-button class="slotBubble passBubble" icon={{passIcon 1}} disabled={{passDisabled 1}}></paper-icon-button>
<paper-icon-button class="slotBubble passBubble" icon={{passIcon 2}} disabled={{passDisabled 2}}></paper-icon-button>
<paper-icon-button class="slotBubble passBubble" icon={{passIcon 3}} disabled={{passDisabled 3}}></paper-icon-button>
</div>
</div>
</div>
</div>
{{/with}}
{{/if}}
</div>
</paper-shadow>
</template>

View File

@@ -0,0 +1,61 @@
Template.healthCard.helpers({
showDeathSave: function(){
return this.attributeValue("hitPoints") <= 0;
},
deathSaveObject: function(){
var char = Characters.findOne(this._id, {fields: {deathSave: 1}});
return char && char.deathSave;
},
failIcon: function(num){
console.log("checking ", num, " against fail", this)
if(num <= this.fail) return "radio-button-on";
else return "radio-button-off";
},
passIcon: function(num){
if(num <= this.pass) return "radio-button-on";
else return "radio-button-off";
},
failDisabled: function(num){
return !(num === this.fail || num - 1 === this.fail)
},
passDisabled: function(num){
return !(num === this.pass || num - 1 === this.pass)
},
dead: function(char){
return this.fail >= 3;
}
})
Template.healthCard.events({
"change #hitPointSlider": function(event){
var value = event.currentTarget.value;
var adjustment = value - this.attributeBase("hitPoints");
Characters.update(this._id, {$set: {"hitPoints.adjustment": adjustment}});
},
"tap .failBubble": function(event){
if(event.currentTarget.disabled) return;
var char = Template.parentData();
if(event.currentTarget.icon === "radio-button-off"){
Characters.update(char._id, {$set: {"deathSave.fail": this.fail + 1}});
} else{
Characters.update(char._id, {$set: {"deathSave.fail": this.fail - 1}});
}
},
"tap .passBubble": function(event){
if(event.currentTarget.disabled) return;
var char = Template.parentData();
if(event.currentTarget.icon === "radio-button-off"){
Characters.update(char._id, {$set: {"deathSave.pass": this.pass + 1}});
} else{
Characters.update(char._id, {$set: {"deathSave.pass": this.pass - 1}});
}
},
"tap #stableButton": function(event){
var char = Template.parentData();
Characters.update(char._id, {$set: {"deathSave.stable": false}});
},
"tap #unstableButton": function(event){
var char = Template.parentData();
Characters.update(char._id, {$set: {"deathSave.stable": true}});
}
});

View File

@@ -34,15 +34,9 @@ evaluateString = function(charId, string){
if(!string) return string;
var brackets = /\{[^\{\}]*\}/g;
var result = string.replace(brackets, function(exp){
var exp = exp.replace(/(\{|\})/g, "") //remove brackets
var span = jQuery('<span/>', {
title: exp,
text: evaluate(charId, exp),
class: "calculatedValue"
});
return span.prop('outerHTML');
var exp = exp.replace(/(\{|\})/g, "") //remove curly brackets
return evaluate(charId, exp);
});
//this is going to return HTML, ensure it is santized!
return result;
}

View File

@@ -1,14 +1,19 @@
Meteor.publish("singleCharacter", function(characterId, userId){
//TODO check if this characer can be viewed by this user
return [
Characters.find({_id: characterId}),
Characters.find({_id: characterId}),
Actions.find({charId: characterId}),
Attacks.find({charId: characterId}),
Classes.find({charId: characterId}),
Containers.find({charId: characterId}),
Items.find({charId: characterId}),
Features.find({charId: characterId}),
Effects.find({charId: characterId}),
Experiences.find({charId: characterId}),
Features.find({charId: characterId}),
Items.find({charId: characterId}),
Levels.find({charId: characterId}),
Notes.find({charId: characterId}),
Spells.find({charId: characterId}),
SpellLists.find({charId: characterId}),
Notes.find({charId: characterId}),
Experiences.find({charId: characterId})
];
});