Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09d1ac9ba3 | ||
|
|
834b9cf384 | ||
|
|
37291b347a | ||
|
|
efdfbeb59e | ||
|
|
a165f9b253 | ||
|
|
4a6fca98bc | ||
|
|
35464128a0 | ||
|
|
398f8a8a2a | ||
|
|
812a1784b2 | ||
|
|
8fa9cd0148 | ||
|
|
0e0662cc9a | ||
|
|
ad4e3f5b20 | ||
|
|
4cd058e1fe | ||
|
|
8f30cee4d3 | ||
|
|
d7f7eb2e6a | ||
|
|
a59cf1162f | ||
|
|
7bc80da99e | ||
|
|
2ddc520bb6 | ||
|
|
d92bb0f4d4 | ||
|
|
abcfe57add | ||
|
|
cdae75beef | ||
|
|
e7bcc2224c | ||
|
|
b591c66dd5 | ||
|
|
402bc0e5ed | ||
|
|
ac772531ee |
@@ -1,13 +1,13 @@
|
|||||||
RPG Docs
|
RPG Docs
|
||||||
========
|
========
|
||||||
|
|
||||||
This is the repo for [DiceCloud](dicecloud.com). The currently deployed version should always be the latest release of the master branch.
|
This is the repo for [DiceCloud](dicecloud.com).
|
||||||
|
|
||||||
Getting started
|
Getting started
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
`git clone https://github.com/ThaumRystra/RPG-Docs RPG-Docs`
|
`git clone https://github.com/ThaumRystra/DiceCloud1 dicecloud`
|
||||||
`cd RPG-Docs`
|
`cd dicecloud`
|
||||||
`cd rpg-docs`
|
`cd rpg-docs`
|
||||||
`bower install`
|
`bower install`
|
||||||
`meteor`
|
`meteor`
|
||||||
|
|||||||
@@ -691,7 +691,7 @@
|
|||||||
"level": 1,
|
"level": 1,
|
||||||
"range": "Self",
|
"range": "Self",
|
||||||
"school": "Divination",
|
"school": "Divination",
|
||||||
"ritual": false,
|
"ritual": true,
|
||||||
"name": "Comprehend Languages",
|
"name": "Comprehend Languages",
|
||||||
"components": {
|
"components": {
|
||||||
"verbal": true,
|
"verbal": true,
|
||||||
@@ -4240,7 +4240,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"castingTime": "1 action",
|
"castingTime": "1 action",
|
||||||
"description": "A creature of your choice that you can see with range perceives everything as ilariously funny and falls into fits of laughter if this spell affects it. The target must succeed on a wisdom saving throw or fall prone, becoming incapacitated and unable to stand up for this duration. A creature with an Intelligence score of 4 or less isn’t affected.\n At the end of each of its turns, and each time it takes damage, the target can make another Wisdom saving throw. The target has an advantage on the saving throw if it’s triggered by damage. On a success, the spell ends.",
|
"description": "A creature of your choice that you can see with range perceives everything as hilariously funny and falls into fits of laughter if this spell affects it. The target must succeed on a wisdom saving throw or fall prone, becoming incapacitated and unable to stand up for this duration. A creature with an Intelligence score of 4 or less isn’t affected.\n At the end of each of its turns, and each time it takes damage, the target can make another Wisdom saving throw. The target has an advantage on the saving throw if it’s triggered by damage. On a success, the spell ends.",
|
||||||
"duration": "Concentration, up to 1 minute",
|
"duration": "Concentration, up to 1 minute",
|
||||||
"level": 1,
|
"level": 1,
|
||||||
"range": "30 feet",
|
"range": "30 feet",
|
||||||
|
|||||||
@@ -47,3 +47,4 @@ ecmascript@0.6.1
|
|||||||
es5-shim@4.6.15
|
es5-shim@4.6.15
|
||||||
differential:vulcanize
|
differential:vulcanize
|
||||||
reactive-dict
|
reactive-dict
|
||||||
|
percolate:synced-cron
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ oauth2@1.1.11
|
|||||||
observe-sequence@1.0.14
|
observe-sequence@1.0.14
|
||||||
ordered-dict@1.0.9
|
ordered-dict@1.0.9
|
||||||
percolate:migrations@0.9.8
|
percolate:migrations@0.9.8
|
||||||
|
percolate:synced-cron@1.3.2
|
||||||
promise@0.8.8
|
promise@0.8.8
|
||||||
raix:eventemitter@0.1.3
|
raix:eventemitter@0.1.3
|
||||||
random@1.0.10
|
random@1.0.10
|
||||||
|
|||||||
@@ -184,6 +184,9 @@ Schemas.Character = new SimpleSchema({
|
|||||||
defaultValue: "whitelist",
|
defaultValue: "whitelist",
|
||||||
allowedValues: ["whitelist", "public"],
|
allowedValues: ["whitelist", "public"],
|
||||||
},
|
},
|
||||||
|
"settings.exportFeatures": {type: Boolean, defaultValue: true},
|
||||||
|
"settings.exportAttacks": {type: Boolean, defaultValue: true},
|
||||||
|
"settings.exportDescription": {type: Boolean, defaultValue: true},
|
||||||
});
|
});
|
||||||
|
|
||||||
Characters.attachSchema(Schemas.Character);
|
Characters.attachSchema(Schemas.Character);
|
||||||
|
|||||||
195
rpg-docs/client/lib/improvedInitiativeJson.js
Normal file
195
rpg-docs/client/lib/improvedInitiativeJson.js
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
improvedInitiativeJson = function(charId, options){
|
||||||
|
options = options || {
|
||||||
|
features: true,
|
||||||
|
attacks: true,
|
||||||
|
description: true,
|
||||||
|
};
|
||||||
|
var char = Characters.findOne(charId);
|
||||||
|
if (!char) return;
|
||||||
|
var baseValue = function(attributeName){
|
||||||
|
return Characters.calculate.attributeBase(charId, attributeName);
|
||||||
|
};
|
||||||
|
var skillMod = function(skillName){
|
||||||
|
return Characters.calculate.skillMod(charId, skillName);
|
||||||
|
};
|
||||||
|
var damageMods = getDamageMods(charId);
|
||||||
|
return JSON.stringify({
|
||||||
|
"Id": char._id,
|
||||||
|
"Name": char.name,
|
||||||
|
"Source": "DiceCloud",
|
||||||
|
"Type": char.race,
|
||||||
|
"HP": {
|
||||||
|
"Value": baseValue("hitPoints"),
|
||||||
|
"Notes": getHitDiceString(charId) || "",
|
||||||
|
},
|
||||||
|
"AC": {
|
||||||
|
"Value": baseValue("armor"),
|
||||||
|
"Notes": getArmorString(charId) || "",
|
||||||
|
},
|
||||||
|
"InitiativeModifier": skillMod("initiative"),
|
||||||
|
"Speed": ["" + baseValue("speed")],
|
||||||
|
"Abilities": {
|
||||||
|
"Str": baseValue("strength"),
|
||||||
|
"Dex": baseValue("dexterity"),
|
||||||
|
"Con": baseValue("constitution"),
|
||||||
|
"Cha": baseValue("charisma"),
|
||||||
|
"Int": baseValue("intelligence"),
|
||||||
|
"Wis": baseValue("wisdom"),
|
||||||
|
},
|
||||||
|
"DamageVulnerabilities": damageMods.vulnerabilities,
|
||||||
|
"DamageResistances": damageMods.resistances,
|
||||||
|
"DamageImmunities": damageMods.immunities,
|
||||||
|
"ConditionImmunities": [],
|
||||||
|
"Saves": [
|
||||||
|
{"Name": "Str", "Modifier": skillMod("strengthSave")},
|
||||||
|
{"Name": "Dex", "Modifier": skillMod("dexteritySave")},
|
||||||
|
{"Name": "Con", "Modifier": skillMod("constitutionSave")},
|
||||||
|
{"Name": "Int", "Modifier": skillMod("intelligenceSave")},
|
||||||
|
{"Name": "Wis", "Modifier": skillMod("wisdomSave")},
|
||||||
|
{"Name": "Cha", "Modifier": skillMod("charismaSave")},
|
||||||
|
],
|
||||||
|
"Skills": getSkills(charId),
|
||||||
|
"Senses": [
|
||||||
|
"passive Perception " +
|
||||||
|
Characters.calculate.passiveSkill(charId, "perception")
|
||||||
|
],
|
||||||
|
"Languages": getLanguages(charId),
|
||||||
|
"Challenge": "",
|
||||||
|
"Traits": options.features ? getTraits(charId) : [],
|
||||||
|
"Actions": options.attacks ? getActions(charId) : [],
|
||||||
|
"Reactions": [],
|
||||||
|
"LegendaryActions": [],
|
||||||
|
"Description": options.description ? char.description : "",
|
||||||
|
"Player": Meteor.user().username,
|
||||||
|
}, null, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
var getHitDiceString = function(charId){
|
||||||
|
var d6 = Characters.calculate.attributeBase(charId, "d6HitDice");
|
||||||
|
var d8 = Characters.calculate.attributeBase(charId, "d8HitDice");
|
||||||
|
var d10 = Characters.calculate.attributeBase(charId, "d10HitDice");
|
||||||
|
var d12 = Characters.calculate.attributeBase(charId, "d12HitDice");
|
||||||
|
var con = Characters.calculate.abilityMod(charId,"constitution");
|
||||||
|
var string = "" +
|
||||||
|
(d6 ? `${d6}d6 + ` : "") +
|
||||||
|
(d8 ? `${d8}d8 + ` : "") +
|
||||||
|
(d10 ? `${d10}d10 + ` : "") +
|
||||||
|
(d12 ? `${d12}d12 + ` : "") +
|
||||||
|
con;
|
||||||
|
}
|
||||||
|
|
||||||
|
var getArmorString = function(charId){
|
||||||
|
var bases = Effects.find({
|
||||||
|
charId: charId,
|
||||||
|
stat: "armor",
|
||||||
|
operation: "base",
|
||||||
|
enabled: true,
|
||||||
|
}).map(e => ({
|
||||||
|
ame: e.name,
|
||||||
|
value: evaluateEffect(charId, e),
|
||||||
|
}));
|
||||||
|
var base = bases.length && _.max(bases, b => b.value).name || "";
|
||||||
|
var effects = Effects.find({
|
||||||
|
charId: charId,
|
||||||
|
stat: "armor",
|
||||||
|
operation: {$ne: "base"},
|
||||||
|
enabled: true,
|
||||||
|
}).map(e => e.name);
|
||||||
|
var strings = base ? [base] : [];
|
||||||
|
strings = strings.concat(effects);
|
||||||
|
return strings.join(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
var getDamageMods = function(charId){
|
||||||
|
// jscs:disable maximumLineLength
|
||||||
|
var multipliers = [
|
||||||
|
{name: "Acid", value: Characters.calculate.attributeValue(charId, "acidMultiplier")},
|
||||||
|
{name: "Bludgeoning", value: Characters.calculate.attributeValue(charId, "bludgeoningMultiplier")},
|
||||||
|
{name: "Cold", value: Characters.calculate.attributeValue(charId, "coldMultiplier")},
|
||||||
|
{name: "Fire", value: Characters.calculate.attributeValue(charId, "fireMultiplier")},
|
||||||
|
{name: "Force", value: Characters.calculate.attributeValue(charId, "forceMultiplier")},
|
||||||
|
{name: "Lightning", value: Characters.calculate.attributeValue(charId, "lightningMultiplier")},
|
||||||
|
{name: "Necrotic", value: Characters.calculate.attributeValue(charId, "necroticMultiplier")},
|
||||||
|
{name: "Piercing", value: Characters.calculate.attributeValue(charId, "piercingMultiplier")},
|
||||||
|
{name: "Poison", value: Characters.calculate.attributeValue(charId, "poisonMultiplier")},
|
||||||
|
{name: "Psychic", value: Characters.calculate.attributeValue(charId, "psychicMultiplier")},
|
||||||
|
{name: "Radiant", value: Characters.calculate.attributeValue(charId, "radiantMultiplier")},
|
||||||
|
{name: "Slashing", value: Characters.calculate.attributeValue(charId, "slashingMultiplier")},
|
||||||
|
{name: "Thunder", value: Characters.calculate.attributeValue(charId, "thunderMultiplier")},
|
||||||
|
];
|
||||||
|
// jscs:enable maximumLineLength
|
||||||
|
multipliers = _.groupBy(multipliers, "value");
|
||||||
|
var names = o => o.name;
|
||||||
|
return {
|
||||||
|
"immunities": _.map(multipliers["0"], names),
|
||||||
|
"resistances": _.map(multipliers["0.5"], names),
|
||||||
|
"vulnerabilities": _.map(multipliers["2"], names),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var getSkills = function(charId){
|
||||||
|
var allSkills = [
|
||||||
|
{name: "acrobatics", attribute: "dexterity"},
|
||||||
|
{name: "animalHandling", attribute: "wisdom"},
|
||||||
|
{name: "arcana", attribute: "intelligence"},
|
||||||
|
{name: "athletics", attribute: "strength"},
|
||||||
|
{name: "deception", attribute: "charisma"},
|
||||||
|
{name: "history", attribute: "intelligence"},
|
||||||
|
{name: "insight", attribute: "wisdom"},
|
||||||
|
{name: "intimidation", attribute: "charisma"},
|
||||||
|
{name: "investigation", attribute: "intelligence"},
|
||||||
|
{name: "medicine", attribute: "wisdom"},
|
||||||
|
{name: "nature", attribute: "intelligence"},
|
||||||
|
{name: "perception", attribute: "wisdom"},
|
||||||
|
{name: "performance", attribute: "charisma"},
|
||||||
|
{name: "persuasion", attribute: "charisma"},
|
||||||
|
{name: "religion", attribute: "intelligence"},
|
||||||
|
{name: "sleightOfHand", attribute: "dexterity"},
|
||||||
|
{name: "stealth", attribute: "dexterity"},
|
||||||
|
{name: "survival", attribute: "wisdom"},
|
||||||
|
];
|
||||||
|
var skills = [];
|
||||||
|
_.each(allSkills, skill => {
|
||||||
|
var value = Characters.calculate.skillMod(charId, skill.name);
|
||||||
|
var mod = Characters.calculate.abilityMod(charId, skill.attribute);
|
||||||
|
if (value !== mod){
|
||||||
|
skills.push({Name: skill.name, Modifier: value});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return skills;
|
||||||
|
};
|
||||||
|
|
||||||
|
var getLanguages = function(charId){
|
||||||
|
return Proficiencies.find({
|
||||||
|
charId,
|
||||||
|
enabled: true,
|
||||||
|
type: "language",
|
||||||
|
}).map(l => l.name);
|
||||||
|
};
|
||||||
|
|
||||||
|
var getTraits = function(charId){
|
||||||
|
return Features.find(
|
||||||
|
{charId: charId},
|
||||||
|
{sort: {color: 1, name: 1}}
|
||||||
|
).map(f => ({
|
||||||
|
Name: f.name,
|
||||||
|
// evaluateShortString helper
|
||||||
|
Content: evaluateString(
|
||||||
|
charId, f.description && f.description.split(/^( *[-*_]){3,} *(?:\n+|$)/m)[0]
|
||||||
|
) || "",
|
||||||
|
Usage: "",
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
var getActions = function(charId){
|
||||||
|
return Attacks.find(
|
||||||
|
{charId, enabled: true},
|
||||||
|
{sort: {color: 1, name: 1}}
|
||||||
|
).map(a => ({
|
||||||
|
Name: a.name,
|
||||||
|
Content: `+${evaluate(charId, a.attackBonus)} to hit, ` +
|
||||||
|
`${evaluateString(charId, a.damage)} ${a.damageType} damage, ` +
|
||||||
|
`${a.details}`,
|
||||||
|
Usage: "",
|
||||||
|
}));
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
column-gap: 0px;
|
column-gap: 0px;
|
||||||
column-width: 304px;
|
column-width: 304px;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
|
transform: translateZ(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.column-container.thin-columns {
|
.column-container.thin-columns {
|
||||||
@@ -22,6 +23,7 @@
|
|||||||
.card {
|
.card {
|
||||||
background: white;
|
background: white;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
|
position: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card .top {
|
.card .top {
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
body .paper-font-display4, body .paper-font-display3, body .paper-font-title, body .paper-font-caption{
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
.white-text {
|
.white-text {
|
||||||
color: #dedede;
|
color: #dedede;
|
||||||
color: rgba(255,255,255,0.87);
|
color: rgba(255,255,255,0.87);
|
||||||
|
|||||||
@@ -24,6 +24,10 @@
|
|||||||
<iron-icon icon="settings" item-icon></iron-icon>
|
<iron-icon icon="settings" item-icon></iron-icon>
|
||||||
Settings
|
Settings
|
||||||
</paper-icon-item>
|
</paper-icon-item>
|
||||||
|
<paper-icon-item id="characterExport">
|
||||||
|
<iron-icon icon="content-copy" item-icon></iron-icon>
|
||||||
|
Export to Improved Initiative
|
||||||
|
</paper-icon-item>
|
||||||
</paper-menu>
|
</paper-menu>
|
||||||
</paper-menu-button>
|
</paper-menu-button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|||||||
@@ -203,4 +203,11 @@ Template.characterSheet.events({
|
|||||||
element: event.currentTarget.parentElement.parentElement,
|
element: event.currentTarget.parentElement.parentElement,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
"click #characterExport": function(event, instance){
|
||||||
|
pushDialogStack({
|
||||||
|
data: this,
|
||||||
|
template: "exportDialog",
|
||||||
|
element: event.currentTarget.parentElement.parentElement,
|
||||||
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,3 +13,13 @@
|
|||||||
.effectEdit .deleteEffect {
|
.effectEdit .deleteEffect {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.effectEdit .effect-table-view {
|
||||||
|
align-self: center;
|
||||||
|
color: #757575;
|
||||||
|
color: rgba(0,0,0,0.54);
|
||||||
|
}
|
||||||
|
|
||||||
|
.effectEdit .iron-selected {
|
||||||
|
color: #ad2a1f;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,45 +1,53 @@
|
|||||||
<template name="effectEdit">
|
<template name="effectEdit">
|
||||||
<div class="effectEdit layout horizontal center">
|
{{#with effect}}
|
||||||
<paper-dropdown-menu label="Stat" class="statDropDown" dynamic-align>
|
{{#baseEditDialog hideColor=true title="Effect"}}
|
||||||
<dicecloud-selector class="statMenu dropdown-content" selected={{stat}} selectable="paper-item">
|
<div class="fit layout vertical effectEdit" style="padding: 24px;">
|
||||||
{{#each statGroups}}
|
<table class="paper-font-display1 effect-table-view">
|
||||||
<div style="font-weight: bold; margin-top: 16px; padding-left: 8px;">
|
<tr>
|
||||||
{{this}}
|
{{> effectView}}
|
||||||
</div>
|
</tr>
|
||||||
{{#each stats}}
|
</table>
|
||||||
<paper-item name={{stat}}>{{name}}</paper-item>
|
<hr class="vertMargin" style="width: 100%;">
|
||||||
{{/each}}
|
{{#if showEffectValueInput}}
|
||||||
{{/each}}
|
<paper-input class="effectValueInput"
|
||||||
</dicecloud-selector>
|
label="Value"
|
||||||
</paper-dropdown-menu>
|
floatinglabel
|
||||||
{{#if operations}}
|
value={{effectValue}}>
|
||||||
<paper-dropdown-menu class="operationDropDown" label="Operation" dynamic-align>
|
</paper-input>
|
||||||
<dicecloud-selector class="dropdown-content operationMenu" selected={{operation}}>
|
{{else}}
|
||||||
{{#each operations}}
|
<div style="height: 62px;"></div>
|
||||||
<paper-item name={{operation}}>{{name}}</paper-item>
|
{{/if}}
|
||||||
|
<div class="effectEdit layout horizontal flex">
|
||||||
|
<dicecloud-selector class="statMenu flex" selected={{stat}} selectable="paper-item" style="height: 100%; overflow-y: auto;">
|
||||||
|
{{#each statGroups}}
|
||||||
|
<div style="font-weight: bold; margin-top: 16px; padding-left: 8px;">
|
||||||
|
{{this}}
|
||||||
|
</div>
|
||||||
|
{{#each stats}}
|
||||||
|
<paper-item name={{stat}}>{{name}}</paper-item>
|
||||||
|
{{/each}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</dicecloud-selector>
|
</dicecloud-selector>
|
||||||
</paper-dropdown-menu>
|
{{#if operations}}
|
||||||
{{/if}}
|
<dicecloud-selector class="operationMenu flex" selected={{operation}} style="height: 100%; overflow-y: auto;">
|
||||||
{{#if effectValueTemplate}}
|
{{#each operations}}
|
||||||
{{> Template.dynamic template=effectValueTemplate}}
|
<paper-item name={{operation}}>{{name}}</paper-item>
|
||||||
{{else}}
|
{{/each}}
|
||||||
<div class="flex"></div>
|
</dicecloud-selector>
|
||||||
{{/if}}
|
{{else}} {{#if showMultiplierOperations}}
|
||||||
<paper-icon-button class="deleteEffect"
|
<dicecloud-selector class="multiplierMenu flex"
|
||||||
icon="delete">
|
selected={{value}}>
|
||||||
</paper-icon-button>
|
<paper-item name="0.5">Resistance</paper-item>
|
||||||
<br>
|
<paper-item name="2">Vulnerability</paper-item>
|
||||||
</div>
|
<paper-item name="0">Immunity</paper-item>
|
||||||
</template>
|
</dicecloud-selector>
|
||||||
|
{{else}}
|
||||||
<template name="regularEffectValue">
|
<div class="flex" style="height: 100%;"></div>
|
||||||
<paper-input class="effectValueInput flex"
|
{{/if}} {{/if}}
|
||||||
label="Value"
|
</div>
|
||||||
floatinglabel
|
</div>
|
||||||
value={{effectValue}}
|
{{/baseEditDialog}}
|
||||||
style="flex-basis: 100px;">
|
{{/with}}
|
||||||
</paper-input>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template name="multiplierEffectValue">
|
<template name="multiplierEffectValue">
|
||||||
@@ -51,5 +59,4 @@
|
|||||||
<paper-item name="0">Immunity</paper-item>
|
<paper-item name="0">Immunity</paper-item>
|
||||||
</dicecloud-selector>
|
</dicecloud-selector>
|
||||||
</paper-dropdown-menu>
|
</paper-dropdown-menu>
|
||||||
<div class="flex"></div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -97,7 +97,19 @@ var skillOperations = [
|
|||||||
{name: "Conditional Benefit", operation: "conditional"},
|
{name: "Conditional Benefit", operation: "conditional"},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
Template.effectEdit.onRendered(function(){
|
||||||
|
_.defer(() => {
|
||||||
|
const statElement = this.find(".statMenu .iron-selected");
|
||||||
|
statElement && statElement.scrollIntoView();
|
||||||
|
const opElement = this.find(".operationMenu .iron-selected");
|
||||||
|
opElement && opElement.scrollIntoView();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
Template.effectEdit.helpers({
|
Template.effectEdit.helpers({
|
||||||
|
effect: function(){
|
||||||
|
return Effects.findOne(this.id);
|
||||||
|
},
|
||||||
statGroups: function(){
|
statGroups: function(){
|
||||||
return statGroupNames;
|
return statGroupNames;
|
||||||
},
|
},
|
||||||
@@ -115,46 +127,77 @@ Template.effectEdit.helpers({
|
|||||||
return attributeOperations;
|
return attributeOperations;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
effectValueTemplate: function(){
|
showMultiplierOperations: function(){
|
||||||
//resistance/vulnerability template
|
var stat = statsDict[this.stat];
|
||||||
|
return stat && stat.group === "Weakness/Resistance"
|
||||||
|
},
|
||||||
|
showEffectValueInput: function(){
|
||||||
var stat = statsDict[this.stat];
|
var stat = statsDict[this.stat];
|
||||||
var group = stat && stat.group;
|
var group = stat && stat.group;
|
||||||
if (group === "Weakness/Resistance") return "multiplierEffectValue";
|
if (
|
||||||
|
group === "Weakness/Resistance"
|
||||||
|
) return false;
|
||||||
var op = this.operation;
|
var op = this.operation;
|
||||||
if (!op) return null;
|
if (
|
||||||
//operations that don't need templates
|
!op ||
|
||||||
if (op === "advantage" || op === "disadvantage" || op === "fail") return null;
|
op === "advantage" ||
|
||||||
|
op === "disadvantage" ||
|
||||||
//default template
|
op === "fail"
|
||||||
return "regularEffectValue";
|
) return false;
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
});
|
|
||||||
|
|
||||||
Template.regularEffectValue.helpers({
|
|
||||||
effectValue: function(){
|
effectValue: function(){
|
||||||
return this.calculation || this.value;
|
return this.calculation || this.value;
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Template.effectEdit.events({
|
Template.effectEdit.events({
|
||||||
"click .deleteEffect": function(event){
|
"click #deleteButton": function(event, instance){
|
||||||
Effects.softRemoveNode(this._id);
|
Effects.softRemoveNode(instance.data.id);
|
||||||
GlobalUI.deletedToast(this._id, "Effects", "Effect");
|
GlobalUI.deletedToast(instance.data.id, "Effects", "Effect");
|
||||||
|
popDialogStack();
|
||||||
},
|
},
|
||||||
"iron-select .statDropDown": function(event){
|
"iron-select .statMenu": function(event){
|
||||||
var detail = event.originalEvent.detail;
|
var detail = event.originalEvent.detail;
|
||||||
var statName = detail.item.getAttribute("name");
|
var statName = detail.item.getAttribute("name");
|
||||||
if (statName == this.stat) return;
|
if (statName == this.stat) return;
|
||||||
Effects.update(this._id, {$set: {stat: statName}});
|
var setter = {stat: statName};
|
||||||
|
var group = Blaze.getData(detail.item).group;
|
||||||
|
var effect = Effects.findOne(this._id);
|
||||||
|
if (group === "Saving Throws" || group === "Skills"){
|
||||||
|
// Skills must have a valid skill operation
|
||||||
|
if (!_.contains(
|
||||||
|
_.map(skillOperations, ao => ao.operation),
|
||||||
|
effect.operation
|
||||||
|
)){
|
||||||
|
setter.operation = "add";
|
||||||
|
}
|
||||||
|
} else if (group !== "Weakness/Resistance"){
|
||||||
|
// Attributes must have a valid attribute operation
|
||||||
|
if (!_.contains(
|
||||||
|
_.map(attributeOperations, ao => ao.operation),
|
||||||
|
effect.operation
|
||||||
|
)){
|
||||||
|
setter.operation = "base";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Weakness/Resistance must have a mul operation and value
|
||||||
|
if (effect.operation !== "mul"){
|
||||||
|
setter.operation = "mul";
|
||||||
|
}
|
||||||
|
if (!_.contains([0, 0.5, 2], effect.value)){
|
||||||
|
setter.value = 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Effects.update(this._id, {$set: setter});
|
||||||
},
|
},
|
||||||
"iron-select .operationDropDown": function(event){
|
"iron-select .operationMenu": function(event){
|
||||||
var detail = event.originalEvent.detail;
|
var detail = event.originalEvent.detail;
|
||||||
var opName = detail.item.getAttribute("name");
|
var opName = detail.item.getAttribute("name");
|
||||||
if (opName == this.operation) return;
|
if (opName == this.operation) return;
|
||||||
Effects.update(this._id, {$set: {operation: opName}});
|
Effects.update(this._id, {$set: {operation: opName}});
|
||||||
},
|
},
|
||||||
"iron-select .damageMultiplierDropDown": function(event){
|
"iron-select .multiplierMenu": function(event){
|
||||||
var detail = event.originalEvent.detail;
|
var detail = event.originalEvent.detail;
|
||||||
var value = +detail.item.getAttribute("name");
|
var value = +detail.item.getAttribute("name");
|
||||||
if (value == this.value) return;
|
if (value == this.value) return;
|
||||||
@@ -164,15 +207,25 @@ Template.effectEdit.events({
|
|||||||
operation: "mul",
|
operation: "mul",
|
||||||
}});
|
}});
|
||||||
},
|
},
|
||||||
"change .effectValueInput": function(event){
|
"change .effectValueInput, input .effectValueInput":
|
||||||
|
_.debounce(function(event){
|
||||||
var value = event.currentTarget.value;
|
var value = event.currentTarget.value;
|
||||||
var numValue = +value;
|
var numValue = value === "" ? NaN : +value;
|
||||||
if (_.isFinite(numValue)){
|
if (_.isFinite(numValue)){
|
||||||
if (this.value === numValue) return;
|
if (this.value === numValue) return;
|
||||||
Effects.update(this._id, {$set: {value: numValue, calculation: ""}});
|
Effects.update(this._id, {
|
||||||
|
$set: {value: numValue},
|
||||||
|
$unset: {calculation: ""},
|
||||||
|
});
|
||||||
} else if (_.isString(value)){
|
} else if (_.isString(value)){
|
||||||
if (this.calculation === value) return;
|
if (this.calculation === value) return;
|
||||||
Effects.update(this._id, {$set: {value: "", calculation: value}});
|
Effects.update(this._id, {
|
||||||
|
$set: {calculation: value},
|
||||||
|
$unset: {value: ""},
|
||||||
|
}, {
|
||||||
|
removeEmptyStrings: false,
|
||||||
|
trimStrings: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
}, 400),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
<template name="effectView">
|
<template name="effectView">
|
||||||
<tr>
|
<td>{{statName}}</td>
|
||||||
<td>{{statName}}</td>
|
<td>{{operationName}}{{statValue}}</td>
|
||||||
<td>{{operationName}}{{statValue}}</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
//TODO add dexterity armor
|
|
||||||
var stats = {
|
var stats = {
|
||||||
"strength":{"name":"Strength"},
|
"strength":{"name":"Strength"},
|
||||||
"dexterity":{"name":"Dexterity"},
|
"dexterity":{"name":"Dexterity"},
|
||||||
@@ -131,8 +130,10 @@ Template.effectView.helpers({
|
|||||||
return stats[this.stat] && stats[this.stat].name || "No Stat";
|
return stats[this.stat] && stats[this.stat].name || "No Stat";
|
||||||
},
|
},
|
||||||
operationName: function(){
|
operationName: function(){
|
||||||
if (this.operation === "proficiency" ||
|
if (
|
||||||
this.operation === "conditional") return null;
|
this.operation === "proficiency" ||
|
||||||
|
this.operation === "conditional"
|
||||||
|
) return null;
|
||||||
if (stats[this.stat] && stats[this.stat].group === "Weakness/Resistance")
|
if (stats[this.stat] && stats[this.stat].group === "Weakness/Resistance")
|
||||||
return null;
|
return null;
|
||||||
if (this.operation === "add" && evaluateEffect(this.charId, this) < 0)
|
if (this.operation === "add" && evaluateEffect(this.charId, this) < 0)
|
||||||
@@ -141,9 +142,11 @@ Template.effectView.helpers({
|
|||||||
operations[this.operation].name || "No Operation";
|
operations[this.operation].name || "No Operation";
|
||||||
},
|
},
|
||||||
statValue: function(){
|
statValue: function(){
|
||||||
if (this.operation === "advantage" ||
|
if (
|
||||||
this.operation === "disadvantage" ||
|
this.operation === "advantage" ||
|
||||||
this.operation === "fail"){
|
this.operation === "disadvantage" ||
|
||||||
|
this.operation === "fail"
|
||||||
|
){
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (this.operation === "proficiency"){
|
if (this.operation === "proficiency"){
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
.effectsEditList .effect {
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
@@ -1,11 +1,19 @@
|
|||||||
<!--needs to be given charId, parentId and parentCollection-->
|
<!--needs to be given charId, parentId and parentCollection-->
|
||||||
<template name="effectsEditList">
|
<template name="effectsEditList">
|
||||||
{{#if effects.count}}
|
{{#if effects.length}}
|
||||||
<div id="effects">
|
<div class="effectsEditList">
|
||||||
<div class="paper-font-title">Effects</div>
|
<div class="paper-font-title">Effects</div>
|
||||||
{{#each effects}}
|
<table class="wideTable" style="width: 100%;">
|
||||||
{{>effectEdit}}
|
{{#each effects}}
|
||||||
{{/each}}
|
<tr class="effect" data-id={{_id}}>
|
||||||
|
{{>effectView}}
|
||||||
|
<td>
|
||||||
|
<paper-icon-button class="edit-effect" icon="create">
|
||||||
|
</paper-icon-button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<paper-button id="addEffectButton" class="red-button" raised>Add Effect</paper-button>
|
<paper-button id="addEffectButton" class="red-button" raised>Add Effect</paper-button>
|
||||||
|
|||||||
@@ -8,17 +8,17 @@ Template.effectsEditList.helpers({
|
|||||||
if (this.parentGroup){
|
if (this.parentGroup){
|
||||||
selector["parent.group"] = this.parentGroup;
|
selector["parent.group"] = this.parentGroup;
|
||||||
}
|
}
|
||||||
var cursor = Effects.find(selector);
|
var effects = Effects.find(selector).fetch();
|
||||||
return cursor;
|
return _.sortBy(effects, effect => statOrder[effect.stat] || 999);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Template.effectsEditList.events({
|
Template.effectsEditList.events({
|
||||||
"tap #addEffectButton": function(){
|
"tap #addEffectButton": function(event, instance){
|
||||||
if (!_.isBoolean(this.enabled)) {
|
if (!_.isBoolean(this.enabled)) {
|
||||||
this.enabled = true;
|
this.enabled = true;
|
||||||
}
|
}
|
||||||
Effects.insert({
|
const effectId = Effects.insert({
|
||||||
name: this.name,
|
name: this.name,
|
||||||
charId: this.charId,
|
charId: this.charId,
|
||||||
parent: {
|
parent: {
|
||||||
@@ -29,5 +29,18 @@ Template.effectsEditList.events({
|
|||||||
operation: "add",
|
operation: "add",
|
||||||
enabled: this.enabled,
|
enabled: this.enabled,
|
||||||
});
|
});
|
||||||
|
pushDialogStack({
|
||||||
|
template: "effectEdit",
|
||||||
|
data: {id: effectId},
|
||||||
|
element: event.currentTarget,
|
||||||
|
returnElement: instance.find(`tr.effect[data-id='${effectId}']`),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
"tap .edit-effect": function(event, template){
|
||||||
|
pushDialogStack({
|
||||||
|
template: "effectEdit",
|
||||||
|
data: {id: this._id},
|
||||||
|
element: event.currentTarget.parentElement.parentElement,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<!--needs to be given charId, (parentId or stat) and type-->
|
<!--needs to be given charId, (parentId or stat) and type-->
|
||||||
<template name="effectsViewList">
|
<template name="effectsViewList">
|
||||||
{{#if effects.count}}
|
{{#if effects.length}}
|
||||||
<div class="effects">
|
<div class="effects">
|
||||||
<div class="spaceAfter paper-font-title">Effects</div>
|
<div class="spaceAfter paper-font-title">Effects</div>
|
||||||
<table class="wideTable">
|
<table class="wideTable">
|
||||||
{{#each effects}}
|
{{#each effects}}
|
||||||
{{>effectView}}
|
<tr>{{>effectView}}</tr>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,11 +2,14 @@ Template.effectsViewList.helpers({
|
|||||||
effects: function(){
|
effects: function(){
|
||||||
var selector = {
|
var selector = {
|
||||||
"parent.id": this.parentId,
|
"parent.id": this.parentId,
|
||||||
"charId": this.charId
|
"charId": this.charId,
|
||||||
};
|
};
|
||||||
if (this.parentGroup){
|
if (this.parentGroup){
|
||||||
selector["parent.group"] = this.parentGroup;
|
selector["parent.group"] = this.parentGroup;
|
||||||
}
|
}
|
||||||
return Effects.find(selector, {fields: {parent: 0}});
|
let effects = Effects.find(selector, {
|
||||||
|
fields: {parent: 0},
|
||||||
|
}).fetch();
|
||||||
|
return _.sortBy(effects, effect => statOrder[effect.stat] || 999);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
.exportDialog .iiexport {
|
||||||
|
overflow-y: auto;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<template name="exportDialog">
|
||||||
|
<div class="exportDialog fit layout vertical">
|
||||||
|
{{#with character}}
|
||||||
|
<app-header fixed effects="waterfall">
|
||||||
|
<app-toolbar>
|
||||||
|
<div main-title>Export Character to Improved Initiative</div>
|
||||||
|
</app-toolbar>
|
||||||
|
</app-header>
|
||||||
|
<div class="form flex layout vertical">
|
||||||
|
<paper-toggle-button id="exportFeatures" checked={{settings.exportFeatures}}>
|
||||||
|
Features
|
||||||
|
</paper-toggle-button>
|
||||||
|
<paper-toggle-button id="exportAttacks" checked={{settings.exportAttacks}}>
|
||||||
|
Attacks
|
||||||
|
</paper-toggle-button>
|
||||||
|
<paper-toggle-button id="exportDescription" checked={{settings.exportDescription}}>
|
||||||
|
Description
|
||||||
|
</paper-toggle-button>
|
||||||
|
<div class="paper-font-title padded">JSON</div>
|
||||||
|
<textarea class="flex iiexport">{{improvedInitiativeJson}}</textarea>
|
||||||
|
<paper-button id="copyExportButton" class="red-button" raised>
|
||||||
|
<iron-icon icon="content-copy"></iron-icon>
|
||||||
|
Copy to Clipboard
|
||||||
|
</paper-button>
|
||||||
|
</div>
|
||||||
|
<div class="buttons layout horizontal end-justified">
|
||||||
|
<paper-button class="doneButton"> Done </paper-button>
|
||||||
|
</div>
|
||||||
|
{{/with}}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
Template.exportDialog.helpers({
|
||||||
|
character: function(){
|
||||||
|
return Characters.findOne(this._id);
|
||||||
|
},
|
||||||
|
improvedInitiativeJson: function(){
|
||||||
|
var options = {
|
||||||
|
features: this.settings.exportFeatures,
|
||||||
|
attacks: this.settings.exportAttacks,
|
||||||
|
description: this.settings.exportDescription,
|
||||||
|
}
|
||||||
|
return improvedInitiativeJson(this._id, options);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.exportDialog.events({
|
||||||
|
"change #exportFeatures": function(event, template){
|
||||||
|
Characters.update(this._id, {$set: {
|
||||||
|
"settings.exportFeatures": event.target.checked,
|
||||||
|
}});
|
||||||
|
},
|
||||||
|
"change #exportAttacks": function(event, template){
|
||||||
|
Characters.update(this._id, {$set: {
|
||||||
|
"settings.exportAttacks": event.target.checked,
|
||||||
|
}});
|
||||||
|
},
|
||||||
|
"change #exportDescription": function(event, template){
|
||||||
|
Characters.update(this._id, {$set: {
|
||||||
|
"settings.exportDescription": event.target.checked,
|
||||||
|
}});
|
||||||
|
},
|
||||||
|
"click #copyExportButton": function(event, template){
|
||||||
|
var copyTextarea = template.find(".iiexport");
|
||||||
|
copyTextarea.select();
|
||||||
|
var msg;
|
||||||
|
try {
|
||||||
|
var successful = document.execCommand("copy");
|
||||||
|
var msg = successful ? "JSON copied to clipboard" : "Unable to copy JSON";
|
||||||
|
} catch (err) {
|
||||||
|
msg = "Unable to copy JSON";
|
||||||
|
} finally {
|
||||||
|
clearSelection();
|
||||||
|
GlobalUI.toast(msg);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"click .doneButton": function(event, instance){
|
||||||
|
popDialogStack();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
var clearSelection = function(){
|
||||||
|
if (window.getSelection) {
|
||||||
|
if (window.getSelection().empty) { // Chrome
|
||||||
|
window.getSelection().empty();
|
||||||
|
} else if (window.getSelection().removeAllRanges) { // Firefox
|
||||||
|
window.getSelection().removeAllRanges();
|
||||||
|
}
|
||||||
|
} else if (document.selection) { // IE?
|
||||||
|
document.selection.empty();
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -78,6 +78,20 @@
|
|||||||
margin-left: 16px;
|
margin-left: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.spell.item > div > div {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spell.item > div {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spell.item iron-icon {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.spellLevel {
|
.spellLevel {
|
||||||
-webkit-backface-visibility: hidden;
|
-webkit-backface-visibility: hidden;
|
||||||
-webkit-transform: translateX(0);
|
-webkit-transform: translateX(0);
|
||||||
|
|||||||
@@ -258,6 +258,7 @@ Template.spells.events({
|
|||||||
}
|
}
|
||||||
// Make the library spell into a regular spell
|
// Make the library spell into a regular spell
|
||||||
let spell = _.omit(result, "library", "attacks", "effects");
|
let spell = _.omit(result, "library", "attacks", "effects");
|
||||||
|
spell._id = spellId;
|
||||||
spell.charId = charId;
|
spell.charId = charId;
|
||||||
spell.parent = {
|
spell.parent = {
|
||||||
id: listId,
|
id: listId,
|
||||||
|
|||||||
@@ -28,8 +28,8 @@
|
|||||||
{{/unless}}
|
{{/unless}}
|
||||||
</div>
|
</div>
|
||||||
<paper-diff-slider class="tempHitPointSlider flex"
|
<paper-diff-slider class="tempHitPointSlider flex"
|
||||||
value={{left}}
|
|
||||||
max={{maximum}}
|
max={{maximum}}
|
||||||
|
value={{left}}
|
||||||
editable pin
|
editable pin
|
||||||
></paper-diff-slider>
|
></paper-diff-slider>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -21,10 +21,15 @@
|
|||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.intro .section .columns {
|
||||||
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.intro paper-button {
|
.intro paper-button {
|
||||||
min-width: 200px;
|
flex-basis: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="section white-text" style="background: #282828">
|
<div class="section white-text" style="background: #282828">
|
||||||
<div class="columns layout horizontal around-justified wrap">
|
<div class="columns layout horizontal around-justified wrap">
|
||||||
<div>
|
<div class="layout vertical center">
|
||||||
<div class="paper-font-headline">
|
<div class="paper-font-headline">
|
||||||
Guide
|
Guide
|
||||||
</div>
|
</div>
|
||||||
@@ -78,7 +78,7 @@
|
|||||||
</paper-button>
|
</paper-button>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="layout vertical center">
|
||||||
<div class="paper-font-headline">
|
<div class="paper-font-headline">
|
||||||
Discuss
|
Discuss
|
||||||
</div>
|
</div>
|
||||||
@@ -91,7 +91,7 @@
|
|||||||
</paper-button>
|
</paper-button>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="layout vertical center">
|
||||||
<div class="paper-font-headline">
|
<div class="paper-font-headline">
|
||||||
Get involved
|
Get involved
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
<div class="form flex scroll-y">
|
<div class="form flex scroll-y" style="position: relative;">
|
||||||
{{#unless editing}}
|
{{#unless editing}}
|
||||||
{{> UI.contentBlock}}
|
{{> UI.contentBlock}}
|
||||||
{{else}}
|
{{else}}
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<template name="baseEditDialog">
|
||||||
|
<div class="fit base-dialog layout vertical">
|
||||||
|
<app-toolbar class={{class}}>
|
||||||
|
<paper-icon-button id="backButton"
|
||||||
|
icon="arrow-back">
|
||||||
|
</paper-icon-button>
|
||||||
|
<div main-title>{{title}}</div>
|
||||||
|
{{#unless hideDelete}}
|
||||||
|
<paper-icon-button id="deleteButton"
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
icon="delete">
|
||||||
|
</paper-icon-button>
|
||||||
|
{{/unless}}
|
||||||
|
{{#unless hideColor}}
|
||||||
|
{{> colorDropdown}}
|
||||||
|
{{/unless}}
|
||||||
|
</app-toolbar>
|
||||||
|
<div class="form flex scroll-y" style="position: relative;">
|
||||||
|
{{> UI.contentBlock}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
Template.baseEditDialog.events({
|
||||||
|
"tap #backButton": function(){
|
||||||
|
popDialogStack();
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -13,14 +13,80 @@ pushDialogStack = function({template, data, element, returnElement, callback}){
|
|||||||
returnElement,
|
returnElement,
|
||||||
callback,
|
callback,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
updateHistory();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var currentResult;
|
||||||
|
|
||||||
popDialogStack = function(result){
|
popDialogStack = function(result){
|
||||||
|
if (history && history.state && history.state.openDialogs){
|
||||||
|
currentResult = result;
|
||||||
|
history.back();
|
||||||
|
} else {
|
||||||
|
popDialogStackAction(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onpopstate = function(event){
|
||||||
|
let state = event.state;
|
||||||
|
let numDialogs = dialogs._array.length;
|
||||||
|
if (_.isFinite(state.openDialogs) && numDialogs > state.openDialogs){
|
||||||
|
popDialogStackAction(currentResult);
|
||||||
|
currentResult = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
popDialogStackAction = function(result){
|
||||||
const dialog = dialogs.pop();
|
const dialog = dialogs.pop();
|
||||||
|
updateHistory();
|
||||||
if (!dialog) return;
|
if (!dialog) return;
|
||||||
dialog.callback && dialog.callback(result);
|
dialog.callback && dialog.callback(result);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let updateHistory = function(){
|
||||||
|
// history should looks like: [{openDialogs: 0}, {openDialogs: n}] where
|
||||||
|
// n is the number of open dialogs
|
||||||
|
|
||||||
|
// If we can't access the history object, give up
|
||||||
|
if (!history) return;
|
||||||
|
// Make sure that there is a state tracking open dialogs
|
||||||
|
// replace the state without bashing it in the process
|
||||||
|
if (!history.state || !_.isFinite(history.state.openDialogs)){
|
||||||
|
let newState = _.clone(history.state) || {};
|
||||||
|
newState.openDialogs = 0;
|
||||||
|
history.replaceState(newState, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
const numDialogs = dialogs._array.length;
|
||||||
|
const stateDialogs = history.state.openDialogs;
|
||||||
|
|
||||||
|
// If the number of dialogs and state dialogs are equal, we don't need to do
|
||||||
|
// anything
|
||||||
|
if (numDialogs === stateDialogs) return;
|
||||||
|
|
||||||
|
if (stateDialogs > 0){
|
||||||
|
// On a dialog count
|
||||||
|
if (numDialogs === 0){
|
||||||
|
// but shouldn't be
|
||||||
|
history.back();
|
||||||
|
} else {
|
||||||
|
// but should replace with correct count
|
||||||
|
let newState = _.clone(history.state) || {};
|
||||||
|
newState.openDialogs = dialogs._array.length;
|
||||||
|
history.replaceState(newState, "");
|
||||||
|
}
|
||||||
|
} else if (numDialogs > 0 && stateDialogs === 0){
|
||||||
|
// On the zero state, push a dialog count
|
||||||
|
history.pushState({openDialogs: numDialogs}, "");
|
||||||
|
} else {
|
||||||
|
console.warn(
|
||||||
|
"History could not be updated correctly, unexpected case",
|
||||||
|
{stateDialogs, numDialogs},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Template.dialogStack.helpers({
|
Template.dialogStack.helpers({
|
||||||
dialogStackClass(){
|
dialogStackClass(){
|
||||||
if (!dialogs.get().length) return "hide";
|
if (!dialogs.get().length) return "hide";
|
||||||
|
|||||||
@@ -25,10 +25,10 @@ Template.fabMenu.helpers({
|
|||||||
});
|
});
|
||||||
|
|
||||||
Template.fabMenu.events({
|
Template.fabMenu.events({
|
||||||
"tap .expand-menu": function(event, instance) {
|
"click .expand-menu": function(event, instance) {
|
||||||
instance.active.set(!instance.active.get());
|
instance.active.set(!instance.active.get());
|
||||||
},
|
},
|
||||||
"tap .mini-holder paper-fab": function(event, instance) {
|
"click .mini-holder paper-fab": function(event, instance) {
|
||||||
instance.active.set(false);
|
instance.active.set(false);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
71
rpg-docs/lib/constants/statOrder.js
Normal file
71
rpg-docs/lib/constants/statOrder.js
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
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,
|
||||||
|
};
|
||||||
45
rpg-docs/server/lib/cron/deleteRemovedDocuments.js
Normal file
45
rpg-docs/server/lib/cron/deleteRemovedDocuments.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
Meteor.startup(() => {
|
||||||
|
const collections = [
|
||||||
|
Attacks, Buffs, Classes, Effects, Experiences,
|
||||||
|
Features, Notes, Proficiencies, SpellLists, Spells,
|
||||||
|
Containers, Items,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes all soft removed documents that were removed more than 30 minutes ago
|
||||||
|
* and were not restored
|
||||||
|
* @return {Number} Number of documents removed
|
||||||
|
*/
|
||||||
|
const deleteOldSoftRemovedDocs = function(){
|
||||||
|
let numRemoved = 0;
|
||||||
|
const now = new Date();
|
||||||
|
const thirtyMinutesAgo = new Date(now.getTime() - 30*60000);
|
||||||
|
_.each(collections, (collection) => {
|
||||||
|
numRemoved += collection.remove({
|
||||||
|
removed: true,
|
||||||
|
removedAt: {$lt: thirtyMinutesAgo} // dates *before* 30 minutes ago
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return numRemoved;
|
||||||
|
};
|
||||||
|
|
||||||
|
SyncedCron.add({
|
||||||
|
name: "Delete all soft removed items that haven't been restored",
|
||||||
|
schedule: function(parser) {
|
||||||
|
return parser.text('every 6 hours');
|
||||||
|
},
|
||||||
|
job: function() {
|
||||||
|
deleteOldSoftRemovedDocs();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add a method to manually trigger removal
|
||||||
|
Meteor.methods({
|
||||||
|
deleteOldSoftRemovedDocs() {
|
||||||
|
const user = Meteor.users.findOne(this.userId);
|
||||||
|
if (user && _.contains(user.roles, "admin")){
|
||||||
|
return deleteOldSoftRemovedDocs();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user