Compare commits
437 Commits
1.10.1
...
2.0-beta.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e0fc5abe7b | ||
|
|
18c9474570 | ||
|
|
4a039e769b | ||
|
|
93ab67a91b | ||
|
|
4352ca5f0d | ||
|
|
fe11c9ec23 | ||
|
|
37d6b32ea3 | ||
|
|
7592332637 | ||
|
|
397ff82c43 | ||
|
|
7e3815a699 | ||
|
|
9214529284 | ||
|
|
60f5588e7d | ||
|
|
ad3bec3521 | ||
|
|
ca5ded7ded | ||
|
|
5c0a2a4d6c | ||
|
|
7024adecaf | ||
|
|
9b6f259358 | ||
|
|
3642d1d249 | ||
|
|
cd2727b61c | ||
|
|
acb9dc342a | ||
|
|
d59d8cb54f | ||
|
|
2c988b8717 | ||
|
|
3af48649f7 | ||
|
|
79e03e0e63 | ||
|
|
2d788f0c07 | ||
|
|
891fd00b5f | ||
|
|
41b05064c8 | ||
|
|
cf110db67d | ||
|
|
db696574f5 | ||
|
|
4478628200 | ||
|
|
cd8a557120 | ||
|
|
5f95471bb6 | ||
|
|
a62726ae3a | ||
|
|
59fa5bcd8c | ||
|
|
4b3f068d87 | ||
|
|
a771d896a8 | ||
|
|
b439befd47 | ||
|
|
b44a18c28c | ||
|
|
bae2f4181a | ||
|
|
9b46a91641 | ||
|
|
fc9467177b | ||
|
|
136dd5fd29 | ||
|
|
cb34363a4e | ||
|
|
a4d6adacff | ||
|
|
29588a87d0 | ||
|
|
97fcb76454 | ||
|
|
9069ee8e35 | ||
|
|
170bac6934 | ||
|
|
809426b183 | ||
|
|
e8728166a9 | ||
|
|
5046a847cf | ||
|
|
b6c7ea8c4f | ||
|
|
bbda0ea1b6 | ||
|
|
47206ccfc4 | ||
|
|
dd213feb0a | ||
|
|
30ab216dc1 | ||
|
|
ea88a9c41e | ||
|
|
f0e22dc1ca | ||
|
|
8c608937bb | ||
|
|
c3ed3d55ce | ||
|
|
323cac9405 | ||
|
|
62689174e2 | ||
|
|
6c8e9037e1 | ||
|
|
e934f92e8b | ||
|
|
8aea5c4fc6 | ||
|
|
5f87bbc4f5 | ||
|
|
4d0ff52853 | ||
|
|
6506cfd727 | ||
|
|
f486c3f176 | ||
|
|
eed11e8833 | ||
|
|
46585406df | ||
|
|
5597acb0ea | ||
|
|
3fa9077124 | ||
|
|
cb8d311fdc | ||
|
|
eb3f8c9105 | ||
|
|
82a2241e4d | ||
|
|
3804323322 | ||
|
|
31185a4b12 | ||
|
|
4ca47d3a62 | ||
|
|
26662b939c | ||
|
|
48e54c42b4 | ||
|
|
d649fb9d54 | ||
|
|
073578b90d | ||
|
|
1ca6bc834a | ||
|
|
4180e153dd | ||
|
|
15db76a2fe | ||
|
|
1763358642 | ||
|
|
f63faa867b | ||
|
|
a2fe8d950a | ||
|
|
d63565633e | ||
|
|
4fd62d17bd | ||
|
|
ed9cfee9f9 | ||
|
|
966fcc449d | ||
|
|
eae35062d0 | ||
|
|
ee0fb72d56 | ||
|
|
528e53fc9b | ||
|
|
17c9d270e6 | ||
|
|
a2bae8d12a | ||
|
|
13cb9253c3 | ||
|
|
88ed912d18 | ||
|
|
6746f5f989 | ||
|
|
b1328e4cf5 | ||
|
|
7bf0e959d7 | ||
|
|
ed35d2e984 | ||
|
|
a6bdfe247c | ||
|
|
a86176a20d | ||
|
|
4e57bd4a73 | ||
|
|
95bfcd79c9 | ||
|
|
7416101a34 | ||
|
|
2164174218 | ||
|
|
1717ee4bc7 | ||
|
|
e06196a54c | ||
|
|
6008d8b47a | ||
|
|
e77513110b | ||
|
|
1856e90d12 | ||
|
|
97111741bf | ||
|
|
fc5576397a | ||
|
|
53b1f16d2f | ||
|
|
2981813751 | ||
|
|
74fef2bd39 | ||
|
|
1a0c2bca78 | ||
|
|
5ed8e08993 | ||
|
|
e3c6949491 | ||
|
|
e0e7693fff | ||
|
|
7fe2292c2a | ||
|
|
9290c9570c | ||
|
|
24725381d7 | ||
|
|
c1aacb9ebe | ||
|
|
0e71d1c719 | ||
|
|
2381769ea2 | ||
|
|
adfe1dc613 | ||
|
|
27300190d3 | ||
|
|
d90894d7c6 | ||
|
|
9e7c1ce405 | ||
|
|
d00ff000ce | ||
|
|
e26031368d | ||
|
|
9ac6b510e4 | ||
|
|
e67b4c72e3 | ||
|
|
1c700121ca | ||
|
|
8e4694f63e | ||
|
|
827d567ac2 | ||
|
|
43a08eb034 | ||
|
|
ea5ac42ec0 | ||
|
|
1be09e48ef | ||
|
|
625455da09 | ||
|
|
4a25c22b64 | ||
|
|
53e9be1407 | ||
|
|
7055f4d990 | ||
|
|
797b6a0d88 | ||
|
|
7afbfa1816 | ||
|
|
d6877905c9 | ||
|
|
69e8a307fc | ||
|
|
66e70c8c94 | ||
|
|
dfa3b057b0 | ||
|
|
f3d86ef274 | ||
|
|
46a0e92402 | ||
|
|
d0c8131d5f | ||
|
|
5578dca6e9 | ||
|
|
724f9574a2 | ||
|
|
2bd7c2908f | ||
|
|
692a7983f8 | ||
|
|
acb9369100 | ||
|
|
21dbab1877 | ||
|
|
86926e593c | ||
|
|
81cec77c9e | ||
|
|
acb8a073de | ||
|
|
6996d2a99e | ||
|
|
83b810ce3d | ||
|
|
328480d2c5 | ||
|
|
8e4c6252cd | ||
|
|
c3cc4c881d | ||
|
|
4ee7307e34 | ||
|
|
eabc0aa32e | ||
|
|
ae0b060f01 | ||
|
|
79a4488a28 | ||
|
|
dfa13ef2c3 | ||
|
|
f6d80f6ae4 | ||
|
|
f4d613a20b | ||
|
|
73f193460d | ||
|
|
ba4cc1a496 | ||
|
|
21030d186a | ||
|
|
4fcae9b3f9 | ||
|
|
66d11d58f3 | ||
|
|
63a20b2bef | ||
|
|
88a2a506b0 | ||
|
|
710c578119 | ||
|
|
a8b3fc3f2f | ||
|
|
6f4710bee3 | ||
|
|
50621ca269 | ||
|
|
16d61eb708 | ||
|
|
b110eadb7c | ||
|
|
45c84e28a3 | ||
|
|
b70634f5de | ||
|
|
e3f18fab69 | ||
|
|
56f3af3c4b | ||
|
|
a58def26d1 | ||
|
|
0014c691d2 | ||
|
|
5436b12108 | ||
|
|
d45b184170 | ||
|
|
0b184c4e85 | ||
|
|
745f4fd353 | ||
|
|
3c4f3e26f8 | ||
|
|
67ea67148f | ||
|
|
f37ff919fb | ||
|
|
76b6501b31 | ||
|
|
229a5dddcf | ||
|
|
e29c77dc67 | ||
|
|
c87a3a3f60 | ||
|
|
9f7d6b8ae7 | ||
|
|
4ccf999fc7 | ||
|
|
549418b395 | ||
|
|
14fe48efb3 | ||
|
|
18eeaf4884 | ||
|
|
4f93ad3e9b | ||
|
|
d0304da4fd | ||
|
|
4b7ff2146f | ||
|
|
2385b69720 | ||
|
|
31bc3663a7 | ||
|
|
cbdd72e09b | ||
|
|
438f128641 | ||
|
|
3460cbf5a0 | ||
|
|
da561bfc83 | ||
|
|
eb63a7a7f3 | ||
|
|
b5ceb7fad0 | ||
|
|
fb5b2b8ada | ||
|
|
ea628ed379 | ||
|
|
d35fa447a3 | ||
|
|
73b43574ee | ||
|
|
02cb690325 | ||
|
|
5662ce3666 | ||
|
|
45e36ac3d4 | ||
|
|
812a6679d5 | ||
|
|
1bfb48c672 | ||
|
|
946b47ea61 | ||
|
|
bd3f676919 | ||
|
|
4062d79e90 | ||
|
|
dfa302a4a9 | ||
|
|
baa1c0967c | ||
|
|
a0dc36557c | ||
|
|
0c002ae5cd | ||
|
|
11d3b0fa8d | ||
|
|
d28d6de684 | ||
|
|
93d8a8d33e | ||
|
|
4abb5edbf3 | ||
|
|
9757da2cae | ||
|
|
bd4fb58935 | ||
|
|
1add44f0e7 | ||
|
|
99db45e522 | ||
|
|
7a59b4542e | ||
|
|
335608f6a9 | ||
|
|
4d47584f4f | ||
|
|
48fe0d3608 | ||
|
|
9a194a20cb | ||
|
|
de183297fc | ||
|
|
d921ad46d8 | ||
|
|
00d02a3bb5 | ||
|
|
b0caffae1a | ||
|
|
e71bfb2691 | ||
|
|
fc24cf4a5b | ||
|
|
062e554629 | ||
|
|
a1d77cdaab | ||
|
|
f4011abf7b | ||
|
|
aa9802b34e | ||
|
|
dabb54b0a3 | ||
|
|
05867c61dd | ||
|
|
ea968ad955 | ||
|
|
b7b0ac9c00 | ||
|
|
ab2ea36541 | ||
|
|
b35e08b9b4 | ||
|
|
021a53ef36 | ||
|
|
681e669e76 | ||
|
|
28ffcc87b4 | ||
|
|
888a7ea592 | ||
|
|
053a7a36a6 | ||
|
|
6d68796a11 | ||
|
|
18493afbbf | ||
|
|
a94f437ba8 | ||
|
|
d21827106c | ||
|
|
caf7f3efb9 | ||
|
|
484b73d836 | ||
|
|
ee788c952a | ||
|
|
ffa98d76fc | ||
|
|
5bb5f047f4 | ||
|
|
1146d56324 | ||
|
|
ef83d54fd9 | ||
|
|
18e3f653f3 | ||
|
|
572aca5906 | ||
|
|
f3d19d3b38 | ||
|
|
94f6631a7d | ||
|
|
febb65a513 | ||
|
|
15c11e16ab | ||
|
|
8772e539da | ||
|
|
4e7c0c5a90 | ||
|
|
0ae2cc5545 | ||
|
|
687b517164 | ||
|
|
8305596373 | ||
|
|
8f624b24a9 | ||
|
|
a2432c7161 | ||
|
|
b7a4a3d3fa | ||
|
|
e5ff116208 | ||
|
|
24f3431900 | ||
|
|
4f402830d8 | ||
|
|
9e208ad3b3 | ||
|
|
9d027aeabf | ||
|
|
97ec5d4b5c | ||
|
|
6c421c3a98 | ||
|
|
665a9d716a | ||
|
|
aa7a426e9f | ||
|
|
da6909a997 | ||
|
|
3129b86ef0 | ||
|
|
e63ae96cb5 | ||
|
|
a31a70f435 | ||
|
|
c5899e0816 | ||
|
|
d30ee06e33 | ||
|
|
5d45788521 | ||
|
|
23cd5d6a12 | ||
|
|
38da95256b | ||
|
|
f773a20a59 | ||
|
|
23654cd09c | ||
|
|
09b2c38b43 | ||
|
|
1103248574 | ||
|
|
96bb072dad | ||
|
|
3f6b3fe4c1 | ||
|
|
ad7a485778 | ||
|
|
58949e14fe | ||
|
|
8bcb4407e6 | ||
|
|
fd15e8a6c7 | ||
|
|
938573a4b4 | ||
|
|
465f6645b7 | ||
|
|
4b25373c7c | ||
|
|
5142b8e1a0 | ||
|
|
f8e83ebe7e | ||
|
|
b67926e0fc | ||
|
|
bf2e9439cf | ||
|
|
de4509ab6a | ||
|
|
f42f9590d2 | ||
|
|
29c5ed2194 | ||
|
|
cf0440a8db | ||
|
|
4917729f29 | ||
|
|
fe8e72d225 | ||
|
|
0e913b1f63 | ||
|
|
58f26cf849 | ||
|
|
836b10d499 | ||
|
|
8d4146f242 | ||
|
|
7742e5deef | ||
|
|
41c69ae040 | ||
|
|
c6e7f8eeb6 | ||
|
|
e3e7b76f02 | ||
|
|
2dcbc91ccd | ||
|
|
2fcf3047ee | ||
|
|
a95ae88a31 | ||
|
|
f6aca08f63 | ||
|
|
87ee39b5bd | ||
|
|
d466bfe428 | ||
|
|
e357b29145 | ||
|
|
a80d070533 | ||
|
|
80d369f0d4 | ||
|
|
f6b0c746cc | ||
|
|
4584499019 | ||
|
|
77d2f87373 | ||
|
|
8c0edfaa93 | ||
|
|
2e6ef52594 | ||
|
|
00e8cbc1c8 | ||
|
|
e8a0e86548 | ||
|
|
60dfba3b46 | ||
|
|
e43718f034 | ||
|
|
36022e4bc4 | ||
|
|
0497223804 | ||
|
|
ef9512f0ec | ||
|
|
369dae17ee | ||
|
|
4728d06c0b | ||
|
|
2d3cb367da | ||
|
|
f2137e26b2 | ||
|
|
0bd654e557 | ||
|
|
f06c4adb32 | ||
|
|
fabf377f72 | ||
|
|
03c244e7c9 | ||
|
|
ea7eabf27d | ||
|
|
bbbed216eb | ||
|
|
c5cb98dc9f | ||
|
|
aa6d973ae1 | ||
|
|
76da2c8393 | ||
|
|
e0195499e5 | ||
|
|
798cf3edd7 | ||
|
|
13669fdc91 | ||
|
|
dcc460d9e6 | ||
|
|
c7fcb4de0c | ||
|
|
a5e7bd95c7 | ||
|
|
51919297df | ||
|
|
5b80032fa1 | ||
|
|
ade614dd5f | ||
|
|
c4984c07bf | ||
|
|
b49ab67390 | ||
|
|
c7f0c0a4c4 | ||
|
|
7cda854d22 | ||
|
|
989706483a | ||
|
|
53a1137848 | ||
|
|
e3065f089f | ||
|
|
6aaa4ebe00 | ||
|
|
158615c25c | ||
|
|
4cd46fe209 | ||
|
|
04059709eb | ||
|
|
d117570165 | ||
|
|
4b900d5664 | ||
|
|
189a1d0a16 | ||
|
|
ca8223ccad | ||
|
|
b2eed9a672 | ||
|
|
d330e15dce | ||
|
|
4ac56a31de | ||
|
|
41a25151fc | ||
|
|
e1c1b727ed | ||
|
|
dc8185df98 | ||
|
|
89820780b5 | ||
|
|
d0ce162315 | ||
|
|
6835f5f4f9 | ||
|
|
e9d5e85e75 | ||
|
|
c46f8c5171 | ||
|
|
f41ff1c52f | ||
|
|
7f418c26da | ||
|
|
acdc084905 | ||
|
|
6a73d3576a | ||
|
|
d82031ab0d | ||
|
|
cf33bfcce1 | ||
|
|
005433268d | ||
|
|
ae470642c1 | ||
|
|
dd89556b7f | ||
|
|
b3c2176de8 | ||
|
|
9fccc3be61 | ||
|
|
bc26a77f92 | ||
|
|
fa3cca7193 | ||
|
|
662fc91e17 | ||
|
|
b4506fd939 | ||
|
|
5c40a84660 | ||
|
|
7c9687955d | ||
|
|
b21a91b0aa | ||
|
|
f4b1da0c80 | ||
|
|
755e7fba30 |
13
.idea/inspectionProfiles/Project_Default.xml
generated
13
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -1,13 +0,0 @@
|
|||||||
<component name="InspectionProjectProfileManager">
|
|
||||||
<profile version="1.0">
|
|
||||||
<option name="myName" value="Project Default" />
|
|
||||||
<inspection_tool class="HtmlUnknownAttribute" enabled="false" level="WARNING" enabled_by_default="false">
|
|
||||||
<option name="myValues">
|
|
||||||
<value>
|
|
||||||
<list size="0" />
|
|
||||||
</value>
|
|
||||||
</option>
|
|
||||||
<option name="myCustomValuesEnabled" value="true" />
|
|
||||||
</inspection_tool>
|
|
||||||
</profile>
|
|
||||||
</component>
|
|
||||||
7
.idea/inspectionProfiles/profiles_settings.xml
generated
7
.idea/inspectionProfiles/profiles_settings.xml
generated
@@ -1,7 +0,0 @@
|
|||||||
<component name="InspectionProjectProfileManager">
|
|
||||||
<settings>
|
|
||||||
<option name="PROJECT_PROFILE" value="Project Default" />
|
|
||||||
<option name="USE_PROJECT_PROFILE" value="true" />
|
|
||||||
<version value="1.0" />
|
|
||||||
</settings>
|
|
||||||
</component>
|
|
||||||
56
.jscsrc
56
.jscsrc
@@ -1,56 +0,0 @@
|
|||||||
{
|
|
||||||
"requireOperatorBeforeLineBreak": true,
|
|
||||||
"requireCamelCaseOrUpperCaseIdentifiers": true,
|
|
||||||
"maximumLineLength": {
|
|
||||||
"value": 80,
|
|
||||||
"allowComments": true,
|
|
||||||
"allowRegex": true
|
|
||||||
},
|
|
||||||
"validateIndentation": "\t",
|
|
||||||
"validateQuoteMarks": "\"",
|
|
||||||
|
|
||||||
"disallowMultipleLineStrings": true,
|
|
||||||
"disallowMixedSpacesAndTabs": "smart",
|
|
||||||
"disallowTrailingWhitespace": true,
|
|
||||||
"disallowSpaceAfterPrefixUnaryOperators": true,
|
|
||||||
"disallowMultipleVarDecl": false,
|
|
||||||
"disallowNewlineBeforeBlockStatements": true,
|
|
||||||
"disallowKeywordsOnNewLine": ["else"],
|
|
||||||
|
|
||||||
"requireSpaceAfterKeywords": [
|
|
||||||
"if",
|
|
||||||
"else",
|
|
||||||
"for",
|
|
||||||
"while",
|
|
||||||
"do",
|
|
||||||
"switch",
|
|
||||||
"return",
|
|
||||||
"try",
|
|
||||||
"catch"
|
|
||||||
],
|
|
||||||
"requireSpaceBeforeBinaryOperators": [
|
|
||||||
"=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=",
|
|
||||||
"&=", "|=", "^=", "+=",
|
|
||||||
|
|
||||||
"+", "-", "*", "/", "%", "<<", ">>", ">>>", "&",
|
|
||||||
"|", "^", "&&", "||", "===", "==", ">=",
|
|
||||||
"<=", "<", ">", "!=", "!=="
|
|
||||||
],
|
|
||||||
"requireSpaceAfterBinaryOperators": true,
|
|
||||||
"requireSpacesInConditionalExpression": true,
|
|
||||||
"requireSpacesInForStatement": true,
|
|
||||||
"requireTrailingComma": {
|
|
||||||
"ignoreSingleValue": true,
|
|
||||||
"ignoreSingleLine": true
|
|
||||||
},
|
|
||||||
"requireLineFeedAtFileEnd": true,
|
|
||||||
"disallowSpacesInAnonymousFunctionExpression": {
|
|
||||||
"beforeOpeningRoundBrace": true
|
|
||||||
},
|
|
||||||
"disallowSpacesInsideObjectBrackets": "all",
|
|
||||||
"disallowSpacesInsideArrayBrackets": "all",
|
|
||||||
"disallowSpacesInsideParentheses": true,
|
|
||||||
|
|
||||||
"disallowMultipleLineBreaks": true,
|
|
||||||
"disallowNewlineBeforeBlockStatements": true
|
|
||||||
}
|
|
||||||
14
Dockerfile
Normal file
14
Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
FROM ubuntu:latest
|
||||||
|
RUN apt-get update --quiet \
|
||||||
|
&& apt-get install --quiet --yes \
|
||||||
|
bsdtar \
|
||||||
|
curl \
|
||||||
|
git
|
||||||
|
RUN ln --symbolic --force $(which bsdtar) $(which tar)
|
||||||
|
RUN useradd --create-home --shell /bin/bash dicecloud
|
||||||
|
USER dicecloud
|
||||||
|
WORKDIR /home/dicecloud
|
||||||
|
RUN curl https://install.meteor.com/?release=1.8.0.2 | sh
|
||||||
|
ENV PATH="${PATH}:/home/dicecloud/.meteor"
|
||||||
|
COPY dev.sh ./dev.sh
|
||||||
|
ENTRYPOINT ./dev.sh
|
||||||
63
README.md
63
README.md
@@ -1,13 +1,70 @@
|
|||||||
RPG Docs
|
DiceCloud
|
||||||
========
|
========
|
||||||
|
|
||||||
This is the repo for [DiceCloud](dicecloud.com).
|
This is the repo for [DiceCloud](dicecloud.com).
|
||||||
|
|
||||||
|
DiceCloud is a free, auditable, real-time character sheet for D&D 5e.
|
||||||
|
|
||||||
|
Philosophy
|
||||||
|
----------
|
||||||
|
|
||||||
|
Setting up your character on DiceCloud takes a little longer than
|
||||||
|
just filling it in on a paper character sheet would. The goal of using an
|
||||||
|
online sheet is to make actually playing the game more streamlined, and
|
||||||
|
ultimately more fun. So putting a little extra effort into setting up a
|
||||||
|
character now pays off over and over again once you're playing.
|
||||||
|
|
||||||
|
The idea is to track where each number comes from, and allow you to easily make
|
||||||
|
changes on the fly. Let's look at a hypothetical example.
|
||||||
|
|
||||||
|
> You need to swim through a sunken section of dungeon to fetch the quest's Thing.
|
||||||
|
> You'll need to take off your magical Plate Armor of +1 Constitution to swim
|
||||||
|
> without sinking, of course.
|
||||||
|
>
|
||||||
|
> Taking it off will take away that disadvantage on
|
||||||
|
> stealth checks, change your armor class, your speed and your constitution, and
|
||||||
|
> which in turn changes your hit points and your constitution saving throw.
|
||||||
|
> Working out all those changes in the middle of a game will drag the game to a
|
||||||
|
> halt.
|
||||||
|
>
|
||||||
|
> Fortunately you have DiceCloud, so it's a matter of dragging
|
||||||
|
> your Plate Armor +1 Con from your "equipment" box to your "backpack" box and
|
||||||
|
> you're done. Your hitpoints change correctly, your saving throws are up to date,
|
||||||
|
> your armor class goes back to reflecting the fact that you have natural armor
|
||||||
|
> from being a dragonborn. Your character sheet keeps up and you
|
||||||
|
> ultimately get more time to play the game. Huzzah!
|
||||||
|
|
||||||
Getting started
|
Getting started
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
`git clone https://github.com/ThaumRystra/DiceCloud1 dicecloud`
|
Running DiceCloud locally, either to host it yourself away from an internet
|
||||||
|
connection, or to contribute to developing it further, is fairly
|
||||||
|
straightforward and it should work on Linux, Windows, and Mac.
|
||||||
|
|
||||||
|
You'll need to have installed:
|
||||||
|
|
||||||
|
- [git](https://www.atlassian.com/git/tutorials/install-git)
|
||||||
|
- [Meteor](https://www.meteor.com/install)
|
||||||
|
|
||||||
|
Then, it's just a matter of cloning this repository into a folder, and running
|
||||||
|
`meteor` in the app directory.
|
||||||
|
|
||||||
|
`git clone https://github.com/ThaumRystra/DiceCloud dicecloud`
|
||||||
`cd dicecloud`
|
`cd dicecloud`
|
||||||
`cd app`
|
`cd app`
|
||||||
`bower install`
|
`meteor npm install`
|
||||||
`meteor`
|
`meteor`
|
||||||
|
|
||||||
|
You should see this:
|
||||||
|
|
||||||
|
```
|
||||||
|
=> Started proxy.
|
||||||
|
=> [HMR] Dev server listening on port 3003.
|
||||||
|
=> Started MongoDB.
|
||||||
|
=> Started your app.
|
||||||
|
|
||||||
|
=> App running at: http://localhost:3000/
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, visiting [](http://localhost:3000/) should show you an empty instance of
|
||||||
|
DiceCloud running.
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
{"directory":"public/components/"}
|
|
||||||
2
app/.gitignore
vendored
2
app/.gitignore
vendored
@@ -1,9 +1,11 @@
|
|||||||
.meteor/local
|
.meteor/local
|
||||||
.meteor/meteorite
|
.meteor/meteorite
|
||||||
.demeteorized
|
.demeteorized
|
||||||
|
.cache
|
||||||
settings.json
|
settings.json
|
||||||
public/components
|
public/components
|
||||||
public/_imports.html
|
public/_imports.html
|
||||||
|
private/oldClient
|
||||||
nohup.out
|
nohup.out
|
||||||
node_modules
|
node_modules
|
||||||
dump
|
dump
|
||||||
|
|||||||
@@ -16,3 +16,4 @@ notices-for-facebook-graph-api-2
|
|||||||
1.4.3-split-account-service-packages
|
1.4.3-split-account-service-packages
|
||||||
1.5-add-dynamic-import-package
|
1.5-add-dynamic-import-package
|
||||||
1.7-split-underscore-from-meteor-base
|
1.7-split-underscore-from-meteor-base
|
||||||
|
1.8.3-split-jquery-from-blaze
|
||||||
|
|||||||
@@ -3,54 +3,49 @@
|
|||||||
# 'meteor add' and 'meteor remove' will edit this file for you,
|
# 'meteor add' and 'meteor remove' will edit this file for you,
|
||||||
# but you can also edit it by hand.
|
# but you can also edit it by hand.
|
||||||
|
|
||||||
accounts-password@1.5.1
|
accounts-password@1.6.0
|
||||||
accounts-ui@1.3.0
|
accounts-ui@1.3.1
|
||||||
random@1.1.0
|
random@1.2.0
|
||||||
dburles:collection-helpers
|
dburles:collection-helpers
|
||||||
reactive-var@1.0.11
|
reactive-var@1.0.11
|
||||||
underscore@1.0.10
|
underscore@1.0.10
|
||||||
aldeed:collection2
|
|
||||||
matb33:collection-hooks
|
|
||||||
zimme:collection-softremovable
|
|
||||||
momentjs:moment
|
momentjs:moment
|
||||||
dburles:mongo-collection-instances
|
dburles:mongo-collection-instances
|
||||||
percolate:migrations
|
percolate:migrations
|
||||||
ecwyne:mathjs
|
accounts-google@1.3.3
|
||||||
useraccounts:polymer
|
|
||||||
accounts-google@1.3.1
|
|
||||||
splendido:accounts-meld
|
|
||||||
email@1.2.3
|
email@1.2.3
|
||||||
meteorhacks:subs-manager
|
meteorhacks:subs-manager
|
||||||
chuangbo:marked
|
chuangbo:marked
|
||||||
reywood:iron-router-ga
|
|
||||||
meteor-base@1.4.0
|
meteor-base@1.4.0
|
||||||
mobile-experience@1.0.5
|
mobile-experience@1.1.0
|
||||||
mongo@1.5.0
|
mongo@1.10.0
|
||||||
blaze-html-templates
|
session@1.2.0
|
||||||
session@1.1.7
|
|
||||||
jquery@1.11.10
|
jquery@1.11.10
|
||||||
tracker@1.2.0
|
tracker@1.2.0
|
||||||
logging@1.1.20
|
logging@1.1.20
|
||||||
reload@1.2.0
|
reload@1.3.0
|
||||||
ejson@1.1.0
|
ejson@1.1.1
|
||||||
spacebars
|
|
||||||
check@1.3.1
|
check@1.3.1
|
||||||
useraccounts:iron-routing
|
standard-minifier-js@2.6.0
|
||||||
wizonesolutions:canonical
|
shell-server@0.5.0
|
||||||
standard-minifier-js@2.3.4
|
|
||||||
shell-server@0.3.1
|
|
||||||
seba:minifiers-autoprefixer
|
seba:minifiers-autoprefixer
|
||||||
nikogosovd:multiple-uihooks
|
|
||||||
templates:array
|
templates:array
|
||||||
ecmascript@0.11.1
|
ecmascript@0.14.3
|
||||||
es5-shim@4.8.0
|
es5-shim@4.8.0
|
||||||
differential:vulcanize
|
reactive-dict@1.3.0
|
||||||
reactive-dict@1.2.0
|
|
||||||
percolate:synced-cron
|
percolate:synced-cron
|
||||||
ongoworks:speakingurl
|
ongoworks:speakingurl
|
||||||
service-configuration@1.0.11
|
service-configuration@1.0.11
|
||||||
google-config-ui@1.0.0
|
google-config-ui@1.0.1
|
||||||
dynamic-import@0.4.0
|
dynamic-import@0.5.2
|
||||||
ddp-rate-limiter@1.0.7
|
ddp-rate-limiter@1.0.7
|
||||||
rate-limit@1.0.9
|
rate-limit@1.0.9
|
||||||
iron:router
|
meteortesting:mocha
|
||||||
|
mdg:validated-method
|
||||||
|
akryum:vue-router2
|
||||||
|
static-html
|
||||||
|
aldeed:collection2@3.0.0
|
||||||
|
aldeed:schema-index
|
||||||
|
akryum:vue-component
|
||||||
|
accounts-patreon
|
||||||
|
bozhao:link-accounts
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
METEOR@1.7.0.3
|
METEOR@1.10.2
|
||||||
|
|||||||
@@ -1,136 +1,126 @@
|
|||||||
accounts-base@1.4.2
|
accounts-base@1.6.0
|
||||||
accounts-google@1.3.1
|
accounts-google@1.3.3
|
||||||
accounts-oauth@1.1.15
|
accounts-oauth@1.2.0
|
||||||
accounts-password@1.5.1
|
accounts-password@1.6.0
|
||||||
accounts-ui@1.3.0
|
accounts-patreon@0.1.0
|
||||||
accounts-ui-unstyled@1.4.1
|
accounts-ui@1.3.1
|
||||||
aldeed:collection2@2.10.0
|
accounts-ui-unstyled@1.4.2
|
||||||
aldeed:collection2-core@1.2.0
|
akryum:npm-check@0.1.2
|
||||||
aldeed:schema-deny@1.1.0
|
akryum:vue-component@0.15.2
|
||||||
aldeed:schema-index@1.1.1
|
akryum:vue-component-dev-client@0.4.7
|
||||||
aldeed:simple-schema@1.5.4
|
akryum:vue-component-dev-server@0.1.4
|
||||||
|
akryum:vue-router2@0.2.3
|
||||||
|
aldeed:collection2@3.0.6
|
||||||
|
aldeed:schema-index@3.0.0
|
||||||
allow-deny@1.1.0
|
allow-deny@1.1.0
|
||||||
autoupdate@1.4.1
|
autoupdate@1.6.0
|
||||||
babel-compiler@7.1.1
|
babel-compiler@7.5.3
|
||||||
babel-runtime@1.2.2
|
babel-runtime@1.5.0
|
||||||
base64@1.0.11
|
base64@1.0.12
|
||||||
binary-heap@1.0.10
|
binary-heap@1.0.11
|
||||||
blaze@2.3.2
|
blaze@2.3.4
|
||||||
blaze-html-templates@1.1.2
|
|
||||||
blaze-tools@1.0.10
|
blaze-tools@1.0.10
|
||||||
boilerplate-generator@1.5.0
|
boilerplate-generator@1.7.0
|
||||||
caching-compiler@1.1.12
|
bozhao:link-accounts@2.1.1
|
||||||
|
caching-compiler@1.2.2
|
||||||
caching-html-compiler@1.1.3
|
caching-html-compiler@1.1.3
|
||||||
callback-hook@1.1.0
|
callback-hook@1.3.0
|
||||||
check@1.3.1
|
check@1.3.1
|
||||||
chuangbo:marked@0.3.5_1
|
chuangbo:marked@0.3.5_1
|
||||||
coffeescript@1.0.17
|
|
||||||
dburles:collection-helpers@1.1.0
|
dburles:collection-helpers@1.1.0
|
||||||
dburles:mongo-collection-instances@0.3.5
|
dburles:mongo-collection-instances@0.3.5
|
||||||
ddp@1.4.0
|
ddp@1.4.0
|
||||||
ddp-client@2.3.3
|
ddp-client@2.3.3
|
||||||
ddp-common@1.4.0
|
ddp-common@1.4.0
|
||||||
ddp-rate-limiter@1.0.7
|
ddp-rate-limiter@1.0.7
|
||||||
ddp-server@2.2.0
|
ddp-server@2.3.1
|
||||||
deps@1.0.12
|
deps@1.0.12
|
||||||
diff-sequence@1.1.0
|
diff-sequence@1.1.1
|
||||||
differential:vulcanize@3.0.0
|
dynamic-import@0.5.2
|
||||||
dynamic-import@0.4.1
|
ecmascript@0.14.3
|
||||||
ecmascript@0.11.1
|
|
||||||
ecmascript-runtime@0.7.0
|
ecmascript-runtime@0.7.0
|
||||||
ecmascript-runtime-client@0.7.1
|
ecmascript-runtime-client@0.10.0
|
||||||
ecmascript-runtime-server@0.7.0
|
ecmascript-runtime-server@0.9.0
|
||||||
ecwyne:mathjs@0.25.0
|
ejson@1.1.1
|
||||||
ejson@1.1.0
|
|
||||||
email@1.2.3
|
email@1.2.3
|
||||||
es5-shim@4.8.0
|
es5-shim@4.8.0
|
||||||
|
fetch@0.1.1
|
||||||
geojson-utils@1.0.10
|
geojson-utils@1.0.10
|
||||||
google-config-ui@1.0.0
|
google-config-ui@1.0.1
|
||||||
google-oauth@1.2.5
|
google-oauth@1.3.0
|
||||||
hot-code-push@1.0.4
|
hot-code-push@1.0.4
|
||||||
html-tools@1.0.11
|
html-tools@1.0.11
|
||||||
htmljs@1.0.11
|
htmljs@1.0.11
|
||||||
http@1.4.1
|
http@1.4.2
|
||||||
id-map@1.1.0
|
id-map@1.1.0
|
||||||
iron:controller@1.0.12
|
inter-process-messaging@0.1.1
|
||||||
iron:core@1.0.11
|
|
||||||
iron:dynamic-template@1.0.12
|
|
||||||
iron:layout@1.0.12
|
|
||||||
iron:location@1.0.11
|
|
||||||
iron:middleware-stack@1.1.0
|
|
||||||
iron:router@1.1.2
|
|
||||||
iron:url@1.1.0
|
|
||||||
jquery@1.11.11
|
jquery@1.11.11
|
||||||
lai:collection-extensions@0.2.1_1
|
lai:collection-extensions@0.2.1_1
|
||||||
launch-screen@1.1.1
|
launch-screen@1.2.0
|
||||||
less@2.7.12
|
less@2.8.0
|
||||||
livedata@1.0.18
|
livedata@1.0.18
|
||||||
|
lmieulet:meteor-coverage@1.1.4
|
||||||
localstorage@1.2.0
|
localstorage@1.2.0
|
||||||
logging@1.1.20
|
logging@1.1.20
|
||||||
matb33:collection-hooks@0.8.4
|
mdg:validated-method@1.2.0
|
||||||
mdg:validation-error@0.5.1
|
meteor@1.9.3
|
||||||
meteor@1.9.2
|
|
||||||
meteor-base@1.4.0
|
meteor-base@1.4.0
|
||||||
|
meteorhacks:picker@1.0.3
|
||||||
meteorhacks:subs-manager@1.6.4
|
meteorhacks:subs-manager@1.6.4
|
||||||
minifier-css@1.3.1
|
meteortesting:browser-tests@1.3.3
|
||||||
minifier-js@2.3.5
|
meteortesting:mocha@1.1.5
|
||||||
minimongo@1.4.4
|
meteortesting:mocha-core@7.0.1
|
||||||
mobile-experience@1.0.5
|
minifier-css@1.5.0
|
||||||
mobile-status-bar@1.0.14
|
minifier-js@2.6.0
|
||||||
modern-browsers@0.1.2
|
minimongo@1.6.0
|
||||||
modules@0.12.2
|
mobile-experience@1.1.0
|
||||||
modules-runtime@0.10.2
|
mobile-status-bar@1.1.0
|
||||||
momentjs:moment@2.22.2
|
modern-browsers@0.1.5
|
||||||
mongo@1.5.1
|
modules@0.15.0
|
||||||
|
modules-runtime@0.12.0
|
||||||
|
momentjs:moment@2.24.0
|
||||||
|
mongo@1.10.0
|
||||||
|
mongo-decimal@0.1.1
|
||||||
mongo-dev-server@1.1.0
|
mongo-dev-server@1.1.0
|
||||||
mongo-id@1.0.7
|
mongo-id@1.0.7
|
||||||
nikogosovd:multiple-uihooks@0.1.8
|
|
||||||
npm-bcrypt@0.9.3
|
npm-bcrypt@0.9.3
|
||||||
npm-mongo@3.0.11
|
npm-mongo@3.7.0
|
||||||
oauth@1.2.3
|
oauth@1.3.0
|
||||||
oauth2@1.2.0
|
oauth2@1.3.0
|
||||||
observe-sequence@1.0.16
|
observe-sequence@1.0.16
|
||||||
ongoworks:speakingurl@9.0.0
|
ongoworks:speakingurl@9.0.0
|
||||||
ordered-dict@1.1.0
|
ordered-dict@1.1.0
|
||||||
|
patreon-oauth@0.1.0
|
||||||
percolate:migrations@0.9.8
|
percolate:migrations@0.9.8
|
||||||
percolate:synced-cron@1.3.2
|
percolate:synced-cron@1.3.2
|
||||||
promise@0.11.1
|
promise@0.11.2
|
||||||
raix:eventemitter@0.1.3
|
raix:eventemitter@0.1.3
|
||||||
random@1.1.0
|
random@1.2.0
|
||||||
rate-limit@1.0.9
|
rate-limit@1.0.9
|
||||||
reactive-dict@1.2.0
|
reactive-dict@1.3.0
|
||||||
reactive-var@1.0.11
|
reactive-var@1.0.11
|
||||||
reload@1.2.0
|
reload@1.3.0
|
||||||
retry@1.1.0
|
retry@1.1.0
|
||||||
reywood:iron-router-ga@0.7.1
|
routepolicy@1.1.0
|
||||||
routepolicy@1.0.13
|
seba:minifiers-autoprefixer@1.1.2
|
||||||
seba:minifiers-autoprefixer@1.0.1
|
|
||||||
service-configuration@1.0.11
|
service-configuration@1.0.11
|
||||||
session@1.1.7
|
session@1.2.0
|
||||||
sha@1.0.9
|
sha@1.0.9
|
||||||
shell-server@0.3.1
|
shell-server@0.5.0
|
||||||
socket-stream-client@0.2.2
|
socket-stream-client@0.3.0
|
||||||
softwarerero:accounts-t9n@1.3.11
|
|
||||||
spacebars@1.0.15
|
spacebars@1.0.15
|
||||||
spacebars-compiler@1.1.3
|
spacebars-compiler@1.1.3
|
||||||
splendido:accounts-emails-field@1.2.0
|
srp@1.1.0
|
||||||
splendido:accounts-meld@1.3.1
|
standard-minifier-js@2.6.0
|
||||||
srp@1.0.10
|
static-html@1.2.2
|
||||||
standard-minifier-js@2.3.4
|
|
||||||
templates:array@1.0.3
|
templates:array@1.0.3
|
||||||
templating@1.3.2
|
templating@1.3.2
|
||||||
templating-compiler@1.3.3
|
templating-compiler@1.3.3
|
||||||
templating-runtime@1.3.2
|
templating-runtime@1.3.2
|
||||||
templating-tools@1.1.2
|
templating-tools@1.1.2
|
||||||
|
tmeasday:check-npm-versions@0.3.2
|
||||||
tracker@1.2.0
|
tracker@1.2.0
|
||||||
ui@1.0.13
|
|
||||||
underscore@1.0.10
|
underscore@1.0.10
|
||||||
url@1.2.0
|
url@1.3.0
|
||||||
useraccounts:core@1.14.2
|
webapp@1.9.1
|
||||||
useraccounts:iron-routing@1.14.2
|
|
||||||
useraccounts:polymer@1.14.2
|
|
||||||
webapp@1.6.2
|
|
||||||
webapp-hashing@1.0.9
|
webapp-hashing@1.0.9
|
||||||
wizonesolutions:canonical@0.0.5
|
|
||||||
zimme:collection-behaviours@1.1.3
|
|
||||||
zimme:collection-softremovable@1.0.5
|
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
Instances = new Mongo.Collection("instances");
|
|
||||||
|
|
||||||
Schemas.Instance = new SimpleSchema({
|
|
||||||
//an instance is a single flow of time all parties in an instance are in-sync time wise
|
|
||||||
});
|
|
||||||
|
|
||||||
Instances.attachSchema(Schemas.Instance);
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
Actions = new Mongo.Collection("actions");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Actions are given to a character by items and features
|
|
||||||
*/
|
|
||||||
Schemas.Action = new SimpleSchema({
|
|
||||||
charId: {
|
|
||||||
type: String,
|
|
||||||
regEx: SimpleSchema.RegEx.Id,
|
|
||||||
index: 1,
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
trim: false,
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
trim: false,
|
|
||||||
},
|
|
||||||
type: {
|
|
||||||
type: String,
|
|
||||||
allowedValues: ["action, bonus, reaction, free"],
|
|
||||||
defaultValue: "action",
|
|
||||||
},
|
|
||||||
//the immediate impact of doing this action (eg. -1 rages)
|
|
||||||
adjustments: {
|
|
||||||
type: [Schemas.Adjustment],
|
|
||||||
defaultValue: [],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Actions.attachSchema(Schemas.Action);
|
|
||||||
|
|
||||||
Actions.attachBehaviour("softRemovable");
|
|
||||||
makeChild(Actions);
|
|
||||||
|
|
||||||
Actions.allow(CHARACTER_SUBSCHEMA_ALLOW);
|
|
||||||
Actions.deny(CHARACTER_SUBSCHEMA_DENY);
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
Attacks = new Mongo.Collection("attacks");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Attacks are given to a character by items and features
|
|
||||||
*/
|
|
||||||
Schemas.Attack = new SimpleSchema({
|
|
||||||
charId: {
|
|
||||||
type: String,
|
|
||||||
regEx: SimpleSchema.RegEx.Id,
|
|
||||||
index: 1,
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
defaultValue: "New Attack",
|
|
||||||
optional: true,
|
|
||||||
trim: false,
|
|
||||||
},
|
|
||||||
details: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
trim: false,
|
|
||||||
},
|
|
||||||
attackBonus: {
|
|
||||||
type: String,
|
|
||||||
defaultValue: "strengthMod + proficiencyBonus",
|
|
||||||
optional: true,
|
|
||||||
trim: false,
|
|
||||||
},
|
|
||||||
damage: {
|
|
||||||
type: String,
|
|
||||||
defaultValue: "1d8 + {strengthMod}",
|
|
||||||
optional: true,
|
|
||||||
trim: false,
|
|
||||||
},
|
|
||||||
damageType: {
|
|
||||||
type: String,
|
|
||||||
allowedValues: [
|
|
||||||
"bludgeoning",
|
|
||||||
"piercing",
|
|
||||||
"slashing",
|
|
||||||
"acid",
|
|
||||||
"cold",
|
|
||||||
"fire",
|
|
||||||
"force",
|
|
||||||
"lightning",
|
|
||||||
"necrotic",
|
|
||||||
"poison",
|
|
||||||
"psychic",
|
|
||||||
"radiant",
|
|
||||||
"thunder",
|
|
||||||
],
|
|
||||||
defaultValue: "slashing",
|
|
||||||
},
|
|
||||||
//the id of the feature, buff or item that created this effect
|
|
||||||
parent: {
|
|
||||||
type: Schemas.Parent
|
|
||||||
},
|
|
||||||
color: {
|
|
||||||
type: String,
|
|
||||||
allowedValues: _.pluck(colorOptions, "key"),
|
|
||||||
defaultValue: "q",
|
|
||||||
},
|
|
||||||
enabled: {
|
|
||||||
type: Boolean,
|
|
||||||
defaultValue: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Attacks.attachSchema(Schemas.Attack);
|
|
||||||
|
|
||||||
Attacks.attachBehaviour("softRemovable");
|
|
||||||
makeChild(Attacks, ["name", "enabled"]); //children of lots of things
|
|
||||||
|
|
||||||
Attacks.after.insert(function (userId, attack) {
|
|
||||||
//Check to see if this attack's parent is a spell, if so, mirror prepared state to enabled
|
|
||||||
if (attack.parent.collection === "Spells") {
|
|
||||||
var parentSpell = Spells.findOne(attack.parent.id);
|
|
||||||
if (parentSpell.prepared === "unprepared") {
|
|
||||||
Attacks.update(attack._id, {$set: {enabled: false}});
|
|
||||||
} else if (parentSpell.prepared === "prepared" || "always") {
|
|
||||||
Attacks.update(attack._id, {$set: {enabled: true}});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Attacks.allow(CHARACTER_SUBSCHEMA_ALLOW);
|
|
||||||
Attacks.deny(CHARACTER_SUBSCHEMA_DENY);
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
Buffs = new Mongo.Collection("buffs");
|
|
||||||
|
|
||||||
Schemas.Buff = new SimpleSchema({
|
|
||||||
charId: {
|
|
||||||
type: String,
|
|
||||||
regEx: SimpleSchema.RegEx.Id,
|
|
||||||
index: 1,
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
trim: false,
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
trim: false,
|
|
||||||
},
|
|
||||||
enabled: {
|
|
||||||
type: Boolean,
|
|
||||||
defaultValue: true,
|
|
||||||
},
|
|
||||||
type: {
|
|
||||||
type: String,
|
|
||||||
allowedValues: [
|
|
||||||
"inate", //this should be "innate", but changing it could be problematic
|
|
||||||
"custom",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
"lifeTime.total": {
|
|
||||||
type: Number,
|
|
||||||
defaultValue: 0, //0 is infinite
|
|
||||||
min: 0,
|
|
||||||
},
|
|
||||||
"lifeTime.spent": {
|
|
||||||
type: Number,
|
|
||||||
defaultValue: 0,
|
|
||||||
min: 0,
|
|
||||||
},
|
|
||||||
color: {
|
|
||||||
type: String,
|
|
||||||
allowedValues: _.pluck(colorOptions, "key"),
|
|
||||||
defaultValue: "q",
|
|
||||||
},
|
|
||||||
appliedBy: { //the charId of whoever applied the buff
|
|
||||||
type: String,
|
|
||||||
regEx: SimpleSchema.RegEx.Id,
|
|
||||||
},
|
|
||||||
appliedByDetails: {//the name and collection of the thing that applied the buff
|
|
||||||
type: Object,
|
|
||||||
optional: true,
|
|
||||||
},
|
|
||||||
"appliedByDetails.name": {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
"appliedByDetails.collection": {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Buffs.attachSchema(Schemas.Buff);
|
|
||||||
|
|
||||||
Buffs.attachBehaviour("softRemovable");
|
|
||||||
makeParent(Buffs, ["name", "enabled"]); //parents of effects, attacks, proficiencies
|
|
||||||
|
|
||||||
Buffs.allow(CHARACTER_SUBSCHEMA_ALLOW);
|
|
||||||
Buffs.deny(CHARACTER_SUBSCHEMA_DENY);
|
|
||||||
@@ -1,589 +0,0 @@
|
|||||||
//set up the collection for characters
|
|
||||||
Characters = new Mongo.Collection("characters");
|
|
||||||
|
|
||||||
Schemas.Character = new SimpleSchema({
|
|
||||||
//strings
|
|
||||||
name: {type: String, defaultValue: "", trim: false, optional: true},
|
|
||||||
urlName: {type: String, defaultValue: "-", trim: false, optional: true},
|
|
||||||
alignment: {type: String, defaultValue: "", trim: false, optional: true},
|
|
||||||
gender: {type: String, defaultValue: "", trim: false, optional: true},
|
|
||||||
race: {type: String, defaultValue: "", trim: false, optional: true},
|
|
||||||
picture: {type: String, defaultValue: "", trim: true, optional: true},
|
|
||||||
description: {type: String, defaultValue: "", trim: false, optional: true},
|
|
||||||
personality: {type: String, defaultValue: "", trim: false, optional: true},
|
|
||||||
ideals: {type: String, defaultValue: "", trim: false, optional: true},
|
|
||||||
bonds: {type: String, defaultValue: "", trim: false, optional: true},
|
|
||||||
flaws: {type: String, defaultValue: "", trim: false, optional: true},
|
|
||||||
backstory: {type: String, defaultValue: "", trim: false, optional: true},
|
|
||||||
|
|
||||||
//attributes
|
|
||||||
//ability scores
|
|
||||||
strength: {type: Schemas.Attribute},
|
|
||||||
dexterity: {type: Schemas.Attribute},
|
|
||||||
constitution: {type: Schemas.Attribute},
|
|
||||||
intelligence: {type: Schemas.Attribute},
|
|
||||||
wisdom: {type: Schemas.Attribute},
|
|
||||||
charisma: {type: Schemas.Attribute},
|
|
||||||
|
|
||||||
//stats
|
|
||||||
hitPoints: {type: Schemas.Attribute},
|
|
||||||
tempHP: {type: Schemas.Attribute},
|
|
||||||
experience: {type: Schemas.Attribute},
|
|
||||||
proficiencyBonus: {type: Schemas.Attribute},
|
|
||||||
speed: {type: Schemas.Attribute},
|
|
||||||
weight: {type: Schemas.Attribute},
|
|
||||||
age: {type: Schemas.Attribute},
|
|
||||||
ageRate: {type: Schemas.Attribute},
|
|
||||||
armor: {type: Schemas.Attribute},
|
|
||||||
carryMultiplier: {type: Schemas.Attribute},
|
|
||||||
|
|
||||||
//resources
|
|
||||||
level1SpellSlots: {type: Schemas.Attribute},
|
|
||||||
level2SpellSlots: {type: Schemas.Attribute},
|
|
||||||
level3SpellSlots: {type: Schemas.Attribute},
|
|
||||||
level4SpellSlots: {type: Schemas.Attribute},
|
|
||||||
level5SpellSlots: {type: Schemas.Attribute},
|
|
||||||
level6SpellSlots: {type: Schemas.Attribute},
|
|
||||||
level7SpellSlots: {type: Schemas.Attribute},
|
|
||||||
level8SpellSlots: {type: Schemas.Attribute},
|
|
||||||
level9SpellSlots: {type: Schemas.Attribute},
|
|
||||||
ki: {type: Schemas.Attribute},
|
|
||||||
sorceryPoints: {type: Schemas.Attribute},
|
|
||||||
rages: {type: Schemas.Attribute},
|
|
||||||
superiorityDice: {type: Schemas.Attribute},
|
|
||||||
expertiseDice: {type: Schemas.Attribute},
|
|
||||||
|
|
||||||
//specific features
|
|
||||||
rageDamage: {type: Schemas.Attribute},
|
|
||||||
|
|
||||||
//hit dice
|
|
||||||
d6HitDice: {type: Schemas.Attribute},
|
|
||||||
d8HitDice: {type: Schemas.Attribute},
|
|
||||||
d10HitDice: {type: Schemas.Attribute},
|
|
||||||
d12HitDice: {type: Schemas.Attribute},
|
|
||||||
|
|
||||||
//vulnerabilities
|
|
||||||
acidMultiplier: {type: Schemas.Attribute},
|
|
||||||
bludgeoningMultiplier: {type: Schemas.Attribute},
|
|
||||||
coldMultiplier: {type: Schemas.Attribute},
|
|
||||||
fireMultiplier: {type: Schemas.Attribute},
|
|
||||||
forceMultiplier: {type: Schemas.Attribute},
|
|
||||||
lightningMultiplier: {type: Schemas.Attribute},
|
|
||||||
necroticMultiplier: {type: Schemas.Attribute},
|
|
||||||
piercingMultiplier: {type: Schemas.Attribute},
|
|
||||||
poisonMultiplier: {type: Schemas.Attribute},
|
|
||||||
psychicMultiplier: {type: Schemas.Attribute},
|
|
||||||
radiantMultiplier: {type: Schemas.Attribute},
|
|
||||||
slashingMultiplier: {type: Schemas.Attribute},
|
|
||||||
thunderMultiplier: {type: Schemas.Attribute},
|
|
||||||
|
|
||||||
//skills
|
|
||||||
//saves
|
|
||||||
strengthSave: {type: Schemas.Skill},
|
|
||||||
"strengthSave.ability": {type: String, defaultValue: "strength"},
|
|
||||||
|
|
||||||
dexteritySave: {type: Schemas.Skill},
|
|
||||||
"dexteritySave.ability": {type: String, defaultValue: "dexterity"},
|
|
||||||
|
|
||||||
constitutionSave:{type: Schemas.Skill},
|
|
||||||
"constitutionSave.ability": {type: String, defaultValue: "constitution"},
|
|
||||||
|
|
||||||
intelligenceSave:{type: Schemas.Skill},
|
|
||||||
"intelligenceSave.ability": {type: String, defaultValue: "intelligence"},
|
|
||||||
|
|
||||||
wisdomSave: {type: Schemas.Skill},
|
|
||||||
"wisdomSave.ability": {type: String, defaultValue: "wisdom"},
|
|
||||||
|
|
||||||
charismaSave: {type: Schemas.Skill},
|
|
||||||
"charismaSave.ability": {type: String, defaultValue: "charisma"},
|
|
||||||
|
|
||||||
//skill skills
|
|
||||||
acrobatics: {type: Schemas.Skill},
|
|
||||||
"acrobatics.ability": {type: String, defaultValue: "dexterity"},
|
|
||||||
|
|
||||||
animalHandling: {type: Schemas.Skill},
|
|
||||||
"animalHandling.ability": {type: String, defaultValue: "wisdom"},
|
|
||||||
|
|
||||||
arcana: {type: Schemas.Skill},
|
|
||||||
"arcana.ability": {type: String, defaultValue: "intelligence"},
|
|
||||||
|
|
||||||
athletics: {type: Schemas.Skill},
|
|
||||||
"athletics.ability": {type: String, defaultValue: "strength"},
|
|
||||||
|
|
||||||
deception: {type: Schemas.Skill},
|
|
||||||
"deception.ability": {type: String, defaultValue: "charisma"},
|
|
||||||
|
|
||||||
history: {type: Schemas.Skill},
|
|
||||||
"history.ability": {type: String, defaultValue: "intelligence"},
|
|
||||||
|
|
||||||
insight: {type: Schemas.Skill},
|
|
||||||
"insight.ability": {type: String, defaultValue: "wisdom"},
|
|
||||||
|
|
||||||
intimidation: {type: Schemas.Skill},
|
|
||||||
"intimidation.ability": {type: String, defaultValue: "charisma"},
|
|
||||||
|
|
||||||
investigation: {type: Schemas.Skill},
|
|
||||||
"investigation.ability": {type: String, defaultValue: "intelligence"},
|
|
||||||
|
|
||||||
medicine: {type: Schemas.Skill},
|
|
||||||
"medicine.ability": {type: String, defaultValue: "wisdom"},
|
|
||||||
|
|
||||||
nature: {type: Schemas.Skill},
|
|
||||||
"nature.ability": {type: String, defaultValue: "intelligence"},
|
|
||||||
|
|
||||||
perception: {type: Schemas.Skill},
|
|
||||||
"perception.ability": {type: String, defaultValue: "wisdom"},
|
|
||||||
|
|
||||||
performance: {type: Schemas.Skill},
|
|
||||||
"performance.ability": {type: String, defaultValue: "charisma"},
|
|
||||||
|
|
||||||
persuasion: {type: Schemas.Skill},
|
|
||||||
"persuasion.ability": {type: String, defaultValue: "charisma"},
|
|
||||||
|
|
||||||
religion: {type: Schemas.Skill},
|
|
||||||
"religion.ability": {type: String, defaultValue: "intelligence"},
|
|
||||||
|
|
||||||
sleightOfHand: {type: Schemas.Skill},
|
|
||||||
"sleightOfHand.ability": {type: String, defaultValue: "dexterity"},
|
|
||||||
|
|
||||||
stealth: {type: Schemas.Skill},
|
|
||||||
"stealth.ability": {type: String, defaultValue: "dexterity"},
|
|
||||||
|
|
||||||
survival: {type: Schemas.Skill},
|
|
||||||
"survival.ability": {type: String, defaultValue: "wisdom"},
|
|
||||||
|
|
||||||
//Mechanical Skills
|
|
||||||
initiative: {type: Schemas.Skill},
|
|
||||||
"initiative.ability": {type: String, defaultValue: "dexterity"},
|
|
||||||
|
|
||||||
dexterityArmor: {type: Schemas.Skill},
|
|
||||||
"dexterityArmor.ability": {type: String, defaultValue: "dexterity"},
|
|
||||||
|
|
||||||
//mechanics
|
|
||||||
deathSave: {type: Schemas.DeathSave},
|
|
||||||
|
|
||||||
//permissions
|
|
||||||
party: {type: String, regEx: SimpleSchema.RegEx.Id, optional: true},
|
|
||||||
owner: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
|
|
||||||
readers: {type: [String], regEx: SimpleSchema.RegEx.Id, defaultValue: [], index: 1},
|
|
||||||
writers: {type: [String], regEx: SimpleSchema.RegEx.Id, defaultValue: [], index: 1},
|
|
||||||
color: {
|
|
||||||
type: String,
|
|
||||||
allowedValues: _.pluck(colorOptions, "key"),
|
|
||||||
defaultValue: "q",
|
|
||||||
},
|
|
||||||
//TODO add per-character settings
|
|
||||||
//how many experiences to load at a time in XP table
|
|
||||||
"settings.experiencesInc": {type: Number, defaultValue: 20},
|
|
||||||
//slowed down by carrying too much?
|
|
||||||
"settings.useVariantEncumbrance": {type: Boolean, defaultValue: false},
|
|
||||||
"settings.useStandardEncumbrance": {type: Boolean, defaultValue: true},
|
|
||||||
//hide spellcasting
|
|
||||||
"settings.hideSpellcasting": {type: Boolean, defaultValue: false},
|
|
||||||
//show to anyone with link
|
|
||||||
"settings.viewPermission": {
|
|
||||||
type: String,
|
|
||||||
defaultValue: "whitelist",
|
|
||||||
allowedValues: ["whitelist", "public"],
|
|
||||||
index: 1,
|
|
||||||
},
|
|
||||||
"settings.swapStatAndModifier": {type: Boolean, defaultValue: false},
|
|
||||||
"settings.exportFeatures": {type: Boolean, defaultValue: true},
|
|
||||||
"settings.exportAttacks": {type: Boolean, defaultValue: true},
|
|
||||||
"settings.exportDescription": {type: Boolean, defaultValue: true},
|
|
||||||
"settings.newUserExperience": {type: Boolean, optional: true},
|
|
||||||
});
|
|
||||||
|
|
||||||
Characters.attachSchema(Schemas.Character);
|
|
||||||
|
|
||||||
var attributeBase = preventLoop(function(charId, statName){
|
|
||||||
check(statName, String);
|
|
||||||
//if it's a damage multiplier, we treat it specially
|
|
||||||
if (_.contains(DAMAGE_MULTIPLIERS, statName)){
|
|
||||||
var invulnerabilityCount = Effects.find({
|
|
||||||
charId: charId,
|
|
||||||
stat: statName,
|
|
||||||
enabled: true,
|
|
||||||
operation: "mul",
|
|
||||||
value: 0,
|
|
||||||
}).count();
|
|
||||||
if (invulnerabilityCount) return 0;
|
|
||||||
var resistCount = Effects.find({
|
|
||||||
charId: charId,
|
|
||||||
stat: statName,
|
|
||||||
enabled: true,
|
|
||||||
operation: "mul",
|
|
||||||
value: 0.5,
|
|
||||||
}).count();
|
|
||||||
var vulnCount = Effects.find({
|
|
||||||
charId: charId,
|
|
||||||
stat: statName,
|
|
||||||
enabled: true,
|
|
||||||
operation: "mul",
|
|
||||||
value: 2,
|
|
||||||
}).count();
|
|
||||||
if (!resistCount && !vulnCount){
|
|
||||||
return 1;
|
|
||||||
} else if (resistCount && !vulnCount){
|
|
||||||
return 0.5;
|
|
||||||
} else if (!resistCount && vulnCount){
|
|
||||||
return 2;
|
|
||||||
} else {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var value;
|
|
||||||
var base = 0;
|
|
||||||
var add = 0;
|
|
||||||
var mul = 1;
|
|
||||||
var min = Number.NEGATIVE_INFINITY;
|
|
||||||
var max = Number.POSITIVE_INFINITY;
|
|
||||||
|
|
||||||
Effects.find({
|
|
||||||
charId: charId,
|
|
||||||
stat: statName,
|
|
||||||
enabled: true,
|
|
||||||
operation: {$in: ["base", "add", "mul", "min", "max"]},
|
|
||||||
}).forEach(function(effect) {
|
|
||||||
value = evaluateEffect(charId, effect);
|
|
||||||
if (effect.operation === "base"){
|
|
||||||
if (value > base) base = value;
|
|
||||||
} else if (effect.operation === "add"){
|
|
||||||
add += value;
|
|
||||||
} else if (effect.operation === "mul"){
|
|
||||||
mul *= value;
|
|
||||||
} else if (effect.operation === "min"){
|
|
||||||
if (value > min) min = value;
|
|
||||||
} else if (effect.operation === "max"){
|
|
||||||
if (value < max) max = value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var result = (base + add) * mul;
|
|
||||||
if (result < min) result = min;
|
|
||||||
if (result > max) result = max;
|
|
||||||
// Don't round carry multiplier
|
|
||||||
if (statName === "carryMultiplier"){
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
return Math.floor(result);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (Meteor.isClient) {
|
|
||||||
Template.registerHelper("characterCalculate", function(func, charId, input) {
|
|
||||||
try {
|
|
||||||
return Characters.calculate[func](charId, input);
|
|
||||||
} catch (e){
|
|
||||||
if (!Characters.calculate[func]){
|
|
||||||
throw new Error(func + "is not a function name");
|
|
||||||
} else {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//create a local memoize with a argument concatenating hash function
|
|
||||||
var memoize = function(f) {
|
|
||||||
if (Meteor.isServer) return f;
|
|
||||||
return Tracker.memoize(f, function() {
|
|
||||||
return _.reduce(arguments, function(memo, arg) {
|
|
||||||
return memo + arg;
|
|
||||||
}, "");
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
//memoize funcitons that have finds and slow loops
|
|
||||||
Characters.calculate = {
|
|
||||||
getField: function(charId, fieldName) {
|
|
||||||
var fieldSelector = {};
|
|
||||||
fieldSelector[fieldName] = 1;
|
|
||||||
var char = Characters.findOne(charId, {fields: fieldSelector});
|
|
||||||
if (!char) return;
|
|
||||||
var field = char[fieldName];
|
|
||||||
if (field === undefined){
|
|
||||||
throw new Meteor.Error(
|
|
||||||
"getField failed",
|
|
||||||
"getField could not find field " +
|
|
||||||
fieldName +
|
|
||||||
" in character " +
|
|
||||||
char._id
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return field;
|
|
||||||
},
|
|
||||||
fieldValue: function(charId, fieldName) {
|
|
||||||
if (!Schemas.Character.schema(fieldName)){
|
|
||||||
throw new Meteor.Error(
|
|
||||||
"Field not found",
|
|
||||||
"Character's schema does not contain a field called: " + fieldName
|
|
||||||
);
|
|
||||||
}
|
|
||||||
//duck typing to get the right value function
|
|
||||||
//.ability implies skill
|
|
||||||
if (Schemas.Character.schema(fieldName + ".ability")){
|
|
||||||
return Characters.calculate.skillMod(charId, fieldName);
|
|
||||||
}
|
|
||||||
//adjustment implies attribute
|
|
||||||
if (Schemas.Character.schema(fieldName + ".adjustment")){
|
|
||||||
return Characters.calculate.attributeValue(charId, fieldName);
|
|
||||||
}
|
|
||||||
//fall back to just returning the field itself
|
|
||||||
return Characters.calculate.getField(charId, fieldName);
|
|
||||||
},
|
|
||||||
attributeValue: memoize(function(charId, attributeName){
|
|
||||||
var attribute = Characters.calculate.getField(charId, attributeName);
|
|
||||||
if (!attribute) return;
|
|
||||||
//base value
|
|
||||||
var value = Characters.calculate.attributeBase(charId, attributeName);
|
|
||||||
//plus adjustment
|
|
||||||
value += attribute.adjustment;
|
|
||||||
return value;
|
|
||||||
}),
|
|
||||||
attributeBase: memoize(function(charId, attributeName){
|
|
||||||
return attributeBase(charId, attributeName);
|
|
||||||
}),
|
|
||||||
skillMod: memoize(preventLoop(function(charId, skillName){
|
|
||||||
var skill = Characters.calculate.getField(charId, skillName);
|
|
||||||
if (!skill) return;
|
|
||||||
//get the final value of the ability score
|
|
||||||
var ability = Characters.calculate.attributeValue(charId, skill.ability);
|
|
||||||
|
|
||||||
//base modifier
|
|
||||||
var mod = +getMod(ability);
|
|
||||||
|
|
||||||
//multiply proficiency bonus by largest value in proficiency array
|
|
||||||
var prof = Characters.calculate.proficiency(charId, skillName);
|
|
||||||
|
|
||||||
//add multiplied proficiency bonus to modifier
|
|
||||||
mod += prof * Characters.calculate.attributeValue(charId, "proficiencyBonus");
|
|
||||||
|
|
||||||
//apply all effects
|
|
||||||
var value;
|
|
||||||
var add = 0;
|
|
||||||
var mul = 1;
|
|
||||||
var min = Number.NEGATIVE_INFINITY;
|
|
||||||
var max = Number.POSITIVE_INFINITY;
|
|
||||||
|
|
||||||
Effects.find({
|
|
||||||
charId: charId,
|
|
||||||
stat: skillName,
|
|
||||||
enabled: true,
|
|
||||||
operation: {$in: ["base", "add", "mul", "min", "max"]},
|
|
||||||
}).forEach(function(effect) {
|
|
||||||
value = evaluateEffect(charId, effect);
|
|
||||||
if (effect.operation === "add"){
|
|
||||||
add += value;
|
|
||||||
} else if (effect.operation === "mul"){
|
|
||||||
mul *= value;
|
|
||||||
} else if (effect.operation === "min"){
|
|
||||||
if (value > min) min = value;
|
|
||||||
} else if (effect.operation === "max"){
|
|
||||||
if (value < max) max = value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
var result = (mod + add) * mul;
|
|
||||||
if (result < min) result = min;
|
|
||||||
if (result > max) result = max;
|
|
||||||
|
|
||||||
return Math.floor(result);
|
|
||||||
})),
|
|
||||||
proficiency: memoize(function(charId, skillName){
|
|
||||||
//return largest value in proficiency array
|
|
||||||
var prof = Proficiencies.findOne(
|
|
||||||
{charId: charId, name: skillName, enabled: true},
|
|
||||||
{sort: {value: -1}}
|
|
||||||
);
|
|
||||||
return prof && prof.value || 0;
|
|
||||||
}),
|
|
||||||
passiveSkill: memoize(function(charId, skillName){
|
|
||||||
var mod = +Characters.calculate.skillMod(charId, skillName);
|
|
||||||
var value = 10 + mod;
|
|
||||||
Effects.find(
|
|
||||||
{charId: charId, stat: skillName, enabled: true, operation: "passiveAdd"}
|
|
||||||
).forEach(function(effect){
|
|
||||||
value += evaluateEffect(charId, effect);
|
|
||||||
});
|
|
||||||
var advantage = Characters.calculate.advantage(charId, skillName);
|
|
||||||
value += 5 * advantage;
|
|
||||||
return Math.floor(value);
|
|
||||||
}),
|
|
||||||
advantage: memoize(function(charId, skillName){
|
|
||||||
var advantage = Effects.find(
|
|
||||||
{charId: charId, stat: skillName, enabled: true, operation: "advantage"}
|
|
||||||
).count();
|
|
||||||
var disadvantage = Effects.find(
|
|
||||||
{charId: charId, stat: skillName, enabled: true, operation: "disadvantage"}
|
|
||||||
).count();
|
|
||||||
if (advantage && !disadvantage) return 1;
|
|
||||||
if (disadvantage && !advantage) return -1;
|
|
||||||
return 0;
|
|
||||||
}),
|
|
||||||
abilityMod: function(charId, attribute){
|
|
||||||
return getMod(
|
|
||||||
Characters.calculate.attributeValue(charId, attribute)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
passiveAbility: function(charId, attribute){
|
|
||||||
var mod = +getMod(Characters.calculate.attributeValue(charId, attribute));
|
|
||||||
return 10 + mod;
|
|
||||||
},
|
|
||||||
xpLevel: function(charId){
|
|
||||||
var xp = Characters.calculate.experience(charId);
|
|
||||||
for (var i = 0; i < 19; i++){
|
|
||||||
if (xp < XP_TABLE[i]){
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (xp > 355000) return 20;
|
|
||||||
return 0;
|
|
||||||
},
|
|
||||||
level: memoize(function(charId){
|
|
||||||
var level = 0;
|
|
||||||
Classes.find({charId: charId}).forEach(function(cls){
|
|
||||||
level += cls.level;
|
|
||||||
});
|
|
||||||
return level;
|
|
||||||
}),
|
|
||||||
experience: memoize(function(charId){
|
|
||||||
var xp = 0;
|
|
||||||
Experiences.find(
|
|
||||||
{charId: charId},
|
|
||||||
{fields: {value: 1}}
|
|
||||||
).forEach(function(e){
|
|
||||||
xp += e.value;
|
|
||||||
});
|
|
||||||
return xp;
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
var deprecated = function() {
|
|
||||||
//var err = new Error("this function has been deprecated");
|
|
||||||
var name = "";
|
|
||||||
if (Template.instance()){
|
|
||||||
name = Template.instance().view.name;
|
|
||||||
}
|
|
||||||
var logString = "this function has been deprecated \n";
|
|
||||||
if (name){
|
|
||||||
logString += "View: " + name + "\n\n";
|
|
||||||
}
|
|
||||||
//logString += err.stack + "\n\n---------------------\n\n";
|
|
||||||
console.log(logString);
|
|
||||||
};
|
|
||||||
|
|
||||||
//functions and calculated values.
|
|
||||||
//These functions can only rely on this._id since no other
|
|
||||||
//field is likely to be attached to all returned characters
|
|
||||||
Characters.helpers({
|
|
||||||
//returns the value stored in the field requested
|
|
||||||
//will set up dependencies on just that field
|
|
||||||
getField : function(fieldName){
|
|
||||||
deprecated();
|
|
||||||
return Characters.calculate.getField(this._id, fieldName);
|
|
||||||
},
|
|
||||||
//returns the value of a field
|
|
||||||
fieldValue : function(fieldName){
|
|
||||||
deprecated();
|
|
||||||
return Characters.calculate.fieldValue(this._id, fieldName);
|
|
||||||
},
|
|
||||||
attributeValue: function(attributeName){
|
|
||||||
deprecated();
|
|
||||||
return Characters.calculate.attributeValue(this._id, attributeName);
|
|
||||||
},
|
|
||||||
attributeBase: function(attributeName){
|
|
||||||
deprecated();
|
|
||||||
return Characters.calculate.attributeBase(this._id, attributeName);
|
|
||||||
},
|
|
||||||
skillMod: function(skillName){
|
|
||||||
deprecated();
|
|
||||||
return Characters.calculate.skillMod(this._id, skillName);
|
|
||||||
},
|
|
||||||
proficiency: function(skillName){
|
|
||||||
deprecated();
|
|
||||||
return Characters.calculate.proficiency(this._id, skillName);
|
|
||||||
},
|
|
||||||
passiveSkill: function(skillName){
|
|
||||||
deprecated();
|
|
||||||
return Characters.calculate.passiveSkill(this._id, skillName);
|
|
||||||
},
|
|
||||||
advantage: function(skillName){
|
|
||||||
deprecated();
|
|
||||||
return Characters.calculate.advantage(this._id, skillName);
|
|
||||||
},
|
|
||||||
abilityMod: function(attribute){
|
|
||||||
deprecated();
|
|
||||||
return Characters.calculate.abilityMod(this._id, attribute);
|
|
||||||
},
|
|
||||||
passiveAbility: function(attribute){
|
|
||||||
deprecated();
|
|
||||||
return Characters.calculate.passiveAbility(this._id, attribute);
|
|
||||||
},
|
|
||||||
xpLevel: function(){
|
|
||||||
deprecated();
|
|
||||||
return Characters.calculate.xpLevel(this._id);
|
|
||||||
},
|
|
||||||
level: function(){
|
|
||||||
deprecated();
|
|
||||||
return Characters.calculate.level(this._id);
|
|
||||||
},
|
|
||||||
experience: function(){
|
|
||||||
deprecated();
|
|
||||||
return Characters.calculate.experience(this._id);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
//clean up all data related to that character before removing it
|
|
||||||
if (Meteor.isServer){
|
|
||||||
Characters.after.remove(function(userId, character) {
|
|
||||||
Actions .remove({charId: character._id});
|
|
||||||
Attacks .remove({charId: character._id});
|
|
||||||
Buffs .remove({charId: character._id});
|
|
||||||
Classes .remove({charId: character._id});
|
|
||||||
CustomBuffs .remove({charId: character._id});
|
|
||||||
Effects .remove({charId: character._id});
|
|
||||||
Experiences .remove({charId: character._id});
|
|
||||||
Features .remove({charId: character._id});
|
|
||||||
Notes .remove({charId: character._id});
|
|
||||||
Proficiencies .remove({charId: character._id});
|
|
||||||
SpellLists .remove({charId: character._id});
|
|
||||||
Items .remove({charId: character._id});
|
|
||||||
Containers .remove({charId: character._id});
|
|
||||||
});
|
|
||||||
Characters.after.update(function(userId, doc, fieldNames, modifier, options) {
|
|
||||||
if (_.contains(fieldNames, "name")){
|
|
||||||
var urlName = getSlug(doc.name, {maintainCase: true}) || "-";
|
|
||||||
Characters.update(doc._id, {$set: {urlName}});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Characters.before.insert(function(userId, doc) {
|
|
||||||
doc.urlName = getSlug(doc.name, {maintainCase: true}) || "-";
|
|
||||||
// The first character a user creates should have the new user experience
|
|
||||||
if (!Characters.find({owner: userId}).count()){
|
|
||||||
doc.settings.newUserExperience = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Characters.allow({
|
|
||||||
insert: function(userId, doc) {
|
|
||||||
// the user must be logged in, and the document must be owned by the user
|
|
||||||
return (userId && doc.owner === userId);
|
|
||||||
},
|
|
||||||
update: function(userId, doc, fields, modifier) {
|
|
||||||
// can only change documents you have write access to
|
|
||||||
return doc.owner === userId ||
|
|
||||||
_.contains(doc.writers, userId);
|
|
||||||
},
|
|
||||||
remove: function(userId, doc) {
|
|
||||||
// can only remove your own documents
|
|
||||||
return doc.owner === userId;
|
|
||||||
},
|
|
||||||
fetch: ["owner", "writers"],
|
|
||||||
});
|
|
||||||
|
|
||||||
Characters.deny({
|
|
||||||
update: function(userId, docs, fields, modifier) {
|
|
||||||
// can't change owners
|
|
||||||
return _.contains(fields, "owner");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
Classes = new Mongo.Collection("classes");
|
|
||||||
|
|
||||||
Schemas.Class = new SimpleSchema({
|
|
||||||
charId: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
|
|
||||||
name: {type: String, optional: true, trim: false},
|
|
||||||
level: {type: Number},
|
|
||||||
createdAt: {
|
|
||||||
type: Date,
|
|
||||||
autoValue: function() {
|
|
||||||
if (this.isInsert) {
|
|
||||||
return new Date();
|
|
||||||
} else if (this.isUpsert) {
|
|
||||||
return {$setOnInsert: new Date()};
|
|
||||||
} else {
|
|
||||||
this.unset();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
color: {
|
|
||||||
type: String,
|
|
||||||
allowedValues: _.pluck(colorOptions, "key"),
|
|
||||||
defaultValue: "q",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Classes.attachSchema(Schemas.Class);
|
|
||||||
|
|
||||||
Classes.attachBehaviour("softRemovable");
|
|
||||||
makeParent(Classes, "name"); //parents of effects and attacks
|
|
||||||
|
|
||||||
Classes.allow(CHARACTER_SUBSCHEMA_ALLOW);
|
|
||||||
Classes.deny(CHARACTER_SUBSCHEMA_DENY);
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
Conditions = new Mongo.Collection("conditions");
|
|
||||||
|
|
||||||
Schemas.Conditions = new SimpleSchema({
|
|
||||||
charId: {
|
|
||||||
type: String,
|
|
||||||
regEx: SimpleSchema.RegEx.Id,
|
|
||||||
index: 1,
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
trim: false,
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
trim: false,
|
|
||||||
},
|
|
||||||
"lifeTime.total": {
|
|
||||||
type: Number,
|
|
||||||
defaultValue: 0, //0 is infinite
|
|
||||||
min: 0,
|
|
||||||
},
|
|
||||||
"lifeTime.spent": {
|
|
||||||
type: Number,
|
|
||||||
defaultValue: 0,
|
|
||||||
min: 0,
|
|
||||||
},
|
|
||||||
color: {
|
|
||||||
type: String,
|
|
||||||
allowedValues: _.pluck(colorOptions, "key"),
|
|
||||||
defaultValue: "q",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Conditions.attachSchema(Schemas.Conditions);
|
|
||||||
|
|
||||||
Conditions.attachBehaviour("softRemovable");
|
|
||||||
makeParent(Conditions, ["name"]); //parents of effects, attacks, proficiencies
|
|
||||||
|
|
||||||
Conditions.allow(CHARACTER_SUBSCHEMA_ALLOW);
|
|
||||||
Conditions.deny(CHARACTER_SUBSCHEMA_DENY);
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
CustomBuffs = new Mongo.Collection("customBuffs");
|
|
||||||
|
|
||||||
Schemas.CustomBuff = new SimpleSchema({
|
|
||||||
charId: {
|
|
||||||
type: String,
|
|
||||||
regEx: SimpleSchema.RegEx.Id,
|
|
||||||
index: 1,
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
trim: false,
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
trim: false,
|
|
||||||
},
|
|
||||||
target: {
|
|
||||||
type: String,
|
|
||||||
allowedValues: [
|
|
||||||
"self",
|
|
||||||
"others",
|
|
||||||
"both"
|
|
||||||
],
|
|
||||||
defaultValue: "self",
|
|
||||||
},
|
|
||||||
enabled: {
|
|
||||||
type: Boolean,
|
|
||||||
autoValue: function(){
|
|
||||||
return false;
|
|
||||||
//enabled is ALWAYS false on these, so that its children are also not enabled, so that the buff templates have no effects.
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"lifeTime.total": {
|
|
||||||
type: Number,
|
|
||||||
defaultValue: 0, //0 is infinite
|
|
||||||
min: 0,
|
|
||||||
},
|
|
||||||
//the id of the feature, buff or item that creates this buff
|
|
||||||
parent: {
|
|
||||||
type: Schemas.Parent,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
CustomBuffs.attachSchema(Schemas.CustomBuff);
|
|
||||||
|
|
||||||
CustomBuffs.attachBehaviour("softRemovable");
|
|
||||||
makeParent(CustomBuffs, ["name", "enabled"]); //parents of effects, attacks, proficiencies. Since this represents a template, "enabled" is always false.
|
|
||||||
makeChild(CustomBuffs); //children of lots of things
|
|
||||||
|
|
||||||
CustomBuffs.allow(CHARACTER_SUBSCHEMA_ALLOW);
|
|
||||||
CustomBuffs.deny(CHARACTER_SUBSCHEMA_DENY);
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
Effects = new Mongo.Collection("effects");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Effects are reason-value attached to skills and abilities
|
|
||||||
* that modify their final value or presentation in some way
|
|
||||||
*/
|
|
||||||
Schemas.Effect = new SimpleSchema({
|
|
||||||
charId: {
|
|
||||||
type: String,
|
|
||||||
regEx: SimpleSchema.RegEx.Id,
|
|
||||||
index: 1,
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
optional: true, //TODO make necessary if there is no owner
|
|
||||||
trim: false,
|
|
||||||
},
|
|
||||||
operation: {
|
|
||||||
type: String,
|
|
||||||
defaultValue: "add",
|
|
||||||
allowedValues: [
|
|
||||||
"base",
|
|
||||||
"proficiency",
|
|
||||||
"add",
|
|
||||||
"mul",
|
|
||||||
"min",
|
|
||||||
"max",
|
|
||||||
"advantage",
|
|
||||||
"disadvantage",
|
|
||||||
"passiveAdd",
|
|
||||||
"fail",
|
|
||||||
"conditional",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
value: {
|
|
||||||
type: Number,
|
|
||||||
decimal: true,
|
|
||||||
optional: true,
|
|
||||||
},
|
|
||||||
calculation: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
trim: false,
|
|
||||||
},
|
|
||||||
//the thing that created this effect
|
|
||||||
parent: {
|
|
||||||
type: Schemas.Parent
|
|
||||||
},
|
|
||||||
//which stat the effect is applied to
|
|
||||||
stat: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
},
|
|
||||||
enabled: {
|
|
||||||
type: Boolean,
|
|
||||||
defaultValue: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Effects.attachSchema(Schemas.Effect);
|
|
||||||
|
|
||||||
Effects.attachBehaviour("softRemovable");
|
|
||||||
makeChild(Effects, ["enabled"]); //children of lots of things
|
|
||||||
|
|
||||||
Effects.allow(CHARACTER_SUBSCHEMA_ALLOW);
|
|
||||||
Effects.deny(CHARACTER_SUBSCHEMA_DENY);
|
|
||||||
|
|
||||||
//give characters default character effects
|
|
||||||
Characters.after.insert(function(userId, char) {
|
|
||||||
if (Meteor.isServer) {
|
|
||||||
Effects.insert({
|
|
||||||
charId: char._id,
|
|
||||||
name: "Constitution modifier for each level",
|
|
||||||
stat: "hitPoints",
|
|
||||||
operation: "add",
|
|
||||||
calculation: "level * constitutionMod",
|
|
||||||
parent: {
|
|
||||||
id: char._id,
|
|
||||||
collection: "Characters",
|
|
||||||
group: "Inate",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
Effects.insert({
|
|
||||||
charId: char._id,
|
|
||||||
name: "Proficiency bonus by level",
|
|
||||||
stat: "proficiencyBonus",
|
|
||||||
operation: "add",
|
|
||||||
calculation: "floor(level / 4 + 1.75)",
|
|
||||||
parent: {
|
|
||||||
id: char._id,
|
|
||||||
collection: "Characters",
|
|
||||||
group: "Inate",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
Effects.insert({
|
|
||||||
charId: char._id,
|
|
||||||
name: "Dexterity Armor Bonus",
|
|
||||||
stat: "armor",
|
|
||||||
operation: "add",
|
|
||||||
calculation: "dexterityArmor",
|
|
||||||
parent: {
|
|
||||||
id: char._id,
|
|
||||||
collection: "Characters",
|
|
||||||
group: "Inate",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
Effects.insert({
|
|
||||||
charId: char._id,
|
|
||||||
name: "Natural Armor",
|
|
||||||
stat: "armor",
|
|
||||||
operation: "base",
|
|
||||||
value: 10,
|
|
||||||
parent: {
|
|
||||||
id: char._id,
|
|
||||||
collection: "Characters",
|
|
||||||
group: "Inate",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
Effects.insert({
|
|
||||||
charId: char._id,
|
|
||||||
name: "Natural Carrying Capacity",
|
|
||||||
stat: "carryMultiplier",
|
|
||||||
operation: "base",
|
|
||||||
value: "1",
|
|
||||||
parent: {
|
|
||||||
id: char._id,
|
|
||||||
collection: "Characters",
|
|
||||||
group: "Inate",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
Experiences = new Mongo.Collection("experience");
|
|
||||||
|
|
||||||
Schemas.Experience = new SimpleSchema({
|
|
||||||
charId: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
|
|
||||||
name: {type: String, optional: true, trim: false, defaultValue: "New Experience"},
|
|
||||||
description: {type: String, optional: true, trim: false},
|
|
||||||
value: {type: Number, defaultValue: 0},
|
|
||||||
dateAdded: {
|
|
||||||
type: Date,
|
|
||||||
autoValue: function() {
|
|
||||||
if (this.isInsert) {
|
|
||||||
return new Date();
|
|
||||||
} else if (this.isUpsert) {
|
|
||||||
return {$setOnInsert: new Date()};
|
|
||||||
} else {
|
|
||||||
this.unset();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Experiences.attachSchema(Schemas.Experience);
|
|
||||||
|
|
||||||
Experiences.attachBehaviour("softRemovable");
|
|
||||||
|
|
||||||
Experiences.allow(CHARACTER_SUBSCHEMA_ALLOW);
|
|
||||||
Experiences.deny(CHARACTER_SUBSCHEMA_DENY);
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
Features = new Mongo.Collection("features");
|
|
||||||
|
|
||||||
Schemas.Feature = new SimpleSchema({
|
|
||||||
charId: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
|
|
||||||
name: {type: String, optional: true, trim: false},
|
|
||||||
description: {type: String, optional: true, trim: false},
|
|
||||||
uses: {type: String, optional: true, trim: false},
|
|
||||||
used: {type: Number, defaultValue: 0},
|
|
||||||
reset: {
|
|
||||||
type: String,
|
|
||||||
allowedValues: ["manual", "longRest", "shortRest"],
|
|
||||||
defaultValue: "manual",
|
|
||||||
},
|
|
||||||
enabled: {type: Boolean, defaultValue: true},
|
|
||||||
alwaysEnabled:{type: Boolean, defaultValue: true},
|
|
||||||
color: {type: String,
|
|
||||||
allowedValues: _.pluck(colorOptions, "key"),
|
|
||||||
defaultValue: "q",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Features.attachSchema(Schemas.Feature);
|
|
||||||
|
|
||||||
Features.helpers({
|
|
||||||
usesLeft: function(){
|
|
||||||
return evaluate(this.charId, this.uses) - this.used;
|
|
||||||
},
|
|
||||||
usesValue: function(){
|
|
||||||
return evaluate(this.charId, this.uses);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Features.attachBehaviour("softRemovable");
|
|
||||||
makeParent(Features, ["name", "enabled"]); //parents of effects and attacks
|
|
||||||
|
|
||||||
Features.allow(CHARACTER_SUBSCHEMA_ALLOW);
|
|
||||||
Features.deny(CHARACTER_SUBSCHEMA_DENY);
|
|
||||||
|
|
||||||
//give characters default feature of base ability scores of 10
|
|
||||||
Characters.after.insert(function(userId, char) {
|
|
||||||
if (Meteor.isServer){
|
|
||||||
var featureId = Features.insert({
|
|
||||||
name: "Base Ability Scores",
|
|
||||||
charId: char._id,
|
|
||||||
enabled: true,
|
|
||||||
alwaysEnabled: true,
|
|
||||||
});
|
|
||||||
Effects.insert({
|
|
||||||
stat: "strength",
|
|
||||||
charId: char._id,
|
|
||||||
parent: {
|
|
||||||
id: featureId,
|
|
||||||
collection: "Features",
|
|
||||||
},
|
|
||||||
operation: "base",
|
|
||||||
value: 10,
|
|
||||||
enabled: true,
|
|
||||||
});
|
|
||||||
Effects.insert({
|
|
||||||
stat: "dexterity",
|
|
||||||
charId: char._id,
|
|
||||||
parent: {
|
|
||||||
id: featureId,
|
|
||||||
collection: "Features",
|
|
||||||
},
|
|
||||||
operation: "base",
|
|
||||||
value: 10,
|
|
||||||
enabled: true,
|
|
||||||
});
|
|
||||||
Effects.insert({
|
|
||||||
stat: "constitution",
|
|
||||||
charId: char._id,
|
|
||||||
parent: {
|
|
||||||
id: featureId,
|
|
||||||
collection: "Features",
|
|
||||||
},
|
|
||||||
operation: "base",
|
|
||||||
value: 10,
|
|
||||||
enabled: true,
|
|
||||||
});
|
|
||||||
Effects.insert({
|
|
||||||
stat: "intelligence",
|
|
||||||
charId: char._id,
|
|
||||||
parent: {
|
|
||||||
id: featureId,
|
|
||||||
collection: "Features",
|
|
||||||
},
|
|
||||||
operation: "base",
|
|
||||||
value: 10,
|
|
||||||
enabled: true,
|
|
||||||
});
|
|
||||||
Effects.insert({
|
|
||||||
stat: "wisdom",
|
|
||||||
charId: char._id,
|
|
||||||
parent: {
|
|
||||||
id: featureId,
|
|
||||||
collection: "Features",
|
|
||||||
},
|
|
||||||
operation: "base",
|
|
||||||
value: 10,
|
|
||||||
enabled: true,
|
|
||||||
});
|
|
||||||
Effects.insert({
|
|
||||||
stat: "charisma",
|
|
||||||
charId: char._id,
|
|
||||||
parent: {
|
|
||||||
id: featureId,
|
|
||||||
collection: "Features",
|
|
||||||
},
|
|
||||||
operation: "base",
|
|
||||||
value: 10,
|
|
||||||
enabled: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
Notes = new Mongo.Collection("notes");
|
|
||||||
|
|
||||||
Schemas.Note = new SimpleSchema({
|
|
||||||
charId: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
|
|
||||||
name: {type: String, optional: true, trim: false},
|
|
||||||
description: {type: String, optional: true, trim: false},
|
|
||||||
color: {
|
|
||||||
type: String,
|
|
||||||
allowedValues:_.pluck(colorOptions, "key"),
|
|
||||||
defaultValue: "q",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Notes.attachSchema(Schemas.Note);
|
|
||||||
|
|
||||||
Notes.attachBehaviour("softRemovable");
|
|
||||||
|
|
||||||
Notes.allow(CHARACTER_SUBSCHEMA_ALLOW);
|
|
||||||
Notes.deny(CHARACTER_SUBSCHEMA_DENY);
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
Proficiencies = new Mongo.Collection("proficiencies");
|
|
||||||
|
|
||||||
Schemas.Proficiency = new SimpleSchema({
|
|
||||||
charId: {
|
|
||||||
type: String,
|
|
||||||
regEx: SimpleSchema.RegEx.Id,
|
|
||||||
index: 1,
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
trim: false,
|
|
||||||
optional: true,
|
|
||||||
},
|
|
||||||
value: {
|
|
||||||
type: Number,
|
|
||||||
allowedValues: [0, 0.5, 1, 2],
|
|
||||||
defaultValue: 1,
|
|
||||||
decimal: true,
|
|
||||||
},
|
|
||||||
type: {
|
|
||||||
type: String,
|
|
||||||
allowedValues: ["skill", "save", "weapon", "armor", "tool", "language"],
|
|
||||||
defaultValue: "skill",
|
|
||||||
},
|
|
||||||
enabled: {
|
|
||||||
type: Boolean,
|
|
||||||
defaultValue: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Proficiencies.attachSchema(Schemas.Proficiency);
|
|
||||||
|
|
||||||
Proficiencies.attachBehaviour("softRemovable");
|
|
||||||
makeChild(Proficiencies, ["enabled"]);
|
|
||||||
|
|
||||||
Proficiencies.allow(CHARACTER_SUBSCHEMA_ALLOW);
|
|
||||||
Proficiencies.deny(CHARACTER_SUBSCHEMA_DENY);
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
SpellLists = new Mongo.Collection("spellLists");
|
|
||||||
|
|
||||||
Schemas.SpellLists = new SimpleSchema({
|
|
||||||
charId: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
|
|
||||||
name: {type: String, optional: true, trim: false},
|
|
||||||
description: {type: String, optional: true, trim: false},
|
|
||||||
saveDC: {type: String, optional: true, trim: false},
|
|
||||||
attackBonus: {type: String, optional: true, trim: false},
|
|
||||||
maxPrepared: {type: String, optional: true, trim: false},
|
|
||||||
color: {
|
|
||||||
type: String,
|
|
||||||
allowedValues: _.pluck(colorOptions, "key"),
|
|
||||||
defaultValue: "q",
|
|
||||||
},
|
|
||||||
"settings.showUnprepared": {type: Boolean, defaultValue: true},
|
|
||||||
});
|
|
||||||
|
|
||||||
SpellLists.attachSchema(Schemas.SpellLists);
|
|
||||||
|
|
||||||
SpellLists.helpers({
|
|
||||||
numPrepared: function(){
|
|
||||||
var num = 0;
|
|
||||||
Spells.find(
|
|
||||||
{charId: this.charId, listId: this._id, prepared: 1},
|
|
||||||
{fields: {prepareCost: 1}}
|
|
||||||
).forEach(function(spell){
|
|
||||||
num += spell.prepareCost;
|
|
||||||
});
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
SpellLists.attachBehaviour("softRemovable");
|
|
||||||
makeParent(SpellLists); //parents of spells
|
|
||||||
|
|
||||||
SpellLists.allow(CHARACTER_SUBSCHEMA_ALLOW);
|
|
||||||
SpellLists.deny(CHARACTER_SUBSCHEMA_DENY);
|
|
||||||
@@ -1,252 +0,0 @@
|
|||||||
Spells = new Mongo.Collection("spells");
|
|
||||||
|
|
||||||
Schemas.Spell = new SimpleSchema({
|
|
||||||
charId: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
|
|
||||||
prepared: {
|
|
||||||
type: String,
|
|
||||||
defaultValue: "prepared",
|
|
||||||
allowedValues: ["prepared", "unprepared", "always"],
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
trim: false,
|
|
||||||
defaultValue: "New Spell",
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
trim: false,
|
|
||||||
},
|
|
||||||
castingTime: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
defaultValue: "action",
|
|
||||||
trim: false,
|
|
||||||
},
|
|
||||||
range: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
trim: false,
|
|
||||||
},
|
|
||||||
duration: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
trim: false,
|
|
||||||
defaultValue: "Instantaneous",
|
|
||||||
},
|
|
||||||
"components.verbal": {type: Boolean, defaultValue: false},
|
|
||||||
"components.somatic": {type: Boolean, defaultValue: false},
|
|
||||||
"components.concentration": {type: Boolean, defaultValue: false},
|
|
||||||
"components.material": {type: String, optional: true},
|
|
||||||
ritual: {
|
|
||||||
type: Boolean,
|
|
||||||
defaultValue: false,
|
|
||||||
},
|
|
||||||
level: {
|
|
||||||
type: Number,
|
|
||||||
defaultValue: 1,
|
|
||||||
},
|
|
||||||
school: {
|
|
||||||
type: String,
|
|
||||||
defaultValue: "Abjuration",
|
|
||||||
allowedValues: magicSchools,
|
|
||||||
},
|
|
||||||
color: {
|
|
||||||
type: String,
|
|
||||||
allowedValues: _.pluck(colorOptions, "key"),
|
|
||||||
defaultValue: "q",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Spells.attachSchema(Schemas.Spell);
|
|
||||||
|
|
||||||
Spells.attachBehaviour("softRemovable");
|
|
||||||
makeChild(Spells); //children of spell lists
|
|
||||||
makeParent(Spells, ["name", "enabled"]); //parents of attacks
|
|
||||||
|
|
||||||
Spells.after.update(function (userId, spell, fieldNames) {
|
|
||||||
//Update prepared state of spell and child attacks to be enabled or not
|
|
||||||
if (_.contains(fieldNames, "prepared")) {
|
|
||||||
var childAttacks = Attacks.find({"parent.id": spell._id}).fetch();
|
|
||||||
if (spell.prepared === "unprepared") {
|
|
||||||
_.each(childAttacks, function(attack){
|
|
||||||
Attacks.update(attack._id, {$set: {enabled: false}});
|
|
||||||
});
|
|
||||||
} else if (spell.prepared === "prepared" || "always") {
|
|
||||||
_.each(childAttacks, function(attack){
|
|
||||||
Attacks.update(attack._id, {$set: {enabled: true}});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Spells.allow(CHARACTER_SUBSCHEMA_ALLOW);
|
|
||||||
Spells.deny(CHARACTER_SUBSCHEMA_DENY);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var checkMovePermission = function(spellId, parent, destinationOnly) {
|
|
||||||
var spell = Spells.findOne(spellId);
|
|
||||||
if (!spell)
|
|
||||||
throw new Meteor.Error("No such spell",
|
|
||||||
"An spell could not be found to move");
|
|
||||||
//handle permissions
|
|
||||||
var permission;
|
|
||||||
|
|
||||||
if (!destinationOnly) { //if we're not modifying the origin, only the destination
|
|
||||||
permission = Meteor.call("canWriteCharacter", spell.charId);
|
|
||||||
if (!permission){
|
|
||||||
throw new Meteor.Error("Access denied",
|
|
||||||
"Not permitted to move spells from this character");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (parent.collection === "Characters"){
|
|
||||||
permission = Meteor.call("canWriteCharacter", parent.id);
|
|
||||||
if (!permission){
|
|
||||||
throw new Meteor.Error("Access denied",
|
|
||||||
"Not permitted to move spells to this character");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var parentCollectionObject = global[parent.collection];
|
|
||||||
var parentObject = null;
|
|
||||||
if (parentCollectionObject)
|
|
||||||
parentObject = parentCollectionObject.findOne(
|
|
||||||
parent.id, {fields: {_id: 1, charId: 1}}
|
|
||||||
);
|
|
||||||
if (!parentObject) throw new Meteor.Error(
|
|
||||||
"Invalid parent",
|
|
||||||
"The destination parent " + parent.id +
|
|
||||||
" does not exist in the collection " + parent.collection
|
|
||||||
);
|
|
||||||
if (parentObject.charId){
|
|
||||||
permission = Meteor.call("canWriteCharacter", parentObject.charId);
|
|
||||||
if (!permission){
|
|
||||||
throw new Meteor.Error("Access denied",
|
|
||||||
"Not permitted to move spells to this character");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var moveSpell = function(spellId, targetCollection, targetId) {
|
|
||||||
var spell = Spells.findOne(spellId);
|
|
||||||
if (!spell) return;
|
|
||||||
targetCollection = targetCollection || spell.parent.collection;
|
|
||||||
targetId = targetId || spell.parent.id;
|
|
||||||
|
|
||||||
if (Meteor.isServer) {
|
|
||||||
checkMovePermission(spellId, {collection: targetCollection, id: targetId}, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetCollection == "Characters") { //then we are copying the spell to a different character.
|
|
||||||
targetList = SpellLists.findOne({"charId": targetId});
|
|
||||||
targetListId = targetList && targetList._id;
|
|
||||||
if (!targetListId) {
|
|
||||||
targetListId = SpellLists.insert({ //create a spell list if we don't already have one
|
|
||||||
name: "New SpellList",
|
|
||||||
charId: targetId,
|
|
||||||
saveDC: "8 + intelligenceMod + proficiencyBonus",
|
|
||||||
attackBonus: "intelligenceMod + proficiencyBonus",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Spells.update(
|
|
||||||
spellId,
|
|
||||||
{$set: {
|
|
||||||
charId: targetId,
|
|
||||||
"parent.collection": "SpellLists",
|
|
||||||
"parent.id": targetListId,
|
|
||||||
}}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else { //we are moving the spell within the same character
|
|
||||||
//update the spell provided the update will actually change something
|
|
||||||
if (
|
|
||||||
spell.parent.collection !== targetCollection ||
|
|
||||||
spell.parent.id !== targetId
|
|
||||||
){
|
|
||||||
Spells.update(
|
|
||||||
spellId,
|
|
||||||
{$set: {
|
|
||||||
"parent.collection": targetCollection,
|
|
||||||
"parent.id": targetId,
|
|
||||||
}}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var copySpell = function(spellId, targetCollection, targetId) {
|
|
||||||
var spell = Spells.findOne(spellId);
|
|
||||||
if (!spell) return;
|
|
||||||
targetCollection = targetCollection || spell.parent.collection;
|
|
||||||
targetId = targetId || spell.parent.id;
|
|
||||||
|
|
||||||
if (Meteor.isServer) {
|
|
||||||
checkMovePermission(spellId, {collection: targetCollection, id: targetId}, true); //we're only reading from the source character
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (targetCollection == "Characters") { //then we are copying the spell to a different character.
|
|
||||||
targetList = SpellLists.findOne({"charId": targetId});
|
|
||||||
targetListId = targetList && targetList._id;
|
|
||||||
if (!targetListId) {
|
|
||||||
targetListId = SpellLists.insert({ //create a spell list if we don't already have one
|
|
||||||
name: "New SpellList",
|
|
||||||
charId: targetId,
|
|
||||||
saveDC: "8 + intelligenceMod + proficiencyBonus",
|
|
||||||
attackBonus: "intelligenceMod + proficiencyBonus",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
newSpell = _.clone(spell);
|
|
||||||
delete newSpell._id;
|
|
||||||
newSpellId = Spells.insert(newSpell); //add a new copy of the spell
|
|
||||||
Spells.update(
|
|
||||||
newSpellId,
|
|
||||||
{$set: {
|
|
||||||
charId: targetId,
|
|
||||||
"parent.collection": "SpellLists",
|
|
||||||
"parent.id": targetListId,
|
|
||||||
}}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else { //else we are copying the spell within the same character
|
|
||||||
newSpell = _.clone(spell);
|
|
||||||
delete newSpell._id;
|
|
||||||
newSpellId = Spells.insert(newSpell); //add a new copy of the spell
|
|
||||||
Spells.update(
|
|
||||||
newSpellId,
|
|
||||||
{$set: {
|
|
||||||
"parent.collection": targetCollection,
|
|
||||||
"parent.id": targetId,
|
|
||||||
}}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
Meteor.methods({
|
|
||||||
moveSpellToList: function(spellId, spellListId) {
|
|
||||||
check(spellId, String);
|
|
||||||
check(spellListId, String);
|
|
||||||
moveSpell(spellId, "SpellLists", spellListId);
|
|
||||||
},
|
|
||||||
copySpellToList: function(spellId, spellListId) {
|
|
||||||
check(spellId, String);
|
|
||||||
check(spellListId, String);
|
|
||||||
copySpell(spellId, "SpellLists", spellListId);
|
|
||||||
},
|
|
||||||
moveSpellToCharacter: function(spellId, charId) {
|
|
||||||
check(spellId, String);
|
|
||||||
check(charId, String);
|
|
||||||
moveSpell(spellId, "Characters", charId);
|
|
||||||
},
|
|
||||||
copySpellToCharacter: function(spellId, charId) {
|
|
||||||
check(spellId, String);
|
|
||||||
check(charId, String);
|
|
||||||
copySpell(spellId, "Characters", charId);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
/*
|
|
||||||
* Adjustments make instantaneous changes to the value of some attribute
|
|
||||||
* Damage, healing and resource cost/recovery are all adjustments
|
|
||||||
*/
|
|
||||||
Schemas.Adjustment = new SimpleSchema({
|
|
||||||
//which stat the adjustment is applied to
|
|
||||||
stat: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
},
|
|
||||||
//the value added to the stat
|
|
||||||
value: {
|
|
||||||
type: Number,
|
|
||||||
decimal: true,
|
|
||||||
optional: true,
|
|
||||||
},
|
|
||||||
calculation: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
Schemas.Attribute = new SimpleSchema({
|
|
||||||
//the temporary shift of the attribute
|
|
||||||
//should be zero after reset
|
|
||||||
adjustment: {
|
|
||||||
type: Number,
|
|
||||||
defaultValue: 0,
|
|
||||||
},
|
|
||||||
reset: {
|
|
||||||
type: String,
|
|
||||||
defaultValue: "longRest",
|
|
||||||
allowedValues: ["longRest", "shortRest"],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
Schemas.Skill = new SimpleSchema({
|
|
||||||
//attribute name that this skill used as base mod for roll
|
|
||||||
ability: {type: String, defaultValue: ""},
|
|
||||||
});
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
TemporaryHitPoints = new Mongo.Collection("temporaryHitPoints");
|
|
||||||
|
|
||||||
Schemas.TemporaryHitPoints = new SimpleSchema({
|
|
||||||
charId: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
|
|
||||||
name: {type: String, optional: true},
|
|
||||||
maximum: {type: Number, defaultValue: 0, min: 0, max: 500},
|
|
||||||
used: {type: Number, defaultValue: 0, min: 0, max: 500},
|
|
||||||
deleteOnZero:{type: Boolean, defaultValue: false},
|
|
||||||
dateAdded: {
|
|
||||||
type: Date,
|
|
||||||
autoValue: function() {
|
|
||||||
if (this.isInsert) {
|
|
||||||
return new Date();
|
|
||||||
} else if (this.isUpsert) {
|
|
||||||
return {$setOnInsert: new Date()};
|
|
||||||
} else {
|
|
||||||
this.unset();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
TemporaryHitPoints.attachSchema(Schemas.TemporaryHitPoints);
|
|
||||||
|
|
||||||
TemporaryHitPoints.helpers({
|
|
||||||
left: function(){
|
|
||||||
return this.maximum - this.used;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//remove the temporary hit points when they hit zero
|
|
||||||
TemporaryHitPoints.after.update(
|
|
||||||
function(userId, thp, fieldNames, modifier, options){
|
|
||||||
if (thp.used >= thp.maximum && thp.deleteOnZero){
|
|
||||||
TemporaryHitPoints.remove(thp._id);
|
|
||||||
}
|
|
||||||
}, {fetchPrevious: false}
|
|
||||||
);
|
|
||||||
|
|
||||||
TemporaryHitPoints.allow(CHARACTER_SUBSCHEMA_ALLOW);
|
|
||||||
TemporaryHitPoints.deny(CHARACTER_SUBSCHEMA_DENY);
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
//set up the collection for containers
|
|
||||||
Containers = new Mongo.Collection("containers");
|
|
||||||
|
|
||||||
Schemas.Container = new SimpleSchema({
|
|
||||||
name: {type: String, optional: true, trim: false},
|
|
||||||
charId: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
|
|
||||||
isCarried: {type: Boolean},
|
|
||||||
weight: {type: Number, min: 0, defaultValue: 0, decimal: true},
|
|
||||||
value: {type: Number, min: 0, defaultValue: 0, decimal: true},
|
|
||||||
description:{type: String, optional: true, trim: false},
|
|
||||||
color: {
|
|
||||||
type: String,
|
|
||||||
allowedValues: _.pluck(colorOptions, "key"),
|
|
||||||
defaultValue: "q",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Containers.attachSchema(Schemas.Container);
|
|
||||||
|
|
||||||
Containers.helpers({
|
|
||||||
contentsValue: function(){
|
|
||||||
var value = 0;
|
|
||||||
Items.find(
|
|
||||||
{"parent.id": this._id},
|
|
||||||
{fields: {quantity: 1, value: 1}}
|
|
||||||
).forEach(function(item){
|
|
||||||
value += item.totalValue();
|
|
||||||
});
|
|
||||||
return value;
|
|
||||||
},
|
|
||||||
totalValue: function(){
|
|
||||||
return this.contentsValue() + this.value;
|
|
||||||
},
|
|
||||||
contentsWeight: function(){
|
|
||||||
var weight = 0;
|
|
||||||
Items.find(
|
|
||||||
{"parent.id": this._id},
|
|
||||||
{fields: {quantity: 1, weight: 1}}
|
|
||||||
).forEach(function(item){
|
|
||||||
weight += item.totalWeight();
|
|
||||||
});
|
|
||||||
return weight;
|
|
||||||
},
|
|
||||||
totalWeight: function(){
|
|
||||||
return this.contentsWeight() + this.weight;
|
|
||||||
},
|
|
||||||
moveToCharacter: function(characterId){
|
|
||||||
if (this.charId === characterId) return;
|
|
||||||
Items.update(this._id, {$set: {charId: characterId}});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Containers.attachBehaviour("softRemovable");
|
|
||||||
makeParent(Containers); //parents of items
|
|
||||||
|
|
||||||
Containers.allow(CHARACTER_SUBSCHEMA_ALLOW);
|
|
||||||
@@ -1,268 +0,0 @@
|
|||||||
Items = new Mongo.Collection("items");
|
|
||||||
|
|
||||||
Schemas.Item = new SimpleSchema({
|
|
||||||
name: {type: String, optional: true, trim: false, defaultValue: "New Item"},
|
|
||||||
plural: {type: String, optional: true, trim: false},
|
|
||||||
description:{type: String, optional: true, trim: false},
|
|
||||||
charId: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1}, //id of owner
|
|
||||||
quantity: {type: Number, min: 0, defaultValue: 1},
|
|
||||||
weight: {type: Number, min: 0, defaultValue: 0, decimal: true},
|
|
||||||
value: {type: Number, min: 0, defaultValue: 0, decimal: true},
|
|
||||||
enabled: {type: Boolean, defaultValue: false},
|
|
||||||
requiresAttunement: {type: Boolean, defaultValue: false},
|
|
||||||
"settings.showIncrement": {type: Boolean, defaultValue: false},
|
|
||||||
color: {
|
|
||||||
type: String,
|
|
||||||
allowedValues: _.pluck(colorOptions, "key"),
|
|
||||||
defaultValue: "q",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Items.attachSchema(Schemas.Item);
|
|
||||||
|
|
||||||
var checkMovePermission = function(itemId, parent) {
|
|
||||||
var item = Items.findOne(itemId);
|
|
||||||
if (!item)
|
|
||||||
throw new Meteor.Error("No such item",
|
|
||||||
"An item could not be found to move");
|
|
||||||
//handle permissions
|
|
||||||
var permission = Meteor.call("canWriteCharacter", item.charId);
|
|
||||||
if (!permission){
|
|
||||||
throw new Meteor.Error("Access denied",
|
|
||||||
"Not permitted to move items from this character");
|
|
||||||
}
|
|
||||||
if (parent.collection === "Characters"){
|
|
||||||
permission = Meteor.call("canWriteCharacter", parent.id);
|
|
||||||
if (!permission){
|
|
||||||
throw new Meteor.Error("Access denied",
|
|
||||||
"Not permitted to move items to this character");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var parentCollectionObject = global[parent.collection];
|
|
||||||
var parentObject = null;
|
|
||||||
if (parentCollectionObject)
|
|
||||||
parentObject = parentCollectionObject.findOne(
|
|
||||||
parent.id, {fields: {_id: 1, charId: 1}}
|
|
||||||
);
|
|
||||||
if (!parentObject) throw new Meteor.Error(
|
|
||||||
"Invalid parent",
|
|
||||||
"The destination parent " + parent.id +
|
|
||||||
" does not exist in the collection " + parent.collection
|
|
||||||
);
|
|
||||||
if (parentObject.charId){
|
|
||||||
permission = Meteor.call("canWriteCharacter", parentObject.charId);
|
|
||||||
if (!permission){
|
|
||||||
throw new Meteor.Error("Access denied",
|
|
||||||
"Not permitted to move items to this character");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var moveItem = function(itemId, enable, parentCollection, parentId) {
|
|
||||||
var item = Items.findOne(itemId);
|
|
||||||
if (!item) return;
|
|
||||||
parentCollection = parentCollection || item.parent.collection;
|
|
||||||
parentId = parentId || item.parent.id;
|
|
||||||
|
|
||||||
if (Meteor.isServer) {
|
|
||||||
checkMovePermission(itemId, {collection: parentCollection, id: parentId});
|
|
||||||
}
|
|
||||||
|
|
||||||
//update the item provided the update will actually change something
|
|
||||||
if (
|
|
||||||
item.parent.collection !== parentCollection ||
|
|
||||||
item.parent.id !== parentId ||
|
|
||||||
item.enabled !== enable
|
|
||||||
){
|
|
||||||
Items.update(
|
|
||||||
itemId,
|
|
||||||
{$set: {
|
|
||||||
"parent.collection": parentCollection,
|
|
||||||
"parent.id": parentId,
|
|
||||||
enabled: enable,
|
|
||||||
}}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Meteor.methods({
|
|
||||||
moveItemToParent: function(itemId, parent) {
|
|
||||||
check(itemId, String);
|
|
||||||
check(parent, {collection: String, id: String});
|
|
||||||
moveItem(itemId, false, parent.collection, parent.id);
|
|
||||||
},
|
|
||||||
moveItemToCharacter: function(itemId, charId) {
|
|
||||||
check(itemId, String);
|
|
||||||
check(charId, String);
|
|
||||||
moveItem(itemId, false, "Characters", charId);
|
|
||||||
},
|
|
||||||
moveItemToContainer: function(itemId, containerId) {
|
|
||||||
check(itemId, String);
|
|
||||||
check(containerId, String);
|
|
||||||
moveItem(itemId, false, "Containers", containerId);
|
|
||||||
},
|
|
||||||
equipItem: function(itemId, charId){
|
|
||||||
check(itemId, String);
|
|
||||||
check(charId, String);
|
|
||||||
moveItem(itemId, true, "Characters", charId);
|
|
||||||
},
|
|
||||||
unequipItem: function(itemId, charId){
|
|
||||||
check(itemId, String);
|
|
||||||
check(charId, String);
|
|
||||||
moveItem(itemId, false, "Characters", charId);
|
|
||||||
},
|
|
||||||
splitItemToParent: function(itemId, moveQuantity, parent){
|
|
||||||
check(itemId, String);
|
|
||||||
check(moveQuantity, Number);
|
|
||||||
check(parent, {id: String, collection: String});
|
|
||||||
|
|
||||||
//get the item
|
|
||||||
var item = Items.findOne(itemId);
|
|
||||||
if (!item) return;
|
|
||||||
|
|
||||||
//don't bother moving nothing
|
|
||||||
if (moveQuantity <= 0 || item.quantity <= 0){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
//ensure we are only moving up to the current stack size
|
|
||||||
if (item.quantity < moveQuantity){
|
|
||||||
moveQuantity = this.quantity;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Meteor.isServer) {
|
|
||||||
checkMovePermission(itemId, parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
//create a new item stack
|
|
||||||
var newStack = _.omit(EJSON.clone(item), "_id");
|
|
||||||
newStack.parent = parent;
|
|
||||||
newStack.quantity = moveQuantity;
|
|
||||||
|
|
||||||
//find out if we have an exact replica in the destination
|
|
||||||
var query = _.omit(newStack, ["parent", "quantity"]);
|
|
||||||
query["parent.collection"] = newStack.parent.collection;
|
|
||||||
query["parent.id"] = newStack.parent.id;
|
|
||||||
query._id = {$ne: itemId}; //make sure we don't join it to itself
|
|
||||||
var existingStack = Items.findOne(query);
|
|
||||||
if (existingStack){
|
|
||||||
//increase the existing stack's size
|
|
||||||
Items.update(
|
|
||||||
existingStack._id,
|
|
||||||
{$inc: {quantity: moveQuantity}}
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
//insert the new stack
|
|
||||||
Items.insert(newStack, function(err, id){
|
|
||||||
if (err) throw err;
|
|
||||||
//copy the children also
|
|
||||||
Meteor.call("cloneChildren", item._id, {collection: "Items", id: id});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//reduce the old stack's size
|
|
||||||
var oldQuantity = item.quantity - moveQuantity;
|
|
||||||
if (oldQuantity === 0){
|
|
||||||
Items.remove(itemId);
|
|
||||||
} else {
|
|
||||||
Items.update(itemId, {$set: {quantity: oldQuantity}});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Items.helpers({
|
|
||||||
totalValue: function(){
|
|
||||||
return this.value * this.quantity;
|
|
||||||
},
|
|
||||||
totalWeight: function(){
|
|
||||||
return this.weight * this.quantity;
|
|
||||||
},
|
|
||||||
pluralName: function(){
|
|
||||||
if (this.plural && this.quantity !== 1){
|
|
||||||
return this.plural;
|
|
||||||
} else {
|
|
||||||
return this.name;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Items.before.update(function(userId, doc, fieldNames, modifier, options){
|
|
||||||
if (
|
|
||||||
modifier && modifier.$set && modifier.$set.enabled && //we are equipping this item
|
|
||||||
!(
|
|
||||||
modifier.$set["parent.collection"] === "Characters" &&
|
|
||||||
modifier.$set["parent.id"]
|
|
||||||
) //and we haven"t specified a character to equip to
|
|
||||||
){
|
|
||||||
//equip it to the current character
|
|
||||||
modifier.$set["parent.collection"] = "Characters";
|
|
||||||
modifier.$set["parent.id"] = doc.charId;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Items.attachBehaviour("softRemovable");
|
|
||||||
makeChild(Items); //children of containers
|
|
||||||
makeParent(Items, ["name", "enabled"]); //parents of effects and attacks
|
|
||||||
|
|
||||||
Items.allow(CHARACTER_SUBSCHEMA_ALLOW);
|
|
||||||
|
|
||||||
//give characters default items
|
|
||||||
Characters.after.insert(function(userId, char) {
|
|
||||||
if (Meteor.isServer){
|
|
||||||
var containerId = Containers.insert({
|
|
||||||
name: "Coin Pouch",
|
|
||||||
charId: char._id,
|
|
||||||
isCarried: true,
|
|
||||||
description: "A sturdy pouch for coins",
|
|
||||||
color: "d",
|
|
||||||
});
|
|
||||||
Items.insert({
|
|
||||||
name: "Gold piece",
|
|
||||||
plural: "Gold pieces",
|
|
||||||
charId: char._id,
|
|
||||||
quantity: 0,
|
|
||||||
weight: 0.02,
|
|
||||||
value: 1,
|
|
||||||
color: "n",
|
|
||||||
parent: {
|
|
||||||
id: containerId,
|
|
||||||
collection: "Containers",
|
|
||||||
},
|
|
||||||
settings: {
|
|
||||||
showIncrement: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
Items.insert({
|
|
||||||
name: "Silver piece",
|
|
||||||
plural: "Silver pieces",
|
|
||||||
charId: char._id,
|
|
||||||
quantity: 0,
|
|
||||||
weight: 0.02,
|
|
||||||
value: 0.1,
|
|
||||||
color: "q",
|
|
||||||
parent: {
|
|
||||||
id: containerId,
|
|
||||||
collection: "Containers",
|
|
||||||
},
|
|
||||||
settings: {
|
|
||||||
showIncrement: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
Items.insert({
|
|
||||||
name: "Copper piece",
|
|
||||||
plural: "Copper pieces",
|
|
||||||
charId: char._id,
|
|
||||||
quantity: 0,
|
|
||||||
weight: 0.02,
|
|
||||||
value: 0.01,
|
|
||||||
color: "s",
|
|
||||||
parent: {
|
|
||||||
id: containerId,
|
|
||||||
collection: "Containers",
|
|
||||||
},
|
|
||||||
settings: {
|
|
||||||
showIncrement: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
Libraries = new Mongo.Collection("library");
|
|
||||||
|
|
||||||
Schemas.Library = new SimpleSchema({
|
|
||||||
name: {type: String},
|
|
||||||
owner: {type: String, regEx: SimpleSchema.RegEx.Id},
|
|
||||||
readers: {type: [String], regEx: SimpleSchema.RegEx.Id, defaultValue: []},
|
|
||||||
writers: {type: [String], regEx: SimpleSchema.RegEx.Id, defaultValue: []},
|
|
||||||
public: {type: Boolean, defaultValue: false},
|
|
||||||
});
|
|
||||||
|
|
||||||
Libraries.attachSchema(Schemas.Library);
|
|
||||||
|
|
||||||
Libraries.allow({
|
|
||||||
insert(userId, doc) {
|
|
||||||
return userId && doc.owner === userId;
|
|
||||||
},
|
|
||||||
update(userId, doc, fields, modifier) {
|
|
||||||
return canEdit(userId, doc);
|
|
||||||
},
|
|
||||||
remove(userId, doc) {
|
|
||||||
return canEdit(userId, doc);
|
|
||||||
},
|
|
||||||
fetch: ["owner", "writers"],
|
|
||||||
});
|
|
||||||
|
|
||||||
Libraries.deny({
|
|
||||||
// For now, only admins can manage libraries
|
|
||||||
insert(userId, doc){
|
|
||||||
var user = Meteor.users.findOne(userId);
|
|
||||||
return !user || !_.contains(user.roles, "admin");
|
|
||||||
},
|
|
||||||
update(userId, doc, fields, modifier) {
|
|
||||||
// Can't change owners
|
|
||||||
return _.contains(fields, "owner")
|
|
||||||
},
|
|
||||||
fetch: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
const canEdit = function(userId, library){
|
|
||||||
if (!userId || !library) return;
|
|
||||||
return library.owner === userId || _.contains(library.writers, userId);
|
|
||||||
};
|
|
||||||
|
|
||||||
Libraries.canEdit = function(userId, libraryId){
|
|
||||||
const library = Libraries.findOne(libraryId);
|
|
||||||
return canEdit(userId, library);
|
|
||||||
};
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
Schemas.LibraryAttacks = new SimpleSchema({
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
defaultValue: "New Attack",
|
|
||||||
trim: false,
|
|
||||||
},
|
|
||||||
details: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
trim: false,
|
|
||||||
},
|
|
||||||
attackBonus: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
trim: false,
|
|
||||||
},
|
|
||||||
damage: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
trim: false,
|
|
||||||
},
|
|
||||||
damageType: {
|
|
||||||
type: String,
|
|
||||||
allowedValues: [
|
|
||||||
"bludgeoning",
|
|
||||||
"piercing",
|
|
||||||
"slashing",
|
|
||||||
"acid",
|
|
||||||
"cold",
|
|
||||||
"fire",
|
|
||||||
"force",
|
|
||||||
"lightning",
|
|
||||||
"necrotic",
|
|
||||||
"poison",
|
|
||||||
"psychic",
|
|
||||||
"radiant",
|
|
||||||
"thunder",
|
|
||||||
],
|
|
||||||
defaultValue: "slashing",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
Schemas.LibraryEffects = new SimpleSchema({
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
optional: true, //TODO make necessary if there is no owner
|
|
||||||
trim: false,
|
|
||||||
},
|
|
||||||
operation: {
|
|
||||||
type: String,
|
|
||||||
defaultValue: "add",
|
|
||||||
allowedValues: [
|
|
||||||
"base",
|
|
||||||
"proficiency",
|
|
||||||
"add",
|
|
||||||
"mul",
|
|
||||||
"min",
|
|
||||||
"max",
|
|
||||||
"advantage",
|
|
||||||
"disadvantage",
|
|
||||||
"passiveAdd",
|
|
||||||
"fail",
|
|
||||||
"conditional",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
// Effects either have a value OR a calculation
|
|
||||||
value: {
|
|
||||||
type: Number,
|
|
||||||
decimal: true,
|
|
||||||
optional: true,
|
|
||||||
},
|
|
||||||
calculation: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
trim: false,
|
|
||||||
},
|
|
||||||
//which stat the effect is applied to
|
|
||||||
stat: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
LibraryItems = new Mongo.Collection("libraryItems");
|
|
||||||
|
|
||||||
Schemas.LibraryItems = new SimpleSchema({
|
|
||||||
libraryName:{type: String, optional: true, trim: false},
|
|
||||||
name: {type: String, defaultValue: "New Item", trim: false},
|
|
||||||
plural: {type: String, optional: true, trim: false},
|
|
||||||
description:{type: String, optional: true, trim: false},
|
|
||||||
quantity: {type: Number, min: 0, defaultValue: 1},
|
|
||||||
weight: {type: Number, min: 0, defaultValue: 0, decimal: true},
|
|
||||||
value: {type: Number, min: 0, defaultValue: 0, decimal: true},
|
|
||||||
requiresAttunement: {type: Boolean, defaultValue: false},
|
|
||||||
|
|
||||||
library: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
|
|
||||||
|
|
||||||
"settings.category": {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
allowedValues: [
|
|
||||||
"adventuringGear", "armor", "weapons", "tools",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
"settings.showIncrement": {
|
|
||||||
type: Boolean,
|
|
||||||
defaultValue: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
effects: {type: [Schemas.LibraryEffects], defaultValue: []},
|
|
||||||
attacks: {type: [Schemas.LibraryAttacks], defaultValue: []},
|
|
||||||
});
|
|
||||||
|
|
||||||
LibraryItems.attachSchema(Schemas.LibraryItems);
|
|
||||||
|
|
||||||
LibraryItems.allow({
|
|
||||||
insert(userId, doc) {
|
|
||||||
return Libraries.canEdit(userId, doc.library);
|
|
||||||
},
|
|
||||||
update(userId, doc, fields, modifier) {
|
|
||||||
return Libraries.canEdit(userId, doc.library);
|
|
||||||
},
|
|
||||||
remove(userId, doc) {
|
|
||||||
return Libraries.canEdit(userId, doc.library);
|
|
||||||
},
|
|
||||||
fetch: ["library"],
|
|
||||||
});
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
LibrarySpells = new Mongo.Collection("librarySpells");
|
|
||||||
|
|
||||||
Schemas.LibrarySpells = new SimpleSchema({
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
trim: false,
|
|
||||||
defaultValue: "New Spell",
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
trim: false,
|
|
||||||
},
|
|
||||||
castingTime: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
defaultValue: "action",
|
|
||||||
trim: false,
|
|
||||||
},
|
|
||||||
range: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
trim: false,
|
|
||||||
},
|
|
||||||
duration: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
trim: false,
|
|
||||||
defaultValue: "Instantaneous",
|
|
||||||
},
|
|
||||||
"components.verbal": {type: Boolean, defaultValue: false},
|
|
||||||
"components.somatic": {type: Boolean, defaultValue: false},
|
|
||||||
"components.concentration": {type: Boolean, defaultValue: false},
|
|
||||||
"components.material": {type: String, optional: true},
|
|
||||||
ritual: {
|
|
||||||
type: Boolean,
|
|
||||||
defaultValue: false,
|
|
||||||
},
|
|
||||||
level: {
|
|
||||||
type: Number,
|
|
||||||
defaultValue: 1,
|
|
||||||
},
|
|
||||||
school: {
|
|
||||||
type: String,
|
|
||||||
defaultValue: "Abjuration",
|
|
||||||
allowedValues: magicSchools,
|
|
||||||
},
|
|
||||||
library: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
|
|
||||||
effects: {type: [Schemas.LibraryEffects], defaultValue: []},
|
|
||||||
attacks: {type: [Schemas.LibraryAttacks], defaultValue: []},
|
|
||||||
});
|
|
||||||
|
|
||||||
LibrarySpells.attachSchema(Schemas.LibrarySpells);
|
|
||||||
|
|
||||||
LibrarySpells.allow({
|
|
||||||
insert(userId, doc) {
|
|
||||||
return Libraries.canEdit(userId, doc.library);
|
|
||||||
},
|
|
||||||
update(userId, doc, fields, modifier) {
|
|
||||||
return Libraries.canEdit(userId, doc.library);
|
|
||||||
},
|
|
||||||
remove(userId, doc) {
|
|
||||||
return Libraries.canEdit(userId, doc.library);
|
|
||||||
},
|
|
||||||
fetch: ["library"],
|
|
||||||
});
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
Schemas.UserProfile = new SimpleSchema({
|
|
||||||
username: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Schemas.User = new SimpleSchema({
|
|
||||||
username: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
},
|
|
||||||
profile: {
|
|
||||||
type: Schemas.UserProfile,
|
|
||||||
optional: true,
|
|
||||||
},
|
|
||||||
emails: {
|
|
||||||
type: Array,
|
|
||||||
optional: true,
|
|
||||||
},
|
|
||||||
"emails.$": {
|
|
||||||
type: Object,
|
|
||||||
},
|
|
||||||
"emails.$.address": {
|
|
||||||
type: String,
|
|
||||||
regEx: SimpleSchema.RegEx.Email,
|
|
||||||
},
|
|
||||||
"emails.$.verified": {
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
registered_emails: {
|
|
||||||
type: Array,
|
|
||||||
optional: true,
|
|
||||||
},
|
|
||||||
"registered_emails.$": {
|
|
||||||
type: Object,
|
|
||||||
blackbox: true,
|
|
||||||
},
|
|
||||||
createdAt: {
|
|
||||||
type: Date
|
|
||||||
},
|
|
||||||
services: {
|
|
||||||
type: Object,
|
|
||||||
optional: true,
|
|
||||||
blackbox: true,
|
|
||||||
},
|
|
||||||
roles: {
|
|
||||||
type: Object,
|
|
||||||
optional: true,
|
|
||||||
blackbox: true,
|
|
||||||
},
|
|
||||||
roles: {
|
|
||||||
type: Array,
|
|
||||||
optional: true,
|
|
||||||
},
|
|
||||||
"roles.$": {
|
|
||||||
type: String
|
|
||||||
},
|
|
||||||
// In order to avoid an 'Exception in setInterval callback' from Meteor
|
|
||||||
heartbeat: {
|
|
||||||
type: Date,
|
|
||||||
optional: true,
|
|
||||||
},
|
|
||||||
apiKey: {
|
|
||||||
type: String,
|
|
||||||
index: 1,
|
|
||||||
optional: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Meteor.users.attachSchema(Schemas.User);
|
|
||||||
|
|
||||||
Meteor.users.allow({
|
|
||||||
update: function(userId, doc, fields, modifier) {
|
|
||||||
if (
|
|
||||||
doc._id === userId &&
|
|
||||||
_.contains(fields, "username") &&
|
|
||||||
_.contains(fields, "profile") &&
|
|
||||||
fields.length === 2 &&
|
|
||||||
_.keys(modifier).length === 1 &&
|
|
||||||
modifier.$set &&
|
|
||||||
modifier.$set["profile.username"] &&
|
|
||||||
modifier.$set.username &&
|
|
||||||
_.keys(modifier.$set).length === 2
|
|
||||||
){
|
|
||||||
var expectedUsername = modifier.$set["profile.username"];
|
|
||||||
expectedUsername = expectedUsername.toLowerCase().replace(/\s+/gm, "");
|
|
||||||
if (modifier.$set.username !== expectedUsername){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
var foundUser = Meteor.call("getUserId", expectedUsername);
|
|
||||||
return !foundUser || foundUser === userId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (Meteor.isServer) Meteor.methods({
|
|
||||||
generateMyApiKey() {
|
|
||||||
var user = Meteor.users.findOne(this.userId);
|
|
||||||
if (!user) return;
|
|
||||||
if (user && user.apiKey) return;
|
|
||||||
var apiKey = Random.id(30);
|
|
||||||
Meteor.users.update(this.userId, {$set: {apiKey}});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
Router.map(function() {
|
|
||||||
this.route("vmixCharacter", {
|
|
||||||
path: "/vmix-character/:_id/",
|
|
||||||
where: "server",
|
|
||||||
action: function() {
|
|
||||||
this.response.setHeader("Content-Type", "application/json");
|
|
||||||
var query = this.params.query;
|
|
||||||
var key = query && query.key;
|
|
||||||
ifKeyValid(key, this.response, "vmixCharacter", () =>
|
|
||||||
this.response.end(vMixCharacter(this.params._id))
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
this.route("vmixParty", {
|
|
||||||
path: "/vmix-party/:_id/",
|
|
||||||
where: "server",
|
|
||||||
action: function() {
|
|
||||||
this.response.setHeader("Content-Type", "application/json");
|
|
||||||
var query = this.params.query;
|
|
||||||
var key = query && query.key;
|
|
||||||
ifKeyValid(key, this.response, "vmixParty", () =>
|
|
||||||
this.response.end(vMixParty(this.params._id))
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
this.route("jsonCharacterSheet", {
|
|
||||||
path: "/character/:_id/json",
|
|
||||||
where: "server",
|
|
||||||
action: function() {
|
|
||||||
this.response.setHeader("Content-Type", "application/json");
|
|
||||||
var query = this.params.query;
|
|
||||||
var key = query && query.key;
|
|
||||||
ifKeyValid(key, this.response, "jsonCharacterSheet", () => {
|
|
||||||
if (canViewCharacter(this.params._id, userIdFromKey(key))){
|
|
||||||
this.response.end(JSONExport(this.params._id))
|
|
||||||
} else {
|
|
||||||
this.response.writeHead(403, "You do not have permission to view this character");
|
|
||||||
this.response.end();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
var ifKeyValid = function(apiKey, response, method, callback){
|
|
||||||
if (!apiKey){
|
|
||||||
response.writeHead(403, "You must use an api key to access this api");
|
|
||||||
response.end();
|
|
||||||
} else if (!isKeyValid(apiKey)){
|
|
||||||
response.writeHead(403, "API key is invalid");
|
|
||||||
response.end();
|
|
||||||
} else if (isRateLimited(apiKey, method)){
|
|
||||||
response.writeHead(429, "Too many requests");
|
|
||||||
response.end(JSON.stringify({
|
|
||||||
"timeToReset": rateLimiter.check({apiKey: apiKey, method: method}).timeToReset
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
rateLimiter.increment({apiKey: apiKey, method: method})
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var isKeyValid = function(apiKey){
|
|
||||||
var user = Meteor.users.findOne({apiKey});
|
|
||||||
if (!user) return false;
|
|
||||||
var blackListed = Blacklist.findOne({userId: user._id});
|
|
||||||
return !blackListed;
|
|
||||||
};
|
|
||||||
|
|
||||||
var userIdFromKey = function(apiKey){
|
|
||||||
var user = Meteor.users.findOne({apiKey}); // we know user exists from isKeyValid
|
|
||||||
return user._id;
|
|
||||||
}
|
|
||||||
|
|
||||||
var rateLimiter = new RateLimiter();
|
|
||||||
rateLimiter.addRule({apiKey: String}, 5, 5000);
|
|
||||||
rateLimiter.addRule({apiKey: String, method: "vmixCharacter"}, 2, 10000);
|
|
||||||
rateLimiter.addRule({apiKey: String, method: "vmixParty"}, 2, 10000);
|
|
||||||
rateLimiter.addRule({apiKey: String, method: "jsonCharacterSheet"}, 5, 5000);
|
|
||||||
|
|
||||||
var isRateLimited = function(apiKey, method){
|
|
||||||
const limited = !rateLimiter.check({apiKey: apiKey, method: method}).allowed
|
|
||||||
if (limited) {
|
|
||||||
console.log(`Rate limit hit by API key ${apiKey}`);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,174 +0,0 @@
|
|||||||
Router.configure({
|
|
||||||
loadingTemplate: "loading",
|
|
||||||
layoutTemplate: "layout",
|
|
||||||
trackPageView: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
Router.plugin("ensureSignedIn", {
|
|
||||||
only: [
|
|
||||||
"profile",
|
|
||||||
"characterList",
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
Router.plugin("dataNotFound", {notFoundTemplate: "notFound"});
|
|
||||||
|
|
||||||
var handleSubError = function(e){
|
|
||||||
Session.set("error", {reason: e.reason, href: location.href});
|
|
||||||
Router.go("/error");
|
|
||||||
};
|
|
||||||
|
|
||||||
Router.map(function() {
|
|
||||||
this.route("/", {
|
|
||||||
name: "home",
|
|
||||||
onAfterAction: function() {
|
|
||||||
document.title = appName;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
this.route("characterList", {
|
|
||||||
path: "/characterList",
|
|
||||||
waitOn: function(){
|
|
||||||
return subsManager.subscribe("characterList");
|
|
||||||
},
|
|
||||||
onAfterAction: function() {
|
|
||||||
document.title = appName + " - Characters";
|
|
||||||
},
|
|
||||||
fastRender: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.route("characterSheetNaked", {
|
|
||||||
path: "/character/:_id/",
|
|
||||||
waitOn: function(){
|
|
||||||
return [
|
|
||||||
subsManager.subscribe(
|
|
||||||
"singleCharacter", this.params._id, {onError: handleSubError}
|
|
||||||
),
|
|
||||||
];
|
|
||||||
},
|
|
||||||
action: function(){
|
|
||||||
var _id = this.params._id
|
|
||||||
var character = Characters.findOne(_id);
|
|
||||||
var urlName = character && character.urlName;
|
|
||||||
var path = `\/character\/${_id}\/${urlName || "-"}`;
|
|
||||||
Router.go(path,{},{replaceState:true});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
this.route("characterSheet", {
|
|
||||||
path: "/character/:_id/:urlName",
|
|
||||||
waitOn: function(){
|
|
||||||
return [
|
|
||||||
subsManager.subscribe(
|
|
||||||
"singleCharacter", this.params._id, {onError: handleSubError}
|
|
||||||
),
|
|
||||||
];
|
|
||||||
},
|
|
||||||
data: function() {
|
|
||||||
var data = Characters.findOne(
|
|
||||||
{_id: this.params._id},
|
|
||||||
{fields: {_id: 1, name: 1, color: 1, writers: 1, readers: 1}}
|
|
||||||
);
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
onAfterAction: function() {
|
|
||||||
var char = Characters.findOne({_id: this.params._id}, {fields: {name: 1}});
|
|
||||||
var name = char && char.name;
|
|
||||||
if (name){
|
|
||||||
document.title = name;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
//analytics
|
|
||||||
trackPageView: false,
|
|
||||||
onRun: function() {
|
|
||||||
window.ga && window.ga("send", "pageview", "/character");
|
|
||||||
this.next();
|
|
||||||
},
|
|
||||||
fastRender: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.route("printedCharacterSheet", {
|
|
||||||
path: "/character/:_id/:urlName/print",
|
|
||||||
waitOn: function(){
|
|
||||||
return [
|
|
||||||
subsManager.subscribe(
|
|
||||||
"singleCharacter", this.params._id, {onError: handleSubError}
|
|
||||||
),
|
|
||||||
];
|
|
||||||
},
|
|
||||||
data: function() {
|
|
||||||
var data = Characters.findOne(
|
|
||||||
{_id: this.params._id},
|
|
||||||
{fields: {_id: 1, name: 1, color: 1, writers: 1, readers: 1}}
|
|
||||||
);
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
onAfterAction: function() {
|
|
||||||
var char = Characters.findOne({_id: this.params._id}, {fields: {name: 1}});
|
|
||||||
var name = char && char.name;
|
|
||||||
if (name){
|
|
||||||
document.title = name + " - Printing";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
//analytics
|
|
||||||
trackPageView: false,
|
|
||||||
onRun: function() {
|
|
||||||
window.ga && window.ga("send", "pageview", "/print-character");
|
|
||||||
this.next();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
this.route("library", {
|
|
||||||
path: "/library",
|
|
||||||
waitOn: function(){
|
|
||||||
return subsManager.subscribe("standardLibraries");
|
|
||||||
},
|
|
||||||
onAfterAction: function() {
|
|
||||||
document.title = appName + " - Library";
|
|
||||||
},
|
|
||||||
fastRender: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.route("loading", {
|
|
||||||
path: "/loading"
|
|
||||||
});
|
|
||||||
|
|
||||||
this.route("profile", {
|
|
||||||
path: "/account",
|
|
||||||
onAfterAction: function() {
|
|
||||||
document.title = appName + " Account";
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
this.route("/changelog", {
|
|
||||||
name: "changeLog",
|
|
||||||
waitOn: function() {
|
|
||||||
return [
|
|
||||||
subsManager.subscribe("changeLog"),
|
|
||||||
]
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
changeLogs: function() {
|
|
||||||
return ChangeLogs.find({}, {sort: {version: -1}});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onAfterAction: function() {
|
|
||||||
document.title = appName;
|
|
||||||
},
|
|
||||||
fastRender: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.route("/guide", {
|
|
||||||
name: "guide",
|
|
||||||
onAfterAction: function() {
|
|
||||||
document.title = appName;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
this.route("/error", {
|
|
||||||
name: "error",
|
|
||||||
onAfterAction: function() {
|
|
||||||
document.title = `${appName} - Error`;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "RPG Docs",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"homepage": "",
|
|
||||||
"authors": [
|
|
||||||
"Stefan Zermatten"
|
|
||||||
],
|
|
||||||
"license": "none",
|
|
||||||
"private": true,
|
|
||||||
"ignore": [
|
|
||||||
"**/.*",
|
|
||||||
"node_modules",
|
|
||||||
"bower_components",
|
|
||||||
"test",
|
|
||||||
"tests"
|
|
||||||
],
|
|
||||||
"dependencies": {
|
|
||||||
"polymer": "Polymer/polymer#^1.0.0",
|
|
||||||
"iron-elements": "PolymerElements/iron-elements#^1.0.0",
|
|
||||||
"platinum-elements": "PolymerElements/platinum-elements#^1.0.1",
|
|
||||||
"neon-elements": "PolymerElements/neon-elements#^1.0.0",
|
|
||||||
"paper-elements": "PolymerElements/paper-elements#^1.0.7",
|
|
||||||
"app-elements": "PolymerElements/app-elements#^0.10.1",
|
|
||||||
"marked-element": "PolymerElements/marked-element#^1.2.0",
|
|
||||||
"paper-swatch-picker": "PolymerElements/paper-swatch-picker#~1.0.2"
|
|
||||||
},
|
|
||||||
"resolutions": {
|
|
||||||
"webcomponentsjs": "0.7.24"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,174 +0,0 @@
|
|||||||
// jscs:disable
|
|
||||||
// https://github.com/chunksnbits/jquery-quickfit
|
|
||||||
(function ($) {
|
|
||||||
var Quickfit, QuickfitHelper, defaults, pluginName;
|
|
||||||
|
|
||||||
pluginName = 'quickfit';
|
|
||||||
|
|
||||||
defaults = {
|
|
||||||
min: 8,
|
|
||||||
max: 12,
|
|
||||||
tolerance: 0.02,
|
|
||||||
truncate: false,
|
|
||||||
width: null,
|
|
||||||
sampleNumberOfLetters: 10,
|
|
||||||
sampleFontSize: 12
|
|
||||||
};
|
|
||||||
QuickfitHelper = (function () {
|
|
||||||
|
|
||||||
var sharedInstance = null;
|
|
||||||
|
|
||||||
QuickfitHelper.instance = function (options) {
|
|
||||||
if (!sharedInstance) {
|
|
||||||
sharedInstance = new QuickfitHelper(options);
|
|
||||||
}
|
|
||||||
return sharedInstance;
|
|
||||||
};
|
|
||||||
|
|
||||||
function QuickfitHelper(options) {
|
|
||||||
this.options = options;
|
|
||||||
|
|
||||||
this.item = $('<span id="meassure"></span>');
|
|
||||||
this.item.css({
|
|
||||||
position: 'absolute',
|
|
||||||
left: '-1000px',
|
|
||||||
top: '-1000px',
|
|
||||||
'font-size': "" + this.options.sampleFontSize + "px"
|
|
||||||
});
|
|
||||||
$('body').append(this.item);
|
|
||||||
|
|
||||||
this.meassures = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
QuickfitHelper.prototype.getMeassure = function (letter) {
|
|
||||||
var currentMeassure;
|
|
||||||
currentMeassure = this.meassures[letter];
|
|
||||||
if (!currentMeassure) {
|
|
||||||
currentMeassure = this.setMeassure(letter);
|
|
||||||
}
|
|
||||||
return currentMeassure;
|
|
||||||
};
|
|
||||||
|
|
||||||
QuickfitHelper.prototype.setMeassure = function (letter) {
|
|
||||||
var currentMeassure, index, sampleLetter, text, _ref;
|
|
||||||
|
|
||||||
text = '';
|
|
||||||
sampleLetter = letter === ' ' ? ' ' : letter;
|
|
||||||
|
|
||||||
for (index = 0, _ref = this.options.sampleNumberOfLetters - 1; 0 <= _ref ? index <= _ref : index >= _ref; 0 <= _ref ? index++ : index--) {
|
|
||||||
text += sampleLetter;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.item.html(text);
|
|
||||||
currentMeassure = this.item.width() / this.options.sampleNumberOfLetters / this.options.sampleFontSize;
|
|
||||||
this.meassures[letter] = currentMeassure;
|
|
||||||
|
|
||||||
return currentMeassure;
|
|
||||||
};
|
|
||||||
|
|
||||||
return QuickfitHelper;
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
Quickfit = (function () {
|
|
||||||
|
|
||||||
function Quickfit(element, options) {
|
|
||||||
this.$element = element;
|
|
||||||
this.options = $.extend({}, defaults, options);
|
|
||||||
this.$element = $(this.$element);
|
|
||||||
this._defaults = defaults;
|
|
||||||
this._name = pluginName;
|
|
||||||
this.quickfitHelper = QuickfitHelper.instance(this.options);
|
|
||||||
}
|
|
||||||
|
|
||||||
Quickfit.prototype.fit = function () {
|
|
||||||
var elementWidth;
|
|
||||||
if (!this.options.width) {
|
|
||||||
elementWidth = this.$element.width();
|
|
||||||
this.options.width = elementWidth - this.options.tolerance * elementWidth;
|
|
||||||
}
|
|
||||||
if (this.text = this.$element.attr('data-quickfit')) {
|
|
||||||
this.previouslyTruncated = true;
|
|
||||||
} else {
|
|
||||||
this.text = this.$element.text();
|
|
||||||
}
|
|
||||||
this.calculateFontSize();
|
|
||||||
|
|
||||||
if (this.options.truncate) this.truncate();
|
|
||||||
|
|
||||||
return {
|
|
||||||
$element: this.$element,
|
|
||||||
size: this.fontSize
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
Quickfit.prototype.calculateFontSize = function () {
|
|
||||||
var letter, textWidth, i;
|
|
||||||
|
|
||||||
textWidth = 0;
|
|
||||||
for (i = 0; i < this.text.length; ++i) {
|
|
||||||
letter = this.text.charAt(i);
|
|
||||||
textWidth += this.quickfitHelper.getMeassure(letter);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.targetFontSize = parseInt(this.options.width / textWidth);
|
|
||||||
return this.fontSize = Math.max(this.options.min, Math.min(this.options.max, this.targetFontSize));
|
|
||||||
};
|
|
||||||
|
|
||||||
Quickfit.prototype.truncate = function () {
|
|
||||||
var index, lastLetter, letter, textToAdd, textWidth;
|
|
||||||
|
|
||||||
if (this.fontSize > this.targetFontSize) {
|
|
||||||
textToAdd = '';
|
|
||||||
textWidth = 3 * this.quickfitHelper.getMeassure('.') * this.fontSize;
|
|
||||||
|
|
||||||
index = 0;
|
|
||||||
while (textWidth < this.options.width && index < this.text.length) {
|
|
||||||
letter = this.text[index++];
|
|
||||||
if (lastLetter) textToAdd += lastLetter;
|
|
||||||
textWidth += this.fontSize * this.quickfitHelper.getMeassure(letter);
|
|
||||||
lastLetter = letter;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (textToAdd.length + 1 === this.text.length) {
|
|
||||||
textToAdd = this.text;
|
|
||||||
} else {
|
|
||||||
textToAdd += '...';
|
|
||||||
}
|
|
||||||
this.textWasTruncated = true;
|
|
||||||
|
|
||||||
return this.$element.attr('data-quickfit', this.text).html(textToAdd);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if (this.previouslyTruncated) {
|
|
||||||
return this.$element.html(this.text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return Quickfit;
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
return $.fn.quickfit = function (options) {
|
|
||||||
var measurements = [];
|
|
||||||
|
|
||||||
// Separate measurements from repaints
|
|
||||||
// First calculate all measurements...
|
|
||||||
var $elements = this.each(function () {
|
|
||||||
var measurement = new Quickfit(this, options).fit();
|
|
||||||
measurements.push(measurement);
|
|
||||||
return measurement.$element;
|
|
||||||
});
|
|
||||||
|
|
||||||
// ... then apply the measurements.
|
|
||||||
for (var i = 0; i < measurements.length; i++) {
|
|
||||||
var measurement = measurements[i];
|
|
||||||
|
|
||||||
measurement.$element.css({ fontSize: measurement.size + 'px' });
|
|
||||||
}
|
|
||||||
|
|
||||||
return $elements;
|
|
||||||
};
|
|
||||||
|
|
||||||
})(jQuery, window);
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
this.GlobalUI = (function() {
|
|
||||||
function GlobalUI() {}
|
|
||||||
var toast;
|
|
||||||
GlobalUI.toast = function(opts) {
|
|
||||||
if (!toast) toast = $("#global-toast")[0];
|
|
||||||
if (_.isObject(opts)){
|
|
||||||
toast.text = opts.text;
|
|
||||||
Session.set("global.ui.toastTemplate", opts.template);
|
|
||||||
Session.set("global.ui.toastData", opts.data);
|
|
||||||
} else {
|
|
||||||
toast.text = opts;
|
|
||||||
Session.set("global.ui.toastTemplate");
|
|
||||||
Session.set("global.ui.toastData");
|
|
||||||
}
|
|
||||||
return toast.show();
|
|
||||||
};
|
|
||||||
|
|
||||||
GlobalUI.deletedToast = function(id, collection, itemName) {
|
|
||||||
GlobalUI.toast({
|
|
||||||
text: itemName ? itemName + " deleted" : "Deleted item from" + collection,
|
|
||||||
template: "undoToast",
|
|
||||||
data: {
|
|
||||||
id: id,
|
|
||||||
collection: collection,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
return GlobalUI;
|
|
||||||
})();
|
|
||||||
|
|
||||||
Template.layout.helpers({
|
|
||||||
globalToastTemplate: function() {
|
|
||||||
return Session.get("global.ui.toastTemplate");
|
|
||||||
},
|
|
||||||
globalToastData: function() {
|
|
||||||
return Session.get("global.ui.toastData");
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
Template.registerHelper("canCast", function() {
|
|
||||||
return Characters.find({_id: this._id, spells: {$size: 0}}).count() === 0;
|
|
||||||
});
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
Template.registerHelper("canEditCharacter", function(charId) {
|
|
||||||
return canEditCharacter(charId);
|
|
||||||
});
|
|
||||||
|
|
||||||
canEditCharacter = function(charId) {
|
|
||||||
var char = Characters.findOne(charId);
|
|
||||||
if (!char) return false;
|
|
||||||
var userId = Meteor.userId();
|
|
||||||
return char.owner === userId ||
|
|
||||||
_.contains(char.writers, userId);
|
|
||||||
};
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
Template.registerHelper("characterPath", function(char) {
|
|
||||||
return `\/character\/${char._id}\/${char.urlName || "-"}`;
|
|
||||||
});
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
Template.registerHelper("colorClass", function(color) {
|
|
||||||
if (color) {
|
|
||||||
return getColorClass(color);
|
|
||||||
} else if (this.color) {
|
|
||||||
return getColorClass(this.color);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.registerHelper("hexColor", function(color) {
|
|
||||||
return getHexColor(color);
|
|
||||||
});
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
Template.registerHelper("detailHero", function(suffix, givenId) {
|
|
||||||
var id = givenId || this._id;
|
|
||||||
if (suffix) {
|
|
||||||
id += suffix;
|
|
||||||
}
|
|
||||||
if (Session.equals("global.ui.detailHeroId", id)) {
|
|
||||||
return "hero";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
Template.registerHelper("evaluate", function(charId, string) {
|
|
||||||
return evaluate(charId, string);
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.registerHelper("evaluateSigned", function(charId, string) {
|
|
||||||
var number = evaluate(charId, string);
|
|
||||||
if (_.isFinite(number)) {
|
|
||||||
return number > 0 ? "+" + number : "" + number;
|
|
||||||
} else {
|
|
||||||
return number;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.registerHelper("evaluateSignedSpaced", function(charId, string) {
|
|
||||||
var number = evaluate(charId, string);
|
|
||||||
if (_.isFinite(number)) {
|
|
||||||
return number > 0 ? "+ " + number : "- " + (-1 * number);
|
|
||||||
} else {
|
|
||||||
return number;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.registerHelper("evaluateString", function(charId, string) {
|
|
||||||
return evaluateString(charId, string);
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.registerHelper("evaluateSpellString", function(charId, spellListId, string) {
|
|
||||||
return evaluateSpellString(charId, spellListId, string);
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.registerHelper("evaluateShortString", function(charId, string) {
|
|
||||||
if (_.isString(string)){
|
|
||||||
return evaluateString(
|
|
||||||
charId, string.split(/^( *[-*_]){3,} *(?:\n+|$)/m)[0]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
openParentDialog = function({
|
|
||||||
parent, charId, element, returnElement, callback,
|
|
||||||
}) {
|
|
||||||
let template;
|
|
||||||
let data;
|
|
||||||
if (parent.collection === "Characters" && parent.group === "racial") {
|
|
||||||
template = "raceDialog";
|
|
||||||
data = {charId: parent.id};
|
|
||||||
} else if (parent.collection === "Features") {
|
|
||||||
template = "featureDialog";
|
|
||||||
data = {featureId: parent.id};
|
|
||||||
} else if (parent.collection === "Classes") {
|
|
||||||
template = "classDialog";
|
|
||||||
data = {classId: parent.id};
|
|
||||||
} else if (parent.collection === "Items") {
|
|
||||||
template = "itemDialog";
|
|
||||||
data = {itemId: parent.id};
|
|
||||||
} else if (parent.collection === "Spells") {
|
|
||||||
template = "spellDialog";
|
|
||||||
data = {spellId: parent.id};
|
|
||||||
} else if (parent.collection === "Buffs") {
|
|
||||||
template = "buffDialog";
|
|
||||||
data = {buffId: parent.id};
|
|
||||||
}
|
|
||||||
pushDialogStack({template, data, element, returnElement, callback});
|
|
||||||
};
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
Template.registerHelper("round", function(value, decimalPlaces) {
|
|
||||||
decimalPlaces = +decimalPlaces || 2;
|
|
||||||
var num = +value;
|
|
||||||
var tens = Math.pow(10, decimalPlaces);
|
|
||||||
return Math.round(num * tens) / tens;
|
|
||||||
});
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
Template.registerHelper("session", function(key) {
|
|
||||||
return Session.get(key);
|
|
||||||
});
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
Template.registerHelper("signedString", function(number) {
|
|
||||||
return number >= 0 ? "+" + number : "" + number;
|
|
||||||
});
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
Template.registerHelper("valueString", function(value) {
|
|
||||||
var intValue = Math.round(value * 100);
|
|
||||||
var cp = intValue % 10;
|
|
||||||
intValue -= cp;
|
|
||||||
cp = Math.round(cp);
|
|
||||||
sp = intValue % 100;
|
|
||||||
intValue -= sp;
|
|
||||||
sp = Math.round(sp / 10)
|
|
||||||
gp = Math.floor(value);
|
|
||||||
|
|
||||||
var resultArray = [];
|
|
||||||
if (gp > 0) {
|
|
||||||
resultArray.push(gp + " gp");
|
|
||||||
}
|
|
||||||
if (sp > 0) {
|
|
||||||
resultArray.push(sp + " sp");
|
|
||||||
}
|
|
||||||
if (cp > 0) {
|
|
||||||
resultArray.push(cp + " cp");
|
|
||||||
}
|
|
||||||
|
|
||||||
//build string with correct spacing
|
|
||||||
var result = "";
|
|
||||||
for (var i = 0, l = resultArray.length; i < l; i++) {
|
|
||||||
//add a space between values
|
|
||||||
if (i !== 0) {
|
|
||||||
result += " ";
|
|
||||||
}
|
|
||||||
result += resultArray[i];
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.registerHelper("longValueString", function(value) {
|
|
||||||
var resultArray = [];
|
|
||||||
//sp
|
|
||||||
var gp = Math.floor(value);
|
|
||||||
if (gp > 0) {
|
|
||||||
resultArray.push(gp + " gp");
|
|
||||||
}
|
|
||||||
//sp
|
|
||||||
var sp = Math.floor(10 * (value % 1));
|
|
||||||
if (sp > 0 || resultArray.length) {
|
|
||||||
resultArray.push(sp + " sp");
|
|
||||||
}
|
|
||||||
//cp
|
|
||||||
var cp = 10 * ((value * 10) % 1);
|
|
||||||
cp = Math.round(cp * 1000) / 1000;
|
|
||||||
if (cp > 0 || resultArray.length) {
|
|
||||||
resultArray.push(cp + " cp");
|
|
||||||
}
|
|
||||||
|
|
||||||
//build string with correct spacing
|
|
||||||
var result = "";
|
|
||||||
for (var i = 0; i < resultArray.length; i++) {
|
|
||||||
//add a space between values
|
|
||||||
if (i !== 0) {
|
|
||||||
result += " ";
|
|
||||||
}
|
|
||||||
result += resultArray[i];
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width initial-scale=1.0, user-scalable=no">
|
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons" rel="stylesheet">
|
||||||
|
<meta name="viewport" content="width=device-width initial-scale=1.0, user-scalable=no">
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
<link href='https://fonts.googleapis.com/css?family=Roboto:400,300,300italic,400italic,500,500italic,700,700italic,900,900italic,100italic,100&subset=latin,latin-ext' rel='stylesheet' type='text/css'>
|
|
||||||
|
|
||||||
<link rel="apple-touch-icon" sizes="57x57" href="/apple-touch-icon-57x57.png?v=lk6WXp6Pmj">
|
<link rel="apple-touch-icon" sizes="57x57" href="/apple-touch-icon-57x57.png?v=lk6WXp6Pmj">
|
||||||
<link rel="apple-touch-icon" sizes="60x60" href="/apple-touch-icon-60x60.png?v=lk6WXp6Pmj">
|
<link rel="apple-touch-icon" sizes="60x60" href="/apple-touch-icon-60x60.png?v=lk6WXp6Pmj">
|
||||||
<link rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-72x72.png?v=lk6WXp6Pmj">
|
<link rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-72x72.png?v=lk6WXp6Pmj">
|
||||||
3
app/client/index.html
Normal file
3
app/client/index.html
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
/**
|
|
||||||
* Take in a map like this:
|
|
||||||
* {
|
|
||||||
* "#someId": {
|
|
||||||
* proprty1() { return someReactiveValue()}
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* and bind the properties to the DOM on autorun.
|
|
||||||
*
|
|
||||||
* Useful for polymer components where you need to set the order of property updating
|
|
||||||
* or alter properties that don't bind well to their attributes
|
|
||||||
*/
|
|
||||||
Blaze.Template.prototype.binding = function(bindingMap){
|
|
||||||
this.onRendered(function(){
|
|
||||||
_.each(bindingMap, (propertyMap, cssPattern) => {
|
|
||||||
node = this.find(cssPattern);
|
|
||||||
_.each(propertyMap, (func, property) => {
|
|
||||||
this.autorun(() => {
|
|
||||||
node[property] = func && func.call && func.call(this, node);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@@ -1,195 +0,0 @@
|
|||||||
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: "",
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
Session.setDefault("isPrinting", false);
|
|
||||||
if (window.matchMedia) {
|
|
||||||
var mediaQueryList = window.matchMedia("print");
|
|
||||||
mediaQueryList.addListener(function(mql) {
|
|
||||||
if (mql.matches) {
|
|
||||||
Session.set("isPrinting", true);
|
|
||||||
Tracker.flush();
|
|
||||||
} else {
|
|
||||||
Session.set("isPrinting", false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
removeDuplicateProficiencies = function(proficiencies) {
|
|
||||||
dict = {};
|
|
||||||
proficiencies.forEach(function(prof) {
|
|
||||||
if (prof.name in dict) { //if we have already gone over another proficiency for the same thing
|
|
||||||
if (dict[prof.name].value < prof.value) {
|
|
||||||
dict[prof.name] = prof; //then take the new one if it's higher, otherwise leave it
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
dict[prof.name] = prof; //if it wasn't already there, store it
|
|
||||||
}
|
|
||||||
});
|
|
||||||
profs = []
|
|
||||||
_.forEach(dict, function(prof) {
|
|
||||||
profs.push(prof);
|
|
||||||
})
|
|
||||||
return profs;
|
|
||||||
};
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
|
|
||||||
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
|
|
||||||
|
|
||||||
// requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel
|
|
||||||
|
|
||||||
// MIT license
|
|
||||||
var lastTime = 0;
|
|
||||||
var vendors = ["ms", "moz", "webkit", "o"];
|
|
||||||
for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
|
|
||||||
window.requestAnimationFrame = window[vendors[x] + "RequestAnimationFrame"];
|
|
||||||
window.cancelAnimationFrame = window[vendors[x] + "CancelAnimationFrame"] ||
|
|
||||||
window[vendors[x] + "CancelRequestAnimationFrame"];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!window.requestAnimationFrame)
|
|
||||||
window.requestAnimationFrame = function(callback, element) {
|
|
||||||
var currTime = new Date().getTime();
|
|
||||||
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
|
|
||||||
var id = window.setTimeout(function() { callback(currTime + timeToCall); },
|
|
||||||
timeToCall);
|
|
||||||
lastTime = currTime + timeToCall;
|
|
||||||
return id;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!window.cancelAnimationFrame)
|
|
||||||
window.cancelAnimationFrame = function(id) {
|
|
||||||
clearTimeout(id);
|
|
||||||
};
|
|
||||||
3
app/client/main.js
Normal file
3
app/client/main.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import '/imports/ui/vueSetup.js';
|
||||||
|
import '/imports/ui/styles/stylesIndex.js';
|
||||||
|
import '/imports/ui/styles/stylesIndex.js';
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
@keyframes bounce {
|
|
||||||
from {
|
|
||||||
transform: translate(0px,0px);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform: translate(0px,-16px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bounce{
|
|
||||||
animation-name: bounce;
|
|
||||||
animation-duration: 0.3s;
|
|
||||||
animation-direction: alternate;
|
|
||||||
animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
|
||||||
animation-delay: 0s;
|
|
||||||
animation-iteration-count: infinite;
|
|
||||||
}
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
/*Column layout*/
|
|
||||||
.column-container {
|
|
||||||
column-fill: balance;
|
|
||||||
column-gap: 0px;
|
|
||||||
column-width: 304px;
|
|
||||||
padding: 4px;
|
|
||||||
transform: translateZ(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.column-container.thin-columns {
|
|
||||||
column-count: 4;
|
|
||||||
column-width: 240px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.column-container > div {
|
|
||||||
padding: 4px;
|
|
||||||
-webkit-column-break-inside: avoid;
|
|
||||||
page-break-inside: avoid;
|
|
||||||
break-inside: avoid;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*Cards*/
|
|
||||||
.card {
|
|
||||||
background: white;
|
|
||||||
border-radius: 2px;
|
|
||||||
position: initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card .top {
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 16px;
|
|
||||||
border-radius: 2px 2px 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card .top.white {
|
|
||||||
cursor: auto;
|
|
||||||
padding: 16px;
|
|
||||||
border-bottom: rgba(0,0,0,0.12) solid 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card .bottom {
|
|
||||||
padding: 16px;
|
|
||||||
border-radius: 0 0 2px 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card .bottom.list {
|
|
||||||
padding: 16px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card .bottom.list .paper-font-subhead {
|
|
||||||
color: rgba(0,0,0,0.54);
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
letter-spacing: 0.010em;
|
|
||||||
padding: 12px 16px 12px 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card .bottom.text {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card .left {
|
|
||||||
padding: 16px;
|
|
||||||
border-radius: 2px 0 0 2px;
|
|
||||||
text-align: center;
|
|
||||||
min-width: 72px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card .right {
|
|
||||||
padding: 16px;
|
|
||||||
border-radius: 0 2px 2px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card .left paper-icon-button {
|
|
||||||
display: block;
|
|
||||||
height: 32px;
|
|
||||||
padding: 0;
|
|
||||||
width: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card .left paper-icon-button[disabled] {
|
|
||||||
color: rgba(255, 255, 255, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card img, .card iron-image {
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
.red {
|
|
||||||
background-color: #F44336;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pink {
|
|
||||||
background-color: #E91E63;
|
|
||||||
}
|
|
||||||
|
|
||||||
.purple {
|
|
||||||
background-color: #9C27B0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.deep-purple {
|
|
||||||
background-color: #673AB7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.indigo {
|
|
||||||
background-color: #3F51B5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blue {
|
|
||||||
background-color: #2196F3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.light-blue {
|
|
||||||
background-color: #03A9F4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cyan {
|
|
||||||
background-color: #00BCD4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.teal {
|
|
||||||
background-color: #009688;
|
|
||||||
}
|
|
||||||
|
|
||||||
.green {
|
|
||||||
background-color: #4CAF50;
|
|
||||||
}
|
|
||||||
|
|
||||||
.light-green {
|
|
||||||
background-color: #8BC34A;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lime {
|
|
||||||
background-color: #CDDC39;
|
|
||||||
}
|
|
||||||
|
|
||||||
.yellow {
|
|
||||||
background-color: #FFEB3B;
|
|
||||||
}
|
|
||||||
|
|
||||||
.amber {
|
|
||||||
background-color: #FFC107;
|
|
||||||
}
|
|
||||||
|
|
||||||
.orange {
|
|
||||||
background-color: #FF9800;
|
|
||||||
}
|
|
||||||
|
|
||||||
.deep-orange {
|
|
||||||
background-color: #FF5722;
|
|
||||||
}
|
|
||||||
|
|
||||||
.brown {
|
|
||||||
background-color: #795548;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grey {
|
|
||||||
background-color: #9E9E9E;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blue-grey {
|
|
||||||
background-color: #607D8B;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-grey {
|
|
||||||
background-color: #424242;
|
|
||||||
}
|
|
||||||
|
|
||||||
.white {
|
|
||||||
background-color: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.black {
|
|
||||||
background-color: #262626;
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
List items
|
|
||||||
*/
|
|
||||||
.item-slot {
|
|
||||||
background-color: rgb(232, 232, 232);
|
|
||||||
background-color: rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.item {
|
|
||||||
background: white;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 16px;
|
|
||||||
height: 40px;
|
|
||||||
margin: 1px 0 1px 0;
|
|
||||||
padding: 0 16px 0 16px;
|
|
||||||
position: relative;
|
|
||||||
transition: box-shadow 0.3s ease, opacity 0.5s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item > .itemName {
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item.small {
|
|
||||||
height: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item.tall {
|
|
||||||
height: 56px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item.flexible {
|
|
||||||
height: auto;
|
|
||||||
padding-top: 16px;
|
|
||||||
padding-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item iron-icon, .item paper-icon-button {
|
|
||||||
color: #747474;
|
|
||||||
color: rgba(0,0,0,0.54);
|
|
||||||
}
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
/*apply a natural box layout model to all elements*/
|
|
||||||
*, *:before, *:after {
|
|
||||||
-moz-box-sizing: border-box;
|
|
||||||
-webkit-box-sizing: border-box;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
root {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: 'Roboto', 'Helvetica Neue', Helvetica, Arial;
|
|
||||||
margin: 0;
|
|
||||||
overflow-x: hidden;
|
|
||||||
background-color: #E0E0E0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*Paragraphs*/
|
|
||||||
p {
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Horizontal rule
|
|
||||||
hr {
|
|
||||||
background-color: #444;
|
|
||||||
opacity: 0.12;
|
|
||||||
border-width: 0;
|
|
||||||
color: #444;
|
|
||||||
height: 1px;
|
|
||||||
line-height: 0;
|
|
||||||
margin: 16px 0;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar {
|
|
||||||
display: inline-block;
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
border-radius: 50%;
|
|
||||||
font-size: 26px;
|
|
||||||
font-color: rgba(255, 255, 255, 0.58) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
temporary fix for https://github.com/PolymerElements/paper-item/issues/71
|
|
||||||
*/
|
|
||||||
paper-icon-item::shadow #contentIcon {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*FABs*/
|
|
||||||
.floatyButton {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 24px;
|
|
||||||
right: 24px;
|
|
||||||
/* stop the fab from flashing during animation */
|
|
||||||
transform: scale(1) translateZ(0px);
|
|
||||||
z-index: 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
paper-fab {
|
|
||||||
background-color: #d13b2e;
|
|
||||||
}
|
|
||||||
|
|
||||||
paper-fab.keyboard-focus {
|
|
||||||
background: #630c05;
|
|
||||||
}
|
|
||||||
|
|
||||||
.red-button:not([disabled]) {
|
|
||||||
background: #d23f31;
|
|
||||||
color: #fff;
|
|
||||||
margin-top: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*Buttons*/
|
|
||||||
paper-button {
|
|
||||||
color: #000;
|
|
||||||
color: rgba(0,0,0,0.87);
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 400;
|
|
||||||
letter-spacing: 0.010;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
|
|
||||||
dicecloud-selector paper-item {
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*Style shortcuts*/
|
|
||||||
.scroll-y {
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.clickable, core-item, paper-tab {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pre-wrap, .prewrap{
|
|
||||||
white-space: pre-wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.padded {
|
|
||||||
padding: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fullwidth {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fab-buffer {
|
|
||||||
height: 100px;
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
td {
|
|
||||||
padding: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.strengthTable{
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.strengthTable td:nth-child(2){
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.summaryTable td:nth-child(3){
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
body .paper-font-display4, body .paper-font-display3, body .paper-font-title, body .paper-font-caption{
|
|
||||||
white-space: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.white-text {
|
|
||||||
color: #dedede;
|
|
||||||
color: rgba(255,255,255,0.87);
|
|
||||||
}
|
|
||||||
|
|
||||||
.white-text .paper-font-display2{
|
|
||||||
color: #8a8a8a;
|
|
||||||
color: rgba(255,255,255,0.54);
|
|
||||||
}
|
|
||||||
|
|
||||||
.white-text .paper-font-display1, .white-text.paper-font-display1{
|
|
||||||
color: #8a8a8a;
|
|
||||||
color: rgba(255,255,255,0.54);
|
|
||||||
}
|
|
||||||
|
|
||||||
.white-text h1, .white-text .paper-font-headline, .white-text.paper-font-headline{
|
|
||||||
color: #dedede;
|
|
||||||
color: rgba(255,255,255,0.87);
|
|
||||||
}
|
|
||||||
|
|
||||||
.white-text h2, .white-text .paper-font-title, .white-text.paper-font-title{
|
|
||||||
color: #dedede;
|
|
||||||
color: rgba(255,255,255,0.87);
|
|
||||||
}
|
|
||||||
|
|
||||||
.white-text h3, .white-text .paper-font-subhead{
|
|
||||||
color: #dedede;
|
|
||||||
color: rgba(255,255,255,0.87);
|
|
||||||
}
|
|
||||||
|
|
||||||
.white-text .paper-font-body2{
|
|
||||||
color: #dedede;
|
|
||||||
color: rgba(255,255,255,0.87);
|
|
||||||
}
|
|
||||||
|
|
||||||
.white-text p, .white-text .paper-font-body1{
|
|
||||||
color: #dedede;
|
|
||||||
color: rgba(255,255,255,0.87);
|
|
||||||
}
|
|
||||||
|
|
||||||
.white-text .paper-font-caption{
|
|
||||||
color: #8a8a8a;
|
|
||||||
color: rgba(255,255,255,0.54);
|
|
||||||
}
|
|
||||||
|
|
||||||
.black54 {
|
|
||||||
color: #757575;
|
|
||||||
color: rgba(0,0,0,0.54);
|
|
||||||
}
|
|
||||||
|
|
||||||
.white54 {
|
|
||||||
color: #8a8a8a;
|
|
||||||
color: rgba(255,255,255,0.54);
|
|
||||||
}
|
|
||||||
|
|
||||||
.black87 {
|
|
||||||
color: #212121;
|
|
||||||
color: rgba(0,0,0,0.87);
|
|
||||||
}
|
|
||||||
|
|
||||||
.white87 {
|
|
||||||
color: #dedede;
|
|
||||||
color: rgba(255,255,255,0.87);
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<template name="attackEdit">
|
|
||||||
<div class="layout horizontal">
|
|
||||||
<div class="layout vertical flex">
|
|
||||||
<div class="layout horizontal wrap">
|
|
||||||
<!--attackBonus-->
|
|
||||||
<paper-input class="attackBonusInput flex" label="Attack Bonus" value={{attackBonus}}
|
|
||||||
style="flex-basis: 200px; margin-right: 16px;">
|
|
||||||
{{> formulaSuffix}}
|
|
||||||
</paper-input>
|
|
||||||
<!--details-->
|
|
||||||
<paper-input class="detailInput" label="Details" value={{details}} style="flex-basis: 200px;">
|
|
||||||
</paper-input>
|
|
||||||
</div>
|
|
||||||
<div class="layout horizontal wrap">
|
|
||||||
<!--damageBonus-->
|
|
||||||
<paper-input class="damageInput flex" label="Damage" value={{damage}}
|
|
||||||
style="flex-basis: 200px; margin-right: 16px;">
|
|
||||||
{{> bracketSuffix}}
|
|
||||||
</paper-input>
|
|
||||||
<!--DamageType-->
|
|
||||||
<paper-dropdown-menu label="Damage Type" dynamic-align>
|
|
||||||
<dicecloud-selector class="dropdown-content damageTypeDropdown" selected={{damageType}}>
|
|
||||||
{{#each damageTypes}}
|
|
||||||
<paper-item name={{this}} class="containerMenuItem">{{this}}</paper-item>
|
|
||||||
{{/each}}
|
|
||||||
</dicecloud-selector>
|
|
||||||
</paper-dropdown-menu>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!--Delete Button-->
|
|
||||||
<paper-icon-button class="deleteAttack" icon="delete"></paper-icon-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
var damageTypes = [
|
|
||||||
"bludgeoning",
|
|
||||||
"piercing",
|
|
||||||
"slashing",
|
|
||||||
"acid",
|
|
||||||
"cold",
|
|
||||||
"fire",
|
|
||||||
"force",
|
|
||||||
"lightning",
|
|
||||||
"necrotic",
|
|
||||||
"poison",
|
|
||||||
"psychic",
|
|
||||||
"radiant",
|
|
||||||
"thunder",
|
|
||||||
];
|
|
||||||
|
|
||||||
Template.attackEdit.events({
|
|
||||||
"click .deleteAttack": function(event, instance) {
|
|
||||||
Attacks.softRemoveNode(this._id);
|
|
||||||
GlobalUI.deletedToast(this._id, "Attacks", "Attack");
|
|
||||||
},
|
|
||||||
"change .attackBonusInput": function(event) {
|
|
||||||
var value = event.currentTarget.value;
|
|
||||||
Attacks.update(this._id, {$set: {attackBonus: value}});
|
|
||||||
},
|
|
||||||
"change .damageInput": function(event) {
|
|
||||||
var value = event.currentTarget.value;
|
|
||||||
Attacks.update(this._id, {$set: {damage: value}});
|
|
||||||
},
|
|
||||||
"change .detailInput": function(event) {
|
|
||||||
var value = event.currentTarget.value;
|
|
||||||
Attacks.update(this._id, {$set: {details: value}});
|
|
||||||
},
|
|
||||||
"iron-select .damageTypeDropdown": function(event) {
|
|
||||||
var detail = event.originalEvent.detail;
|
|
||||||
var value = detail.item.getAttribute("name");
|
|
||||||
if (value == this.damageType) return;
|
|
||||||
Attacks.update(this._id, {$set: {damageType: value}});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.attackEdit.helpers({
|
|
||||||
damageTypes: function() {
|
|
||||||
return damageTypes;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<!--needs to be given charId, parentId and parentCollection-->
|
|
||||||
<template name="attackEditList">
|
|
||||||
{{#if attacks.count}}
|
|
||||||
<hr style="margin: 16px 0 16px 0;">
|
|
||||||
<div id="attacks">
|
|
||||||
<div class="paper-font-title">Attacks</div>
|
|
||||||
{{#each attacks}}
|
|
||||||
{{>attackEdit}}
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
<paper-button id="addAttackButton" class="red-button" raised>Add Attack</paper-button>
|
|
||||||
</template>
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
Template.attackEditList.helpers({
|
|
||||||
attacks: function() {
|
|
||||||
var cursor = Attacks.find({"parent.id": this.parentId, charId: this.charId});
|
|
||||||
return cursor;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.attackEditList.events({
|
|
||||||
"tap #addAttackButton": function() {
|
|
||||||
if (typeof this.isSpell !== 'undefined' && this.isSpell) {
|
|
||||||
var parentSpell = Spells.findOne({"_id": this.parentId})
|
|
||||||
if (parentSpell && parentSpell.parent.collection == "SpellLists") {
|
|
||||||
var spellList = SpellLists.findOne({"_id":parentSpell.parent.id});
|
|
||||||
if (spellList && spellList.attackBonus) {
|
|
||||||
Attacks.insert({
|
|
||||||
charId: this.charId,
|
|
||||||
parent: {
|
|
||||||
id: this.parentId,
|
|
||||||
collection: this.parentCollection
|
|
||||||
},
|
|
||||||
attackBonus: "attackBonus",
|
|
||||||
damage: "1d10",
|
|
||||||
damageType: "fire",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Attacks.insert({
|
|
||||||
charId: this.charId,
|
|
||||||
parent: {
|
|
||||||
id: this.parentId,
|
|
||||||
collection: this.parentCollection
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
<template name="attackView">
|
|
||||||
<div class="attackView layout horizontal">
|
|
||||||
<div class="paper-font-headline layout horizontal center" style="margin-right: 16px;">
|
|
||||||
{{evaluateAttackBonus charId attack}}
|
|
||||||
</div>
|
|
||||||
<div class="layout vertical">
|
|
||||||
<div>
|
|
||||||
{{evaluateDamage charId attack}} {{attack.damageType}}
|
|
||||||
</div>
|
|
||||||
{{#if attack.details}}
|
|
||||||
<div class="paper-font-caption">
|
|
||||||
{{attack.details}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
Template.attackView.helpers ({
|
|
||||||
evaluateAttackBonus: function(charId, attack) {
|
|
||||||
if (attack.parent.collection == "Spells") {
|
|
||||||
var spell = Spells.findOne(attack.parent.id);
|
|
||||||
if (spell) {
|
|
||||||
bonus = evaluate(charId, attack.attackBonus, {"spellListId": spell.parent.id});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var bonus = evaluate(charId, attack.attackBonus);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_.isFinite(bonus)) {
|
|
||||||
return bonus > 0 ? "+" + bonus : "" + bonus;
|
|
||||||
} else {
|
|
||||||
return bonus;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
evaluateDamage: function(charId, attack) {
|
|
||||||
if (attack.parent.collection == "Spells") {
|
|
||||||
var spell = Spells.findOne(attack.parent.id);
|
|
||||||
if (spell) {
|
|
||||||
return evaluateSpellString(charId, spell.parent.id, attack.damage);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return evaluateString(charId, attack.damage);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<template name="attacksViewList">
|
|
||||||
{{#if attacks.count}}
|
|
||||||
<hr style="margin: 16px 0 16px 0;">
|
|
||||||
<div class="attacks">
|
|
||||||
<div class="spaceAfter paper-font-title">Attacks</div>
|
|
||||||
{{#each attack in attacks}}
|
|
||||||
{{> attackView attack=attack charId=charId}}
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</template>
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
Template.attacksViewList.helpers({
|
|
||||||
attacks: function() {
|
|
||||||
return Attacks.find({"parent.id": this.parentId, charId: this.charId});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<!-- data is the CustomBuff -->
|
|
||||||
<template name="applyBuffDialog">
|
|
||||||
<div class="fit layout vertical applyBuffDialog">
|
|
||||||
<app-header fixed effects="waterfall">
|
|
||||||
<app-toolbar>
|
|
||||||
Apply Buff
|
|
||||||
</app-toolbar>
|
|
||||||
</app-header>
|
|
||||||
<div class="flex layout horizontal" style="height:100%">
|
|
||||||
<div class="flex" style="margin-right: 16px; height: 100%; max-width: 240px; overflow-y: auto;">
|
|
||||||
{{> characterPicker selfId=buff.charId includeSelf=canApplyToSelf writableOnly=true}}
|
|
||||||
</div>
|
|
||||||
<div class="flex buff-description" style="height: 100%; overflow-y: auto">
|
|
||||||
<hr style="margin: 16px 0 16px 0;">
|
|
||||||
{{#if buff.description}}
|
|
||||||
<div>{{#markdown}}{{evaluateString buff.charId buff.description}}{{/markdown}}</div>
|
|
||||||
<hr style="margin: 16px 0 16px 0;">
|
|
||||||
{{/if}}
|
|
||||||
{{> effectsViewList charId=buff.charId parentId=buff._id}}
|
|
||||||
{{> proficiencyViewList charId=buff.charId parentId=buff._id}}
|
|
||||||
{{> attacksViewList charId=buff.charId parentId=buff._id}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="buttons layout horizontal end-justified">
|
|
||||||
<paper-button id="cancelButton">
|
|
||||||
Cancel
|
|
||||||
</paper-button>
|
|
||||||
<paper-button id="applyButton" disabled={{cantApply}}>
|
|
||||||
Apply
|
|
||||||
</paper-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
Template.applyBuffDialog.onCreated(function(){
|
|
||||||
this.selectedTarget = new ReactiveVar("default");
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.applyBuffDialog.helpers({
|
|
||||||
cantApply: function() {
|
|
||||||
return this.buff.target === "others" && Template.instance().selectedTarget.get() === "default"; //this is the only case where we can't apply a buff
|
|
||||||
},
|
|
||||||
canApplyToSelf: function() {
|
|
||||||
return this.buff.target !== "others"; //i.e. it is "self" or "both"
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.applyBuffDialog.events({
|
|
||||||
"iron-select .characterPicker": function(event){
|
|
||||||
var detail = event.originalEvent.detail;
|
|
||||||
var value = detail.item.getAttribute("name");
|
|
||||||
Template.instance().selectedTarget.set(value);
|
|
||||||
},
|
|
||||||
"click #applyButton": function(event, instance){
|
|
||||||
var targetId = Template.instance().selectedTarget.get();
|
|
||||||
if (targetId === "default") {
|
|
||||||
if (this.buff.target === "others") return; //since we have "Select a character" selected
|
|
||||||
targetId = this.buff.charId; //otherwise, the default is to target self
|
|
||||||
}
|
|
||||||
|
|
||||||
popDialogStack(targetId);
|
|
||||||
},
|
|
||||||
"click #cancelButton": function(event, instance){
|
|
||||||
popDialogStack();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
<template name="buffDialog">
|
|
||||||
{{#with buff}}
|
|
||||||
{{#baseDialog title=name class="white" hideColor=true startEditing=true editOnly=true}}
|
|
||||||
{{> buffDetails}}
|
|
||||||
{{else}}
|
|
||||||
{{> buffDetails}}
|
|
||||||
{{/baseDialog}}
|
|
||||||
{{/with}}
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template name="buffDetails">
|
|
||||||
<div>
|
|
||||||
{{appliedBy}}
|
|
||||||
</div>
|
|
||||||
<hr style="margin: 16px 0 16px 0;">
|
|
||||||
{{#if description}}
|
|
||||||
<div>{{#markdown}}{{evaluateString charId description}}{{/markdown}}</div>
|
|
||||||
<hr style="margin: 16px 0 16px 0;">
|
|
||||||
{{/if}}
|
|
||||||
{{> effectsViewList charId=charId parentId=_id}}
|
|
||||||
{{> proficiencyViewList charId=charId parentId=_id}}
|
|
||||||
{{> attacksViewList charId=charId parentId=_id}}
|
|
||||||
</template>
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
Template.buffDialog.onCreated(function(){
|
|
||||||
var buff = Buffs.findOne(this.buffId);
|
|
||||||
Meteor.subscribe("singleCharacterName", buff.charId); //so we can access the names of public characters
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.buffDialog.helpers({
|
|
||||||
buff: function(){
|
|
||||||
return Buffs.findOne(this.buffId);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.buffDialog.events({
|
|
||||||
"click #deleteButton": function(event, instance){
|
|
||||||
Buffs.softRemoveNode(instance.data.buffId);
|
|
||||||
popDialogStack();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const typeDict = {
|
|
||||||
"Features": "feature",
|
|
||||||
"Items": "item",
|
|
||||||
"Spells": "spell",
|
|
||||||
}; //really, we should only need these three
|
|
||||||
|
|
||||||
Template.buffDetails.helpers({
|
|
||||||
appliedBy: function() {
|
|
||||||
if (this.type == "inate") {
|
|
||||||
return "Innate.";
|
|
||||||
} else {
|
|
||||||
var myName = Characters.findOne(this.charId).name;
|
|
||||||
var applierCharacter = Characters.findOne(this.appliedBy) || {name: "???"}
|
|
||||||
// "???" indicates that either we do not have read access to the buff-giver, or that the buff-giver does not exist.
|
|
||||||
|
|
||||||
if (applierCharacter.name === myName) {
|
|
||||||
var charName = "your "
|
|
||||||
} else {
|
|
||||||
if (applierCharacter.name && applierCharacter.name[applierCharacter.name.length - 1] === 's') {
|
|
||||||
var charName = applierCharacter.name + "' ";
|
|
||||||
} else {
|
|
||||||
var charName = applierCharacter.name + "'s ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var type = typeDict[this.appliedByDetails.collection] + " ";
|
|
||||||
var applierThing = this.appliedByDetails.name;
|
|
||||||
|
|
||||||
return "Applied by " + charName + type + applierThing + ".";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
<template name="buffListItem">
|
|
||||||
<div class="item buffListItem layout horizontal center">
|
|
||||||
<div class="flex">
|
|
||||||
{{buff.name}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#if canEditCharacter buff.charId}}
|
|
||||||
<paper-icon-button class="deleteButton"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
icon="delete">
|
|
||||||
</paper-icon-button>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
Template.buffListItem.helpers({
|
|
||||||
name: function() {
|
|
||||||
return this.buff.name
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.buffListItem.events({
|
|
||||||
"click .buffListItem": function(event){
|
|
||||||
var buffId = this.buff._id;
|
|
||||||
var charId = this.buff.charId;
|
|
||||||
pushDialogStack({
|
|
||||||
template: "buffDialog",
|
|
||||||
data: {buffId: buffId, charId: charId},
|
|
||||||
element: event.currentTarget,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
"tap .deleteButton": function(event){
|
|
||||||
event.stopPropagation();
|
|
||||||
Buffs.remove(this.buff._id);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
.condition-library-dialog .item.selected {
|
|
||||||
background-color: #e4e4e4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.condition-library-dialog table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
|
|
||||||
.condition-library-dialog .library-condition td, tr {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
<template name="conditionLibraryDialog">
|
|
||||||
<div class="fit condition-library-dialog layout vertical">
|
|
||||||
<app-toolbar class="app-grey white-text">
|
|
||||||
<paper-icon-button id="backButton"
|
|
||||||
icon="arrow-back">
|
|
||||||
</paper-icon-button>
|
|
||||||
<div main-title>Conditions</div>
|
|
||||||
</app-toolbar>
|
|
||||||
<div class="flex scroll-y">
|
|
||||||
<div class="conditions" style="padding:8px">
|
|
||||||
<table style="width: 100%">
|
|
||||||
<tbody>
|
|
||||||
{{#each condition in conditions}}
|
|
||||||
{{>libraryCondition condition=condition selected=(isSelected condition)}}
|
|
||||||
{{/each}}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="layout horizontal end-justified">
|
|
||||||
<paper-button class="cancelButton">Cancel</paper-button>
|
|
||||||
<paper-button class="okButton">OK</paper-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template name="libraryCondition">
|
|
||||||
<tr class="item library-condition {{#if selected}}selected{{/if}}">
|
|
||||||
<td class="conditionName">
|
|
||||||
{{conditionName condition}}
|
|
||||||
<paper-ripple></paper-ripple>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
Template.conditionLibraryDialog.onCreated(function(){
|
|
||||||
this.selectedCondition = new ReactiveVar();
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.conditionLibraryDialog.helpers({
|
|
||||||
conditions: function(){
|
|
||||||
return Object.keys(LIBRARY_CONDITIONS)
|
|
||||||
},
|
|
||||||
isSelected(condition){
|
|
||||||
const selected = Template.instance().selectedCondition.get();
|
|
||||||
return selected && selected === condition;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.conditionLibraryDialog.events({
|
|
||||||
"click .cancelButton": function(event, template){
|
|
||||||
popDialogStack();
|
|
||||||
},
|
|
||||||
"click .okButton": function(event, template){
|
|
||||||
popDialogStack(template.selectedCondition.get());
|
|
||||||
},
|
|
||||||
"click .library-condition": function(event, template){
|
|
||||||
template.selectedCondition.set(this.condition);
|
|
||||||
},
|
|
||||||
"click #backButton": function(event, template){
|
|
||||||
popDialogStack();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.libraryCondition.helpers({
|
|
||||||
conditionName: function(name){
|
|
||||||
return LIBRARY_CONDITIONS[name].buff.name;
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
LIBRARY_CONDITIONS = {
|
|
||||||
//Conditions
|
|
||||||
blind: {
|
|
||||||
buff: {
|
|
||||||
name: "Blind",
|
|
||||||
description: "A blinded creature can’t see and automatically fails any ability check that requires sight.\n\nAttack rolls against the creature have advantage, and the creature’s attack rolls have disadvantage.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
deaf: {
|
|
||||||
buff: {
|
|
||||||
name: "Deaf",
|
|
||||||
description: "A deafened creature can’t hear and automatically fails any ability check that requires hearing.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
frightened: {
|
|
||||||
buff: {
|
|
||||||
name: "Frightened",
|
|
||||||
description: "A frightened creature has disadvantage on ability checks and attack rolls while the source of its fear is within line of sight.\n\nThe creature can’t willingly move closer to the source of its fear.",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
grappled: {
|
|
||||||
buff:{
|
|
||||||
name: "Grappled",
|
|
||||||
description: "A grappled creature’s speed becomes 0, and it can’t benefit from any bonus to its speed.\n\nThe condition ends if the grappler is incapacitated.\n\nThe condition also ends if an effect removes the grappled creature from the reach of the grappler or grappling effect, such as when a creature is hurled away by the thunder wave spell.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
incapacitated: {
|
|
||||||
buff: {
|
|
||||||
name: "Incapacitated",
|
|
||||||
description: "An incapacitated creature can’t take actions or reactions.",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
invisible: {
|
|
||||||
buff: {
|
|
||||||
name: "Invisible",
|
|
||||||
description: "An invisible creature is impossible to see without the aid of magic or a special sense. For the purpose of hiding, the creature is heavily obscured. The creature’s location can be detected by any noise it makes or any tracks it leaves.\n\nAttack rolls against the creature have disadvantage, and the creature’s attack rolls have advantage.",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
paralyzed: {
|
|
||||||
buff: {
|
|
||||||
name: "Paralyzed",
|
|
||||||
description: "A paralyzed creature is **incapacitated** and can’t move or speak.\n\nAttack rolls against the creature have advantage.\n\nAny attack that hits the creature is a critical hit if the attacker is within 5 feet of the creature.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
petrified: {
|
|
||||||
buff: {
|
|
||||||
name: "Petrified",
|
|
||||||
description: "A petrified creature is transformed, along with any nonmagical object it is wearing or carrying, into a solid inanimate substance (usually stone). Its weight increases by a factor of ten, and it ceases aging.\n\nA petrified creature is **incapacitated** and can’t move or speak, and is unaware of its surroundings.\n\nAttack rolls against the creature have advantage.\n\nThe creature is immune to poison and disease, although a poison or disease already in its system is suspended, not neutralized.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
poisoned: {
|
|
||||||
buff: {
|
|
||||||
name: "Poisoned",
|
|
||||||
description: "A poisoned creature has disadvantage on attack rolls and ability checks.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
prone: {
|
|
||||||
buff: {
|
|
||||||
name: "Prone",
|
|
||||||
description: "A prone creature’s only movement option is to crawl, unless it stands up and thereby ends the condition.\n\nThe creature has disadvantage on attack rolls.\n\nAn attack roll against the creature has advantage if the attacker is within 5 feet of the creature. Otherwise, the attack roll has disadvantage.",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
restrained: {
|
|
||||||
buff: {
|
|
||||||
name: "Restrained",
|
|
||||||
description: "A restrained creature’s speed becomes 0, and it can’t benefit from any bonus to its speed.\n\nAttack rolls against the creature have advantage, and the creature’s attack rolls have disadvantage.\n\nThe creature has disadvantage on Dexterity saving throws.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
stunned: {
|
|
||||||
buff: {
|
|
||||||
name: "Stunned",
|
|
||||||
description: "A stunned creature is **incapacitated**, can’t move, and can speak only falteringly\n\nThe creature automatically fails Strength and Dexterity saving throws.\n\nAttack rolls against the creature have advantage.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
unconscious: {
|
|
||||||
buff: {
|
|
||||||
name: "Unconscious",
|
|
||||||
description: "An unconscious creature is **incapacitated**, can’t move or speak, and is unaware of its surroundings.\n\nThe creature drops whatever it’s holding and falls **prone**.\n\nThe creature automatically fails Strength and Dexterity saving throws.\n\nAttack rolls against the creature have advantage.\n\nAny attack that hits the creature is a critical hit if the attacker is within 5 feet of the creature.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
exhaustion1: {
|
|
||||||
buff: {
|
|
||||||
name: "Exhaustion - 1",
|
|
||||||
description: "Disadvantage on ability checks\n\nFinishing a long rest reduces a creature’s exhaustion level by 1, provided that the creature has also ingested some food and drink.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
exhaustion2: {
|
|
||||||
buff: {
|
|
||||||
name: "Exhaustion - 2",
|
|
||||||
description: "Speed halved",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
exhaustion3: {
|
|
||||||
buff: {
|
|
||||||
name: "Exhaustion - 3",
|
|
||||||
description: "Disadvantage on attack rolls and saving throws",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
exhaustion4: {
|
|
||||||
buff: {
|
|
||||||
name: "Exhaustion - 4",
|
|
||||||
description: "Hit point maximum halved",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
exhaustion5: {
|
|
||||||
buff: {
|
|
||||||
name: "Exhaustion - 5",
|
|
||||||
description: "Speed reduced to 0",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
exhaustion6: {
|
|
||||||
buff: {
|
|
||||||
name: "Exhaustion - 6",
|
|
||||||
description: "You have died of exhaustion",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
<template name="conditionView">
|
|
||||||
<div class="item conditionView layout horizontal center">
|
|
||||||
<div class="flex">
|
|
||||||
{{condition.name}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#if canEditCharacter condition.charId}}
|
|
||||||
<paper-icon-button class="deleteButton"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
icon="delete">
|
|
||||||
</paper-icon-button>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
Template.conditionView.events({
|
|
||||||
"click .conditionView": function(event){
|
|
||||||
var condition = this.condition;
|
|
||||||
var charId = Template.parentData()._id;
|
|
||||||
pushDialogStack({
|
|
||||||
template: "conditionViewDialog",
|
|
||||||
data: {condition: condition},
|
|
||||||
element: event.currentTarget,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
"tap .deleteButton": function(event){
|
|
||||||
event.stopPropagation();
|
|
||||||
Conditions.remove(this.condition._id);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
<template name="conditionViewDialog">
|
|
||||||
{{#baseDialog title=condition.name class="white" hideColor=true startEditing=true editOnly=true}}}
|
|
||||||
{{> conditionDetails condition=condition}}
|
|
||||||
{{else}}
|
|
||||||
{{> conditionDetails condition=condition}}
|
|
||||||
{{/baseDialog}}
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template name="conditionDetails">
|
|
||||||
{{#if condition.description}}
|
|
||||||
<div>{{#markdown}}{{evaluateString condition.charId condition.description}}{{/markdown}}</div>
|
|
||||||
{{/if}}
|
|
||||||
{{> effectsViewList charId=condition.charId parentId=condition._id}}
|
|
||||||
</template>
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
Template.conditionViewDialog.events({
|
|
||||||
"click #deleteButton": function(event, instance){
|
|
||||||
Conditions.remove(instance.data.condition._id);
|
|
||||||
popDialogStack();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
<template name="customBuffEdit">
|
|
||||||
{{#baseEditDialog title=buff.name hideColor=true}}
|
|
||||||
<!--name-->
|
|
||||||
<paper-input id="buffNameInput" class="fullwidth" label="Name" value={{buff.name}}></paper-input>
|
|
||||||
|
|
||||||
<div class="layout horizontal center wrap justified">
|
|
||||||
<paper-dropdown-menu class=flex label="Target" style="flex-basis: 150px; max-width: 200px;">
|
|
||||||
<dicecloud-selector selected={{buff.target}} class="dropdown-content target-dropdown">
|
|
||||||
<paper-item name="self" style="width: 150px;">
|
|
||||||
Self only
|
|
||||||
</paper-item>
|
|
||||||
<paper-item name="others">
|
|
||||||
Others only
|
|
||||||
</paper-item>
|
|
||||||
<paper-item name="both">
|
|
||||||
Both
|
|
||||||
</paper-item>
|
|
||||||
</dicecloud-selector>
|
|
||||||
</paper-dropdown-menu>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!--description-->
|
|
||||||
<paper-textarea label="Description" id="buffDescriptionInput" value={{buff.description}}></paper-textarea>
|
|
||||||
|
|
||||||
{{> effectsEditList parentId=buff._id parentCollection="CustomBuffs" charId=buff.charId name=name enabled=false}}
|
|
||||||
{{> attackEditList parentId=buff._id parentCollection="CustomBuffs" charId=buff.charId name=name enabled=false}}
|
|
||||||
{{> proficiencyEditList parentId=buff._id parentCollection="CustomBuffs" charId=buff.charId enabled=false}}
|
|
||||||
{{/baseEditDialog}}
|
|
||||||
</template>
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
Template.customBuffEdit.helpers({
|
|
||||||
buff(){
|
|
||||||
return CustomBuffs.findOne(this.customBuffId);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const debounce = (f) => _.debounce(f, 300);
|
|
||||||
|
|
||||||
Template.customBuffEdit.events({
|
|
||||||
"input #buffNameInput": debounce(function(event){
|
|
||||||
const input = event.currentTarget;
|
|
||||||
var name = input.value;
|
|
||||||
if (!name){
|
|
||||||
input.invalid = true;
|
|
||||||
input.errorMessage = "Name is required";
|
|
||||||
} else {
|
|
||||||
input.invalid = false;
|
|
||||||
CustomBuffs.update(this.customBuffId, {
|
|
||||||
$set: {name: name}
|
|
||||||
}, {
|
|
||||||
removeEmptyStrings: false,
|
|
||||||
trimStrings: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
"input #buffDescriptionInput": debounce(function(event){
|
|
||||||
var description = event.currentTarget.value;
|
|
||||||
CustomBuffs.update(this.customBuffId, {
|
|
||||||
$set: {description: description}
|
|
||||||
}, {
|
|
||||||
removeEmptyStrings: false,
|
|
||||||
trimStrings: false,
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
"iron-select .target-dropdown": function(event){
|
|
||||||
var detail = event.originalEvent.detail;
|
|
||||||
var value = detail.item.getAttribute("name");
|
|
||||||
const buff = CustomBuffs.findOne(this.customBuffId);
|
|
||||||
if (value === buff.target) return;
|
|
||||||
CustomBuffs.update(this.customBuffId, {$set: {target: value}});
|
|
||||||
},
|
|
||||||
"click #deleteButton": function(event, instance){
|
|
||||||
CustomBuffs.softRemoveNode(instance.data.customBuffId);
|
|
||||||
GlobalUI.deletedToast(instance.data.customBuffId, "Buffs", "Buff");
|
|
||||||
popDialogStack();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
<!--needs to be given charId, parentId and parentCollection-->
|
|
||||||
<template name="customBuffEditList">
|
|
||||||
{{#if buffs.count}}
|
|
||||||
<div class="buffs">
|
|
||||||
<div class="paper-font-title" style="margin-bottom: 8px;">
|
|
||||||
Buffs
|
|
||||||
</div>
|
|
||||||
<table class="wideTable" style="width: 100%;">
|
|
||||||
{{#each buff in buffs}}
|
|
||||||
{{> customBuffEditListItem buff=buff}}
|
|
||||||
{{/each}}
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
<paper-button id="addBuffButton"
|
|
||||||
class="red-button"
|
|
||||||
raised>
|
|
||||||
Add Buff
|
|
||||||
</paper-button>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template name="customBuffEditListItem">
|
|
||||||
<div class="buff layout horizontal center" data-id={{buff._id}}>
|
|
||||||
{{> customBuffView buff=buff}}
|
|
||||||
<div>
|
|
||||||
<paper-icon-button class="edit-buff" icon="create">
|
|
||||||
</paper-icon-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
Template.customBuffEditList.helpers({
|
|
||||||
buffs: function(){
|
|
||||||
var selector = {
|
|
||||||
"parent.id": this.parentId,
|
|
||||||
"charId": this.charId,
|
|
||||||
};
|
|
||||||
return CustomBuffs.find(selector);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.customBuffEditList.events({
|
|
||||||
"tap #addBuffButton": function(event, instance){
|
|
||||||
if (!_.isBoolean(this.enabled)) {
|
|
||||||
this.enabled = true;
|
|
||||||
}
|
|
||||||
const customBuffId = CustomBuffs.insert({
|
|
||||||
name: this.name || "New Buff",
|
|
||||||
charId: this.charId,
|
|
||||||
parent: {
|
|
||||||
id: this.parentId,
|
|
||||||
collection: this.parentCollection,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
pushDialogStack({
|
|
||||||
template: "customBuffEdit",
|
|
||||||
data: {customBuffId},
|
|
||||||
element: event.currentTarget,
|
|
||||||
returnElement: () => instance.find(`tr.buff[data-id='${customBuffId}']`),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.customBuffEditListItem.events({
|
|
||||||
"tap .edit-buff": function(event, template){
|
|
||||||
pushDialogStack({
|
|
||||||
template: "customBuffEdit",
|
|
||||||
data: {customBuffId: this.buff._id},
|
|
||||||
element: event.currentTarget.parentElement.parentElement,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
<template name="customBuffView">
|
|
||||||
<div class="flex">{{buff.name}}</div>
|
|
||||||
<div class="flex">
|
|
||||||
{{#if canEditCharacter buff.charId}}
|
|
||||||
<paper-button class="apply-buff-button">Apply{{toSelf}}</paper-button>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
const applyBuff = function(targetId, buff) {
|
|
||||||
var parent = global[buff.parent.collection].findOne(buff.parent.id);
|
|
||||||
|
|
||||||
//insert new buff
|
|
||||||
newBuffId = Buffs.insert({
|
|
||||||
charId: targetId,
|
|
||||||
name: buff.name,
|
|
||||||
description: buff.description,
|
|
||||||
lifeTime: {total: buff.lifeTime.total},
|
|
||||||
type: "custom",
|
|
||||||
|
|
||||||
appliedBy: buff.charId,
|
|
||||||
appliedByDetails: {
|
|
||||||
name: parent.name,
|
|
||||||
collection: buff.parent.collection,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
//insert children
|
|
||||||
Attacks.find({"parent.id": buff._id}).forEach(function(doc){
|
|
||||||
temp = _.clone(doc);
|
|
||||||
temp.parent.id = newBuffId;
|
|
||||||
temp.parent.collection = "Buffs";
|
|
||||||
delete temp._id;
|
|
||||||
|
|
||||||
Attacks.insert(temp);
|
|
||||||
});
|
|
||||||
Effects.find({"parent.id": buff._id}).forEach(function(doc){
|
|
||||||
temp = _.clone(doc);
|
|
||||||
temp.parent.id = newBuffId;
|
|
||||||
temp.parent.collection = "Buffs";
|
|
||||||
delete temp._id;
|
|
||||||
|
|
||||||
Effects.insert(temp);
|
|
||||||
});
|
|
||||||
Proficiencies.find({"parent.id": buff._id}).forEach(function(doc){
|
|
||||||
temp = _.clone(doc);
|
|
||||||
temp.parent.id = newBuffId;
|
|
||||||
temp.parent.collection = "Buffs";
|
|
||||||
delete temp._id;
|
|
||||||
|
|
||||||
Proficiencies.insert(temp);
|
|
||||||
});
|
|
||||||
|
|
||||||
let target;
|
|
||||||
if (targetId == buff.charId) {
|
|
||||||
target = "self";
|
|
||||||
} else {
|
|
||||||
target = Characters.findOne(targetId) || {};
|
|
||||||
target = target && target.name || "target"
|
|
||||||
}
|
|
||||||
GlobalUI.toast(`${buff.name || "Buff"} applied to ${target}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
Template.customBuffView.helpers({
|
|
||||||
toSelf: function() {
|
|
||||||
if (this.buff.target === "self") {
|
|
||||||
return " to self";
|
|
||||||
} else {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Template.customBuffView.events({
|
|
||||||
"click .apply-buff-button": function(){
|
|
||||||
if (this.buff.target !== "self") {
|
|
||||||
pushDialogStack({
|
|
||||||
template: "applyBuffDialog",
|
|
||||||
data: {buff: this.buff},
|
|
||||||
element: event.currentTarget,
|
|
||||||
callback: (targetId) => {
|
|
||||||
if (!targetId) return;
|
|
||||||
applyBuff(targetId, this.buff);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
var targetId = this.buff.charId;
|
|
||||||
applyBuff(targetId, this.buff);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
<template name="customBuffViewList">
|
|
||||||
{{#if buffs.count}}
|
|
||||||
<div class="buffs">
|
|
||||||
<div class="paper-font-title" style="margin-bottom: 8px;">
|
|
||||||
Buffs
|
|
||||||
</div>
|
|
||||||
{{#each buff in buffs}}
|
|
||||||
<div class="layout horizontal center">
|
|
||||||
{{> customBuffView buff=buff}}
|
|
||||||
</div>
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</template>
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
Template.customBuffViewList.helpers({
|
|
||||||
buffs: function(){
|
|
||||||
var selector = {
|
|
||||||
"parent.id": this.parentId,
|
|
||||||
"charId": this.charId,
|
|
||||||
};
|
|
||||||
return CustomBuffs.find(selector);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
<template name="characterSettings">
|
|
||||||
{{#with character}}
|
|
||||||
<div class="fit layout vertical">
|
|
||||||
<app-header-layout has-scrolling-region class="feedback flex">
|
|
||||||
<app-header fixed effects="waterfall">
|
|
||||||
<app-toolbar>
|
|
||||||
<div main-title>Character Settings</div>
|
|
||||||
</app-toolbar>
|
|
||||||
</app-header>
|
|
||||||
<div class="form flex">
|
|
||||||
<paper-toggle-button id="hideSpellcasting" checked={{settings.hideSpellcasting}}>
|
|
||||||
Hide Spells tab
|
|
||||||
</paper-toggle-button>
|
|
||||||
<paper-toggle-button id="variantEncumbrance" checked={{settings.useVariantEncumbrance}}>
|
|
||||||
Use variant encumbrance
|
|
||||||
</paper-toggle-button>
|
|
||||||
<paper-toggle-button id="swapStatAndModifier" checked={{settings.swapStatAndModifier}}>
|
|
||||||
Swap stats and modifiers on Stats page
|
|
||||||
</paper-toggle-button>
|
|
||||||
</div>
|
|
||||||
</app-header-layout>
|
|
||||||
<div class="buttons layout horizontal end-justified">
|
|
||||||
<paper-button class="doneButton"> Done </paper-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/with}}
|
|
||||||
</template>
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user