Merge pull request #122 from Dumbgenius/feature-22-temp-hp, requires migration

Feature - TempHP as a character attribute,
This commit is contained in:
Stefan Zermatten
2017-09-07 16:29:55 +02:00
committed by GitHub
11 changed files with 198 additions and 113 deletions

View File

@@ -27,6 +27,7 @@ Schemas.Character = new SimpleSchema({
//stats //stats
hitPoints: {type: Schemas.Attribute}, hitPoints: {type: Schemas.Attribute},
tempHP: {type: Schemas.Attribute},
experience: {type: Schemas.Attribute}, experience: {type: Schemas.Attribute},
proficiencyBonus: {type: Schemas.Attribute}, proficiencyBonus: {type: Schemas.Attribute},
speed: {type: Schemas.Attribute}, speed: {type: Schemas.Attribute},

View File

@@ -7,32 +7,36 @@ var stats = [
{stat: "intelligence", name: "Intelligence", group: "Ability Scores"}, {stat: "intelligence", name: "Intelligence", group: "Ability Scores"},
{stat: "wisdom", name: "Wisdom", group: "Ability Scores"}, {stat: "wisdom", name: "Wisdom", group: "Ability Scores"},
{stat: "charisma", name: "Charisma", 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"}, {stat: "strengthSave", name: "Strength Save", group: "Saving Throws"},
{name: "Constitution Save", stat: "constitutionSave", group: "Saving Throws"}, {stat: "dexteritySave", name: "Dexterity Save", group: "Saving Throws"},
{name: "Intelligence Save", stat: "intelligenceSave", group: "Saving Throws"}, {stat: "constitutionSave", name: "Constitution Save", group: "Saving Throws"},
{name: "Wisdom Save", stat: "wisdomSave", group: "Saving Throws"}, {stat: "intelligenceSave", name: "Intelligence Save", group: "Saving Throws"},
{name: "Charisma Save", stat: "charismaSave", group: "Saving Throws"}, {stat: "wisdomSave", name: "Wisdom Save", group: "Saving Throws"},
{name: "Acrobatics", stat: "acrobatics", group: "Skills"}, {stat: "charismaSave", name: "Charisma Save", group: "Saving Throws"},
{name: "Animal Handling", stat: "animalHandling", group: "Skills"},
{name: "Arcana", stat: "arcana", group: "Skills"}, {stat: "acrobatics", name: "Acrobatics", group: "Skills"},
{name: "Athletics", stat: "athletics", group: "Skills"}, {stat: "animalHandling", name: "Animal Handling", group: "Skills"},
{name: "Deception", stat: "deception", group: "Skills"}, {stat: "arcana", name: "Arcana", group: "Skills"},
{name: "History", stat: "history", group: "Skills"}, {stat: "athletics", name: "Athletics", group: "Skills"},
{name: "Insight", stat: "insight", group: "Skills"}, {stat: "deception", name: "Deception", group: "Skills"},
{name: "Intimidation", stat: "intimidation", group: "Skills"}, {stat: "history", name: "History", group: "Skills"},
{name: "Investigation", stat: "investigation", group: "Skills"}, {stat: "insight", name: "Insight", group: "Skills"},
{name: "Medicine", stat: "medicine", group: "Skills"}, {stat: "intimidation", name: "Intimidation", group: "Skills"},
{name: "Nature", stat: "nature", group: "Skills"}, {stat: "investigation", name: "Investigation", group: "Skills"},
{name: "Perception", stat: "perception", group: "Skills"}, {stat: "medicine", name: "Medicine", group: "Skills"},
{name: "Performance", stat: "performance", group: "Skills"}, {stat: "nature", name: "Nature", group: "Skills"},
{name: "Persuasion", stat: "persuasion", group: "Skills"}, {stat: "perception", name: "Perception", group: "Skills"},
{name: "Religion", stat: "religion", group: "Skills"}, {stat: "performance", name: "Performance", group: "Skills"},
{name: "Sleight of Hand", stat: "sleightOfHand", group: "Skills"}, {stat: "persuasion", name: "Persuasion", group: "Skills"},
{name: "Stealth", stat: "stealth", group: "Skills"}, {stat: "religion", name: "Religion", group: "Skills"},
{name: "Survival", stat: "survival", group: "Skills"}, {stat: "sleightOfHand", name: "Sleight of Hand", group: "Skills"},
{name: "Initiative", stat: "initiative", group: "Skills"}, {stat: "stealth", name: "Stealth", group: "Skills"},
{stat: "survival", name: "Survival", group: "Skills"},
{stat: "initiative", name: "Initiative", group: "Skills"},
{stat: "hitPoints", name: "Hit Points", group: "Stats"}, {stat: "hitPoints", name: "Hit Points", group: "Stats"},
{stat: "tempHP", name: "Temporary Hit Points", group: "Stats"},
{stat: "armor", name: "Armor", group: "Stats"}, {stat: "armor", name: "Armor", group: "Stats"},
{stat: "dexterityArmor", name: "Dexterity Armor Bonus", group: "Stats"}, {stat: "dexterityArmor", name: "Dexterity Armor Bonus", group: "Stats"},
{stat: "speed", name: "Speed", group: "Stats"}, {stat: "speed", name: "Speed", group: "Stats"},
@@ -44,6 +48,7 @@ var stats = [
{stat: "expertiseDice", name: "Expertise Dice", group: "Stats"}, {stat: "expertiseDice", name: "Expertise Dice", group: "Stats"},
{stat: "superiorityDice", name: "Superiority Dice", group: "Stats"}, {stat: "superiorityDice", name: "Superiority Dice", group: "Stats"},
{stat: "carryMultiplier", name: "Carry Capacity Multiplier", group: "Stats"}, {stat: "carryMultiplier", name: "Carry Capacity Multiplier", group: "Stats"},
{stat: "level1SpellSlots", name: "level 1", group: "Spell Slots"}, {stat: "level1SpellSlots", name: "level 1", group: "Spell Slots"},
{stat: "level2SpellSlots", name: "level 2", group: "Spell Slots"}, {stat: "level2SpellSlots", name: "level 2", group: "Spell Slots"},
{stat: "level3SpellSlots", name: "level 3", group: "Spell Slots"}, {stat: "level3SpellSlots", name: "level 3", group: "Spell Slots"},
@@ -53,10 +58,12 @@ var stats = [
{stat: "level7SpellSlots", name: "level 7", group: "Spell Slots"}, {stat: "level7SpellSlots", name: "level 7", group: "Spell Slots"},
{stat: "level8SpellSlots", name: "level 8", group: "Spell Slots"}, {stat: "level8SpellSlots", name: "level 8", group: "Spell Slots"},
{stat: "level9SpellSlots", name: "level 9", group: "Spell Slots"}, {stat: "level9SpellSlots", name: "level 9", group: "Spell Slots"},
{stat: "d6HitDice", name: "d6 Hit Dice", group: "Hit Dice"}, {stat: "d6HitDice", name: "d6 Hit Dice", group: "Hit Dice"},
{stat: "d8HitDice", name: "d8 Hit Dice", group: "Hit Dice"}, {stat: "d8HitDice", name: "d8 Hit Dice", group: "Hit Dice"},
{stat: "d10HitDice", name: "d10 Hit Dice", group: "Hit Dice"}, {stat: "d10HitDice", name: "d10 Hit Dice", group: "Hit Dice"},
{stat: "d12HitDice", name: "d12 Hit Dice", group: "Hit Dice"}, {stat: "d12HitDice", name: "d12 Hit Dice", group: "Hit Dice"},
{stat: "acidMultiplier", name: "Acid", group: "Weakness/Resistance"}, {stat: "acidMultiplier", name: "Acid", group: "Weakness/Resistance"},
{stat: "bludgeoningMultiplier", name: "Bludgeoning", group: "Weakness/Resistance"}, {stat: "bludgeoningMultiplier", name: "Bludgeoning", group: "Weakness/Resistance"},
{stat: "coldMultiplier", name: "Cold", group: "Weakness/Resistance"}, {stat: "coldMultiplier", name: "Cold", group: "Weakness/Resistance"},

View File

@@ -5,12 +5,14 @@ var stats = {
"intelligence":{"name":"Intelligence"}, "intelligence":{"name":"Intelligence"},
"wisdom":{"name":"Wisdom"}, "wisdom":{"name":"Wisdom"},
"charisma":{"name":"Charisma"}, "charisma":{"name":"Charisma"},
"strengthSave":{"name":"Strength Save"}, "strengthSave":{"name":"Strength Save"},
"dexteritySave":{"name":"Dexterity Save"}, "dexteritySave":{"name":"Dexterity Save"},
"constitutionSave":{"name":"Constitution Save"}, "constitutionSave":{"name":"Constitution Save"},
"intelligenceSave":{"name":"Intelligence Save"}, "intelligenceSave":{"name":"Intelligence Save"},
"wisdomSave":{"name":"Wisdom Save"}, "wisdomSave":{"name":"Wisdom Save"},
"charismaSave":{"name":"Charisma Save"}, "charismaSave":{"name":"Charisma Save"},
"acrobatics":{"name":"Acrobatics"}, "acrobatics":{"name":"Acrobatics"},
"animalHandling":{"name":"Animal Handling"}, "animalHandling":{"name":"Animal Handling"},
"arcana":{"name":"Arcana"}, "arcana":{"name":"Arcana"},
@@ -30,7 +32,9 @@ var stats = {
"stealth":{"name":"Stealth"}, "stealth":{"name":"Stealth"},
"survival":{"name":"Survival"}, "survival":{"name":"Survival"},
"initiative":{"name":"Initiative"}, "initiative":{"name":"Initiative"},
"hitPoints":{"name":"Hit Points"}, "hitPoints":{"name":"Hit Points"},
"tempHP":{"name":"Temporary Hit Points"},
"armor":{"name":"Armor"}, "armor":{"name":"Armor"},
"dexterityArmor":{"name":"Dexterity Armor Bonus"}, "dexterityArmor":{"name":"Dexterity Armor Bonus"},
"speed":{"name":"Speed"}, "speed":{"name":"Speed"},
@@ -42,6 +46,7 @@ var stats = {
"expertiseDice":{"name":"Expertise Dice"}, "expertiseDice":{"name":"Expertise Dice"},
"superiorityDice":{"name":"Superiority Dice"}, "superiorityDice":{"name":"Superiority Dice"},
"carryMultiplier": {"name": "Carry Capacity Multiplier"}, "carryMultiplier": {"name": "Carry Capacity Multiplier"},
"level1SpellSlots":{"name":"level 1 Spell Slots"}, "level1SpellSlots":{"name":"level 1 Spell Slots"},
"level2SpellSlots":{"name":"level 2 Spell Slots"}, "level2SpellSlots":{"name":"level 2 Spell Slots"},
"level3SpellSlots":{"name":"level 3 Spell Slots"}, "level3SpellSlots":{"name":"level 3 Spell Slots"},
@@ -51,10 +56,12 @@ var stats = {
"level7SpellSlots":{"name":"level 7 Spell Slots"}, "level7SpellSlots":{"name":"level 7 Spell Slots"},
"level8SpellSlots":{"name":"level 8 Spell Slots"}, "level8SpellSlots":{"name":"level 8 Spell Slots"},
"level9SpellSlots":{"name":"level 9 Spell Slots"}, "level9SpellSlots":{"name":"level 9 Spell Slots"},
"d6HitDice":{"name":"d6 Hit Dice"}, "d6HitDice":{"name":"d6 Hit Dice"},
"d8HitDice":{"name":"d8 Hit Dice"}, "d8HitDice":{"name":"d8 Hit Dice"},
"d10HitDice":{"name":"d10 Hit Dice"}, "d10HitDice":{"name":"d10 Hit Dice"},
"d12HitDice":{"name":"d12 Hit Dice"}, "d12HitDice":{"name":"d12 Hit Dice"},
"acidMultiplier":{"name":"Acid damage", "group": "Weakness/Resistance"}, "acidMultiplier":{"name":"Acid damage", "group": "Weakness/Resistance"},
"bludgeoningMultiplier":{ "bludgeoningMultiplier":{
"name":"Bludgeoning damage", "group": "Weakness/Resistance", "name":"Bludgeoning damage", "group": "Weakness/Resistance",

View File

@@ -1,5 +1,5 @@
<!-- data just needs charId --> <!-- data just needs charId -->
<template name="addTHPDialog"> <template name="addEHPDialog">
<div class="fit layout vertical"> <div class="fit layout vertical">
<app-header-layout has-scrolling-region class="new-character-dialog flex"> <app-header-layout has-scrolling-region class="new-character-dialog flex">
<app-header fixed effects="waterfall"> <app-header fixed effects="waterfall">

View File

@@ -1,8 +1,8 @@
Template.addTHPDialog.onRendered(function(){ Template.addEHPDialog.onRendered(function(){
this.find("#quantityInput").focus(); this.find("#quantityInput").focus();
}); });
Template.addTHPDialog.events({ Template.addEHPDialog.events({
"tap .addButton": function(event, instance){ "tap .addButton": function(event, instance){
popDialogStack(); popDialogStack();
var max = +instance.find("#quantityInput").value; var max = +instance.find("#quantityInput").value;

View File

@@ -3,10 +3,15 @@
margin-right: 8px; margin-right: 8px;
} }
.healthCard .bottom-border {
border-bottom: rgba(0,0,0,0.24) solid 1px;
margin-bottom: 12px;
}
.healthCard #stableButton { .healthCard #stableButton {
color: #b71c1c; color: #b71c1c;
transition: color 0.4s ease; transition: color 0.4s ease;
width: 100% width: 100%;
} }
.healthCard #stableButton:before { .healthCard #stableButton:before {

View File

@@ -5,33 +5,52 @@
Hit Points Hit Points
</div> </div>
<paper-icon-button class="white54" <paper-icon-button class="white54"
id="addTempHP" id="addExtraHP"
icon="add" icon="add"
disabled={{#unless canEditCharacter _id}}true{{/unless}}> disabled={{#unless canEditCharacter _id}}true{{/unless}}>
</paper-icon-button> </paper-icon-button>
</div> </div>
<div class="right flex layout vertical center-justified" style="min-width: 180px;"> <div class="right flex layout vertical center-justified" style="min-width: 180px;">
<!-- main HP slider -->
<div class="layout horizontal"> <div class="layout horizontal">
<paper-diff-slider id="hitPointSlider" <paper-diff-slider id="hitPointSlider"
editable pin editable pin
disabled={{#unless canEditCharacter _id}}true{{/unless}}> disabled={{#unless canEditCharacter _id}}true{{/unless}}>
</paper-diff-slider> </paper-diff-slider>
</div> </div>
{{#each tempHitPoints}} {{#if characterCalculate "attributeBase" _id "tempHP"}}
<div> <!-- main THP slider -->
{{name}} <div class="layout horizontal center {{#if extraHitPoints.count}}bottom-border{{/if}}">
<div class="self-center">
Temporary Hit Points
</div>
<paper-diff-slider id="temporaryHitPointSlider"
class="flex"
editable pin
disabled={{#unless canEditCharacter _id}}true{{/unless}}
max={{characterCalculate "attributeBase" _id "tempHP"}}
value={{characterCalculate "attributeValue" _id "tempHP"}}
>
</paper-diff-slider>
</div> </div>
{{/if}}
{{#each extraHitPoints}}
<div class="layout horizontal center"> <div class="layout horizontal center">
<div class="self-center">
{{name}}
</div>
<div style="height: 40px; width: 40px;"> <div style="height: 40px; width: 40px;">
{{#unless left}} {{#unless left}}
<paper-icon-button class="deleteTHP" icon="delete"></paper-icon-button> <paper-icon-button class="deleteEHP" icon="delete"></paper-icon-button>
{{/unless}} {{/unless}}
</div> </div>
<paper-diff-slider class="tempHitPointSlider flex" <paper-diff-slider class="extraHitPointSlider flex"
max={{maximum}} max={{maximum}}
value={{left}} value={{left}}
editable pin editable pin
></paper-diff-slider> >
</paper-diff-slider>
</div> </div>
{{/each}} {{/each}}
<div class="paper-font-caption"> <div class="paper-font-caption">

View File

@@ -5,7 +5,7 @@ Template.healthCard.binding({
"#hitPointSlider": { "#hitPointSlider": {
max: () => Characters.calculate.attributeBase(currentId() , "hitPoints"), max: () => Characters.calculate.attributeBase(currentId() , "hitPoints"),
value: () => Characters.calculate.attributeValue(currentId() , "hitPoints"), value: () => Characters.calculate.attributeValue(currentId() , "hitPoints"),
} },
}); });
// Reset the old value between characters so that we don't get red health lost // Reset the old value between characters so that we don't get red health lost
@@ -16,13 +16,14 @@ Template.healthCard.onRendered(function(){
const id = Template.currentData()._id; const id = Template.currentData()._id;
if (oldId !== id){ if (oldId !== id){
this.find("#hitPointSlider").resetOldValue(); this.find("#hitPointSlider").resetOldValue();
this.find("#temporaryHitPointSlider").resetOldValue();
oldId = id; oldId = id;
} }
}); });
}); });
Template.healthCard.helpers({ Template.healthCard.helpers({
tempHitPoints: function(){ extraHitPoints: function(){
return TemporaryHitPoints.find({charId: this._id}); return TemporaryHitPoints.find({charId: this._id});
}, },
showDeathSave: function(){ showDeathSave: function(){
@@ -93,17 +94,23 @@ Template.healthCard.events({
}} }}
); );
}, },
"change .tempHitPointSlider": function(event){ "change #temporaryHitPointSlider": function(event){ //this is the actual THP stat
var value = event.currentTarget.value;
var base = Characters.calculate.attributeBase(this._id, "tempHP");
var adjustment = value - base;
Characters.update(this._id, {$set: {"tempHP.adjustment": adjustment}});
},
"change .extraHitPointSlider": function(event){ //this is the extra bars
var value = event.currentTarget.value; var value = event.currentTarget.value;
var used = this.maximum - value; var used = this.maximum - value;
TemporaryHitPoints.update(this._id, {$set: {"used": used}}); TemporaryHitPoints.update(this._id, {$set: {"used": used}});
}, },
"click .deleteTHP": function(event){ "click .deleteEHP": function(event){
TemporaryHitPoints.remove(this._id); TemporaryHitPoints.remove(this._id);
}, },
"click #addTempHP": function(event){ "click #addExtraHP": function(event){
pushDialogStack({ pushDialogStack({
template: "addTHPDialog", template: "addEHPDialog",
data: {charId: this._id}, data: {charId: this._id},
element: event.currentTarget.parentElement, element: event.currentTarget.parentElement,
}); });

View File

@@ -1,71 +1,82 @@
statOrder = { var statsInOrder = [
"strength": 1, "strength",
"dexterity": 2, "dexterity",
"constitution": 3, "constitution",
"intelligence": 4, "intelligence",
"wisdom": 5, "wisdom",
"charisma": 6, "charisma",
"strengthSave": 7,
"dexteritySave": 8, "strengthSave",
"constitutionSave": 9, "dexteritySave",
"intelligenceSave": 10, "constitutionSave",
"wisdomSave": 11, "intelligenceSave",
"charismaSave": 12, "wisdomSave",
"acrobatics": 13, "charismaSave",
"animalHandling": 14,
"arcana": 15, "acrobatics",
"athletics": 16, "animalHandling",
"deception": 17, "arcana",
"history": 18, "athletics",
"insight": 19, "deception",
"intimidation": 20, "history",
"investigation": 21, "insight",
"medicine": 22, "intimidation",
"nature": 23, "investigation",
"perception": 24, "medicine",
"performance": 25, "nature",
"persuasion": 26, "perception",
"religion": 27, "performance",
"sleightOfHand": 28, "persuasion",
"stealth": 29, "religion",
"survival": 30, "sleightOfHand",
"initiative": 31, "stealth",
"hitPoints": 32, "survival",
"armor": 33, "initiative",
"dexterityArmor": 34,
"speed": 35, "hitPoints",
"proficiencyBonus": 36, "armor",
"ki": 37, "dexterityArmor",
"sorceryPoints": 38, "speed",
"rages": 39, "proficiencyBonus",
"rageDamage": 40, "ki",
"expertiseDice": 41, "sorceryPoints",
"superiorityDice": 42, "rages",
"carryMultiplier": 43, "rageDamage",
"level1SpellSlots": 44, "expertiseDice",
"level2SpellSlots": 45, "superiorityDice",
"level3SpellSlots": 46, "carryMultiplier",
"level4SpellSlots": 47,
"level5SpellSlots": 48, "level1SpellSlots",
"level6SpellSlots": 49, "level2SpellSlots",
"level7SpellSlots": 50, "level3SpellSlots",
"level8SpellSlots": 51, "level4SpellSlots",
"level9SpellSlots": 52, "level5SpellSlots",
"d6HitDice": 53, "level6SpellSlots",
"d8HitDice": 54, "level7SpellSlots",
"d10HitDice": 55, "level8SpellSlots",
"d12HitDice": 56, "level9SpellSlots",
"acidMultiplier": 57,
"bludgeoningMultiplier": 58, "d6HitDice",
"coldMultiplier": 59, "d8HitDice",
"fireMultiplier": 60, "d10HitDice",
"forceMultiplier": 61, "d12HitDice",
"lightningMultiplier": 62,
"necroticMultiplier": 63, "acidMultiplier",
"piercingMultiplier": 64, "bludgeoningMultiplier",
"poisonMultiplier": 65, "coldMultiplier",
"psychicMultiplier": 66, "fireMultiplier",
"radiantMultiplier": 67, "forceMultiplier",
"slashingMultiplier": 68, "lightningMultiplier",
"thunderMultiplier": 69, "necroticMultiplier",
}; "piercingMultiplier",
"poisonMultiplier",
"psychicMultiplier",
"radiantMultiplier",
"slashingMultiplier",
"thunderMultiplier",
];
statOrder = {};
_.each(statsInOrder, function(element, index){
statOrder[element] = index;
});

View File

@@ -60,4 +60,14 @@
color: rgba(255,255,255,0.87) !important; color: rgba(255,255,255,0.87) !important;
} }
} }
.healthCard #temporaryHitPointSlider {
--paper-diff-slider-active-color: #009688; /* teal */
--paper-diff-slider-knob-color: #009688;
--paper-diff-slider-pin-color: #009688;
}
.healthCard .extraHitPointSlider {
--paper-diff-slider-active-color: #00BCD4; /* cyan */
--paper-diff-slider-knob-color: #00BCD4;
--paper-diff-slider-pin-color: #00BCD4;
}
</style> </style>

View File

@@ -36,3 +36,21 @@ Migrations.add({
return; return;
}, },
}); });
Migrations.add({
version: 2,
name: "Adds TempHP as a character attribute",
up: function() {
//update characters
Characters.find({}).forEach(function(char){
if (char.tempHP) return;
Characters.update(char._id, {$set: {
"tempHP.adjustment": 0,
"tempHP.reset": "longRest",
}});
});
},
down: function(){
return;
},
});