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
hitPoints: {type: Schemas.Attribute},
tempHP: {type: Schemas.Attribute},
experience: {type: Schemas.Attribute},
proficiencyBonus: {type: Schemas.Attribute},
speed: {type: Schemas.Attribute},

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,7 +5,7 @@ Template.healthCard.binding({
"#hitPointSlider": {
max: () => Characters.calculate.attributeBase(currentId() , "hitPoints"),
value: () => Characters.calculate.attributeValue(currentId() , "hitPoints"),
}
},
});
// 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;
if (oldId !== id){
this.find("#hitPointSlider").resetOldValue();
this.find("#temporaryHitPointSlider").resetOldValue();
oldId = id;
}
});
});
Template.healthCard.helpers({
tempHitPoints: function(){
extraHitPoints: function(){
return TemporaryHitPoints.find({charId: this._id});
},
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 used = this.maximum - value;
TemporaryHitPoints.update(this._id, {$set: {"used": used}});
},
"click .deleteTHP": function(event){
"click .deleteEHP": function(event){
TemporaryHitPoints.remove(this._id);
},
"click #addTempHP": function(event){
"click #addExtraHP": function(event){
pushDialogStack({
template: "addTHPDialog",
template: "addEHPDialog",
data: {charId: this._id},
element: event.currentTarget.parentElement,
});

View File

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

View File

@@ -36,3 +36,21 @@ Migrations.add({
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;
},
});