Compare commits
99 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6207ffa516 | ||
|
|
9d33612054 | ||
|
|
face6387a0 | ||
|
|
b308595dac | ||
|
|
1d2de197a4 | ||
|
|
a3d790b47d | ||
|
|
efe6dd87db | ||
|
|
5b33a6e783 | ||
|
|
8730fab40b | ||
|
|
992776bb40 | ||
|
|
bc9ec4421c | ||
|
|
4c31ab601c | ||
|
|
c4e77c7eae | ||
|
|
2cd6e27f70 | ||
|
|
f6b2dde479 | ||
|
|
44da62a962 | ||
|
|
4e96047e90 | ||
|
|
212986ac37 | ||
|
|
877f516565 | ||
|
|
750022f0f1 | ||
|
|
614284c73d | ||
|
|
6528fc8bab | ||
|
|
020930b2e4 | ||
|
|
dcd76e06e1 | ||
|
|
8a58002415 | ||
|
|
535fcd77cf | ||
|
|
7c2aed26a4 | ||
|
|
fab052050a | ||
|
|
b7bdb141c8 | ||
|
|
0b8fabde14 | ||
|
|
3336e177d9 | ||
|
|
2e9440e325 | ||
|
|
e4ac400cbd | ||
|
|
f600999c5f | ||
|
|
0af905699a | ||
|
|
6e900cfaae | ||
|
|
1f42d3c622 | ||
|
|
ee453d968f | ||
|
|
0850e59b30 | ||
|
|
f3e44cf033 | ||
|
|
907f9d15d4 | ||
|
|
4c3d5d40dd | ||
|
|
ef0deb20aa | ||
|
|
b43ee08d75 | ||
|
|
7e7f1ec997 | ||
|
|
0f652f5c74 | ||
|
|
3e02875eaf | ||
|
|
b9a5230344 | ||
|
|
28780b96c3 | ||
|
|
bec0b33805 | ||
|
|
ad9ccbe7ef | ||
|
|
e2933c2df5 | ||
|
|
87583fdac6 | ||
|
|
68e1382aed | ||
|
|
7b62c82e32 | ||
|
|
6dd92586a4 | ||
|
|
b3d0db1f02 | ||
|
|
5f35c71c9d | ||
|
|
85baf4e5d1 | ||
|
|
15d797131e | ||
|
|
06ac9f70c8 | ||
|
|
471a3e274e | ||
|
|
d4031dc4a7 | ||
|
|
18e5ab3f21 | ||
|
|
c9fe2f17a0 | ||
|
|
818cb3905f | ||
|
|
64ef426035 | ||
|
|
2b188f1a8d | ||
|
|
08735ea4f7 | ||
|
|
bce1b85600 | ||
|
|
0d023e2ba3 | ||
|
|
dad575de64 | ||
|
|
cb648b4a28 | ||
|
|
1279137362 | ||
|
|
3c06529906 | ||
|
|
a3b0c6cafd | ||
|
|
a1d9f7f5bb | ||
|
|
3b03e9c71c | ||
|
|
0eea6f2386 | ||
|
|
8f8714d3e5 | ||
|
|
7c38e8d70a | ||
|
|
826859ca3f | ||
|
|
2ca13fbb56 | ||
|
|
6d801e0178 | ||
|
|
9ddac7d5cd | ||
|
|
7a6f751e30 | ||
|
|
2389768234 | ||
|
|
c76fe99148 | ||
|
|
53afaa4f37 | ||
|
|
3599b5fbc4 | ||
|
|
275fb1ca65 | ||
|
|
d5d937b04a | ||
|
|
aa554adbce | ||
|
|
cb739eb207 | ||
|
|
66df2ea4aa | ||
|
|
bb95fe0d9a | ||
|
|
17a390a8aa | ||
|
|
b3ef43eb70 | ||
|
|
be92ef224c |
@@ -21,32 +21,40 @@
|
|||||||
"weight": 1
|
"weight": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Arrows (20)",
|
"libraryName": "Arrows (20)",
|
||||||
"plural": "Arrows (20)",
|
"name": "Arrow",
|
||||||
|
"plural": "Arrows",
|
||||||
"description": "",
|
"description": "",
|
||||||
"value": 1,
|
"value": 0.05,
|
||||||
"weight": 1
|
"weight": 0.05,
|
||||||
|
"quantity": 20
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Blowgun needles (5)",
|
"libraryName": "Blowgun needles (5)",
|
||||||
"plural": "Blowgun needles (5)",
|
"name": "Blowgun needle",
|
||||||
|
"plural": "Blowgun needles",
|
||||||
"description": "",
|
"description": "",
|
||||||
"value": 1,
|
"value": 0.2,
|
||||||
"weight": 1
|
"weight": 0.2,
|
||||||
|
"quantity": 5
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Crossbow bolts (20)",
|
"libraryName": "Crossbow bolts (20)",
|
||||||
"plural": "Crossbow bolts (20)",
|
"name": "Crossbow bolt",
|
||||||
|
"plural": "Crossbow bolts",
|
||||||
"description": "",
|
"description": "",
|
||||||
"value": 1,
|
"value": 0.05,
|
||||||
"weight": 1.5
|
"weight": 0.075,
|
||||||
|
"quantity": 20
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Sling bullets (20)",
|
"libraryName": "Sling bullets (20)",
|
||||||
"plural": "Sling bullets (20)",
|
"name": "Sling bullet",
|
||||||
|
"plural": "Sling bullets",
|
||||||
"description": "",
|
"description": "",
|
||||||
"value": 0.04,
|
"value": 0.002,
|
||||||
"weight": 1.5
|
"weight": 0.075,
|
||||||
|
"quantity": 20
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Antitoxin (vial)",
|
"name": "Antitoxin (vial)",
|
||||||
@@ -651,11 +659,13 @@
|
|||||||
"weight": 3
|
"weight": 3
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Spikes, iron (10)",
|
"libraryName": "Spikes, iron (10)",
|
||||||
"plural": "Spikes, iron (10)",
|
"name": "Spike, iron",
|
||||||
|
"plural": "Spikes, iron",
|
||||||
"description": "",
|
"description": "",
|
||||||
"value": 1,
|
"value": 0.1,
|
||||||
"weight": 5
|
"weight": 0.5,
|
||||||
|
"quantity": 10
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Spyglass",
|
"name": "Spyglass",
|
||||||
|
|||||||
@@ -135,7 +135,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"castingTime": "action",
|
"castingTime": "action",
|
||||||
"description": "Objects come to life at your command. Choose up to ten nonmagical objects within range that are not being worn or carried. Medium targets count as two objects, Large targets count as four objects, Huge targets count as eight objects. You can’t animate any object larger than Huge. Each target animates and becomes a creature under your control until the spell ends or until reduced to 0 hit points.\n\n As a bonus action, you can mentally command any creature you made with this spell if the creature is within 500 feet of you (if you control multiple creatures, you can command any or all of them at the same time, issuing the same command to each one). You decide what action the creature will take and where it will move during its next turn, or you can issue a general command, such as to guard a particular chamber or corridor. If you issue no commands, the creature only defends itself against hostile creatures. Once given an order, the creature continues to follow it until its task is complete.\n\nAnimated Object Statistics Size HP AC Attack Str Dex Tiny\n\n20 18 +8 to hit, 1d4 + 4 damage 4 18\n\nSmall 25 16 +6 to hit, 1d8 + 2 damage 6 14\n\nMedium 40 13 +5 to hit, 2d6 + 1 damage 10 12\n\nLarge 50 10 +6 to hit, 2d10 + 2 damage 14 10\n\nHuge 80 10 +8 to hit, 2d12 + 4 damage 18 6\n\n An animated object is a construct with AC, hit points, attacks, Strength, and Dexterity determined by its size. Its Constitution is 10 and its Intelligence and Wisdom are 3, and its Charisma is 1. Its speed is 30 feet; if the object lacks legs or other appendages it can use for locomotion, it instead has a flying speed of 30 feet and can hover. If the object is securely attached to a surface or a larger object, such as a chain bolted to a wall, its speed is 0. It has blindsight with a radius of 30 feet and is blind beyond that distance. When the animated object drops to 0 hit points, it reverts to its original object form, and any remaining damage carries over to its original object form.\n\n If you command an object to attack, it can make a single melee attack against a creature within 5 feet of it. It makes a slam attack with an attack bonus and bludgeoning damage determined by its size. The GM might rule that a specific object inflicts slashing or piercing damage based on its form.\n\n ***At Higher Levels.***If you cast this spell using a spell slot of 6th level or higher, you can animate two additional objects for each slot level above 5th.",
|
"description": "Objects come to life at your command. Choose up to ten nonmagical objects within range that are not being worn or carried. Medium targets count as two objects, Large targets count as four objects, Huge targets count as eight objects. You can’t animate any object larger than Huge. Each target animates and becomes a creature under your control until the spell ends or until reduced to 0 hit points.\n\nAs a bonus action, you can mentally command any creature you made with this spell if the creature is within 500 feet of you (if you control multiple creatures, you can command any or all of them at the same time, issuing the same command to each one). You decide what action the creature will take and where it will move during its next turn, or you can issue a general command, such as to guard a particular chamber or corridor. If you issue no commands, the creature only defends itself against hostile creatures. Once given an order, the creature continues to follow it until its task is complete.\n\n### Animated Object Statistics \n\nSize | HP | AC | Attack | Str | Dex \n--- | --- | --- | ---| --- | --- \nTiny | 20 | 18 | +8 to hit, 1d4 + 4 damage | 4 | 18 \nSmall | 25 | 16 | +6 to hit, 1d8 + 2 damage | 6 | 14 \nMedium | 40 | 13 | +5 to hit, 2d6 + 1 damage | 10 | 12 \nLarge | 50 | 10 | +6 to hit, 2d10 + 2 damage | 14 | 10 \nHuge | 80 | 10 | +8 to hit, 2d12 + 4 damage | 18 | 6 \n\nAn animated object is a construct with AC, hit points, attacks, Strength, and Dexterity determined by its size. Its Constitution is 10 and its Intelligence and Wisdom are 3, and its Charisma is 1. Its speed is 30 feet; if the object lacks legs or other appendages it can use for locomotion, it instead has a flying speed of 30 feet and can hover. If the object is securely attached to a surface or a larger object, such as a chain bolted to a wall, its speed is 0. It has blindsight with a radius of 30 feet and is blind beyond that distance. When the animated object drops to 0 hit points, it reverts to its original object form, and any remaining damage carries over to its original object form.\n\nIf you command an object to attack, it can make a single melee attack against a creature within 5 feet of it. It makes a slam attack with an attack bonus and bludgeoning damage determined by its size. The GM might rule that a specific object inflicts slashing or piercing damage based on its form.\n\n***At Higher Levels.***If you cast this spell using a spell slot of 6th level or higher, you can animate two additional objects for each slot level above 5th.",
|
||||||
"duration": "Concentration, up to 1 minute",
|
"duration": "Concentration, up to 1 minute",
|
||||||
"level": 5,
|
"level": 5,
|
||||||
"range": "120 feet",
|
"range": "120 feet",
|
||||||
@@ -2180,7 +2180,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"castingTime": "bonus action",
|
"castingTime": "bonus action",
|
||||||
"description": "A creature of your choice that you can see within range regains hit points equal to 1d4 + your spellcasting ability modifier. This spell has no effect on undead or constructs. ***At Higher Levels.*** When you cast this spell using a spell slot of 2nd level or higher, the healing increases by 1d4 for each slot level above 1st.",
|
"description": "A creature of your choice that you can see within range regains hit points equal to 1d4 + your spellcasting ability modifier. This spell has no effect on undead or constructs.\n\n***At Higher Levels.*** When you cast this spell using a spell slot of 2nd level or higher, the healing increases by 1d4 for each slot level above 1st.",
|
||||||
"duration": "Instantaneous",
|
"duration": "Instantaneous",
|
||||||
"level": 1,
|
"level": 1,
|
||||||
"range": "60 feet",
|
"range": "60 feet",
|
||||||
@@ -3618,7 +3618,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"castingTime": "1 hour",
|
"castingTime": "1 hour",
|
||||||
"description": "You touch a dead humanoid or a piece of a dead humanoid. Provided that the creature has been dead no longer than 10 days, the spell forms a new adult body for it and then calls the soul to enter that body. If the target’s soul isn’t free or willing to do so, the spell fails.\n\n The magic fashions a new body for the creature to inhabit, which likely causes the creature’s race to change. The DM rolls a d100 and consults the following table to determine what form the creature takes when restored to life, or the DM chooses a form.\n\nOn a d100 roll:\n\n 01-04 - Dragonborn\n\n05-13 - Dwarf, hill\n\n14-21 - Dwarf, mountain\n\n22-25 - Elf, dark\n\n26–34 - Elf, high\n\n35-42 - Elf, wood\n\n43-46 - Gnome, forest\n\n47-52 - Gnome, Rock\n\n53-56 - Half-elf\n\n57-60 - Half Orc,61-68 - Halfling, lightfoot\n\n69-76 - Halfling, stout\n\n77-96 - Human\n\n97-00 - Tiefling\n\n The reincarnated creature recalls its former life and experiences. It retains the capabilities it had in its original form, except it exchanges its original race for the new one and changes its racial traits accordingly. ",
|
"description":"You touch a dead humanoid or a piece of a dead humanoid. Provided that the creature has been dead no longer than 10 days, the spell forms a new adult body for it and then calls the soul to enter that body. If the target’s soul isn’t free or willing to do so, the spell fails.\n\nThe magic fashions a new body for the creature to inhabit, which likely causes the creature’s race to change. The DM rolls a d100 and consults the following table to determine what form the creature takes when restored to life, or the DM chooses a form.\n\nd100 | Race\n---|---\n01-04 | Dragonborn\n05-13 | Dwarf, hill\n14-21 | Dwarf, mountain\n22-25 | Elf, dark\n26–34 | Elf, high\n35-42 | Elf, wood\n43-46 | Gnome, forest\n47-52 | Gnome, Rock\n53-56 | Half-elf\n57-60 | Half Orc,61-68 - Halfling, lightfoot\n69-76 | Halfling, stout\n77-96 | Human\n97-00 | Tiefling\n\nThe reincarnated creature recalls its former life and experiences. It retains the capabilities it had in its original form, except it exchanges its original race for the new one and changes its racial traits accordingly. ",
|
||||||
"duration": "Instantaneous",
|
"duration": "Instantaneous",
|
||||||
"level": 5,
|
"level": 5,
|
||||||
"range": "Touch",
|
"range": "Touch",
|
||||||
@@ -3783,7 +3783,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"castingTime": "10 minutes",
|
"castingTime": "10 minutes",
|
||||||
"description": "You can see and hear a particular creature you choose that is on the same plane of existence as you. The target must make a DC {DC} Wisdom saving throw, which is modified by how well you know the target and the sort of physical connection you have to it. If a target knows you’re casting this spell, it can fail the saving throw voluntarily if it wants to be observed.\n\n - Knowledge / Save Modifier - \n\nSecondhand (you have heard of the target) / +5\n\nFirsthand (you have met the target) / +0\n\nFamiliar (you know the target well) / -5\n\nConnection / Save Modifier\n\nLikeness or picture / -2\n\nPossession or garment / -4\n\nBody part, lock of hair, bit of nail, or the like / -10\n\n On a successful save. the target isn’t affected. and you can’t use this spell against it again for 24 hours.\n\n On a failed save. the spell creates an invisible sensor within 10 feet of the target. You can see and hear through the sensor as if you were there. The sensor moves with the target, remaining within 10 feet of it for the duration. A creature that can see invisible objects sees the sensor as a luminous orb about the size of your fist.\n\n Instead of targeting a creature. you can choose a location you have seen before as the target of this spell. When you do, the sensor appears al that location and doesn’t move. ",
|
"description": "You can see and hear a particular creature you choose that is on the same plane of existence as you. The target must make a DC {DC} Wisdom saving throw, which is modified by how well you know the target and the sort of physical connection you have to it. If a target knows you’re casting this spell, it can fail the saving throw voluntarily if it wants to be observed.\n\nKnowledge | Save Modifier\n---|---\nSecondhand (you have heard of the target) | +5\nFirsthand (you have met the target) | +0\nFamiliar (you know the target well) | -5\n\nConnection | Save Modifier\n---|---\nLikeness or picture | -2\nPossession or garment | -4\nBody part, lock of hair, bit of nail, or the like | -10\n\nOn a successful save. the target isn’t affected. and you can’t use this spell against it again for 24 hours.\n\nOn a failed save. the spell creates an invisible sensor within 10 feet of the target. You can see and hear through the sensor as if you were there. The sensor moves with the target, remaining within 10 feet of it for the duration. A creature that can see invisible objects sees the sensor as a luminous orb about the size of your fist.\n\nInstead of targeting a creature. you can choose a location you have seen before as the target of this spell. When you do, the sensor appears al that location and doesn’t move. ",
|
||||||
"duration": "Concentration, up to 10 minutes",
|
"duration": "Concentration, up to 10 minutes",
|
||||||
"level": 5,
|
"level": 5,
|
||||||
"range": "Self",
|
"range": "Self",
|
||||||
@@ -4330,7 +4330,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"castingTime": "action",
|
"castingTime": "action",
|
||||||
"description": "This spell instantly transports you and up to eight willing creatures of your choice that you can see within range, or a single object that you can see within range, to a destination you select. If you target an object, it must be able to fit entirely inside a 10-foot cube, and it can’t be held or carried by an unwilling creature.\n\n The destination you choose must be known to you, and it must be on the same plane of existence as you. Your familiarity with the destination determines whether you arrive there successfully. The DM rolls d100 and consults the table. \n\n Familiarity. “Permanent circle” means a permanent teleportation circle whose sigil sequence you know. “Associated object” means that you possess an object taken from the desired destination within the last six months, such as a book from a wizard’s library, bed linen from a royal suite, or a chunk of marble from a lich’s secret tomb. \n\n “Very familiar” is a place you have been very often, a place you have carefully studied, or a place you can see when you cast the spell. “Seen casually” is someplace you have seen more than once but with which you aren’t very familiar. “Viewed once” is a place you have seen once, possibly using magic. “Description” is a place whose location and appearance you know through someone else’s description, perhaps from a map.\n\n “False destination” is a place that doesn’t exist. Perhaps you tried to scry an enemy’s sanctum but instead viewed an illusion, or you are attempting to teleport to a familiar location that no longer exists.\n\n On Target. You and your group (or the target object) appear where you want to.\n\n Off Target. You and your group (or the target object) appear a random distance away from the destination in a random direction. Distance off target is 1d10 × 1d10 percent of the distance that was to be traveled. For example, if you tried to travel 120 miles, landed off target, and rolled a 5 and 3 on the two d10s, then you would be off target by 15 percent, or 18 miles. The DM determines the direction off target randomly by rolling a d8 and designating 1 as north, 2 as northeast, 3 as east, and so on around the points of the compass. If you were teleporting to a coastal city and wound up 18 miles out at sea, you could be in trouble.\n\n Similar Area. You and your group (or the target object) wind up in a different area that’s visually or thematically similar to the target area. If you are heading for your home laboratory, for example, you might wind up in another wizard’s laboratory or in an alchemical supply shop that has many of the same tools and implements as your laboratory. Generally, you appear in the closest similar place, but since the spell has no range limit, you could conceivably wind up anywhere on the plane.\n\n Mishap. The spell’s unpredictable magic results in a difficult journey. Each teleporting creature (or the target object) takes 3d10 force damage, and the DM rerolls on the table to see where you wind up (multiple mishaps can occur, dealing damage each time). Similar Off On Familiarity Mishap Area Target Target Permanent circle — — — 01–100 Associated object — — — 01–100 Very familiar 01–05 06–13 14–24 25–100 Seen casually 01–33 34–43 44–53 54–100 Viewed once 01–43 44–53 54–73 74–100 Description 01–43 44–53 54–73 74–100 False destination 01–50 51–100 — —",
|
"description": "This spell instantly transports you and up to eight willing creatures of your choice that you can see within range, or a single object that you can see within range, to a destination you select. If you target an object, it must be able to fit entirely inside a 10-foot cube, and it can’t be held or carried by an unwilling creature.\n\nThe destination you choose must be known to you, and it must be on the same plane of existence as you. Your familiarity with the destination determines whether you arrive there successfully. The DM rolls d100 and consults the table. \n\nFamiliarity. “Permanent circle” means a permanent teleportation circle whose sigil sequence you know. “Associated object” means that you possess an object taken from the desired destination within the last six months, such as a book from a wizard’s library, bed linen from a royal suite, or a chunk of marble from a lich’s secret tomb. \n\n“Very familiar” is a place you have been very often, a place you have carefully studied, or a place you can see when you cast the spell. “Seen casually” is someplace you have seen more than once but with which you aren’t very familiar. “Viewed once” is a place you have seen once, possibly using magic. “Description” is a place whose location and appearance you know through someone else’s description, perhaps from a map.\n\n“False destination” is a place that doesn’t exist. Perhaps you tried to scry an enemy’s sanctum but instead viewed an illusion, or you are attempting to teleport to a familiar location that no longer exists.\n\nOn Target. You and your group (or the target object) appear where you want to.\n\nOff Target. You and your group (or the target object) appear a random distance away from the destination in a random direction. Distance off target is 1d10 × 1d10 percent of the distance that was to be traveled. For example, if you tried to travel 120 miles, landed off target, and rolled a 5 and 3 on the two d10s, then you would be off target by 15 percent, or 18 miles. The DM determines the direction off target randomly by rolling a d8 and designating 1 as north, 2 as northeast, 3 as east, and so on around the points of the compass. If you were teleporting to a coastal city and wound up 18 miles out at sea, you could be in trouble.\n\nSimilar Area. You and your group (or the target object) wind up in a different area that’s visually or thematically similar to the target area. If you are heading for your home laboratory, for example, you might wind up in another wizard’s laboratory or in an alchemical supply shop that has many of the same tools and implements as your laboratory. Generally, you appear in the closest similar place, but since the spell has no range limit, you could conceivably wind up anywhere on the plane.\n\nMishap. The spell’s unpredictable magic results in a difficult journey. Each teleporting creature (or the target object) takes 3d10 force damage, and the DM rerolls on the table to see where you wind up (multiple mishaps can occur, dealing damage each time). \n\nFamiliarity | Mishap | Similar Area | Off Target | On Target\n---|---|---|---|---\nPermanent circle | — | — | — | 01–100\nAssociated object | — | — | — | 01–100\nVery familiar | 01–05 | 06–13 | 14–24 | 25–100\nSeen casually | 01–33 | 34–43 | 44–53 | 54–100\nViewed once | 01–43 | 44–53 | 54–73 | 74–100\nDescription | 01–43 | 44–53 | 54–73 | 74–100\nFalse destination | 01–50 | 51–100 | — | —",
|
||||||
"duration": "Instantaneous",
|
"duration": "Instantaneous",
|
||||||
"level": 7,
|
"level": 7,
|
||||||
"range": "10 feet",
|
"range": "10 feet",
|
||||||
|
|||||||
@@ -13,3 +13,5 @@ notices-for-facebook-graph-api-2
|
|||||||
1.3.0-split-minifiers-package
|
1.3.0-split-minifiers-package
|
||||||
1.4.0-remove-old-dev-bundle-link
|
1.4.0-remove-old-dev-bundle-link
|
||||||
1.4.1-add-shell-server-package
|
1.4.1-add-shell-server-package
|
||||||
|
1.4.3-split-account-service-packages
|
||||||
|
1.5-add-dynamic-import-package
|
||||||
|
|||||||
@@ -3,13 +3,12 @@
|
|||||||
# '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.
|
||||||
|
|
||||||
iron:router
|
accounts-password@1.4.0
|
||||||
accounts-password@1.3.3
|
|
||||||
accounts-ui@1.1.9
|
accounts-ui@1.1.9
|
||||||
random@1.0.10
|
random@1.0.10
|
||||||
dburles:collection-helpers
|
dburles:collection-helpers
|
||||||
reactive-var@1.0.11
|
reactive-var@1.0.11
|
||||||
underscore@1.0.10
|
underscore
|
||||||
aldeed:collection2
|
aldeed:collection2
|
||||||
matb33:collection-hooks
|
matb33:collection-hooks
|
||||||
zimme:collection-softremovable
|
zimme:collection-softremovable
|
||||||
@@ -18,35 +17,40 @@ dburles:mongo-collection-instances
|
|||||||
percolate:migrations
|
percolate:migrations
|
||||||
ecwyne:mathjs
|
ecwyne:mathjs
|
||||||
useraccounts:polymer
|
useraccounts:polymer
|
||||||
accounts-google@1.0.11
|
accounts-google@1.2.0
|
||||||
splendido:accounts-meld
|
splendido:accounts-meld
|
||||||
email@1.1.18
|
email@1.2.3
|
||||||
meteorhacks:subs-manager
|
meteorhacks:subs-manager
|
||||||
chuangbo:marked
|
chuangbo:marked
|
||||||
reywood:iron-router-ga
|
reywood:iron-router-ga
|
||||||
meteor-base@1.0.4
|
meteor-base@1.1.0
|
||||||
mobile-experience@1.0.4
|
mobile-experience@1.0.4
|
||||||
mongo@1.1.14
|
mongo@1.2.0
|
||||||
blaze-html-templates
|
blaze-html-templates
|
||||||
session@1.1.7
|
session@1.1.7
|
||||||
jquery@1.11.10
|
jquery@1.11.10
|
||||||
tracker@1.1.1
|
tracker@1.1.3
|
||||||
logging@1.1.16
|
logging@1.1.17
|
||||||
reload@1.1.11
|
reload@1.1.11
|
||||||
ejson@1.0.13
|
ejson@1.0.14
|
||||||
spacebars
|
spacebars
|
||||||
check@1.2.4
|
check@1.2.5
|
||||||
useraccounts:iron-routing
|
useraccounts:iron-routing
|
||||||
wizonesolutions:canonical
|
wizonesolutions:canonical
|
||||||
standard-minifier-js@1.2.1
|
standard-minifier-js@2.1.1
|
||||||
shell-server@0.2.1
|
shell-server@0.2.4
|
||||||
seba:minifiers-autoprefixer
|
seba:minifiers-autoprefixer
|
||||||
nikogosovd:multiple-uihooks
|
nikogosovd:multiple-uihooks
|
||||||
templates:array
|
templates:array
|
||||||
ecmascript@0.6.1
|
ecmascript@0.8.2
|
||||||
es5-shim@4.6.15
|
es5-shim@4.6.15
|
||||||
differential:vulcanize
|
differential:vulcanize
|
||||||
reactive-dict
|
reactive-dict@1.1.9
|
||||||
percolate:synced-cron
|
percolate:synced-cron
|
||||||
ongoworks:speakingurl
|
ongoworks:speakingurl
|
||||||
service-configuration
|
service-configuration@1.0.11
|
||||||
|
google-config-ui
|
||||||
|
dynamic-import
|
||||||
|
ddp-rate-limiter
|
||||||
|
rate-limit
|
||||||
|
iron:router
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
METEOR@1.4.2.6
|
METEOR@1.6
|
||||||
|
|||||||
@@ -1,53 +1,56 @@
|
|||||||
accounts-base@1.2.14
|
accounts-base@1.4.0
|
||||||
accounts-google@1.0.11
|
accounts-google@1.3.0
|
||||||
accounts-oauth@1.1.15
|
accounts-oauth@1.1.15
|
||||||
accounts-password@1.3.3
|
accounts-password@1.5.0
|
||||||
accounts-ui@1.1.9
|
accounts-ui@1.2.0
|
||||||
accounts-ui-unstyled@1.1.13
|
accounts-ui-unstyled@1.3.0
|
||||||
aldeed:collection2@2.10.0
|
aldeed:collection2@2.10.0
|
||||||
aldeed:collection2-core@1.2.0
|
aldeed:collection2-core@1.2.0
|
||||||
aldeed:schema-deny@1.1.0
|
aldeed:schema-deny@1.1.0
|
||||||
aldeed:schema-index@1.1.1
|
aldeed:schema-index@1.1.1
|
||||||
aldeed:simple-schema@1.5.3
|
aldeed:simple-schema@1.5.3
|
||||||
allow-deny@1.0.5
|
allow-deny@1.1.0
|
||||||
autoupdate@1.3.12
|
autoupdate@1.3.12
|
||||||
babel-compiler@6.13.0
|
babel-compiler@6.24.7
|
||||||
babel-runtime@1.0.1
|
babel-runtime@1.1.1
|
||||||
base64@1.0.10
|
base64@1.0.10
|
||||||
binary-heap@1.0.10
|
binary-heap@1.0.10
|
||||||
blaze@2.3.0
|
blaze@2.3.2
|
||||||
blaze-html-templates@1.1.0
|
blaze-html-templates@1.1.2
|
||||||
blaze-tools@1.0.10
|
blaze-tools@1.0.10
|
||||||
boilerplate-generator@1.0.11
|
boilerplate-generator@1.3.1
|
||||||
caching-compiler@1.1.9
|
caching-compiler@1.1.9
|
||||||
caching-html-compiler@1.1.0
|
caching-html-compiler@1.1.2
|
||||||
callback-hook@1.0.10
|
callback-hook@1.0.10
|
||||||
check@1.2.4
|
check@1.2.5
|
||||||
chuangbo:marked@0.3.5_1
|
chuangbo:marked@0.3.5_1
|
||||||
coffeescript@1.11.1_4
|
coffeescript@1.11.1_4
|
||||||
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.2.5
|
ddp@1.4.0
|
||||||
ddp-client@1.3.2
|
ddp-client@2.2.0
|
||||||
ddp-common@1.2.8
|
ddp-common@1.3.0
|
||||||
ddp-rate-limiter@1.0.6
|
ddp-rate-limiter@1.0.7
|
||||||
ddp-server@1.3.12
|
ddp-server@2.1.1
|
||||||
deps@1.0.12
|
deps@1.0.12
|
||||||
diff-sequence@1.0.7
|
diff-sequence@1.0.7
|
||||||
differential:vulcanize@3.0.0
|
differential:vulcanize@3.0.0
|
||||||
ecmascript@0.6.1
|
dynamic-import@0.2.1
|
||||||
ecmascript-runtime@0.3.15
|
ecmascript@0.9.0
|
||||||
|
ecmascript-runtime@0.5.0
|
||||||
|
ecmascript-runtime-client@0.5.0
|
||||||
|
ecmascript-runtime-server@0.5.0
|
||||||
ecwyne:mathjs@0.25.0
|
ecwyne:mathjs@0.25.0
|
||||||
ejson@1.0.13
|
ejson@1.1.0
|
||||||
email@1.1.18
|
email@1.2.3
|
||||||
es5-shim@4.6.15
|
es5-shim@4.6.15
|
||||||
fastclick@1.0.13
|
|
||||||
geojson-utils@1.0.10
|
geojson-utils@1.0.10
|
||||||
google@1.1.15
|
google-config-ui@1.0.0
|
||||||
|
google-oauth@1.2.4
|
||||||
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.2.10
|
http@1.3.0
|
||||||
id-map@1.0.9
|
id-map@1.0.9
|
||||||
iron:controller@1.0.12
|
iron:controller@1.0.12
|
||||||
iron:core@1.0.11
|
iron:core@1.0.11
|
||||||
@@ -55,45 +58,46 @@ iron:dynamic-template@1.0.12
|
|||||||
iron:layout@1.0.12
|
iron:layout@1.0.12
|
||||||
iron:location@1.0.11
|
iron:location@1.0.11
|
||||||
iron:middleware-stack@1.1.0
|
iron:middleware-stack@1.1.0
|
||||||
iron:router@1.1.1
|
iron:router@1.1.2
|
||||||
iron:url@1.0.11
|
iron:url@1.1.0
|
||||||
jquery@1.11.10
|
jquery@1.11.10
|
||||||
lai:collection-extensions@0.2.1_1
|
lai:collection-extensions@0.2.1_1
|
||||||
launch-screen@1.1.0
|
launch-screen@1.1.1
|
||||||
less@2.7.9
|
less@2.7.11
|
||||||
livedata@1.0.18
|
livedata@1.0.18
|
||||||
localstorage@1.0.12
|
localstorage@1.2.0
|
||||||
logging@1.1.16
|
logging@1.1.19
|
||||||
matb33:collection-hooks@0.8.4
|
matb33:collection-hooks@0.8.4
|
||||||
mdg:validation-error@0.5.1
|
mdg:validation-error@0.5.1
|
||||||
meteor@1.6.0
|
meteor@1.8.2
|
||||||
meteor-base@1.0.4
|
meteor-base@1.2.0
|
||||||
meteorhacks:subs-manager@1.6.4
|
meteorhacks:subs-manager@1.6.4
|
||||||
minifier-css@1.2.16
|
minifier-css@1.2.16
|
||||||
minifier-js@1.2.17
|
minifier-js@2.2.2
|
||||||
minimongo@1.0.19
|
minimongo@1.4.2
|
||||||
mobile-experience@1.0.4
|
mobile-experience@1.0.5
|
||||||
mobile-status-bar@1.0.13
|
mobile-status-bar@1.0.14
|
||||||
modules@0.7.7
|
modules@0.11.0
|
||||||
modules-runtime@0.7.8
|
modules-runtime@0.9.1
|
||||||
momentjs:moment@2.17.1
|
momentjs:moment@2.19.2
|
||||||
mongo@1.1.14
|
mongo@1.3.0
|
||||||
|
mongo-dev-server@1.1.0
|
||||||
mongo-id@1.0.6
|
mongo-id@1.0.6
|
||||||
nikogosovd:multiple-uihooks@0.1.8
|
nikogosovd:multiple-uihooks@0.1.8
|
||||||
npm-bcrypt@0.9.2
|
npm-bcrypt@0.9.3
|
||||||
npm-mongo@2.2.16_1
|
npm-mongo@2.2.33
|
||||||
oauth@1.1.12
|
oauth@1.2.0
|
||||||
oauth2@1.1.11
|
oauth2@1.2.0
|
||||||
observe-sequence@1.0.14
|
observe-sequence@1.0.16
|
||||||
ongoworks:speakingurl@9.0.0
|
ongoworks:speakingurl@9.0.0
|
||||||
ordered-dict@1.0.9
|
ordered-dict@1.0.9
|
||||||
percolate:migrations@0.9.8
|
percolate:migrations@0.9.8
|
||||||
percolate:synced-cron@1.3.2
|
percolate:synced-cron@1.3.2
|
||||||
promise@0.8.8
|
promise@0.10.0
|
||||||
raix:eventemitter@0.1.3
|
raix:eventemitter@0.1.3
|
||||||
random@1.0.10
|
random@1.0.10
|
||||||
rate-limit@1.0.6
|
rate-limit@1.0.8
|
||||||
reactive-dict@1.1.8
|
reactive-dict@1.2.0
|
||||||
reactive-var@1.0.11
|
reactive-var@1.0.11
|
||||||
reload@1.1.11
|
reload@1.1.11
|
||||||
retry@1.0.9
|
retry@1.0.9
|
||||||
@@ -103,27 +107,27 @@ seba:minifiers-autoprefixer@1.0.1
|
|||||||
service-configuration@1.0.11
|
service-configuration@1.0.11
|
||||||
session@1.1.7
|
session@1.1.7
|
||||||
sha@1.0.9
|
sha@1.0.9
|
||||||
shell-server@0.2.1
|
shell-server@0.3.0
|
||||||
softwarerero:accounts-t9n@1.3.7
|
softwarerero:accounts-t9n@1.3.11
|
||||||
spacebars@1.0.13
|
spacebars@1.0.15
|
||||||
spacebars-compiler@1.1.0
|
spacebars-compiler@1.1.3
|
||||||
splendido:accounts-emails-field@1.2.0
|
splendido:accounts-emails-field@1.2.0
|
||||||
splendido:accounts-meld@1.3.1
|
splendido:accounts-meld@1.3.1
|
||||||
srp@1.0.10
|
srp@1.0.10
|
||||||
standard-minifier-js@1.2.2
|
standard-minifier-js@2.2.2
|
||||||
templates:array@1.0.3
|
templates:array@1.0.3
|
||||||
templating@1.3.0
|
templating@1.3.2
|
||||||
templating-compiler@1.3.0
|
templating-compiler@1.3.3
|
||||||
templating-runtime@1.3.0
|
templating-runtime@1.3.2
|
||||||
templating-tools@1.1.0
|
templating-tools@1.1.2
|
||||||
tracker@1.1.1
|
tracker@1.1.3
|
||||||
ui@1.0.12
|
ui@1.0.13
|
||||||
underscore@1.0.10
|
underscore@1.0.10
|
||||||
url@1.0.11
|
url@1.1.0
|
||||||
useraccounts:core@1.14.2
|
useraccounts:core@1.14.2
|
||||||
useraccounts:iron-routing@1.14.2
|
useraccounts:iron-routing@1.14.2
|
||||||
useraccounts:polymer@1.14.2
|
useraccounts:polymer@1.14.2
|
||||||
webapp@1.3.12
|
webapp@1.4.0
|
||||||
webapp-hashing@1.0.9
|
webapp-hashing@1.0.9
|
||||||
wizonesolutions:canonical@0.0.5
|
wizonesolutions:canonical@0.0.5
|
||||||
zimme:collection-behaviours@1.1.3
|
zimme:collection-behaviours@1.1.3
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ Schemas.Buff = new SimpleSchema({
|
|||||||
type: {
|
type: {
|
||||||
type: String,
|
type: String,
|
||||||
allowedValues: [
|
allowedValues: [
|
||||||
"inate",
|
"inate", //this should be "innate", but changing it could be problematic
|
||||||
"custom",
|
"custom",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -42,12 +42,26 @@ Schemas.Buff = new SimpleSchema({
|
|||||||
allowedValues: _.pluck(colorOptions, "key"),
|
allowedValues: _.pluck(colorOptions, "key"),
|
||||||
defaultValue: "q",
|
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.attachSchema(Schemas.Buff);
|
||||||
|
|
||||||
Buffs.attachBehaviour("softRemovable");
|
Buffs.attachBehaviour("softRemovable");
|
||||||
makeParent(Buffs, ["name", "enabled"]); //parents of effects
|
makeParent(Buffs, ["name", "enabled"]); //parents of effects, attacks, proficiencies
|
||||||
|
|
||||||
Buffs.allow(CHARACTER_SUBSCHEMA_ALLOW);
|
Buffs.allow(CHARACTER_SUBSCHEMA_ALLOW);
|
||||||
Buffs.deny(CHARACTER_SUBSCHEMA_DENY);
|
Buffs.deny(CHARACTER_SUBSCHEMA_DENY);
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ Schemas.Character = new SimpleSchema({
|
|||||||
|
|
||||||
//stats
|
//stats
|
||||||
hitPoints: {type: Schemas.Attribute},
|
hitPoints: {type: Schemas.Attribute},
|
||||||
|
tempHP: {type: Schemas.Attribute},
|
||||||
experience: {type: Schemas.Attribute},
|
experience: {type: Schemas.Attribute},
|
||||||
proficiencyBonus: {type: Schemas.Attribute},
|
proficiencyBonus: {type: Schemas.Attribute},
|
||||||
speed: {type: Schemas.Attribute},
|
speed: {type: Schemas.Attribute},
|
||||||
@@ -163,9 +164,9 @@ Schemas.Character = new SimpleSchema({
|
|||||||
|
|
||||||
//permissions
|
//permissions
|
||||||
party: {type: String, regEx: SimpleSchema.RegEx.Id, optional: true},
|
party: {type: String, regEx: SimpleSchema.RegEx.Id, optional: true},
|
||||||
owner: {type: String, regEx: SimpleSchema.RegEx.Id},
|
owner: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
|
||||||
readers: {type: [String], regEx: SimpleSchema.RegEx.Id, defaultValue: []},
|
readers: {type: [String], regEx: SimpleSchema.RegEx.Id, defaultValue: [], index: 1},
|
||||||
writers: {type: [String], regEx: SimpleSchema.RegEx.Id, defaultValue: []},
|
writers: {type: [String], regEx: SimpleSchema.RegEx.Id, defaultValue: [], index: 1},
|
||||||
color: {
|
color: {
|
||||||
type: String,
|
type: String,
|
||||||
allowedValues: _.pluck(colorOptions, "key"),
|
allowedValues: _.pluck(colorOptions, "key"),
|
||||||
@@ -184,11 +185,13 @@ Schemas.Character = new SimpleSchema({
|
|||||||
type: String,
|
type: String,
|
||||||
defaultValue: "whitelist",
|
defaultValue: "whitelist",
|
||||||
allowedValues: ["whitelist", "public"],
|
allowedValues: ["whitelist", "public"],
|
||||||
|
index: 1,
|
||||||
},
|
},
|
||||||
"settings.swapStatAndModifier": {type: Boolean, defaultValue: false},
|
"settings.swapStatAndModifier": {type: Boolean, defaultValue: false},
|
||||||
"settings.exportFeatures": {type: Boolean, defaultValue: true},
|
"settings.exportFeatures": {type: Boolean, defaultValue: true},
|
||||||
"settings.exportAttacks": {type: Boolean, defaultValue: true},
|
"settings.exportAttacks": {type: Boolean, defaultValue: true},
|
||||||
"settings.exportDescription": {type: Boolean, defaultValue: true},
|
"settings.exportDescription": {type: Boolean, defaultValue: true},
|
||||||
|
"settings.newUserExperience": {type: Boolean, optional: true},
|
||||||
});
|
});
|
||||||
|
|
||||||
Characters.attachSchema(Schemas.Character);
|
Characters.attachSchema(Schemas.Character);
|
||||||
@@ -282,6 +285,7 @@ if (Meteor.isClient) {
|
|||||||
|
|
||||||
//create a local memoize with a argument concatenating hash function
|
//create a local memoize with a argument concatenating hash function
|
||||||
var memoize = function(f) {
|
var memoize = function(f) {
|
||||||
|
if (Meteor.isServer) return f;
|
||||||
return Tracker.memoize(f, function() {
|
return Tracker.memoize(f, function() {
|
||||||
return _.reduce(arguments, function(memo, arg) {
|
return _.reduce(arguments, function(memo, arg) {
|
||||||
return memo + arg;
|
return memo + arg;
|
||||||
@@ -295,6 +299,7 @@ Characters.calculate = {
|
|||||||
var fieldSelector = {};
|
var fieldSelector = {};
|
||||||
fieldSelector[fieldName] = 1;
|
fieldSelector[fieldName] = 1;
|
||||||
var char = Characters.findOne(charId, {fields: fieldSelector});
|
var char = Characters.findOne(charId, {fields: fieldSelector});
|
||||||
|
if (!char) return;
|
||||||
var field = char[fieldName];
|
var field = char[fieldName];
|
||||||
if (field === undefined){
|
if (field === undefined){
|
||||||
throw new Meteor.Error(
|
throw new Meteor.Error(
|
||||||
@@ -328,6 +333,7 @@ Characters.calculate = {
|
|||||||
},
|
},
|
||||||
attributeValue: memoize(function(charId, attributeName){
|
attributeValue: memoize(function(charId, attributeName){
|
||||||
var attribute = Characters.calculate.getField(charId, attributeName);
|
var attribute = Characters.calculate.getField(charId, attributeName);
|
||||||
|
if (!attribute) return;
|
||||||
//base value
|
//base value
|
||||||
var value = Characters.calculate.attributeBase(charId, attributeName);
|
var value = Characters.calculate.attributeBase(charId, attributeName);
|
||||||
//plus adjustment
|
//plus adjustment
|
||||||
@@ -339,6 +345,7 @@ Characters.calculate = {
|
|||||||
}),
|
}),
|
||||||
skillMod: memoize(preventLoop(function(charId, skillName){
|
skillMod: memoize(preventLoop(function(charId, skillName){
|
||||||
var skill = Characters.calculate.getField(charId, skillName);
|
var skill = Characters.calculate.getField(charId, skillName);
|
||||||
|
if (!skill) return;
|
||||||
//get the final value of the ability score
|
//get the final value of the ability score
|
||||||
var ability = Characters.calculate.attributeValue(charId, skill.ability);
|
var ability = Characters.calculate.attributeValue(charId, skill.ability);
|
||||||
|
|
||||||
@@ -390,7 +397,6 @@ Characters.calculate = {
|
|||||||
return prof && prof.value || 0;
|
return prof && prof.value || 0;
|
||||||
}),
|
}),
|
||||||
passiveSkill: memoize(function(charId, skillName){
|
passiveSkill: memoize(function(charId, skillName){
|
||||||
var skill = Characters.calculate.getField(charId, skillName);
|
|
||||||
var mod = +Characters.calculate.skillMod(charId, skillName);
|
var mod = +Characters.calculate.skillMod(charId, skillName);
|
||||||
var value = 10 + mod;
|
var value = 10 + mod;
|
||||||
Effects.find(
|
Effects.find(
|
||||||
@@ -533,6 +539,7 @@ if (Meteor.isServer){
|
|||||||
Attacks .remove({charId: character._id});
|
Attacks .remove({charId: character._id});
|
||||||
Buffs .remove({charId: character._id});
|
Buffs .remove({charId: character._id});
|
||||||
Classes .remove({charId: character._id});
|
Classes .remove({charId: character._id});
|
||||||
|
CustomBuffs .remove({charId: character._id});
|
||||||
Effects .remove({charId: character._id});
|
Effects .remove({charId: character._id});
|
||||||
Experiences .remove({charId: character._id});
|
Experiences .remove({charId: character._id});
|
||||||
Features .remove({charId: character._id});
|
Features .remove({charId: character._id});
|
||||||
@@ -550,6 +557,10 @@ if (Meteor.isServer){
|
|||||||
});
|
});
|
||||||
Characters.before.insert(function(userId, doc) {
|
Characters.before.insert(function(userId, doc) {
|
||||||
doc.urlName = getSlug(doc.name, {maintainCase: true}) || "-";
|
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;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
42
rpg-docs/Model/Character/Conditions.js
Normal file
42
rpg-docs/Model/Character/Conditions.js
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
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);
|
||||||
53
rpg-docs/Model/Character/CustomBuffs.js
Normal file
53
rpg-docs/Model/Character/CustomBuffs.js
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
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);
|
||||||
@@ -83,3 +83,170 @@ Spells.after.update(function (userId, spell, fieldNames) {
|
|||||||
|
|
||||||
Spells.allow(CHARACTER_SUBSCHEMA_ALLOW);
|
Spells.allow(CHARACTER_SUBSCHEMA_ALLOW);
|
||||||
Spells.deny(CHARACTER_SUBSCHEMA_DENY);
|
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,6 +1,7 @@
|
|||||||
LibraryItems = new Mongo.Collection("libraryItems");
|
LibraryItems = new Mongo.Collection("libraryItems");
|
||||||
|
|
||||||
Schemas.LibraryItems = new SimpleSchema({
|
Schemas.LibraryItems = new SimpleSchema({
|
||||||
|
libraryName:{type: String, optional: true, trim: false},
|
||||||
name: {type: String, defaultValue: "New Item", trim: false},
|
name: {type: String, defaultValue: "New Item", trim: false},
|
||||||
plural: {type: String, optional: true, trim: false},
|
plural: {type: String, optional: true, trim: false},
|
||||||
description:{type: String, optional: true, trim: false},
|
description:{type: String, optional: true, trim: false},
|
||||||
|
|||||||
@@ -1,3 +1,75 @@
|
|||||||
|
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({
|
Meteor.users.allow({
|
||||||
update: function(userId, doc, fields, modifier) {
|
update: function(userId, doc, fields, modifier) {
|
||||||
if (
|
if (
|
||||||
@@ -21,3 +93,13 @@ Meteor.users.allow({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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}});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|||||||
53
rpg-docs/Routes/API.js
Normal file
53
rpg-docs/Routes/API.js
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
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, () =>
|
||||||
|
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, () =>
|
||||||
|
this.response.end(vMixParty(this.params._id))
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var ifKeyValid = function(apiKey, response, 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)){
|
||||||
|
response.writeHead(429, "Too many requests");
|
||||||
|
response.end();
|
||||||
|
} else {
|
||||||
|
rateLimiter.increment({apiKey})
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var isKeyValid = function(apiKey){
|
||||||
|
return !!Meteor.users.findOne({apiKey});
|
||||||
|
};
|
||||||
|
|
||||||
|
var rateLimiter = new RateLimiter();
|
||||||
|
rateLimiter.addRule({apiKey: String}, 2, 10000);
|
||||||
|
|
||||||
|
var isRateLimited = function(apiKey){
|
||||||
|
return !rateLimiter.check({apiKey}).allowed;
|
||||||
|
};
|
||||||
@@ -3,7 +3,8 @@ Template.registerHelper("canEditCharacter", function(charId) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
canEditCharacter = function(charId) {
|
canEditCharacter = function(charId) {
|
||||||
var char = Characters.findOne(charId)
|
var char = Characters.findOne(charId);
|
||||||
|
if (!char) return false;
|
||||||
var userId = Meteor.userId();
|
var userId = Meteor.userId();
|
||||||
return char.owner === userId ||
|
return char.owner === userId ||
|
||||||
_.contains(char.writers, userId);
|
_.contains(char.writers, userId);
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ openParentDialog = function({
|
|||||||
} else if (parent.collection === "Spells") {
|
} else if (parent.collection === "Spells") {
|
||||||
template = "spellDialog";
|
template = "spellDialog";
|
||||||
data = {spellId: parent.id};
|
data = {spellId: parent.id};
|
||||||
|
} else if (parent.collection === "Buffs") {
|
||||||
|
template = "buffDialog";
|
||||||
|
data = {buffId: parent.id};
|
||||||
}
|
}
|
||||||
pushDialogStack({template, data, element, returnElement, callback});
|
pushDialogStack({template, data, element, returnElement, callback});
|
||||||
};
|
};
|
||||||
|
|||||||
17
rpg-docs/client/style/bounce.css
Normal file
17
rpg-docs/client/style/bounce.css
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
@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;
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="layout vertical">
|
<div class="layout vertical">
|
||||||
<div>
|
<div>
|
||||||
{{evaluateDamage charId attack}} {{damageType}}
|
{{evaluateDamage charId attack}} {{attack.damageType}}
|
||||||
</div>
|
</div>
|
||||||
{{#if attack.details}}
|
{{#if attack.details}}
|
||||||
<div class="paper-font-caption">
|
<div class="paper-font-caption">
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<!-- 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>
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
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,15 +1,23 @@
|
|||||||
<template name="buffDialog">
|
<template name="buffDialog">
|
||||||
{{#with buff}}
|
{{#with buff}}
|
||||||
{{#baseDialog title=name class=colorClass hideEdit=true}}
|
{{#baseDialog title=name class="white" hideColor=true startEditing=true editOnly=true}}
|
||||||
|
{{> buffDetails}}
|
||||||
|
{{else}}
|
||||||
{{> buffDetails}}
|
{{> buffDetails}}
|
||||||
{{/baseDialog}}
|
{{/baseDialog}}
|
||||||
{{/with}}
|
{{/with}}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template name="buffDetails">
|
<template name="buffDetails">
|
||||||
|
<div>
|
||||||
|
{{appliedBy}}
|
||||||
|
</div>
|
||||||
|
<hr style="margin: 16px 0 16px 0;">
|
||||||
{{#if description}}
|
{{#if description}}
|
||||||
<div class="pre-wrap">{{evaluateString charId description}}</div>
|
<div>{{#markdown}}{{evaluateString charId description}}{{/markdown}}</div>
|
||||||
|
<hr style="margin: 16px 0 16px 0;">
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{> effectsViewList charId=charId parentId=_id}}
|
{{> effectsViewList charId=charId parentId=_id}}
|
||||||
|
{{> proficiencyViewList charId=charId parentId=_id}}
|
||||||
|
{{> attacksViewList charId=charId parentId=_id}}
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,5 +1,50 @@
|
|||||||
|
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({
|
Template.buffDialog.helpers({
|
||||||
buff: function(){
|
buff: function(){
|
||||||
return Buffs.findOne(this.buffId);
|
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 + ".";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
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);
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
.condition-library-dialog .item.selected {
|
||||||
|
background-color: #e4e4e4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.condition-library-dialog table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.condition-library-dialog .library-condition td, tr {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
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",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
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);
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
Template.conditionViewDialog.events({
|
||||||
|
"click #deleteButton": function(event, instance){
|
||||||
|
Conditions.remove(instance.data.condition._id);
|
||||||
|
popDialogStack();
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
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();
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<!--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>
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
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,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
Template.customBuffViewList.helpers({
|
||||||
|
buffs: function(){
|
||||||
|
var selector = {
|
||||||
|
"parent.id": this.parentId,
|
||||||
|
"charId": this.charId,
|
||||||
|
};
|
||||||
|
return CustomBuffs.find(selector);
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -10,7 +10,7 @@ Template.deleteCharacterConfirmation.helpers({
|
|||||||
if (Template.instance().canDelete.get()) {
|
if (Template.instance().canDelete.get()) {
|
||||||
return "background: #d23f31; color: white;";
|
return "background: #d23f31; color: white;";
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Template.deleteCharacterConfirmation.events({
|
Template.deleteCharacterConfirmation.events({
|
||||||
@@ -20,9 +20,7 @@ Template.deleteCharacterConfirmation.events({
|
|||||||
},
|
},
|
||||||
"click #deleteButton": function(event, instance) {
|
"click #deleteButton": function(event, instance) {
|
||||||
if (instance.find("#nameInput").value === this.name) {
|
if (instance.find("#nameInput").value === this.name) {
|
||||||
popDialogStack();
|
popDialogStack(true);
|
||||||
Router.go("/characterList");
|
|
||||||
Characters.remove(this._id);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"click .cancelButton": function(event, instance){
|
"click .cancelButton": function(event, instance){
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<!-- shamelessly nicked and renamed from deleteCharacterConfirmation.html -->
|
||||||
|
<template name="unshareCharacterConfirmation">
|
||||||
|
<div class="fit layout vertical">
|
||||||
|
<app-header-layout has-scrolling-region class="feedback flex">
|
||||||
|
<app-header fixed effects="waterfall">
|
||||||
|
<app-toolbar>
|
||||||
|
<div main-title>Unshare Character</div>
|
||||||
|
</app-toolbar>
|
||||||
|
</app-header>
|
||||||
|
<div class="form flex">
|
||||||
|
Removing (unsharing) a character does not delete it.<br>
|
||||||
|
However, you will be no longer be able to access or view it, unless it is publicly visible.<br>
|
||||||
|
The character's owner or anyone with write permissions for the character can return read access.<br><br>
|
||||||
|
To continue type "{{name}}" into the box below.<br>
|
||||||
|
<paper-input id="nameInput" label="type the characters's name here" style="width: 100%;"></paper-input><br>
|
||||||
|
<paper-button id="unshareButton" style={{getStyle}} disabled={{cantUnshare}}>Unshare Character</paper-button>
|
||||||
|
</div>
|
||||||
|
</app-header-layout>
|
||||||
|
<div class="buttons layout horizontal end-justified">
|
||||||
|
<paper-button class="cancelButton"> Cancel </paper-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
Template.unshareCharacterConfirmation.onCreated(function() {
|
||||||
|
this.canUnshare = new ReactiveVar(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.unshareCharacterConfirmation.helpers({
|
||||||
|
cantUnshare: function() {
|
||||||
|
return !Template.instance().canUnshare.get();
|
||||||
|
},
|
||||||
|
getStyle: function() {
|
||||||
|
if (Template.instance().canUnshare.get()) {
|
||||||
|
return "background: #d23f31; color: white;";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.unshareCharacterConfirmation.events({
|
||||||
|
"change #nameInput, input #nameInput": function(event, instance) {
|
||||||
|
var can = instance.find("#nameInput").value === this.name;
|
||||||
|
instance.canUnshare.set(can);
|
||||||
|
},
|
||||||
|
"click #unshareButton": function(event, instance) {
|
||||||
|
if (instance.find("#nameInput").value === this.name) {
|
||||||
|
setTimeout(popDialogStack, 100); //weird things happen without the delay.
|
||||||
|
Router.go("/characterList");
|
||||||
|
Meteor.call("removeMeFromReaders", this._id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"click .cancelButton": function(event, instance){
|
||||||
|
popDialogStack();
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<template name="characterSheet">
|
<template name="characterSheet">
|
||||||
<div class="fit layout vertical character-sheet">
|
<div class="fit layout vertical character-sheet">
|
||||||
<app-header fixed effects="waterfall">
|
<app-header fixed effects="waterfall">
|
||||||
<app-toolbar class="medium-tall {{colorClass}}">
|
<app-toolbar class="medium-tall {{colorClass}}" style="z-index: 2;">
|
||||||
<div top-item class="layout horizontal center">
|
<div top-item class="layout horizontal center">
|
||||||
<paper-icon-button icon="menu" drawer-toggle></paper-icon-button>
|
<paper-icon-button icon="menu" drawer-toggle></paper-icon-button>
|
||||||
<div class="flex character-name">
|
<div class="flex character-name">
|
||||||
@@ -30,21 +30,32 @@
|
|||||||
</paper-icon-item>
|
</paper-icon-item>
|
||||||
</paper-menu>
|
</paper-menu>
|
||||||
</paper-menu-button>
|
</paper-menu-button>
|
||||||
|
{{else}}
|
||||||
|
<paper-menu-button class="character-menu" horizontal-align="right">
|
||||||
|
<paper-icon-button icon="more-vert" class="dropdown-trigger"></paper-icon-button>
|
||||||
|
<paper-menu class="dropdown-content black87">
|
||||||
|
<paper-icon-item id="unshareCharacter">
|
||||||
|
<iron-icon icon="delete" item-icon></iron-icon>
|
||||||
|
Unshare
|
||||||
|
</paper-icon-item>
|
||||||
|
</paper-menu>
|
||||||
|
</paper-menu-button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
<div bottom-item>
|
<div bottom-item>
|
||||||
<paper-tabs id="characterSheetTabs" selected={{selectedTab}} class="{{colorClass}}">
|
<paper-tabs id="characterSheetTabs" selected={{selectedTab}} class="{{colorClass}}">
|
||||||
<paper-tab name="stats">Stats</paper-tab>
|
<paper-tab name="stats" class="{{#if shouldBounce 0}}bounce{{/if}}">Stats</paper-tab>
|
||||||
<paper-tab name="features">Features</paper-tab>
|
<paper-tab name="features" class="{{#if shouldBounce 1}}bounce{{/if}}">Features</paper-tab>
|
||||||
<paper-tab name="inventory">Inventory</paper-tab>
|
<paper-tab name="inventory">Inventory</paper-tab>
|
||||||
{{#unless hideSpellcasting}}
|
{{#unless hideSpellcasting}}
|
||||||
<paper-tab name="spells">Spells</paper-tab>
|
<paper-tab name="spells">Spells</paper-tab>
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
<paper-tab name="persona">Persona</paper-tab>
|
<paper-tab name="persona">Persona</paper-tab>
|
||||||
<paper-tab name="journal">Journal</paper-tab>
|
<paper-tab name="journal" class="{{#if shouldBounce 5}}bounce{{/if}}">Journal</paper-tab>
|
||||||
</paper-tabs>
|
</paper-tabs>
|
||||||
</div>
|
</div>
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
|
{{#if newUserExperience}}{{> newUserStepper}}{{/if}}
|
||||||
</app-header>
|
</app-header>
|
||||||
<div class="flex" style="position: relative;">
|
<div class="flex" style="position: relative;">
|
||||||
<iron-pages id="tabPages" class="fit" selected={{selectedTab}}>
|
<iron-pages id="tabPages" class="fit" selected={{selectedTab}}>
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ Template.characterSheet.onRendered(function() {
|
|||||||
tabFabMenus = _.times(6, (n) =>
|
tabFabMenus = _.times(6, (n) =>
|
||||||
tabPages[n].find(".mini-holder")
|
tabPages[n].find(".mini-holder")
|
||||||
);
|
);
|
||||||
})
|
});
|
||||||
|
|
||||||
//watch this character and make sure their encumbrance is updated
|
//watch this character and make sure their encumbrance is updated
|
||||||
//trackEncumbranceConditions(this.data._id, this);
|
//trackEncumbranceConditions(this.data._id, this);
|
||||||
@@ -172,6 +172,18 @@ Template.characterSheet.helpers({
|
|||||||
var char = Characters.findOne(this._id);
|
var char = Characters.findOne(this._id);
|
||||||
return char && char.settings.hideSpellcasting;
|
return char && char.settings.hideSpellcasting;
|
||||||
},
|
},
|
||||||
|
newUserExperience: function(){
|
||||||
|
var char = Characters.findOne(this._id);
|
||||||
|
return char && char.settings.newUserExperience;
|
||||||
|
},
|
||||||
|
shouldBounce: function(tab){
|
||||||
|
const selected = Session.get(this._id + ".selectedTab")
|
||||||
|
const step = Session.get("newUserExperienceStep");
|
||||||
|
if (selected == tab) return false;
|
||||||
|
return (tab === 1 && step === 0) ||
|
||||||
|
(tab === 5 && step === 1) ||
|
||||||
|
(tab === 0 && step === 2);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Template.characterSheet.events({
|
Template.characterSheet.events({
|
||||||
@@ -187,6 +199,12 @@ Template.characterSheet.events({
|
|||||||
data: this,
|
data: this,
|
||||||
template: "deleteCharacterConfirmation",
|
template: "deleteCharacterConfirmation",
|
||||||
element: event.currentTarget.parentElement.parentElement,
|
element: event.currentTarget.parentElement.parentElement,
|
||||||
|
callback: (result) => {
|
||||||
|
if (result === true){
|
||||||
|
Router.go("/characterList");
|
||||||
|
Tracker.afterFlush(() => Characters.remove(this._id));
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
"click #shareCharacter": function(event, instance){
|
"click #shareCharacter": function(event, instance){
|
||||||
@@ -210,4 +228,11 @@ Template.characterSheet.events({
|
|||||||
element: event.currentTarget.parentElement.parentElement,
|
element: event.currentTarget.parentElement.parentElement,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
"click #unshareCharacter": function(event, instance){
|
||||||
|
pushDialogStack({
|
||||||
|
data: this,
|
||||||
|
template: "unshareCharacterConfirmation",
|
||||||
|
element: event.currentTarget.parentElement.parentElement,
|
||||||
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,32 +7,36 @@ var stats = [
|
|||||||
{stat: "intelligence", name: "Intelligence", group: "Ability Scores"},
|
{stat: "intelligence", name: "Intelligence", group: "Ability Scores"},
|
||||||
{stat: "wisdom", name: "Wisdom", group: "Ability Scores"},
|
{stat: "wisdom", name: "Wisdom", group: "Ability Scores"},
|
||||||
{stat: "charisma", name: "Charisma", group: "Ability Scores"},
|
{stat: "charisma", name: "Charisma", group: "Ability Scores"},
|
||||||
{name: "Strength Save", stat: "strengthSave", group: "Saving Throws"},
|
|
||||||
{name: "Dexterity Save", stat: "dexteritySave", group: "Saving Throws"},
|
{stat: "strengthSave", name: "Strength Save", group: "Saving Throws"},
|
||||||
{name: "Constitution Save", stat: "constitutionSave", group: "Saving Throws"},
|
{stat: "dexteritySave", name: "Dexterity Save", group: "Saving Throws"},
|
||||||
{name: "Intelligence Save", stat: "intelligenceSave", group: "Saving Throws"},
|
{stat: "constitutionSave", name: "Constitution Save", group: "Saving Throws"},
|
||||||
{name: "Wisdom Save", stat: "wisdomSave", group: "Saving Throws"},
|
{stat: "intelligenceSave", name: "Intelligence Save", group: "Saving Throws"},
|
||||||
{name: "Charisma Save", stat: "charismaSave", group: "Saving Throws"},
|
{stat: "wisdomSave", name: "Wisdom Save", group: "Saving Throws"},
|
||||||
{name: "Acrobatics", stat: "acrobatics", group: "Skills"},
|
{stat: "charismaSave", name: "Charisma Save", group: "Saving Throws"},
|
||||||
{name: "Animal Handling", stat: "animalHandling", group: "Skills"},
|
|
||||||
{name: "Arcana", stat: "arcana", group: "Skills"},
|
{stat: "acrobatics", name: "Acrobatics", group: "Skills"},
|
||||||
{name: "Athletics", stat: "athletics", group: "Skills"},
|
{stat: "animalHandling", name: "Animal Handling", group: "Skills"},
|
||||||
{name: "Deception", stat: "deception", group: "Skills"},
|
{stat: "arcana", name: "Arcana", group: "Skills"},
|
||||||
{name: "History", stat: "history", group: "Skills"},
|
{stat: "athletics", name: "Athletics", group: "Skills"},
|
||||||
{name: "Insight", stat: "insight", group: "Skills"},
|
{stat: "deception", name: "Deception", group: "Skills"},
|
||||||
{name: "Intimidation", stat: "intimidation", group: "Skills"},
|
{stat: "history", name: "History", group: "Skills"},
|
||||||
{name: "Investigation", stat: "investigation", group: "Skills"},
|
{stat: "insight", name: "Insight", group: "Skills"},
|
||||||
{name: "Medicine", stat: "medicine", group: "Skills"},
|
{stat: "intimidation", name: "Intimidation", group: "Skills"},
|
||||||
{name: "Nature", stat: "nature", group: "Skills"},
|
{stat: "investigation", name: "Investigation", group: "Skills"},
|
||||||
{name: "Perception", stat: "perception", group: "Skills"},
|
{stat: "medicine", name: "Medicine", group: "Skills"},
|
||||||
{name: "Performance", stat: "performance", group: "Skills"},
|
{stat: "nature", name: "Nature", group: "Skills"},
|
||||||
{name: "Persuasion", stat: "persuasion", group: "Skills"},
|
{stat: "perception", name: "Perception", group: "Skills"},
|
||||||
{name: "Religion", stat: "religion", group: "Skills"},
|
{stat: "performance", name: "Performance", group: "Skills"},
|
||||||
{name: "Sleight of Hand", stat: "sleightOfHand", group: "Skills"},
|
{stat: "persuasion", name: "Persuasion", group: "Skills"},
|
||||||
{name: "Stealth", stat: "stealth", group: "Skills"},
|
{stat: "religion", name: "Religion", group: "Skills"},
|
||||||
{name: "Survival", stat: "survival", group: "Skills"},
|
{stat: "sleightOfHand", name: "Sleight of Hand", group: "Skills"},
|
||||||
{name: "Initiative", stat: "initiative", group: "Skills"},
|
{stat: "stealth", name: "Stealth", group: "Skills"},
|
||||||
|
{stat: "survival", name: "Survival", group: "Skills"},
|
||||||
|
{stat: "initiative", name: "Initiative", group: "Skills"},
|
||||||
|
|
||||||
{stat: "hitPoints", name: "Hit Points", group: "Stats"},
|
{stat: "hitPoints", name: "Hit Points", group: "Stats"},
|
||||||
|
{stat: "tempHP", name: "Temporary Hit Points", group: "Stats"},
|
||||||
{stat: "armor", name: "Armor", group: "Stats"},
|
{stat: "armor", name: "Armor", group: "Stats"},
|
||||||
{stat: "dexterityArmor", name: "Dexterity Armor Bonus", group: "Stats"},
|
{stat: "dexterityArmor", name: "Dexterity Armor Bonus", group: "Stats"},
|
||||||
{stat: "speed", name: "Speed", group: "Stats"},
|
{stat: "speed", name: "Speed", group: "Stats"},
|
||||||
@@ -44,6 +48,7 @@ var stats = [
|
|||||||
{stat: "expertiseDice", name: "Expertise Dice", group: "Stats"},
|
{stat: "expertiseDice", name: "Expertise Dice", group: "Stats"},
|
||||||
{stat: "superiorityDice", name: "Superiority Dice", group: "Stats"},
|
{stat: "superiorityDice", name: "Superiority Dice", group: "Stats"},
|
||||||
{stat: "carryMultiplier", name: "Carry Capacity Multiplier", group: "Stats"},
|
{stat: "carryMultiplier", name: "Carry Capacity Multiplier", group: "Stats"},
|
||||||
|
|
||||||
{stat: "level1SpellSlots", name: "level 1", group: "Spell Slots"},
|
{stat: "level1SpellSlots", name: "level 1", group: "Spell Slots"},
|
||||||
{stat: "level2SpellSlots", name: "level 2", group: "Spell Slots"},
|
{stat: "level2SpellSlots", name: "level 2", group: "Spell Slots"},
|
||||||
{stat: "level3SpellSlots", name: "level 3", group: "Spell Slots"},
|
{stat: "level3SpellSlots", name: "level 3", group: "Spell Slots"},
|
||||||
@@ -53,10 +58,12 @@ var stats = [
|
|||||||
{stat: "level7SpellSlots", name: "level 7", group: "Spell Slots"},
|
{stat: "level7SpellSlots", name: "level 7", group: "Spell Slots"},
|
||||||
{stat: "level8SpellSlots", name: "level 8", group: "Spell Slots"},
|
{stat: "level8SpellSlots", name: "level 8", group: "Spell Slots"},
|
||||||
{stat: "level9SpellSlots", name: "level 9", group: "Spell Slots"},
|
{stat: "level9SpellSlots", name: "level 9", group: "Spell Slots"},
|
||||||
|
|
||||||
{stat: "d6HitDice", name: "d6 Hit Dice", group: "Hit Dice"},
|
{stat: "d6HitDice", name: "d6 Hit Dice", group: "Hit Dice"},
|
||||||
{stat: "d8HitDice", name: "d8 Hit Dice", group: "Hit Dice"},
|
{stat: "d8HitDice", name: "d8 Hit Dice", group: "Hit Dice"},
|
||||||
{stat: "d10HitDice", name: "d10 Hit Dice", group: "Hit Dice"},
|
{stat: "d10HitDice", name: "d10 Hit Dice", group: "Hit Dice"},
|
||||||
{stat: "d12HitDice", name: "d12 Hit Dice", group: "Hit Dice"},
|
{stat: "d12HitDice", name: "d12 Hit Dice", group: "Hit Dice"},
|
||||||
|
|
||||||
{stat: "acidMultiplier", name: "Acid", group: "Weakness/Resistance"},
|
{stat: "acidMultiplier", name: "Acid", group: "Weakness/Resistance"},
|
||||||
{stat: "bludgeoningMultiplier", name: "Bludgeoning", group: "Weakness/Resistance"},
|
{stat: "bludgeoningMultiplier", name: "Bludgeoning", group: "Weakness/Resistance"},
|
||||||
{stat: "coldMultiplier", name: "Cold", group: "Weakness/Resistance"},
|
{stat: "coldMultiplier", name: "Cold", group: "Weakness/Resistance"},
|
||||||
|
|||||||
@@ -5,12 +5,14 @@ var stats = {
|
|||||||
"intelligence":{"name":"Intelligence"},
|
"intelligence":{"name":"Intelligence"},
|
||||||
"wisdom":{"name":"Wisdom"},
|
"wisdom":{"name":"Wisdom"},
|
||||||
"charisma":{"name":"Charisma"},
|
"charisma":{"name":"Charisma"},
|
||||||
|
|
||||||
"strengthSave":{"name":"Strength Save"},
|
"strengthSave":{"name":"Strength Save"},
|
||||||
"dexteritySave":{"name":"Dexterity Save"},
|
"dexteritySave":{"name":"Dexterity Save"},
|
||||||
"constitutionSave":{"name":"Constitution Save"},
|
"constitutionSave":{"name":"Constitution Save"},
|
||||||
"intelligenceSave":{"name":"Intelligence Save"},
|
"intelligenceSave":{"name":"Intelligence Save"},
|
||||||
"wisdomSave":{"name":"Wisdom Save"},
|
"wisdomSave":{"name":"Wisdom Save"},
|
||||||
"charismaSave":{"name":"Charisma Save"},
|
"charismaSave":{"name":"Charisma Save"},
|
||||||
|
|
||||||
"acrobatics":{"name":"Acrobatics"},
|
"acrobatics":{"name":"Acrobatics"},
|
||||||
"animalHandling":{"name":"Animal Handling"},
|
"animalHandling":{"name":"Animal Handling"},
|
||||||
"arcana":{"name":"Arcana"},
|
"arcana":{"name":"Arcana"},
|
||||||
@@ -30,7 +32,9 @@ var stats = {
|
|||||||
"stealth":{"name":"Stealth"},
|
"stealth":{"name":"Stealth"},
|
||||||
"survival":{"name":"Survival"},
|
"survival":{"name":"Survival"},
|
||||||
"initiative":{"name":"Initiative"},
|
"initiative":{"name":"Initiative"},
|
||||||
|
|
||||||
"hitPoints":{"name":"Hit Points"},
|
"hitPoints":{"name":"Hit Points"},
|
||||||
|
"tempHP":{"name":"Temporary Hit Points"},
|
||||||
"armor":{"name":"Armor"},
|
"armor":{"name":"Armor"},
|
||||||
"dexterityArmor":{"name":"Dexterity Armor Bonus"},
|
"dexterityArmor":{"name":"Dexterity Armor Bonus"},
|
||||||
"speed":{"name":"Speed"},
|
"speed":{"name":"Speed"},
|
||||||
@@ -42,6 +46,7 @@ var stats = {
|
|||||||
"expertiseDice":{"name":"Expertise Dice"},
|
"expertiseDice":{"name":"Expertise Dice"},
|
||||||
"superiorityDice":{"name":"Superiority Dice"},
|
"superiorityDice":{"name":"Superiority Dice"},
|
||||||
"carryMultiplier": {"name": "Carry Capacity Multiplier"},
|
"carryMultiplier": {"name": "Carry Capacity Multiplier"},
|
||||||
|
|
||||||
"level1SpellSlots":{"name":"level 1 Spell Slots"},
|
"level1SpellSlots":{"name":"level 1 Spell Slots"},
|
||||||
"level2SpellSlots":{"name":"level 2 Spell Slots"},
|
"level2SpellSlots":{"name":"level 2 Spell Slots"},
|
||||||
"level3SpellSlots":{"name":"level 3 Spell Slots"},
|
"level3SpellSlots":{"name":"level 3 Spell Slots"},
|
||||||
@@ -51,10 +56,12 @@ var stats = {
|
|||||||
"level7SpellSlots":{"name":"level 7 Spell Slots"},
|
"level7SpellSlots":{"name":"level 7 Spell Slots"},
|
||||||
"level8SpellSlots":{"name":"level 8 Spell Slots"},
|
"level8SpellSlots":{"name":"level 8 Spell Slots"},
|
||||||
"level9SpellSlots":{"name":"level 9 Spell Slots"},
|
"level9SpellSlots":{"name":"level 9 Spell Slots"},
|
||||||
|
|
||||||
"d6HitDice":{"name":"d6 Hit Dice"},
|
"d6HitDice":{"name":"d6 Hit Dice"},
|
||||||
"d8HitDice":{"name":"d8 Hit Dice"},
|
"d8HitDice":{"name":"d8 Hit Dice"},
|
||||||
"d10HitDice":{"name":"d10 Hit Dice"},
|
"d10HitDice":{"name":"d10 Hit Dice"},
|
||||||
"d12HitDice":{"name":"d12 Hit Dice"},
|
"d12HitDice":{"name":"d12 Hit Dice"},
|
||||||
|
|
||||||
"acidMultiplier":{"name":"Acid damage", "group": "Weakness/Resistance"},
|
"acidMultiplier":{"name":"Acid damage", "group": "Weakness/Resistance"},
|
||||||
"bludgeoningMultiplier":{
|
"bludgeoningMultiplier":{
|
||||||
"name":"Bludgeoning damage", "group": "Weakness/Resistance",
|
"name":"Bludgeoning damage", "group": "Weakness/Resistance",
|
||||||
|
|||||||
@@ -36,12 +36,26 @@
|
|||||||
|
|
||||||
{{> effectsViewList charId=charId parentId=_id}}
|
{{> effectsViewList charId=charId parentId=_id}}
|
||||||
{{> proficiencyViewList charId=charId parentId=_id}}
|
{{> proficiencyViewList charId=charId parentId=_id}}
|
||||||
|
{{> attacksViewList charId=charId parentId=_id}}
|
||||||
|
{{> customBuffViewList charId=charId parentId=_id}}
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template name="featureEdit">
|
<template name="featureEdit">
|
||||||
|
{{#if showNewUserExperience}}
|
||||||
|
{{# infoBox}}
|
||||||
|
<p>
|
||||||
|
Features represent all the permanent things your character can do.
|
||||||
|
</p><p>
|
||||||
|
A feature can change a character's stats with effects,
|
||||||
|
or give the character proficiencies, attacks, and buffs.
|
||||||
|
</p><p>
|
||||||
|
Give the feature a name, and close it to continue.
|
||||||
|
</p>
|
||||||
|
{{/infoBox}}
|
||||||
|
{{/if}}
|
||||||
<!--name-->
|
<!--name-->
|
||||||
<paper-input id="featureNameInput" class="fullwidth" label="Name" value={{name}}></paper-input>
|
<paper-input id="featureNameInput" class="fullwidth" label="Name" value={{name}}></paper-input>
|
||||||
|
|
||||||
<div class="layout horizontal center wrap justified">
|
<div class="layout horizontal center wrap justified">
|
||||||
<paper-dropdown-menu class=flex label="Enable Feature" style="flex-basis: 150px; max-width: 200px;">
|
<paper-dropdown-menu class=flex label="Enable Feature" style="flex-basis: 150px; max-width: 200px;">
|
||||||
<dicecloud-selector selected={{enabledSelection}} class="dropdown-content enabled-dropdown">
|
<dicecloud-selector selected={{enabledSelection}} class="dropdown-content enabled-dropdown">
|
||||||
@@ -76,4 +90,6 @@
|
|||||||
|
|
||||||
{{> effectsEditList parentId=_id parentCollection="Features" charId=charId name=name enabled=enabled}}
|
{{> effectsEditList parentId=_id parentCollection="Features" charId=charId name=name enabled=enabled}}
|
||||||
{{> proficiencyEditList parentId=_id parentCollection="Features" charId=charId enabled=enabled}}
|
{{> proficiencyEditList parentId=_id parentCollection="Features" charId=charId enabled=enabled}}
|
||||||
|
{{> attackEditList parentId=_id parentCollection="Features" charId=charId enabled=enabled name=name}}
|
||||||
|
{{> customBuffEditList parentId=_id parentCollection="Features" charId=charId}}
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -47,6 +47,10 @@ Template.featureDetails.events({
|
|||||||
});
|
});
|
||||||
|
|
||||||
Template.featureEdit.helpers({
|
Template.featureEdit.helpers({
|
||||||
|
showNewUserExperience: function(){
|
||||||
|
return Session.get("newUserExperienceStep") === 0 ||
|
||||||
|
Session.get("newUserExperienceStep") === 1;
|
||||||
|
},
|
||||||
usesSet: function(){
|
usesSet: function(){
|
||||||
return _.isString(this.uses);
|
return _.isString(this.uses);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6,6 +6,10 @@
|
|||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card.featureCard .bottom {
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.containerMain.featureDescription {
|
.containerMain.featureDescription {
|
||||||
white-space: pre-line;
|
white-space: pre-line;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,13 +74,14 @@
|
|||||||
checked={{enabled}}
|
checked={{enabled}}
|
||||||
disabled={{#unless canEditCharacter charId}}true{{/unless}}>
|
disabled={{#unless canEditCharacter charId}}true{{/unless}}>
|
||||||
</paper-checkbox>
|
</paper-checkbox>
|
||||||
<paper-tooltip position="left">Feature enabled</paper-tooltip>
|
{{#simpleTooltip}}Feature enabled{{/simpleTooltip}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
{{#if description}}
|
{{#if hasCharacters (evaluateShortString charId description)}}
|
||||||
<div class="bottom flex">
|
<div class="bottom flex">
|
||||||
{{#markdown}}{{evaluateShortString charId description}}{{/markdown}}
|
{{#markdown}}{{evaluateShortString charId description}}{{/markdown}}
|
||||||
|
{{> customBuffViewList charId=charId parentId=_id}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if hasUses}}
|
{{#if hasUses}}
|
||||||
@@ -100,11 +101,13 @@
|
|||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
{{#if canEditCharacter _id}}
|
{{#if canEditCharacter _id}}
|
||||||
|
<div class="floatyButton">
|
||||||
<paper-fab id="addFeature"
|
<paper-fab id="addFeature"
|
||||||
class="floatyButton"
|
class="{{#if shouldFloatyButtonBounce}}bounce{{/if}}"
|
||||||
icon="add">
|
icon="add">
|
||||||
<paper-tooltip position="left">Add Feature</paper-tooltip>
|
|
||||||
</paper-fab>
|
</paper-fab>
|
||||||
|
{{#simpleTooltip}}Add Feature{{/simpleTooltip}}
|
||||||
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -56,6 +56,13 @@ Template.features.helpers({
|
|||||||
var profs = Proficiencies.find({charId: this._id, type: "tool"});
|
var profs = Proficiencies.find({charId: this._id, type: "tool"});
|
||||||
return removeDuplicateProficiencies(profs);
|
return removeDuplicateProficiencies(profs);
|
||||||
},
|
},
|
||||||
|
hasCharacters: function(string){
|
||||||
|
return string && string.match(/\S/);
|
||||||
|
},
|
||||||
|
shouldFloatyButtonBounce: function(){
|
||||||
|
const step = Session.get("newUserExperienceStep");
|
||||||
|
return step === 0 && Features.find({charId: this._id}).count() <= 1;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Template.features.events({
|
Template.features.events({
|
||||||
|
|||||||
@@ -17,6 +17,9 @@
|
|||||||
</paper-input>
|
</paper-input>
|
||||||
<paper-input id="valueInput" label="Value" type="number" value={{value}}>
|
<paper-input id="valueInput" label="Value" type="number" value={{value}}>
|
||||||
</paper-input>
|
</paper-input>
|
||||||
|
<paper-toggle-button id="carriedToggle" checked={{isCarried}}>
|
||||||
|
Carried
|
||||||
|
</paper-toggle-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr class="vertMargin">
|
<hr class="vertMargin">
|
||||||
|
|||||||
@@ -54,4 +54,8 @@ Template.containerEdit.events({
|
|||||||
trimStrings: false,
|
trimStrings: false,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
"change #carriedToggle": function(event, instance){
|
||||||
|
var carried = !this.isCarried;
|
||||||
|
Containers.update(this._id, {$set: {isCarried: carried}});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -28,16 +28,16 @@
|
|||||||
<div class="bottom green" style="padding: 0;">
|
<div class="bottom green" style="padding: 0;">
|
||||||
{{> carryCapacityBar}}
|
{{> carryCapacityBar}}
|
||||||
</div>
|
</div>
|
||||||
{{#if encumberedBuffs.count}}
|
{{#if encumberedConditions.count}}
|
||||||
<div class="bottom list">
|
<div class="bottom list">
|
||||||
{{#each encumberedBuffs}}
|
{{#each condition in encumberedConditions}}
|
||||||
<div class="item-slot">
|
<div class="item-slot">
|
||||||
<div class="item buff layout horizontal center">
|
<div class="item condition layout horizontal center">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<iron-icon icon="work"
|
<iron-icon icon="work"
|
||||||
style="margin-right: 16px">
|
style="margin-right: 16px">
|
||||||
</iron-icon>
|
</iron-icon>
|
||||||
{{name}}
|
{{condition.name}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -110,12 +110,12 @@
|
|||||||
<div class="paper-font-caption" style="margin-right: 8px">
|
<div class="paper-font-caption" style="margin-right: 8px">
|
||||||
{{round totalWeight}} lbs
|
{{round totalWeight}} lbs
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div style="position: relative;">
|
||||||
<paper-checkbox class="carriedCheckbox"
|
<paper-checkbox class="carriedCheckbox"
|
||||||
disabled={{#unless canEditCharacter charId}}true{{/unless}}
|
disabled={{#unless canEditCharacter charId}}true{{/unless}}
|
||||||
checked={{isCarried}}>
|
checked={{isCarried}}>
|
||||||
</paper-checkbox>
|
</paper-checkbox>
|
||||||
<paper-tooltip position="left"> Container carried</paper-tooltip>
|
{{#simpleTooltip}} Container carried{{/simpleTooltip}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="bottom list">
|
<div class="bottom list">
|
||||||
@@ -136,21 +136,21 @@
|
|||||||
class="addContainer"
|
class="addContainer"
|
||||||
mini>
|
mini>
|
||||||
</paper-fab>
|
</paper-fab>
|
||||||
<paper-tooltip position="left"> New container </paper-tooltip>
|
{{#simpleTooltip class="always"}} Container {{/simpleTooltip}}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<paper-fab icon="av:library-books"
|
<paper-fab icon="av:library-books"
|
||||||
class="libraryItem"
|
class="libraryItem"
|
||||||
mini>
|
mini>
|
||||||
</paper-fab>
|
</paper-fab>
|
||||||
<paper-tooltip position="left"> Library item </paper-tooltip>
|
{{#simpleTooltip class="always"}} Item from library {{/simpleTooltip}}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<paper-fab icon="note-add"
|
<paper-fab icon="note-add"
|
||||||
class="addItem"
|
class="addItem"
|
||||||
mini>
|
mini>
|
||||||
</paper-fab>
|
</paper-fab>
|
||||||
<paper-tooltip position="left"> New item </paper-tooltip>
|
{{#simpleTooltip class="always"}} Item {{/simpleTooltip}}
|
||||||
</div>
|
</div>
|
||||||
{{/fabMenu}}
|
{{/fabMenu}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|||||||
@@ -68,9 +68,8 @@ Template.inventory.helpers({
|
|||||||
return weight;
|
return weight;
|
||||||
},
|
},
|
||||||
encumberedBuffs: function(){
|
encumberedBuffs: function(){
|
||||||
return Buffs.find({
|
return Conditions.find({
|
||||||
charId: this._id,
|
charId: this._id,
|
||||||
type: "inate",
|
|
||||||
name: {$in: [
|
name: {$in: [
|
||||||
"Encumbered",
|
"Encumbered",
|
||||||
"Heavily encumbered",
|
"Heavily encumbered",
|
||||||
@@ -157,7 +156,7 @@ Template.inventory.events({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Make the library item into a regular item
|
// Make the library item into a regular item
|
||||||
let item = _.omit(result, "library", "attacks", "effects");
|
let item = _.omit(result, "libraryName", "library", "attacks", "effects");
|
||||||
delete item.settings.category;
|
delete item.settings.category;
|
||||||
// Update the item to match library item
|
// Update the item to match library item
|
||||||
Items.update(itemId, {$set: item});
|
Items.update(itemId, {$set: item});
|
||||||
@@ -201,12 +200,10 @@ Template.inventory.events({
|
|||||||
element: event.currentTarget.parentElement,
|
element: event.currentTarget.parentElement,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
"click .buff": function(event, instance){
|
"click .condition": function(event, instance){
|
||||||
var buffId = this._id;
|
|
||||||
var charId = Template.parentData()._id;
|
|
||||||
pushDialogStack({
|
pushDialogStack({
|
||||||
template: "buffDialog",
|
template: "conditionViewDialogDialog",
|
||||||
data: {buffId: buffId, charId: charId},
|
data: {condition: this.condition},
|
||||||
element: event.currentTarget,
|
element: event.currentTarget,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -334,6 +331,7 @@ Template.layout.events({
|
|||||||
Session.set("inventory.dragItemId", null);
|
Session.set("inventory.dragItemId", null);
|
||||||
},
|
},
|
||||||
"drop .characterRepresentative": function(event, instance) {
|
"drop .characterRepresentative": function(event, instance) {
|
||||||
|
if (_.contains(event.originalEvent.dataTransfer.types, "dicecloud-id/items")){
|
||||||
var itemId = event.originalEvent.dataTransfer.getData("dicecloud-id/items");
|
var itemId = event.originalEvent.dataTransfer.getData("dicecloud-id/items");
|
||||||
if (event.ctrlKey){
|
if (event.ctrlKey){
|
||||||
//split the stack to the container
|
//split the stack to the container
|
||||||
@@ -350,5 +348,6 @@ Template.layout.events({
|
|||||||
Meteor.call("moveItemToCharacter", itemId, this._id);
|
Meteor.call("moveItemToCharacter", itemId, this._id);
|
||||||
}
|
}
|
||||||
Session.set("inventory.dragItemId", null);
|
Session.set("inventory.dragItemId", null);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
{{/if}}
|
{{/if}}
|
||||||
{{> effectsViewList charId=charId parentId=_id}}
|
{{> effectsViewList charId=charId parentId=_id}}
|
||||||
{{> attacksViewList charId=charId parentId=_id}}
|
{{> attacksViewList charId=charId parentId=_id}}
|
||||||
|
{{> customBuffViewList charId=charId parentId=_id}}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template name="itemEdit">
|
<template name="itemEdit">
|
||||||
@@ -65,10 +66,13 @@
|
|||||||
<paper-textarea id="itemDescriptionInput" label="Description" value={{description}}></paper-textarea>
|
<paper-textarea id="itemDescriptionInput" label="Description" value={{description}}></paper-textarea>
|
||||||
{{> textareaBracketSuffix}}
|
{{> textareaBracketSuffix}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!--Effects-->
|
<!--Effects-->
|
||||||
{{> effectsEditList parentId=_id parentCollection="Items" charId=charId enabled=equipped name=name}}
|
{{> effectsEditList parentId=_id parentCollection="Items" charId=charId enabled=equipped name=name}}
|
||||||
<!--Attacks-->
|
<!--Attacks-->
|
||||||
{{> attackEditList parentId=_id parentCollection="Items" charId=charId enabled=equipped name=name}}
|
{{> attackEditList parentId=_id parentCollection="Items" charId=charId enabled=equipped name=name}}
|
||||||
|
<!-- Buffs -->
|
||||||
|
{{> customBuffEditList parentId=_id parentCollection="Items" charId=charId}}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template name="containerDropdown">
|
<template name="containerDropdown">
|
||||||
|
|||||||
@@ -61,15 +61,15 @@
|
|||||||
<template name="libraryItem">
|
<template name="libraryItem">
|
||||||
<tr class="item library-item {{#if selected}}selected{{/if}}">
|
<tr class="item library-item {{#if selected}}selected{{/if}}">
|
||||||
<td class="itemName">
|
<td class="itemName">
|
||||||
{{item.name}}
|
{{itemName}}
|
||||||
<paper-ripple></paper-ripple>
|
<paper-ripple></paper-ripple>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{item.weight}} lb.
|
{{itemWeight}} lb.
|
||||||
<paper-ripple></paper-ripple>
|
<paper-ripple></paper-ripple>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{valueString item.value}}
|
{{valueString itemValue}}
|
||||||
<paper-ripple></paper-ripple>
|
<paper-ripple></paper-ripple>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -107,3 +107,24 @@ Template.itemLibraryDialog.events({
|
|||||||
template.searchTerm.set(value);
|
template.searchTerm.set(value);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Template.libraryItem.helpers({
|
||||||
|
itemName: function(){
|
||||||
|
return this.item.libraryName || this.item.name;
|
||||||
|
},
|
||||||
|
itemWeight: function(){
|
||||||
|
if (this.item.quantity) {
|
||||||
|
return this.item.weight * this.item.quantity;
|
||||||
|
} else {
|
||||||
|
return this.item.weight;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
itemValue: function(){
|
||||||
|
if (this.item.quantity) {
|
||||||
|
return this.item.value * this.item.quantity;
|
||||||
|
} else {
|
||||||
|
return this.item.value;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
});
|
||||||
|
|||||||
@@ -53,7 +53,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="bottom list">
|
<div class="bottom list">
|
||||||
<div class="item-slot">
|
<div class="item-slot">
|
||||||
<div class="item race layout horizontal center">
|
<div class="item race layout horizontal center {{#if shouldRaceBounce}}bounce{{/if}}">
|
||||||
{{race}}
|
{{race}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -83,9 +83,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="fab-buffer"></div>
|
<div class="fab-buffer"></div>
|
||||||
{{#if canEditCharacter _id}}
|
{{#if canEditCharacter _id}}
|
||||||
|
<div class="floatyButton">
|
||||||
<paper-fab id="addNote"
|
<paper-fab id="addNote"
|
||||||
class="floatyButton"
|
|
||||||
icon="add"
|
icon="add"
|
||||||
title="Add"></paper-fab>
|
title="Add">
|
||||||
|
</paper-fab>
|
||||||
|
{{#simpleTooltip}}Add Note{{/simpleTooltip}}
|
||||||
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -50,6 +50,9 @@ Template.journal.helpers({
|
|||||||
var char = Characters.findOne(this._id, {fields: {race: 1}});
|
var char = Characters.findOne(this._id, {fields: {race: 1}});
|
||||||
return char && char.race;
|
return char && char.race;
|
||||||
},
|
},
|
||||||
|
shouldRaceBounce: function(){
|
||||||
|
return Session.get("newUserExperienceStep") === 1;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Template.journal.events({
|
Template.journal.events({
|
||||||
|
|||||||
@@ -1,11 +1,34 @@
|
|||||||
<template name="raceDialog">
|
<template name="raceDialog">
|
||||||
{{#baseDialog title="Race" class=color hideColor="true" hideDelete="true" startEditing=startEditing}}
|
{{#baseDialog title="Race" class=color hideColor="true" hideDelete="true" startEditing=startEditing}}
|
||||||
|
{{#if showNewUserExperience}}
|
||||||
|
{{#infoBox}}
|
||||||
|
{{#if stepComplete}}
|
||||||
|
<p>You can add all the effects you need to represent how your race affects your character's attributes.</p>
|
||||||
|
{{else}}
|
||||||
|
<p>Click the edit button to edit your race and add a racial effect</p>
|
||||||
|
{{/if}}
|
||||||
|
{{/infoBox}}
|
||||||
|
{{/if}}
|
||||||
<div class="horizontal layout center-justified paper-font-display2">
|
<div class="horizontal layout center-justified paper-font-display2">
|
||||||
{{race}}
|
{{race}}
|
||||||
</div>
|
</div>
|
||||||
{{> effectsViewList charId=charId parentId=charId parentGroup="racial"}}
|
{{> effectsViewList charId=charId parentId=charId parentGroup="racial"}}
|
||||||
{{> proficiencyViewList charId=charId parentId=charId parentGroup="racial"}}
|
{{> proficiencyViewList charId=charId parentId=charId parentGroup="racial"}}
|
||||||
{{else}}
|
{{else}}
|
||||||
|
{{#if showNewUserExperience}}
|
||||||
|
{{#infoBox}}
|
||||||
|
{{#if stepComplete}}
|
||||||
|
<p>You can add all the effects you need to represent how your race affects your character's attributes.</p>
|
||||||
|
{{else}}
|
||||||
|
<p>
|
||||||
|
Add an effect with the following options: <br>
|
||||||
|
Attribute: <b>stats > speed</b> <br>
|
||||||
|
Operation: <b>Base Value</b> <br>
|
||||||
|
Value: <b>30</b> (might be different for some races)
|
||||||
|
</p>
|
||||||
|
{{/if}}
|
||||||
|
{{/infoBox}}
|
||||||
|
{{/if}}
|
||||||
<paper-input id="raceInput" label="Race" value={{race}}></paper-input>
|
<paper-input id="raceInput" label="Race" value={{race}}></paper-input>
|
||||||
{{> effectsEditList parentId=charId parentCollection="Characters" charId=charId parentGroup="racial"}}
|
{{> effectsEditList parentId=charId parentCollection="Characters" charId=charId parentGroup="racial"}}
|
||||||
{{> proficiencyEditList parentId=charId parentCollection="Characters" charId=charId parentGroup="racial"}}
|
{{> proficiencyEditList parentId=charId parentCollection="Characters" charId=charId parentGroup="racial"}}
|
||||||
|
|||||||
@@ -19,4 +19,10 @@ Template.raceDialog.helpers({
|
|||||||
var char = Characters.findOne(this.charId, {fields: {color: 1}});
|
var char = Characters.findOne(this.charId, {fields: {color: 1}});
|
||||||
if (char) return getColorClass(char.color);
|
if (char) return getColorClass(char.color);
|
||||||
},
|
},
|
||||||
|
stepComplete: function(){
|
||||||
|
return Session.get("newUserExperienceStep") > 1;
|
||||||
|
},
|
||||||
|
showNewUserExperience: function(){
|
||||||
|
return Session.get("newUserExperienceStep") >= 1;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
.newUserStepper {
|
||||||
|
height: 180px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.newUserStepper paper-step .invalid-step-message {
|
||||||
|
color: #d13b2e;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.newUserStepper paper-step[invalid] .invalid-step-message {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<template name="newUserStepper">
|
||||||
|
<paper-stepper linear selected="0" class="newUserStepper">
|
||||||
|
<paper-step id="step0" label="Add a feature">
|
||||||
|
<p>
|
||||||
|
To get started, add a feature
|
||||||
|
</p>
|
||||||
|
</paper-step>
|
||||||
|
<paper-step id="step1" label="Add an effect">
|
||||||
|
<p>
|
||||||
|
Add a racial effect to set your speed
|
||||||
|
</p>
|
||||||
|
</paper-step>
|
||||||
|
<paper-step id="step2" label="See the effect in action">
|
||||||
|
<p>
|
||||||
|
View your speed stat
|
||||||
|
</p>
|
||||||
|
</paper-step>
|
||||||
|
<paper-step id="step3" label="Finish">
|
||||||
|
Done! If you get stuck, be sure to check out the <a href="/guide">guide</a>, or ask for help using the feedback form
|
||||||
|
<div class="layout vertical end">
|
||||||
|
<paper-button class="done-button" style="color: #d13b2e">Finish</paper-button>
|
||||||
|
</div>
|
||||||
|
</paper-step>
|
||||||
|
</paper-stepper>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template name="newUserStepperPlaceholder">
|
||||||
|
<div style="height: 300px"></div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
Template.newUserStepper.onRendered(function(){
|
||||||
|
Session.set("newUserExperienceStep", 0);
|
||||||
|
let stepper = this.find("paper-stepper");
|
||||||
|
_.defer(() => {
|
||||||
|
this.autorun((c) => {
|
||||||
|
var step = Session.get("newUserExperienceStep");
|
||||||
|
var hasFeatures = Features.find({charId: this.data._id}).count() > 1;
|
||||||
|
if (step === 0 && hasFeatures){
|
||||||
|
stepper.continue();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.autorun((c) => {
|
||||||
|
var step = Session.get("newUserExperienceStep");
|
||||||
|
var hasEffect = !!Effects.find({
|
||||||
|
charId: this.data._id,
|
||||||
|
stat: "speed",
|
||||||
|
"parent.group": "racial",
|
||||||
|
operation: "base",
|
||||||
|
value: {$gt: 0},
|
||||||
|
}).count();
|
||||||
|
if (step === 1 && hasEffect){
|
||||||
|
stepper.continue();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.autorun((c) => {
|
||||||
|
var step = Session.get("newUserExperienceStep");
|
||||||
|
if (step === 2 && Session.get("viewedSpeed")){
|
||||||
|
Session.set("viewedSpeed", undefined);
|
||||||
|
stepper.continue();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.newUserStepper.events({
|
||||||
|
"paper-stepper-progressed paper-stepper": function(event, template){
|
||||||
|
const step = template.find("paper-stepper").selected;
|
||||||
|
Session.set("newUserExperienceStep", step);
|
||||||
|
},
|
||||||
|
"paper-stepper-completed paper-stepper": function(event, template){
|
||||||
|
Session.set("newUserExperienceStep", undefined);
|
||||||
|
Session.set("showNewUserExperience", undefined);
|
||||||
|
Characters.update(this._id, {$unset: {"settings.newUserExperience": 1}});
|
||||||
|
},
|
||||||
|
"click .done-button": function(event, instance){
|
||||||
|
const stepper = instance.find("paper-stepper");
|
||||||
|
stepper.continue();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.stats.events({
|
||||||
|
"click .stat-card": function(event, instance){
|
||||||
|
var step = Session.get("newUserExperienceStep");
|
||||||
|
if (this.stat === "speed" && step === 2){
|
||||||
|
Session.set("viewedSpeed", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
<template name="spellDetails">
|
<template name="spellDetails">
|
||||||
<div class="paper-font-body2">
|
<div class="paper-font-body2">
|
||||||
Level {{level}} {{school}} {{#if ritual}}ritual{{/if}}, {{preparedString}}
|
{{schoolAndLevel}}{{#if ritual}} (ritual){{/if}}, {{preparedString}}
|
||||||
</div>
|
</div>
|
||||||
<div style="margin: 16px 0 16px 0;">
|
<div style="margin: 16px 0 16px 0;">
|
||||||
{{#if castingTime}}
|
{{#if castingTime}}
|
||||||
@@ -38,6 +38,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>{{#markdown}}{{evaluateSpellString charId parent.id description}}{{/markdown}}</div>
|
<div>{{#markdown}}{{evaluateSpellString charId parent.id description}}{{/markdown}}</div>
|
||||||
{{> attacksViewList charId=charId parentId=_id}}
|
{{> attacksViewList charId=charId parentId=_id}}
|
||||||
|
{{> customBuffViewList charId=charId parentId=_id}}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template name="spellEdit">
|
<template name="spellEdit">
|
||||||
@@ -111,10 +112,12 @@
|
|||||||
</paper-checkbox>
|
</paper-checkbox>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!--Description-->
|
||||||
<div class="description-input layout horizontal end">
|
<div class="description-input layout horizontal end">
|
||||||
<paper-textarea id="descriptionInput" label="Description" style="width: calc(100% - 24px)" value={{description}}></paper-textarea>
|
<paper-textarea id="descriptionInput" label="Description" style="width: calc(100% - 24px)" value={{description}}></paper-textarea>
|
||||||
{{> textareaBracketSuffix}}
|
{{> textareaBracketSuffix}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{> customBuffEditList parentId=_id parentCollection="Spells" charId=charId}}
|
||||||
{{> attackEditList parentId=_id parentCollection="Spells" charId=charId enabled=true name=name isSpell=true}}
|
{{> attackEditList parentId=_id parentCollection="Spells" charId=charId enabled=true name=name isSpell=true}}
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -29,6 +29,13 @@ Template.spellDialog.events({
|
|||||||
});
|
});
|
||||||
|
|
||||||
Template.spellDetails.helpers({
|
Template.spellDetails.helpers({
|
||||||
|
schoolAndLevel: function(){
|
||||||
|
if (this.level == 0) {
|
||||||
|
return this.school + " cantrip";
|
||||||
|
} else {
|
||||||
|
return "Level " + this.level + " " + this.school;
|
||||||
|
}
|
||||||
|
},
|
||||||
getComponents: function(){
|
getComponents: function(){
|
||||||
var components = "";
|
var components = "";
|
||||||
if (this.components.concentration) components += "C";
|
if (this.components.concentration) components += "C";
|
||||||
|
|||||||
@@ -53,22 +53,22 @@
|
|||||||
{{numPrepared}} / {{evaluate charId maxPrepared}}
|
{{numPrepared}} / {{evaluate charId maxPrepared}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<div>
|
<div style="position: relative;">
|
||||||
<paper-tooltip position="left">
|
|
||||||
Done
|
|
||||||
</paper-tooltip>
|
|
||||||
<paper-icon-button class="finishPrep" icon="done">
|
<paper-icon-button class="finishPrep" icon="done">
|
||||||
</paper-icon-button>
|
</paper-icon-button>
|
||||||
|
{{#simpleTooltip}}
|
||||||
|
Done
|
||||||
|
{{/simpleTooltip}}
|
||||||
</div>
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
<div>
|
<div style="position: relative;">
|
||||||
<paper-tooltip position="left">
|
|
||||||
Change prepared spells
|
|
||||||
</paper-tooltip>
|
|
||||||
<paper-icon-button class="prepSpells"
|
<paper-icon-button class="prepSpells"
|
||||||
disabled={{#unless canEditCharacter charId}}true{{/unless}}
|
disabled={{#unless canEditCharacter charId}}true{{/unless}}
|
||||||
icon="book">
|
icon="book">
|
||||||
</paper-icon-button>
|
</paper-icon-button>
|
||||||
|
{{#simpleTooltip}}
|
||||||
|
Change prepared spells
|
||||||
|
{{/simpleTooltip}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
@@ -83,7 +83,9 @@
|
|||||||
{{#each spells ../_id ../../_id}}
|
{{#each spells ../_id ../../_id}}
|
||||||
{{#if showSpell ../../_id}}
|
{{#if showSpell ../../_id}}
|
||||||
<div class="item-slot">
|
<div class="item-slot">
|
||||||
<div class="tall spell item layout horizontal center" data-id={{_id}}>
|
<div class="tall spell item layout horizontal center spellItem"
|
||||||
|
data-id={{_id}}
|
||||||
|
draggable={{canEditCharacter charId}}>
|
||||||
<iron-icon icon="social:whatshot"
|
<iron-icon icon="social:whatshot"
|
||||||
style="color: {{hexColor color}};
|
style="color: {{hexColor color}};
|
||||||
margin-right: 16px;"
|
margin-right: 16px;"
|
||||||
@@ -122,32 +124,31 @@
|
|||||||
{{#if canEditCharacter _id}}
|
{{#if canEditCharacter _id}}
|
||||||
{{#fabMenu}}
|
{{#fabMenu}}
|
||||||
<div>
|
<div>
|
||||||
<paper-tooltip position="left">
|
|
||||||
New spell list
|
|
||||||
</paper-tooltip>
|
|
||||||
<paper-fab icon="work"
|
<paper-fab icon="work"
|
||||||
class="addSpellList"
|
class="addSpellList"
|
||||||
mini>
|
mini>
|
||||||
</paper-fab>
|
</paper-fab>
|
||||||
|
{{#simpleTooltip class="always"}}
|
||||||
|
Spell list
|
||||||
|
{{/simpleTooltip}}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<paper-tooltip position="left">
|
|
||||||
Spell library
|
|
||||||
</paper-tooltip>
|
|
||||||
<paper-fab icon="av:library-books"
|
<paper-fab icon="av:library-books"
|
||||||
class="librarySpell"
|
class="librarySpell"
|
||||||
mini>
|
mini>
|
||||||
</paper-fab>
|
</paper-fab>
|
||||||
|
{{#simpleTooltip class="always"}}
|
||||||
|
Spell from library
|
||||||
|
{{/simpleTooltip}}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<paper-tooltip position="left">
|
|
||||||
New spell
|
|
||||||
</paper-tooltip>
|
|
||||||
<paper-fab icon="note-add"
|
<paper-fab icon="note-add"
|
||||||
class="addSpell"
|
class="addSpell"
|
||||||
mini>
|
mini>
|
||||||
</paper-fab>
|
</paper-fab>
|
||||||
|
{{#simpleTooltip class="always"}}
|
||||||
|
Spell
|
||||||
|
{{/simpleTooltip}}
|
||||||
</div>
|
</div>
|
||||||
{{/fabMenu}}
|
{{/fabMenu}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|||||||
@@ -253,8 +253,8 @@ Template.spells.events({
|
|||||||
pushDialogStack({
|
pushDialogStack({
|
||||||
template: "spellLibraryDialog",
|
template: "spellLibraryDialog",
|
||||||
element: event.currentTarget,
|
element: event.currentTarget,
|
||||||
callback: (result) => {
|
callback: (resultArray) => {
|
||||||
if (!result) return;
|
if (!resultArray) return;
|
||||||
if (!listId){
|
if (!listId){
|
||||||
listId = SpellLists.insert({
|
listId = SpellLists.insert({
|
||||||
name: "New SpellList",
|
name: "New SpellList",
|
||||||
@@ -263,33 +263,39 @@ Template.spells.events({
|
|||||||
attackBonus: "intelligenceMod + proficiencyBonus",
|
attackBonus: "intelligenceMod + proficiencyBonus",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//loop through all returned spells
|
||||||
|
_.each(resultArray, (rawSpell, index) =>{
|
||||||
// Make the library spell into a regular spell
|
// Make the library spell into a regular spell
|
||||||
let spell = _.omit(result, "library", "attacks", "effects");
|
let spell = _.omit(rawSpell, "_id", "library", "attacks", "effects");
|
||||||
|
// Use the ID generated earlier for the first spell so we
|
||||||
|
// can animate to it
|
||||||
|
if (index == 0) {
|
||||||
spell._id = spellId;
|
spell._id = spellId;
|
||||||
|
}
|
||||||
spell.charId = charId;
|
spell.charId = charId;
|
||||||
spell.parent = {
|
spell.parent = {
|
||||||
id: listId,
|
id: listId,
|
||||||
collection: "SpellLists",
|
collection: "SpellLists",
|
||||||
};
|
};
|
||||||
spell.prepared = "prepared";
|
spell.prepared = "prepared";
|
||||||
Spells.insert(spell);
|
let insertedSpellId = Spells.insert(spell);
|
||||||
// Copy over attacks and effects
|
// Copy over attacks and effects
|
||||||
_.each(result.attacks, (attack) => {
|
_.each(rawSpell.attacks, (attack) => {
|
||||||
if (!("attackBonus" in attack)) {attack.attackBonus = "attackBonus"} //if no attack bonus provided, use spell list's
|
if (!("attackBonus" in attack)) {attack.attackBonus = "attackBonus"} //if no attack bonus provided, use spell list's
|
||||||
attack.charId = charId;
|
attack.charId = charId;
|
||||||
attack.parent = {id: spellId, collection: "Spells"};
|
attack.parent = {id: insertedSpellId, collection: "Spells"};
|
||||||
Attacks.insert(attack);
|
Attacks.insert(attack);
|
||||||
});
|
});
|
||||||
_.each(result.effects, (effect) => {
|
_.each(rawSpell.effects, (effect) => {
|
||||||
effect.charId = charId;
|
effect.charId = charId;
|
||||||
effect.parent = {id: spellId, collection: "Spells"};
|
effect.parent = {id: insertedSpellId, collection: "Spells"};
|
||||||
Effects.insert(effect);
|
Effects.insert(effect);
|
||||||
});
|
});
|
||||||
|
|
||||||
/******[UNCOMMENT ONCE BUFFS ARE ADDED]*******
|
_.each(rawSpell.buffs, (buff) => {
|
||||||
_.each(result.buffs, (buff) => {
|
|
||||||
buff.charId = charId;
|
buff.charId = charId;
|
||||||
buff.parent = {id: spellId, collection: "Spells"};
|
buff.parent = {id: insertedSpellId, collection: "Spells"};
|
||||||
buffId = Buffs.insert(buff);
|
buffId = Buffs.insert(buff);
|
||||||
|
|
||||||
_.each(buff.attacks, (attack) => {
|
_.each(buff.attacks, (attack) => {
|
||||||
@@ -309,7 +315,7 @@ Template.spells.events({
|
|||||||
Proficiencies.insert(prof);
|
Proficiencies.insert(prof);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
*******[UNCOMMENT ONCE BUFFS ARE ADDED]******/
|
});
|
||||||
},
|
},
|
||||||
returnElement: () => $(`[data-id='${spellId}']`).get(0),
|
returnElement: () => $(`[data-id='${spellId}']`).get(0),
|
||||||
})
|
})
|
||||||
@@ -333,3 +339,49 @@ Template.spells.events({
|
|||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Template.layout.events({
|
||||||
|
"dragstart .spellItem": function(event, instance){
|
||||||
|
event.originalEvent.dataTransfer.setData("dicecloud-id/spells", this._id);
|
||||||
|
Session.set("spellLists.dragSpellId", this._id);
|
||||||
|
},
|
||||||
|
"dragend .spellItem": function(event, instance){
|
||||||
|
Session.set("spellLists.dragSpellId", null);
|
||||||
|
},
|
||||||
|
|
||||||
|
"dragover .spellList, dragenter .spellList": function(event, instance){
|
||||||
|
if (_.contains(event.originalEvent.dataTransfer.types, "dicecloud-id/spells")){
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"drop .spellList": function(event, instance){
|
||||||
|
var spellId = event.originalEvent.dataTransfer.getData("dicecloud-id/spells");
|
||||||
|
if (event.ctrlKey){
|
||||||
|
//copy spell to new list
|
||||||
|
Meteor.call("copySpellToList", spellId, this._id);
|
||||||
|
} else {
|
||||||
|
//move spell to new list
|
||||||
|
Meteor.call("moveSpellToList", spellId, this._id);
|
||||||
|
}
|
||||||
|
Session.set("spellLists.dragSpellId", null);
|
||||||
|
},
|
||||||
|
|
||||||
|
"dragover .characterRepresentative, dragenter .characterRepresentative": function(event, instance){
|
||||||
|
if (_.contains(event.originalEvent.dataTransfer.types, "dicecloud-id/spells")){
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"drop .characterRepresentative": function(event, instance) {
|
||||||
|
if (_.contains(event.originalEvent.dataTransfer.types, "dicecloud-id/spells")){ //to prevent conflicts with item drag/drop
|
||||||
|
var spellId = event.originalEvent.dataTransfer.getData("dicecloud-id/spells");
|
||||||
|
if (event.ctrlKey){
|
||||||
|
//copy spell to character
|
||||||
|
Meteor.call("copySpellToCharacter", spellId, this._id);
|
||||||
|
} else {
|
||||||
|
//move spell to character
|
||||||
|
Meteor.call("moveSpellToCharacter", spellId, this._id);
|
||||||
|
}
|
||||||
|
Session.set("spellLists.dragSpellId", null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
.spell-library-dialog .spell.selected {
|
|
||||||
background-color: #e4e4e4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spell-library-dialog .category-header {
|
.spell-library-dialog .category-header {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
@@ -13,11 +9,3 @@
|
|||||||
.spell-library-dialog .category-header iron-icon.open {
|
.spell-library-dialog .category-header iron-icon.open {
|
||||||
transform: rotate(90deg);
|
transform: rotate(90deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.spell-library-dialog table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spell-library-dialog .library-spell td {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -13,15 +13,18 @@
|
|||||||
<div class="spells" style="padding:8px">
|
<div class="spells" style="padding:8px">
|
||||||
{{#if searchTerm}}
|
{{#if searchTerm}}
|
||||||
{{#if searchSpells.count}}
|
{{#if searchSpells.count}}
|
||||||
<table style="width: 100%">
|
<div>
|
||||||
<tbody>
|
|
||||||
{{#each spell in searchSpells}}
|
{{#each spell in searchSpells}}
|
||||||
{{>librarySpell spell=spell selected=(isSelected spell)}}
|
{{>librarySpell spell=spell selected=(isSelected spell)}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</tbody>
|
</div>
|
||||||
</table>
|
|
||||||
{{else}}{{#if searchReady}}
|
{{else}}{{#if searchReady}}
|
||||||
|
<p>
|
||||||
No spells match "{{searchTerm}}"
|
No spells match "{{searchTerm}}"
|
||||||
|
</p>
|
||||||
|
<p class="paper-font-caption">
|
||||||
|
DiceCloud only includes content provided by Wizards of the Coast in the official system reference document. If the spell you are looking for is not available in the system reference document, you will need to add it manually.
|
||||||
|
</p>
|
||||||
{{/if}}{{/if}}
|
{{/if}}{{/if}}
|
||||||
{{#unless searchReady}}
|
{{#unless searchReady}}
|
||||||
<div class="layout vertical center" style="width: 100%; padding: 16px;">
|
<div class="layout vertical center" style="width: 100%; padding: 16px;">
|
||||||
@@ -36,13 +39,11 @@
|
|||||||
{{name}}
|
{{name}}
|
||||||
</div>
|
</div>
|
||||||
<iron-collapse opened={{isOpen key}}>
|
<iron-collapse opened={{isOpen key}}>
|
||||||
<table style="width: 100%">
|
<div>
|
||||||
<tbody>
|
|
||||||
{{#each spell in (spellsInCategory key)}}
|
{{#each spell in (spellsInCategory key)}}
|
||||||
{{>librarySpell spell=spell selected=(isSelected spell)}}
|
{{>librarySpell spell=spell selected=(isSelected spell)}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</tbody>
|
</div>
|
||||||
</table>
|
|
||||||
{{#unless ready key}}
|
{{#unless ready key}}
|
||||||
<paper-spinner active></paper-spinner>
|
<paper-spinner active></paper-spinner>
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
@@ -59,10 +60,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template name="librarySpell">
|
<template name="librarySpell">
|
||||||
<tr class="spell library-spell {{#if selected}}selected{{/if}}">
|
<div style="margin: ">
|
||||||
<td class="spellName">
|
<paper-checkbox class="spell library-spell" checked={{selected}} style="padding: 2px 0 2px 16px; width: 100%;">
|
||||||
{{spell.name}}
|
{{spell.name}}
|
||||||
<paper-ripple></paper-ripple>
|
<paper-ripple></paper-ripple>
|
||||||
</td>
|
</paper-checkbox>
|
||||||
</tr>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const categories = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
Template.spellLibraryDialog.onCreated(function(){
|
Template.spellLibraryDialog.onCreated(function(){
|
||||||
this.selectedSpell = new ReactiveVar();
|
this.selectedSpells = new ReactiveVar([]); //this holds an array of the selected spells by ID
|
||||||
this.searchTerm = new ReactiveVar();
|
this.searchTerm = new ReactiveVar();
|
||||||
this.categoriesOpen = new ReactiveVar([]);
|
this.categoriesOpen = new ReactiveVar([]);
|
||||||
this.readyDict = new ReactiveDict();
|
this.readyDict = new ReactiveDict();
|
||||||
@@ -59,8 +59,12 @@ Template.spellLibraryDialog.helpers({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
isSelected(spell){
|
isSelected(spell){
|
||||||
const selected = Template.instance().selectedSpell.get();
|
const selected = Template.instance().selectedSpells.get();
|
||||||
return selected && selected._id === spell._id;
|
return _.contains(selected, spell._id);
|
||||||
|
},
|
||||||
|
selectedCount(){
|
||||||
|
const selected = Template.instance().selectedSpells.get();
|
||||||
|
return selected && selected.length;
|
||||||
},
|
},
|
||||||
isOpen(key){
|
isOpen(key){
|
||||||
const cats = Template.instance().categoriesOpen.get();
|
const cats = Template.instance().categoriesOpen.get();
|
||||||
@@ -89,10 +93,26 @@ Template.spellLibraryDialog.events({
|
|||||||
popDialogStack();
|
popDialogStack();
|
||||||
},
|
},
|
||||||
"click .okButton": function(event, template){
|
"click .okButton": function(event, template){
|
||||||
popDialogStack(template.selectedSpell.get());
|
const selectedIds = template.selectedSpells.get();
|
||||||
|
var returnSpells = [];
|
||||||
|
_.each(selectedIds, (id) => {
|
||||||
|
let spell = LibrarySpells.findOne(id);
|
||||||
|
if (spell) {
|
||||||
|
returnSpells.push(spell)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
popDialogStack(returnSpells);
|
||||||
},
|
},
|
||||||
"click .library-spell": function(event, template){
|
"click .library-spell": function(event, template){
|
||||||
template.selectedSpell.set(this.spell);
|
let selected = template.selectedSpells.get();
|
||||||
|
const spellId = this.spell._id;
|
||||||
|
// Toggle whether this spellId is in the array or not
|
||||||
|
if (_.contains(selected, spellId)){
|
||||||
|
selected = _.without(selected, spellId);
|
||||||
|
} else {
|
||||||
|
selected.push(spellId);
|
||||||
|
}
|
||||||
|
template.selectedSpells.set(selected);
|
||||||
},
|
},
|
||||||
"click #backButton": function(event, template){
|
"click #backButton": function(event, template){
|
||||||
popDialogStack();
|
popDialogStack();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- data just needs charId -->
|
<!-- data just needs charId -->
|
||||||
<template name="addTHPDialog">
|
<template name="addEHPDialog">
|
||||||
<div class="fit layout vertical">
|
<div class="fit layout vertical">
|
||||||
<app-header-layout has-scrolling-region class="new-character-dialog flex">
|
<app-header-layout has-scrolling-region class="new-character-dialog flex">
|
||||||
<app-header fixed effects="waterfall">
|
<app-header fixed effects="waterfall">
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
Template.addTHPDialog.onRendered(function(){
|
Template.addEHPDialog.onRendered(function(){
|
||||||
this.find("#quantityInput").focus();
|
this.find("#quantityInput").focus();
|
||||||
});
|
});
|
||||||
|
|
||||||
Template.addTHPDialog.events({
|
Template.addEHPDialog.events({
|
||||||
"tap .addButton": function(event, instance){
|
"tap .addButton": function(event, instance){
|
||||||
popDialogStack();
|
popDialogStack();
|
||||||
var max = +instance.find("#quantityInput").value;
|
var max = +instance.find("#quantityInput").value;
|
||||||
@@ -6,6 +6,16 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template name="attributeDialogView">
|
<template name="attributeDialogView">
|
||||||
|
{{#if showNewUserExperience}}
|
||||||
|
{{#infoBox}}
|
||||||
|
<p>
|
||||||
|
This dialog shows how your speed is set by the effect you added to your character's race.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
In DiceCloud you don't change stats directly, rather you add effects which impact your stats in different ways. This way, you can always tell where your stats came from, and how they got to their current value.
|
||||||
|
</p>
|
||||||
|
{{/infoBox}}
|
||||||
|
{{/if}}
|
||||||
<div class="layout horizontal center-justified end">
|
<div class="layout horizontal center-justified end">
|
||||||
<div class="paper-font-display2">
|
<div class="paper-font-display2">
|
||||||
{{attributeValue}}
|
{{attributeValue}}
|
||||||
|
|||||||
@@ -157,4 +157,9 @@ Template.attributeDialogView.helpers({
|
|||||||
statValue: function(){
|
statValue: function(){
|
||||||
return evaluateEffect(this.charId, this);
|
return evaluateEffect(this.charId, this);
|
||||||
},
|
},
|
||||||
|
showNewUserExperience: function(){
|
||||||
|
if (this.statName === "speed"){
|
||||||
|
return Session.get("newUserExperienceStep") >= 2;
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,10 +3,15 @@
|
|||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.healthCard .bottom-border {
|
||||||
|
border-bottom: rgba(0,0,0,0.24) solid 1px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.healthCard #stableButton {
|
.healthCard #stableButton {
|
||||||
color: #b71c1c;
|
color: #b71c1c;
|
||||||
transition: color 0.4s ease;
|
transition: color 0.4s ease;
|
||||||
width: 100%
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.healthCard #stableButton:before {
|
.healthCard #stableButton:before {
|
||||||
|
|||||||
@@ -5,33 +5,52 @@
|
|||||||
Hit Points
|
Hit Points
|
||||||
</div>
|
</div>
|
||||||
<paper-icon-button class="white54"
|
<paper-icon-button class="white54"
|
||||||
id="addTempHP"
|
id="addExtraHP"
|
||||||
icon="add"
|
icon="add"
|
||||||
disabled={{#unless canEditCharacter _id}}true{{/unless}}>
|
disabled={{#unless canEditCharacter _id}}true{{/unless}}>
|
||||||
</paper-icon-button>
|
</paper-icon-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="right flex layout vertical center-justified" style="min-width: 180px;">
|
<div class="right flex layout vertical center-justified" style="min-width: 180px;">
|
||||||
|
<!-- main HP slider -->
|
||||||
<div class="layout horizontal">
|
<div class="layout horizontal">
|
||||||
<paper-diff-slider id="hitPointSlider"
|
<paper-diff-slider id="hitPointSlider"
|
||||||
editable pin
|
editable pin
|
||||||
disabled={{#unless canEditCharacter _id}}true{{/unless}}>
|
disabled={{#unless canEditCharacter _id}}true{{/unless}}>
|
||||||
</paper-diff-slider>
|
</paper-diff-slider>
|
||||||
</div>
|
</div>
|
||||||
{{#each tempHitPoints}}
|
{{#if characterCalculate "attributeBase" _id "tempHP"}}
|
||||||
<div>
|
<!-- main THP slider -->
|
||||||
|
<div class="layout horizontal center {{#if extraHitPoints.count}}bottom-border{{/if}}">
|
||||||
|
<div class="self-center">
|
||||||
|
Temporary Hit Points
|
||||||
|
</div>
|
||||||
|
<paper-diff-slider id="temporaryHitPointSlider"
|
||||||
|
class="flex"
|
||||||
|
editable pin
|
||||||
|
disabled={{#unless canEditCharacter _id}}true{{/unless}}
|
||||||
|
max={{characterCalculate "attributeBase" _id "tempHP"}}
|
||||||
|
value={{characterCalculate "attributeValue" _id "tempHP"}}
|
||||||
|
>
|
||||||
|
</paper-diff-slider>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#each extraHitPoints}}
|
||||||
|
<div class="layout horizontal center">
|
||||||
|
<div class="self-center">
|
||||||
{{name}}
|
{{name}}
|
||||||
</div>
|
</div>
|
||||||
<div class="layout horizontal center">
|
|
||||||
<div style="height: 40px; width: 40px;">
|
<div style="height: 40px; width: 40px;">
|
||||||
{{#unless left}}
|
{{#unless left}}
|
||||||
<paper-icon-button class="deleteTHP" icon="delete"></paper-icon-button>
|
<paper-icon-button class="deleteEHP" icon="delete"></paper-icon-button>
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
</div>
|
</div>
|
||||||
<paper-diff-slider class="tempHitPointSlider flex"
|
<paper-diff-slider class="extraHitPointSlider flex"
|
||||||
max={{maximum}}
|
max={{maximum}}
|
||||||
value={{left}}
|
value={{left}}
|
||||||
editable pin
|
editable pin
|
||||||
></paper-diff-slider>
|
>
|
||||||
|
</paper-diff-slider>
|
||||||
</div>
|
</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
<div class="paper-font-caption">
|
<div class="paper-font-caption">
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Template.healthCard.binding({
|
|||||||
"#hitPointSlider": {
|
"#hitPointSlider": {
|
||||||
max: () => Characters.calculate.attributeBase(currentId() , "hitPoints"),
|
max: () => Characters.calculate.attributeBase(currentId() , "hitPoints"),
|
||||||
value: () => Characters.calculate.attributeValue(currentId() , "hitPoints"),
|
value: () => Characters.calculate.attributeValue(currentId() , "hitPoints"),
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Reset the old value between characters so that we don't get red health lost
|
// Reset the old value between characters so that we don't get red health lost
|
||||||
@@ -16,13 +16,15 @@ Template.healthCard.onRendered(function(){
|
|||||||
const id = Template.currentData()._id;
|
const id = Template.currentData()._id;
|
||||||
if (oldId !== id){
|
if (oldId !== id){
|
||||||
this.find("#hitPointSlider").resetOldValue();
|
this.find("#hitPointSlider").resetOldValue();
|
||||||
|
var thpSlider = this.find("#temporaryHitPointSlider");
|
||||||
|
thpSlider && thpSlider.resetOldValue();
|
||||||
oldId = id;
|
oldId = id;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Template.healthCard.helpers({
|
Template.healthCard.helpers({
|
||||||
tempHitPoints: function(){
|
extraHitPoints: function(){
|
||||||
return TemporaryHitPoints.find({charId: this._id});
|
return TemporaryHitPoints.find({charId: this._id});
|
||||||
},
|
},
|
||||||
showDeathSave: function(){
|
showDeathSave: function(){
|
||||||
@@ -93,17 +95,23 @@ Template.healthCard.events({
|
|||||||
}}
|
}}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
"change .tempHitPointSlider": function(event){
|
"change #temporaryHitPointSlider": function(event){ //this is the actual THP stat
|
||||||
|
var value = event.currentTarget.value;
|
||||||
|
var base = Characters.calculate.attributeBase(this._id, "tempHP");
|
||||||
|
var adjustment = value - base;
|
||||||
|
Characters.update(this._id, {$set: {"tempHP.adjustment": adjustment}});
|
||||||
|
},
|
||||||
|
"change .extraHitPointSlider": function(event){ //this is the extra bars
|
||||||
var value = event.currentTarget.value;
|
var value = event.currentTarget.value;
|
||||||
var used = this.maximum - value;
|
var used = this.maximum - value;
|
||||||
TemporaryHitPoints.update(this._id, {$set: {"used": used}});
|
TemporaryHitPoints.update(this._id, {$set: {"used": used}});
|
||||||
},
|
},
|
||||||
"click .deleteTHP": function(event){
|
"click .deleteEHP": function(event){
|
||||||
TemporaryHitPoints.remove(this._id);
|
TemporaryHitPoints.remove(this._id);
|
||||||
},
|
},
|
||||||
"click #addTempHP": function(event){
|
"click #addExtraHP": function(event){
|
||||||
pushDialogStack({
|
pushDialogStack({
|
||||||
template: "addTHPDialog",
|
template: "addEHPDialog",
|
||||||
data: {charId: this._id},
|
data: {charId: this._id},
|
||||||
element: event.currentTarget.parentElement,
|
element: event.currentTarget.parentElement,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template name="statCard">
|
<template name="statCard">
|
||||||
<div>
|
<div>
|
||||||
<paper-material class="stat-card layout horizontal">
|
<paper-material class="stat-card layout horizontal {{#if bounce}}bounce{{/if}}">
|
||||||
<div class="numbers paper-font-display1">
|
<div class="numbers paper-font-display1">
|
||||||
{{#if isSkill}}
|
{{#if isSkill}}
|
||||||
{{prefix}}{{skillMod}}
|
{{prefix}}{{skillMod}}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<!--Armor-->
|
<!--Armor-->
|
||||||
{{> statCard stat="armor" name="Armor Class" color="teal"}}
|
{{> statCard stat="armor" name="Armor Class" color="teal"}}
|
||||||
<!--Speed-->
|
<!--Speed-->
|
||||||
{{> statCard stat="speed" name="Speed" color="teal"}}
|
{{> statCard stat="speed" name="Speed" color="teal" bounce=shouldSpeedBounce}}
|
||||||
<!--Initiative-->
|
<!--Initiative-->
|
||||||
{{> statCard stat="initiative" name="Initiative" color="indigo" isSkill="true"}}
|
{{> statCard stat="initiative" name="Initiative" color="indigo" isSkill="true"}}
|
||||||
<!--Proficiency Bonus-->
|
<!--Proficiency Bonus-->
|
||||||
@@ -42,6 +42,34 @@
|
|||||||
</div>
|
</div>
|
||||||
</paper-material>
|
</paper-material>
|
||||||
</div>
|
</div>
|
||||||
|
<!--Condtions-->
|
||||||
|
<div>
|
||||||
|
<paper-material class="card">
|
||||||
|
<div class="top white subhead layout horizontal center">
|
||||||
|
<div class="flex">Conditions</div>
|
||||||
|
{{#if canEditCharacter _id}}
|
||||||
|
<paper-icon-button class="black54" id="addCondition" icon="add"></paper-icon-button>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
<div flex class="bottom list">
|
||||||
|
<div class="conditionsList">
|
||||||
|
{{#each condition in conditions}}
|
||||||
|
{{>conditionView condition=condition}}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
{{#if buffs.count}}
|
||||||
|
<div class="layout horizontal">
|
||||||
|
<div class="paper-font-subhead flex">Buffs</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
<div class="buffsList">
|
||||||
|
{{#each buff in buffs}}
|
||||||
|
{{>buffListItem buff=buff}}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</paper-material>
|
||||||
|
</div>
|
||||||
<!--Skills-->
|
<!--Skills-->
|
||||||
<div>
|
<div>
|
||||||
<paper-material class="card">
|
<paper-material class="card">
|
||||||
|
|||||||
@@ -1,3 +1,19 @@
|
|||||||
|
Template.stats.helpers({
|
||||||
|
conditions: function() {
|
||||||
|
return Conditions.find({charId: this._id});
|
||||||
|
},
|
||||||
|
buffs: function() {
|
||||||
|
var selector = {
|
||||||
|
"charId": this._id,
|
||||||
|
};
|
||||||
|
return Buffs.find(selector);
|
||||||
|
},
|
||||||
|
// New user experience
|
||||||
|
shouldSpeedBounce: function(){
|
||||||
|
return Session.get("newUserExperienceStep") === 2;
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
Template.stats.events({
|
Template.stats.events({
|
||||||
"click .stat-card": function(event, instance){
|
"click .stat-card": function(event, instance){
|
||||||
var charId = instance.data._id;
|
var charId = instance.data._id;
|
||||||
@@ -65,4 +81,16 @@ Template.stats.events({
|
|||||||
element: event.currentTarget.parentElement.parentElement,
|
element: event.currentTarget.parentElement.parentElement,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
"click #addCondition": function(event, template){
|
||||||
|
pushDialogStack({
|
||||||
|
template: "conditionLibraryDialog",
|
||||||
|
element: event.currentTarget,
|
||||||
|
callback: (result) => {
|
||||||
|
if (!result) {
|
||||||
|
return;
|
||||||
|
} else Meteor.call("giveCondition", this._id, result)
|
||||||
|
},
|
||||||
|
//returnElement: () => $(`[data-id='${itemId}']`).get(0),
|
||||||
|
})
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -49,14 +49,14 @@
|
|||||||
class="addParty"
|
class="addParty"
|
||||||
mini>
|
mini>
|
||||||
</paper-fab>
|
</paper-fab>
|
||||||
<paper-tooltip position="left"> New Party </paper-tooltip>
|
{{#simpleTooltip class="always"}} New Party {{/simpleTooltip}}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<paper-fab icon="face"
|
<paper-fab icon="face"
|
||||||
class="addCharacter"
|
class="addCharacter"
|
||||||
mini>
|
mini>
|
||||||
</paper-fab>
|
</paper-fab>
|
||||||
<paper-tooltip position="left"> New Character </paper-tooltip>
|
{{#simpleTooltip class="always"}} New Character {{/simpleTooltip}}
|
||||||
</div>
|
</div>
|
||||||
{{/fabMenu}}
|
{{/fabMenu}}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
.characterPicker .character-name {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.characterPicker .partyHead {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.characterPicker .partyHead iron-icon {
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.characterPicker .partyHead iron-icon.open {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<template name="characterPicker">
|
||||||
|
<dicecloud-selector class="characterPicker" selected={{selected}} selectable="paper-item" style="height: 100%; overflow-y: auto;">
|
||||||
|
{{#if selfId}}{{#if includeSelf}}
|
||||||
|
<paper-item class="short clickable" name={{selfId}}>
|
||||||
|
<div class="character-name">
|
||||||
|
Self
|
||||||
|
</div>
|
||||||
|
</paper-item>
|
||||||
|
{{/if}}{{/if}}
|
||||||
|
{{#each charactersWithNoParty}}
|
||||||
|
<paper-item class="short clickable" name={{_id}}>
|
||||||
|
<div class="character-name">
|
||||||
|
{{name}}
|
||||||
|
</div>
|
||||||
|
</paper-item>
|
||||||
|
{{/each}}
|
||||||
|
{{#each parties}}
|
||||||
|
<div class="paper-font-subhead partyHead clickable">
|
||||||
|
<iron-icon icon="chevron-right" class="{{#if isOpen _id}}open{{/if}}">
|
||||||
|
</iron-icon>
|
||||||
|
{{name}}
|
||||||
|
</div>
|
||||||
|
<iron-collapse opened={{isOpen _id}}>
|
||||||
|
{{#each charactersInParty}}
|
||||||
|
<paper-item class="short clickable" name={{_id}}>
|
||||||
|
<div class="character-name">
|
||||||
|
{{name}}
|
||||||
|
</div>
|
||||||
|
</paper-item>
|
||||||
|
{{/each}}
|
||||||
|
</iron-collapse>
|
||||||
|
{{/each}}
|
||||||
|
</dicecloud-selector>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
Template.characterPicker.onCreated(function() {
|
||||||
|
this.subscribe("characterList");
|
||||||
|
this.openedParties = new ReactiveVar(new Set());
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.characterPicker.helpers({
|
||||||
|
parties() {
|
||||||
|
return Parties.find(
|
||||||
|
{owner: Meteor.userId()},
|
||||||
|
{sort: {name: 1}},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
charactersInParty() {
|
||||||
|
var userId = Meteor.userId();
|
||||||
|
var selector = {
|
||||||
|
_id: {$in: this.characters, $ne: this.selfId},
|
||||||
|
$or: [{readers: userId}, {writers: userId}, {owner: userId}],
|
||||||
|
};
|
||||||
|
if (this.writableOnly) {
|
||||||
|
selector.$or = [{writers: userId}, {owner: userId}];
|
||||||
|
}
|
||||||
|
return Characters.find(selector,{sort: {name: 1}});
|
||||||
|
},
|
||||||
|
charactersWithNoParty() {
|
||||||
|
var userId = Meteor.userId();
|
||||||
|
var charArrays = Parties.find({owner: userId}).map(p => p.characters);
|
||||||
|
var partyChars = _.uniq(_.flatten(charArrays));
|
||||||
|
var selector = {
|
||||||
|
_id: {$nin: partyChars, $ne: this.selfId},
|
||||||
|
$or: [{readers: userId}, {writers: userId}, {owner: userId}],
|
||||||
|
};
|
||||||
|
if (this.writableOnly) {
|
||||||
|
selector.$or = [{writers: userId}, {owner: userId}];
|
||||||
|
}
|
||||||
|
return Characters.find(selector, {sort: {name: 1}});
|
||||||
|
},
|
||||||
|
isOpen(id) {
|
||||||
|
var openedParties = Template.instance().openedParties.get();
|
||||||
|
return openedParties.has(id);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.characterPicker.events({
|
||||||
|
"click .partyHead": function(event, instance){
|
||||||
|
var openedParties = instance.openedParties.get();
|
||||||
|
if (openedParties.has(this._id)){
|
||||||
|
openedParties.delete(this._id);
|
||||||
|
} else {
|
||||||
|
openedParties.add(this._id);
|
||||||
|
}
|
||||||
|
instance.openedParties.set(openedParties);
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -34,7 +34,6 @@ Template.characterSideList.helpers({
|
|||||||
},
|
},
|
||||||
isOpen(id) {
|
isOpen(id) {
|
||||||
var openedParties = Template.instance().openedParties.get();
|
var openedParties = Template.instance().openedParties.get();
|
||||||
console.log(openedParties);
|
|
||||||
return openedParties.has(id);
|
return openedParties.has(id);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -85,7 +85,7 @@
|
|||||||
<div>
|
<div>
|
||||||
On the official subreddit
|
On the official subreddit
|
||||||
</div>
|
</div>
|
||||||
<a href="http://www.reddit.com/r/dicecloud/">
|
<a href="http://www.reddit.com/r/dicecloud/" target="_blank">
|
||||||
<paper-button class="redditButton">
|
<paper-button class="redditButton">
|
||||||
/r/dicecloud
|
/r/dicecloud
|
||||||
</paper-button>
|
</paper-button>
|
||||||
@@ -93,14 +93,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="layout vertical center">
|
<div class="layout vertical center">
|
||||||
<div class="paper-font-headline">
|
<div class="paper-font-headline">
|
||||||
Get involved
|
Open Source
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
Shape upcoming features and track bugs on the DiceCloud Trello board
|
Shape upcoming features, track bugs, and contribute to the DiceCloud codebase
|
||||||
</div>
|
</div>
|
||||||
<a href="https://trello.com/b/94M0SCnq/dicecloud-roadmap">
|
<a href="https://github.com/ThaumRystra/DiceCloud1/" target="_blank">
|
||||||
<paper-button class="trelloButton">
|
<paper-button class="githubButton">
|
||||||
Trello Roadmap
|
GitHub Repo
|
||||||
</paper-button>
|
</paper-button>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
.base-dialog h1,
|
||||||
|
.base-dialog h2,
|
||||||
|
.base-dialog h3,
|
||||||
|
.base-dialog h4,
|
||||||
|
.base-dialog h5,
|
||||||
|
.base-dialog h6{
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.base-dialog p{
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
@@ -16,9 +16,11 @@
|
|||||||
{{#unless hideColor}}
|
{{#unless hideColor}}
|
||||||
{{> colorDropdown}}
|
{{> colorDropdown}}
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
|
{{#unless editOnly}}
|
||||||
<paper-icon-button id="doneEditingButton"
|
<paper-icon-button id="doneEditingButton"
|
||||||
icon="done">
|
icon="done">
|
||||||
</paper-icon-button>
|
</paper-icon-button>
|
||||||
|
{{/unless}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{#if showEdit}}
|
{{#if showEdit}}
|
||||||
<paper-icon-button id="editButton"
|
<paper-icon-button id="editButton"
|
||||||
|
|||||||
15
rpg-docs/client/views/paperTemplates/infoBox/infoBox.css
Normal file
15
rpg-docs/client/views/paperTemplates/infoBox/infoBox.css
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
.infoBox iron-icon {
|
||||||
|
color: #747474;
|
||||||
|
color: rgba(0,0,0,0.54);
|
||||||
|
height: 32px;
|
||||||
|
width: 32px;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.infoBox > div > p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.infoBox > div > p + p {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
10
rpg-docs/client/views/paperTemplates/infoBox/infoBox.html
Normal file
10
rpg-docs/client/views/paperTemplates/infoBox/infoBox.html
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<template name="infoBox">
|
||||||
|
<div class="layout horizontal center infoBox">
|
||||||
|
<div>
|
||||||
|
<iron-icon icon="info-outline"></iron-icon>
|
||||||
|
</div>
|
||||||
|
<div class="flex">
|
||||||
|
{{> Template.contentBlock}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -25,7 +25,6 @@
|
|||||||
{{/ simpleTooltip}}
|
{{/ simpleTooltip}}
|
||||||
</div>
|
</div>
|
||||||
<div class="brackets" style="position: relative">
|
<div class="brackets" style="position: relative">
|
||||||
<!--<paper-tooltip position="left" animation-delay="0">This field accepts formulae in {curly brackets}</paper-tooltip>-->
|
|
||||||
<iron-icon icon="dicecloud:code-braces"></iron-icon>
|
<iron-icon icon="dicecloud:code-braces"></iron-icon>
|
||||||
{{# simpleTooltip}}
|
{{# simpleTooltip}}
|
||||||
This field accepts formulae in {curly brackets}
|
This field accepts formulae in {curly brackets}
|
||||||
|
|||||||
@@ -1,4 +1,18 @@
|
|||||||
.simple-tooltip:hover .tooltip {
|
.simple-tooltip {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-tooltip:active {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Show the tooltip if a older sibling is hovered */
|
||||||
|
*:hover ~ .simple-tooltip > .tooltip {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Show the tooltip if parent is hovered */
|
||||||
|
*:hover > .simple-tooltip > .tooltip {
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -16,3 +30,7 @@
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tooltip.always {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template name="simpleTooltip">
|
<template name="simpleTooltip">
|
||||||
<div class="simple-tooltip fit">
|
<div class="simple-tooltip fit layout vertical center-justified">
|
||||||
<div class="tooltip">
|
<div class="tooltip {{class}}">
|
||||||
{{> Template.contentBlock}}
|
{{> Template.contentBlock}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
.profile #at-nav-button {
|
.profile paper-button, .profile a, .profile #at-nav-button {
|
||||||
color: #212121;
|
color: #d13b2e;
|
||||||
color: rgba(0,0,0,0.87);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
{{#if verified}}
|
{{#if verified}}
|
||||||
<span>
|
<span>
|
||||||
<iron-icon icon="check"></iron-icon>
|
<iron-icon icon="check"></iron-icon>
|
||||||
<paper-tooltip>Verified</paper-tooltip>
|
{{#simpleTooltip}}Verified{{/simpleTooltip}}
|
||||||
</span>
|
</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
@@ -35,9 +35,36 @@
|
|||||||
</td>
|
</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">
|
||||||
|
<a href="/change-password">
|
||||||
|
<paper-button>Change password</paper-button>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
API Key
|
||||||
|
</td>
|
||||||
|
<td class="apiKey">
|
||||||
|
{{#if apiKey}}
|
||||||
|
{{#unless showApiKey}}
|
||||||
|
<paper-button class="showApiKey">
|
||||||
|
Show
|
||||||
|
</paper-button>
|
||||||
|
{{else}}
|
||||||
|
{{apiKey}}
|
||||||
|
{{/unless}}
|
||||||
|
{{else}}
|
||||||
|
<paper-button class="generateMyApiKey">
|
||||||
|
Generate
|
||||||
|
</paper-button>
|
||||||
|
{{/if}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<div style="max-width: 250px">
|
<div style="max-width: 250px">
|
||||||
{{> atForm}}
|
{{> atForm state="signIn"}}
|
||||||
</div>
|
</div>
|
||||||
{{> atNavButton }}
|
{{> atNavButton }}
|
||||||
</paper-material>
|
</paper-material>
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
|
Template.profile.onCreated(function(){
|
||||||
|
this.showApiKey = new ReactiveVar(false);
|
||||||
|
});
|
||||||
|
|
||||||
Template.profile.helpers({
|
Template.profile.helpers({
|
||||||
profileName: function() {
|
profileName: function() {
|
||||||
var user = Meteor.user();
|
var user = Meteor.user();
|
||||||
return user.profile && user.profile.username ||
|
return user.profile && user.profile.username ||
|
||||||
user.username ||
|
user.username ||
|
||||||
"Tap to set username";
|
"Tap to set username";
|
||||||
}
|
},
|
||||||
|
showApiKey: function(){
|
||||||
|
return Template.instance().showApiKey.get();
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Template.profile.events({
|
Template.profile.events({
|
||||||
@@ -25,4 +32,11 @@ Template.profile.events({
|
|||||||
data: {},
|
data: {},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
"click .showApiKey": function(event, instance){
|
||||||
|
instance.showApiKey.set(!instance.showApiKey.get());
|
||||||
|
},
|
||||||
|
"click .generateMyApiKey": function(event, instance){
|
||||||
|
Meteor.call("generateMyApiKey");
|
||||||
|
instance.showApiKey.set(true);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<app-header-layout has-scrolling-region class="feedback flex">
|
<app-header-layout has-scrolling-region class="feedback flex">
|
||||||
<app-header fixed effects="waterfall">
|
<app-header fixed effects="waterfall">
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<div main-title>Feedback</div>
|
<div main-title>Change Username</div>
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
</app-header>
|
</app-header>
|
||||||
<div class="form flex">
|
<div class="form flex">
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user