Compare commits

...

203 Commits
0.5.0 ... 1.0.0

Author SHA1 Message Date
Stefan Zermatten
eefb3895ad Bumped version to 1.0.0 2017-03-06 11:37:15 +02:00
Stefan Zermatten
5b1caa7e95 Merge pull request #57 from ThaumRystra/update-meteor-1.4-polymer-1.0
Update to polymer 1.0, Meteor 1.4
2017-03-06 10:36:03 +02:00
Stefan Zermatten
21fdad1054 Added resolution for webcomponentsjs 2017-02-21 09:37:32 +02:00
Stefan Zermatten
c482395426 Fixed FABs not reappearing after dialog close 2017-02-13 09:22:17 +02:00
Stefan Zermatten
cb493a9f16 Firefox now works... mostly 2017-02-13 09:21:55 +02:00
Stefan Zermatten
576c1f953b Firefox now works... barely 2017-02-10 14:01:06 +02:00
Stefan Zermatten
eb4336b3e9 Moved back to differential:vulcanize for imports 2017-01-31 15:26:05 +02:00
Stefan Zermatten
03cb32bf34 Updated color dropdown to Polymer 1 paper-swatch-picker 2017-01-31 13:36:15 +02:00
Stefan Zermatten
a869772238 Updated journal page to Polymer 1 2017-01-31 11:09:57 +02:00
Stefan Zermatten
9c61493a12 Updated persona tab to Polymer 1 2017-01-31 09:58:35 +02:00
Stefan Zermatten
7af3b8e06e Upgraded spells page to Polymer 1 2017-01-30 13:02:22 +02:00
Stefan Zermatten
fd21f4f7d7 Fixed some dialog animation edge cases 2017-01-27 15:34:59 +02:00
Stefan Zermatten
3530eefb2a Added custom tab pages animations 2017-01-27 15:34:09 +02:00
Stefan Zermatten
d710579025 Updated inventory to Polymer 1 2017-01-26 10:23:27 +02:00
Stefan Zermatten
f7a3929c05 Fixed toast 2017-01-25 08:40:30 +02:00
Stefan Zermatten
1af15eff3c Small style and event fixes 2017-01-23 15:08:19 +02:00
Stefan Zermatten
33ca60c2e6 Migrated the Feature page to Polymer 1 2017-01-20 15:45:18 +02:00
Stefan Zermatten
4261776d8c Removed SCSS, using CSS only now
SCSS wasn't being utilized enough to add much value to the build times
2017-01-20 09:06:24 +02:00
Stefan Zermatten
b44fe33b30 Finished upgrading stat tab to Polymer 1 2017-01-20 09:05:16 +02:00
Stefan Zermatten
c4a488a176 Updated useraccounts, character settings to Polymer 1 2017-01-19 15:43:48 +02:00
Stefan Zermatten
137a94f251 Began moving character sheet and related menus to Polymer 1 2017-01-18 15:04:09 +02:00
Stefan Zermatten
817020bea8 Updated hitpoint / temporary hitpoint box to Polymer 1 2017-01-18 11:19:03 +02:00
Stefan Zermatten
420de9b005 Migrated stats tab to Polymer 1.0 2017-01-17 15:01:11 +02:00
Stefan Zermatten
b8fdc27df9 Rebuilt dialog animations with cross-fade effect 2017-01-17 15:00:05 +02:00
Stefan Zermatten
bdc64dfb10 Updated the new character dialog to Polymer 1 2017-01-16 15:34:23 +02:00
Stefan Zermatten
d4bec4f5e7 Cleaned up dialogStack 2017-01-16 15:34:03 +02:00
Stefan Zermatten
041186059c Updated static pages to Polymer 1 2017-01-16 13:48:43 +02:00
Stefan Zermatten
01535a414c Updated character lists to Polymer 1 2017-01-16 13:48:18 +02:00
Stefan Zermatten
d9e180bac0 Simplified toast to just take text 2017-01-13 15:35:08 +02:00
Stefan Zermatten
235089e790 Fixed typos of the app name 2017-01-13 15:34:51 +02:00
Stefan Zermatten
c560d80c26 Updated front page to Polymer 1 2017-01-13 15:34:28 +02:00
Stefan Zermatten
a67b13f659 Updated feedback form to Polymer 1 2017-01-13 13:21:42 +02:00
Stefan Zermatten
c416adc85b Improved the dialog stack animations 2017-01-13 13:21:05 +02:00
Stefan Zermatten
dbbb3739d0 Added a new animated dialog stack
This is to replace the animated-pages hack with Blaze component built for purpose
2017-01-12 15:30:06 +02:00
Stefan Zermatten
38ea89995a Moved views out of private folder 2017-01-12 15:28:59 +02:00
Stefan Zermatten
37268495ae Got some Polymer 1.0 elements working with 1.4
Character sheets now visible, but vulcanize broke, using raw head imports instead
2016-12-22 11:15:30 +02:00
Stefan Zermatten
042b67dd77 Fixed merge conflict 2016-12-20 13:58:32 +02:00
Stefan Zermatten
07cb3859db Merge branch 'update-meteor-1.4' into update-meteor-1.4-polymer-1.0 2016-12-20 13:57:56 +02:00
Stefan Zermatten
b1fdaa726c Removed useless Kadira 2016-12-08 13:52:04 +02:00
Stefan Zermatten
a333b0bdc8 Fixed broken drawer swiping 2016-12-08 13:51:54 +02:00
Stefan Zermatten
c32680a15a Upgraded meteor 2016-12-08 13:51:35 +02:00
Stefan Zermatten
d1950b4598 Bumped changelog for webcomponentsjs update 2016-12-08 09:55:07 +02:00
Greg Williams
af20eb4f3c Updated webcomponentsjs to v0.7.23 2016-11-23 22:03:09 -08:00
Stefan Zermatten
be0283a342 Chrome 54 broke transitions again 2016-10-17 14:51:24 +02:00
Stefan Zermatten
e05561dcf9 Trying a different version of webcomponents 2016-10-03 14:27:57 +02:00
Stefan Zermatten
3c776ed018 added webcomponent dependency resolution 2016-10-03 14:19:48 +02:00
Stefan Zermatten
ebecb46935 Fixed drop-down bug, for now
Expect it to break again in Chrome 54
2016-10-03 14:05:30 +02:00
Stefan Zermatten
7af2e80ec1 Removed Polymer 0.5, started implementing Polymer 1.0 2016-10-03 13:00:27 +02:00
Stefan Zermatten
d83fe917d0 Fixed change logs version numbers 2016-09-19 10:02:25 +02:00
Stefan Zermatten
f526de88a7 Fixed invisible dropdown boxes in Chrome 52. Bumped version 2016-09-08 14:40:47 +02:00
Stefan Zermatten
55adca36d5 Added bower 2016-09-07 10:04:21 +02:00
Stefan Zermatten
be6f54e53a Added Package.json to get Scalingo working 2016-09-07 09:52:14 +02:00
Stefan Zermatten
d41c27c86d Bumped version 2016-06-03 14:49:04 +02:00
Stefan Zermatten
f4397e65ab Global update of packages 2016-06-03 14:43:11 +02:00
Stefan Zermatten
c00c69a0c7 Merge pull request #54 from ThaumRystra/fix-bug-156
closes #54
2016-05-20 09:42:25 +02:00
Robert Perce
6737983782 [#156] add "ritual" after the school in the spell detail view if the spell is, in fact, a ritual. 2016-05-13 11:40:39 -05:00
Stefan Zermatten
78441439c3 Merge pull request #52 from ThaumRystra/user-story-127
Added 'if' function for math.js to use whenever eval-ing input, closes #32
2016-05-04 07:43:21 +02:00
Robert Perce
9b72e1aea2 Added 'if' function for math.js to use whenever eval-ing input 2016-05-03 14:44:40 -05:00
Stefan Zermatten
684eca8603 Merge pull request #51 from ThaumRystra/fix-bug-163
[#163] Changed the x to a × in effectView.js
2016-05-03 11:13:32 +02:00
Stefan Zermatten
868e5f96a4 Update README.md 2016-05-03 09:03:26 +02:00
Stefan Zermatten
6fe368cde7 Updated bourbon and scss 2016-05-03 08:12:44 +02:00
Stefan Zermatten
2ffacb88e0 Included custom vulcanize package in repo 2016-05-03 07:50:12 +02:00
Stefan Zermatten
af3af0e550 Fixed missing bower dependency 2016-05-03 07:48:14 +02:00
Robert Perce
81f50d94c1 [#163] Changed the x to a × in effectView.js 2016-04-22 15:20:32 -05:00
Stefan Zermatten
49e6d51b95 Merge pull request #40 from ThaumRystra/feature-coins-inc-default
Make coins incrementable by default, closes #27
2016-03-09 14:17:23 +02:00
Stefan Zermatten
1e4e1a3b11 Merge pull request #38 from ThaumRystra/feature-base-ability-scores-10
Base ability scores default to 10, closes #36
2016-03-09 13:56:36 +02:00
Stefan Zermatten
4440c88417 Removed grouping from effects
Grouping is currently only used to differentiate effects that have the same parent, but are ambiguous as to which part of that parent they come from, like race or background. Features do not need grouped effects, because there can be no ambiguity.
2016-03-09 12:57:23 +02:00
Connor Petersen
98047ca806 Moved base-10 default to Features.js, cleaned up default Effects, and Features code to be consistent. 2016-03-07 23:21:42 -08:00
Connor Petersen
051cabc712 Cleaned up a little more 2016-03-07 22:53:27 -08:00
Connor Petersen
636fa504f1 Updated to not require caching the id or making a separate update call 2016-03-07 22:51:41 -08:00
Connor Petersen
ff9fc916f6 Added a call to adding a feature and 6 related effects to the newCharacterDialog event handler 2016-02-11 23:29:27 -08:00
Connor Petersen
2206a607b2 Added an update call to default Item adding function to update the showIncrement field to true for coins 2016-02-10 22:11:41 -08:00
Stefan Zermatten
ce224301b2 Fixed layout on chrome 49+, made character names into links 2016-02-01 09:34:22 +02:00
Stefan Zermatten
e6a9911dfc Replaced the head that was accidentally deleted 2015-11-19 12:44:11 +02:00
Stefan Zermatten
8a1871ee18 Fixed character list FAB not staying put 2015-11-19 12:43:54 +02:00
Stefan Zermatten
402f885f85 Specified thaum:vulcanize at 0.5 2015-11-18 08:52:52 +02:00
Stefan Zermatten
d07c118d47 Removed useless public items, removed Appcache, fixed vulcanize 2015-11-17 15:20:33 +02:00
Stefan Zermatten
103d44eeec Merge branch 'feature-fast-render' 2015-11-17 13:59:22 +02:00
Stefan Zermatten
33196c6771 Bumped change log 2015-11-17 13:58:50 +02:00
Stefan Zermatten
80dc862047 Added Appcache 2015-11-17 12:26:55 +02:00
Stefan Zermatten
314da14ad1 Added indexes to charId on character fields 2015-11-02 09:35:05 +02:00
Stefan Zermatten
e5dbe81ac1 Added fast render support to routes which have subscriptions
Closes #18
2015-10-13 08:22:00 +02:00
Stefan Zermatten
7e68ef64cc Merge pull request #16 from ThaumRystra/bugfix-encumberance-meter-swapping
Change character reference to be reactive based on the active, undestroyed template
2015-10-09 08:58:53 +02:00
Connor Petersen
c9d71cad52 Change character reference to be reactive based on the active, undestroyed template 2015-10-08 23:05:57 -07:00
Stefan Zermatten
d79a808c81 Bumped Version 2015-10-05 08:59:25 +02:00
Stefan Zermatten
1016c39bdf Merge branch 'fix-www-route' 2015-10-05 08:59:07 +02:00
Stefan Zermatten
5f4923e049 Added canonical URL 2015-10-05 08:57:50 +02:00
Stefan Zermatten
e83237a728 Added demeteorized to gitignore 2015-10-05 08:57:33 +02:00
Stefan Zermatten
9f323738bf Renamed custom useraccount-polymer package to be more obvious 2015-10-05 08:34:43 +02:00
Stefan Zermatten
1fc76fa50d Removed Vulcanize entirely
There is no version compatible with both Meteor 1.2 and Polymer 0.5, so screw it, clients can deal with a few ms more load time.
2015-10-05 08:34:18 +02:00
Stefan Zermatten
36d5ff0a88 Update Meteor to 1.2
This requires local copies of packages that are broken or not Polymer 0.5

Closes #15
2015-10-03 22:40:04 +02:00
Stefan Zermatten
d05874ed13 Bumped version 2015-10-01 08:06:29 +02:00
Stefan Zermatten
df2521e69c Merge pull request #5 from ThaumRystra/bugfix-unprepared-spell-attacks
Spell child attacks now set their enabled state to match the parent spell prepared state
2015-10-01 08:03:30 +02:00
Stefan Zermatten
3c6a685fe8 Fixed reference to parentId which should be parent.id 2015-10-01 07:45:38 +02:00
Connor Petersen
a77e560284 Spell attacks correctly enable based on preparedness, Model correction with collection hooks 2015-09-30 13:30:59 -07:00
Stefan Zermatten
4cec83918f Updated README.md to remove out of date info 2015-09-28 07:35:10 +02:00
Stefan Zermatten
fec95c51c6 Rolled back changes to spells tab, It wasn't a column-layout 2015-09-25 13:03:14 +02:00
Stefan Zermatten
425c42d049 Bumped version 2015-09-25 12:52:28 +02:00
Stefan Zermatten
ab6f0c4f5b Merge branch 'fix-columns' 2015-09-25 12:51:04 +02:00
Stefan Zermatten
5d6e57b896 Wrap cards in padded divs to make columns behave 2015-09-25 12:49:48 +02:00
Stefan Zermatten
7c0a8125f2 Merge branch 'fix-carry-capacity-effectView' 2015-09-04 13:56:06 +02:00
Stefan Zermatten
7481ef08a8 Carry capacity effects no longer show up as "no stat" when viewed 2015-09-04 13:55:42 +02:00
Stefan Zermatten
b578dd5fb0 Merge branch 'feature-carry-capacity-modifier' 2015-09-03 14:09:51 +02:00
Stefan Zermatten
5d6f934d88 Bumped version 2015-09-03 14:09:23 +02:00
Stefan Zermatten
337f0bfa8a Made sure migration touches every character 2015-09-03 14:09:17 +02:00
Stefan Zermatten
c62784894b Made sure encumbered conditions respect carry capacity 2015-09-03 13:53:00 +02:00
Stefan Zermatten
75fff43d7d Gave an effect menu option for carry capacity 2015-09-03 13:52:40 +02:00
Stefan Zermatten
a9eeeac0df Fixed carry capacity bar 2015-09-03 13:52:22 +02:00
Stefan Zermatten
c8af0ff0a9 Fixed carry capacity table 2015-09-03 13:52:06 +02:00
Stefan Zermatten
9e200db7b9 Made carry capacity an attribute, migrations need testing 2015-08-31 15:51:52 +02:00
Stefan Zermatten
c08cf83096 Bumped version 2015-08-27 12:21:23 +02:00
Stefan Zermatten
d9368b06d0 Merge branch 'bugfix-0.6.8' 2015-08-27 12:07:42 +02:00
Stefan Zermatten
2703367681 Proficiencies now get disabled when their features are disabled 2015-08-27 12:05:56 +02:00
Stefan Zermatten
d419442549 Fixed share dialog not finding usernames or email addresses 2015-08-27 11:59:38 +02:00
Stefan Zermatten
99df01c950 Prevented negative temporary hitpoints being added 2015-08-27 11:43:34 +02:00
Stefan Zermatten
d76349b3bb Merge branch 'release-0.6.7a' 2015-07-29 09:57:06 +02:00
Stefan Zermatten
39c061f4e8 Fixed formatting of not found page, now has a toolbar and centered text 2015-07-29 09:55:22 +02:00
Stefan Zermatten
6d167ddb22 Fixed formatting of loading page, more padding 2015-07-29 09:55:01 +02:00
Stefan Zermatten
037acbd459 Added Starter Set Wizard to the front page 2015-07-29 09:54:43 +02:00
Stefan Zermatten
4d3fc3bb09 Fixed accidental back() requests when closing detail dialogs 2015-07-29 09:54:18 +02:00
Stefan Zermatten
4b984d4fac Made analytics show all characters as the same page 2015-07-29 09:53:46 +02:00
Stefan Zermatten
58843613ba Merge branch 'release-0.6.7' 2015-07-27 13:12:42 +02:00
Stefan Zermatten
39b549b24b Bumped version 2015-07-27 13:12:28 +02:00
Stefan Zermatten
c79177de72 Added Google Analytics 2015-07-27 13:10:33 +02:00
Stefan Zermatten
11d09b1487 Fixed effect values not being visible on mobile 2015-07-27 12:16:02 +02:00
Stefan Zermatten
0e4918d57d Merge branch 'hotfix-style' 2015-07-27 12:08:22 +02:00
Stefan Zermatten
949f313af2 removed 1st column of all tables being 100px 2015-07-27 12:08:15 +02:00
Stefan Zermatten
85b63f152f Merge branch 'release-0.6.6' into develop 2015-07-27 11:23:12 +02:00
Stefan Zermatten
2141d52a7a Merge branch 'release-0.6.6' 2015-07-27 11:22:41 +02:00
Stefan Zermatten
4c84235064 Bumped version 2015-07-27 11:22:26 +02:00
Stefan Zermatten
0e194a5408 Added markdown to text areas 2015-07-27 10:21:26 +02:00
Stefan Zermatten
4b60eac330 Fixed typo on change log 2015-07-24 11:52:46 +02:00
Stefan Zermatten
cb017c359d Merge branch 'release-0.6.5' into develop 2015-07-24 11:31:05 +02:00
Stefan Zermatten
15aaaa5c14 Merge branch 'release-0.6.5' 2015-07-24 11:29:50 +02:00
Stefan Zermatten
290bee83b4 Bumped version 2015-07-24 11:29:03 +02:00
Stefan Zermatten
fcd2461205 Merge branch 'master' into release-0.6.5 2015-07-24 11:26:46 +02:00
Stefan Zermatten
52198d0249 Disabled edit buttons for read-only viewers 2015-07-23 15:07:39 +02:00
Stefan Zermatten
ba7ccfdfa0 Added support for character profile pictures 2015-07-23 15:04:43 +02:00
Stefan Zermatten
92d3b086fa net worth calculations now take into account your containers' values 2015-07-23 09:14:55 +02:00
Stefan Zermatten
e180595dcd Merge branch 'release-0.6.4' 2015-07-06 13:37:02 +02:00
Stefan Zermatten
ed708bdde0 bumped version 2015-07-06 13:36:50 +02:00
Stefan Zermatten
d7852d640f Fixed Math using min and max incorrectly in skills 2015-07-03 13:21:26 +02:00
Stefan Zermatten
a2fdee88b6 Ordered Character lists 2015-06-29 14:00:55 +02:00
Stefan Zermatten
af070b1578 Attacks inherit spell names correctly 2015-06-29 13:55:27 +02:00
Stefan Zermatten
83a8eeef0f Fixed editing of multiple attacks 2015-06-29 13:55:15 +02:00
Stefan Zermatten
34f8e7402b Hit dice constitution now has a + sign when positive 2015-06-29 10:21:49 +02:00
Stefan Zermatten
1b7e2cd850 Merge branch 'hotfix-rounding' into develop 2015-06-29 10:04:45 +02:00
Stefan Zermatten
463b7f0fc9 Merge branch 'hotfix-rounding' 2015-06-29 09:45:38 +02:00
Stefan Zermatten
266495abc8 Bumped version 2015-06-29 09:44:57 +02:00
Stefan Zermatten
453d4365d3 Prevent memoized values being used in dependency loops 2015-06-29 09:41:01 +02:00
Stefan Zermatten
e0ce6275bf Floor character attribute values 2015-06-29 09:40:37 +02:00
Stefan Zermatten
16b16ce6c6 Merge branch 'hotfix-minmax' into develop 2015-06-26 10:49:10 +02:00
Stefan Zermatten
80c72a274e Merge branch 'hotfix-minmax' 2015-06-26 10:48:43 +02:00
Stefan Zermatten
91f0f7954c Bumped version 2015-06-26 10:48:33 +02:00
Stefan Zermatten
c74abcb608 Fixed min and max error 2015-06-26 10:48:04 +02:00
Stefan Zermatten
da8b91594e Removed reference to character helper 2015-06-25 14:22:07 +02:00
Stefan Zermatten
fc26f5a73e Merge branch 'hotfix-experience-inputs' into develop 2015-06-25 14:20:54 +02:00
Stefan Zermatten
4f60766d5d Merge branch 'hotfix-experience-edit-inputs' 2015-06-25 14:07:34 +02:00
Stefan Zermatten
e992aeebef Bumped version 2015-06-25 14:07:24 +02:00
Stefan Zermatten
4108346a98 Fixed experience inputs updating 2015-06-25 14:07:13 +02:00
Stefan Zermatten
946fadadc2 Merge branch 'release-0.6.0' 2015-06-25 13:49:25 +02:00
Stefan Zermatten
d2cc2833a9 Merge branch 'release-0.6.0' into develop 2015-06-25 13:48:32 +02:00
Stefan Zermatten
af57326194 Bumped version 2015-06-25 13:48:12 +02:00
Stefan Zermatten
98c69e9e17 Style tweaks 2015-06-25 13:38:33 +02:00
Stefan Zermatten
395edd0563 Character settings now formatted and has done button 2015-06-25 13:38:20 +02:00
Stefan Zermatten
43e87e7786 Container weight summaries now round off 2015-06-25 13:37:55 +02:00
Stefan Zermatten
ad347504c6 Fixed experienceDialog updating polymer inputs 2015-06-25 13:37:03 +02:00
Stefan Zermatten
4e6e99b695 Fixed spellDialog formatting 2015-06-25 13:36:42 +02:00
Stefan Zermatten
104624a322 Merge branch 'feature-soft-memoize' into develop 2015-06-25 13:35:19 +02:00
Stefan Zermatten
79d166e6af Removed references to old calculations 2015-06-25 13:34:44 +02:00
Stefan Zermatten
86c934e8ac Began replacing calls to helpers with calls to memoized functions 2015-06-18 15:14:37 +02:00
Stefan Zermatten
a034cbf30e Duplicate character helpers with memoized functions not attached to characters 2015-06-18 13:34:59 +02:00
Stefan Zermatten
d5680ebf8a Add memoize functionality 2015-06-18 13:33:54 +02:00
Stefan Zermatten
53f2fcc945 Merge branch 'release-0.5.7' into develop 2015-06-17 14:32:35 +02:00
Stefan Zermatten
612e127be4 Merge branch 'release-0.5.7' 2015-06-17 14:22:33 +02:00
Stefan Zermatten
2b0d975cee change logs 2015-06-17 14:22:12 +02:00
Stefan Zermatten
248ab9bb6b Added moving item dialog, that can't actually be triggered 2015-06-17 14:15:49 +02:00
Stefan Zermatten
314ce85410 Attacks can now be added to spells 2015-06-17 09:05:23 +02:00
Stefan Zermatten
9ff45dbcc2 backgrounds now have proficiencies 2015-06-17 08:49:26 +02:00
Stefan Zermatten
6caf19bc99 Merge branch 'release-0.5.6' into develop 2015-06-15 13:36:50 +02:00
Stefan Zermatten
c2b04d0977 Merge branch 'release-0.5.6' 2015-06-15 13:34:29 +02:00
Stefan Zermatten
9012c4a558 change logs 2015-06-15 13:34:10 +02:00
Stefan Zermatten
65a84937f2 Merge branch 'feature-share-publically' into develop 2015-06-15 13:30:42 +02:00
Stefan Zermatten
eebb88b6b1 New front page and darker style 2015-06-15 13:29:33 +02:00
Stefan Zermatten
06ab7c5116 Merge branch 'master' into feature-share-publically 2015-06-15 10:04:40 +02:00
Stefan Zermatten
89f03c7601 Merge branch 'hotfix-gmail-report' 2015-06-12 08:04:23 +02:00
Stefan Zermatten
9d2eb14c0c Change logs 2015-06-12 08:03:46 +02:00
Stefan Zermatten
7b3cb54983 Added gmail email address senders to the report emails 2015-06-12 08:02:33 +02:00
Stefan Zermatten
a09bad2fed Change logs 2015-06-10 11:13:58 +02:00
Stefan Zermatten
afd897edfe Merge branch 'Hotfix' 2015-06-10 11:05:01 +02:00
Stefan Zermatten
efc79cb6e7 Fixed net value calculation to avoid rounding errors 2015-06-10 11:00:42 +02:00
Stefan Zermatten
35efe39ea7 Made feedback reports send emails "from" their creator's address 2015-06-10 11:00:19 +02:00
Stefan Zermatten
034067bd6e Added link to example character 2015-06-10 10:58:47 +02:00
Stefan Zermatten
0d75cd5d15 Added sharing to anyone with link, changed home page 2015-06-09 17:06:51 +02:00
Stefan Zermatten
4f1376a666 Change log 2015-06-04 09:57:49 +02:00
Stefan Zermatten
78b1d71b9d Overhauled how effects are edited 2015-05-27 13:13:51 +02:00
Stefan Zermatten
1323d8006c Made feedback not sendable without title & description 2015-05-27 09:18:34 +02:00
Stefan Zermatten
87d722adaf Now send emails to myself when feedback gets reported 2015-05-27 08:33:24 +02:00
Stefan Zermatten
90e511eb00 Added the ability to hide the spells tab for a character 2015-05-27 08:10:14 +02:00
Stefan Zermatten
5b8c25f5de Fixed a harmless error with un-set effect views 2015-05-27 08:04:39 +02:00
Stefan Zermatten
2fbc54fee8 Edited the guide to be "open beta" 2015-05-25 09:23:38 +02:00
Stefan Zermatten
a064ae3fe8 Added Kadira 2015-05-25 08:33:14 +02:00
Stefan Zermatten
ba9b518d7e Added subsManager 2015-05-22 14:36:05 +02:00
233 changed files with 6991 additions and 4735 deletions

View File

@@ -13,7 +13,7 @@
"disallowMixedSpacesAndTabs": "smart",
"disallowTrailingWhitespace": true,
"disallowSpaceAfterPrefixUnaryOperators": true,
"disallowMultipleVarDecl": true,
"disallowMultipleVarDecl": false,
"disallowNewlineBeforeBlockStatements": true,
"disallowKeywordsOnNewLine": ["else"],
@@ -53,4 +53,4 @@
"disallowMultipleLineBreaks": true,
"disallowNewlineBeforeBlockStatements": true
}
}

View File

@@ -1,58 +1,13 @@
TODO
====
RPG Docs
========
* Get Polymer installed using bower.
* Install Vulcanize package listed below
* Copy the differential polymer demo to get polymer implemented nicely
* Update Meteor
* Install and use LESS
This is the repo for [DiceCloud](dicecloud.com). The currently deployed version should always be the latest release of the master branch.
Packages used
=============
Getting started
---------------
* meteor-platform
* Base Meteor.
* [Docs](http://docs.meteor.com/#/full/)
* autopublish
* Publishes everything to the client.
* Must be removed before release
* insecure
* Allows the client the freedom to modify any colleciton.
* Must be removed before release
* iron:router
* Enables pagination and URL's to direct to specific templates.
* [Tutorial](http://www.manuel-schoebel.com/blog/iron-router-tutorial)
* accounts-password
* Lets users create accounts with a simple password
* accounts-ui
* Adds simple UI for logging in
* random
* Somewhat decent cryptographically strong psuedo random number generation.
* [readme](https://atmospherejs.com/meteor/random)
* dburles:collection-helpers
* Adds template-style helpers to collections. [github page](https://github.com/dburles/meteor-collection-helpers)
* reactive-var
* Friendly reactive variables
* [Meteor Docs](http://docs.meteor.com/#/full/reactivevar_pkg)
* cw4gn3r:jquery-event-drag
* Adds jquery drag events
* underscore
* Handy javascript utilities
* [Docs](http://underscorejs.org/)
* aldeed:collection2
* Extends collections with Schemas
* [(gitHub page)](https://github.com/aldeed/meteor-collection2)
* uses [SimpleSchema](https://github.com/aldeed/meteor-simple-schema)
* aldeed:autoform
* Automatically generates bootstrap forms for collection2 Schemas.
* [github](https://github.com/aldeed/meteor-autoform)
* differential:vulcanize
* Bakes all the polymer imports into one file
* [github](https://github.com/Differential/meteor-vulcanize)
************
Resources
=========
[differential's polymer demo](https://github.com/Differential/polymer-demo)
`git clone https://github.com/ThaumRystra/RPG-Docs RPG-Docs`
`cd RPG-Docs`
`cd rpg-docs`
`bower install`
`meteor`

6
rpg-docs/.gitignore vendored
View File

@@ -1,5 +1,9 @@
.meteor/local
.meteor/meteorite
.demeteorized
settings.json
public/components
public/_imports.html
nohup.out
dump
node_modules
dump

View File

@@ -6,3 +6,10 @@ notices-for-0.9.0
notices-for-0.9.1
0.9.4-platform-file
notices-for-facebook-graph-api-2
1.2.0-standard-minifiers-package
1.2.0-meteor-platform-split
1.2.0-cordova-changes
1.2.0-breaking-changes
1.3.0-split-minifiers-package
1.4.0-remove-old-dev-bundle-link
1.4.1-add-shell-server-package

View File

@@ -1 +0,0 @@

View File

@@ -3,16 +3,14 @@
# 'meteor add' and 'meteor remove' will edit this file for you,
# but you can also edit it by hand.
meteor-platform
iron:router
accounts-password
accounts-ui
random
accounts-password@1.3.3
accounts-ui@1.1.9
random@1.0.10
dburles:collection-helpers
reactive-var
underscore
reactive-var@1.0.11
underscore@1.0.10
aldeed:collection2
differential:vulcanize
matb33:collection-hooks
zimme:collection-softremovable
momentjs:moment
@@ -20,9 +18,31 @@ dburles:mongo-collection-instances
percolate:migrations
ecwyne:mathjs
useraccounts:polymer
accounts-google
accounts-google@1.0.11
splendido:accounts-meld
email
fourseven:scss@2.1.1
wolves:bourbon
email@1.1.18
meteorhacks:subs-manager
chuangbo:marked
reywood:iron-router-ga
meteor-base@1.0.4
mobile-experience@1.0.4
mongo@1.1.14
blaze-html-templates
session@1.1.7
jquery@1.11.10
tracker@1.1.1
logging@1.1.16
reload@1.1.11
ejson@1.0.13
spacebars
check@1.2.4
useraccounts:iron-routing
wizonesolutions:canonical
standard-minifier-js@1.2.1
shell-server@0.2.1
seba:minifiers-autoprefixer
nikogosovd:multiple-uihooks
templates:array
ecmascript@0.6.1
es5-shim@4.6.15
differential:vulcanize

View File

@@ -1 +1 @@
METEOR@1.1.0.2
METEOR@1.4.2.6

View File

@@ -1,91 +1,128 @@
accounts-base@1.2.0
accounts-google@1.0.4
accounts-oauth@1.1.5
accounts-password@1.1.1
accounts-ui@1.1.5
accounts-ui-unstyled@1.1.7
aldeed:collection2@2.3.3
aldeed:simple-schema@1.3.3
autoupdate@1.2.1
base64@1.0.3
binary-heap@1.0.3
blaze@2.1.2
blaze-tools@1.0.3
boilerplate-generator@1.0.3
callback-hook@1.0.3
check@1.0.5
coffeescript@1.0.6
dburles:collection-helpers@1.0.3
dburles:mongo-collection-instances@0.3.3
ddp@1.1.0
deps@1.0.7
differential:vulcanize@0.0.5
accounts-base@1.2.14
accounts-google@1.0.11
accounts-oauth@1.1.15
accounts-password@1.3.3
accounts-ui@1.1.9
accounts-ui-unstyled@1.1.13
aldeed:collection2@2.10.0
aldeed:collection2-core@1.2.0
aldeed:schema-deny@1.1.0
aldeed:schema-index@1.1.1
aldeed:simple-schema@1.5.3
allow-deny@1.0.5
autoupdate@1.3.12
babel-compiler@6.13.0
babel-runtime@1.0.1
base64@1.0.10
binary-heap@1.0.10
blaze@2.3.0
blaze-html-templates@1.1.0
blaze-tools@1.0.10
boilerplate-generator@1.0.11
caching-compiler@1.1.9
caching-html-compiler@1.1.0
callback-hook@1.0.10
check@1.2.4
chuangbo:marked@0.3.5_1
coffeescript@1.11.1_4
dburles:collection-helpers@1.1.0
dburles:mongo-collection-instances@0.3.5
ddp@1.2.5
ddp-client@1.3.2
ddp-common@1.2.8
ddp-rate-limiter@1.0.6
ddp-server@1.3.12
deps@1.0.12
diff-sequence@1.0.7
differential:vulcanize@3.0.0
ecmascript@0.6.1
ecmascript-runtime@0.3.15
ecwyne:mathjs@0.25.0
ejson@1.0.6
email@1.0.6
fastclick@1.0.3
fourseven:scss@2.1.1
geojson-utils@1.0.3
google@1.1.5
html-tools@1.0.4
htmljs@1.0.4
http@1.1.0
id-map@1.0.3
iron:controller@1.0.7
iron:core@1.0.7
iron:dynamic-template@1.0.7
iron:layout@1.0.7
iron:location@1.0.7
iron:middleware-stack@1.0.7
iron:router@1.0.7
iron:url@1.0.7
jquery@1.11.3_2
json@1.0.3
lai:collection-extensions@0.1.3
launch-screen@1.0.2
less@1.0.14
livedata@1.0.13
localstorage@1.0.3
logging@1.0.7
matb33:collection-hooks@0.7.13
meteor@1.1.6
meteor-platform@1.2.2
meteorhacks:subs-manager@1.3.0
minifiers@1.1.5
minimongo@1.0.8
mobile-status-bar@1.0.3
momentjs:moment@2.10.3
mongo@1.1.0
npm-bcrypt@0.7.8_2
oauth@1.1.4
oauth2@1.1.3
observe-sequence@1.0.6
ordered-dict@1.0.3
percolate:migrations@0.7.5
random@1.0.3
reactive-dict@1.1.0
reactive-var@1.0.5
reload@1.1.3
retry@1.0.3
routepolicy@1.0.5
service-configuration@1.0.4
session@1.1.0
sha@1.0.3
softwarerero:accounts-t9n@1.0.9
spacebars@1.0.6
spacebars-compiler@1.0.6
ejson@1.0.13
email@1.1.18
es5-shim@4.6.15
fastclick@1.0.13
geojson-utils@1.0.10
google@1.1.15
hot-code-push@1.0.4
html-tools@1.0.11
htmljs@1.0.11
http@1.2.10
id-map@1.0.9
iron:controller@1.0.12
iron:core@1.0.11
iron:dynamic-template@1.0.12
iron:layout@1.0.12
iron:location@1.0.11
iron:middleware-stack@1.1.0
iron:router@1.1.1
iron:url@1.0.11
jquery@1.11.10
lai:collection-extensions@0.2.1_1
launch-screen@1.1.0
less@2.7.9
livedata@1.0.18
localstorage@1.0.12
logging@1.1.16
matb33:collection-hooks@0.8.4
mdg:validation-error@0.5.1
meteor@1.6.0
meteor-base@1.0.4
meteorhacks:subs-manager@1.6.4
minifier-css@1.2.16
minifier-js@1.2.17
minimongo@1.0.19
mobile-experience@1.0.4
mobile-status-bar@1.0.13
modules@0.7.7
modules-runtime@0.7.8
momentjs:moment@2.17.1
mongo@1.1.14
mongo-id@1.0.6
nikogosovd:multiple-uihooks@0.1.8
npm-bcrypt@0.9.2
npm-mongo@2.2.16_1
oauth@1.1.12
oauth2@1.1.11
observe-sequence@1.0.14
ordered-dict@1.0.9
percolate:migrations@0.9.8
promise@0.8.8
raix:eventemitter@0.1.3
random@1.0.10
rate-limit@1.0.6
reactive-dict@1.1.8
reactive-var@1.0.11
reload@1.1.11
retry@1.0.9
reywood:iron-router-ga@0.7.1
routepolicy@1.0.12
seba:minifiers-autoprefixer@1.0.1
service-configuration@1.0.11
session@1.1.7
sha@1.0.9
shell-server@0.2.1
softwarerero:accounts-t9n@1.3.7
spacebars@1.0.13
spacebars-compiler@1.1.0
splendido:accounts-emails-field@1.2.0
splendido:accounts-meld@1.3.0
srp@1.0.3
templating@1.1.1
tracker@1.0.7
ui@1.0.6
underscore@1.0.3
url@1.0.4
useraccounts:core@1.9.1
useraccounts:polymer@1.9.1
webapp@1.2.0
webapp-hashing@1.0.3
wolves:bourbon@1.0.0
zimme:collection-behaviours@1.0.4
zimme:collection-softremovable@1.0.4
splendido:accounts-meld@1.3.1
srp@1.0.10
standard-minifier-js@1.2.2
templates:array@1.0.3
templating@1.3.0
templating-compiler@1.3.0
templating-runtime@1.3.0
templating-tools@1.1.0
tracker@1.1.1
ui@1.0.12
underscore@1.0.10
url@1.0.11
useraccounts:core@1.14.2
useraccounts:iron-routing@1.14.2
useraccounts:polymer@1.14.2
webapp@1.3.12
webapp-hashing@1.0.9
wizonesolutions:canonical@0.0.5
zimme:collection-behaviours@1.1.3
zimme:collection-softremovable@1.0.5

View File

@@ -7,6 +7,7 @@ Schemas.Action = new SimpleSchema({
charId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
index: 1,
},
name: {
type: String,

View File

@@ -7,6 +7,7 @@ Schemas.Attack = new SimpleSchema({
charId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
index: 1,
},
name: {
type: String,
@@ -69,5 +70,17 @@ Attacks.attachSchema(Schemas.Attack);
Attacks.attachBehaviour("softRemovable");
makeChild(Attacks, ["name", "enabled"]); //children of lots of things
Attacks.after.insert(function (userId, attack) {
//Check to see if this attack's parent is a spell, if so, mirror prepared state to enabled
if (attack.parent.collection === "Spells") {
var parentSpell = Spells.findOne(attack.parent.id);
if (parentSpell.prepared === "unprepared") {
Attacks.update(attack._id, {$set: {enabled: false}});
} else if (parentSpell.prepared === "prepared" || "always") {
Attacks.update(attack._id, {$set: {enabled: true}});
}
}
});
Attacks.allow(CHARACTER_SUBSCHEMA_ALLOW);
Attacks.deny(CHARACTER_SUBSCHEMA_DENY);

View File

@@ -4,6 +4,7 @@ Schemas.Buff = new SimpleSchema({
charId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
index: 1,
},
name: {
type: String,

View File

@@ -3,16 +3,17 @@ Characters = new Mongo.Collection("characters");
Schemas.Character = new SimpleSchema({
//strings
name: {type: String, defaultValue: "", trim: false},
alignment: {type: String, defaultValue: "", trim: false},
gender: {type: String, defaultValue: "", trim: false},
race: {type: String, defaultValue: "", trim: false},
description: {type: String, defaultValue: "", trim: false},
personality: {type: String, defaultValue: "", trim: false},
ideals: {type: String, defaultValue: "", trim: false},
bonds: {type: String, defaultValue: "", trim: false},
flaws: {type: String, defaultValue: "", trim: false},
backstory: {type: String, defaultValue: "", trim: false},
name: {type: String, defaultValue: "", trim: false, optional: true},
alignment: {type: String, defaultValue: "", trim: false, optional: true},
gender: {type: String, defaultValue: "", trim: false, optional: true},
race: {type: String, defaultValue: "", trim: false, optional: true},
picture: {type: String, defaultValue: "", trim: true, optional: true},
description: {type: String, defaultValue: "", trim: false, optional: true},
personality: {type: String, defaultValue: "", trim: false, optional: true},
ideals: {type: String, defaultValue: "", trim: false, optional: true},
bonds: {type: String, defaultValue: "", trim: false, optional: true},
flaws: {type: String, defaultValue: "", trim: false, optional: true},
backstory: {type: String, defaultValue: "", trim: false, optional: true},
//attributes
//ability scores
@@ -32,6 +33,7 @@ Schemas.Character = new SimpleSchema({
age: {type: Schemas.Attribute},
ageRate: {type: Schemas.Attribute},
armor: {type: Schemas.Attribute},
carryMultiplier: {type: Schemas.Attribute},
//resources
level1SpellSlots: {type: Schemas.Attribute},
@@ -159,6 +161,7 @@ Schemas.Character = new SimpleSchema({
deathSave: {type: Schemas.DeathSave},
//permissions
party: {type: String, regEx: SimpleSchema.RegEx.Id, optional: true},
owner: {type: String, regEx: SimpleSchema.RegEx.Id},
readers: {type: [String], regEx: SimpleSchema.RegEx.Id, defaultValue: []},
writers: {type: [String], regEx: SimpleSchema.RegEx.Id, defaultValue: []},
@@ -173,99 +176,117 @@ Schemas.Character = new SimpleSchema({
//slowed down by carrying too much?
"settings.useVariantEncumbrance": {type: Boolean, defaultValue: false},
"settings.useStandardEncumbrance": {type: Boolean, defaultValue: true},
//hide spellcasting
"settings.hideSpellcasting": {type: Boolean, defaultValue: false},
//show to anyone with link
"settings.viewPermission": {
type: String,
defaultValue: "whitelist",
allowedValues: ["whitelist", "public"],
},
});
Characters.attachSchema(Schemas.Character);
var attributeBase = function(charId, statName){
var attributeBase = preventLoop(function(charId, statName){
check(statName, String);
//if it's a damage multiplier, we treat it specially
if (_.contains(DAMAGE_MULTIPLIERS, statName)){
var effects = Effects.find(
{charId: charId, stat: statName, enabled: true, operation: "mul"}
).fetch();
var resistCount = 0;
var vulnCount = 0;
var multiplierEvaluationFail = false;
_.each(effects, function(effect){
var val = evaluateEffect(charId, effect);
if (val === 0.5){ //resistance
resistCount += 1;
} else if (val === 2){ //vulnerability
vulnCount += 1;
} else if (val === 0){ //imunity
return 0; //imunity is absolute
} else {
multiplierEvaluationFail = true;
}
});
if (multiplierEvaluationFail){
//we can't work it out correctly, set the value to 1
//and try work it out using regular maths below
value = 1;
} else if (resistCount && !vulnCount){
var invulnerabilityCount = Effects.find({
charId: charId,
stat: statName,
enabled: true,
operation: "mul",
value: 0,
}).count();
if (invulnerabilityCount) return 0;
var resistCount = Effects.find({
charId: charId,
stat: statName,
enabled: true,
operation: "mul",
value: 0.5,
}).count();
var vulnCount = Effects.find({
charId: charId,
stat: statName,
enabled: true,
operation: "mul",
value: 2,
}).count();
if (!resistCount && !vulnCount){
return 1;
} else if (resistCount && !vulnCount){
return 0.5;
} else if (!resistCount && vulnCount){
} else if (!resistCount && vulnCount){
return 2;
} else {
return 1;
}
}
var value;
var base = 0;
var add = 0;
var mul = 1;
var min = Number.NEGATIVE_INFINITY;
var max = Number.POSITIVE_INFINITY;
var value = 0;
//start with the highest base value
Effects.find(
{charId: charId, stat: statName, enabled: true, operation: "base"}
).forEach(function(effect){
var efv = evaluateEffect(charId, effect);
if (efv > value){
value = efv;
Effects.find({
charId: charId,
stat: statName,
enabled: true,
operation: {$in: ["base", "add", "mul", "min", "max"]},
}).forEach(function(effect) {
value = evaluateEffect(charId, effect);
if (effect.operation === "base"){
if (value > base) base = value;
} else if (effect.operation === "add"){
add += value;
} else if (effect.operation === "mul"){
mul *= value;
} else if (effect.operation === "min"){
if (value > min) min = value;
} else if (effect.operation === "max"){
if (value < max) max = value;
}
});
//add all the add values
Effects.find(
{charId: charId, stat: statName, enabled: true, operation: "add"}
).forEach(function(effect){
value += evaluateEffect(charId, effect);
});
var result = (base + add) * mul;
if (result < min) result = min;
if (result > max) result = max;
//multiply all the mul values
Effects.find(
{charId: charId, stat: statName, enabled: true, operation: "mul"}
).forEach(function(effect){
value *= evaluateEffect(charId, effect);
});
return Math.floor(result);
});
//ensure value is >= all mins
Effects.find(
{charId: charId, stat: statName, enabled: true, operation: "min"}
).forEach(function(effect){
var min = evaluateEffect(charId, effect);
value = value > min ? value : min;
if (Meteor.isClient) {
Template.registerHelper("characterCalculate", function(func, charId, input) {
try {
return Characters.calculate[func](charId, input);
} catch (e){
if (!Characters.calculate[func]){
throw new Error(func + "is not a function name");
} else {
throw e;
}
}
});
}
//ensure value is <= all maxes
Effects.find(
{charId: charId, stat: statName, enabled: true, operation: "max"}
).forEach(function(effect){
var max = evaluateEffect(charId, effect);
value = value < max ? value : max;
//create a local memoize with a argument concatenating hash function
var memoize = function(f) {
return Tracker.memoize(f, function() {
return _.reduce(arguments, function(memo, arg) {
return memo + arg;
}, "");
});
return value;
};
//functions and calculated values.
//These functions can only rely on this._id since no other
//field is likely to be attached to all returned characters
Characters.helpers({
//returns the value stored in the field requested
//will set up dependencies on just that field
getField : function(fieldName){
//memoize funcitons that have finds and slow loops
Characters.calculate = {
getField: function(charId, fieldName) {
var fieldSelector = {};
fieldSelector[fieldName] = 1;
var char = Characters.findOne(this._id, {fields: fieldSelector});
var char = Characters.findOne(charId, {fields: fieldSelector});
var field = char[fieldName];
if (field === undefined){
throw new Meteor.Error(
@@ -278,8 +299,7 @@ Characters.helpers({
}
return field;
},
//returns the value of a field
fieldValue : function(fieldName){
fieldValue: function(charId, fieldName) {
if (!Schemas.Character.schema(fieldName)){
throw new Meteor.Error(
"Field not found",
@@ -289,102 +309,92 @@ Characters.helpers({
//duck typing to get the right value function
//.ability implies skill
if (Schemas.Character.schema(fieldName + ".ability")){
return this.skillMod(fieldName);
return Characters.calculate.skillMod(charId, fieldName);
}
//adjustment implies attribute
if (Schemas.Character.schema(fieldName + ".adjustment")){
return this.attributeValue(fieldName);
return Characters.calculate.attributeValue(charId, fieldName);
}
//fall back to just returning the field itself
return this.getField(fieldName);
return Characters.calculate.getField(charId, fieldName);
},
attributeValue: function(attributeName){
var charId = this._id;
var attribute = this.getField(attributeName);
attributeValue: memoize(function(charId, attributeName){
var attribute = Characters.calculate.getField(charId, attributeName);
//base value
var value = this.attributeBase(attributeName);
var value = Characters.calculate.attributeBase(charId, attributeName);
//plus adjustment
value += attribute.adjustment;
return value;
},
attributeBase: preventLoop(function(attributeName){
var charId = this._id;
//base value
}),
attributeBase: memoize(function(charId, attributeName){
return attributeBase(charId, attributeName);
}),
skillMod: preventLoop(function(skillName){
var charId = this._id;
var skill = this.getField(skillName);
skillMod: memoize(preventLoop(function(charId, skillName){
var skill = Characters.calculate.getField(charId, skillName);
//get the final value of the ability score
var ability = this.attributeValue(skill.ability);
var ability = Characters.calculate.attributeValue(charId, skill.ability);
//base modifier
var mod = +getMod(ability);
//multiply proficiency bonus by largest value in proficiency array
var prof = this.proficiency(skillName);
var prof = Characters.calculate.proficiency(charId, skillName);
//add multiplied proficiency bonus to modifier
mod += prof * this.attributeValue("proficiencyBonus");
mod += prof * Characters.calculate.attributeValue(charId, "proficiencyBonus");
//apply all effects
var rawEffects = Effects.find(
{charId: charId, stat: skillName, enabled: true}
).fetch();
var effects = _.groupBy(rawEffects, "operation");
_.forEach(effects.add, function(effect){
mod += evaluateEffect(charId, effect);
});
_.forEach(effects.mul, function(effect){
mod *= evaluateEffect(charId, effect);
});
_.forEach(effects.min, function(effect){
var min = evaluateEffect(charId, effect);
mod = mod > min ? mod : min;
});
_.forEach(effects.max, function(effect){
var max = evaluateEffect(charId, effect);
mod = mod < max ? mod : max;
});
return signedString(mod);
}),
var value;
var add = 0;
var mul = 1;
var min = Number.NEGATIVE_INFINITY;
var max = Number.POSITIVE_INFINITY;
proficiency: function(skillName){
var charId = this._id;
//return largest value in proficiency array
var prof = 0;
Proficiencies.find(
{charId: charId, name: skillName, enabled: true}
).forEach(function(proficiency){
var newProf = proficiency.value;
if (newProf > prof){
prof = newProf;
Effects.find({
charId: charId,
stat: skillName,
enabled: true,
operation: {$in: ["base", "add", "mul", "min", "max"]},
}).forEach(function(effect) {
value = evaluateEffect(charId, effect);
if (effect.operation === "add"){
add += value;
} else if (effect.operation === "mul"){
mul *= value;
} else if (effect.operation === "min"){
if (value > min) min = value;
} else if (effect.operation === "max"){
if (value < max) max = value;
}
});
return prof;
},
var result = (mod + add) * mul;
if (result < min) result = min;
if (result > max) result = max;
passiveSkill: function(skillName){
if (_.isString(skillName)){
var skill = this.getField(skillName);
}
var charId = this._id;
var mod = +this.skillMod(skillName);
return Math.floor(result);
})),
proficiency: memoize(function(charId, skillName){
//return largest value in proficiency array
var prof = Proficiencies.findOne(
{charId: charId, name: skillName, enabled: true},
{sort: {value: -1}}
);
return prof && prof.value || 0;
}),
passiveSkill: memoize(function(charId, skillName){
var skill = Characters.calculate.getField(charId, skillName);
var mod = +Characters.calculate.skillMod(charId, skillName);
var value = 10 + mod;
Effects.find(
{charId: charId, stat: skillName, enabled: true, operation: "passiveAdd"}
).forEach(function(effect){
value += evaluateEffect(charId, effect);
});
return value;
//TODO decide whether (dis)advantage gives (-)+5 to passive checks
},
advantage: function(skillName){
var charId = this._id;
var advantage = Characters.calculate.advantage(charId, skillName);
value += 5 * advantage;
return Math.floor(value);
}),
advantage: memoize(function(charId, skillName){
var advantage = Effects.find(
{charId: charId, stat: skillName, enabled: true, operation: "advantage"}
).count();
@@ -394,19 +404,18 @@ Characters.helpers({
if (advantage && !disadvantage) return 1;
if (disadvantage && !advantage) return -1;
return 0;
}),
abilityMod: function(charId, attribute){
return getMod(
Characters.calculate.attributeValue(charId, attribute)
);
},
abilityMod: function(attribute){
return signedString(getMod(this.attributeValue(attribute)));
},
passiveAbility: function(attribute){
var mod = +getMod(this.attributeValue(attribute));
passiveAbility: function(charId, attribute){
var mod = +getMod(Characters.calculate.attributeValue(charId, attribute));
return 10 + mod;
},
xpLevel: function(){
var xp = this.experience();
xpLevel: function(charId){
var xp = Characters.calculate.experience(charId);
for (var i = 0; i < 19; i++){
if (xp < XP_TABLE[i]){
return i;
@@ -415,30 +424,103 @@ Characters.helpers({
if (xp > 355000) return 20;
return 0;
},
level: function(){
level: memoize(function(charId){
var level = 0;
Classes.find({charId: this._id}).forEach(function(cls){
Classes.find({charId: charId}).forEach(function(cls){
level += cls.level;
});
return level;
},
experience: function(){
}),
experience: memoize(function(charId){
var xp = 0;
Experiences.find(
{charId: this._id},
{charId: charId},
{fields: {value: 1}}
).forEach(function(e){
xp += e.value;
});
return xp;
}),
};
var deprecated = function() {
//var err = new Error("this function has been deprecated");
var name = "";
if (Template.instance()){
name = Template.instance().view.name;
}
var logString = "this function has been deprecated \n";
if (name){
logString += "View: " + name + "\n\n";
}
//logString += err.stack + "\n\n---------------------\n\n";
console.log(logString);
};
//functions and calculated values.
//These functions can only rely on this._id since no other
//field is likely to be attached to all returned characters
Characters.helpers({
//returns the value stored in the field requested
//will set up dependencies on just that field
getField : function(fieldName){
deprecated();
return Characters.calculate.getField(this._id, fieldName);
},
//returns the value of a field
fieldValue : function(fieldName){
deprecated();
return Characters.calculate.fieldValue(this._id, fieldName);
},
attributeValue: function(attributeName){
deprecated();
return Characters.calculate.attributeValue(this._id, attributeName);
},
attributeBase: function(attributeName){
deprecated();
return Characters.calculate.attributeBase(this._id, attributeName);
},
skillMod: function(skillName){
deprecated();
return Characters.calculate.skillMod(this._id, skillName);
},
proficiency: function(skillName){
deprecated();
return Characters.calculate.proficiency(this._id, skillName);
},
passiveSkill: function(skillName){
deprecated();
return Characters.calculate.passiveSkill(this._id, skillName);
},
advantage: function(skillName){
deprecated();
return Characters.calculate.advantage(this._id, skillName);
},
abilityMod: function(attribute){
deprecated();
return Characters.calculate.abilityMod(this._id, attribute);
},
passiveAbility: function(attribute){
deprecated();
return Characters.calculate.passiveAbility(this._id, attribute);
},
xpLevel: function(){
deprecated();
return Characters.calculate.xpLevel(this._id);
},
level: function(){
deprecated();
return Characters.calculate.level(this._id);
},
experience: function(){
deprecated();
return Characters.calculate.experience(this._id);
},
});
//clean up all data related to that character before removing it
Characters.after.remove(function(userId, character) {
if (Meteor.isServer){
if (Meteor.isServer){
Characters.after.remove(function(userId, character) {
Actions .remove({charId: character._id});
Attacks .remove({charId: character._id});
Buffs .remove({charId: character._id});
@@ -451,8 +533,8 @@ Characters.after.remove(function(userId, character) {
SpellLists .remove({charId: character._id});
Items .remove({charId: character._id});
Containers .remove({charId: character._id});
}
});
});
}
Characters.allow({
insert: function(userId, doc) {

View File

@@ -1,7 +1,7 @@
Classes = new Mongo.Collection("classes");
Schemas.Class = new SimpleSchema({
charId: {type: String, regEx: SimpleSchema.RegEx.Id},
charId: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
name: {type: String, trim: false},
level: {type: Number},
createdAt: {

View File

@@ -8,6 +8,7 @@ Schemas.Effect = new SimpleSchema({
charId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
index: 1,
},
name: {
type: String,
@@ -58,59 +59,74 @@ Schemas.Effect = new SimpleSchema({
Effects.attachSchema(Schemas.Effect);
if (Meteor.isServer) Characters.after.insert(function(userId, char) {
Effects.insert({
charId: char._id,
name: "Constitution modifier for each level",
stat: "hitPoints",
operation: "add",
calculation: "level * constitutionMod",
parent: {
id: char._id,
collection: "Characters",
group: "Inate",
},
});
Effects.insert({
charId: char._id,
name: "Proficiency bonus by level",
stat: "proficiencyBonus",
operation: "add",
calculation: "floor(level / 4 + 1.75)",
parent: {
id: char._id,
collection: "Characters",
group: "Inate",
},
});
Effects.insert({
charId: char._id,
name: "Dexterity Armor Bonus",
stat: "armor",
operation: "add",
calculation: "dexterityArmor",
parent: {
id: char._id,
collection: "Characters",
group: "Inate",
},
});
Effects.insert({
charId: char._id,
name: "Natural Armor",
stat: "armor",
operation: "base",
value: 10,
parent: {
id: char._id,
collection: "Characters",
group: "Inate",
},
});
});
Effects.attachBehaviour("softRemovable");
makeChild(Effects, ["enabled"]); //children of lots of things
Effects.allow(CHARACTER_SUBSCHEMA_ALLOW);
Effects.deny(CHARACTER_SUBSCHEMA_DENY);
//give characters default character effects
Characters.after.insert(function(userId, char) {
if (Meteor.isServer) {
Effects.insert({
charId: char._id,
name: "Constitution modifier for each level",
stat: "hitPoints",
operation: "add",
calculation: "level * constitutionMod",
parent: {
id: char._id,
collection: "Characters",
group: "Inate",
},
});
Effects.insert({
charId: char._id,
name: "Proficiency bonus by level",
stat: "proficiencyBonus",
operation: "add",
calculation: "floor(level / 4 + 1.75)",
parent: {
id: char._id,
collection: "Characters",
group: "Inate",
},
});
Effects.insert({
charId: char._id,
name: "Dexterity Armor Bonus",
stat: "armor",
operation: "add",
calculation: "dexterityArmor",
parent: {
id: char._id,
collection: "Characters",
group: "Inate",
},
});
Effects.insert({
charId: char._id,
name: "Natural Armor",
stat: "armor",
operation: "base",
value: 10,
parent: {
id: char._id,
collection: "Characters",
group: "Inate",
},
});
Effects.insert({
charId: char._id,
name: "Natural Carrying Capacity",
stat: "carryMultiplier",
operation: "base",
value: "1",
parent: {
id: char._id,
collection: "Characters",
group: "Inate",
},
});
}
});

View File

@@ -1,7 +1,7 @@
Experiences = new Mongo.Collection("experience");
Schemas.Experience = new SimpleSchema({
charId: {type: String, regEx: SimpleSchema.RegEx.Id},
charId: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
name: {type: String, defaultValue: "New Experience", trim: false},
description: {type: String, optional: true, trim: false},
value: {type: Number, defaultValue: 0},

View File

@@ -1,7 +1,7 @@
Features = new Mongo.Collection("features");
Schemas.Feature = new SimpleSchema({
charId: {type: String, regEx: SimpleSchema.RegEx.Id},
charId: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
name: {type: String, trim: false},
description: {type: String, optional: true, trim: false},
uses: {type: String, optional: true, trim: false},
@@ -35,3 +35,81 @@ makeParent(Features, ["name", "enabled"]); //parents of effects and attacks
Features.allow(CHARACTER_SUBSCHEMA_ALLOW);
Features.deny(CHARACTER_SUBSCHEMA_DENY);
//give characters default feature of base ability scores of 10
Characters.after.insert(function(userId, char) {
if (Meteor.isServer){
var featureId = Features.insert({
name: "Base Ability Scores",
charId: char._id,
enabled: true,
alwaysEnabled: true,
});
Effects.insert({
stat: "strength",
charId: char._id,
parent: {
id: featureId,
collection: "Features",
},
operation: "base",
value: 10,
enabled: true,
});
Effects.insert({
stat: "dexterity",
charId: char._id,
parent: {
id: featureId,
collection: "Features",
},
operation: "base",
value: 10,
enabled: true,
});
Effects.insert({
stat: "constitution",
charId: char._id,
parent: {
id: featureId,
collection: "Features",
},
operation: "base",
value: 10,
enabled: true,
});
Effects.insert({
stat: "intelligence",
charId: char._id,
parent: {
id: featureId,
collection: "Features",
},
operation: "base",
value: 10,
enabled: true,
});
Effects.insert({
stat: "wisdom",
charId: char._id,
parent: {
id: featureId,
collection: "Features",
},
operation: "base",
value: 10,
enabled: true,
});
Effects.insert({
stat: "charisma",
charId: char._id,
parent: {
id: featureId,
collection: "Features",
},
operation: "base",
value: 10,
enabled: true,
});
}
});

View File

@@ -1,7 +1,7 @@
Notes = new Mongo.Collection("notes");
Schemas.Note = new SimpleSchema({
charId: {type: String, regEx: SimpleSchema.RegEx.Id},
charId: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
name: {type: String, trim: false},
description: {type: String, optional: true, trim: false},
color: {

View File

@@ -4,6 +4,7 @@ Schemas.Proficiency = new SimpleSchema({
charId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
index: 1,
},
name: {
type: String,
@@ -30,7 +31,7 @@ Schemas.Proficiency = new SimpleSchema({
Proficiencies.attachSchema(Schemas.Proficiency);
Proficiencies.attachBehaviour("softRemovable");
makeChild(Proficiencies);
makeChild(Proficiencies, ["enabled"]);
Proficiencies.allow(CHARACTER_SUBSCHEMA_ALLOW);
Proficiencies.deny(CHARACTER_SUBSCHEMA_DENY);

View File

@@ -1,7 +1,7 @@
SpellLists = new Mongo.Collection("spellLists");
Schemas.SpellLists = new SimpleSchema({
charId: {type: String, regEx: SimpleSchema.RegEx.Id},
charId: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
name: {type: String, trim: false},
description: {type: String, optional: true, trim: false},
saveDC: {type: String, optional: true, trim: false},

View File

@@ -1,7 +1,7 @@
Spells = new Mongo.Collection("spells");
Schemas.Spell = new SimpleSchema({
charId: {type: String, regEx: SimpleSchema.RegEx.Id},
charId: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
prepared: {
type: String,
defaultValue: "prepared",
@@ -62,6 +62,23 @@ Spells.attachSchema(Schemas.Spell);
Spells.attachBehaviour("softRemovable");
makeChild(Spells); //children of spell lists
makeParent(Spells, ["name", "enabled"]); //parents of attacks
Spells.after.update(function (userId, spell, fieldNames) {
//Update prepared state of spell and child attacks to be enabled or not
if (_.contains(fieldNames, "prepared")) {
var childAttacks = Attacks.find({"parent.id": spell._id}).fetch();
if (spell.prepared === "unprepared") {
_.each(childAttacks, function(attack){
Attacks.update(attack._id, {$set: {enabled: false}});
});
} else if (spell.prepared === "prepared" || "always") {
_.each(childAttacks, function(attack){
Attacks.update(attack._id, {$set: {enabled: true}});
});
}
}
});
Spells.allow(CHARACTER_SUBSCHEMA_ALLOW);
Spells.deny(CHARACTER_SUBSCHEMA_DENY);

View File

@@ -3,10 +3,6 @@
* Damage, healing and resource cost/recovery are all adjustments
*/
Schemas.Adjustment = new SimpleSchema({
name: {
type: String,
optional: true,
},
//which stat the adjustment is applied to
stat: {
type: String,

View File

@@ -1,11 +1,11 @@
TemporaryHitPoints = new Mongo.Collection("temporaryHitPoints");
Schemas.TemporaryHitPoints = new SimpleSchema({
charId: {type: String, regEx: SimpleSchema.RegEx.Id},
charId: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
name: {type: String, optional: true},
maximum: {type: Number, defaultValue: 0},
used: {type: Number, defaultValue: 0},
deleteOnZero:{type: Boolean, defaultValue: true},
maximum: {type: Number, defaultValue: 0, min: 0, max: 500},
used: {type: Number, defaultValue: 0, min: 0, max: 500},
deleteOnZero:{type: Boolean, defaultValue: false},
dateAdded: {
type: Date,
autoValue: function() {

View File

@@ -3,7 +3,7 @@ Containers = new Mongo.Collection("containers");
Schemas.Container = new SimpleSchema({
name: {type: String, trim: false},
charId: {type: String, regEx: SimpleSchema.RegEx.Id},
charId: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
isCarried: {type: Boolean},
weight: {type: Number, min: 0, defaultValue: 0, decimal: true},
value: {type: Number, min: 0, defaultValue: 0, decimal: true},

View File

@@ -4,7 +4,7 @@ Schemas.Item = new SimpleSchema({
name: {type: String, defaultValue: "New Item", trim: false},
plural: {type: String, optional: true, trim: false},
description:{type: String, optional: true, trim: false},
charId: {type: String, regEx: SimpleSchema.RegEx.Id}, //id of owner
charId: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1}, //id of owner
quantity: {type: Number, min: 0, defaultValue: 1},
weight: {type: Number, min: 0, defaultValue: 0, decimal: true},
value: {type: Number, min: 0, defaultValue: 0, decimal: true},
@@ -228,6 +228,9 @@ Characters.after.insert(function(userId, char) {
id: containerId,
collection: "Containers",
},
settings: {
showIncrement: true,
},
});
Items.insert({
name: "Silver piece",
@@ -241,6 +244,9 @@ Characters.after.insert(function(userId, char) {
id: containerId,
collection: "Containers",
},
settings: {
showIncrement: true,
},
});
Items.insert({
name: "Copper piece",
@@ -254,6 +260,9 @@ Characters.after.insert(function(userId, char) {
id: containerId,
collection: "Containers",
},
settings: {
showIncrement: true,
},
});
}
});

View File

@@ -17,8 +17,8 @@ Schemas.Report = new SimpleSchema({
},
type: {
type: String,
allowedValues: ["bug", "change", "feature", "general"],
defaultValue: "bug",
allowedValues: ["General Feedback", "Bug", "Suggested Change", "Feature Request"],
defaultValue: "General Feedback",
},
//the immediate impact of doing this action (eg. -1 rages)
severity: {
@@ -45,7 +45,26 @@ Meteor.methods({
metaData: Object,
});
report.owner = this.userId;
Reports.insert(report);
var id = Reports.insert(report);
var user = Meteor.users.findOne(this.userId);
var sender = user &&
user.emails &&
user.emails[0] &&
user.emails[0].address ||
user.services &&
user.services.google &&
user.services.google.email ||
"reports@dicecloud.com";
var bodyText = "Report ID: " + id +
"\nSeverity: " + report.severity +
"\nType: " + report.type +
"\n\n" + report.description;
Email.send({
from: sender,
to: "stefan.zermatten@gmail.com",
subject: "DiceCloud feedback - " + report.title,
text: bodyText,
});
},
deleteReport: function(id) {
var user = Meteor.users.findOne(this.userId);

View File

@@ -1,20 +1,13 @@
Router.configure({
loadingTemplate: "loading",
layoutTemplate: "layout",
trackPageView: true,
});
Router.plugin("ensureSignedIn", {
except: [
"home",
"atSignIn",
"atSignUp",
"atForgotPassword",
"atResetPwd",
"atEnrollAccount",
"atVerifyEmail",
"atResendVerificationEmail",
"loginButtons",
"notFound",
only: [
"profile",
"characterList",
]
});
@@ -31,23 +24,19 @@ Router.map(function() {
this.route("characterList", {
path: "/characterList",
waitOn: function(){
return Meteor.subscribe("characterList", Meteor.userId());
},
data: {
characters: function(){
return Characters.find({}, {fields: {_id: 1}});
}
return subsManager.subscribe("characterList", Meteor.userId());
},
onAfterAction: function() {
document.title = appName;
document.title = appName + " - Characters";
},
fastRender: true,
});
this.route("characterSheet", {
path: "/character/:_id",
waitOn: function(){
return [
Meteor.subscribe("singleCharacter", this.params._id, Meteor.userId()),
subsManager.subscribe("singleCharacter", this.params._id, Meteor.userId()),
];
},
data: function() {
@@ -64,6 +53,13 @@ Router.map(function() {
document.title = name;
}
},
//analytics
trackPageView: false,
onRun: function() {
window.ga && window.ga("send", "pageview", "/character");
this.next();
},
fastRender: true,
});
this.route("loading", {
@@ -81,7 +77,7 @@ Router.map(function() {
name: "changeLog",
waitOn: function() {
return [
Meteor.subscribe("changeLog"),
subsManager.subscribe("changeLog"),
]
},
data: {
@@ -92,5 +88,13 @@ Router.map(function() {
onAfterAction: function() {
document.title = appName;
},
fastRender: true,
});
this.route("/guide", {
name: "guide",
onAfterAction: function() {
document.title = appName;
},
});
});

View File

@@ -2,7 +2,9 @@
"name": "RPG Docs",
"version": "0.0.0",
"homepage": "",
"authors": ["Stefan Zermatten"],
"authors": [
"Stefan Zermatten"
],
"license": "none",
"private": true,
"ignore": [
@@ -13,13 +15,16 @@
"tests"
],
"dependencies": {
"polymer": "Polymer/polymer#~0.5.5",
"core-elements": "Polymer/core-elements#~0.5.5",
"paper-elements": "Polymer/paper-elements#~0.5.5"
"polymer": "Polymer/polymer#^1.0.0",
"iron-elements": "PolymerElements/iron-elements#^1.0.0",
"platinum-elements": "PolymerElements/platinum-elements#^1.0.1",
"neon-elements": "PolymerElements/neon-elements#^1.0.0",
"paper-elements": "PolymerElements/paper-elements#^1.0.7",
"app-elements": "PolymerElements/app-elements#^0.10.1",
"marked-element": "PolymerElements/marked-element#^1.2.0",
"paper-swatch-picker": "PolymerElements/paper-swatch-picker#~1.0.2"
},
"resolutions": {
"core-component-page": "^0.5.0",
"polymer": "^0.5.0",
"webcomponentsjs": "^0.5.0"
"webcomponentsjs": "0.7.24"
}
}
}

View File

@@ -1,14 +1,17 @@
this.GlobalUI = (function() {
function GlobalUI() {}
GlobalUI.dialog = {};
var toast;
GlobalUI.toast = function(opts) {
var toast;
toast = $("[global-toast]")[0];
toast.text = opts.text;
Session.set("global.ui.toastTemplate", opts.template);
Session.set("global.ui.toastData", opts.data);
if (!toast) toast = $("#global-toast")[0];
if (_.isObject(opts)){
toast.text = opts.text;
Session.set("global.ui.toastTemplate", opts.template);
Session.set("global.ui.toastData", opts.data);
} else {
toast.text = opts;
Session.set("global.ui.toastTemplate");
Session.set("global.ui.toastData");
}
return toast.show();
};
@@ -18,147 +21,18 @@ this.GlobalUI = (function() {
template: "undoToast",
data: {
id: id,
collection: collection
}
collection: collection,
},
});
};
GlobalUI.setDialog = function(opts) {
this.dialog = $("[global-dialog]")[0];
Session.set("global.ui.dialogHeader", opts.heading);
Session.set("global.ui.dialogData", opts.data);
Session.set("global.ui.dialogTemplate", opts.template);
Session.set("global.ui.dialogFullOnMobile", opts.fullOnMobile !== null);
};
GlobalUI.showDialog = function(opts) {
this.dialog = $("[global-dialog]")[0];
Session.set("global.ui.dialogHeader", opts.heading);
Session.set("global.ui.dialogData", opts.data);
Session.set("global.ui.dialogTemplate", opts.template);
Session.set("global.ui.dialogFullOnMobile", opts.fullOnMobile !== null);
return Tracker.afterFlush((function(_this) {
return function() {
return _this.dialog.open();
};
})(this));
};
GlobalUI.closeDialog = function() {
return this.dialog.close();
};
//To show a detail, first animate the click, with raising z-depth
//then call this function with a template and data
//the element should have a hero-id of detail-main
GlobalUI.showDetail = function(opts) {
Session.set("global.ui.detailData", opts.data);
Session.set("global.ui.detailTemplate", opts.template);
Session.set("global.ui.detailHeroId", opts.heroId);
Session.set("global.ui.detailShow", true);
};
//if setting the detail rather than showing it,
//the template should contain the following in template.rendered
//
//if (!this.alreadyRendered){
// Session.set("global.ui.detailShow", true);
// this.alreadyRendered = true;
//}
GlobalUI.setDetail = function(opts) {
Session.set("global.ui.detailData", opts.data);
Session.set("global.ui.detailTemplate", opts.template);
Session.set("global.ui.detailHeroId", opts.heroId);
if (window.history && window.history.pushState) {
history.replaceState({detail: "closed", opts: opts}, "Detail Dialog");
history.pushState({detail: "opened", opts: opts}, "Detail Dialog");
}
};
var throttleBack = _.throttle(function() {
history.back();
}, 800, {trailing: false});
GlobalUI.closeDetail = function() {
if (!!(window.history && window.history.pushState)) {
throttleBack();
} else {
Session.set("global.ui.detailShow", false);
}
};
GlobalUI.popStateHandler = function(e) {
var state = e.originalEvent.state;
if (state) {
if (state.detail === "closed") {
Session.set("global.ui.detailShow", false);
} else if (state.detail === "opened") {
var opts = state.opts;
Session.set("global.ui.detailData", opts.data);
Session.set("global.ui.detailTemplate", opts.template);
Session.set("global.ui.detailHeroId", opts.heroId);
}
}
};
return GlobalUI;
})();
Template.layout.helpers({
globalDialogTemplate: function() {
return Session.get("global.ui.dialogTemplate");
},
globalDialogData: function() {
return Session.get("global.ui.dialogData");
},
globalDialogFullOnMobile: function() {
return Session.get("global.ui.dialogFullOnMobile");
},
globalDialogHeader: function() {
return Session.get("global.ui.dialogHeader");
},
globalDetailSelected: function() {
return Session.get("global.ui.detailShow") ? 1 : 0;
},
globalDetailTemplate: function() {
return Session.get("global.ui.detailTemplate");
},
globalDetailData: function() {
return Session.get("global.ui.detailData");
},
globalToastTemplate: function() {
return Session.get("global.ui.toastTemplate");
},
globalToastData: function() {
return Session.get("global.ui.toastData");
}
});
Template.layout.events({
"core-overlay-close-completed [global-dialog]": function(e) {
Session.set("global.ui.dialogTemplate", null);
Session.set("global.ui.dialogData", null);
return Session.set("global.ui.dialogFullOnMobile", null);
},
"core-animated-pages-transition-end [detail-pages]": function(e) {
var detailOpened = Session.get("global.ui.detailShow");
if (!detailOpened) {
Session.set("global.ui.detailData", null);
Session.set("global.ui.detailTemplate", null);
Session.set("global.ui.detailHeroId", null);
}
},
"core-animated-pages-transition-prepare": function(e) {
var detailOpened = Session.get("global.ui.detailShow");
if (detailOpened) {
//set up the transition
} else {
//undo hack
$("#mainContentSection").removeClass("fake-selected");
}
},
"tap #screenDim": function(e) {
GlobalUI.closeDetail();
}
},
});

View File

@@ -1,6 +1,10 @@
Template.registerHelper("canEditCharacter", function(charId) {
return canEditCharacter(charId);
});
canEditCharacter = function(charId) {
var char = Characters.findOne(charId)
var userId = Meteor.userId();
return char.owner === userId ||
_.contains(char.writers, userId);
});
};

View File

@@ -23,3 +23,11 @@ Template.registerHelper("evaluateSignedSpaced", function(charId, string) {
Template.registerHelper("evaluateString", function(charId, string) {
return evaluateString(charId, string);
});
Template.registerHelper("evaluateShortString", function(charId, string) {
if (_.isString(string)){
return evaluateString(
charId, string.split(/^( *[-*_]){3,} *(?:\n+|$)/m)[0]
);
}
});

View File

@@ -1,27 +1,23 @@
openParentDialog = function(parent, charId, heroId) {
var detail;
openParentDialog = function({
parent, charId, element, returnElement, callback,
}) {
let template;
let data;
if (parent.collection === "Characters" && parent.group === "racial") {
detail = {
template: "raceDialog",
data: {charId: parent.id},
};
template = "raceDialog";
data = {charId: parent.id};
} else if (parent.collection === "Features") {
detail = {
template: "featureDialog",
data: {featureId: parent.id},
};
template = "featureDialog";
data = {featureId: parent.id};
} else if (parent.collection === "Classes") {
detail = {
template: "classDialog",
data: {classId: parent.id},
};
template = "classDialog";
data = {classId: parent.id};
} else if (parent.collection === "Items") {
detail = {
template: "itemDialog",
data: {itemId: parent.id},
};
template = "itemDialog";
data = {itemId: parent.id};
} else if (parent.collection === "Spells") {
template = "spellDialog";
data = {spellId: parent.id};
}
detail.heroId = heroId;
detail.charId = charId;
GlobalUI.setDetail(detail);
pushDialogStack({template, data, element, returnElement, callback});
};

View File

@@ -1,25 +1,27 @@
Template.registerHelper("valueString", function(value) {
var intValue = Math.round(value * 100);
var cp = intValue % 10;
intValue -= cp;
cp = Math.round(cp);
sp = intValue % 100;
intValue -= sp;
sp = Math.round(sp / 10)
gp = Math.floor(value);
var resultArray = [];
//sp
var gp = Math.floor(value);
if (gp > 0) {
resultArray.push(gp + "gp");
}
//sp
var sp = Math.floor(10 * (value % 1));
if (sp > 0) {
resultArray.push(sp + "sp");
}
//cp
var cp = 10 * ((value * 10) % 1);
cp = Math.round(cp * 1000) / 1000;
if (cp > 0) {
resultArray.push(cp + "cp");
}
//build string with correct spacing
var result = "";
for (var i = 0; i < resultArray.length; i++) {
for (var i = 0, l = resultArray.length; i < l; i++) {
//add a space between values
if (i !== 0) {
result += " ";

View File

@@ -0,0 +1,85 @@
/*Column layout*/
.column-container {
column-fill: balance;
column-gap: 0px;
column-width: 304px;
padding: 4px;
}
.column-container.thin-columns {
column-count: 4;
column-width: 240px;
}
.column-container > div {
padding: 4px;
-webkit-column-break-inside: avoid;
page-break-inside: avoid;
break-inside: avoid;
}
/*Cards*/
.card {
background: white;
border-radius: 2px;
}
.card .top {
cursor: pointer;
padding: 16px;
border-radius: 2px 2px 0 0;
}
.card .top.white {
cursor: auto;
padding: 16px;
border-bottom: rgba(0,0,0,0.12) solid 1px;
}
.card .bottom {
padding: 16px;
border-radius: 0 0 2px 2px;
}
.card .bottom.list {
padding: 16px 0;
}
.card .bottom.list .paper-font-subhead {
color: rgba(0,0,0,0.54);
font-size: 14px;
font-weight: 500;
letter-spacing: 0.010em;
padding: 12px 16px 12px 16px;
}
.card .bottom.text {
white-space: pre-wrap;
}
.card .left {
padding: 16px;
border-radius: 2px 0 0 2px;
text-align: center;
min-width: 72px;
}
.card .right {
padding: 16px;
border-radius: 0 2px 2px 0;
}
.card .left paper-icon-button {
display: block;
height: 32px;
padding: 0;
width: 32px;
}
.card .left paper-icon-button[disabled] {
color: rgba(255, 255, 255, 0.2);
}
.card img, .card iron-image {
max-width: 100%;
}

View File

@@ -1,96 +0,0 @@
@import "bourbon/bourbon";
$thickColumnWidth: 304px;
$thinColumnWidth: 240px;
//Column layouts of cards
.column-container {
@include column-fill(balance);
@include column-gap(8px);
@include column-width($thickColumnWidth);
padding: 8px;
&.thin-columns {
@include column-count(4);
@include column-width($thinColumnWidth);
}
}
//Cards
.card {
background: white;
border-radius: 2px;
.column-container & {
margin-bottom: 8px;
width: 100%;
//hack to stop flickering
-webkit-backface-visibility: hidden;
-webkit-transform: translateX(0);
//stop breaking over column divide
-webkit-column-break-inside: avoid;
page-break-inside: avoid;
break-inside: avoid;
//Fixes extra margin at top of columns
display: inline-block;
}
.top {
cursor: pointer;
padding: 16px;
border-radius: 2px 2px 0 0;
&.white {
cursor: auto;
padding: 16px;
border-bottom: rgba(0,0,0,0.12) solid 1px;
}
paper-checkbox::shadow #ink[checked] {
color: #ffffff;
}
paper-checkbox::shadow #ink {
color: #ffffff;
}
paper-checkbox::shadow #checkbox.checked {
background-color: #ffffff;
background-color: rgba(255,255,255,0.27);
border-color: #ffffff;
border-color: rgba(255,255,255,0.27);
}
paper-checkbox::shadow #checkbox {
border-color: #ffffff;
border-color: rgba(255,255,255,0.54);
}
}
.bottom {
padding: 16px;
border-radius: 0 0 2px 2px;
&.list {
padding: 0 0 16px 0;
.subhead {
color: rgba(0,0,0,0.54);
font-size: 14px;
font-weight: 500;
letter-spacing: 0.010em;
padding: 12px 16px 12px 16px;
}
}
&.text {
white-space: pre-wrap;
}
}
.left {
padding: 16px;
border-radius: 2px 0 0 2px;
text-align: center;
min-width: 72px;
}
.right {
padding: 16px;
border-radius: 0 2px 2px 0;
}
}
/* undo pointer cursor on detail box heading */
#globalDetail .card .top {
cursor: auto;
}

View File

@@ -74,10 +74,14 @@
background-color: #607D8B;
}
.app-grey {
background-color: #424242;
}
.white {
background-color: #ffffff;
}
.black {
background-color: #262626;
}
}

View File

@@ -0,0 +1,43 @@
/*
List items
*/
.item-slot {
background-color: rgb(232, 232, 232);
background-color: rgba(0, 0, 0, 0.1);
}
.item {
background: white;
cursor: pointer;
font-size: 16px;
height: 40px;
margin: 1px 0 1px 0;
padding: 0 16px 0 16px;
position: relative;
transition: box-shadow 0.3s ease, opacity 0.5s ease-in-out;
}
.item > .itemName {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.item.small {
height: 32px;
}
.item.tall {
height: 56px;
}
.item.flexible {
height: auto;
padding-top: 16px;
padding-bottom: 16px;
}
.item iron-icon, .item paper-icon-button {
color: #747474;
color: rgba(0,0,0,0.54);
}

View File

@@ -1,37 +0,0 @@
/*
List items
*/
.item-slot {
background-color: rgb(232, 232, 232);
background-color: rgba(0, 0, 0, 0.1);
}
.item {
background: white;
cursor: pointer;
font-size: 16px;
height: 40px;
margin: 1px 0 1px 0;
padding: 0 16px 0 16px;
position: relative;
transition: box-shadow 0.3s ease, opacity 0.5s ease-in-out;
&.small {
height: 32px;
}
&.tall {
height: 56px;
}
&.flexible {
height: auto;
padding-top: 16px;
padding-bottom: 16px;
}
&[hero], &:active{
box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.37);
z-index: 10;
}
core-icon, paper-icon-button {
color: #747474;
color: rgba(0,0,0,0.54);
}
}

View File

@@ -0,0 +1,115 @@
/*apply a natural box layout model to all elements*/
*, *:before, *:after {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
root {
display: block;
}
body {
font-family: 'Roboto', 'Helvetica Neue', Helvetica, Arial;
margin: 0;
overflow-x: hidden;
background-color: #E0E0E0;
}
/*Paragraphs*/
p {
margin-bottom: 8px;
}
//Horizontal rule
hr {
background-color: #444;
opacity: 0.12;
border-width: 0;
color: #444;
height: 1px;
line-height: 0;
margin: 16px 0;
text-align: center;
}
.avatar {
display: inline-block;
box-sizing: border-box;
width: 40px;
height: 40px;
border-radius: 50%;
font-size: 26px;
font-color: rgba(255, 255, 255, 0.58) !important;
}
/*
temporary fix for https://github.com/PolymerElements/paper-item/issues/71
*/
paper-icon-item::shadow #contentIcon {
flex-shrink: 0;
}
/*FABs*/
.floatyButton {
position: fixed;
bottom: 24px;
right: 24px;
/* stop the fab from flashing during animation */
transform: scale(1) translateZ(0px);
z-index: 3;
}
paper-fab {
background-color: #d13b2e;
}
paper-fab.keyboard-focus {
background: #630c05;
}
.red-button:not([disabled]) {
background: #d23f31;
color: #fff;
margin-top: 16px;
}
/*Buttons*/
paper-button {
color: #000;
color: rgba(0,0,0,0.87);
font-size: 14px;
font-weight: 400;
letter-spacing: 0.010;
text-transform: uppercase;
}
dicecloud-selector paper-item {
white-space: nowrap;
overflow: hidden;
}
/*Style shortcuts*/
.scroll-y {
overflow-y: auto;
}
.clickable, core-item, paper-tab {
cursor: pointer;
}
.pre-wrap, .prewrap{
white-space: pre-wrap;
}
.padded {
padding: 8px;
}
.fullwidth {
width: 100%;
}
.fab-buffer {
height: 100px;
}

View File

@@ -1,74 +0,0 @@
@import "bourbon/bourbon";
@import "colors";
//apply a natural box layout model to all elements
*, *:before, *:after {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
root {
display: block;
}
body {
font-family: 'Roboto', 'Helvetica Neue', Helvetica, Arial;
margin: 0;
overflow-x: hidden;
background-color: #E0E0E0;
}
//Horizontal rule
hr {
background-color: #444;
opacity: 0.12;
border-width: 0;
color: #444;
height: 1px;
line-height: 0;
margin: 16px -16px;
text-align: center;
}
//FABs
.floatyButton {
position: absolute;
bottom: 24px;
right: 24px;
}
//Buttons
paper-button {
color: #000;
color: rgba(0,0,0,0.87);
font-size: 14px;
font-weight: 400;
letter-spacing: 0.010;
text-transform: uppercase;
}
//Style shortcuts
.scroll-y {
overflow-y: auto;
}
.clickable, core-item, paper-tab {
cursor: pointer;
}
.pre-wrap, .prewrap{
white-space: pre-wrap;
}
.padded {
padding: 8px;
}
.fullwidth {
width: 100%;
}
.fab-buffer {
height: 100px;
}

View File

@@ -1,20 +0,0 @@
.card .left paper-icon-button {
display: block;
height: 32px;
padding: 0;
width: 32px;
}
.card .left paper-icon-button[disabled] {
color: rgba(255, 255, 255, 0.2);
}
.card .left paper-icon-button /deep/ core-icon {
height: 32px;
width: 32px;
}
/*fix paper-dropdowns*/
body /deep/ core-menu {
overflow-x: hidden !important;
}

View File

@@ -0,0 +1,15 @@
td {
padding: 8px;
}
.strengthTable{
width: 100%;
}
.strengthTable td:nth-child(2){
text-align: right;
}
.summaryTable td:nth-child(3){
text-align: right;
}

View File

@@ -1,18 +0,0 @@
td {
padding: 8px;
&:nth-child(1) {
min-width: 100px;
}
}
.strengthTable{
width: 100%;
td{
&:nth-child(2) {
text-align: right;
}
&:nth-child(3) {
width: 250px;
}
}
}

View File

@@ -1,68 +0,0 @@
#detailScreenFiller {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 2;
display: flex;
justify-content: center;
align-items: center;
}
#screenDim {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: grey;
background-color: rgba(0,0,0,0.35);
}
#fadeDummy {
pointer-events: none;
}
.detailContent {
padding: 24px;
}
html /deep/ .red-button:not([disabled]) {
background: #d23f31;
color: #fff;
margin-top: 16px;
}
#globalDetail {
background-color: white;
width: 624px;
height: 500px;
border-radius: 2px;
}
#globalDetail core-toolbar {
border-radius: 2px 2px 0 0;
}
@media (max-width: 640px) {
#globalDetail core-toolbar {
border-radius: 0;
}
}
@media (max-width: 640px) {
#globalDetail {
top: 0 !important;
left: 0 !important;
width: 100%;
height: 100%;
margin: 0;
border-radius: 0;
}
}
.not-selected [hero] {
visibility: hidden;
}

View File

@@ -1,113 +1,64 @@
.display2 {
font-size: 45px;
font-weight: 400;
color: #000;
color: rgba(0,0,0,0.54);
letter-spacing: 0;
.white-text {
color: #dedede;
color: rgba(255,255,255,0.87);
}
.white-text .display2{
.white-text .paper-font-display2{
color: #8a8a8a;
color: rgba(255,255,255,0.54);
}
.display1 {
font-size: 34px;
font-weight: 400;
color: #000;
color: rgba(0,0,0,0.54);
letter-spacing: 0;
}
.white-text .display1, .white-text.display1{
.white-text .paper-font-display1, .white-text.paper-font-display1{
color: #8a8a8a;
color: rgba(255,255,255,0.54);
}
h1, .headline {
font-size: 24px;
font-weight: 400;
margin: 0;
color: #000;
color: rgba(0,0,0,0.87);
letter-spacing: 0;
}
.white-text h1, .white-text .headline, .white-text.headline{
.white-text h1, .white-text .paper-font-headline, .white-text.paper-font-headline{
color: #dedede;
color: rgba(255,255,255,0.87);
}
h2, .title {
font-size: 20px;
font-weight: 500;
margin: 0;
color: #000;
color: rgba(0,0,0,0.87);
letter-spacing: 0.005em;
}
.white-text h2, .white-text .title, .white-text.title{
.white-text h2, .white-text .paper-font-title, .white-text.paper-font-title{
color: #dedede;
color: rgba(255,255,255,0.87);
}
h3, .subhead {
font-size: 16px;
font-weight: 400;
margin: 0;
color: #000;
color: rgba(0,0,0,0.87);
letter-spacing: 0.010em;
}
.white-text h3, .white-text .subhead{
.white-text h3, .white-text .paper-font-subhead{
color: #dedede;
color: rgba(255,255,255,0.87);
}
.body2 {
font-size: 14px;
font-weight: 500;
color: #000;
color: rgba(0,0,0,0.87);
letter-spacing: 0.010em;
}
.white-text .body2{
.white-text .paper-font-body2{
color: #dedede;
color: rgba(255,255,255,0.87);
}
p, .body1, body {
font-size: 14px;
font-weight: 400;
margin: 0;
color: #000;
color: rgba(0,0,0,0.87);
letter-spacing: 0.010em;
}
.white-text p, .white-text .body1{
.white-text p, .white-text .paper-font-body1{
color: #dedede;
color: rgba(255,255,255,0.87);
}
.caption{
font-size: 12px;
font-weight: 400;
color: #000;
color: rgba(0,0,0,0.54);
letter-spacing: 0.020em;
}
.white-text .caption{
.white-text .paper-font-caption{
color: #8a8a8a;
color: rgba(255,255,255,0.54);
}
html /deep/ .white-text{
color: #fff;
}
.black54 {
color: #444;
color: #757575;
color: rgba(0,0,0,0.54);
}
.white54 {
color: #eee;
color: #8a8a8a;
color: rgba(255,255,255,0.54);
}
.black87 {
color: #212121;
color: rgba(0,0,0,0.87);
}
.white87 {
color: #dedede;
color: rgba(255,255,255,0.87);
}

View File

@@ -1,39 +1,33 @@
<template name="attackEdit">
<div layout horizontal>
<div layout vertical flex>
<div layout horizontal>
<div class="layout horizontal">
<div class="layout vertical flex">
<div class="layout horizontal wrap">
<!--attackBonus-->
<paper-input id="attackBonusInput"
label="Attack Bonus"
floatinglabel
value={{attackBonus}}
flex></paper-input>
<paper-input class="attackBonusInput flex" label="Attack Bonus" value={{attackBonus}}
style="flex-basis: 200px; margin-right: 16px;">
{{> formulaSuffix}}
</paper-input>
<!--details-->
<paper-input id="detailInput"
label="Details"
floatinglabel
value={{details}}></paper-input>
<paper-input class="detailInput" label="Details" value={{details}} style="flex-basis: 200px;">
</paper-input>
</div>
<div layout horizontal>
<div class="layout horizontal wrap">
<!--damageBonus-->
<paper-input id="damageInput"
label="Damage"
floatinglabel
value={{damage}}
flex></paper-input>
<paper-input class="damageInput flex" label="Damage" value={{damage}}
style="flex-basis: 200px; margin-right: 16px;">
{{> bracketSuffix}}
</paper-input>
<!--DamageType-->
<paper-dropdown-menu id="damageTypeDropdown" label="Damage Type">
<paper-dropdown layered class="dropdown">
<core-menu class="menu" selected={{damageType}}>
{{#each damageTypes}}
<paper-item name={{this}} class="containerMenuItem">{{this}}</paper-item>
{{/each}}
</core-menu>
</paper-dropdown>
<paper-dropdown-menu label="Damage Type" dynamic-align>
<dicecloud-selector class="dropdown-content damageTypeDropdown" selected={{damageType}}>
{{#each damageTypes}}
<paper-item name={{this}} class="containerMenuItem">{{this}}</paper-item>
{{/each}}
</dicecloud-selector>
</paper-dropdown-menu>
</div>
</div>
<!--Delete Button-->
<paper-icon-button id="deleteAttack" role="button" tabindex="0" icon="delete" aria-label="Delete"></paper-icon-button>
<paper-icon-button class="deleteAttack" icon="delete"></paper-icon-button>
</div>
</template>
</template>

View File

@@ -15,25 +15,24 @@ var damageTypes = [
];
Template.attackEdit.events({
"tap #deleteAttack": function(event, instance) {
"click .deleteAttack": function(event, instance) {
Attacks.softRemoveNode(this._id);
GlobalUI.deletedToast(this._id, "Attacks", "Attack");
},
"change #attackBonusInput": function(event) {
"change .attackBonusInput": function(event) {
var value = event.currentTarget.value;
Attacks.update(this._id, {$set: {attackBonus: value}});
},
"change #damageInput": function(event) {
"change .damageInput": function(event) {
var value = event.currentTarget.value;
Attacks.update(this._id, {$set: {damage: value}});
},
"change #detailInput": function(event) {
"change .detailInput": function(event) {
var value = event.currentTarget.value;
Attacks.update(this._id, {$set: {details: value}});
},
"core-select #damageTypeDropdown": function(event) {
"iron-select .damageTypeDropdown": function(event) {
var detail = event.originalEvent.detail;
if (!detail.isSelected) return;
var value = detail.item.getAttribute("name");
if (value == this.damageType) return;
Attacks.update(this._id, {$set: {damageType: value}});
@@ -44,7 +43,4 @@ Template.attackEdit.helpers({
damageTypes: function() {
return damageTypes;
},
DAMAGE_DICE: function() {
return DAMAGE_DICE;
},
});

View File

@@ -3,7 +3,7 @@
{{#if attacks.count}}
<hr style="margin: 16px 0 16px 0;">
<div id="attacks">
<h2>Attacks</h2>
<div class="paper-font-title">Attacks</div>
{{#each attacks}}
{{>attackEdit}}
{{/each}}

View File

@@ -1,14 +1,14 @@
<template name="attackView">
<div class="attackView" layout horizontal>
<div class="headline" style="margin-right: 16px;" layout horizontal center>
<div class="attackView layout horizontal">
<div class="paper-font-headline layout horizontal center" style="margin-right: 16px;">
{{evaluateSigned charId attackBonus}}
</div>
<div layout vertical>
<div class="layout vertical">
<div>
{{evaluateString charId damage}}&nbsp;{{damageType}}
</div>
{{#if details}}
<div class="caption">
<div class="paper-font-caption">
{{details}}
</div>
{{/if}}

View File

@@ -2,10 +2,10 @@
{{#if attacks.count}}
<hr style="margin: 16px 0 16px 0;">
<div class="attacks">
<h2 class="spaceAfter">Attacks</h2>
<div class="spaceAfter paper-font-title">Attacks</div>
{{#each attacks}}
{{> attackView}}
{{/each}}
</div>
{{/if}}
</template>
</template>

View File

@@ -1,12 +1,23 @@
<template name="characterSettings">
{{#with character}}
<div>
<div layout horizontal>
<div>Use variant encumbrance </div>
<paper-toggle-button id="variantEncumbrance"
checked={{settings.useVariantEncumbrance}}
touch-action="pan-y">
</paper-toggle-button>
<div class="fit layout vertical">
<app-header-layout has-scrolling-region class="feedback flex">
<app-header fixed effects="waterfall">
<app-toolbar>
<div main-title>Character Settings</div>
</app-toolbar>
</app-header>
<div class="form flex">
<paper-toggle-button id="hideSpellcasting" checked={{settings.hideSpellcasting}}>
Hide Spells tab
</paper-toggle-button>
<paper-toggle-button id="variantEncumbrance" checked={{settings.useVariantEncumbrance}}>
Use variant encumbrance
</paper-toggle-button>
</div>
</app-header-layout>
<div class="buttons layout horizontal end-justified">
<paper-button class="doneButton"> Done </paper-button>
</div>
</div>
{{/with}}

View File

@@ -14,4 +14,16 @@ Template.characterSettings.events({
);
}
},
"change #hideSpellcasting": function(event, instance){
var value = instance.find("#hideSpellcasting").checked;
if (this.settings.hideSpellcasting !== value){
Characters.update(
this._id,
{$set: {"settings.hideSpellcasting": value}}
);
}
},
"click .doneButton": function(event, instance){
popDialogStack();
},
});

View File

@@ -1,9 +1,20 @@
<template name="deleteCharacterConfirmation">
<div>
Deleting a character cannot be undone.<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="deleteButton" style={{getStyle}} disabled={{cantDelete}}>Delete Character</paper-button>
<div class="fit layout vertical">
<app-header-layout has-scrolling-region class="feedback flex">
<app-header fixed effects="waterfall">
<app-toolbar>
<div main-title>Delete Character</div>
</app-toolbar>
</app-header>
<div class="form flex">
Deleting a character cannot be undone.<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="deleteButton" style={{getStyle}} disabled={{cantDelete}}>Delete Character</paper-button>
</div>
</app-header-layout>
<div class="buttons layout horizontal end-justified">
<paper-button class="cancelButton"> Cancel </paper-button>
</div>
</div>
<paper-button id="cancelButton" affirmative> Cancel </paper-button>
</template>
</template>

View File

@@ -18,11 +18,14 @@ Template.deleteCharacterConfirmation.events({
var canDel = instance.find("#nameInput").value === this.name;
instance.canDelete.set(canDel);
},
"tap #deleteButton": function(event, instance) {
"click #deleteButton": function(event, instance) {
if (instance.find("#nameInput").value === this.name) {
GlobalUI.closeDialog();
Router.go("/");
popDialogStack();
Router.go("/characterList");
Characters.remove(this._id);
}
}
},
"click .cancelButton": function(event, instance){
popDialogStack();
},
});

View File

@@ -1,42 +1,65 @@
<template name="shareDialog">
<div style="width: 360px;">
<div>
{{#if readers.count}}
<div style="font-weight: 500;">
Can View
</div>
{{#each readers}}
<div layout horizontal center>
<div flex>{{username}}</div>
<paper-icon-button class="deleteShare" icon="delete"></paper-icon-button>
</div>
{{/each}}
{{/if}}
{{#if writers.count}}
<div style="font-weight: 500;">
Can Edit
</div>
{{#each writers}}
<div layout horizontal center>
<div flex>{{username}}</div>
<paper-icon-button class="deleteShare" icon="delete"></paper-icon-button>
</div>
{{/each}}
{{/if}}
<div class="fit layout vertical">
<app-header-layout has-scrolling-region class="feedback flex">
<app-header fixed effects="waterfall">
<app-toolbar>
<div main-title>Share</div>
</app-toolbar>
</app-header>
<div class="form flex">
<paper-dropdown-menu label="Who can view this character">
<dicecloud-selector class="visibilityDropdown dropdown-content" selected={{viewPermission}}>
<paper-item name="whitelist">Only people I share with</paper-item>
<paper-item name="public">Anyone with link</paper-item>
</dicecloud-selector>
</paper-dropdown-menu>
<div class="layout horizontal center wrap">
<paper-input class="flex" id="userNameOrEmailInput" label="Share with username or email" floatinglabel></paper-input>
<paper-button id="shareButton"
class="red-button"
style="width: 80px; height: 37px; margin-top: 16px;"
raised
disabled={{shareButtonDisabled}}>Share</paper-button>
<paper-radio-group id="accessLevelMenu" selected="read">
<paper-radio-button name="read">View Only</paper-radio-button>
<paper-radio-button name="write">Can Edit</paper-radio-button>
</paper-radio-group>
</div>
<p style="color: red;">{{userFindError}}</p>
<div>
{{#if readers.length}}
<div class="paper-font-subhead">
Can View
</div>
{{#each id in readers}}
<div class="layout horizontal center">
{{#with id=id}}
<paper-icon-button class="deleteShare" icon="delete">
</paper-icon-button>
{{/with}}
<div class="flex">{{username id}}</div>
</div>
{{/each}}
{{/if}}
{{#if writers.length}}
<div class="paper-font-subhead">
Can Edit
</div>
{{#each id in writers}}
<div class="layout horizontal center">
{{#with id=id}}
<paper-icon-button class="deleteShare" icon="delete">
</paper-icon-button>
{{/with}}
<div class="flex">{{username id}}</div>
</div>
{{/each}}
{{/if}}
</div>
</div>
</app-header-layout>
<div class="buttons layout horizontal end-justified">
<paper-button class="doneButton"> Done </paper-button>
</div>
<div layout horizontal center>
<paper-input flex id="userNameOrEmailInput" label="Username or email" floatinglabel></paper-input>
<paper-button id="shareButton"
class="red-button"
style="width: 80px; height: 37px; margin-top: 16px;"
raised
disabled={{shareButtonDisabled}}>Share</paper-button>
</div>
<p style="color: red;">{{userFindError}}</p>
<paper-radio-group id="accessLevelMenu" selected="read">
<paper-radio-button name="read" label="View Only"></paper-radio-button>
<paper-radio-button name="write" label="Can Edit"></paper-radio-button>
</paper-radio-group>
</div>
<paper-button id="doneButton" affirmative> Done </paper-button>
</template>

View File

@@ -1,15 +1,38 @@
Template.shareDialog.onCreated(function(){
this.userId = new ReactiveVar();
this.autorun(() => {
var char = Characters.findOne(Template.currentData()._id, {
fields: {readers: 1, writers: 1}
});
if (!char) return;
this.subscribe("userNames", _.union(char.readers, char.writers));
});
});
Template.shareDialog.onRendered(function(){
// Polymer not mirroring selected attribute properly
this.$("paper-listbox").each(function(){
this.selected = this.getAttribute("selected");
});
});
Template.shareDialog.helpers({
viewPermission: function() {
var char = Characters.findOne(this._id, {fields: {settings: 1}});
return char.settings.viewPermission || "whitelist";
},
readers: function(){
var char = Characters.findOne(this._id, {fields: {readers: 1}});
return Meteor.users.find({_id: {$in: char.readers}});
return char.readers;
},
writers: function(){
var char = Characters.findOne(this._id, {fields: {writers: 1}});
return Meteor.users.find({_id: {$in: char.writers}});
//Meteor.users.find({_id: {$in: char.writers}});
return char.writers
},
username: function(id){
const user = Meteor.users.findOne(id);
return user && user.username || "user: " + id;
},
shareButtonDisabled: function(){
return !Template.instance().userId.get();
@@ -22,6 +45,13 @@ Template.shareDialog.helpers({
});
Template.shareDialog.events({
"iron-select .visibilityDropdown": function(event){
var detail = event.originalEvent.detail;
var value = detail.item.getAttribute("name");
var char = Characters.findOne(this._id, {fields: {settings: 1}});
if (value == char.settings.viewPermission) return;
Characters.update(this._id, {$set: {"settings.viewPermission": value}});
},
"input #userNameOrEmailInput":
function(event, instance){
var userName = instance.find("#userNameOrEmailInput").value;
@@ -35,7 +65,7 @@ Template.shareDialog.events({
}
});
},
"tap #shareButton": function(event, instance){
"click #shareButton": function(event, instance){
var self = this;
var permission = instance.find("#accessLevelMenu").selected;
if (!permission) throw "no permission set";
@@ -53,9 +83,12 @@ Template.shareDialog.events({
});
}
},
"tap .deleteShare": function(event, instance) {
"click .deleteShare": function(event, instance) {
Characters.update(instance.data._id, {
$pull: {writers: this._id, readers: this._id}
$pull: {writers: this.id, readers: this.id}
});
},
"click .doneButton": function(event, instance){
popDialogStack();
},
});

View File

@@ -1,28 +1,24 @@
paper-tabs, core-toolbar {
box-shadow: 0px 3px 2px rgba(0, 0, 0, 0.2);
}
core-toolbar paper-tabs {
box-shadow: none;
}
paper-tabs[noink][nobar] paper-tab.core-selected {
color: #fff;
}
paper-tabs /deep/ #selectionBar, #onRadio {
background-color: #d50000;
}
paper-tabs ::shadow #ink {
color: #b22 !important;
}
paper-tabs.transparent-brown {
background-color: transparent;
box-shadow: none;
}
core-toolbar.medium-tall {
app-toolbar.medium-tall {
height: 108px;
}
.character-name {
padding-left: 16px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.character-menu paper-icon-item{
cursor: pointer;
}
.character-sheet app-header {
position: relative;
z-index: 1;
}
.character-sheet iron-pages > div {
overflow-y: auto;
overflow-x: hidden;
}

View File

@@ -1,50 +1,58 @@
<template name="characterSheet">
<!--<div tool horizontal layout flex end-justified class="bottom">Title-bottom</div>-->
<core-toolbar class="medium-tall {{colorClass}}">
<core-icon-button icon="menu" core-drawer-toggle></core-icon-button>
<div flex>
{{name}}
<div class="fit layout vertical character-sheet">
<app-header fixed effects="waterfall">
<app-toolbar class="medium-tall {{colorClass}}">
<div top-item class="layout horizontal center">
<paper-icon-button icon="menu" drawer-toggle></paper-icon-button>
<div class="flex character-name">
{{name}}
</div>
{{#if canEditCharacter _id}}
{{> colorDropdown}}
<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="deleteCharacter">
<iron-icon icon="delete" item-icon></iron-icon>
Delete
</paper-icon-item>
<paper-icon-item id="shareCharacter">
<iron-icon icon="social:share" item-icon></iron-icon>
Share
</paper-icon-item>
<paper-icon-item id="characterSettings">
<iron-icon icon="settings" item-icon></iron-icon>
Settings
</paper-icon-item>
</paper-menu>
</paper-menu-button>
{{/if}}
</div>
<div bottom-item>
<paper-tabs id="characterSheetTabs" selected={{selectedTab}} class="{{colorClass}}">
<paper-tab name="stats">Stats</paper-tab>
<paper-tab name="features">Features</paper-tab>
<paper-tab name="inventory">Inventory</paper-tab>
{{#unless hideSpellcasting}}
<paper-tab name="spells">Spells</paper-tab>
{{/unless}}
<paper-tab name="persona">Persona</paper-tab>
<paper-tab name="journal">Journal</paper-tab>
</paper-tabs>
</div>
</app-toolbar>
</app-header>
<div class="flex" style="position: relative;">
<iron-pages id="tabPages" class="fit" selected={{selectedTab}}>
<div name="stats" class="tab-page fit">{{> stats}}</div>
<div name="features" class="tab-page fit">{{> features}}</div>
<div name="inventory" class="tab-page fit">{{> inventory}}</div>
{{#unless hideSpellcasting}}
<div name="spells" class="tab-page fit">{{> spells}}</div>
{{/unless}}
<div name="persona" class="tab-page fit">{{> persona}}</div>
<div name="journal" class="tab-page fit">{{> journal}}</div>
</iron-pages>
</div>
{{#if canEditCharacter _id}}
<div>
{{> colorDropdown}}
</div>
<paper-menu-button>
<paper-icon-button icon="more-vert" noink></paper-icon-button>
<paper-dropdown class="dropdown" halign="right">
<core-menu class="menu" style="color: black; color: rgba(0,0,0,0.87);">
<paper-item id="deleteCharacter">
<core-icon icon="delete"></core-icon>Delete
</paper-item>
<paper-item id="shareCharacter">
<core-icon icon="social:share"></core-icon>Share
</paper-item>
<paper-item id="characterSettings">
<core-icon icon="settings"></core-icon>Settings
</paper-item>
</core-menu>
</paper-dropdown>
</paper-menu-button>
{{/if}}
<div class="bottom fit" horizontal layout>
<paper-tabs flex horizontal center layout id="characterSheetTabs" selected={{selectedTab}} class="{{colorClass}}">
<paper-tab name="stats">Stats</paper-tab>
<paper-tab name="features">Features</paper-tab>
<paper-tab name="inventory">Inventory</paper-tab>
<paper-tab name="spells">Spells</paper-tab>
<paper-tab name="persona">Persona</paper-tab>
<paper-tab name="journal">Journal</paper-tab>
</paper-tabs>
</div>
</core-toolbar>
<div fit>
<core-animated-pages id="tabPages" selected={{selectedTab}} transitions="slide-from-right" fit>
<section flex name="stats">{{> stats}}</section>
<section flex name="features">{{> features}}</section>
<section flex name="inventory">{{> inventory}}</section>
<section flex name="spells">{{> spells}}</section>
<section flex name="persona">{{> persona}}</section>
<section flex name="journal">{{> journal}}</section>
</core-animated-pages>
</div>
</template>

View File

@@ -1,11 +1,162 @@
Template.characterSheet.onCreated(function() {
//default to the first tab
Session.setDefault(this.data._id + ".selectedTab", "stats");
let tabPages, tabSliders, tabFabs, tabFabMenus;
Template.characterSheet.onRendered(function() {
//default to the stats tab
Session.setDefault(this.data._id + ".selectedTab", "0");
// Keep the header's scroll target up to date with the currently selected tab
let header;
this.autorun(() => {
const tab = getTab(Template.currentData()._id);
header = header || this.find("app-header");
if (!header) return;
Tracker.afterFlush(() => {
header.scrollTarget = this.find("#tabPages .iron-selected");
header._scrollHandler && header._scrollHandler();
});
});
_.defer(() => {
// Store all the tab page components for use in animations
tabPages = _.times(6, (n) =>
this.$(`.tab-page:nth-child(${n + 1})`)
);
tabSliders = _.times(6, (n) =>
tabPages[n].find(".animation-slider")
);
tabFabs = _.times(6, (n) =>
tabPages[n].find(".floatyButton")
);
tabFabMenus = _.times(6, (n) =>
tabPages[n].find(".mini-holder")
);
})
//watch this character and make sure their encumbrance is updated
trackEncumbranceConditions(this.data._id, this);
//trackEncumbranceConditions(this.data._id, this);
});
/**
* Page change animations that suck less than neon-animated-pages
*/
const tabAnimation = ({oldTab, newTab, duration}) => {
if (newTab === oldTab) return;
duration = duration || 400;
const delay = (element, f) => {
element.on(
"transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd",
(event) => {
if (event.target == event.currentTarget){
f();
$(this).off(event);
}
}
);
}
const isForward = newTab > oldTab;
const entryAnimation = {
before: {
transform: isForward ? "translateX(100%)" : "translateX(-100%)",
},
during: {
transition: `transform ${duration}ms ease`,
transform: "",
},
after: {
transition: "",
transform: "",
},
}
const exitAnimation = {
before: {
transform: "translateZ(0)",
},
during: {
transition: `transform ${duration}ms ease`,
transform: isForward ? "translateX(-100%)" : "translateX(100%)",
},
after: {
transition: "",
transform: "",
display: "",
},
}
const oldPage = tabPages[oldTab];
const newPage = tabPages[newTab];
if (oldPage.length && newPage.length){
oldPage[0].style.setProperty("display", "initial", "important");
oldPage.css({zIndex: "1"});
newPage.css({zIndex: "0"});
_.defer(() => {
oldPage.css({
transition: `z-index ${duration}ms linear`,
zIndex: "0",
});
newPage.css({
transition: `z-index ${duration}ms linear`,
zIndex: "1",
});
});
delay(oldPage, () => oldPage.css({
display: "",
transition: "",
zIndex: "",
}));
delay(newPage, () => newPage.css({
transition: "",
zIndex: "",
}));
}
const oldSlider = tabSliders[oldTab];
if (oldSlider.length){
oldSlider.css(exitAnimation.before);
_.defer(() => oldSlider.css(exitAnimation.during));
delay(oldSlider, () => oldSlider.css(exitAnimation.after));
}
const newSlider = tabSliders[newTab];
if (newSlider.length){
newSlider.css(entryAnimation.before);
_.defer(() => newSlider.css(entryAnimation.during));
delay(newSlider, () => newSlider.css(entryAnimation.after));
}
slideDown = ({element, reverse}) => {
element.css({
transform: reverse ? "translateY(80px)" : "",
});
const fraction = duration / 4;
_.defer(() => element.css({
transition: reverse ?
`transform ${fraction}ms ease-out ${duration - fraction}ms` :
`transform ${fraction}ms ease-in`,
transform: reverse ? "" : "translateY(80px)",
}));
delay(element, () => element.css({
transition: "",
}));
}
const oldFab = tabFabs[oldTab];
const newFab = tabFabs[newTab];
if (oldFab.length && !newFab.length){
slideDown({element: oldFab});
}
if (newFab.length && !oldFab.length){
slideDown({element: newFab, reverse: true});
}
if (newFab.length && oldFab.length){
newFab.css({transform: ""});
}
const oldFabMenu = tabFabMenus[oldTab];
if (oldFabMenu.length) {
Blaze.getView(oldFabMenu[0]).templateInstance().active.set(false);
}
}
var setTab = function(charId, tab){
tabAnimation({
oldTab: +Session.get(charId + ".selectedTab"),
newTab: +tab,
});
return Session.set(charId + ".selectedTab", tab);
};
@@ -17,37 +168,39 @@ Template.characterSheet.helpers({
selectedTab: function(){
return getTab(this._id);
},
hideSpellcasting: function() {
var char = Characters.findOne(this._id);
return char && char.settings.hideSpellcasting;
},
});
Template.characterSheet.events({
"core-animated-pages-transition-end #tabPages": function(event) {
event.stopPropagation();
},
"tap #characterSheetTabs paper-tab": function(event, instance){
setTab(this._id, event.currentTarget.getAttribute("name"));
"iron-select #characterSheetTabs": function(event, instance){
setTab(this._id, event.target.selected);
},
"color-change": function(event, instance){
console.log("character color change")
Characters.update(this._id, {$set: {color: event.color}});
},
"tap #deleteCharacter": function(event, instance){
GlobalUI.showDialog({
heading: "Delete " + this.name,
"click #deleteCharacter": function(event, instance){
pushDialogStack({
data: this,
template: "deleteCharacterConfirmation",
element: event.currentTarget.parentElement.parentElement,
});
},
"tap #shareCharacter": function(event, instance){
GlobalUI.showDialog({
heading: "Share " + this.name,
"click #shareCharacter": function(event, instance){
pushDialogStack({
data: this,
template: "shareDialog",
element: event.currentTarget.parentElement.parentElement,
});
},
"tap #characterSettings": function(event, instance){
GlobalUI.showDialog({
heading: this.name + " Settings",
"click #characterSettings": function(event, instance){
pushDialogStack({
data: this,
template: "characterSettings",
element: event.currentTarget.parentElement.parentElement,
});
},
});

View File

@@ -1,29 +1,15 @@
body /deep/ #statGroupDropDown {
width: 120px;
.effectEdit .operationDropDown {
width: 152px;
}
body /deep/ #statDropDown {
width: 120px;
.effectEdit .statDropDown {
width: 152px;
}
body /deep/ #operationDropDown {
width: 100px;
.effectEdit .damageMultiplierDropDown {
width: 152px;
}
body /deep/ #damageMultiplierDropDown {
width: 120px;
}
body /deep/ #proficiencyDropDown {
width: 120px;
}
html /deep/ .effectEdit paper-input {
position: relative;
bottom: 5px;
}
html /deep/ .effectEdit {
display: flex;
align-items: flex-end;
.effectEdit .deleteEffect {
flex-shrink: 0;
}

View File

@@ -1,54 +1,55 @@
<template name="effectEdit">
<div class="effectEdit" layout horizontal center>
<paper-dropdown-menu class="statGroupDropDown" label="Stat Group" flex>
<paper-dropdown layered class="dropdown">
<core-menu class="menu statGroupMenu" selected={{selectedStatGroup}}>
{{#each statGroups}}
<paper-item class="statGroupSelect" name={{this}}>{{this}}</paper-item>
<div class="effectEdit layout horizontal center">
<paper-dropdown-menu label="Stat" class="statDropDown" dynamic-align>
<dicecloud-selector class="statMenu dropdown-content" selected={{stat}} selectable="paper-item">
{{#each statGroups}}
<div style="font-weight: bold; margin-top: 16px; padding-left: 8px;">
{{this}}
</div>
{{#each stats}}
<paper-item name={{stat}}>{{name}}</paper-item>
{{/each}}
</core-menu>
</paper-dropdown>
{{/each}}
</dicecloud-selector>
</paper-dropdown-menu>
{{#if stats}}
<paper-dropdown-menu class="statDropDown" label="Stat" flex>
<paper-dropdown layered class="dropdown">
<core-menu class="menu statMenu" selected={{stat}} on-tap="onStatMenuTap">
{{#each stats}}
<paper-item name={{stat}}>{{name}}</paper-item>
{{/each}}
</core-menu>
</paper-dropdown>
</paper-dropdown-menu>
{{/if}}
{{#if operations}}
<paper-dropdown-menu class="operationDropDown" label="Operation" flex>
<paper-dropdown layered class="dropdown">
<core-menu class="menu operationMenu" selected={{operation}}>
{{#each operations}}
<paper-item name={{operation}}>{{name}}</paper-item>
{{/each}}
</core-menu>
</paper-dropdown>
<paper-dropdown-menu class="operationDropDown" label="Operation" dynamic-align>
<dicecloud-selector class="dropdown-content operationMenu" selected={{operation}}>
{{#each operations}}
<paper-item name={{operation}}>{{name}}</paper-item>
{{/each}}
</dicecloud-selector>
</paper-dropdown-menu>
{{/if}}
{{> Template.dynamic template=effectValueTemplate}}
<paper-icon-button class="deleteEffect" role="button" tabindex="0" icon="delete" aria-label="Delete"></paper-icon-button>
{{#if effectValueTemplate}}
{{> Template.dynamic template=effectValueTemplate}}
{{else}}
<div class="flex"></div>
{{/if}}
<paper-icon-button class="deleteEffect"
icon="delete">
</paper-icon-button>
<br>
</div>
</template>
<template name="regularEffectValue">
<paper-input class="effectValueInput" label="Value" floatinglabel value={{effectValue}} flex></paper-input>
<paper-input class="effectValueInput flex"
label="Value"
floatinglabel
value={{effectValue}}
style="flex-basis: 100px;">
</paper-input>
</template>
<template name="multiplierEffectValue">
<paper-dropdown-menu class="damageMultiplierDropDown" label="Damage Multiplier" flex>
<paper-dropdown layered class="dropdown">
<core-menu class="menu multiplierMenu" selected={{value}}>
<paper-dropdown-menu class="damageMultiplierDropDown" label="Damage Multiplier" dynamic-align>
<dicecloud-selector class="menu multiplierMenu dropdown-content"
selected={{value}}>
<paper-item name="0.5">Resistance</paper-item>
<paper-item name="2">Vulnerability</paper-item>
<paper-item name="0">Immunity</paper-item>
</core-menu>
</paper-dropdown>
</dicecloud-selector>
</paper-dropdown-menu>
<div class="flex"></div>
</template>

View File

@@ -1,4 +1,5 @@
//TODO add dexterity armor
// jscs:disable maximumLineLength
var stats = [
{stat: "strength", name: "Strength", group: "Ability Scores"},
{stat: "dexterity", name: "Dexterity", group: "Ability Scores"},
@@ -42,6 +43,7 @@ var stats = [
{stat: "rageDamage", name: "Rage Damage", group: "Stats"},
{stat: "expertiseDice", name: "Expertise Dice", group: "Stats"},
{stat: "superiorityDice", name: "Superiority Dice", group: "Stats"},
{stat: "carryMultiplier", name: "Carry Capacity Multiplier", group: "Stats"},
{stat: "level1SpellSlots", name: "level 1", group: "Spell Slots"},
{stat: "level2SpellSlots", name: "level 2", group: "Spell Slots"},
{stat: "level3SpellSlots", name: "level 3", group: "Spell Slots"},
@@ -67,9 +69,11 @@ var stats = [
{stat: "psychicMultiplier", name: "Psychic", group: "Weakness/Resistance"},
{stat: "radiantMultiplier", name: "Radiant", group: "Weakness/Resistance"},
{stat: "slashingMultiplier", name: "Slashing", group: "Weakness/Resistance"},
{stat: "thunderMultiplier", name: "Thunder", group: "Weakness/Resistance"}
{stat: "thunderMultiplier", name: "Thunder", group: "Weakness/Resistance"},
];
// jscs:enable maximumLineLength
var statsDict = _.indexBy(stats, "stat");
var statGroups = _.groupBy(stats, "group");
var statGroupNames = _.keys(statGroups);
@@ -79,7 +83,7 @@ var attributeOperations = [
{name: "Add", operation: "add"},
{name: "Multiply", operation: "mul"},
{name: "Min", operation: "min"},
{name: "Max", operation: "max"}
{name: "Max", operation: "max"},
];
var skillOperations = [
{name: "Add", operation: "add"},
@@ -90,27 +94,20 @@ var skillOperations = [
{name: "Disadvantage", operation: "disadvantage"},
{name: "Passive Bonus", operation: "passiveAdd"},
{name: "Automatically Fail", operation: "fail"},
{name: "Conditional Benefit", operation: "conditional"}
{name: "Conditional Benefit", operation: "conditional"},
];
Template.effectEdit.created = function(){
var statGroup = statsDict[this.data.stat] && statsDict[this.data.stat].group;
this.selectedStatGroup = new ReactiveVar(statGroup);
};
Template.effectEdit.helpers({
selectedStatGroup: function(){
return Template.instance().selectedStatGroup.get();
},
statGroups: function(){
return statGroupNames;
},
stats: function(){
var group = Template.instance().selectedStatGroup.get();
var group = this;
return statGroups[group];
},
operations: function(){
var group = Template.instance().selectedStatGroup.get();
var stat = statsDict[this.stat];
var group = stat && stat.group;
if (group === "Weakness/Resistance") return null;
if (group === "Saving Throws" || group === "Skills"){
return skillOperations;
@@ -120,7 +117,8 @@ Template.effectEdit.helpers({
},
effectValueTemplate: function(){
//resistance/vulnerability template
var group = Template.instance().selectedStatGroup.get();
var stat = statsDict[this.stat];
var group = stat && stat.group;
if (group === "Weakness/Resistance") return "multiplierEffectValue";
var op = this.operation;
@@ -130,7 +128,7 @@ Template.effectEdit.helpers({
//default template
return "regularEffectValue";
}
},
});
Template.regularEffectValue.helpers({
@@ -140,52 +138,30 @@ Template.regularEffectValue.helpers({
});
Template.effectEdit.events({
"tap .deleteEffect": function(event){
"click .deleteEffect": function(event){
Effects.softRemoveNode(this._id);
GlobalUI.deletedToast(this._id, "Effects", "Effect");
},
"core-select .statGroupDropDown": function(event, instance){
"iron-select .statDropDown": function(event){
var detail = event.originalEvent.detail;
if (!detail.isSelected) return;
var groupName = detail.item.getAttribute("name");
var oldName = Template.instance().selectedStatGroup.get();
if (oldName != groupName){
instance.selectedStatGroup.set(groupName);
if (groupName === "Weakness/Resistance"){
Effects.update(this._id, {$set: {
value: 0.5,
calculation: "",
operation: "mul"}, $unset: {stat: ""}});
} else {
Effects.update(this._id,
{$set: {operation: "add"},
$unset: {stat: "", value: "", calculation: ""}});
}
}
},
"core-select .statDropDown": function(event){
var detail = event.originalEvent.detail;
if (!detail.isSelected) return;
var statName = detail.item.getAttribute("name");
if (statName == this.stat) return;
Effects.update(this._id, {$set: {stat: statName}});
},
"core-select .operationDropDown": function(event){
"iron-select .operationDropDown": function(event){
var detail = event.originalEvent.detail;
if (!detail.isSelected) return;
var opName = detail.item.getAttribute("name");
if (opName == this.operation) return;
Effects.update(this._id, {$set: {operation: opName}});
},
"core-select .damageMultiplierDropDown": function(event){
"iron-select .damageMultiplierDropDown": function(event){
var detail = event.originalEvent.detail;
if (!detail.isSelected) return;
var value = +detail.item.getAttribute("name");
if (value == this.value) return;
Effects.update(this._id, {$set: {
value: value,
calculation: "",
operation: "mul"
operation: "mul",
}});
},
"change .effectValueInput": function(event){
@@ -198,5 +174,5 @@ Template.effectEdit.events({
if (this.calculation === value) return;
Effects.update(this._id, {$set: {value: "", calculation: value}});
}
}
},
});

View File

@@ -42,6 +42,7 @@ var stats = {
"rageDamage":{"name":"Rage Damage"},
"expertiseDice":{"name":"Expertise Dice"},
"superiorityDice":{"name":"Superiority Dice"},
"carryMultiplier": {"name": "Carry Capacity Multiplier"},
"level1SpellSlots":{"name":"level 1 Spell Slots"},
"level2SpellSlots":{"name":"level 2 Spell Slots"},
"level3SpellSlots":{"name":"level 3 Spell Slots"},
@@ -57,40 +58,40 @@ var stats = {
"d12HitDice":{"name":"d12 Hit Dice"},
"acidMultiplier":{"name":"Acid damage", "group": "Weakness/Resistance"},
"bludgeoningMultiplier":{
"name":"Bludgeoning damage", "group": "Weakness/Resistance"
"name":"Bludgeoning damage", "group": "Weakness/Resistance",
},
"coldMultiplier":{
"name":"Cold damage", "group": "Weakness/Resistance"
"name":"Cold damage", "group": "Weakness/Resistance",
},
"fireMultiplier":{
"name":"Fire damage", "group": "Weakness/Resistance"
"name":"Fire damage", "group": "Weakness/Resistance",
},
"forceMultiplier":{
"name":"Force damage", "group": "Weakness/Resistance"
"name":"Force damage", "group": "Weakness/Resistance",
},
"lightningMultiplier":{
"name":"Lightning damage", "group": "Weakness/Resistance"
"name":"Lightning damage", "group": "Weakness/Resistance",
},
"necroticMultiplier":{
"name":"Necrotic damage", "group": "Weakness/Resistance"
"name":"Necrotic damage", "group": "Weakness/Resistance",
},
"piercingMultiplier":{
"name":"Piercing damage", "group": "Weakness/Resistance"
"name":"Piercing damage", "group": "Weakness/Resistance",
},
"poisonMultiplier":{
"name":"Poison damage", "group": "Weakness/Resistance"
"name":"Poison damage", "group": "Weakness/Resistance",
},
"psychicMultiplier":{
"name":"Psychic damage", "group": "Weakness/Resistance"
"name":"Psychic damage", "group": "Weakness/Resistance",
},
"radiantMultiplier":{
"name":"Radiant damage", "group": "Weakness/Resistance"
"name":"Radiant damage", "group": "Weakness/Resistance",
},
"slashingMultiplier":{
"name":"Slashing damage", "group": "Weakness/Resistance"
"name":"Slashing damage", "group": "Weakness/Resistance",
},
"thunderMultiplier":{
"name":"Thunder damage", "group": "Weakness/Resistance"
"name":"Thunder damage", "group": "Weakness/Resistance",
},
};
@@ -98,7 +99,7 @@ var operations = {
base: {name: "Base Value: "},
proficiency: {name: "Proficiency"},
add: {name: "+"},
mul: {name: "x"},
mul: {name: "×"},
min: {name: "Min: "},
max: {name: "Max: "},
advantage: {name: "Advantage"},
@@ -110,8 +111,8 @@ var operations = {
Template.effectView.helpers({
sourceName: function(){
var id = this.parent.id;
if(!id) return;
switch(this.parent.collection){
if (!id) return;
switch (this.parent.collection){
case "Features":
return "Feature - " + Features.findOne(id, {fields: {name: 1}}).name;
case "Classes":
@@ -130,33 +131,39 @@ Template.effectView.helpers({
return stats[this.stat] && stats[this.stat].name || "No Stat";
},
operationName: function(){
if(this.operation === "proficiency" ||
if (this.operation === "proficiency" ||
this.operation === "conditional") return null;
if(stats[this.stat].group === "Weakness/Resistance") return null;
if(this.operation === "add" && evaluateEffect(this.charId, this) < 0) return null;
return operations[this.operation] && operations[this.operation].name || "No Operation";
if (stats[this.stat] && stats[this.stat].group === "Weakness/Resistance")
return null;
if (this.operation === "add" && evaluateEffect(this.charId, this) < 0)
return null;
return operations[this.operation] &&
operations[this.operation].name || "No Operation";
},
statValue: function(){
if(this.operation === "advantage" ||
if (this.operation === "advantage" ||
this.operation === "disadvantage" ||
this.operation === "fail"){
return null;
}
if(this.operation === "proficiency"){
if(this.value == 0.5 || this.calculation == 0.5) return "Half Proficiency";
if(this.value == 1 || this.calculation == 1) return "Proficiency";
if(this.value == 2 || this.calculation == 2) return "Double Proficiency";
if (this.operation === "proficiency"){
if (this.value == 0.5 || this.calculation == 0.5)
return "Half Proficiency";
if (this.value == 1 || this.calculation == 1)
return "Proficiency";
if (this.value == 2 || this.calculation == 2)
return "Double Proficiency";
}
if(this.operation === "conditional"){
if (this.operation === "conditional"){
return this.calculation || this.value;
}
if(stats[this.stat].group === "Weakness/Resistance"){
if(this.value === 0.5) return "Resistance";
if(this.value === 2) return "Vulnerability";
if(this.value === 0) return "Immunity";
if (stats[this.stat] && stats[this.stat].group === "Weakness/Resistance"){
if (this.value === 0.5) return "Resistance";
if (this.value === 2) return "Vulnerability";
if (this.value === 0) return "Immunity";
}
var value = evaluateEffect(this.charId, this);
if(_.isNumber(value)) return value;
if (_.isNumber(value)) return value;
return this.calculation || this.value;
}
},
});

View File

@@ -1,13 +1,12 @@
<!--needs to be given charId, parentId and parentCollection-->
<template name="effectsEditList">
{{#if effects.count}}
<hr class="vertMargin">
<div id="effects">
<h2>Effects</h2>
<div class="paper-font-title">Effects</div>
{{#each effects}}
{{>effectEdit}}
{{/each}}
</div>
{{/if}}
<paper-button id="addEffectButton" class="red-button" raised>Add Effect</paper-button>
</template>
</template>

View File

@@ -1,9 +1,8 @@
<!--needs to be given charId, (parentId or stat) and type-->
<template name="effectsViewList">
{{#if effects.count}}
<hr style="margin: 16px 0 16px 0;">
<div class="effects">
<h2 class="spaceAfter">Effects</h2>
<div class="spaceAfter paper-font-title">Effects</div>
<table class="wideTable">
{{#each effects}}
{{>effectView}}

View File

@@ -10,10 +10,9 @@
<template name="featureDetails">
{{#if or canEnable hasUses}}
<div layout horizontal center justified wrap>
<div class="layout horizontal center justified wrap">
{{#if canEnable}}
<div>enabled:</div>
<paper-checkbox class="sideMargin" checked={{enabled}}></paper-checkbox>
<paper-checkbox class="sideMargin" checked={{enabled}}>enabled</paper-checkbox>
{{/if}}
{{#if hasUses}}
<div class="subhead" style="margin-right: 16px">
@@ -21,7 +20,7 @@
</div>
{{/if}}
{{#if hasUses}}
<div layout horizontal>
<div class="layout horizontal">
<paper-button class="useFeature" disabled={{noUsesLeft}}>Use</paper-button>
<paper-button class="resetFeature" disabled={{usesFull}}>Reset</paper-button>
</div>
@@ -32,7 +31,7 @@
{{/if}}
{{#if description}}
<div class="pre-wrap">{{evaluateString charId description}}</div>
<div>{{#markdown}}{{evaluateString charId description}}{{/markdown}}</div>
{{/if}}
{{> effectsViewList charId=charId parentId=_id}}
@@ -41,44 +40,36 @@
<template name="featureEdit">
<!--name-->
<paper-input id="featureNameInput" class="fullwidth" label="Name" floatinglabel value={{name}}></paper-input>
<paper-input id="featureNameInput" class="fullwidth" label="Name" value={{name}}></paper-input>
<hr class="vertMargin">
<div layout horizontal center style="height: 60px;">
<paper-dropdown-menu id="enabledDropdown" label="Enable Feature">
<paper-dropdown layered class="dropdown">
<core-menu id="enabledMenu" class="menu" selected={{enabledSelection}} on-tap="onStatMenuTap">
<paper-item name="alwaysEnabled"> Always Enabled </paper-item>
<paper-item name="enabled"> Enabled </paper-item>
<paper-item name="disabled"> Disabled </paper-item>
</core-menu>
</paper-dropdown>
<div class="layout horizontal center wrap justified">
<paper-dropdown-menu class=flex label="Enable Feature" style="flex-basis: 150px; max-width: 200px;">
<dicecloud-selector selected={{enabledSelection}} class="dropdown-content enabled-dropdown">
<paper-item name="alwaysEnabled" style="width: 150px;">
Always Enabled
</paper-item>
<paper-item name="enabled">
Enabled
</paper-item>
<paper-item name="disabled">
Disabled
</paper-item>
</dicecloud-selector>
</paper-dropdown-menu>
<div layout horizontal center class="sideMargin">
<div>Limit Uses: </div>
<paper-toggle-button id="limitUseCheck"
class="sideMargin"
checked={{usesSet}}
role="button"
aria-pressed="false"
tabindex="0"
touch-action="pan-y">
</paper-toggle-button>
</div>
<paper-toggle-button id="limitUseCheck" checked={{usesSet}} style="margin: 0 16px; height: 62px;">
Limit uses
</paper-toggle-button>
{{#if usesSet}}
<paper-input flex id="usesInput" label="Uses" floatinglabel value={{uses}}></paper-input>
<paper-input id="usesInput" type="number" allowed-pattern="[0-9]" class="flex"
label="Uses" value={{uses}} style="flex-basis: 100px; max-width: 200px">
</paper-input>
{{else}}
<div class="flex" style="flex-basis: 100px; max-width: 200px"></div>
{{/if}}
</div>
<hr class="vertMargin">
<!--description-->
<paper-input-decorator label="Description" floatinglabel layout vertical>
<paper-autogrow-textarea>
<textarea id="featureDescriptionInput" placeholder aria-label="Description" value={{description}}></textarea>
</paper-autogrow-textarea>
</paper-input-decorator>
<paper-textarea label="Description" id="featureDescriptionInput" value={{description}}></paper-textarea>
{{> effectsEditList parentId=_id parentCollection="Features" charId=charId name=name enabled=enabled}}
{{> proficiencyEditList parentId=_id parentCollection="Features" charId=charId enabled=enabled}}

View File

@@ -11,7 +11,7 @@ Template.featureDialog.events({
"tap #deleteButton": function(event, instance){
Features.softRemoveNode(instance.data.featureId);
GlobalUI.deletedToast(instance.data.featureId, "Features", "Feature");
GlobalUI.closeDetail();
popDialogStack();
},
});
@@ -31,11 +31,11 @@ Template.featureDetails.helpers({
});
Template.featureDetails.events({
"tap .useFeature": function(event){
"click .useFeature": function(event){
var featureId = this._id;
Features.update(featureId, {$inc: {used: 1}});
},
"tap .resetFeature": function(event){
"click .resetFeature": function(event){
var featureId = this._id;
Features.update(featureId, {$set: {used: 0}});
},
@@ -46,10 +46,6 @@ Template.featureDetails.events({
},
});
Template.featureEdit.onRendered(function(){
updatePolymerInputs(this);
});
Template.featureEdit.helpers({
usesSet: function(){
return _.isString(this.uses);
@@ -67,32 +63,60 @@ Template.featureEdit.helpers({
},
});
const debounce = (f) => _.debounce(f, 300);
Template.featureEdit.events({
"change #featureNameInput": function(event){
var name = Template.instance().find("#featureNameInput").value;
Features.update(this._id, {$set: {name: name}});
},
"change #featureDescriptionInput": function(event){
var description = Template.instance().find("#featureDescriptionInput").value;
Features.update(this._id, {$set: {description: description}});
},
"change #limitUseCheck": function(event){
"input #featureNameInput": 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;
Features.update(this._id, {
$set: {name: name}
}, {
removeEmptyStrings: false,
trimStrings: false,
});
}
}),
"input #featureDescriptionInput": debounce(function(event){
var description = event.currentTarget.value;
Features.update(this._id, {
$set: {description: description}
}, {
removeEmptyStrings: false,
trimStrings: false,
});
}),
"change #limitUseCheck": debounce(function(event){
var currentUses = this.uses;
var featureId = this._id;
if (event.target.checked && !_.isString(currentUses)){
Features.update(featureId, {$set: {uses: ""}}, {removeEmptyStrings: false});
Features.update(featureId, {
$set: {uses: ""}
}, {
removeEmptyStrings: false
});
} else if (!event.target.checked && _.isString(currentUses)){
Features.update(featureId, {$unset: {uses: ""}});
Features.update(featureId, {
$unset: {uses: ""}
});
}
},
"change #usesInput, input #quantityInput": function(event){
var value = event.target.value;
}),
"input #usesInput, input #quantityInput": debounce(function(event){
var value = event.currentTarget.value;
var featureId = this._id;
Features.update(featureId, {$set: {uses: value}});
},
"core-select #enabledDropdown": function(event){
Features.update(featureId, {
$set: {uses: value}
}, {
removeEmptyStrings: false,
});
}),
"iron-select .enabled-dropdown": function(event){
var detail = event.originalEvent.detail;
if (!detail.isSelected) return;
var value = detail.item.getAttribute("name");
var setter;
if (value === "enabled"){

View File

@@ -2,14 +2,6 @@
cursor: pointer;
}
.features {
display: flex !important;
justify-content: center;
align-items: stretch;
flex-wrap: wrap;
padding: 4px;
}
.featureCardTop {
margin-bottom: 8px;
}
@@ -18,7 +10,7 @@
white-space: pre-line;
}
.resourceCards paper-shadow.healthCard {
.resourceCards paper-material.healthCard {
width: 100%;
}

View File

@@ -1,43 +1,41 @@
<template name="features">
<div fit>
<div class="scroll-y" fit>
<div class="column-container">
<!--expertiseDice-->
{{>resource name="expertiseDice" title="Expertise Dice" color="teal" char=this}}
<!--ki-->
{{>resource name="ki" title="Ki Points" color="teal" char=this}}
<!--rages-->
{{>resource name="rages" title="Rages" color="teal" char=this}}
<!--sorceryPoints-->
{{>resource name="sorceryPoints" title="Sorcery Points" color="teal" char=this}}
<!--superiorityDice-->
{{>resource name="superiorityDice" title="Superiority Dice" color="teal" char=this}}
<!--Attacks-->
<paper-shadow class="card">
<div class="features">
<div class="column-container animation-slider">
<!--expertiseDice-->
{{>resource name="expertiseDice" title="Expertise Dice" color="teal" char=this}}
<!--ki-->
{{>resource name="ki" title="Ki Points" color="teal" char=this}}
<!--rages-->
{{>resource name="rages" title="Rages" color="teal" char=this}}
<!--sorceryPoints-->
{{>resource name="sorceryPoints" title="Sorcery Points" color="teal" char=this}}
<!--superiorityDice-->
{{>resource name="superiorityDice" title="Superiority Dice" color="teal" char=this}}
<!--Attacks-->
<div>
<paper-material class="card">
<div class="top white">
Attacks
</div>
<div class="bottom list">
{{#each attacks}}
<div class="item-slot">
<div class="flexible attack item"
hero-id="main" {{detailHero}}>
<div layout horizontal>
<div class="headline"
style="margin-right: 16px;"
layout horizontal center>
<div class="flexible attack item">
<div class="layout horizontal">
<div class="paper-font-headline layout horizontal center"
style="margin-right: 16px;">
{{evaluateSigned ../_id attackBonus}}
</div>
<div flex layout vertical>
<div class="body2">
<div class="flex layout vertical">
<div class="paper-font-body2">
{{name}}
</div>
<div>
{{evaluateString ../_id damage}}&nbsp;{{damageType}}
</div>
{{#if details}}
<div class="caption">
<div>
{{details}}
</div>
{{/if}}
@@ -47,43 +45,44 @@
</div>
{{/each}}
</div>
</paper-shadow>
</paper-material>
</div>
<!--Proficiencies-->
<paper-shadow class="card">
<!--Proficiencies-->
<div>
<paper-material class="card">
<div class="white top">
Proficiencies
</div>
<div flex class="bottom list">
{{#if weaponProfs.count}}
<div class="subhead">Weapons</div>
<div class="paper-font-subhead">Weapons</div>
{{/if}}
{{#each weaponProfs}}
{{> proficiencyListItem}}
{{/each}}
{{#if armorProfs.count}}
<div class="subhead">Armor</div>
<div class="paper-font-subhead">Armor</div>
{{/if}}
{{#each armorProfs}}
{{> proficiencyListItem}}
{{/each}}
{{#if toolProfs.count}}
<div class="subhead">Tools</div>
<div class="paper-font-subhead">Tools</div>
{{/if}}
{{#each toolProfs}}
{{> proficiencyListItem}}
{{/each}}
</div>
</paper-shadow>
</paper-material>
</div>
<!--features-->
{{#each features}}
<paper-shadow class="card featureCard"
hero-id="main" {{detailHero}}>
<div class="top {{colorClass}} subhead"
layout horizontal
hero-id="toolbar" {{detailHero}}>
<div flex hero-id="title" {{detailHero}}>
<!--features-->
{{#each features}}
<div>
<paper-material class="card featureCard" data-id={{_id}}>
<div class="top {{colorClass}} paper-font-subhead layout horizontal">
<div class="flex">
{{name}}
</div>
{{#if hasUses}}
@@ -92,59 +91,54 @@
</div>
{{/if}}
{{#if canEnable}}
<core-tooltip label="Feature enabled"
position="left">
<paper-checkbox class="enabledCheckbox"
checked={{enabled}}>
<div>
<paper-checkbox class="enabledCheckbox"
checked={{enabled}}
disabled={{#unless canEditCharacter charId}}true{{/unless}}>
</paper-checkbox>
</core-tooltip>
<paper-tooltip position="left">Feature enabled</paper-tooltip>
</div>
{{/if}}
</div>
{{#if description}}
<div flex class="bottom text"
>{{evaluateString charId description}}</div>
<div class="bottom flex">
{{#markdown}}{{evaluateShortString charId description}}{{/markdown}}
</div>
{{/if}}
{{#if hasUses}}
<div layout horizontal center end-justified>
<paper-button class="useFeature"
<div class="layout horizontal center end-justified">
<paper-button class="useFeature"
disabled={{noUsesLeft}}>
Use
</paper-button>
<paper-button class="resetFeature"
<paper-button class="resetFeature"
disabled={{usesFull}}>
Reset
</paper-button>
</div>
{{/if}}
</paper-shadow>
{{/each}}
</div>
<div class="fab-buffer"></div>
</paper-material>
</div>
{{/each}}
</div>
{{#if canEditCharacter _id}}
<paper-fab id="addFeature"
class="floatyButton"
icon="add"
title="Add"
role="button"
tabindex="0"
aria-label="Add"
hero-id="main"></paper-fab>
<paper-fab id="addFeature"
class="floatyButton"
icon="add">
<paper-tooltip position="left">Add Feature</paper-tooltip>
</paper-fab>
{{/if}}
</div>
</template>
<template name="resource">
{{#if char.attributeBase name}}
<paper-shadow class="card"
hero-id="main" {{detailHero name char._id}}
layout horizontal>
<div class="left {{getColor}} display1 white-text"
hero-id="toolbar" {{detailHero name char._id}}
layout horizontal center>
{{#if characterCalculate "attributeBase" char._id name}}
<div>
<paper-material class="card layout horizontal">
<div class="left {{getColor}} paper-font-display1 white-text layout horizontal center">
<div style="margin-right: 8px;">
<paper-icon-button class="resourceUp"
icon="arrow-drop-up"
<paper-icon-button class="resourceUp"
icon="arrow-drop-up"
disabled={{cantIncrement}}>
</paper-icon-button>
<paper-icon-button class="resourceDown"
@@ -152,13 +146,13 @@
disabled={{cantDecrement}}>
</paper-icon-button>
</div>
<div>{{char.attributeValue name}}</div>
<div>{{characterCalculate "attributeValue" char._id name}}</div>
<!--<div>/{{char.attributeBase name}}</div>-->
</div>
<div class="right clickable"
flex layout horizontal center>
<div class="right clickable flex layout horizontal center">
{{title}}
</div>
</paper-shadow>
</paper-material>
</div>
{{/if}}
</template>

View File

@@ -7,10 +7,10 @@ Template.features.helpers({
return this.usesValue() > 0;
},
noUsesLeft: function(){
return this.usesLeft() <= 0;
return this.usesLeft() <= 0 || !canEditCharacter(this.charId);
},
usesFull: function(){
return this.usesLeft() >= this.usesValue();
return this.usesLeft() >= this.usesValue() || !canEditCharacter(this.charId);
},
colorClass: function(){
return getColorClass(this.color);
@@ -38,54 +38,45 @@ Template.features.helpers({
});
Template.features.events({
"tap #addFeature": function(event){
"click #addFeature": function(event, instance){
var featureId = Features.insert({
name: "New Feature",
charId: this._id,
enabled: true,
alwaysEnabled: true,
});
GlobalUI.setDetail({
pushDialogStack({
template: "featureDialog",
data: {featureId: featureId, charId: this._id, startEditing: true},
heroId: featureId,
element: event.currentTarget,
returnElement: () => instance.find(`.featureCard[data-id='${featureId}']`),
});
},
"tap #addAttackButton": function(event){
var charId = this._id;
Attacks.insert({
charId: charId
}, function(error, id){
if (!error){
GlobalUI.setDetail({
template: "attackDialog",
data: {attackId: id, charId: charId},
heroId: id,
});
}
});
},
"tap .featureCard .top": function(event){
"click .featureCard .top": function(event){
var featureId = this._id;
var charId = Template.parentData()._id;
GlobalUI.setDetail({
pushDialogStack({
template: "featureDialog",
data: {featureId: featureId, charId: charId},
heroId: featureId,
element: event.currentTarget.parentElement,
});
},
"tap .attack": function(event){
openParentDialog(this.parent, this.charId, this._id);
"click .attack": function(event){
openParentDialog({
parent: this.parent,
charId: this.charId,
element: event.currentTarget,
});
},
"tap .useFeature": function(event){
"click .useFeature": function(event){
var featureId = this._id;
Features.update(featureId, {$inc: {used: 1}});
},
"tap .resetFeature": function(event){
"click .resetFeature": function(event){
var featureId = this._id;
Features.update(featureId, {$set: {used: 0}});
},
"tap .enabledCheckbox": function(event){
"click .enabledCheckbox": function(event){
event.stopPropagation();
},
"change .enabledCheckbox": function(event){
@@ -96,16 +87,19 @@ Template.features.events({
Template.resource.helpers({
cantIncrement: function(){
var baseBigger = this.char.attributeValue(this.name) <
this.char.attributeBase(this.name);
return !baseBigger;
var value = Characters.calculate.attributeValue(this.char._id, this.name);
var base = Characters.calculate.attributeBase(this.char._id, this.name);
var baseBigger = value < base;
return !baseBigger || !canEditCharacter(this.char._id);
},
cantDecrement: function(){
var valuePositive = this.char.attributeValue(this.name) > 0;
return !valuePositive;
var value = Characters.calculate.attributeValue(this.char._id, this.name);
var valuePositive = value > 0;
return !valuePositive || !canEditCharacter(this.char._id);
},
getColor: function(){
if (this.char.attributeValue(this.name) > 0){
var value = Characters.calculate.attributeValue(this.char._id, this.name);
if (value > 0){
return this.color;
} else {
return "grey";
@@ -114,25 +108,28 @@ Template.resource.helpers({
});
Template.resource.events({
"tap .resourceUp": function(event){
if (this.char.attributeValue(this.name) < this.char.attributeBase(this.name)){
"click .resourceUp": function(event){
var value = Characters.calculate.attributeValue(this.char._id, this.name);
var base = Characters.calculate.attributeBase(this.char._id, this.name);
if (value < base){
var modifier = {$inc: {}};
modifier.$inc[this.name + ".adjustment"] = 1;
Characters.update(this.char._id, modifier, {validate: false});
}
},
"tap .resourceDown": function(event){
if (this.char.attributeValue(this.name) > 0){
"click .resourceDown": function(event){
var value = Characters.calculate.attributeValue(this.char._id, this.name);
if (value > 0){
var modifier = {$inc: {}};
modifier.$inc[this.name + ".adjustment"] = -1;
Characters.update(this.char._id, modifier, {validate: false});
}
},
"tap .right": function(event, instance) {
GlobalUI.setDetail({
"click .right": function(event, instance) {
pushDialogStack({
template: "attributeDialog",
data: {name: this.title, statName: this.name, charId: this.char._id},
heroId: this.char._id + this.name,
element: event.currentTarget.parentElement,
});
},
});

View File

@@ -0,0 +1,15 @@
.carryCapacityBar {
background-color: #7DC580;
background-color: rgba(255,255,255,0.27);
position: relative;
height: 4px;
}
.carryCapacityBar div{
height: 100%;
position: absolute;
}
.carryCapacityBar .tick {
border-right: solid 2px #E5E5E5;
border-right-color: rgba(255,255,255,0.54);
}

View File

@@ -13,8 +13,10 @@ var getFractionCarried = function(char) {
weight += item.totalWeight();
});
//get strength
var strength = char.attributeValue("strength");
var capacity = strength * 15;
var strength = Characters.calculate.attributeValue(char._id, "strength");
var carryMultiplier = Characters.calculate
.attributeValue(char._id, "carryMultiplier");
var capacity = strength * 15 * carryMultiplier;
return weight / capacity;
};
@@ -22,7 +24,7 @@ Template.carryCapacityBar.onCreated(function() {
var self = this;
self.carriedFraction = new ReactiveVar(0);
self.autorun(function() {
self.carriedFraction.set(getFractionCarried(self.data));
self.carriedFraction.set(getFractionCarried(Template.currentData()));
});
});

View File

@@ -1,14 +0,0 @@
.carryCapacityBar {
background-color: #7DC580;
background-color: rgba(255,255,255,0.27);
position: relative;
height: 4px;
div{
height: 100%;
position: absolute;
}
.tick {
border-right: solid 2px #E5E5E5;
border-right-color: rgba(255,255,255,0.54);
}
}

View File

@@ -1,17 +1,17 @@
<template name="carryDialog">
{{#baseDialog title="Weight Carried" class=color hideEdit=true}}
<div layout horizontal center-justified end>
<div class="display2">
<div class="layout horizontal center-justified end">
<div class="paper-font-display2">
{{round carriedWeight 1}}
</div>
<div class="display1">
<div class="paper-font-display1">
lbs
</div>
</div>
<hr class="vertMargin">
{{> carryCapacityTable}}
{{/baseDialog}}
</template>
</template>

View File

@@ -11,36 +11,30 @@
<template name="containerEdit">
<paper-input id="containerNameInput"
label="Name"
floatinglabel
value={{name}}></paper-input>
<div layout horizontal around-justified wrap>
<paper-input-decorator label="Weight" floatinglabel>
<input id="weightInput" type="number" value={{weight}}>
</paper-input-decorator>
<paper-input-decorator label="Value" floatinglabel>
<input id="valueInput" type="number" value={{value}}>
</paper-input-decorator>
<div class="layout horizontal around-justified wrap">
<paper-input id="weightInput" label="Weight" type="number" value={{weight}}>
</paper-input>
<paper-input id="valueInput" label="Value" type="number" value={{value}}>
</paper-input>
</div>
<hr class="vertMargin">
<paper-input-decorator label="Description" floatinglabel layout vertical>
<paper-autogrow-textarea>
<textarea id="containerDescriptionInput" placeholder aria-label="Description" value={{description}}></textarea>
</paper-autogrow-textarea>
</paper-input-decorator>
<paper-textarea label="Description" id="containerDescriptionInput" value={{description}}>
</paper-textarea>
</template>
<template name="containerView">
<div layout horizontal wrap center justified>
<div class="layout horizontal wrap center justified">
<table class="summaryTable fullwidth">
<tr><td>Container</td><td>{{weight}}lbs</td><td>{{longValueString value}}</td></tr>
<tr><td>Contents</td><td>{{contentsWeight}}lbs</td><td>{{longValueString contentsValue}}</td></tr>
<tr class="body2"><td>Total</td><td>{{totalWeight}}lbs</td><td>{{longValueString totalValue}}</td></tr>
<tr><td>Container</td><td>{{round weight}}lbs</td><td>{{longValueString value}}</td></tr>
<tr><td>Contents</td><td>{{round contentsWeight}}lbs</td><td>{{longValueString contentsValue}}</td></tr>
<tr class="paper-font-body2"><td>Total</td><td>{{round totalWeight}}lbs</td><td>{{longValueString totalValue}}</td></tr>
</table>
</div>
{{#if description}}
<hr class="vertMargin">
<div class="pre-wrap">{{evaluateString charId description}}</div>
<div>{{#markdown}}{{evaluateString charId description}}{{/markdown}}</div>
{{/if}}
</template>

View File

@@ -14,30 +14,44 @@ Template.containerDialog.events({
instance.data.containerId,
"Containers", "Container and contents"
);
GlobalUI.closeDetail();
popDialogStack();
},
});
Template.containerEdit.onRendered(function(){
updatePolymerInputs(this);
});
Template.containerEdit.events({
//TODO validate input (integer, non-negative, etc) for these inputs and give validation errors
"change #containerNameInput": function(event){
"input #containerNameInput": function(event){
var name = Template.instance().find("#containerNameInput").value;
Containers.update(this._id, {$set: {name: name}});
Containers.update(this._id, {
$set: {name: name}
}, {
removeEmptyStrings: false,
trimStrings: false,
});
},
"change #weightInput, input #weightInput": function(event){
var weight = +Template.instance().find("#weightInput").value;
Containers.update(this._id, {$set: {weight: weight}});
Containers.update(this._id, {
$set: {weight: weight}
}, {
removeEmptyStrings: false,
});
},
"change #valueInput, input #valueInput": function(event){
var value = +Template.instance().find("#valueInput").value;
Containers.update(this._id, {$set: {value: value}});
Containers.update(this._id, {
$set: {value: value}
}, {
removeEmptyStrings: false,
});
},
"change #containerDescriptionInput": function(event, instance){
"input #containerDescriptionInput": function(event, instance){
var description = instance.find("#containerDescriptionInput").value;
Containers.update(this._id, {$set: {description: description}});
Containers.update(this._id, {
$set: {description: description}
}, {
removeEmptyStrings: false,
trimStrings: false,
});
},
});

View File

@@ -1,162 +1,163 @@
<template name="inventory">
<div fit>
<div id="inventory" class="scroll-y" fit>
<div class="column-container">
<!--Net Worth-->
<paper-shadow class="card">
<div class="white top" layout horizontal center>
<div class="subhead" flex>
Net Worth
</div>
<div>
{{valueString netWorth}}
</div>
<div id="inventory" class="animation-slider">
<div class="column-container">
<!--Net Worth-->
<div>
<paper-material class="card">
<div class="white top layout horizontal center">
<div class="paper-font-subhead flex">
Net Worth
</div>
</paper-shadow>
<!--Weight Carried-->
<paper-shadow class="card"
hero-id="main" {{detailHero "weightCarried" _id}}>
<div class="top green white-text weightCarried"
hero-id="toolbar" {{detailHero "weightCarried" _id}}
layout horizontal center>
<div class="subhead" flex>
Weight Carried
</div>
<div>
{{round weightCarried}}lbs
</div>
<div>
{{valueString netWorth}}
</div>
<div class="bottom green" style="padding: 0;">
{{> carryCapacityBar}}
</div>
</paper-material>
</div>
<!--Weight Carried-->
<div>
<paper-material class="card">
<div class="top green white-text weightCarried layout horizontal center">
<div class="paper-font-subhead" flex>
Weight Carried
</div>
{{#if encumberedBuffs.count}}
<div class="bottom list">
{{#each encumberedBuffs}}
<div class="item-slot">
<div class="item buff"
hero-id="main" {{detailHero}}
layout horizontal center
draggable="true">
<div flex>
<core-icon icon="work"
style="margin-right: 16px">
</core-icon>
{{name}}
</div>
</div>
</div>
{{/each}}
<div>
{{round weightCarried}}lbs
</div>
{{/if}}
</paper-shadow>
<!--Equipment-->
<paper-shadow class="card equipmentContainer">
<div class="white top" layout horizontal center>
<div class="subhead" flex>
Equipment
</div>
<div class="caption" style="margin-right: 8px">
{{valueString equipmentValue}}
</div>
<div class="caption">
{{round equipmentWeight}}lbs
</div>
</div>
<div flex class="bottom list">
{{#if attuned.count}}
<div class="subhead">Attuned</div>
{{/if}}
{{#each attuned}}
{{>inventoryItem}}
{{/each}}
{{#if attuned.count}}
<div class="subhead">Equipment</div>
{{/if}}
{{#each equipment}}
{{>inventoryItem}}
{{/each}}
</div>
</paper-shadow>
<!--Carried Items-->
<paper-shadow class="card carriedContainer">
<div class="white top" layout horizontal center>
<div class="subhead" flex>
Carried
</div>
<div class="caption" style="margin-right: 8px">
{{valueString carriedValue}}
</div>
<div class="caption">
{{round carriedWeight}}lbs
</div>
</div>
<div flex class="bottom list">
{{#each carriedItems}}
{{>inventoryItem}}
{{/each}}
</div>
</paper-shadow>
{{#each containers}}
<paper-shadow class="card itemContainer"
hero-id="main" {{detailHero}}>
<div class="top {{colorClass}}"
hero-id="toolbar" {{detailHero}}
layout horizontal center>
<div class="subhead" flex
hero-id="title" {{detailHero}}>
</div>
<div class="bottom green" style="padding: 0;">
{{> carryCapacityBar}}
</div>
{{#if encumberedBuffs.count}}
<div class="bottom list">
{{#each encumberedBuffs}}
<div class="item-slot">
<div class="item buff layout horizontal center">
<div class="flex">
<iron-icon icon="work"
style="margin-right: 16px">
</iron-icon>
{{name}}
</div>
<div class="caption" style="margin-right: 8px">
{{valueString totalValue}}
</div>
<div class="caption" style="margin-right: 8px">
{{round totalWeight}}lbs
</div>
<core-tooltip label="Container carried" position="left">
<paper-checkbox class="carriedCheckbox"
checked={{isCarried}}>
</paper-checkbox>
</core-tooltip>
</div>
<div class="bottom list">
{{#each items ../_id _id}}
{{>inventoryItem}}
{{/each}}
</div>
</paper-shadow>
{{/each}}
</div>
{{/each}}
</div>
{{/if}}
</paper-material>
</div>
<div class="fab-buffer"></div>
<!--Equipment-->
<div>
<paper-material class="card equipmentContainer">
<div class="white top layout horizontal center">
<div class="paper-font-subhead flex">
Equipment
</div>
<div class="paper-font-caption" style="margin-right: 8px">
{{valueString equipmentValue}}
</div>
<div class="paper-font-caption">
{{round equipmentWeight}}lbs
</div>
</div>
<div flex class="bottom list">
{{#if attuned.count}}
<div class="paper-font-subhead">Attuned</div>
{{/if}}
{{#each attuned}}
{{>inventoryItem}}
{{/each}}
{{#if attuned.count}}
<div class="paper-font-subhead">Equipment</div>
{{/if}}
{{#each equipment}}
{{>inventoryItem}}
{{/each}}
</div>
</paper-material>
</div>
<!--Carried Items-->
<div>
<paper-material class="card carriedContainer">
<div class="white top layout horizontal center">
<div class="paper-font-subhead flex">
Carried
</div>
<div class="paper-font-caption" style="margin-right: 8px">
{{valueString carriedValue}}
</div>
<div class="paper-font-caption">
{{round carriedWeight}}lbs
</div>
</div>
<div flex class="bottom list">
{{#each carriedItems}}
{{>inventoryItem}}
{{/each}}
</div>
</paper-material>
</div>
{{#each containers}}
<div>
<paper-material class="card itemContainer" data-id={{_id}}>
<div class="top {{colorClass}} layout horizontal center">
<div class="paper-font-subhead flex">
{{name}}
</div>
<div class="paper-font-caption" style="margin-right: 8px">
{{valueString totalValue}}
</div>
<div class="paper-font-caption" style="margin-right: 8px">
{{round totalWeight}}lbs
</div>
<div>
<paper-checkbox class="carriedCheckbox"
disabled={{#unless canEditCharacter charId}}true{{/unless}}
checked={{isCarried}}>
</paper-checkbox>
<paper-tooltip position="left"> Container carried</paper-tooltip>
</div>
</div>
<div class="bottom list">
{{#each items ../_id _id}}
{{>inventoryItem}}
{{/each}}
</div>
</paper-material>
</div>
{{/each}}
</div>
{{#if canEditCharacter _id}}
{{#fabMenu}}
<core-tooltip label="New container" position="left">
<paper-fab icon="work"
class="addContainer"
mini>
</paper-fab>
</core-tooltip>
<core-tooltip label="New item" position="left">
<paper-fab icon="note-add"
class="addItem"
mini>
</paper-fab>
</core-tooltip>
{{/fabMenu}}
{{/if}}
<div class="fab-buffer"></div>
</div>
{{#if canEditCharacter _id}}
{{#fabMenu}}
<div>
<paper-fab icon="work"
class="addContainer"
mini>
</paper-fab>
<paper-tooltip position="left"> New container </paper-tooltip>
</div>
<div>
<paper-fab icon="note-add"
class="addItem"
mini>
</paper-fab>
<paper-tooltip position="left"> New item </paper-tooltip>
</div>
{{/fabMenu}}
{{/if}}
</template>
<template name="inventoryItem">
<div class="item-slot">
<div class="item {{hidden}} inventoryItem"
hero-id="main" {{detailHero}}
layout horizontal center
draggable="true">
<div flex class="itemName">
<div class="item {{hidden}} inventoryItem layout horizontal center"
draggable={{canEditCharacter charId}}
data-id={{_id}}>
<div class="itemName flex">
{{#if ne1 quantity}}{{quantity}}&nbsp;{{/if}}{{pluralName}}
</div>
{{#if settings.showIncrement}}
{{#if settings.showIncrement}}{{#if canEditCharacter charId}}
<div class="incrementButtons">
<paper-icon-button class="addItemQuantity"
icon="add"
@@ -166,7 +167,7 @@
icon="remove"
style="margin-right: -8px"></paper-icon-button>
</div>
{{/if}}
{{/if}}{{/if}}
</div>
</div>
</template>

View File

@@ -44,6 +44,12 @@ Template.inventory.helpers({
).forEach(function(item){
worth += item.totalValue();
});
Containers.find(
{charId: this._id},
{fields: {value : 1}}
).forEach(function(container) {
if (container.value) worth += container.value;
});
return worth;
},
weightCarried: function(){
@@ -85,9 +91,11 @@ Template.inventory.helpers({
},
equipmentWeight: function(){
var weight = 0;
Items.find({charId: this._id, enabled: true},
{fields: {weight : 1, quantity: 1}}
).forEach(function(item){
Items.find({
charId: this._id, enabled: true,
}, {
fields: {weight : 1, quantity: 1}
}).forEach(function(item){
weight += item.totalWeight();
});
return weight;
@@ -115,87 +123,88 @@ Template.inventory.helpers({
});
Template.inventory.events({
"tap .addItem": function(event){
"click .addItem": function(event, instance){
var charId = this._id;
Items.insert({
var itemId = Items.insert({
charId: charId,
parent:{
id: charId,
collection: "Characters",
},
}, function(err, itemId){
if (err) throw err;
GlobalUI.setDetail({
template: "itemDialog",
data: {itemId: itemId, charId: charId, startEditing: true},
heroId: itemId,
});
});
pushDialogStack({
template: "itemDialog",
data: {itemId: itemId, charId: charId, startEditing: true},
element: event.currentTarget,
returnElement: () => $(`[data-id='${itemId}']`).get(0),
});
},
"tap .addContainer": function(event){
"click .addContainer": function(event, instance){
var containerId = Containers.insert({
name: "New Container",
isCarried: true,
charId: this._id,
});
GlobalUI.setDetail({
pushDialogStack({
template: "containerDialog",
data: {
containerId: containerId,
charId: this.charId,
startEditing: true,
},
heroId: containerId,
element: event.currentTarget,
returnElement: instance.find(`.itemContainer[data-id='${containerId}']`),
});
},
"tap .weightCarried": function(event) {
"click .weightCarried": function(event, instance) {
var charId = this._id;
GlobalUI.setDetail({
pushDialogStack({
template: "carryDialog",
data: {charId: charId, color: "green"},
heroId: charId + "weightCarried",
element: event.currentTarget.parentElement,
});
},
"tap .buff": function(event){
"click .buff": function(event, instance){
var buffId = this._id;
var charId = Template.parentData()._id;
GlobalUI.setDetail({
pushDialogStack({
template: "buffDialog",
data: {buffId: buffId, charId: charId},
heroId: buffId,
element: event.currentTarget,
});
},
"tap .inventoryItem": function(event){
"click .inventoryItem": function(event, instance){
var itemId = this._id;
var charId = Template.parentData()._id;
GlobalUI.setDetail({
pushDialogStack({
template: "itemDialog",
data: {itemId: itemId, charId: charId},
heroId: itemId,
element: event.currentTarget,
returnElement: () => $(`[data-id='${itemId}']`).get(0),
});
},
"tap .incrementButtons": function(event) {
"click .incrementButtons": function(event, instance) {
event.stopPropagation();
},
"tap .addItemQuantity": function(event) {
"click .addItemQuantity": function(event, instance) {
var itemId = this._id;
Items.update(itemId, {$set: {quantity: this.quantity + 1}});
},
"tap .subItemQuantity": function(event) {
"click .subItemQuantity": function(event, instance) {
var itemId = this._id;
Items.update(itemId, {$set: {quantity: this.quantity - 1}});
},
"tap .itemContainer .top": function(event){
GlobalUI.setDetail({
"click .itemContainer .top": function(event, instance){
pushDialogStack({
template: "containerDialog",
data: {containerId: this._id, charId: this.charId},
heroId: this._id,
element: event.currentTarget.parentElement,
});
},
"tap .carriedCheckbox": function(event){
"click .carriedCheckbox": function(event, instance){
event.stopPropagation();
},
"change .carriedCheckbox": function(event){
"change .carriedCheckbox": function(event, instance){
var carried;
if (this.isCarried) carried = false;
else carried = true;
@@ -251,7 +260,7 @@ Template.layout.events({
var itemId = event.originalEvent.dataTransfer.getData("dicecloud-id/items");
if (event.ctrlKey){
//split the stack to the container
GlobalUI.showDialog({
pushDialogStack({
template: "splitStackDialog",
data: {
id: itemId,
@@ -274,12 +283,11 @@ Template.layout.events({
var itemId = event.originalEvent.dataTransfer.getData("dicecloud-id/items");
if (event.ctrlKey){
//split the stack to the container
GlobalUI.showDialog({
pushDialogStack({
template: "splitStackDialog",
data: {
id: itemId,
parentCollection: "Characters",
parentId: this._id,
},
});
} else {
@@ -292,7 +300,7 @@ Template.layout.events({
var itemId = event.originalEvent.dataTransfer.getData("dicecloud-id/items");
if (event.ctrlKey){
//split the stack to the container
GlobalUI.showDialog({
pushDialogStack({
template: "splitStackDialog",
data: {
id: itemId,

View File

@@ -9,91 +9,64 @@
</template>
<template name="itemDetails">
<div layout horizontal wrap center justified class="headline">
<div class="paper-font-headline layout horizontal wrap center justified">
{{#if weight}}<div class="sideMargin">{{round totalWeight}}lbs</div>{{/if}}
{{#if value}}<div>{{valueString totalValue}}</div>{{/if}}
</div>
<div layout horizontal wrap class="caption">
<div class="paper-font-caption layout horizontal wrap">
{{#if enabled}}<div class="vertMargin" style="margin-right: 16px">Equipped</div>{{/if}}
{{#if requiresAttunement}}<div class="vertMargin">Requires Attunement</div>{{/if}}
</div>
{{#if description}}
<hr class="vertMargin">
<div class="pre-wrap">{{evaluateString charId description}}</div>
<hr style="margin: 16px 0 16px 0;">
<div>{{#markdown}}{{evaluateString charId description}}{{/markdown}}</div>
{{/if}}
{{> effectsViewList charId=charId parentId=_id}}
{{> attacksViewList charId=charId parentId=_id}}
</template>
<template name="itemEdit">
<paper-input class="fullwidth" id="itemNameInput" label="Name" floatinglabel value={{name}}></paper-input>
<div layout horizontal wrap>
<paper-input-decorator label="Quantity"
floatinglabel
style="width: 80px">
<input id="quantityInput"
type="number"
value={{quantity}}>
</paper-input-decorator>
<paper-input class="fullwidth" id="itemNameInput" label="Name" value={{name}}></paper-input>
<div class="layout horizontal center wrap">
<paper-input id="quantityInput" type="number" label="Quantity" style="width: 80px" value={{quantity}}>
</paper-input>
{{# if ne1 quantity}}
<paper-input flex id="itemPluralInput"
label="Plural Name"
floatinglabel
value={{plural}}></paper-input>
<paper-input class="flex" id="itemPluralInput" label="Plural Name" value={{plural}} style="flex-basis: 182px;">
</paper-input>
{{else}}
<div class="flex" style="flex-basis: 182px;">
</div>
{{/if}}
</div>
<div center horizontal layout>
<div class="padded">Show increment buttons</div>
<paper-checkbox id="incrementCheckbox"
checked={{settings.showIncrement}}>
<paper-checkbox id="incrementCheckbox" checked={{settings.showIncrement}}>
Show increment buttons
</paper-checkbox>
</div>
<hr class="vertMargin">
<div layout horizontal wrap justified>
<div center horizontal layout>
<div class="padded">Container</div>
{{> containerDropdown}}
</div>
<div center horizontal layout>
<div class="padded">Equipped</div>
<paper-toggle-button id="equippedInput"
checked={{enabled}}
role="button"
aria-pressed="false"
tabindex="0"
touch-action="pan-y">
</paper-toggle-button>
</div>
<div center horizontal layout>
<div class="padded">Requires Attunement</div>
<paper-checkbox id="attunementCheckbox"
checked={{requiresAttunement}}>
</paper-checkbox>
</div>
<div class="layout horizontal center wrap justified" style="margin-top: 16px;">
{{> containerDropdown}}
<paper-toggle-button id="equippedInput" checked={{enabled}}>
Equipped
</paper-toggle-button>
<paper-checkbox id="attunementCheckbox" checked={{requiresAttunement}}>
Requires Attunement
</paper-checkbox>
</div>
<hr class="vertMargin">
<div layout horizontal around-justified>
<paper-input-decorator label="Weight Each (lbs)" floatinglabel>
<input id="weightInput" type="number" value={{weight}}>
</paper-input-decorator>
<div class="layout horizontal around-justified" style="margin-top: 16px;">
<paper-input id="weightInput" type="number" value={{weight}} label="Weight Each (lbs)">
</paper-input>
<!--Value-->
<paper-input-decorator label="Value Each (GP)" floatinglabel>
<input id="valueInput" type="number" value={{value}}>
</paper-input-decorator>
<paper-input id="valueInput" type="number" value={{value}} label="Value Each (GP)">
</paper-input>
</div>
<hr class="vertMargin">
<!--Description-->
<paper-input-decorator label="Description" floatinglabel layout vertical>
<paper-autogrow-textarea>
<textarea id="itemDescriptionInput" placeholder aria-label="Description" value={{description}}></textarea>
</paper-autogrow-textarea>
</paper-input-decorator>
<paper-textarea id="itemDescriptionInput" label="Description" value={{description}}>
<div suffix>
<paper-tooltip position="left" animation-delay="0">This field accepts formulae in {curly brackets}</paper-tooltip>
<iron-icon icon="dicecloud:code-braces"></iron-icon>
</div>
</paper-textarea>
<!--Effects-->
{{> effectsEditList parentId=_id parentCollection="Items" charId=charId enabled=equipped name=name}}
<!--Attacks-->
@@ -101,13 +74,11 @@
</template>
<template name="containerDropdown">
<paper-dropdown-menu id="containerDropDown" label="Container">
<paper-dropdown layered class="dropdown">
<core-menu class="menu" selected={{parent.id}}>
{{#each containers}}
<paper-item name={{_id}} class="containerMenuItem">{{name}}</paper-item>
{{/each}}
</core-menu>
</paper-dropdown>
<paper-dropdown-menu label="Container">
<dicecloud-selector class="dropdown-content" id="containerDropDown" selected={{parent.id}}>
{{#each containers}}
<paper-item name={{_id}} class="containerMenuItem">{{name}}</paper-item>
{{/each}}
</dicecloud-selector>
</paper-dropdown-menu>
</template>

View File

@@ -27,60 +27,95 @@ Template.itemDialog.helpers({
});
Template.itemDialog.events({
"tap #editButton": function(event, instance){
"click #editButton": function(event, instance){
instance.editing.set(true);
},
"tap #doneEditingButton": function(event, instance){
"click #doneEditingButton": function(event, instance){
instance.editing.set(false);
},
"color-change": function(event, instance){
Items.update(instance.data.itemId, {$set: {color: event.color}});
},
"tap #deleteButton": function(event, instance){
"click #deleteButton": function(event, instance){
Items.softRemoveNode(instance.data.itemId);
GlobalUI.deletedToast(instance.data.itemId, "Items", "Item");
GlobalUI.closeDetail();
popDialogStack();
},
});
Template.itemEdit.onRendered(function(){
updatePolymerInputs(this);
});
Template.itemEdit.helpers({
ne1: function(num){
return num != 1;
},
});
const debounce = (f) => _.debounce(f, 200);
Template.itemEdit.events({
//TODO validate input (integer, non-negative, etc) for these inputs and give validation errors
"change #itemNameInput": function(event){
var name = Template.instance().find("#itemNameInput").value;
Items.update(this._id, {$set: {name: name}});
},
"change #itemPluralInput": function(event){
var plural = Template.instance().find("#itemPluralInput").value;
Items.update(this._id, {$set: {plural: plural}});
},
"change #quantityInput": function(event){
var quantity = +Template.instance().find("#quantityInput").value;
Items.update(this._id, {$set: {quantity: quantity}});
},
"change #weightInput": function(event){
var weight = +Template.instance().find("#weightInput").value;
Items.update(this._id, {$set: {weight: weight}});
},
"change #valueInput": function(event){
var value = +Template.instance().find("#valueInput").value;
Items.update(this._id, {$set: {value: value}});
},
"change #itemDescriptionInput": function(event){
var description = Template.instance().find("#itemDescriptionInput").value;
Items.update(this._id, {$set: {description: description}});
},
"input #itemNameInput": debounce(function(event, instance){
const input = event.currentTarget;
var name = input.value;
if (!name){
input.invalid = true;
input.errorMessage = "Name is required";
} else {
input.invalid = false;
Items.update(this._id, {
$set: {name: name}
}, {
removeEmptyStrings: false,
trimStrings: false,
});
}
}),
"input #itemPluralInput": debounce(function(event, instance){
var plural = event.currentTarget.value;
Items.update(this._id, {
$set: {plural: plural}
}, {
removeEmptyStrings: false,
trimStrings: false,
});
}),
"input #quantityInput": debounce(function(event, instance){
var quantity = +event.currentTarget.value;
Items.update(this._id, {
$set: {quantity: quantity}
}, {
removeEmptyStrings: false,
trimStrings: false,
});
}),
"input #weightInput": debounce(function(event, instance){
var weight = +event.currentTarget.value;
Items.update(this._id, {
$set: {weight: weight}
}, {
removeEmptyStrings: false,
trimStrings: false,
});
}),
"input #valueInput": debounce(function(event, instance){
var value = +event.currentTarget.value;
Items.update(this._id, {
$set: {value: value}
}, {
removeEmptyStrings: false,
trimStrings: false,
});
}),
"input #itemDescriptionInput": debounce(function(event, instance){
var description = event.currentTarget.value;
Items.update(this._id, {
$set: {description: description}
}, {
removeEmptyStrings: false,
trimStrings: false,
});
}),
"change #equippedInput": function(event){
var equipped = Template.instance().find("#equippedInput").checked;
var equipped = event.currentTarget.checked;
if (equipped){
Meteor.call("equipItem", this._id, this.charId);
} else {
@@ -104,9 +139,8 @@ Template.containerDropdown.helpers({
});
Template.containerDropdown.events({
"core-select #containerDropDown": function(event){
"iron-select #containerDropDown": function(event){
var detail = event.originalEvent.detail;
if (!detail.isSelected) return;
var containerId = detail.item.getAttribute("name");
Meteor.call("moveItemToContainer", Template.currentData()._id, containerId);
}

View File

@@ -0,0 +1,7 @@
html /deep/ .moveItemDialog paper-tabs::shadow #selectionBar {
background-color: #D50000;
}
html /deep/ .moveItemDialog paper-tab::shadow #ink {
color: #D50000;
}

View File

@@ -0,0 +1,49 @@
<template name="moveItemDialog">
<div class="moveItemDialog">
<paper-tabs selected="{{selectedTab}}">
<paper-tab name="containers"
class="clickable">
Containers
</paper-tab>
<paper-tab name="characters"
class="clickable">
Characters
</paper-tab>
</paper-tabs>
<core-animated-pages selected="{{selectedTab}}"
transitions="slide-from-right"
style="width: 250px;
height: 200px;
overflow-y: auto;
overflow-x: hidden;">
<section name="containers">
<core-menu id="containerMenu" style="margin: 0;">
{{#each containers}}
<paper-item name={{_id}}
layout horizontal center>
<iron-icon icon="image:brightness-1"
style="color: {{hexColor color}};
margin-right: 16px;">
</iron-icon>
<div>{{name}}</div>
</paper-item>
{{/each}}
</core-menu>
</section>
<section name="characters">
<core-menu id="characterMenu" style="margin: 0;">
{{#each characters}}
<paper-item name={{_id}}
layout horizontal center>
<div class="item small">
{{name}}
</div>
</paper-item>
{{/each}}
</core-menu>
</section>
</core-animated-pages>
</div>
<paper-button id="cancelButton" affirmative> Cancel </paper-button>
<paper-button id="moveButton" affirmative> Move </paper-button>
</template>

View File

@@ -0,0 +1,56 @@
Template.moveItemDialog.onCreated(function() {
Session.setDefault("moveItemDialogTab", "containers");
});
Template.moveItemDialog.helpers({
selectedTab: function() {
return Session.get("moveItemDialogTab");
},
characters: function() {
var userId = Meteor.userId();
return Characters.find(
{
$or: [
{readers: userId},
{writers: userId},
{owner: userId},
],
_id: {$ne: this.charId},
},
{fields: {name: 1}}
);
},
containers: function(){
return Containers.find(
{
charId: this.charId,
_id: {$ne: this.containerId},
},
{
fields: {color: 1, name: 1},
sort: {color: 1, name: 1},
}
);
},
});
Template.moveItemDialog.events({
"tap paper-tab": function(event) {
Session.set("moveItemDialogTab", event.currentTarget.getAttribute("name"));
},
"tap #moveButton": function(event, instance) {
var tab = Session.get("moveItemDialogTab");
if (tab === "containers"){
var containerId = instance.find("#containerMenu").selected;
if (!containerId) throw "no menu selection";
Meteor.call("moveItemToContainer", this.itemId, containerId);
} else if (tab === "characters"){
var characterId = instance.find("#characterMenu").selected;
if (!characterId) throw "no menu selection";
Meteor.call("moveItemToCharacter", this.itemId, characterId);
} else {
throw "Move item dialog tab is not set to containers or character," +
" it is set to " + tab;
}
},
});

View File

@@ -1,13 +1,29 @@
<!-- data needs to include id of item, parentCollection, parentId -->
<template name="splitStackDialog">
<div style="width: 300px; height: 110px;">
<paper-input-decorator label="Quantity" floatinglabel>
<input autoFocus id="quantityInput" type="number" value={{quantity}}>
</paper-input-decorator>
<paper-button id="oneButton"> One </paper-button>
<paper-button id="halfButton"> Half </paper-button>
<paper-button id="allButton"> All </paper-button>
<div class="fit layout vertical">
<app-header-layout has-scrolling-region class="feedback flex">
<app-header fixed effects="waterfall">
<app-toolbar>
Split Stack
</app-toolbar>
</app-header>
<div class="form flex">
<paper-input autoFocus label="Quantity" id="quantityInput" type="number" value={{quantity}}>
</paper-input>
<div class="layout horizontal justified">
<paper-button id="oneButton"> One </paper-button>
<paper-button id="halfButton"> Half </paper-button>
<paper-button id="allButton"> All </paper-button>
</div>
</div>
</app-header-layout>
<div class="buttons layout horizontal end-justified">
<paper-button id="cancelButton">
Cancel
</paper-button>
<paper-button id="moveButton">
move
</paper-button>
</div>
</div>
<paper-button id="cancelButton" affirmative> Cancel </paper-button>
<paper-button id="moveButton" affirmative> Move </paper-button>
</template>

View File

@@ -1,3 +1,7 @@
Template.splitStackDialog.onRendered(function(){
this.find("#quantityInput").focus();
});
Template.splitStackDialog.helpers({
quantity: function(){
var item = Items.findOne(this.id);
@@ -6,22 +10,26 @@ Template.splitStackDialog.helpers({
});
Template.splitStackDialog.events({
"tap #moveButton": function(event, instance){
"click #moveButton": function(event, instance){
Meteor.call(
"splitItemToParent",
this.id,
+instance.find("#quantityInput").value,
{collection: this.parentCollection , id: this.parentId}
);
popDialogStack();
},
"tap #oneButton":function(event, instance){
"click #cancelButton": function(event, instance){
popDialogStack();
},
"click #oneButton":function(event, instance){
instance.find("#quantityInput").value = 1;
},
"tap #halfButton":function(event, instance){
"click #halfButton":function(event, instance){
var val = Math.round(Items.findOne(this.id).quantity / 2);
instance.find("#quantityInput").value = val;
},
"tap #allButton":function(event, instance){
"click #allButton":function(event, instance){
instance.find("#quantityInput").value = Items.findOne(this.id).quantity;
},
});

View File

@@ -1,8 +1,8 @@
<template name="classDialog">
{{#with class}}
{{#baseDialog title=name class=colorClass startEditing=../startEditing}}
<div layout vertical center>
<div class="display2">
<div class="layout vertical center">
<div class="paper-font-display2">
{{level}}
</div>
<div>
@@ -14,12 +14,12 @@
{{> proficiencyViewList charId=charId parentId=_id}}
{{else}}
<!--Name-->
<paper-input id="classNameInput" label="Class Name" floatinglabel value={{name}}></paper-input>
<paper-input id="classNameInput" label="Class Name" value={{name}}></paper-input>
<!--Level-->
<paper-input id="levelValueInput" label="Level" floatinglabel value={{level}}></paper-input>
<paper-input id="levelValueInput" label="Level" value={{level}}></paper-input>
<!--Effects-->
{{> effectsEditList parentId=_id parentCollection="Classes" charId=charId}}
{{> proficiencyEditList parentId=_id parentCollection="Classes" charId=charId}}
{{/baseDialog}}
{{/with}}
</template>
</template>

View File

@@ -1,28 +1,35 @@
Template.classDialog.onRendered(function(){
updatePolymerInputs(this);
});
Template.classDialog.events({
"color-change": function(event, instance){
Classes.update(instance.data.classId, {$set: {color: event.color}});
},
"tap #deleteButton": function(event, instance){
Classes.softRemoveNode(instance.data.classId);
GlobalUI.deletedToast(instance.data.classId, "Classes", "Class");
GlobalUI.closeDetail();
},
"change #classNameInput": function(event){
var value = event.currentTarget.value;
Classes.update(this._id, {$set: {name: value}});
},
"change #levelValueInput": function(event){
var value = event.currentTarget.value;
Classes.update(this._id, {$set: {level: value}});
},
});
const debounce = (f) => _.debounce(f, 300);
Template.classDialog.helpers({
class: function(){
return Classes.findOne(this.classId);
}
});
Template.classDialog.events({
"color-change": function(event, instance){
Classes.update(instance.data.classId, {$set: {color: event.color}});
},
"click #deleteButton": function(event, instance){
Classes.softRemoveNode(instance.data.classId);
GlobalUI.deletedToast(instance.data.classId, "Classes", "Class");
popDialogStack();
},
"input #classNameInput, change #classNameInput": debounce(function(event){
var value = event.currentTarget.value;
Classes.update(this._id, {
$set: {name: value}
}, {
removeEmptyStrings: false,
trimStrings: false,
});
}),
"input #levelValueInput, change #levelValueInput": debounce(function(event){
var value = event.currentTarget.value;
Classes.update(this._id, {
$set: {level: value}
}, {
removeEmptyStrings: false,
});
}),
});

View File

@@ -1,28 +1,26 @@
<template name="experienceDialog">
{{#with experience}}
{{#baseDialog title=name class=color hideColor="true" startEditing=../startEditing}}
<div horizontal layout center-justified class= "display2">
<div class="horizontal layout center-justified paper-font-display2">
{{value}}
</div>
{{#if description}}
<hr class="vertMargin">
<div class="pre-wrap">{{description}}</div>
<div>{{#markdown}}{{description}}{{/markdown}}</div>
{{/if}}
{{else}}
<div horizontal layout>
<!--Name-->
<paper-input id="experienceNameInput" label="Name" floatinglabel value={{name}} flex></paper-input>
<!--Value-->
<paper-input-decorator label="Value" floatinglabel>
<input id="valueInput" type="number" value={{value}}>
</paper-input-decorator>
</div>
<!--Description-->
<paper-input-decorator label="Description" floatinglabel layout vertical>
<paper-autogrow-textarea>
<textarea id="experienceDescriptionInput" placeholder value={{description}}></textarea>
</paper-autogrow-textarea>
</paper-input-decorator>
{{> experienceEdit}}
{{/baseDialog}}
{{/with}}
</template>
</template>
<template name="experienceEdit">
<div class="horizontal layout">
<!--Name-->
<paper-input id="experienceNameInput" label="Name" value={{name}} class="flex"></paper-input>
<!--Value-->
<paper-input id="valueInput" type="number" value={{value}} label="Value"></paper-input>
</div>
<!--Description-->
<paper-textarea id="experienceDescriptionInput" label="Description" value={{description}}></paper-textarea>
</template>

View File

@@ -16,19 +16,40 @@ Template.experienceDialog.events({
instance.data.experienceId,
"Experiences", "Experience"
);
GlobalUI.closeDetail();
},
//TODO validate input (integer, non-negative, etc) for these inputs and give validation errors
"change #experienceNameInput, input #experienceNameInput": function(event){
var value = event.currentTarget.value;
Experiences.update(this._id, {$set: {name: value}});
},
"change #valueInput": function(event){
var value = +event.currentTarget.value;
Experiences.update(this._id, {$set: {value: value}});
},
"change #experienceDescriptionInput": function(event){
var value = event.currentTarget.value;
Experiences.update(this._id, {$set: {description: value}});
popDialogStack();
},
});
const debounce = (f) => _.debounce(f, 300);
Template.experienceEdit.events({
"input #experienceNameInput, change #experienceNameInput":
debounce(function(event){
var value = event.currentTarget.value;
Experiences.update(this._id, {
$set: {name: value}
}, {
removeEmptyStrings: false,
trimStrings: false,
});
}),
"input #valueInput, change #valueInput":
debounce(function(event){
var value = +event.currentTarget.value;
Experiences.update(this._id, {
$set: {value: value}
}, {
removeEmptyStrings: false,
});
}),
"input #experienceDescriptionInput":
debounce(function(event){
var value = event.currentTarget.value;
Experiences.update(this._id, {
$set: {description: value}
}, {
removeEmptyStrings: false,
trimStrings: false,
});
}),
});

View File

@@ -1,104 +1,91 @@
<template name="journal">
<div fit>
<div id="journal" class="scroll-y" fit>
<div class="column-container">
<!--Experience Table-->
<paper-shadow class="card experiencesCard"
hero-id="main" {{detailHero}}>
<div class="top white subhead"
hero-id="toolbar" {{detailHero}}
layout horizontal center>
<div flex>Experience</div>
<div >{{experience}} XP</div>
<paper-icon-button class="black54" id="addXP" icon="add"></paper-icon-button>
</div>
<div class="bottom list">
{{#each experiences}}
<div class="item-slot">
<div class="item experience"
hero-id="main" {{detailHero}}
layout horizontal center>
<div flex>{{name}}</div>
<div class="xpValue">{{value}}</div>
</div>
</div>
{{/each}}
</div>
{{#if moreExperiencesOrCollapse}}
<div layout horizontal center end-justified>
<paper-button id="moreExperiences"
disabled={{notMoreExperiences}}>
Load More
</paper-button>
<paper-button id="lessExperiences"
disabled={{cantCollapse}}>
Collapse
</paper-button>
</div>
{{/if}}
</paper-shadow>
<!--Class Table-->
<paper-shadow class="card"
hero-id="main" {{detailHero}}>
<div class="white top"
hero-id="toolbar" {{detailHero}}
layout horizontal center>
<div flex>
<div class="containerName subhead">
Level {{level}}
</div>
{{#if nextLevelXP}}
<div class="caption">
Next Level: {{nextLevelXP}}XP
</div>
{{/if}}
</div>
<paper-icon-button class="black54"
id="addClassButton"
icon="add">
</paper-icon-button>
</div>
<div class="bottom list">
<div class="column-container animation-slider">
<!--Experience Table-->
<div>
<paper-material class="card experiencesCard">
<div class="top white subhead layout horizontal center">
<div class="flex">Experience</div>
<div>{{characterCalculate "experience" _id}} XP</div>
<paper-icon-button class="black54" id="addXP" icon="add"
disabled={{#unless canEditCharacter _id}}true{{/unless}}></paper-icon-button>
</div>
<div class="bottom list">
{{#each experiences}}
<div class="item-slot">
<div class="item race"
hero-id="main" {{detailHero "race" _id}}
layout horizontal center>
{{race}}
<div class="item experience layout horizontal center" data-id={{_id}}>
<div class="flex">{{name}}</div>
<div class="xpValue">{{value}}</div>
</div>
</div>
{{#each classes}}
<div class="item-slot">
<div class="item class"
hero-id="main" {{detailHero}}
layout horizontal center>
{{name}}&nbsp;{{level}}
</div>
</div>
{{/each}}
{{/each}}
</div>
{{#if moreExperiencesOrCollapse}}
<div class="layout horizontal center end-justified">
<paper-button id="moreExperiences" disabled={{notMoreExperiences}}>
Load More
</paper-button>
<paper-button id="lessExperiences" disabled={{cantCollapse}}>
Collapse
</paper-button>
</div>
</paper-shadow>
<!--Notes-->
{{#each notes}}
<paper-shadow class="card" hero-id="main" {{detailHero}}>
<div class="top {{colorClass}} noteTop subhead"
hero-id="toolbar" {{detailHero}}
layout horizontal center>
{{name}}
</div>
<div class="bottom text">{{description}}</div>
</paper-shadow>
{{/each}}
</div>
<div class="fab-buffer"></div>
{{/if}}
</paper-material>
</div>
<!--Class Table-->
<div>
<paper-material class="card">
<div class="white top layout horizontal center">
<div class="flex">
<div class="containerName paper-font-subhead">
Level {{characterCalculate "level" _id}}
</div>
{{#if nextLevelXP}}
<div class="paper-font-caption">
Next Level: {{nextLevelXP}}XP
</div>
{{/if}}
</div>
<paper-icon-button class="black54"
id="addClassButton"
icon="add"
disabled={{#unless canEditCharacter _id}}true{{/unless}}>
</paper-icon-button>
</div>
<div class="bottom list">
<div class="item-slot">
<div class="item race layout horizontal center">
{{race}}
</div>
</div>
{{#each classes}}
<div class="item-slot">
<div class="item class layout horizontal center" data-id={{_id}}>
{{name}}&nbsp;{{level}}
</div>
</div>
{{/each}}
</div>
</paper-material>
</div>
<!--Notes-->
{{#each notes}}
<div>
<paper-material class="card note" data-id={{_id}}>
<div class="top {{colorClass}} noteTop paper-font-subhead layout horizontal center">
{{name}}
</div>
<div class="bottom">
{{#markdown}}{{evaluateShortString charId description}}{{/markdown}}
</div>
</paper-material>
</div>
{{/each}}
</div>
<div class="fab-buffer"></div>
{{#if canEditCharacter _id}}
<paper-fab id="addNote"
class="floatyButton"
icon="add"
title="Add"
role="button"
tabindex="0"
hero-id="main"></paper-fab>
<paper-fab id="addNote"
class="floatyButton"
icon="add"
title="Add"></paper-fab>
{{/if}}
</template>
</template>

View File

@@ -41,7 +41,7 @@ Template.journal.helpers({
return Levels.find({charId: charId, classId: this._id}, {sort: {value: 1}});
},
nextLevelXP: function(){
var currentLevel = this.level();
var currentLevel = Characters.calculate.level(this._id);
if (currentLevel < 20){
return XP_TABLE[currentLevel];
}
@@ -53,101 +53,93 @@ Template.journal.helpers({
});
Template.journal.events({
"tap .noteTop": function(event){
GlobalUI.setDetail({
"click .noteTop": function(event){
pushDialogStack({
template: "noteDialog",
data: {noteId: this._id, charId: this.charId},
heroId: this._id,
element: event.currentTarget.parentElement,
});
},
"tap .experience": function(event){
GlobalUI.setDetail({
"click .experience": function(event){
pushDialogStack({
template: "experienceDialog",
data: {experienceId: this._id, charId: this.charId},
heroId: this._id,
element: event.currentTarget,
});
},
"tap .class": function(event){
GlobalUI.setDetail({
"click .class": function(event){
pushDialogStack({
template: "classDialog",
data: {classId: this._id, charId: this.charId},
heroId: this._id,
element: event.currentTarget,
});
},
"tap .race": function(event){
GlobalUI.setDetail({
"click .race": function(event){
pushDialogStack({
template: "raceDialog",
data: {charId: this._id},
heroId: this._id + "race",
element: event.currentTarget,
});
},
"tap #addNote": function(event){
"click #addNote": function(event, instance){
var charId = this._id;
Notes.insert({
var noteId = Notes.insert({
name: "New Note",
charId: charId,
}, function(error, id){
if (!error){
GlobalUI.setDetail({
template: "noteDialog",
data: {noteId: id, charId: charId, startEditing: true},
heroId: id,
});
}
});
pushDialogStack({
template: "noteDialog",
data: {noteId: noteId, charId: charId, startEditing: true},
element: event.currentTarget,
returnElement: () => instance.find(`.note[data-id='${noteId}']`),
});
},
"tap #addXP": function(event){
"click #addXP": function(event, instance){
var charId = this._id;
Experiences.insert({
var expId = Experiences.insert({
charId: charId
}, function(error, id){
if (!error){
GlobalUI.setDetail({
template: "experienceDialog",
data: {experienceId: id, charId: charId, startEditing: true},
heroId: id,
});
}
});
pushDialogStack({
template: "experienceDialog",
data: {experienceId: expId, charId: charId, startEditing: true},
element: event.currentTarget,
returnElement: () => instance.find(`.experience[data-id='${expId}']`),
});
},
"tap #addClassButton":function(event){
"click #addClassButton":function(event, instance){
var charId = this._id;
Classes.insert({
var classId = Classes.insert({
charId: charId,
name: "new Class",
level: 1,
}, function(error, id){
if (!error){
GlobalUI.setDetail({
template: "classDialog",
data: {classId: id, charId: charId, startEditing: true},
heroId: id,
});
}
});
pushDialogStack({
template: "classDialog",
data: {classId: classId, charId: charId, startEditing: true},
element: event.currentTarget,
returnElement: () => instance.find(`.class[data-id='${classId}']`),
});
},
"tap #moreExperiences": function(event){
var inst = Template.instance();
inst.experiencesLimit.set(
inst.experiencesLimit.get() +
"click #moreExperiences": function(event, instance){
instance.experiencesLimit.set(
instance.experiencesLimit.get() +
(this.settings && this.settings.experiencesInc || 10)
);
},
"tap #lessExperiences": function(event){
var inst = Template.instance();
inst.experiencesLimit.set(
"click #lessExperiences": function(event, instance){
instance.experiencesLimit.set(
this.settings && this.settings.experiencesInc || 10
);
//scroll to the top of the div
inst.$(".scroll-y").animate({
// Scroll to the top of the div
instance.$(".scroll-y").animate({
scrollTop: (
inst.$(".scroll-y").scrollTop() +
inst.$(".experiencesCard").position().top -
instance.$(".scroll-y").scrollTop() +
instance.$(".experiencesCard").position().top -
8
)
}, 300);
//HACK giggle the columns :( to workaround chrome bug that stops .containers height from updating
var cs = inst.$(".containers").removeClass("containers");
// HACK jiggle the columns :( to workaround chrome bug that stops .containers height from updating
var cs = instance.$(".containers").removeClass("containers");
_.defer(function(){cs.addClass("containers");});
},
});

View File

@@ -1,7 +1,7 @@
<template name="noteDialog">
{{#with note}}
{{#baseDialog title=name class=colorClass startEditing=../startEditing}}
<div class="pre-wrap">{{description}}</div>
<div>{{#markdown}}{{evaluateString charId description}}{{/markdown}}</div>
{{else}}
{{> noteDialogEdit}}
{{/baseDialog}}
@@ -10,21 +10,13 @@
<template name="noteDialogEdit">
<!--Name-->
<div horizontal layout>
<div class="horizontal layout">
<paper-input id="noteNameInput"
label="Name"
floatinglabel
value={{name}}
flex>
class="flex">
</paper-input>
</div>
<!--Description, formatting this nicely breaks it, leave it as is-->
<paper-input-decorator label="Description"
floatinglabel
layout vertical>
<paper-autogrow-textarea>
<textarea id="noteDescriptionInput"
value={{description}}></textarea>
</paper-autogrow-textarea>
</paper-input-decorator>
</template>
<paper-textarea id="noteDescriptionInput" label="Description" value={{description}}></paper-textarea>
</template>

View File

@@ -11,21 +11,36 @@ Template.noteDialog.events({
"tap #deleteButton": function(event, instance){
Notes.softRemove(instance.data.noteId);
GlobalUI.deletedToast(instance.data.noteId, "Notes", "Note");
GlobalUI.closeDetail();
popDialogStack();
},
});
Template.noteDialogEdit.onRendered(function(){
updatePolymerInputs(this);
});
const debounce = (f) => _.debounce(f, 300);
Template.noteDialogEdit.events({
"change #noteNameInput, input #noteNameInput": function(event){
"change #noteNameInput, input #noteNameInput": 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;
Notes.update(this._id, {
$set: {name: name}
}, {
removeEmptyStrings: false,
trimStrings: false,
});
}
}),
"input #noteDescriptionInput": debounce(function(event){
var value = event.currentTarget.value;
Notes.update(this._id, {$set: {name: value}});
},
"change #noteDescriptionInput": function(event){
var value = event.currentTarget.value;
Notes.update(this._id, {$set: {description: value}});
},
Notes.update(this._id, {
$set: {description: value}
}, {
removeEmptyStrings: false,
trimStrings: false,
});
}),
});

View File

@@ -1,13 +1,13 @@
<template name="raceDialog">
{{#baseDialog title="Race" class=color hideColor="true" hideDelete="true" startEditing=startEditing}}
<div horizontal layout center-justified class= "display2">
<div class="horizontal layout center-justified paper-font-display2">
{{race}}
</div>
{{> effectsViewList charId=charId parentId=charId parentGroup="racial"}}
{{> proficiencyViewList charId=charId parentId=charId parentGroup="racial"}}
{{else}}
<paper-input id="raceInput" label="Race" floatinglabel value={{race}}></paper-input>
<paper-input id="raceInput" label="Race" value={{race}}></paper-input>
{{> effectsEditList parentId=charId parentCollection="Characters" charId=charId parentGroup="racial"}}
{{> proficiencyEditList parentId=charId parentCollection="Characters" charId=charId parentGroup="racial"}}
{{/baseDialog}}
</template>
</template>

View File

@@ -1,12 +1,13 @@
Template.raceDialog.onRendered(function(){
updatePolymerInputs(this);
});
Template.raceDialog.events({
"change #raceInput": function(event){
"input #raceInput, change #raceInput": _.debounce(function(event){
var value = event.currentTarget.value;
Characters.update(this.charId, {$set: {race: value}});
}
Characters.update(this.charId, {
$set: {race: value}
}, {
removeEmptyStrings: false,
trimStrings: false,
});
}, 300),
});
Template.raceDialog.helpers({

View File

@@ -1,40 +1,27 @@
<template name="newCharacterDialog">
<div>
<paper-input id="nameInput" label="Name"></paper-input><br>
<paper-input id="genderInput" label="Gender"></paper-input><br>
<paper-input id="raceInput" label="Race"></paper-input>
<!--
<div>
<div layout horizontal center-justified>
{{pointsUsed}}/<paper-input-decorator><input type="number" value="27"></paper-input-decorator>
</div>
<div layout horizontal wrap>
<div>Strength</div>
<paper-slider id="strSlider" min="8" max="15" value="8" secondaryProgress={{secondaryProgress}}></paper-slider>
</div>
<div layout horizontal wrap>
<div>Dexterity</div>
<paper-slider id="dexSlider" min="8" max="15" value="8" secondaryProgress={{secondaryProgress}}></paper-slider>
</div>
<div layout horizontal wrap>
<div>Constitution</div>
<paper-slider id="conSlider" min="8" max="15" value="8" secondaryProgress={{secondaryProgress}}></paper-slider>
</div>
<div layout horizontal wrap>
<div>Intelligence</div>
<paper-slider id="intSlider" min="8" max="15" value="8" secondaryProgress={{secondaryProgress}}></paper-slider>
</div>
<div layout horizontal wrap>
<div>Wisdom</div>
<paper-slider id="wisSlider" min="8" max="15" value="8" secondaryProgress={{secondaryProgress}}></paper-slider>
</div>
<div layout horizontal wrap>
<div>Charisma</div>
<paper-slider id="chaSlider" min="8" max="15" value="8" secondaryProgress={{secondaryProgress}}></paper-slider>
<div class="fit layout vertical">
<app-header-layout has-scrolling-region class="new-character-dialog flex">
<app-header fixed effects="waterfall">
<app-toolbar>
<div main-title>New Character</div>
</app-toolbar>
</app-header>
<div class="form">
<p>
Input the basics of your character, but don't worry, you can always change these values later
</p>
<paper-input class="nameInput" label="Name" {{errorAtts "name"}}></paper-input><br>
<paper-input class="genderInput" label="Gender" {{errorAtts "gender"}}></paper-input><br>
<paper-input class="raceInput" label="Race" {{errorAtts "race"}}></paper-input>
</div>
</app-header-layout>
<div class="buttons layout horizontal end-justified">
<paper-button class="cancelButton">
Cancel
</paper-button>
<paper-button class="addButton" disabled={{invalid}}>
Add
</paper-button>
</div>
-->
</div>
<paper-button id="cancelButton" affirmative> Cancel </paper-button>
<paper-button id="addButton" affirmative> Add </paper-button>
</template>
</template>

View File

@@ -1,13 +1,50 @@
Template.newCharacterDialog.events({
"tap #addButton": function(event, instance){
Characters.insert({
name: instance.find("#nameInput").value,
gender: instance.find("#genderInput").value,
race: instance.find("#raceInput").value,
owner: Meteor.userId(),
}, function(err, id){
if (err) throw err;
Router.go("characterSheet", {_id: id});
});
}
Template.newCharacterDialog.onCreated(function(){
this.character = {};
this.schema = new SimpleSchema({
//strings
name: {type: String},
gender: {type: String, optional: true},
race: {type: String, optional: true},
});
this.context = this.schema.newContext();
this.context.runOnce = new ReactiveVar(false);
});
Template.newCharacterDialog.helpers({
invalid(){
let context = Template.instance().context;
let valid = context.isValid() && context.runOnce.get();
return !valid;
},
errorAtts(key){
let error = Template.instance().context.keyErrorMessage(key);
if (error){
return {
invalid: true,
["error-message"]: error,
}
}
},
});
changeFunction = function(field){
return _.debounce(function(event, instance){
console.log({field, event})
instance.character[field] = event.currentTarget.value;
instance.schema.clean(instance.character);
instance.context.validate(instance.character);
if (!instance.context.runOnce.get()) instance.context.runOnce.set(true);
}, 200);
};
Template.newCharacterDialog.events({
"input .nameInput": changeFunction("name"),
"input .genderInput": changeFunction("gender"),
"input .raceInput": changeFunction("race"),
"click .cancelButton": function(event, instance){
popDialogStack();
},
"click .addButton": function(event, instance){
popDialogStack(instance.character);
},
});

View File

@@ -0,0 +1,9 @@
<template name="backgroundDialog">
{{#baseDialog title=title class=colorClass hideColor="true" hideDelete="true"}}
<div>{{#markdown}}{{evaluateString charId value}}{{/markdown}}</div>
{{> proficiencyViewList charId=charId parentId=charId parentGroup="background"}}
{{else}}
{{> textDialogEdit}}
{{> proficiencyEditList parentId=charId parentCollection="Characters" charId=charId parentGroup="background"}}
{{/baseDialog}}
</template>

View File

@@ -0,0 +1,8 @@
Template.backgroundDialog.helpers({
value: function(){
var fieldSelector = {fields: {}};
fieldSelector.fields[this.field] = 1;
var char = Characters.findOne(this.charId, fieldSelector);
return char[this.field];
}
});

View File

@@ -1,18 +1,34 @@
<template name="personaDetailsDialog">
{{#baseDialog title=name class="deep-purple white-text" hideColor="true" hideDelete="true" startEditing=startEditing}}
{{alignment}} {{gender}} {{race}}
{{#with char}}
<div>{{alignment}} {{gender}} {{race}}</div>
<iron-image style="width: 350px; height: 350px; margin-top: 8px;"
sizing="contain"
src={{picture}}></iron-image>
{{/with}}
{{else}}
{{> personaDetailsEdit}}
{{#with char}}
{{> personaDetailsEdit}}
{{/with}}
{{/baseDialog}}
</template>
<template name="personaDetailsEdit">
<!--Name-->
<paper-input id="nameInput" label="Name" floatinglabel value={{name}}></paper-input><br>
<!--Alignment-->
<paper-input id="alignmentInput" label="Alignment" floatinglabel value={{alignment}}></paper-input><br>
<!--Gender-->
<paper-input id="genderInput" label="Gender" floatinglabel value={{gender}}></paper-input><br>
<!--Race-->
<paper-input id="raceInput" label="Race" floatinglabel value={{race}}></paper-input><br>
<div class="layout horizontal center-justified">
<div class="flex layout vertical" style="max-width: 400px;">
<!--Name-->
<paper-input id="nameInput" label="Name" value={{name}}></paper-input>
<!--Alignment-->
<paper-input id="alignmentInput" label="Alignment" value={{alignment}}></paper-input>
<!--Gender-->
<paper-input id="genderInput" label="Gender" value={{gender}}></paper-input>
<!--Race-->
<paper-input id="raceInput" label="Race" value={{race}}></paper-input>
<!--Picture-->
<paper-input id="pictureInput" label="Picture URL" value={{picture}}></paper-input>
<iron-image style="margin-top: 8px; width: 100%; height: 400px;"
sizing="contain"
src={{picture}}></iron-image>
</div>
</div>
</template>

View File

@@ -1,22 +1,26 @@
Template.personaDetailsEdit.onRendered(function(){
updatePolymerInputs(this);
Template.personaDetailsDialog.helpers({
char: function() {
return Characters.findOne(
this._id,
{fields: {name: 1, alignment: 1, gender: 1, race: 1, picture: 1}}
);
}
});
inputHandler = (field) => _.debounce(function(event){
var input = event.currentTarget.value;
Characters.update(this._id, {
$set: {[field]: input}
}, {
removeEmptyStrings: false,
trimStrings: false,
});
}, 300);
Template.personaDetailsEdit.events({
"change #nameInput": function(event){
var input = event.currentTarget.value;
Characters.update(this.charId, {$set: {name: input}});
},
"change #alignmentInput": function(event){
var input = event.currentTarget.value;
Characters.update(this.charId, {$set: {alignment: input}});
},
"change #genderInput": function(event){
var input = event.currentTarget.value;
Characters.update(this.charId, {$set: {gender: input}});
},
"change #raceInput": function(event){
var input = event.currentTarget.value;
Characters.update(this.charId, {$set: {race: input}});
},
"input #nameInput, change #nameInput": inputHandler("name"),
"input #alignmentInput, change #alignmentInput": inputHandler("alignment"),
"input #genderInput, change #genderInput": inputHandler("gender"),
"input #raceInput, change #raceInput": inputHandler("race"),
"input #pictureInput, change #pictureInput": inputHandler("picture"),
});

Some files were not shown because too many files have changed in this diff Show More