Compare commits

...

13 Commits
1.2.0 ... 1.2.7

Author SHA1 Message Date
Stefan Zermatten
4cd058e1fe Set max of slider before value
Prevents unnecessary capping of temp HP to 100
Fixes #89
2017-06-08 09:48:51 +02:00
Stefan Zermatten
8f30cee4d3 Hotfixed column layout with translateZ(0) hack 2017-06-07 09:46:38 +02:00
Stefan Zermatten
d7f7eb2e6a Fixed some spells in the library 2017-06-07 09:35:59 +02:00
Stefan Zermatten
a59cf1162f added return value to remove old soft removed docs 2017-05-29 11:58:19 +02:00
Stefan Zermatten
7bc80da99e Moved cron setup to startup 2017-05-29 11:35:30 +02:00
Stefan Zermatten
2ddc520bb6 Added cron job to remove old documents 2017-05-29 11:16:46 +02:00
Stefan Zermatten
d92bb0f4d4 Replace tap event with click event for mini-FABs
fixes #68, probably
2017-05-16 10:36:40 +02:00
Stefan Zermatten
abcfe57add Fixed results not being used when popping dialogs
Fixes #66
2017-05-11 11:08:00 +02:00
Stefan Zermatten
cdae75beef Merge branch 'bugfix-iamastick' 2017-05-10 16:40:05 +02:00
Stefan Zermatten
e7bcc2224c Replaced effects in-line editing with edit dialogs
Closes #41
2017-05-10 16:39:36 +02:00
Stefan Zermatten
b591c66dd5 Back button now closes dialogs
closes #9
2017-05-10 10:38:28 +02:00
Stefan Zermatten
402bc0e5ed Fixed Readme 2017-05-10 10:37:30 +02:00
Stefan Zermatten
ac772531ee Fixed SRD spells not getting a random ID when inserted
fixes #64
fixes #65
2017-05-08 11:21:30 +02:00
24 changed files with 410 additions and 98 deletions

View File

@@ -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`

View File

@@ -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 isnt 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 its 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 isnt 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 its 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",

View File

@@ -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

View File

@@ -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

View File

@@ -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 {

View File

@@ -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;
}

View File

@@ -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>

View File

@@ -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),
}); });

View File

@@ -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>

View File

@@ -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"){

View File

@@ -0,0 +1,3 @@
.effectsEditList .effect {
background: white;
}

View File

@@ -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>

View File

@@ -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,
});
}, },
}); });

View File

@@ -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>

View File

@@ -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);
} }
}); });

View File

@@ -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,

View File

@@ -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>

View File

@@ -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}}

View File

@@ -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>

View File

@@ -0,0 +1,5 @@
Template.baseEditDialog.events({
"tap #backButton": function(){
popDialogStack();
},
});

View File

@@ -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";

View File

@@ -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);
}, },
}); });

View 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,
};

View 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();
}
},
});
});