Compare commits

...

140 Commits
0.2.6 ... 0.7.0

Author SHA1 Message Date
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
Stefan Zermatten
2f729070b2 Updated change log 2015-05-22 14:24:22 +02:00
Stefan Zermatten
7aedb9451c Base values now don't look like added values 2015-05-22 14:17:38 +02:00
Stefan Zermatten
c6886dd49e Floaty menus now close when clicking on a sub-button 2015-05-22 14:14:20 +02:00
Stefan Zermatten
038ce490e4 Added subsmanager to stop characters getting forgotten between page changes 2015-05-22 14:11:22 +02:00
Stefan Zermatten
52bef57637 Added encumbrance effects, conditions and encumbrance buffs 2015-05-22 14:04:09 +02:00
Stefan Zermatten
29e9f8c8dc Added quality-of-life UI to determining a character's encumbrance 2015-05-20 16:14:01 +02:00
Stefan Zermatten
fea02811ff Added buffs and standard conditions, no UI yet though 2015-05-20 16:13:25 +02:00
Stefan Zermatten
73cee52fff Fixed a bug in combining multiple resistances/vulnerabilities 2015-05-20 16:11:59 +02:00
Stefan Zermatten
b58c006ed4 Updated change log 2015-05-19 11:45:38 +02:00
Stefan Zermatten
ef44f6c1a5 Fixed migration of attack data 2015-05-19 11:44:06 +02:00
Stefan Zermatten
f455cea43f Fixed maths for strength calculations rounding, rather than rounding down 2015-05-18 14:31:07 +02:00
Stefan Zermatten
e4083bc744 Updated the change log 2015-05-18 14:16:24 +02:00
Stefan Zermatten
baffafb62a Added table of calculated values to strength detail 2015-05-18 14:13:28 +02:00
Stefan Zermatten
4143929667 Resolve merge conflicts 2015-05-18 13:21:28 +02:00
Stefan Zermatten
18286d1b9c Merge remote-tracking branch 'origin/master'
Conflicts:
	rpg-docs/client/style/main.scss
2015-05-18 13:20:24 +02:00
Stefan Zermatten
9f51567162 Added migrations for new data structure 2015-05-18 13:18:37 +02:00
Stefan Zermatten
66d8a3bfbf Moved dice.js and renamed it 2015-05-18 13:18:27 +02:00
Stefan Zermatten
a9648c10cc Fixed line break formatting in dialogs 2015-05-18 13:18:11 +02:00
Stefan Zermatten
679292373c Added item increment buttons 2015-05-18 13:17:40 +02:00
Stefan Zermatten
ae416458b5 Made attacks less rigid, now using inline computations 2015-05-18 13:16:33 +02:00
Stefan Zermatten
0ff4a887ea Updated the change log with v0.3.1 2015-05-16 00:26:00 +02:00
Stefan Zermatten
955794b5c0 Re-implemented paper-fab menu 2015-05-16 00:20:49 +02:00
Stefan Zermatten
b0ac1dcc29 Split cards into their own scss 2015-05-16 00:20:31 +02:00
Stefan Zermatten
83150bc527 Updated the change log 2015-05-15 16:59:54 +02:00
Stefan Zermatten
061f1fd0a5 Rewrite all css to scss and refactor html 2015-05-15 16:55:05 +02:00
Stefan Zermatten
e40dd196e6 Migrated stats page to scss 2015-05-13 15:25:22 +02:00
Stefan Zermatten
5dbb59ef80 skill-row now scss 2015-05-13 13:53:53 +02:00
Stefan Zermatten
49e25d7304 Started implementing core styles 2015-05-13 13:23:44 +02:00
Stefan Zermatten
85df0257e2 Added scss 2015-05-13 13:23:18 +02:00
Stefan Zermatten
b88bb95928 Stopped the sidebar from appearing except on displays> 900px 2015-05-13 12:51:35 +02:00
Stefan Zermatten
2122e543d5 Ensured disabled effects don't show up in skill and attribute detail boxes 2015-05-13 08:46:28 +02:00
Stefan Zermatten
a71519aaa7 Updated Polymer 2015-05-13 08:46:04 +02:00
Stefan Zermatten
2404845d51 Styling and rounding fixes for detail boxes 2015-05-12 11:34:56 +02:00
Stefan Zermatten
bf032bcdf3 Now only show edit and add buttons to writers, not readers 2015-05-12 11:34:37 +02:00
Stefan Zermatten
ff8ae89722 Improved feedback form style 2015-05-12 10:36:15 +02:00
Stefan Zermatten
80ca7307ce Added change log 2015-05-12 10:10:15 +02:00
Stefan Zermatten
a539b0bc6c limited how much info gets published to users about themselves 2015-05-12 09:33:32 +02:00
Stefan Zermatten
c6b3cad9c8 Improved character side list style 2015-05-12 09:32:40 +02:00
Stefan Zermatten
95b7b66390 Added quick feedback form 2015-05-12 09:32:28 +02:00
152 changed files with 3856 additions and 1677 deletions

1
rpg-docs/.gitignore vendored
View File

@@ -1,5 +1,6 @@
.meteor/local .meteor/local
.meteor/meteorite .meteor/meteorite
settings.json
public/components public/components
nohup.out nohup.out
dump dump

View File

@@ -16,7 +16,6 @@ differential:vulcanize
matb33:collection-hooks matb33:collection-hooks
zimme:collection-softremovable zimme:collection-softremovable
momentjs:moment momentjs:moment
mike:mocha
dburles:mongo-collection-instances dburles:mongo-collection-instances
percolate:migrations percolate:migrations
ecwyne:mathjs ecwyne:mathjs
@@ -24,3 +23,9 @@ useraccounts:polymer
accounts-google accounts-google
splendido:accounts-meld splendido:accounts-meld
email email
fourseven:scss@2.1.1
wolves:bourbon
meteorhacks:subs-manager
meteorhacks:kadira
chuangbo:marked
reywood:iron-router-ga

View File

@@ -6,7 +6,6 @@ accounts-ui@1.1.5
accounts-ui-unstyled@1.1.7 accounts-ui-unstyled@1.1.7
aldeed:collection2@2.3.3 aldeed:collection2@2.3.3
aldeed:simple-schema@1.3.3 aldeed:simple-schema@1.3.3
amplify@1.0.0
autoupdate@1.2.1 autoupdate@1.2.1
base64@1.0.3 base64@1.0.3
binary-heap@1.0.3 binary-heap@1.0.3
@@ -15,6 +14,7 @@ blaze-tools@1.0.3
boilerplate-generator@1.0.3 boilerplate-generator@1.0.3
callback-hook@1.0.3 callback-hook@1.0.3
check@1.0.5 check@1.0.5
chuangbo:marked@0.3.5
coffeescript@1.0.6 coffeescript@1.0.6
dburles:collection-helpers@1.0.3 dburles:collection-helpers@1.0.3
dburles:mongo-collection-instances@0.3.3 dburles:mongo-collection-instances@0.3.3
@@ -25,6 +25,7 @@ ecwyne:mathjs@0.25.0
ejson@1.0.6 ejson@1.0.6
email@1.0.6 email@1.0.6
fastclick@1.0.3 fastclick@1.0.3
fourseven:scss@2.1.1
geojson-utils@1.0.3 geojson-utils@1.0.3
google@1.1.5 google@1.1.5
html-tools@1.0.4 html-tools@1.0.4
@@ -50,30 +51,28 @@ logging@1.0.7
matb33:collection-hooks@0.7.13 matb33:collection-hooks@0.7.13
meteor@1.1.6 meteor@1.1.6
meteor-platform@1.2.2 meteor-platform@1.2.2
mike:mocha@0.5.4 meteorhacks:kadira@2.21.0
meteorhacks:meteorx@1.3.1
meteorhacks:subs-manager@1.3.0
minifiers@1.1.5 minifiers@1.1.5
minimongo@1.0.8 minimongo@1.0.8
mobile-status-bar@1.0.3 mobile-status-bar@1.0.3
momentjs:moment@2.10.3 momentjs:moment@2.10.3
mongo@1.1.0 mongo@1.1.0
mongo-livedata@1.0.8
npm-bcrypt@0.7.8_2 npm-bcrypt@0.7.8_2
oauth@1.1.4 oauth@1.1.4
oauth2@1.1.3 oauth2@1.1.3
observe-sequence@1.0.6 observe-sequence@1.0.6
ordered-dict@1.0.3 ordered-dict@1.0.3
package-version-parser@3.0.3
percolate:migrations@0.7.5 percolate:migrations@0.7.5
practicalmeteor:chai@1.9.2_3
practicalmeteor:loglevel@1.1.0_3
random@1.0.3 random@1.0.3
reactive-dict@1.1.0 reactive-dict@1.1.0
reactive-var@1.0.5 reactive-var@1.0.5
reload@1.1.3 reload@1.1.3
retry@1.0.3 retry@1.0.3
reywood:iron-router-ga@0.6.0
routepolicy@1.0.5 routepolicy@1.0.5
sanjo:long-running-child-process@1.0.3
sanjo:meteor-files-helpers@1.1.0_4
sanjo:meteor-version@1.0.0
service-configuration@1.0.4 service-configuration@1.0.4
session@1.1.0 session@1.1.0
sha@1.0.3 sha@1.0.3
@@ -90,12 +89,8 @@ underscore@1.0.3
url@1.0.4 url@1.0.4
useraccounts:core@1.9.1 useraccounts:core@1.9.1
useraccounts:polymer@1.9.1 useraccounts:polymer@1.9.1
velocity:chokidar@0.12.6_1
velocity:core@0.6.1
velocity:html-reporter@0.5.3
velocity:meteor-internals@1.1.0_7
velocity:shim@0.1.0
webapp@1.2.0 webapp@1.2.0
webapp-hashing@1.0.3 webapp-hashing@1.0.3
wolves:bourbon@1.0.0
zimme:collection-behaviours@1.0.4 zimme:collection-behaviours@1.0.4
zimme:collection-softremovable@1.0.4 zimme:collection-softremovable@1.0.4

View File

@@ -24,18 +24,12 @@ Schemas.Attack = new SimpleSchema({
optional: true, optional: true,
trim: false, trim: false,
}, },
damageBonus: { damage: {
type: String, type: String,
defaultValue: "strengthMod", defaultValue: "1d8 + {strengthMod}",
optional: true, optional: true,
trim: false, trim: false,
}, },
damageDice: {
type: String,
optional: true,
defaultValue: "1d8",
allowedValues: DAMAGE_DICE,
},
damageType: { damageType: {
type: String, type: String,
allowedValues: [ allowedValues: [

View File

@@ -1,28 +1,51 @@
Buffs = new Mongo.Collection("buffs"); Buffs = new Mongo.Collection("buffs");
//buffs are temporary once applied and store things which expire and their expiry time
Schemas.Buff = new SimpleSchema({ Schemas.Buff = new SimpleSchema({
//buff id
_id: {
type: String,
regEx: SimpleSchema.RegEx.Id,
autoValue: function(){
if (!this.isSet) return Random.id();
},
},
charId: { charId: {
type: String, type: String,
regEx: SimpleSchema.RegEx.Id, regEx: SimpleSchema.RegEx.Id,
}, },
//expiry time name: {
expiry: {type: Number, optional: true}, type: String,
duration: {type: Number}, trim: false,
},
description: {
type: String,
optional: true,
trim: false,
},
enabled: {
type: Boolean,
defaultValue: true,
},
type: {
type: String,
allowedValues: [
"inate",
"custom",
],
},
"lifeTime.total": {
type: Number,
defaultValue: 0, //0 is infinite
min: 0,
},
"lifeTime.spent": {
type: Number,
defaultValue: 0,
min: 0,
},
color: {
type: String,
allowedValues: _.pluck(colorOptions, "key"),
defaultValue: "q",
},
}); });
Buffs.attachSchema(Schemas.Buff); Buffs.attachSchema(Schemas.Buff);
Buffs.attachBehaviour("softRemovable"); Buffs.attachBehaviour("softRemovable");
makeParent(Buffs, "name"); //parents of effects and attacks makeParent(Buffs, ["name", "enabled"]); //parents of effects
Buffs.allow(CHARACTER_SUBSCHEMA_ALLOW); Buffs.allow(CHARACTER_SUBSCHEMA_ALLOW);
Buffs.deny(CHARACTER_SUBSCHEMA_DENY); Buffs.deny(CHARACTER_SUBSCHEMA_DENY);

View File

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

View File

@@ -61,7 +61,6 @@ Effects.attachSchema(Schemas.Effect);
if (Meteor.isServer) Characters.after.insert(function(userId, char) { if (Meteor.isServer) Characters.after.insert(function(userId, char) {
Effects.insert({ Effects.insert({
charId: char._id, charId: char._id,
type: "inate",
name: "Constitution modifier for each level", name: "Constitution modifier for each level",
stat: "hitPoints", stat: "hitPoints",
operation: "add", operation: "add",
@@ -69,11 +68,11 @@ if (Meteor.isServer) Characters.after.insert(function(userId, char) {
parent: { parent: {
id: char._id, id: char._id,
collection: "Characters", collection: "Characters",
group: "Inate",
}, },
}); });
Effects.insert({ Effects.insert({
charId: char._id, charId: char._id,
type: "inate",
name: "Proficiency bonus by level", name: "Proficiency bonus by level",
stat: "proficiencyBonus", stat: "proficiencyBonus",
operation: "add", operation: "add",
@@ -81,11 +80,11 @@ if (Meteor.isServer) Characters.after.insert(function(userId, char) {
parent: { parent: {
id: char._id, id: char._id,
collection: "Characters", collection: "Characters",
group: "Inate",
}, },
}); });
Effects.insert({ Effects.insert({
charId: char._id, charId: char._id,
type: "inate",
name: "Dexterity Armor Bonus", name: "Dexterity Armor Bonus",
stat: "armor", stat: "armor",
operation: "add", operation: "add",
@@ -93,11 +92,11 @@ if (Meteor.isServer) Characters.after.insert(function(userId, char) {
parent: { parent: {
id: char._id, id: char._id,
collection: "Characters", collection: "Characters",
group: "Inate",
}, },
}); });
Effects.insert({ Effects.insert({
charId: char._id, charId: char._id,
type: "inate",
name: "Natural Armor", name: "Natural Armor",
stat: "armor", stat: "armor",
operation: "base", operation: "base",
@@ -105,6 +104,19 @@ if (Meteor.isServer) Characters.after.insert(function(userId, char) {
parent: { parent: {
id: char._id, id: char._id,
collection: "Characters", 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

@@ -30,7 +30,7 @@ Schemas.Proficiency = new SimpleSchema({
Proficiencies.attachSchema(Schemas.Proficiency); Proficiencies.attachSchema(Schemas.Proficiency);
Proficiencies.attachBehaviour("softRemovable"); Proficiencies.attachBehaviour("softRemovable");
makeChild(Proficiencies); makeChild(Proficiencies, ["enabled"]);
Proficiencies.allow(CHARACTER_SUBSCHEMA_ALLOW); Proficiencies.allow(CHARACTER_SUBSCHEMA_ALLOW);
Proficiencies.deny(CHARACTER_SUBSCHEMA_DENY); Proficiencies.deny(CHARACTER_SUBSCHEMA_DENY);

View File

@@ -62,6 +62,7 @@ Spells.attachSchema(Schemas.Spell);
Spells.attachBehaviour("softRemovable"); Spells.attachBehaviour("softRemovable");
makeChild(Spells); //children of spell lists makeChild(Spells); //children of spell lists
makeParent(Spells, ["name", "enabled"]); //parents of attacks
Spells.allow(CHARACTER_SUBSCHEMA_ALLOW); Spells.allow(CHARACTER_SUBSCHEMA_ALLOW);
Spells.deny(CHARACTER_SUBSCHEMA_DENY); Spells.deny(CHARACTER_SUBSCHEMA_DENY);

View File

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

View File

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

View File

@@ -10,6 +10,7 @@ Schemas.Item = new SimpleSchema({
value: {type: Number, min: 0, defaultValue: 0, decimal: true}, value: {type: Number, min: 0, defaultValue: 0, decimal: true},
enabled: {type: Boolean, defaultValue: false}, enabled: {type: Boolean, defaultValue: false},
requiresAttunement: {type: Boolean, defaultValue: false}, requiresAttunement: {type: Boolean, defaultValue: false},
"settings.showIncrement": {type: Boolean, defaultValue: false},
color: { color: {
type: String, type: String,
allowedValues: _.pluck(colorOptions, "key"), allowedValues: _.pluck(colorOptions, "key"),

View File

@@ -0,0 +1,27 @@
ChangeLogs = new Mongo.Collection("changeLogs");
Schemas.ChangeLog = new SimpleSchema({
version: {
type: String,
},
changes: {
type: [String],
},
});
ChangeLogs.attachSchema(Schemas.ChangeLog);
ChangeLogs.allow({
insert: function(userId, doc) {
var user = Meteor.users.findOne(userId);
if (user) return _.contains(user.roles, "admin");
},
update: function(userId, doc, fields, modifier) {
var user = Meteor.users.findOne(userId);
if (user) return _.contains(user.roles, "admin");
},
remove: function(userId, doc) {
var user = Meteor.users.findOne(userId);
if (user) return _.contains(user.roles, "admin");
},
});

View File

@@ -0,0 +1,79 @@
Reports = new Mongo.Collection("reports");
Schemas.Report = new SimpleSchema({
owner: {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
title: {
type: String,
trim: false,
optional: true,
},
description: {
type: String,
trim: false,
optional: true,
},
type: {
type: String,
allowedValues: ["bug", "change", "feature", "general"],
defaultValue: "bug",
},
//the immediate impact of doing this action (eg. -1 rages)
severity: {
type: Number,
defaultValue: 5,
min: 1,
max: 10,
},
metaData: {
type: Object,
blackbox: true,
},
});
Reports.attachSchema(Schemas.Report);
Meteor.methods({
insertReport: function(report) {
check(report, {
title: String,
description: String,
type: String,
severity: Number,
metaData: Object,
});
report.owner = this.userId;
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);
if (!_.contains(user.roles, "admin")){
throw new Meteor.Error(
"not admin",
"The user must be an administrator to delete feedback"
);
}
Reports.remove(id);
},
});

View File

@@ -1,20 +1,13 @@
Router.configure({ Router.configure({
loadingTemplate: "loading", loadingTemplate: "loading",
layoutTemplate: "layout", layoutTemplate: "layout",
trackPageView: true,
}); });
Router.plugin("ensureSignedIn", { Router.plugin("ensureSignedIn", {
except: [ only: [
"home", "profile",
"atSignIn", "characterList",
"atSignUp",
"atForgotPassword",
"atResetPwd",
"atEnrollAccount",
"atVerifyEmail",
"atresendVerificationEmail",
"loginButtons",
"notFound",
] ]
}); });
@@ -23,16 +16,19 @@ Router.plugin("dataNotFound", {notFoundTemplate: "notFound"});
Router.map(function() { Router.map(function() {
this.route("/", { this.route("/", {
name: "home", name: "home",
onAfterAction: function() {
document.title = appName;
},
}); });
this.route("characterList", { this.route("characterList", {
path: "/characterList", path: "/characterList",
waitOn: function(){ waitOn: function(){
return Meteor.subscribe("characterList", Meteor.userId()); return subsManager.subscribe("characterList", Meteor.userId());
}, },
data: { data: {
characters: function(){ characters: function(){
return Characters.find({}, {fields: {_id: 1}}); return Characters.find({}, {fields: {_id: 1}, sort: {name: 1}});
} }
}, },
onAfterAction: function() { onAfterAction: function() {
@@ -44,13 +40,13 @@ Router.map(function() {
path: "/character/:_id", path: "/character/:_id",
waitOn: function(){ waitOn: function(){
return [ return [
Meteor.subscribe("singleCharacter", this.params._id, Meteor.userId()), subsManager.subscribe("singleCharacter", this.params._id, Meteor.userId()),
]; ];
}, },
data: function() { data: function() {
var data = Characters.findOne( var data = Characters.findOne(
{_id: this.params._id}, {_id: this.params._id},
{fields: {_id: 1, name: 1, color: 1}} {fields: {_id: 1, name: 1, color: 1, writers: 1, readers: 1}}
); );
return data; return data;
}, },
@@ -61,6 +57,12 @@ Router.map(function() {
document.title = name; document.title = name;
} }
}, },
//analytics
trackPageView: false,
onRun: function() {
window.ga && window.ga("send", "pageview", "/character");
this.next();
},
}); });
this.route("loading", { this.route("loading", {
@@ -74,7 +76,27 @@ Router.map(function() {
}, },
}); });
this.route("/loginButtons", { this.route("/changelog", {
name: "loginButtons", name: "changeLog",
}) waitOn: function() {
return [
subsManager.subscribe("changeLog"),
]
},
data: {
changeLogs: function() {
return ChangeLogs.find({}, {sort: {version: -1}});
}
},
onAfterAction: function() {
document.title = appName;
},
});
this.route("/guide", {
name: "guide",
onAfterAction: function() {
document.title = appName;
},
});
}); });

View File

@@ -13,10 +13,9 @@
"tests" "tests"
], ],
"dependencies": { "dependencies": {
"polymer": "Polymer/polymer#~0.5.4", "polymer": "Polymer/polymer#~0.5.5",
"core-elements": "Polymer/core-elements#~0.5.4", "core-elements": "Polymer/core-elements#~0.5.5",
"paper-elements": "Polymer/paper-elements#~0.5.4", "paper-elements": "Polymer/paper-elements#~0.5.5"
"paper-fab-menu": "cwdoh/paper-fab-menu"
}, },
"resolutions": { "resolutions": {
"core-component-page": "^0.5.0", "core-component-page": "^0.5.0",

View File

@@ -77,10 +77,10 @@ this.GlobalUI = (function() {
var throttleBack = _.throttle(function() { var throttleBack = _.throttle(function() {
history.back(); history.back();
}, 800, {trailing: false}); }, 100, {trailing: false});
GlobalUI.closeDetail = function() { GlobalUI.closeDetail = function() {
if (!!(window.history && window.history.pushState)) { if (window.history && history.pushState && history.state.detail === "opened") {
throttleBack(); throttleBack();
} else { } else {
Session.set("global.ui.detailShow", false); Session.set("global.ui.detailShow", false);

View File

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

@@ -20,6 +20,11 @@ openParentDialog = function(parent, charId, heroId) {
template: "itemDialog", template: "itemDialog",
data: {itemId: parent.id}, data: {itemId: parent.id},
}; };
} else if (parent.collection === "Spells") {
detail = {
template: "spellDialog",
data: {spellId: parent.id},
};
} }
detail.heroId = heroId; detail.heroId = heroId;
detail.charId = charId; detail.charId = charId;

View File

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

View File

@@ -0,0 +1,96 @@
@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

@@ -70,8 +70,8 @@
background-color: #9E9E9E; background-color: #9E9E9E;
} }
.blue-grey { .app-grey {
background-color: #607D8B; background-color: #424242;
} }
.white { .white {

View File

@@ -0,0 +1,37 @@
/*
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,89 @@
@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;
}
//fix tabs and core-toolbar having box shadow
core-toolbar {
box-shadow: none;
}
//give drawer panel a shadow always
core-header-panel[drawer] {
box-shadow: 2px 0px 5px 0px rgba(0,0,0,0.2);
}
//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;
}
//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

@@ -0,0 +1,20 @@
.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,21 @@
td {
padding: 8px;
}
.strengthTable{
width: 100%;
td{
&:nth-child(2) {
text-align: right;
}
&:nth-child(3) {
width: 250px;
}
}
}
.summaryTable {
&:nth-child(3){
text-align: right;
}
}

View File

@@ -1,268 +0,0 @@
root {
display: block;
}
body {
font-family: 'Roboto', 'Helvetica Neue', Helvetica, Arial;
margin: 0;
overflow-x: hidden;
}
body.core-narrow {
padding: 8px;
}
body /deep/ core-menu {
overflow-x: hidden !important;
}
.calculatedValue {
color: #021C33;
font-weight: bold;
}
* {
box-sizing: border-box;
}
td {
padding: 0;
}
table {
border-spacing: 0;
}
.summaryTable td{
text-align: right;
padding: 4px;
min-width: 80px;
}
.summaryTable td:first-child {
text-align: left;
}
hr {
background-color: #444;
opacity: 0.12;
border-width: 0;
color: #444;
height: 1px;
line-height: 0;
margin: 0 -16px;
text-align: center;
}
paper-button {
font-size: 14px;
font-weight: 400;
text-transform: uppercase;
color: #000;
color: rgba(0,0,0,0.87);
letter-spacing: 0.010;
}
core-item {
cursor: pointer;
}
.listRow {
height: 32px;
}
.card {
margin-bottom: 8px;
/*hack to stop flickering*/
-webkit-backface-visibility: hidden;
-webkit-transform: translateX(0);
/*stop divs breaking over divide*/
-webkit-column-break-inside: avoid;
page-break-inside: avoid;
break-inside: avoid;
/*Fixes extra margin at top of columns*/
display: inline-block;
width: 100%;
font-size: 14px;
border-radius: 2px;
background-color: white;
}
.card.double {
padding: 0;
}
.card paper-button {
font-size: 14px;
letter-spacing: 0.01em;
}
.cardHeader {
height: 48px;
padding: 0 16px 0 16px;
align-content: center;
font-size: 14px;
font-weight: 400;
color: rgba(0, 0, 0, 0.54);
}
.clickable, .statCard, .abilityMiniCard {
cursor: pointer;
}
.skillRow {
cursor: pointer;
}
.resourceCards {
padding: 4px 4px 0 4px;
margin-bottom: -4px;
}
.resourceCards .card {
width: 180px;
margin: 4px;
flex-grow: 1;
flex-shrink: 1;
}
.grey-background, body {
background-color: #E0E0E0;
}
.center {
text-align: center;
}
.screen-center {
position: fixed;
top: 50%;
left: 50%;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
.scroll-y {
overflow-y: auto;
}
.fab-buffer {
height: 88px;
width: 100%;
order: 999;
}
*[hidden] {
visibility: hidden;
}
.hidden{
opacity: 0;
}
@media (max-width: 640px) {
html /deep/ paper-action-dialog[global-dialog] {
top: 0 !important;
left: 0 !important;
width: 100%;
height: 100%;
margin: 0;
border-radius: 0;
}
}
.floatyButton {
position: absolute;
bottom: 24px;
right: 24px;
}
.wideTable td {
padding: 4px 8px 4px 8px;
}
.wideTable table {
padding: 8px;
}
paper-fab-menu /deep/ .container {
padding: 24px !important;
}
paper-slider {
margin-left: -8px;
}
.list-subhead {
color: rgba(0,0,0,0.54);
font-size: 14px;
height: 40px;
padding-left: 16px;
font-weight: 500;
}
.whiteTop {
cursor: initial;
border-bottom: black solid 0.5px;
border-bottom: rgba(0,0,0,0.12) solid 1px;
background: white;
padding: 16px;
position: relative;
border-radius: 2px 2px 0 0;
}
.whiteTop paper-icon-button {
margin: -8px;
}
.fullwidth {
width: 100%;
}
.padded {
padding: 16px;
}
.listPadded {
padding: 0 0 16px 0;
}
.rightPadded {
padding-right: 16px;
}
.bottomPadded {
padding-bottom: 16px;
}
.sideMargin {
margin-left: 16px;
margin-right: 16px;
}
.vertMargin {
margin-top: 16px;
margin-bottom: 16px;
}
.spaceAfter {
margin-bottom: 8px;
}
.s {
padding: 0 0 16px 0;
}
.leftRound{
border-radius: 2px 0 0 2px;
}
.preline {
white-space: pre-line;
}
.prewrap{
white-space: pre-wrap;
}

View File

@@ -18,7 +18,7 @@
letter-spacing: 0; letter-spacing: 0;
} }
.white-text .display1{ .white-text .display1, .white-text.display1{
color: rgba(255,255,255,0.54); color: rgba(255,255,255,0.54);
} }

View File

@@ -3,36 +3,26 @@
<div layout vertical flex> <div layout vertical flex>
<div layout horizontal> <div layout horizontal>
<!--attackBonus--> <!--attackBonus-->
<paper-input id="attackBonusInput" <paper-input class="attackBonusInput"
label="Attack Bonus" label="Attack Bonus"
floatinglabel floatinglabel
value={{attackBonus}} value={{attackBonus}}
flex></paper-input> flex></paper-input>
<!--details--> <!--details-->
<paper-input id="detailInput" <paper-input class="detailInput"
label="Details" label="Details"
floatinglabel floatinglabel
value={{details}}></paper-input> value={{details}}></paper-input>
</div> </div>
<div layout horizontal> <div layout horizontal>
<!--DamageType-->
<paper-dropdown-menu id="damageDiceDropdown" label="Damage Dice">
<paper-dropdown layered class="dropdown">
<core-menu class="menu" selected={{damageDice}}>
{{#each DAMAGE_DICE}}
<paper-item name={{this}} class="containerMenuItem">{{this}}</paper-item>
{{/each}}
</core-menu>
</paper-dropdown>
</paper-dropdown-menu>
<!--damageBonus--> <!--damageBonus-->
<paper-input id="damageInput" <paper-input class="damageInput"
label="Damage Bonus" label="Damage"
floatinglabel floatinglabel
value={{damageBonus}} value={{damage}}
flex></paper-input> flex></paper-input>
<!--DamageType--> <!--DamageType-->
<paper-dropdown-menu id="damageTypeDropdown" label="Damage Type"> <paper-dropdown-menu class="damageTypeDropdown" label="Damage Type">
<paper-dropdown layered class="dropdown"> <paper-dropdown layered class="dropdown">
<core-menu class="menu" selected={{damageType}}> <core-menu class="menu" selected={{damageType}}>
{{#each damageTypes}} {{#each damageTypes}}
@@ -44,6 +34,6 @@
</div> </div>
</div> </div>
<!--Delete Button--> <!--Delete Button-->
<paper-icon-button id="deleteAttack" role="button" tabindex="0" icon="delete" aria-label="Delete"></paper-icon-button> <paper-icon-button class="deleteAttack" role="button" tabindex="0" icon="delete" aria-label="Delete"></paper-icon-button>
</div> </div>
</template> </template>

View File

@@ -1,38 +1,43 @@
var damageTypes = ["bludgeoning", "piercing", "slashing", var damageTypes = [
"acid", "cold", "fire", "force", "lightning", "necrotic", "bludgeoning",
"poison", "psychic", "radiant", "thunder"]; "piercing",
"slashing",
"acid",
"cold",
"fire",
"force",
"lightning",
"necrotic",
"poison",
"psychic",
"radiant",
"thunder",
];
Template.attackEdit.events({ Template.attackEdit.events({
"tap #deleteAttack": function(event, instance) { "tap .deleteAttack": function(event, instance) {
Attacks.softRemoveNode(this._id); Attacks.softRemoveNode(this._id);
GlobalUI.deletedToast(this._id, "Attacks", "Attack"); GlobalUI.deletedToast(this._id, "Attacks", "Attack");
}, },
"change #attackBonusInput": function(event) { "change .attackBonusInput": function(event) {
var value = event.currentTarget.value; var value = event.currentTarget.value;
Attacks.update(this._id, {$set: {attackBonus: value}}); Attacks.update(this._id, {$set: {attackBonus: value}});
}, },
"change #damageInput": function(event) { "change .damageInput": function(event) {
var value = event.currentTarget.value; var value = event.currentTarget.value;
Attacks.update(this._id, {$set: {damageBonus: value}}); Attacks.update(this._id, {$set: {damage: value}});
}, },
"change #detailInput": function(event) { "change .detailInput": function(event) {
var value = event.currentTarget.value; var value = event.currentTarget.value;
Attacks.update(this._id, {$set: {details: value}}); Attacks.update(this._id, {$set: {details: value}});
}, },
"core-select #damageTypeDropdown": function(event) { "core-select .damageTypeDropdown": function(event) {
var detail = event.originalEvent.detail; var detail = event.originalEvent.detail;
if (!detail.isSelected) return; if (!detail.isSelected) return;
var value = detail.item.getAttribute("name"); var value = detail.item.getAttribute("name");
if (value == this.damageType) return; if (value == this.damageType) return;
Attacks.update(this._id, {$set: {damageType: value}}); Attacks.update(this._id, {$set: {damageType: value}});
}, },
"core-select #damageDiceDropdown": function(event) {
var detail = event.originalEvent.detail;
if (!detail.isSelected) return;
var value = detail.item.getAttribute("name");
if (value == this.damageDice) return;
Attacks.update(this._id, {$set: {damageDice: value}});
}
}); });
Template.attackEdit.helpers({ Template.attackEdit.helpers({
@@ -41,5 +46,5 @@ Template.attackEdit.helpers({
}, },
DAMAGE_DICE: function() { DAMAGE_DICE: function() {
return DAMAGE_DICE; return DAMAGE_DICE;
} },
}); });

View File

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

View File

@@ -0,0 +1,15 @@
<template name="buffDialog">
{{#with buff}}
{{#baseDialog title=name class=colorClass hideEdit=true}}
{{> buffDetails}}
{{/baseDialog}}
{{/with}}
</template>
<template name="buffDetails">
{{#if description}}
<div class="pre-wrap">{{evaluateString charId description}}</div>
{{/if}}
{{> effectsViewList charId=charId parentId=_id}}
</template>

View File

@@ -0,0 +1,5 @@
Template.buffDialog.helpers({
buff: function(){
return Buffs.findOne(this.buffId);
},
});

View File

@@ -1,3 +1,27 @@
<template name="characterSettings"> <template name="characterSettings">
{{#with character}}
<div style="height: 100px;">
<table style="width: 100%;">
<tr>
<td>Hide Spells tab</td>
<td>
<paper-toggle-button id="hideSpellcasting"
checked={{settings.hideSpellcasting}}
touch-action="pan-y">
</paper-toggle-button>
</td>
</tr>
<tr>
<td>Use variant encumbrance</td>
<td>
<paper-toggle-button id="variantEncumbrance"
checked={{settings.useVariantEncumbrance}}
touch-action="pan-y">
</paper-toggle-button>
</td>
</tr>
</table>
</div>
{{/with}}
<paper-button id="doneButton" affirmative> Done </paper-button>
</template> </template>

View File

@@ -1,3 +1,26 @@
Template.characterSettings.events({ Template.characterSettings.helpers({
character: function() {
return Characters.findOne(this._id, {fields: {settings: 1}});
}
});
Template.characterSettings.events({
"change #variantEncumbrance": function(event, instance){
var value = instance.find("#variantEncumbrance").checked;
if (this.settings.useVariantEncumbrance !== value){
Characters.update(
this._id,
{$set: {"settings.useVariantEncumbrance": value}}
);
}
},
"change #hideSpellcasting": function(event, instance){
var value = instance.find("#hideSpellcasting").checked;
if (this.settings.hideSpellcasting !== value){
Characters.update(
this._id,
{$set: {"settings.hideSpellcasting": value}}
);
}
},
}); });

View File

@@ -1,5 +1,17 @@
<template name="shareDialog"> <template name="shareDialog">
<div style="width: 360px;"> <div style="width: 360px;">
<div layout horizontal center>
<div>Who can view this character: </div>
<paper-dropdown-menu class="visibilityDropdown"
label="Visibility">
<paper-dropdown layered class="dropdown">
<core-menu class="menu visibilityMenu" selected={{viewPermission}}>
<paper-item name="whitelist">Only people I share with</paper-item>
<paper-item name="public">Anyone with link</paper-item>
</core-menu>
</paper-dropdown>
</paper-dropdown-menu>
</div>
<div> <div>
{{#if readers.count}} {{#if readers.count}}
<div style="font-weight: 500;"> <div style="font-weight: 500;">
@@ -7,7 +19,7 @@
</div> </div>
{{#each readers}} {{#each readers}}
<div layout horizontal center> <div layout horizontal center>
<div flex>{{username}}</div> <div flex>{{getUserName}}</div>
<paper-icon-button class="deleteShare" icon="delete"></paper-icon-button> <paper-icon-button class="deleteShare" icon="delete"></paper-icon-button>
</div> </div>
{{/each}} {{/each}}

View File

@@ -3,6 +3,10 @@ Template.shareDialog.onCreated(function(){
}); });
Template.shareDialog.helpers({ Template.shareDialog.helpers({
viewPermission: function() {
var char = Characters.findOne(this._id, {fields: {settings: 1}});
return char.settings.viewPermission || "whitelist";
},
readers: function(){ readers: function(){
var char = Characters.findOne(this._id, {fields: {readers: 1}}); var char = Characters.findOne(this._id, {fields: {readers: 1}});
return Meteor.users.find({_id: {$in: char.readers}}); return Meteor.users.find({_id: {$in: char.readers}});
@@ -19,9 +23,20 @@ Template.shareDialog.helpers({
return "User not found"; return "User not found";
} }
}, },
getUserName: function() {
return this.username || "user: " + this._id;
}
}); });
Template.shareDialog.events({ Template.shareDialog.events({
"core-select .visibilityDropdown": function(event){
var detail = event.originalEvent.detail;
if (!detail.isSelected) return;
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": "input #userNameOrEmailInput":
function(event, instance){ function(event, instance){
var userName = instance.find("#userNameOrEmailInput").value; var userName = instance.find("#userNameOrEmailInput").value;

View File

@@ -5,24 +5,35 @@
<div flex> <div flex>
{{name}} {{name}}
</div> </div>
<div> {{#if canEditCharacter _id}}
{{> colorDropdown}} <div>
</div> {{> colorDropdown}}
<paper-menu-button> </div>
<paper-icon-button icon="more-vert" noink></paper-icon-button> <paper-menu-button>
<paper-dropdown class="dropdown" halign="right"> <paper-icon-button icon="more-vert" noink></paper-icon-button>
<core-menu class="menu" style="color: black; color: rgba(0,0,0,0.87);"> <paper-dropdown class="dropdown" halign="right">
<paper-item id="deleteCharacter"><core-icon icon="delete"></core-icon>Delete</paper-item> <core-menu class="menu" style="color: black; color: rgba(0,0,0,0.87);">
<paper-item id="shareCharacter"><core-icon icon="social:share"></core-icon>Share</paper-item> <paper-item id="deleteCharacter">
</core-menu> <core-icon icon="delete"></core-icon>Delete
</paper-dropdown> </paper-item>
</paper-menu-button> <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> <div class="bottom fit" horizontal layout>
<paper-tabs flex horizontal center layout id="characterSheetTabs" selected={{selectedTab}} class="{{colorClass}}"> <paper-tabs flex horizontal center layout id="characterSheetTabs" selected={{selectedTab}} class="{{colorClass}}">
<paper-tab name="stats">Stats</paper-tab> <paper-tab name="stats">Stats</paper-tab>
<paper-tab name="features">Features</paper-tab> <paper-tab name="features">Features</paper-tab>
<paper-tab name="inventory">Inventory</paper-tab> <paper-tab name="inventory">Inventory</paper-tab>
{{#unless hideSpellcasting}}
<paper-tab name="spells">Spells</paper-tab> <paper-tab name="spells">Spells</paper-tab>
{{/unless}}
<paper-tab name="persona">Persona</paper-tab> <paper-tab name="persona">Persona</paper-tab>
<paper-tab name="journal">Journal</paper-tab> <paper-tab name="journal">Journal</paper-tab>
</paper-tabs> </paper-tabs>
@@ -33,7 +44,9 @@
<section flex name="stats">{{> stats}}</section> <section flex name="stats">{{> stats}}</section>
<section flex name="features">{{> features}}</section> <section flex name="features">{{> features}}</section>
<section flex name="inventory">{{> inventory}}</section> <section flex name="inventory">{{> inventory}}</section>
{{#unless hideSpellcasting}}
<section flex name="spells">{{> spells}}</section> <section flex name="spells">{{> spells}}</section>
{{/unless}}
<section flex name="persona">{{> persona}}</section> <section flex name="persona">{{> persona}}</section>
<section flex name="journal">{{> journal}}</section> <section flex name="journal">{{> journal}}</section>
</core-animated-pages> </core-animated-pages>

View File

@@ -1,6 +1,9 @@
Template.characterSheet.created = function(){ Template.characterSheet.onCreated(function() {
//default to the first tab
Session.setDefault(this.data._id + ".selectedTab", "stats"); Session.setDefault(this.data._id + ".selectedTab", "stats");
}; //watch this character and make sure their encumbrance is updated
trackEncumbranceConditions(this.data._id, this);
});
var setTab = function(charId, tab){ var setTab = function(charId, tab){
return Session.set(charId + ".selectedTab", tab); return Session.set(charId + ".selectedTab", tab);
@@ -13,7 +16,11 @@ var getTab = function(charId){
Template.characterSheet.helpers({ Template.characterSheet.helpers({
selectedTab: function(){ selectedTab: function(){
return getTab(this._id); return getTab(this._id);
} },
hideSpellcasting: function() {
var char = Characters.findOne(this._id);
return char && char.settings.hideSpellcasting;
},
}); });
Template.characterSheet.events({ Template.characterSheet.events({
@@ -40,4 +47,11 @@ Template.characterSheet.events({
template: "shareDialog", template: "shareDialog",
}); });
}, },
"tap #characterSettings": function(event, instance){
GlobalUI.showDialog({
heading: this.name + " Settings",
data: this,
template: "characterSettings",
});
},
}); });

View File

@@ -1,21 +1,13 @@
body /deep/ #statGroupDropDown { html /deep/ .operationDropDown {
width: 120px; width: 152px;
} }
body /deep/ #statDropDown { html /deep/ .statDropDown {
width: 120px; width: 152px;
} }
body /deep/ #operationDropDown { html /deep/ .damageMultiplierDropDown {
width: 100px; width: 152px;
}
body /deep/ #damageMultiplierDropDown {
width: 120px;
}
body /deep/ #proficiencyDropDown {
width: 120px;
} }
html /deep/ .effectEdit paper-input { html /deep/ .effectEdit paper-input {
@@ -24,6 +16,7 @@ html /deep/ .effectEdit paper-input {
} }
html /deep/ .effectEdit { html /deep/ .effectEdit {
height: 64px;
display: flex; display: flex;
align-items: flex-end; align-items: flex-end;
} }

View File

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

View File

@@ -42,6 +42,7 @@ var stats = [
{stat: "rageDamage", name: "Rage Damage", group: "Stats"}, {stat: "rageDamage", name: "Rage Damage", group: "Stats"},
{stat: "expertiseDice", name: "Expertise Dice", group: "Stats"}, {stat: "expertiseDice", name: "Expertise Dice", group: "Stats"},
{stat: "superiorityDice", name: "Superiority Dice", group: "Stats"}, {stat: "superiorityDice", name: "Superiority Dice", group: "Stats"},
{stat: "carryMultiplier", name: "Carry Capacity Multiplier", group: "Stats"},
{stat: "level1SpellSlots", name: "level 1", group: "Spell Slots"}, {stat: "level1SpellSlots", name: "level 1", group: "Spell Slots"},
{stat: "level2SpellSlots", name: "level 2", group: "Spell Slots"}, {stat: "level2SpellSlots", name: "level 2", group: "Spell Slots"},
{stat: "level3SpellSlots", name: "level 3", group: "Spell Slots"}, {stat: "level3SpellSlots", name: "level 3", group: "Spell Slots"},
@@ -93,24 +94,17 @@ var skillOperations = [
{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({ Template.effectEdit.helpers({
selectedStatGroup: function(){
return Template.instance().selectedStatGroup.get();
},
statGroups: function(){ statGroups: function(){
return statGroupNames; return statGroupNames;
}, },
stats: function(){ stats: function(){
var group = Template.instance().selectedStatGroup.get(); var group = this;
return statGroups[group]; return statGroups[group];
}, },
operations: function(){ 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 === "Weakness/Resistance") return null;
if (group === "Saving Throws" || group === "Skills"){ if (group === "Saving Throws" || group === "Skills"){
return skillOperations; return skillOperations;
@@ -120,7 +114,8 @@ Template.effectEdit.helpers({
}, },
effectValueTemplate: function(){ effectValueTemplate: function(){
//resistance/vulnerability template //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"; if (group === "Weakness/Resistance") return "multiplierEffectValue";
var op = this.operation; var op = this.operation;
@@ -144,25 +139,6 @@ Template.effectEdit.events({
Effects.softRemoveNode(this._id); Effects.softRemoveNode(this._id);
GlobalUI.deletedToast(this._id, "Effects", "Effect"); GlobalUI.deletedToast(this._id, "Effects", "Effect");
}, },
"core-select .statGroupDropDown": function(event, instance){
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){ "core-select .statDropDown": function(event){
var detail = event.originalEvent.detail; var detail = event.originalEvent.detail;
if (!detail.isSelected) return; if (!detail.isSelected) return;

View File

@@ -57,40 +57,40 @@ var stats = {
"d12HitDice":{"name":"d12 Hit Dice"}, "d12HitDice":{"name":"d12 Hit Dice"},
"acidMultiplier":{"name":"Acid damage", "group": "Weakness/Resistance"}, "acidMultiplier":{"name":"Acid damage", "group": "Weakness/Resistance"},
"bludgeoningMultiplier":{ "bludgeoningMultiplier":{
"name":"Bludgeoning damage", "group": "Weakness/Resistance" "name":"Bludgeoning damage", "group": "Weakness/Resistance",
}, },
"coldMultiplier":{ "coldMultiplier":{
"name":"Cold damage", "group": "Weakness/Resistance" "name":"Cold damage", "group": "Weakness/Resistance",
}, },
"fireMultiplier":{ "fireMultiplier":{
"name":"Fire damage", "group": "Weakness/Resistance" "name":"Fire damage", "group": "Weakness/Resistance",
}, },
"forceMultiplier":{ "forceMultiplier":{
"name":"Force damage", "group": "Weakness/Resistance" "name":"Force damage", "group": "Weakness/Resistance",
}, },
"lightningMultiplier":{ "lightningMultiplier":{
"name":"Lightning damage", "group": "Weakness/Resistance" "name":"Lightning damage", "group": "Weakness/Resistance",
}, },
"necroticMultiplier":{ "necroticMultiplier":{
"name":"Necrotic damage", "group": "Weakness/Resistance" "name":"Necrotic damage", "group": "Weakness/Resistance",
}, },
"piercingMultiplier":{ "piercingMultiplier":{
"name":"Piercing damage", "group": "Weakness/Resistance" "name":"Piercing damage", "group": "Weakness/Resistance",
}, },
"poisonMultiplier":{ "poisonMultiplier":{
"name":"Poison damage", "group": "Weakness/Resistance" "name":"Poison damage", "group": "Weakness/Resistance",
}, },
"psychicMultiplier":{ "psychicMultiplier":{
"name":"Psychic damage", "group": "Weakness/Resistance" "name":"Psychic damage", "group": "Weakness/Resistance",
}, },
"radiantMultiplier":{ "radiantMultiplier":{
"name":"Radiant damage", "group": "Weakness/Resistance" "name":"Radiant damage", "group": "Weakness/Resistance",
}, },
"slashingMultiplier":{ "slashingMultiplier":{
"name":"Slashing damage", "group": "Weakness/Resistance" "name":"Slashing damage", "group": "Weakness/Resistance",
}, },
"thunderMultiplier":{ "thunderMultiplier":{
"name":"Thunder damage", "group": "Weakness/Resistance" "name":"Thunder damage", "group": "Weakness/Resistance",
}, },
}; };
@@ -110,8 +110,8 @@ var operations = {
Template.effectView.helpers({ Template.effectView.helpers({
sourceName: function(){ sourceName: function(){
var id = this.parent.id; var id = this.parent.id;
if(!id) return; if (!id) return;
switch(this.parent.collection){ switch (this.parent.collection){
case "Features": case "Features":
return "Feature - " + Features.findOne(id, {fields: {name: 1}}).name; return "Feature - " + Features.findOne(id, {fields: {name: 1}}).name;
case "Classes": case "Classes":
@@ -130,33 +130,39 @@ Template.effectView.helpers({
return stats[this.stat] && stats[this.stat].name || "No Stat"; return stats[this.stat] && stats[this.stat].name || "No Stat";
}, },
operationName: function(){ operationName: function(){
if(this.operation === "proficiency" || if (this.operation === "proficiency" ||
this.operation === "conditional") return null; this.operation === "conditional") return null;
if(stats[this.stat].group === "Weakness/Resistance") return null; if (stats[this.stat] && stats[this.stat].group === "Weakness/Resistance")
if(this.operation === "add" && evaluateEffect(this.charId, this) < 0) return null; return null;
return operations[this.operation] && operations[this.operation].name || "No Operation"; if (this.operation === "add" && evaluateEffect(this.charId, this) < 0)
return null;
return operations[this.operation] &&
operations[this.operation].name || "No Operation";
}, },
statValue: function(){ statValue: function(){
if(this.operation === "advantage" || if (this.operation === "advantage" ||
this.operation === "disadvantage" || this.operation === "disadvantage" ||
this.operation === "fail"){ this.operation === "fail"){
return null; return null;
} }
if(this.operation === "proficiency"){ if (this.operation === "proficiency"){
if(this.value == 0.5 || this.calculation == 0.5) return "Half Proficiency"; if (this.value == 0.5 || this.calculation == 0.5)
if(this.value == 1 || this.calculation == 1) return "Proficiency"; return "Half Proficiency";
if(this.value == 2 || this.calculation == 2) return "Double 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; return this.calculation || this.value;
} }
if(stats[this.stat].group === "Weakness/Resistance"){ if (stats[this.stat] && stats[this.stat].group === "Weakness/Resistance"){
if(this.value === 0.5) return "Resistance"; if (this.value === 0.5) return "Resistance";
if(this.value === 2) return "Vulnerability"; if (this.value === 2) return "Vulnerability";
if(this.value === 0) return "Immunity"; if (this.value === 0) return "Immunity";
} }
var value = evaluateEffect(this.charId, this); var value = evaluateEffect(this.charId, this);
if(_.isNumber(value)) return value; if (_.isNumber(value)) return value;
return this.calculation || this.value; return this.calculation || this.value;
} },
}); });

View File

@@ -32,7 +32,7 @@
{{/if}} {{/if}}
{{#if description}} {{#if description}}
<div class="prewrap">{{description}}</div> <div>{{#markdown}}{{evaluateString charId description}}{{/markdown}}</div>
{{/if}} {{/if}}
{{> effectsViewList charId=charId parentId=_id}} {{> effectsViewList charId=charId parentId=_id}}

View File

@@ -1,7 +1,7 @@
<template name="features"> <template name="features">
<div fit> <div fit>
<div class="scroll-y" fit> <div class="scroll-y" fit>
<div class="containers"> <div class="column-container">
<!--expertiseDice--> <!--expertiseDice-->
{{>resource name="expertiseDice" title="Expertise Dice" color="teal" char=this}} {{>resource name="expertiseDice" title="Expertise Dice" color="teal" char=this}}
<!--ki--> <!--ki-->
@@ -14,27 +14,27 @@
{{>resource name="superiorityDice" title="Superiority Dice" color="teal" char=this}} {{>resource name="superiorityDice" title="Superiority Dice" color="teal" char=this}}
<!--Attacks--> <!--Attacks-->
<paper-shadow class="card container" hero-id="main" {{detailHero}}> <paper-shadow class="card">
<div class="whiteTop" hero-id="toolbar" layout horizontal center {{detailHero}}> <div class="top white">
<div flex> Attacks
<div class="containerName subhead">Attacks</div>
</div>
<!--<paper-icon-button class="black54" id="addAttackButton" icon="add"></paper-icon-button>-->
</div> </div>
<div class="containerMain listPadded"> <div class="bottom list">
{{#each attacks}} {{#each attacks}}
<div class="itemSlot"> <div class="item-slot">
<paper-item class="white attack" hero-id="main" {{detailHero}}> <div class="flexible attack item"
<div layout horizontal class="fullwidth"> hero-id="main" {{detailHero}}>
<div class="headline rightPadded" layout horizontal center> <div layout horizontal>
<div class="headline"
style="margin-right: 16px;"
layout horizontal center>
{{evaluateSigned ../_id attackBonus}} {{evaluateSigned ../_id attackBonus}}
</div> </div>
<div layout vertical flex> <div flex layout vertical>
<div class="body2"> <div class="body2">
{{name}} {{name}}
</div> </div>
<div> <div>
{{damageDice}}&nbsp;{{{evaluateSignedSpaced ../_id damageBonus}}}&nbsp;{{damageType}} {{evaluateString ../_id damage}}&nbsp;{{damageType}}
</div> </div>
{{#if details}} {{#if details}}
<div class="caption"> <div class="caption">
@@ -43,34 +43,32 @@
{{/if}} {{/if}}
</div> </div>
</div> </div>
</paper-item> </div>
</div> </div>
{{/each}} {{/each}}
</div> </div>
</paper-shadow> </paper-shadow>
<!--Proficiencies--> <!--Proficiencies-->
<paper-shadow class="card container" hero-id="main" {{detailHero "proficiencies"}}> <paper-shadow class="card">
<div id="proficiencies" <div class="white top">
class="whiteTop" Proficiencies
layout horizontal center>
<div class="containerName subhead">Proficiencies</div>
</div> </div>
<div flex class="containerMain listPadded"> <div flex class="bottom list">
{{#if weaponProfs.count}} {{#if weaponProfs.count}}
<div class="list-subhead" layout horizontal center>Weapons</div> <div class="subhead">Weapons</div>
{{/if}} {{/if}}
{{#each weaponProfs}} {{#each weaponProfs}}
{{> proficiencyListItem}} {{> proficiencyListItem}}
{{/each}} {{/each}}
{{#if armorProfs.count}} {{#if armorProfs.count}}
<div class="list-subhead" layout horizontal center>Armor</div> <div class="subhead">Armor</div>
{{/if}} {{/if}}
{{#each armorProfs}} {{#each armorProfs}}
{{> proficiencyListItem}} {{> proficiencyListItem}}
{{/each}} {{/each}}
{{#if toolProfs.count}} {{#if toolProfs.count}}
<div class="list-subhead" layout horizontal center>Tools</div> <div class="subhead">Tools</div>
{{/if}} {{/if}}
{{#each toolProfs}} {{#each toolProfs}}
{{> proficiencyListItem}} {{> proficiencyListItem}}
@@ -80,23 +78,44 @@
<!--features--> <!--features-->
{{#each features}} {{#each features}}
<paper-shadow class="card container featureCard" hero-id="main" {{detailHero}}> <paper-shadow class="card featureCard"
<div class="containerTop {{colorClass}}" hero-id="toolbar" layout horizontal center {{detailHero}}> hero-id="main" {{detailHero}}>
<paper-ripple fit></paper-ripple> <div class="top {{colorClass}} subhead"
<div class="containerName subhead" hero-id="title" flex {{detailHero}}>{{name}}</div> layout horizontal
{{#if hasUses}}<div class="subhead" style="margin-right: 8px">{{usesLeft}}/{{usesValue}}</div>{{/if}} hero-id="toolbar" {{detailHero}}>
<paper-ripple fit></paper-ripple> <div flex hero-id="title" {{detailHero}}>
{{name}}
</div>
{{#if hasUses}}
<div style="margin-right: 8px">
{{usesLeft}}/{{usesValue}}
</div>
{{/if}}
{{#if canEnable}} {{#if canEnable}}
<core-tooltip label="Feature enabled" position="left"> <core-tooltip label="Feature enabled"
<paper-checkbox class="enabledCheckbox" checked={{enabled}}></paper-checkbox> position="left">
<paper-checkbox class="enabledCheckbox"
checked={{enabled}}
disabled={{#unless canEditCharacter charId}}true{{/unless}}>
</paper-checkbox>
</core-tooltip> </core-tooltip>
{{/if}} {{/if}}
</div> </div>
{{#if description}}<div flex class="containerMain body1 featureDescription">{{description}}</div>{{/if}} {{#if description}}
<div flex class="bottom">
{{#markdown}}{{evaluateString charId shortDescription}}{{/markdown}}
</div>
{{/if}}
{{#if hasUses}} {{#if hasUses}}
<div class="containerFoot" layout horizontal center end-justified> <div layout horizontal center end-justified>
<paper-button class="useFeature" disabled={{noUsesLeft}}>Use</paper-button> <paper-button class="useFeature"
<paper-button class="resetFeature" disabled={{usesFull}}>Reset</paper-button> disabled={{noUsesLeft}}>
Use
</paper-button>
<paper-button class="resetFeature"
disabled={{usesFull}}>
Reset
</paper-button>
</div> </div>
{{/if}} {{/if}}
</paper-shadow> </paper-shadow>
@@ -104,31 +123,43 @@
</div> </div>
<div class="fab-buffer"></div> <div class="fab-buffer"></div>
</div> </div>
<paper-fab id="addFeature" {{#if canEditCharacter _id}}
class="floatyButton" <paper-fab id="addFeature"
icon="add" class="floatyButton"
title="Add" icon="add"
role="button" title="Add"
tabindex="0" role="button"
aria-label="Add" tabindex="0"
hero-id="main"></paper-fab> aria-label="Add"
hero-id="main"></paper-fab>
{{/if}}
</div> </div>
</template> </template>
<template name="resource"> <template name="resource">
{{#if char.attributeBase name}} {{#if characterCalculate "attributeBase" char._id name}}
<paper-shadow class="card container" hero-id="main" {{detailHero name char._id}} layout horizontal> <paper-shadow class="card"
<div class="containerLeft {{getColor}}" hero-id="toolbar" {{detailHero name char._id}} > hero-id="main" {{detailHero name char._id}}
<div class="resourceButtons"> layout horizontal>
<paper-icon-button class="resourceUp" icon="arrow-drop-up" disabled={{cantIncrement}}></paper-icon-button> <div class="left {{getColor}} display1 white-text"
<paper-icon-button class="resourceDown" icon="arrow-drop-down" disabled={{cantDecrement}}></paper-icon-button> hero-id="toolbar" {{detailHero name char._id}}
layout horizontal center>
<div style="margin-right: 8px;">
<paper-icon-button class="resourceUp"
icon="arrow-drop-up"
disabled={{cantIncrement}}>
</paper-icon-button>
<paper-icon-button class="resourceDown"
icon="arrow-drop-down"
disabled={{cantDecrement}}>
</paper-icon-button>
</div> </div>
<div class="resourceValue">{{char.attributeValue name}}</div> <div>{{characterCalculate "attributeValue" char._id name}}</div>
<!--<div class="resourceMax">{{char.attributeBase name}}</div>--> <!--<div>/{{char.attributeBase name}}</div>-->
</div> </div>
<div class="containerRight clickable" flex relative horizontal layout center> <div class="right clickable"
flex layout horizontal center>
{{title}} {{title}}
<paper-ripple fit></paper-ripple>
</div> </div>
</paper-shadow> </paper-shadow>
{{/if}} {{/if}}

View File

@@ -3,14 +3,19 @@ Template.features.helpers({
var features = Features.find({charId: this._id}, {sort: {color: 1, name: 1}}); var features = Features.find({charId: this._id}, {sort: {color: 1, name: 1}});
return features; return features;
}, },
shortDescription: function() {
if (_.isString(this.description)){
return this.description.split(/^( *[-*_]){3,} *(?:\n+|$)/m)[0];
}
},
hasUses: function(){ hasUses: function(){
return this.usesValue() > 0; return this.usesValue() > 0;
}, },
noUsesLeft: function(){ noUsesLeft: function(){
return this.usesLeft() <= 0; return this.usesLeft() <= 0 || !canEditCharacter(this.charId);
}, },
usesFull: function(){ usesFull: function(){
return this.usesLeft() >= this.usesValue(); return this.usesLeft() >= this.usesValue() || !canEditCharacter(this.charId);
}, },
colorClass: function(){ colorClass: function(){
return getColorClass(this.color); return getColorClass(this.color);
@@ -65,7 +70,7 @@ Template.features.events({
} }
}); });
}, },
"tap .featureCard .containerTop": function(event){ "tap .featureCard .top": function(event){
var featureId = this._id; var featureId = this._id;
var charId = Template.parentData()._id; var charId = Template.parentData()._id;
GlobalUI.setDetail({ GlobalUI.setDetail({
@@ -96,16 +101,19 @@ Template.features.events({
Template.resource.helpers({ Template.resource.helpers({
cantIncrement: function(){ cantIncrement: function(){
var baseBigger = this.char.attributeValue(this.name) < var value = Characters.calculate.attributeValue(this.char._id, this.name);
this.char.attributeBase(this.name); var base = Characters.calculate.attributeBase(this.char._id, this.name);
return !baseBigger; var baseBigger = value < base;
return !baseBigger || !canEditCharacter(this.char._id);
}, },
cantDecrement: function(){ cantDecrement: function(){
var valuePositive = this.char.attributeValue(this.name) > 0; var value = Characters.calculate.attributeValue(this.char._id, this.name);
return !valuePositive; var valuePositive = value > 0;
return !valuePositive || !canEditCharacter(this.char._id);
}, },
getColor: function(){ 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; return this.color;
} else { } else {
return "grey"; return "grey";
@@ -115,20 +123,23 @@ Template.resource.helpers({
Template.resource.events({ Template.resource.events({
"tap .resourceUp": function(event){ "tap .resourceUp": function(event){
if (this.char.attributeValue(this.name) < this.char.attributeBase(this.name)){ 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: {}}; var modifier = {$inc: {}};
modifier.$inc[this.name + ".adjustment"] = 1; modifier.$inc[this.name + ".adjustment"] = 1;
Characters.update(this.char._id, modifier, {validate: false}); Characters.update(this.char._id, modifier, {validate: false});
} }
}, },
"tap .resourceDown": function(event){ "tap .resourceDown": function(event){
if (this.char.attributeValue(this.name) > 0){ var value = Characters.calculate.attributeValue(this.char._id, this.name);
if (value > 0){
var modifier = {$inc: {}}; var modifier = {$inc: {}};
modifier.$inc[this.name + ".adjustment"] = -1; modifier.$inc[this.name + ".adjustment"] = -1;
Characters.update(this.char._id, modifier, {validate: false}); Characters.update(this.char._id, modifier, {validate: false});
} }
}, },
"tap .containerRight": function(event, instance) { "tap .right": function(event, instance) {
GlobalUI.setDetail({ GlobalUI.setDetail({
template: "attributeDialog", template: "attributeDialog",
data: {name: this.title, statName: this.name, charId: this.char._id}, data: {name: this.title, statName: this.name, charId: this.char._id},

View File

@@ -0,0 +1,22 @@
<template name="carryCapacityBar">
<div class="carryCapacityBar">
<div class="carriedWeightBar"
style="width: {{carriedPercent}}%;
background-color: {{carriedColor}}">
</div>
<div class="tick"
style="width: 33.333%;">
</div>
<div class="tick"
style="width: 66.666%;">
</div>
</div>
{{#if overCarriedPercent}}
<div class="carryCapacityBar">
<div class="carriedWeightBar"
style="width: {{overCarriedPercent}}%;
background-color: {{overCarriedColor}}">
</div>
</div>
{{/if}}
</template>

View File

@@ -0,0 +1,67 @@
var getFractionCarried = function(char) {
//find out the weight
var weight = 0;
Containers.find(
{charId: char._id, isCarried: true}
).forEach(function(container){
weight += container.totalWeight();
});
Items.find(
{charId: char._id, "parent.id": char._id},
{fields: {weight : 1, quantity: 1}}
).forEach(function(item){
weight += item.totalWeight();
});
//get strength
var strength = Characters.calculate.attributeValue(char._id, "strength");
var carryMultiplier = Characters.calculate
.attributeValue(char._id, "carryMultiplier");
var capacity = strength * 15 * carryMultiplier;
return weight / capacity;
};
Template.carryCapacityBar.onCreated(function() {
var self = this;
self.carriedFraction = new ReactiveVar(0);
self.autorun(function() {
self.carriedFraction.set(getFractionCarried(self.data));
});
});
Template.carryCapacityBar.helpers({
carriedPercent: function() {
var percent = 100 * Template.instance().carriedFraction.get();
return percent > 100 ? 100 : percent;
},
overCarriedPercent: function() {
var percent = 100 * Template.instance().carriedFraction.get();
var overPercent = percent - 100;
if (overPercent < 0) return 0;
if (overPercent > 100) return 100;
return overPercent;
},
carriedColor: function() {
var frac = Template.instance().carriedFraction.get();
if (frac < 1 / 3){
return "#2196F3";
} else if (frac < 2 / 3){
return "#CDDC39";
} else if (frac < 1) {
return "#FFC107";
} else {
return "#F44336";
}
},
overCarriedColor: function() {
var frac = Template.instance().carriedFraction.get();
if (frac < 1 / 3){
return "#2196F3";
} else if (frac < 2 / 3){
return "#CDDC39";
} else if (frac < 1) {
return "#FFC107";
} else {
return "#F44336";
}
},
});

View File

@@ -0,0 +1,14 @@
.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

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

View File

@@ -0,0 +1,20 @@
Template.carryDialog.helpers({
carriedWeight: function() {
var weight = 0;
Containers.find(
{charId: this.charId, isCarried: true}
).forEach(function(container){
weight += container.totalWeight();
});
Items.find(
{charId: this.charId, "parent.id": this.charId},
{fields: {weight : 1, quantity: 1}}
).forEach(function(item){
weight += item.totalWeight();
});
return weight;
},
color: function() {
if (this.color) return this.color + " white-text";
},
});

View File

@@ -34,13 +34,13 @@
<template name="containerView"> <template name="containerView">
<div layout horizontal wrap center justified> <div layout horizontal wrap center justified>
<table class="summaryTable fullwidth"> <table class="summaryTable fullwidth">
<tr><td>Container</td><td>{{weight}}lbs</td><td>{{longValueString value}}</td></tr> <tr><td>Container</td><td>{{round weight}}lbs</td><td>{{longValueString value}}</td></tr>
<tr><td>Contents</td><td>{{contentsWeight}}lbs</td><td>{{longValueString contentsValue}}</td></tr> <tr><td>Contents</td><td>{{round contentsWeight}}lbs</td><td>{{longValueString contentsValue}}</td></tr>
<tr class="body2"><td>Total</td><td>{{totalWeight}}lbs</td><td>{{longValueString totalValue}}</td></tr> <tr class="body2"><td>Total</td><td>{{round totalWeight}}lbs</td><td>{{longValueString totalValue}}</td></tr>
</table> </table>
</div> </div>
{{#if description}} {{#if description}}
<hr class="vertMargin"> <hr class="vertMargin">
<div class="prewrap">{{description}}</div> <div>{{#markdown}}{{evaluateString charId description}}{{/markdown}}</div>
{{/if}} {{/if}}
</template> </template>

View File

@@ -1,152 +0,0 @@
div#stats {
-webkit-column-width: 200px;
-moz-column-width: 200px;
column-width: 200px;
-webkit-column-count: 4;
-moz-column-count: 4;
column-count: 4;
}
.containers {
-webkit-column-width: 300px;
-moz-column-width: 300px;
column-width: 300px;
-webkit-column-gap: 8px;
-moz-column-gap: 8px;
column-gap: 8px;
-moz-column-fill: balance;
column-fill: balance;
padding: 8px;
}
.containerLeft {
padding: 16px 16px 16px 24px;
display: flex;
justify-content: center;
flex-direction: row;
border-radius: 2px 0 0 2px;
/* same style as display-1 */
font-size: 34px;
font-weight: 400;
color: #ffffff;
color: rgba(255,255,255,0.54);
letter-spacing: 0;
}
.statCard .containerLeft {
padding: 16px;
}
.containerRight {
padding: 16px;
/* same style as subhead */
font-size: 16px;
font-weight: 400;
margin: 0;
color: #000;
color: rgba(0,0,0,0.87);
letter-spacing: 0.010em;
}
.resourceValue {
display: inline-block;
}
.resourceMax {
display: inline-block;
align-self: flex-end;
/* same style as subhead */
font-size: 16px;
font-weight: 400;
margin: 0;
color: #fff;
color: rgba(255,255,255,0.54);
letter-spacing: 0.010em;
}
.resourceMax:before {
content: "/";
}
.resourceButtons {
margin: -16px 8px -16px -16px;
align-self: center;
}
.resourceButtons paper-icon-button{
width: 32px;
height: 32px;
padding: 0;
display: block;
}
.resourceButtons paper-icon-button[disabled]{
color: rgba(255, 255, 255, 0.26);
}
.resourceButtons /deep/ core-icon {
width: 32px;
height: 32px;
}
.containerTop {
cursor: pointer;
padding: 16px;
position: relative;
border-radius: 2px 2px 0 0;
}
.equipmentTop {
padding: 16px;
border-bottom: rgba(0,0,0,0.12) solid 1px;
}
.containerMain {
padding: 16px;
}
.equipmentMain {
padding-bottom: 16px;
}
.inventoryItem {
background: white;
transition: box-shadow 0.3s ease,
opacity 0.5s ease-in-out;
height: 40px;
margin: 1px 0 1px 0;
font-size: 16px;
color: rgba(0,0,0,0.87);
letter-spacing: 0.010em;
}
.inventoryItem core-icon, .inventoryItem paper-icon-button {
color: rgba(0,0,0,0.54);
}
.inventoryItem core-icon {
margin-right: 16px;
}
.inventoryItem /deep/ .button-content {
-webkit-flex: 1;
flex: 1;
-webkit-flex-basis: 0.000000001px;
flex-basis: 0.000000001px;
}
.inventoryItem[hero] {
box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.37);
}
.itemSlot {
background-color: rgb(232, 232, 232);
background-color: rgba(0, 0, 0, 0.1);
}
#inventory .containerMain {
padding: 0 0 16px 0;
}

View File

@@ -1,73 +1,124 @@
<template name="inventory"> <template name="inventory">
<div fit> <div fit>
<div id="inventory" class="scroll-y" fit> <div id="inventory" class="scroll-y" fit>
<div class="containers"> <div class="column-container">
<!--Net Worth--> <!--Net Worth-->
<paper-shadow class="card container" hero-id="main" {{detailHero}} layout horizontal> <paper-shadow class="card">
<div class="indigo white-text subhead padded leftRound" layout horizontal center> <div class="white top" layout horizontal center>
Net Worth <div class="subhead" flex>
</div> Net Worth
<div class="padded" layout horizontal center> </div>
{{valueString netWorth}} <div>
{{valueString netWorth}}
</div>
</div> </div>
</paper-shadow> </paper-shadow>
<!--Weight Carried--> <!--Weight Carried-->
<paper-shadow class="card container" hero-id="main" {{detailHero}} layout horizontal> <paper-shadow class="card"
<div class="green white-text subhead padded leftRound" layout horizontal center> hero-id="main" {{detailHero "weightCarried" _id}}>
Weight Carried <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> </div>
<div class="padded" layout horizontal center> <div class="bottom green" style="padding: 0;">
{{round weightCarried}}lbs {{> carryCapacityBar}}
</div> </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>
<div flex>
<core-icon icon="work"
style="margin-right: 16px">
</core-icon>
{{name}}
</div>
</div>
</div>
{{/each}}
</div>
{{/if}}
</paper-shadow> </paper-shadow>
<!--Equipment--> <!--Equipment-->
<paper-shadow class="card container equipmentContainer"> <paper-shadow class="card equipmentContainer">
<div class="equipmentTop" layout horizontal center> <div class="white top" layout horizontal center>
<div class="containerName subhead" flex> <div class="subhead" flex>
Equipment Equipment
</div> </div>
<div class="caption" style="margin-right: 8px">{{valueString equipmentValue}}</div> <div class="caption" style="margin-right: 8px">
<div class="caption">{{round equipmentWeight}}lbs</div> {{valueString equipmentValue}}
</div>
<div class="caption">
{{round equipmentWeight}}lbs
</div>
</div> </div>
<div flex class="equipmentMain"> <div flex class="bottom list">
{{#if attuned.count}} {{#if attuned.count}}
<div class="list-subhead" layout horizontal center>Attuned</div> <div class="subhead">Attuned</div>
{{/if}} {{/if}}
{{#each attuned}} {{#each attuned}}
{{>inventoryItem}} {{>inventoryItem}}
{{/each}} {{/each}}
{{#if attuned.count}}<div class="list-subhead" layout horizontal center>Equipment</div>{{/if}} {{#if attuned.count}}
<div class="subhead">Equipment</div>
{{/if}}
{{#each equipment}} {{#each equipment}}
{{>inventoryItem}} {{>inventoryItem}}
{{/each}} {{/each}}
</div> </div>
</paper-shadow> </paper-shadow>
<!--Carried Items--> <!--Carried Items-->
<paper-shadow class="card container carriedContainer"> <paper-shadow class="card carriedContainer">
<div class="equipmentTop" layout horizontal center> <div class="white top" layout horizontal center>
<div class="containerName subhead" flex> <div class="subhead" flex>
Carried Carried
</div> </div>
<div class="caption" style="margin-right: 8px">{{valueString carriedValue}}</div> <div class="caption" style="margin-right: 8px">
<div class="caption">{{round carriedWeight}}lbs</div> {{valueString carriedValue}}
</div>
<div class="caption">
{{round carriedWeight}}lbs
</div>
</div> </div>
<div flex class="containerMain"> <div flex class="bottom list">
{{#each carriedItems}} {{#each carriedItems}}
{{>inventoryItem}} {{>inventoryItem}}
{{/each}} {{/each}}
</div> </div>
</paper-shadow> </paper-shadow>
{{#each containers}} {{#each containers}}
<paper-shadow class="card container itemContainer" hero-id="main" {{detailHero}} style="order: {{containerOrder}};"> <paper-shadow class="card itemContainer"
<div class="containerTop {{colorClass}}" hero-id="toolbar" layout horizontal center {{detailHero}}> hero-id="main" {{detailHero}}>
<div class="containerName subhead" hero-id="title" flex {{detailHero}}>{{name}}</div> <div class="top {{colorClass}}"
<div class="caption" style="margin-right: 8px">{{valueString totalValue}}</div> hero-id="toolbar" {{detailHero}}
<div class="caption" style="margin-right: 8px">{{round totalWeight}}lbs</div> layout horizontal center>
<div class="subhead" flex
hero-id="title" {{detailHero}}>
{{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"> <core-tooltip label="Container carried" position="left">
<paper-checkbox class="carriedCheckbox" checked={{isCarried}}></paper-checkbox> <paper-checkbox class="carriedCheckbox"
disabled={{#unless canEditCharacter charId}}true{{/unless}}
checked={{isCarried}}>
</paper-checkbox>
</core-tooltip> </core-tooltip>
</div> </div>
<div flex class="containerMain"> <div class="bottom list">
{{#each items ../_id _id}} {{#each items ../_id _id}}
{{>inventoryItem}} {{>inventoryItem}}
{{/each}} {{/each}}
@@ -77,17 +128,45 @@
</div> </div>
<div class="fab-buffer"></div> <div class="fab-buffer"></div>
</div> </div>
<paper-fab-menu id="inventoryAddMenu" icon="add" closeIcon="close" duration="0.3"> {{#if canEditCharacter _id}}
<paper-fab-menu-item id="addItem" icon="note-add" color="#d23f31" tooltip="Item"></paper-fab-menu-item> {{#fabMenu}}
<paper-fab-menu-item id="addContainer" icon="work" color="#d23f31" tooltip="Container"></paper-fab-menu-item> <core-tooltip label="New container" position="left">
</paper-fab-menu> <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> </div>
</template> </template>
<template name="inventoryItem"> <template name="inventoryItem">
<div class="itemSlot"> <div class="item-slot">
<paper-item class="inventoryItem {{hidden}}" hero-id="main" noink {{detailHero}} layout horizontal draggable="true"> <div class="item {{hidden}} inventoryItem"
{{#if ne1 quantity}}{{quantity}}&nbsp;{{/if}}{{pluralName}} hero-id="main" {{detailHero}}
</paper-item> layout horizontal center
draggable={{canEditCharacter charId}}>
<div flex class="itemName">
{{#if ne1 quantity}}{{quantity}}&nbsp;{{/if}}{{pluralName}}
</div>
{{#if settings.showIncrement}}{{#if canEditCharacter charId}}
<div class="incrementButtons">
<paper-icon-button class="addItemQuantity"
icon="add"
style="margin-right: -8px"></paper-icon-button>
<paper-icon-button class="subItemQuantity"
disabled={{lt1 quantity}}
icon="remove"
style="margin-right: -8px"></paper-icon-button>
</div>
{{/if}}{{/if}}
</div>
</div> </div>
</template> </template>

View File

@@ -44,6 +44,12 @@ Template.inventory.helpers({
).forEach(function(item){ ).forEach(function(item){
worth += item.totalValue(); worth += item.totalValue();
}); });
Containers.find(
{charId: this._id},
{fields: {value : 1}}
).forEach(function(container) {
if (container.value) worth += container.value;
});
return worth; return worth;
}, },
weightCarried: function(){ weightCarried: function(){
@@ -61,6 +67,18 @@ Template.inventory.helpers({
}); });
return weight; return weight;
}, },
encumberedBuffs: function(){
return Buffs.find({
charId: this._id,
type: "inate",
name: {$in: [
"Encumbered",
"Heavily encumbered",
"Over encumbered",
"Can't move load",
]},
});
},
equipmentValue: function(){ equipmentValue: function(){
var value = 0; var value = 0;
Items.find( Items.find(
@@ -103,7 +121,7 @@ Template.inventory.helpers({
}); });
Template.inventory.events({ Template.inventory.events({
"tap #addItem": function(event){ "tap .addItem": function(event){
var charId = this._id; var charId = this._id;
Items.insert({ Items.insert({
charId: charId, charId: charId,
@@ -120,7 +138,7 @@ Template.inventory.events({
}); });
}); });
}, },
"tap #addContainer": function(event){ "tap .addContainer": function(event){
var containerId = Containers.insert({ var containerId = Containers.insert({
name: "New Container", name: "New Container",
isCarried: true, isCarried: true,
@@ -136,6 +154,23 @@ Template.inventory.events({
heroId: containerId, heroId: containerId,
}); });
}, },
"tap .weightCarried": function(event) {
var charId = this._id;
GlobalUI.setDetail({
template: "carryDialog",
data: {charId: charId, color: "green"},
heroId: charId + "weightCarried",
});
},
"tap .buff": function(event){
var buffId = this._id;
var charId = Template.parentData()._id;
GlobalUI.setDetail({
template: "buffDialog",
data: {buffId: buffId, charId: charId},
heroId: buffId,
});
},
"tap .inventoryItem": function(event){ "tap .inventoryItem": function(event){
var itemId = this._id; var itemId = this._id;
var charId = Template.parentData()._id; var charId = Template.parentData()._id;
@@ -145,7 +180,32 @@ Template.inventory.events({
heroId: itemId, heroId: itemId,
}); });
}, },
"tap .containerTop": function(event){ "hold .inventoryItem": function(event, instance) {
var itemId = this._id;
var charId = Template.parentData()._id;
var containerId = this.parent.id;
GlobalUI.showDialog({
template: "moveItemDialog",
data: {
charId: charId,
itemId: itemId,
containerId: containerId,
},
heading: "Move " + this.pluralName(),
});
},
"tap .incrementButtons": function(event) {
event.stopPropagation();
},
"tap .addItemQuantity": function(event) {
var itemId = this._id;
Items.update(itemId, {$set: {quantity: this.quantity + 1}});
},
"tap .subItemQuantity": function(event) {
var itemId = this._id;
Items.update(itemId, {$set: {quantity: this.quantity - 1}});
},
"tap .itemContainer .top": function(event){
GlobalUI.setDetail({ GlobalUI.setDetail({
template: "containerDialog", template: "containerDialog",
data: {containerId: this._id, charId: this.charId}, data: {containerId: this._id, charId: this.charId},
@@ -167,6 +227,9 @@ Template.inventoryItem.helpers({
ne1: function(num){ ne1: function(num){
return num !== 1; return num !== 1;
}, },
lt1: function(num) {
return num < 1;
},
hidden: function(){ hidden: function(){
return Session.equals("inventory.dragItemId", this._id) ? "hidden" : null; return Session.equals("inventory.dragItemId", this._id) ? "hidden" : null;
}, },

View File

@@ -10,7 +10,7 @@
<template name="itemDetails"> <template name="itemDetails">
<div layout horizontal wrap center justified class="headline"> <div layout horizontal wrap center justified class="headline">
{{#if weight}}<div class="sideMargin">{{totalWeight}}lbs</div>{{/if}} {{#if weight}}<div class="sideMargin">{{round totalWeight}}lbs</div>{{/if}}
{{#if value}}<div>{{valueString totalValue}}</div>{{/if}} {{#if value}}<div>{{valueString totalValue}}</div>{{/if}}
</div> </div>
<div layout horizontal wrap class="caption"> <div layout horizontal wrap class="caption">
@@ -18,8 +18,8 @@
{{#if requiresAttunement}}<div class="vertMargin">Requires Attunement</div>{{/if}} {{#if requiresAttunement}}<div class="vertMargin">Requires Attunement</div>{{/if}}
</div> </div>
{{#if description}} {{#if description}}
<hr class="vertMargin"> <hr style="margin: 16px 0 16px 0;">
<div class="prewrap">{{description}}</div> <div>{{#markdown}}{{evaluateString charId description}}{{/markdown}}</div>
{{/if}} {{/if}}
{{> effectsViewList charId=charId parentId=_id}} {{> effectsViewList charId=charId parentId=_id}}
{{> attacksViewList charId=charId parentId=_id}} {{> attacksViewList charId=charId parentId=_id}}
@@ -28,10 +28,25 @@
<template name="itemEdit"> <template name="itemEdit">
<paper-input class="fullwidth" id="itemNameInput" label="Name" floatinglabel value={{name}}></paper-input> <paper-input class="fullwidth" id="itemNameInput" label="Name" floatinglabel value={{name}}></paper-input>
<div layout horizontal wrap> <div layout horizontal wrap>
<paper-input-decorator label="Quantity" floatinglabel> <paper-input-decorator label="Quantity"
<input id="quantityInput" type="number" value={{quantity}}> floatinglabel
style="width: 80px">
<input id="quantityInput"
type="number"
value={{quantity}}>
</paper-input-decorator> </paper-input-decorator>
{{# if ne1 quantity}}<paper-input flex id="itemPluralInput" label="Plural Name" floatinglabel value={{plural}}></paper-input>{{/if}} {{# if ne1 quantity}}
<paper-input flex id="itemPluralInput"
label="Plural Name"
floatinglabel
value={{plural}}></paper-input>
{{/if}}
</div>
<div center horizontal layout>
<div class="padded">Show increment buttons</div>
<paper-checkbox id="incrementCheckbox"
checked={{settings.showIncrement}}>
</paper-checkbox>
</div> </div>
<hr class="vertMargin"> <hr class="vertMargin">
@@ -53,7 +68,9 @@
</div> </div>
<div center horizontal layout> <div center horizontal layout>
<div class="padded">Requires Attunement</div> <div class="padded">Requires Attunement</div>
<paper-checkbox id="attunementCheckbox" checked={{requiresAttunement}}></paper-checkbox> <paper-checkbox id="attunementCheckbox"
checked={{requiresAttunement}}>
</paper-checkbox>
</div> </div>
</div> </div>

View File

@@ -50,7 +50,7 @@ Template.itemEdit.onRendered(function(){
Template.itemEdit.helpers({ Template.itemEdit.helpers({
ne1: function(num){ ne1: function(num){
return num != 1; return num != 1;
} },
}); });
Template.itemEdit.events({ Template.itemEdit.events({
@@ -87,6 +87,10 @@ Template.itemEdit.events({
Meteor.call("unequipItem", this._id, this.charId); Meteor.call("unequipItem", this._id, this.charId);
} }
}, },
"change #incrementCheckbox": function(event){
var value = event.currentTarget.checked;
Items.update(this._id, {$set: {"settings.showIncrement": value}});
},
"change #attunementCheckbox": function(event){ "change #attunementCheckbox": function(event){
var value = event.currentTarget.checked; var value = event.currentTarget.checked;
Items.update(this._id, {$set: {requiresAttunement: value}}); Items.update(this._id, {$set: {requiresAttunement: value}});

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>
<core-icon icon="image:brightness-1"
style="color: {{hexColor color}};
margin-right: 16px;">
</core-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

@@ -3,6 +3,9 @@ Template.classDialog.onRendered(function(){
}); });
Template.classDialog.events({ Template.classDialog.events({
"color-change": function(event, instance){
Classes.update(instance.data.classId, {$set: {color: event.color}});
},
"tap #deleteButton": function(event, instance){ "tap #deleteButton": function(event, instance){
Classes.softRemoveNode(instance.data.classId); Classes.softRemoveNode(instance.data.classId);
GlobalUI.deletedToast(instance.data.classId, "Classes", "Class"); GlobalUI.deletedToast(instance.data.classId, "Classes", "Class");

View File

@@ -1,28 +1,32 @@
<template name="experienceDialog"> <template name="experienceDialog">
{{#with experience}} {{#with experience}}
{{#baseDialog title=name class=colorClass hideColor="true" startEditing=../startEditing}} {{#baseDialog title=name class=color hideColor="true" startEditing=../startEditing}}
<div horizontal layout center-justified> <div horizontal layout center-justified class= "display2">
{{value}} {{value}}
</div> </div>
{{#if description}} {{#if description}}
<hr class="vertMargin"> <hr class="vertMargin">
<div class="prewrap">{{description}}</div> <div>{{#markdown}}{{description}}{{/markdown}}</div>
{{/if}} {{/if}}
{{else}} {{else}}
<div horizontal layout> {{> experienceEdit}}
<!--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>
{{/baseDialog}} {{/baseDialog}}
{{/with}} {{/with}}
</template> </template>
<template name="experienceEdit">
<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>
</template>

View File

@@ -1,6 +1,15 @@
Template.experienceEdit.onRendered(function(){
updatePolymerInputs(this);
});
Template.experienceDialog.helpers({ Template.experienceDialog.helpers({
feature: function(){ experience: function(){
return Features.findOne(this.featureId); Experiences.findOne(this.experienceId);
return Experiences.findOne(this.experienceId);
},
color: function() {
var char = Characters.findOne(this.charId, {fields: {color: 1}});
if (char) return getColorClass(char.color);
}, },
}); });
@@ -13,8 +22,10 @@ Template.experienceDialog.events({
); );
GlobalUI.closeDetail(); GlobalUI.closeDetail();
}, },
//TODO validate input (integer, non-negative, etc) for these inputs and give validation errors });
"change #experienceNameInput, input #experienceNameInput": function(event){
Template.experienceEdit.events({
"change #experienceNameInput": function(event){
var value = event.currentTarget.value; var value = event.currentTarget.value;
Experiences.update(this._id, {$set: {name: value}}); Experiences.update(this._id, {$set: {name: value}});
}, },
@@ -27,10 +38,3 @@ Template.experienceDialog.events({
Experiences.update(this._id, {$set: {description: value}}); Experiences.update(this._id, {$set: {description: value}});
}, },
}); });
Template.experienceDialog.helpers({
experience: function(){
Experiences.findOne(this.experienceId);
return Experiences.findOne(this.experienceId);
}
});

View File

@@ -1,73 +1,100 @@
<template name="journal"> <template name="journal">
<div fit> <div fit>
<div id="journal" class="scroll-y" fit> <div id="journal" class="scroll-y" fit>
<div class="containers"> <div class="column-container">
<!--Experience Table--> <!--Experience Table-->
<paper-shadow class="card container experiencesCard" hero-id="main" {{detailHero}}> <paper-shadow class="card experiencesCard"
<div class="whiteTop" hero-id="toolbar" layout horizontal center {{detailHero}}> hero-id="main" {{detailHero}}>
<div class="containerName subhead" flex>Experience</div> <div class="top white subhead"
<div class="subhead">{{experience}} XP</div> hero-id="toolbar" {{detailHero}}
<paper-icon-button class="black54" id="addXP" icon="add"></paper-icon-button> layout horizontal center>
<div 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>
<div class="containerMain experiences"> <div class="bottom list">
{{#each experiences}} {{#each experiences}}
<div class="itemSlot"> <div class="item-slot">
<paper-item class="inventoryItem experience" hero-id="main" {{detailHero}} layout horizontal> <div class="item experience"
<div flex>{{name}}</div><div class="xpValue">{{value}}</div> hero-id="main" {{detailHero}}
</paper-item> layout horizontal center>
<div flex>{{name}}</div>
<div class="xpValue">{{value}}</div>
</div>
</div> </div>
{{/each}} {{/each}}
</div> </div>
{{#if moreExperiencesOrCollapse}} {{#if moreExperiencesOrCollapse}}
<div class="containerFoot" layout="" horizontal="" center="" end-justified=""> <div layout horizontal center end-justified>
<paper-button id="moreExperiences" disabled={{notMoreExperiences}}>Load More</paper-button> <paper-button id="moreExperiences"
<paper-button id="lessExperiences" disabled={{cantCollapse}}>Collapse</paper-button> disabled={{notMoreExperiences}}>
Load More
</paper-button>
<paper-button id="lessExperiences"
disabled={{cantCollapse}}>
Collapse
</paper-button>
</div> </div>
{{/if}} {{/if}}
</paper-shadow> </paper-shadow>
<!--Class Table--> <!--Class Table-->
<paper-shadow class="card container" hero-id="main" {{detailHero}}> <paper-shadow class="card"
<div class="whiteTop" hero-id="toolbar" layout horizontal center {{detailHero}}> hero-id="main" {{detailHero}}>
<div class="white top"
hero-id="toolbar" {{detailHero}}
layout horizontal center>
<div flex> <div flex>
<div class="containerName subhead">Level {{level}}</div> <div class="containerName subhead">
Level {{characterCalculate "level" _id}}
</div>
{{#if nextLevelXP}} {{#if nextLevelXP}}
<div class="caption"> <div class="caption">
Next Level: {{nextLevelXP}}XP Next Level: {{nextLevelXP}}XP
</div> </div>
{{/if}} {{/if}}
</div> </div>
<paper-icon-button class="black54" id="addClassButton" icon="add"></paper-icon-button> <paper-icon-button class="black54"
id="addClassButton"
icon="add"
disabled={{#unless canEditCharacter _id}}true{{/unless}}>
</paper-icon-button>
</div> </div>
<div class="containerMain experiences"> <div class="bottom list">
<div class="itemSlot"> <div class="item-slot">
<paper-item class="inventoryItem race" hero-id="main" {{detailHero "race" _id}} layout horizontal> <div class="item race"
hero-id="main" {{detailHero "race" _id}}
layout horizontal center>
{{race}} {{race}}
</paper-item> </div>
</div> </div>
{{#each classes}} {{#each classes}}
<div class="itemSlot"> <div class="item-slot">
<paper-item class="inventoryItem class" hero-id="main" {{detailHero}} layout horizontal> <div class="item class"
hero-id="main" {{detailHero}}
layout horizontal center>
{{name}}&nbsp;{{level}} {{name}}&nbsp;{{level}}
</paper-item> </div>
</div> </div>
{{/each}} {{/each}}
</div> </div>
</paper-shadow> </paper-shadow>
<!--Notes--> <!--Notes-->
{{#each notes}} {{#each notes}}
<paper-shadow class="card container" hero-id="main" {{detailHero}}> <paper-shadow class="card" hero-id="main" {{detailHero}}>
<div class="containerTop {{colorClass}} noteTop" hero-id="toolbar" layout horizontal center {{detailHero}}> <div class="top {{colorClass}} noteTop subhead"
<div flex> hero-id="toolbar" {{detailHero}}
<div class="containerName subhead">{{name}}</div> layout horizontal center>
</div> {{name}}
</div> </div>
<div class="containerMain preline">{{description}}</div> <div class="bottom">{{#markdown}}{{description}}{{/markdown}}</div>
</paper-shadow> </paper-shadow>
{{/each}} {{/each}}
</div> </div>
<div class="fab-buffer"></div> <div class="fab-buffer"></div>
</div> </div>
</div> </div>
{{#if canEditCharacter _id}}
<paper-fab id="addNote" <paper-fab id="addNote"
class="floatyButton" class="floatyButton"
icon="add" icon="add"
@@ -75,4 +102,5 @@
role="button" role="button"
tabindex="0" tabindex="0"
hero-id="main"></paper-fab> hero-id="main"></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}}); return Levels.find({charId: charId, classId: this._id}, {sort: {value: 1}});
}, },
nextLevelXP: function(){ nextLevelXP: function(){
var currentLevel = this.level(); var currentLevel = Characters.calculate.level(this._id);
if (currentLevel < 20){ if (currentLevel < 20){
return XP_TABLE[currentLevel]; return XP_TABLE[currentLevel];
} }

View File

@@ -1,18 +1,30 @@
<template name="noteDialog"> <template name="noteDialog">
{{#with note}} {{#with note}}
{{#baseDialog title=name class=colorClass startEditing=../startEditing}} {{#baseDialog title=name class=colorClass startEditing=../startEditing}}
<div class="prewrap">{{description}}</div> <div>{{#markdown}}{{description}}{{/markdown}}</div>
{{else}} {{else}}
<!--Name--> {{> noteDialogEdit}}
<div horizontal layout>
<paper-input id="noteNameInput" label="Name" floatinglabel value={{name}} flex></paper-input>
</div>
<!--Description-->
<paper-input-decorator label="Description" floatinglabel layout vertical>
<paper-autogrow-textarea>
<textarea id="noteDescriptionInput" value={{description}}></textarea>
</paper-autogrow-textarea>
</paper-input-decorator>
{{/baseDialog}} {{/baseDialog}}
{{/with}} {{/with}}
</template>
<template name="noteDialogEdit">
<!--Name-->
<div horizontal layout>
<paper-input id="noteNameInput"
label="Name"
floatinglabel
value={{name}}
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> </template>

View File

@@ -1,5 +1,7 @@
Template.noteDialog.onRendered(function(){ Template.noteDialog.helpers({
updatePolymerInputs(this); note: function(){
return Notes.findOne(this.noteId);
}
}); });
Template.noteDialog.events({ Template.noteDialog.events({
@@ -11,6 +13,13 @@ Template.noteDialog.events({
GlobalUI.deletedToast(instance.data.noteId, "Notes", "Note"); GlobalUI.deletedToast(instance.data.noteId, "Notes", "Note");
GlobalUI.closeDetail(); GlobalUI.closeDetail();
}, },
});
Template.noteDialogEdit.onRendered(function(){
updatePolymerInputs(this);
});
Template.noteDialogEdit.events({
"change #noteNameInput, input #noteNameInput": function(event){ "change #noteNameInput, input #noteNameInput": function(event){
var value = event.currentTarget.value; var value = event.currentTarget.value;
Notes.update(this._id, {$set: {name: value}}); Notes.update(this._id, {$set: {name: value}});
@@ -20,9 +29,3 @@ Template.noteDialog.events({
Notes.update(this._id, {$set: {description: value}}); Notes.update(this._id, {$set: {description: value}});
}, },
}); });
Template.noteDialog.helpers({
note: function(){
return Notes.findOne(this.noteId);
}
});

View File

@@ -1,5 +1,8 @@
<template name="raceDialog"> <template name="raceDialog">
{{#baseDialog title="Race" class=colorClass hideColor="true" hideDelete="true" startEditing=startEditing}} {{#baseDialog title="Race" class=color hideColor="true" hideDelete="true" startEditing=startEditing}}
<div horizontal layout center-justified class= "display2">
{{race}}
</div>
{{> effectsViewList charId=charId parentId=charId parentGroup="racial"}} {{> effectsViewList charId=charId parentId=charId parentGroup="racial"}}
{{> proficiencyViewList charId=charId parentId=charId parentGroup="racial"}} {{> proficiencyViewList charId=charId parentId=charId parentGroup="racial"}}
{{else}} {{else}}

View File

@@ -13,5 +13,9 @@ Template.raceDialog.helpers({
race: function(){ race: function(){
var char = Characters.findOne(this.charId, {fields: {race: 1}}); var char = Characters.findOne(this.charId, {fields: {race: 1}});
return char && char.race; return char && char.race;
} },
color: function() {
var char = Characters.findOne(this.charId, {fields: {color: 1}});
if (char) return getColorClass(char.color);
},
}); });

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

View File

@@ -2,21 +2,34 @@ Template.personaDetailsEdit.onRendered(function(){
updatePolymerInputs(this); updatePolymerInputs(this);
}); });
Template.personaDetailsDialog.helpers({
char: function() {
return Characters.findOne(
this._id,
{fields: {name: 1, alignment: 1, gender: 1, race: 1, picture: 1}}
);
}
});
Template.personaDetailsEdit.events({ Template.personaDetailsEdit.events({
"change #nameInput": function(event){ "change #nameInput": function(event){
var input = event.currentTarget.value; var input = event.currentTarget.value;
Characters.update(this.charId, {$set: {name: input}}); Characters.update(this._id, {$set: {name: input}});
}, },
"change #alignmentInput": function(event){ "change #alignmentInput": function(event){
var input = event.currentTarget.value; var input = event.currentTarget.value;
Characters.update(this.charId, {$set: {alignment: input}}); Characters.update(this._id, {$set: {alignment: input}});
}, },
"change #genderInput": function(event){ "change #genderInput": function(event){
var input = event.currentTarget.value; var input = event.currentTarget.value;
Characters.update(this.charId, {$set: {gender: input}}); Characters.update(this._id, {$set: {gender: input}});
}, },
"change #raceInput": function(event){ "change #raceInput": function(event){
var input = event.currentTarget.value; var input = event.currentTarget.value;
Characters.update(this.charId, {$set: {race: input}}); Characters.update(this._id, {$set: {race: input}});
},
"change #pictureInput": function(event){
var input = event.currentTarget.value;
Characters.update(this._id, {$set: {picture: input}});
}, },
}); });

View File

@@ -1,9 +1,37 @@
<template name="persona"> <template name="persona">
<div fit> <div fit>
<div id="persona" class="scroll-y" fit> <div id="persona" class="scroll-y" fit>
<div class="containers"> <div class="column-container">
{{#with characterDetails}} {{#with characterDetails}}
{{#containerCardHelper this}}{{alignment}} {{gender}} {{race}}{{/containerCardHelper}} <paper-shadow class="card"
hero-id="main" {{detailHero "details" _id}}>
{{#unless picture}}
<div class="top subhead characterField {{colorClass}}"
hero-id="toolbar" {{detailHero "details" _id}}>
<div class="subhead" flex
hero-id="title" {{detailHero "details" _id}}>
{{name}}
</div>
</div>
{{else}}
<core-image class="characterField clickable"
style="height:350px; width: 100%;
background-color: #e8e8e8;"
sizing="cover"
hero-id="image" {{detailHero "details" _id}}
src={{picture}}></core-image>
{{/unless}}
<div class="bottom">
{{#if picture}}
<div class="title" hero-id="title" {{detailHero "details" _id}}>
{{name}}
</div>
{{/if}}
<div class="subhead">
{{alignment}} {{gender}} {{race}}
</div>
</div>
</paper-shadow>
{{/with}} {{/with}}
{{> containerCard characterField "description" "Description"}} {{> containerCard characterField "description" "Description"}}
{{> containerCard characterField "personality" "Personality Traits"}} {{> containerCard characterField "personality" "Personality Traits"}}
@@ -11,11 +39,11 @@
{{> containerCard characterField "bonds" "Bonds"}} {{> containerCard characterField "bonds" "Bonds"}}
{{> containerCard characterField "flaws" "Flaws"}} {{> containerCard characterField "flaws" "Flaws"}}
{{> containerCard characterField "backstory" "Background"}} {{> containerCard characterField "backstory" "Background"}}
<paper-shadow class="card container"> <paper-shadow class="card">
<div class="containerTop whiteTop" layout horizontal center> <div class="white top subhead">
<div class="containerName subhead" flex>Languages</div> Languages
</div> </div>
<div flex class="containerMain listPadded"> <div class="bottom list">
{{#each languages}} {{#each languages}}
{{> proficiencyListItem}} {{> proficiencyListItem}}
{{/each}} {{/each}}
@@ -27,14 +55,19 @@
</template> </template>
<template name="containerCard"> <template name="containerCard">
{{#containerCardHelper this}}{{body}}{{/containerCardHelper}} {{#containerCardHelper this}}{{evaluateString _id body}}{{/containerCardHelper}}
</template> </template>
<template name="containerCardHelper"> <template name="containerCardHelper">
<paper-shadow class="card container {{class}}" hero-id="main" {{detailHero field ../_id}}> <paper-shadow class="card {{class}}"
<div class="containerTop {{colorClass}} {{topClass}}" hero-id="toolbar" layout horizontal center {{detailHero field ../_id}}> hero-id="main" {{detailHero field ../_id}}>
<div class="containerName subhead" hero-id="title" flex {{detailHero field ../_id}}>{{title}}</div> <div class="top subhead {{colorClass}} {{topClass}}"
hero-id="toolbar" {{detailHero field ../_id}}>
<div class="subhead" flex
hero-id="title" {{detailHero field ../_id}}>
{{title}}
</div>
</div> </div>
<div flex class="containerMain prewrap">{{> UI.contentBlock}}</div> <div class="bottom">{{#markdown}}{{> UI.contentBlock}}{{/markdown}}</div>
</paper-shadow> </paper-shadow>
</template> </template>

View File

@@ -11,12 +11,12 @@ Template.persona.helpers({
characterDetails: function(){ characterDetails: function(){
var char = Characters.findOne( var char = Characters.findOne(
this._id, this._id,
{fields: {name: 1, gender: 1, alignment: 1, race:1}} {fields: {name: 1, gender: 1, alignment: 1, race:1, picture: 1}}
); );
char.field = "details"; char.field = "details";
char.title = char.name; char.title = char.name;
char.color = "d"; char.color = "d";
char.topClass = "characterField"; char.startEditing = true;
return char; return char;
}, },
characterField: function(field, title){ characterField: function(field, title){
@@ -40,25 +40,28 @@ Template.persona.helpers({
Template.persona.events({ Template.persona.events({
"tap .characterField": function(event){ "tap .characterField": function(event){
if (this.field !== "details"){ if (this.field == "details"){
var charId = Template.parentData()._id;
GlobalUI.setDetail({
template: "textDialog",
data: {
charId: charId,
field: this.field,
title: this.title,
color: this.color,
},
heroId: this._id + this.field,
});
} else {
this.charId = Template.parentData()._id; this.charId = Template.parentData()._id;
GlobalUI.setDetail({ GlobalUI.setDetail({
template: "personaDetailsDialog", template: "personaDetailsDialog",
data: this, data: this,
heroId: this._id + this.field, heroId: this._id + this.field,
}); });
} else {
var template = "textDialog";
if (this.field === "backstory") template = "backgroundDialog";
var charId = Template.parentData()._id;
GlobalUI.setDetail({
template: template,
data: {
charId: charId,
field: this.field,
title: this.title,
color: this.color,
startEditing: true,
},
heroId: this._id + this.field,
});
} }
} }
}); });

View File

@@ -1,6 +1,6 @@
<template name="textDialog"> <template name="textDialog">
{{#baseDialog title=title class=colorClass hideColor="true" hideDelete="true" startEditing=startEditing}} {{#baseDialog title=title class=colorClass hideColor="true" hideDelete="true" startEditing=startEditing}}
<div class="prewrap">{{value}}</div> <div>{{#markdown}}{{evaluateString charId value}}{{/markdown}}</div>
{{else}} {{else}}
{{> textDialogEdit}} {{> textDialogEdit}}
{{/baseDialog}} {{/baseDialog}}

View File

@@ -1,8 +1,11 @@
<template name="proficiencyListItem"> <template name="proficiencyListItem">
<div class="itemSlot"> <div class="item-slot">
<paper-item noink class="white proficiencyItem" hero-id="main" {{detailHero}}> <div class="proficiency item small"
<core-icon icon="{{profIcon}}" class="black54"></core-icon> hero-id="main" {{detailHero}}
<div class="sideMargin">{{getName}}</div> layout horizontal center>
</paper-item> <core-icon icon="{{profIcon}}"
style="margin-right: 16px;"></core-icon>
<div flex>{{getName}}</div>
</div>
</div> </div>
</template> </template>

View File

@@ -14,7 +14,7 @@ Template.proficiencyListItem.helpers({
}); });
Template.proficiencyListItem.events({ Template.proficiencyListItem.events({
"tap .proficiencyItem": function(event, instance){ "tap .proficiency": function(event, instance){
openParentDialog(this.parent, this.charId, this._id); openParentDialog(this.parent, this.charId, this._id);
} }
}); });

View File

@@ -1,6 +1,10 @@
<template name="proficiencyView"> <template name="proficiencyView">
<div class="proficiencyView" layout horizontal center> <div class="proficiencyView item small"
<core-icon icon="{{profIcon}}"></core-icon> style="padding: 0;"
<div class="sideMargin">{{getName}}</div> layout horizontal center>
<core-icon icon="{{profIcon}}" style="margin-right: 16px;"></core-icon>
<div>{{getName}}</div>
</div> </div>
</template> </template>

View File

@@ -2,7 +2,7 @@
{{#if proficiencies.count}} {{#if proficiencies.count}}
<hr class="vertMargin"> <hr class="vertMargin">
<div class="proficiencies"> <div class="proficiencies">
<h2 class="spaceAfter">Proficiencies</h2> <h2 style="margin-bottom: 8px;">Proficiencies</h2>
{{#each proficiencies}} {{#each proficiencies}}
{{> proficiencyView}} {{> proficiencyView}}
{{/each}} {{/each}}

View File

@@ -9,24 +9,33 @@
</template> </template>
<template name="spellDetails"> <template name="spellDetails">
<div class="caption"> <div class="body2">
Level {{level}} {{school}}, {{preparedString}} Level {{level}} {{school}}, {{preparedString}}
</div> </div>
<div class="vertMargin"> <div style="margin: 16px 0 16px 0;">
{{#if castingTime}}
<div> <div>
<span class="body2">Casting Time: </span><span>{{castingTime}}</span> <span class="body2">Casting Time: </span><span>{{castingTime}}</span>
</div> </div>
{{/if}}
{{#if range}}
<div> <div>
<span class="body2">Range: </span><span>{{range}}</span> <span class="body2">Range: </span><span>{{range}}</span>
</div> </div>
{{/if}}
{{#if getComponents}}
<div> <div>
<span class="body2">Components: </span><span>{{getComponents}}</span> <span class="body2">Components: </span><span>{{getComponents}}</span>
</div> </div>
{{/if}}
{{#if duration}}
<div> <div>
<span class="body2">Duration: </span><span>{{duration}}</span> <span class="body2">Duration: </span><span>{{duration}}</span>
</div> </div>
{{/if}}
</div> </div>
<div class="prewrap">{{description}}</div> <div>{{#markdown}}{{evaluateString charId description}}{{/markdown}}</div>
{{> attacksViewList charId=charId parentId=_id}}
</template> </template>
<template name="spellEdit"> <template name="spellEdit">
@@ -126,4 +135,5 @@
<textarea id="descriptionInput" placeholder value={{description}}></textarea> <textarea id="descriptionInput" placeholder value={{description}}></textarea>
</paper-autogrow-textarea> </paper-autogrow-textarea>
</paper-input-decorator> </paper-input-decorator>
{{> attackEditList parentId=_id parentCollection="Spells" charId=charId enabled=true name=name}}
</template> </template>

View File

@@ -20,7 +20,7 @@
{{/if}} {{/if}}
</div> </div>
<hr class="vertMargin"> <hr class="vertMargin">
<div class="prewrap">{{description}}</div> <div>{{#markdown}}{{evaluateString charId description}}{{/markdown}}</div>
</div> </div>
{{else}} {{else}}
<!--Name--> <!--Name-->

View File

@@ -1,84 +1,121 @@
<template name="spells"> <template name="spells">
<div fit> <div fit>
<div id="spells" class="scroll-y" fit> <div id="spells" class="scroll-y" fit>
<div class="spellsContainer" layout horizontal start wrap> <div style="padding: 4px;"
layout horizontal start wrap>
{{#if hasSlots}} {{#if hasSlots}}
<paper-shadow class="card container spellSlotContainer" hero-id="main" {{detailHero}}> <paper-shadow class="card"
<div class="containerTop whiteTop" layout horizontal center> style="margin: 4px;"
<div class="containerName subhead" hero-id="title" flex>Spell Slots</div> hero-id="main" {{detailHero}}>
<div class="white top subhead"
layout horizontal center>
Spell Slots
</div> </div>
<div flex class="containerMain"> <div class="bottom list">
{{#each levels}}{{#if showSlots ..}} {{#each levels}}{{#if showSlots ..}}
<div class="itemSlot"> <div class="item-slot">
<paper-item class="inventoryItem spellSlot" hero-id="main" {{detailHero slotStatName ../_id}} layout horizontal> <div class="item spellSlot"
<div class="slotName"> hero-id="main" {{detailHero slotStatName ../_id}}
layout horizontal center>
<div style="margin-right: 16px">
{{name}} {{name}}
</div> </div>
<div flex layout horizontal center> <div flex layout horizontal center>
{{#each slotBubbles ..}} {{#each slotBubbles ..}}
<paper-icon-button class="slotBubble" icon={{icon}} disabled={{disabled}}></paper-icon-button> <paper-icon-button class="slotBubble"
icon={{icon}}
disabled={{disabled}}>
</paper-icon-button>
{{/each}} {{/each}}
</div> </div>
</paper-item> </div>
</div> </div>
{{/if}}{{/each}} {{/if}}{{/each}}
</div> </div>
</paper-shadow> </paper-shadow>
{{/if}} {{/if}}
{{#each spellLists}} {{#each spellLists}}
<paper-shadow class="card container spellList" hero-id="main" {{detailHero}} flex> <paper-shadow class="card spellList" flex
<div class="containerTop {{colorClass}}" hero-id="toolbar" layout horizontal center {{detailHero}}> hero-id="main" {{detailHero}}
style="margin: 4px;">
<div class="top {{colorClass}}"
hero-id="toolbar" {{detailHero}}
layout horizontal center>
<div flex> <div flex>
<div class="containerName subhead">{{name}}</div> <div class="subhead">{{name}}</div>
<div class="caption"> <div class="caption">
{{#if saveDC}} {{#if saveDC}}
Save DC: {{evaluate charId saveDC}} <span style="margin-right: 16px;">
<div style="width: 16px; display: inline-block;"></div> Save DC: {{evaluate charId saveDC}}
</span>
{{/if}} {{/if}}
{{#if attackBonus}} {{#if attackBonus}}
Attack Bonus: {{evaluateSigned charId attackBonus}} <span>
Attack Bonus: {{evaluateSigned charId attackBonus}}
</span>
{{/if}} {{/if}}
</div> </div>
</div> </div>
{{#if settings.showUnprepared}} {{#if settings.showUnprepared}}
{{#if maxPrepared}}<div class="subhead">{{numPrepared}} / {{evaluate charId maxPrepared}}</div>{{/if}} {{#if maxPrepared}}
<core-tooltip label="Done" position="left"> <div class="subhead">
<paper-icon-button class="finishPrep" icon="done"></paper-icon-button> {{numPrepared}} / {{evaluate charId maxPrepared}}
</core-tooltip> </div>
{{/if}}
<core-tooltip label="Done"
position="left">
<paper-icon-button class="finishPrep"
icon="done">
</paper-icon-button>
</core-tooltip>
{{else}} {{else}}
<core-tooltip label="Change prepared spells" position="left"> <core-tooltip label="Change prepared spells"
<paper-icon-button class="prepSpells" icon="book"></paper-icon-button> position="left">
<paper-icon-button class="prepSpells"
disabled={{#unless canEditCharacter charId}}true{{/unless}}
icon="book">
</paper-icon-button>
</core-tooltip> </core-tooltip>
{{/if}} {{/if}}
</div> </div>
<div class="containerMain"> <div class="bottom list column-container">
{{#each levels}} {{#each levels}}
<div class="spellLevel"> <div class="spellLevel">
{{#if spellCount .. ../../_id}} {{#if spellCount .. ../../_id}}
<div class="list-subhead" layout horizontal center> <div class="subhead">
{{name}} {{name}}
</div> </div>
{{/if}} {{/if}}
{{#each spells ../_id ../../_id}} {{#each spells ../_id ../../_id}}
{{#if showSpell ../../settings.showUnprepared}} {{#if showSpell ../../settings.showUnprepared}}
<div class="itemSlot"> <div class="item-slot">
<paper-item class="inventoryItem spell" hero-id="main" {{detailHero}} <div class="tall spell item"
layout horizontal center> hero-id="main" {{detailHero}}
<!--disabled={{cantCast ../level ../../..}} to grey out spells above highest usable slot--> layout horizontal center>
<core-icon icon="social:whatshot" <core-icon icon="social:whatshot"
style="color: {{hexColor color}};" style="color: {{hexColor color}};
margin-right: 16px;"
></core-icon> ></core-icon>
<div flex layout vertical> <div flex layout vertical>
<div>{{name}}</div> <div>{{name}}</div>
<div class="caption"> <div class="caption">
{{school}} {{castingTime}} {{school}}
{{#if ritual}}(ritual){{/if}}{{#if spellComponents}} - {{spellComponents}}{{/if}} {{castingTime}}
{{#if ritual}}
(ritual)
{{/if}}
{{#if spellComponents}}
- {{spellComponents}}
{{/if}}
</div> </div>
</div> </div>
{{#if ../../settings.showUnprepared}} {{#if ../../settings.showUnprepared}}
<paper-checkbox class="preparedCheckbox" checked={{isPrepared}} disabled={{cantUnprepare}}></paper-checkbox> <paper-checkbox class="preparedCheckbox"
checked={{isPrepared}}
disabled={{cantUnprepare}}>
</paper-checkbox>
{{/if}} {{/if}}
</paper-item> </div>
</div> </div>
{{/if}} {{/if}}
{{/each}} {{/each}}
@@ -91,8 +128,20 @@
<div class="fab-buffer"></div> <div class="fab-buffer"></div>
</div> </div>
</div> </div>
<paper-fab-menu id="inventoryAddMenu" icon="add" closeIcon="close" duration="0.3"> {{#if canEditCharacter _id}}
<paper-fab-menu-item id="addSpell" icon="note-add" color="#d23f31" tooltip="Spell"></paper-fab-menu-item> {{#fabMenu}}
<paper-fab-menu-item id="addSpellList" icon="work" color="#d23f31" tooltip="Spell List"></paper-fab-menu-item> <core-tooltip label="New spell list" position="left">
</paper-fab-menu> <paper-fab icon="work"
class="addSpellList"
mini>
</paper-fab>
</core-tooltip>
<core-tooltip label="New spell" position="left">
<paper-fab icon="note-add"
class="addSpell"
mini>
</paper-fab>
</core-tooltip>
{{/fabMenu}}
{{/if}}
</template> </template>

View File

@@ -84,39 +84,35 @@ Template.spells.helpers({
}, },
cantCast: function(level, char){ cantCast: function(level, char){
for (var i = level; i <= 9; i++){ for (var i = level; i <= 9; i++){
if (char.attributeValue("level" + i + "SpellSlots") > 0){ if (Characters.calculate.attributeValue(char._id, "level" + i + "SpellSlots") > 0){
return false; return false;
} }
} }
return true; return true;
}, },
baseSlots: function(char){
return char.attributeBase("level" + this.level + "SpellSlots");
},
slots: function(char){
return char.attributeValue("level" + this.level + "SpellSlots");
},
showSlots: function(char){ showSlots: function(char){
return this.level && char.attributeBase("level" + this.level + "SpellSlots"); return this.level && Characters.calculate.attributeBase(
char._id, "level" + this.level + "SpellSlots"
);
}, },
hasSlots: function(){ hasSlots: function(){
for (var i = 1; i <= 9; i += 1){ for (var i = 1; i <= 9; i += 1){
if (this.attributeBase("level" + i + "SpellSlots")){ if (Characters.calculate.attributeBase(this._id, "level" + i + "SpellSlots")){
return true; return true;
} }
} }
return false; return false;
}, },
slotBubbles: function(char){ slotBubbles: function(char){
var baseSlots = char.attributeBase("level" + this.level + "SpellSlots"); var baseSlots = Characters.calculate.attributeBase(char._id, "level" + this.level + "SpellSlots");
var currentSlots = char.attributeValue("level" + this.level + "SpellSlots"); var currentSlots = Characters.calculate.attributeValue(char._id, "level" + this.level + "SpellSlots");
var slotsUsed = baseSlots - currentSlots; var slotsUsed = baseSlots - currentSlots;
var bubbles = []; var bubbles = [];
var i; var i;
for (i = 0; i < currentSlots; i++){ for (i = 0; i < currentSlots; i++){
bubbles.push({ bubbles.push({
icon: "radio-button-on", icon: "radio-button-on",
disabled: i !== currentSlots - 1, //last full slot not disabled disabled: i !== currentSlots - 1 || !canEditCharacter(char._id), //last full slot not disabled
attribute: "level" + this.level + "SpellSlots", attribute: "level" + this.level + "SpellSlots",
charId: char._id, charId: char._id,
}); });
@@ -124,7 +120,7 @@ Template.spells.helpers({
for (i = 0; i < slotsUsed; i++){ for (i = 0; i < slotsUsed; i++){
bubbles.push({ bubbles.push({
icon: "radio-button-off", icon: "radio-button-off",
disabled: i !== 0, //first empty slot not disabled disabled: i !== 0 || !canEditCharacter(char._id), //first empty slot not disabled
attribute: "level" + this.level + "SpellSlots", attribute: "level" + this.level + "SpellSlots",
charId: char._id, charId: char._id,
}); });
@@ -143,15 +139,15 @@ Template.spells.events({
var char = Characters.findOne(this.charId); var char = Characters.findOne(this.charId);
if (event.currentTarget.icon === "radio-button-off"){ if (event.currentTarget.icon === "radio-button-off"){
if ( if (
char.attributeValue(this.attribute) < Characters.calculate.attributeValue(char._id, this.attribute) <
char.attributeBase(this.attribute) Characters.calculate.attributeBase(char._id, this.attribute)
){ ){
modifier = {$inc: {}}; modifier = {$inc: {}};
modifier.$inc[this.attribute + ".adjustment"] = 1; modifier.$inc[this.attribute + ".adjustment"] = 1;
Characters.update(this.charId, modifier, {validate: false}); Characters.update(this.charId, modifier, {validate: false});
} }
} else { } else {
if (char.attributeValue(this.attribute) > 0){ if (Characters.calculate.attributeValue(char._id, this.attribute) > 0){
modifier = {$inc: {}}; modifier = {$inc: {}};
modifier.$inc[this.attribute + ".adjustment"] = -1; modifier.$inc[this.attribute + ".adjustment"] = -1;
Characters.update(this.charId, modifier, {validate: false}); Characters.update(this.charId, modifier, {validate: false});
@@ -170,7 +166,7 @@ Template.spells.events({
heroId: charId + stat, heroId: charId + stat,
}); });
}, },
"tap .containerTop": function(event){ "tap .spellList .top": function(event){
GlobalUI.setDetail({ GlobalUI.setDetail({
template: "spellListDialog", template: "spellListDialog",
data: {spellListId: this._id, charId: this.charId}, data: {spellListId: this._id, charId: this.charId},
@@ -184,7 +180,7 @@ Template.spells.events({
heroId: this._id, heroId: this._id,
}); });
}, },
"tap #addSpellList": function(event){ "tap .addSpellList": function(event){
var charId = this.charId; var charId = this.charId;
SpellLists.insert({ SpellLists.insert({
name: "New SpellList", name: "New SpellList",
@@ -201,7 +197,7 @@ Template.spells.events({
} }
}); });
}, },
"tap #addSpell": function(event){ "tap .addSpell": function(event){
var charId = this.charId; var charId = this.charId;
var listId = SpellLists.findOne({charId: this._id})._id; var listId = SpellLists.findOne({charId: this._id})._id;
Spells.insert({ Spells.insert({

View File

@@ -1,34 +0,0 @@
.card.double {
display: flex;
}
.card.double > div{
vertical-align: top;
padding: 16px;
}
.abilityScore {
width: 70px;
text-align: center;
background-color: #D50000;
padding: 16px;
position: relative;
border-radius: 2px 0 0 2px;
}
#stats .card {
padding: 0;
}
.abilityCardRight {
flex-grow: 1;
padding-right: 0;
}
.abilityCardRight hr{
margin: 8px 0 8px -16px;
}
.abilityCardRight h1{
margin-bottom: 8px;
}

View File

@@ -1,122 +1,14 @@
<template name="abilityMiniCard"> <template name="abilityMiniCard">
<paper-shadow class="card double abilityMiniCard" <paper-shadow class="card abilityMiniCard clickable"
hero-id="main" {{detailHero ability ../_id}}> hero-id="main" {{detailHero ability ../_id}}
<div class="abilityScore white-text {{color}}" layout horizontal>
<div class="left white-text {{color}}"
hero-id="toolbar" {{detailHero ability ../_id}}> hero-id="toolbar" {{detailHero ability ../_id}}>
<h1 class="display1">{{../attributeValue ability}}</h1> <div class="display1">{{characterCalculate "attributeValue" ../_id ability}}</div>
<h2>{{../abilityMod ability}}</h2> <div class="title">{{abilityMod}}</div>
</div> </div>
<div class="abilityCardRight subhead" layout horizontal center> <div class="right subhead" layout horizontal center>
{{title}} {{title}}
</div> </div>
<paper-ripple fit></paper-ripple>
</paper-shadow> </paper-shadow>
</template> </template>
<template name="strengthCard">
<paper-shadow class="card double">
<div class="abilityScore red white-text">
{{> ripple color="#eee"}}
<h1 class="display1">{{attributeValue "strength"}}</h1>
<h2>{{abilityMod "strength"}}</h2>
</div>
<div class="abilityCardRight">
<h1>Strength</h1>
{{> skillRow name="Save" skill="strengthSave"}}
<hr>
{{> skillRow name="Athletics" skill="athletics"}}
</div>
</paper-shadow>
</template>
<template name="dexterityCard">
<paper-shadow class="card double">
<div class="abilityScore green white-text">
{{> ripple color="#eee"}}
<h1 class="display1">{{attributeValue "dexterity"}}</h1>
<h2>{{abilityMod "dexterity"}}</h2>
</div>
<div class="abilityCardRight">
<h1>Dexterity</h1>
{{> skillRow name="Save" skill="dexteritySave"}}
<hr>
{{> skillRow name="Acrobatics" skill="acrobatics"}}
{{> skillRow name="Sleight of Hand" skill="sleightOfHand"}}
{{> skillRow name="Stealth" skill="stealth"}}
</div>
</paper-shadow>
</template>
<template name="constitutionCard">
<paper-shadow class="card double">
<div class="abilityScore deep-orange white-text">
{{> ripple color="#eee"}}
<h1 class="display1">{{attributeValue "constitution"}}</h1>
<h2>{{abilityMod "constitution"}}</h2>
</div>
<div class="abilityCardRight">
<h1>Constitution</h1>
{{> skillRow name="Save" skill="constitutionSave"}}
<hr>
</div>
</paper-shadow>
</template>
<template name="intelligenceCard">
<paper-shadow class="card double">
<div class="abilityScore indigo white-text">
{{> ripple color="#eee"}}
<h1 class="display1">{{attributeValue "intelligence"}}</h1>
<h2>{{abilityMod "intelligence"}}</h2>
</div>
<div class="abilityCardRight">
<h1>Intelligence</h1>
{{> skillRow name="Save" skill="intelligenceSave"}}
<hr>
{{> skillRow name="Arcana" skill="arcana"}}
{{> skillRow name="History" skill="history"}}
{{> skillRow name="Investigation" skill="investigation"}}
{{> skillRow name="Nature" skill="nature"}}
{{> skillRow name="Religion" skill="religion"}}
</div>
</paper-shadow>
</template>
<template name="wisdomCard">
<paper-shadow class="card double">
<div class="abilityScore purple white-text">
{{> ripple color="#eee"}}
<h1 class="display1">{{attributeValue "wisdom"}}</h1>
<h2>{{abilityMod "wisdom"}}</h2>
</div>
<div class="abilityCardRight">
<h1>Wisdom</h1>
{{> skillRow name="Save" skill="wisdomSave"}}
<hr>
{{> skillRow name="Animal Handling" skill="animalHandling"}}
{{> skillRow name="Insight" skill="insight"}}
{{> skillRow name="Medicine" skill="medicine"}}
{{> skillRow name="Perception" skill="perception" showPassive="true"}}
{{> skillRow name="Survival" skill="survival"}}
</div>
</paper-shadow>
</template>
<template name="charismaCard">
<paper-shadow class="card double">
<div class="abilityScore pink white-text">
{{> ripple color="#eee"}}
<h1 class="display1">{{attributeValue "charisma"}}</h1>
<h2>{{abilityMod "charisma"}}</h2>
</div>
<div class="abilityCardRight">
<h1>Charisma</h1>
{{> skillRow name="Save" skill="charismaSave"}}
<hr>
{{> skillRow name="Deception" skill="deception"}}
{{> skillRow name="Intimidation" skill="intimidation"}}
{{> skillRow name="Performance" skill="performance"}}
{{> skillRow name="Persuasion" skill="persuasion"}}
</div>
</paper-shadow>
</template>

View File

@@ -0,0 +1,9 @@
Template.abilityMiniCard.helpers({
abilityMod: function() {
return signedString(
Characters.calculate.abilityMod(
Template.parentData()._id, this.ability
)
);
}
});

View File

@@ -1,9 +1,11 @@
Template.addTHPDialog.events({ Template.addTHPDialog.events({
"tap #addButton": function(event, instance){ "tap #addButton": function(event, instance){
var max = +instance.find("#quantityInput").value;
if (!max || max < 0) max = 0;
TemporaryHitPoints.insert({ TemporaryHitPoints.insert({
charId: this.charId, charId: this.charId,
name: instance.find("#nameInput").value, name: instance.find("#nameInput").value,
maximum: +instance.find("#quantityInput").value, maximum: max,
deleteOnZero: !!instance.find("#deleteWhenZeroCheckbox").checked, deleteOnZero: !!instance.find("#deleteWhenZeroCheckbox").checked,
}); });
} }

View File

@@ -1,6 +1,6 @@
<!-- needs name, char, and statName --> <!-- needs name, char, and statName -->
<template name="attributeDialog"> <template name="attributeDialog">
{{#baseDialog title=name class=colorClass hideEdit=true}} {{#baseDialog title=name class=color hideEdit=true}}
{{> attributeDialogView}} {{> attributeDialogView}}
{{/baseDialog}} {{/baseDialog}}
</template> </template>
@@ -23,7 +23,7 @@
{{#each baseEffects}} {{#each baseEffects}}
<tr> <tr>
<td>{{sourceName}}</td> <td>{{sourceName}}</td>
<td>{{signedString statValue}}</td> <td>Base: {{statValue}}</td>
</tr> </tr>
{{/each}} {{/each}}
{{#each addEffects}} {{#each addEffects}}
@@ -35,7 +35,7 @@
{{#each mulEffects}} {{#each mulEffects}}
<tr> <tr>
<td>{{sourceName}}</td> <td>{{sourceName}}</td>
<td>&times;{{statValue}}</td> <td>&times; {{statValue}}</td>
</tr> </tr>
{{/each}} {{/each}}
{{#each minEffects}} {{#each minEffects}}

View File

@@ -93,54 +93,61 @@ var abilities = {
charisma: {name: "Charisma"}, charisma: {name: "Charisma"},
}; };
Template.attributeDialog.helpers({
color: function(){
if (this.color) return this.color + " white-text";
var char = Characters.findOne(this.charId, {fields: {color: 1}});
if (char) return getColorClass(char.color);
},
});
Template.attributeDialogView.helpers({ Template.attributeDialogView.helpers({
or: function(a, b, c){ or: function(a, b, c){
return a || b || c; return a || b || c;
}, },
adjustment: function(){ adjustment: function(){
var char = Characters.findOne(this.charId); var value = Characters.calculate.attributeValue(this.charId, this.statName);
if (!char) return; var base = Characters.calculate.attributeBase(this.charId, this.statName);
var value = char.attributeValue(this.statName);
var base = char.attributeBase(this.statName);
return value - base; return value - base;
}, },
baseEffects: function(){ baseEffects: function(){
return Effects.find( return Effects.find(
{charId: this.charId, stat: this.statName, operation: "base"} {charId: this.charId, stat: this.statName, operation: "base", enabled: true}
); );
}, },
addEffects: function(){ addEffects: function(){
return Effects.find( return Effects.find(
{charId: this.charId, stat: this.statName, operation: "add"} {charId: this.charId, stat: this.statName, operation: "add", enabled: true}
); );
}, },
mulEffects: function(){ mulEffects: function(){
return Effects.find( return Effects.find(
{charId: this.charId, stat: this.statName, operation: "mul"} {charId: this.charId, stat: this.statName, operation: "mul", enabled: true}
); );
}, },
minEffects: function(){ minEffects: function(){
return Effects.find( return Effects.find(
{charId: this.charId, stat: this.statName, operation: "min"} {charId: this.charId, stat: this.statName, operation: "min", enabled: true}
); );
}, },
maxEffects: function(){ maxEffects: function(){
return Effects.find( return Effects.find(
{charId: this.charId, stat: this.statName, operation: "max"} {charId: this.charId, stat: this.statName, operation: "max", enabled: true}
); );
}, },
attributeBase: function(){ attributeBase: function(){
var char = Characters.findOne(this.charId); return Characters.calculate.attributeBase(this.charId, this.statName);
if (!char) throw "character is " + char;
return char.attributeBase(this.statName);
}, },
attributeValue: function() { attributeValue: function() {
var char = Characters.findOne(this.charId); return Characters.calculate.attributeValue(this.charId, this.statName);
if (!char) throw "character is " + char;
return char.attributeValue(this.statName);
}, },
sourceName: function(){ sourceName: function(){
if (this.parent.collection === "Characters") return this.name; if (this.parent.group === "racial"){
return this.getParent().race;
}
if (this.parent.collection === "Characters"){
return this.name;
}
return this.getParent().name; return this.getParent().name;
}, },
operationName: function(){ operationName: function(){

View File

@@ -0,0 +1,39 @@
<template name="strengthDialog">
{{#baseDialog title=name class=color hideEdit=true}}
{{> attributeDialogView}}
<hr class="vertMargin">
<div>
<div class="title padded">Carrying</div>
{{> carryCapacityTable}}
<div class="title padded">Jumping</div>
<table class="strengthTable">
<tr>
<td>Running long jump</td>
<td>{{evaluate charId "strength"}} feet</td>
</tr>
<tr>
<td>Standing long jump</td>
<td>{{evaluate charId "floor(strength/2)"}} feet</td>
</tr>
<tr>
<td>Running high jump</td>
<td>{{evaluate charId "3 + strengthMod"}} feet</td>
<td class="caption">
Can reach a ledge as high as
{{evaluate charId "3 + strengthMod"}} feet
+ 1.5&times; your height
</td>
</tr>
<tr>
<td>Standing high jump</td>
<td>{{evaluate charId "floor((3 + strengthMod)/2)"}} feet</td>
<td class="caption">
Can reach a ledge as high as
{{evaluate charId "floor((3 + strengthMod)/2)"}} feet
+ 1.5&times; your height
</td>
</tr>
</table>
</div>
{{/baseDialog}}
</template>

View File

@@ -0,0 +1,7 @@
Template.strengthDialog.helpers({
color: function(){
if (this.color) return this.color + " white-text";
var char = Characters.findOne(this.charId, {fields: {color: 1}});
if (char) return getColorClass(char.color);
},
});

View File

@@ -0,0 +1,28 @@
<template name="carryCapacityTable">
<table class="carryCapacityTable strengthTable">
<tr>
<td>Encumbered</td>
<td>&gt;{{evaluate charId "strength * 5 * carryMultiplier"}}lbs</td>
<td class="caption">Variant rule, encumbered characters move 10 feet slower</td>
</tr>
<tr>
<td>Heavily encumbered</td>
<td>&gt;{{evaluate charId "strength * 10 * carryMultiplier"}}lbs</td>
<td class="caption">
Variant rule, heavily encumbered characters move 20 feet slower and have disadvantage on ability checks, attack rolls, and saving thows that use Strength, Dexterity, or Constitution
</td>
</tr>
<tr>
<td>Over Encumbered</td>
<td>&gt;{{evaluate charId "strength * 15 * carryMultiplier"}}lbs</td>
<td class="caption">
Characters that can only just lift, push or drag their current load can only move at 5 feet.
</td>
</tr>
<tr>
<td>Push, drag or lift maximum</td>
<td>{{evaluate charId "strength * 30 * carryMultiplier"}}lbs</td>
<td class="caption"></td>
</tr>
</table>
</template>

View File

@@ -1,4 +1,4 @@
.healthCard paper-slider{ .healthCard paper-diff-slider{
width: 100%; width: 100%;
margin-right: 8px; margin-right: 8px;
} }

View File

@@ -1,41 +1,60 @@
<template name="healthCard"> <template name="healthCard">
<paper-shadow class="card container healthCard" <paper-shadow class="card container healthCard"
hero-id="main" {{detailHero "hitPoints" _id}} hero-id="main" {{detailHero "hitPoints" _id}}
layout horizontal wrap> layout horizontal wrap>
<div class="green white-text subhead padded leftRound" <div class="green white-text subhead left"
hero-id="toolbar" {{detailHero "hitPoints" _id}} hero-id="toolbar" {{detailHero "hitPoints" _id}}
layout vertical center> layout vertical center center-justified>
<div class="hitPointTitle clickable">Hit Points</div> <div class="hitPointTitle clickable">Hit Points</div>
<paper-icon-button class="white54" id="addTempHP" icon="add"></paper-icon-button> <paper-icon-button class="white54"
id="addTempHP"
icon="add"
disabled={{#unless canEditCharacter _id}}true{{/unless}}>
</paper-icon-button>
</div> </div>
<div class="padded" flex layout vertical center-justified style="min-width: 180px;"> <div class="right" flex layout vertical center-justified style="min-width: 180px;">
<paper-slider id="hitPointSlider" <div layout horizontal>
value={{attributeValue "hitPoints"}} <paper-diff-slider id="hitPointSlider"
max={{attributeBase "hitPoints"}} value={{characterCalculate "attributeValue" _id "hitPoints"}}
editable pin max={{characterCalculate "attributeBase" _id "hitPoints"}}
role="slider" editable pin
></paper-slider> disabled={{#unless canEditCharacter _id}}true{{/unless}}
role="slider">
</paper-diff-slider>
</div>
{{#each tempHitPoints}} {{#each tempHitPoints}}
<div> <div>
{{name}} {{name}}
<div layout horizontal> <div layout horizontal>
<paper-slider class="tempHitPointSlider" <paper-diff-slider class="tempHitPointSlider"
value={{left}} value={{left}}
max={{maximum}} max={{maximum}}
editable pin editable pin
role="slider" role="slider"
flex flex
></paper-slider> ></paper-diff-slider>
{{#unless left}}{{#unless deleteOnZero}} {{#unless left}}
<paper-icon-button class="deleteTHP" icon="delete"></paper-icon-button> <paper-icon-button class="deleteTHP" icon="delete"></paper-icon-button>
{{/unless}}{{/unless}} {{/unless}}
</div> </div>
</div> </div>
{{/each}} {{/each}}
<div class="caption"> <div class="caption">
{{#if multipliers.immunities.length}} <div>Immune: {{#each multipliers.immunities}} {{name}} {{/each}}</div>{{/if}} {{#if multipliers.immunities.length}}
{{#if multipliers.resistances.length}}<div>Resistance: {{#each multipliers.resistances}} {{name}} {{/each}}</div>{{/if}} <div>
{{#if multipliers.weaknesses.length}} <div>Weakness: {{#each multipliers.weaknesses}} {{name}} {{/each}}</div>{{/if}} Immune: {{#each multipliers.immunities}} {{name}} {{/each}}
</div>
{{/if}}
{{#if multipliers.resistances.length}}
<div>
Resistance: {{#each multipliers.resistances}} {{name}} {{/each}}
</div>
{{/if}}
{{#if multipliers.weaknesses.length}}
<div>
Weakness: {{#each multipliers.weaknesses}} {{name}} {{/each}}
</div>
{{/if}}
</div> </div>
{{#if showDeathSave}} {{#if showDeathSave}}
{{#with deathSaveObject}} {{#with deathSaveObject}}

View File

@@ -3,7 +3,7 @@ Template.healthCard.helpers({
return TemporaryHitPoints.find({charId: this._id}); return TemporaryHitPoints.find({charId: this._id});
}, },
showDeathSave: function(){ showDeathSave: function(){
return this.attributeValue("hitPoints") <= 0; return Characters.calculate.attributeValue(this._id, "hitPoints") <= 0;
}, },
deathSaveObject: function(){ deathSaveObject: function(){
var char = Characters.findOne(this._id, {fields: {deathSave: 1}}); var char = Characters.findOne(this._id, {fields: {deathSave: 1}});
@@ -27,21 +27,20 @@ Template.healthCard.helpers({
return this.fail >= 3; return this.fail >= 3;
}, },
multipliers: function(){ multipliers: function(){
var char = Characters.findOne(this._id, {fields: {_id: 1}});
var multipliers = [ var multipliers = [
{name: "Acid", value: char.attributeValue("acidMultiplier", 1)}, {name: "Acid", value: Characters.calculate.attributeValue(this._id, "acidMultiplier")},
{name: "Bludgeoning", value: char.attributeValue("bludgeoningMultiplier", 1)}, {name: "Bludgeoning", value: Characters.calculate.attributeValue(this._id, "bludgeoningMultiplier")},
{name: "Cold", value: char.attributeValue("coldMultiplier", 1)}, {name: "Cold", value: Characters.calculate.attributeValue(this._id, "coldMultiplier")},
{name: "Fire", value: char.attributeValue("fireMultiplier", 1)}, {name: "Fire", value: Characters.calculate.attributeValue(this._id, "fireMultiplier")},
{name: "Force", value: char.attributeValue("forceMultiplier", 1)}, {name: "Force", value: Characters.calculate.attributeValue(this._id, "forceMultiplier")},
{name: "Lightning", value: char.attributeValue("lightningMultiplier", 1)}, {name: "Lightning", value: Characters.calculate.attributeValue(this._id, "lightningMultiplier")},
{name: "Necrotic", value: char.attributeValue("necroticMultiplier", 1)}, {name: "Necrotic", value: Characters.calculate.attributeValue(this._id, "necroticMultiplier")},
{name: "Piercing", value: char.attributeValue("piercingMultiplier", 1)}, {name: "Piercing", value: Characters.calculate.attributeValue(this._id, "piercingMultiplier")},
{name: "Poison", value: char.attributeValue("poisonMultiplier", 1)}, {name: "Poison", value: Characters.calculate.attributeValue(this._id, "poisonMultiplier")},
{name: "Psychic", value: char.attributeValue("psychicMultiplier", 1)}, {name: "Psychic", value: Characters.calculate.attributeValue(this._id, "psychicMultiplier")},
{name: "Radiant", value: char.attributeValue("radiantMultiplier", 1)}, {name: "Radiant", value: Characters.calculate.attributeValue(this._id, "radiantMultiplier")},
{name: "Slashing", value: char.attributeValue("slashingMultiplier", 1)}, {name: "Slashing", value: Characters.calculate.attributeValue(this._id, "slashingMultiplier")},
{name: "Thunder", value: char.attributeValue("thunderMultiplier", 1)}, {name: "Thunder", value: Characters.calculate.attributeValue(this._id, "thunderMultiplier")},
]; ];
multipliers = _.groupBy(multipliers, "value"); multipliers = _.groupBy(multipliers, "value");
return { return {
@@ -55,7 +54,8 @@ Template.healthCard.helpers({
Template.healthCard.events({ Template.healthCard.events({
"change #hitPointSlider": function(event){ "change #hitPointSlider": function(event){
var value = event.currentTarget.value; var value = event.currentTarget.value;
var adjustment = value - this.attributeBase("hitPoints"); var base = Characters.calculate.attributeBase(this._id, "hitPoints")
var adjustment = value - base;
Characters.update(this._id, {$set: {"hitPoints.adjustment": adjustment}}); Characters.update(this._id, {$set: {"hitPoints.adjustment": adjustment}});
//reset the death saves if we are gaining HP //reset the death saves if we are gaining HP
if (value > 0) if (value > 0)

View File

@@ -1,23 +1,32 @@
<template name="hitDice"> <template name="hitDice">
{{#if ../attributeBase name}} {{#if characterCalculate "attributeBase" ../_id name}}
<paper-shadow class="card container" hero-id="main" {{detailHero name ../_id}} layout horizontal> <paper-shadow class="card hit-dice" hero-id="main"
<div class="containerLeft green" layout horizontal hero-id="toolbar" {{detailHero name ../_id}}> {{detailHero name ../_id}}
<div class="resourceButtons"> layout horizontal>
<paper-icon-button class="resourceUp" icon="arrow-drop-up" disabled={{cantIncrement}}></paper-icon-button> <div class="left green display1 white-text"
<paper-icon-button class="resourceDown" icon="arrow-drop-down" disabled={{cantDecrement}}></paper-icon-button> hero-id="toolbar" {{detailHero name ../_id}}
layout horizontal>
<div>
<paper-icon-button class="resourceUp"
icon="arrow-drop-up"
disabled={{cantIncrement}}>
</paper-icon-button>
<paper-icon-button class="resourceDown"
icon="arrow-drop-down"
disabled={{cantDecrement}}>
</paper-icon-button>
</div> </div>
<div class="resourceValue" layout vertical center> <div class="resourceValue" layout vertical center>
<div> <div>
{{../attributeValue name}} {{characterCalculate "attributeValue" ../_id name}}
</div> </div>
<div class="title white-text"> <div class="title white-text">
d{{diceNum}} {{../abilityMod "constitution"}} d{{diceNum}} {{conMod}}
</div> </div>
</div> </div>
</div> </div>
<div class="containerRight clickable" flex relative horizontal layout center> <div class="right clickable" flex relative horizontal layout center>
Hit Dice Hit Dice
<paper-ripple fit></paper-ripple>
</div> </div>
</paper-shadow> </paper-shadow>
{{/if}} {{/if}}

View File

@@ -1,36 +1,49 @@
Template.hitDice.helpers({ Template.hitDice.helpers({
cantIncrement: function(){ cantIncrement: function(){
var valueSmallerThanBase = this.char.attributeValue(this.name) < var value = Characters.calculate.attributeValue(this.char._id, this.name);
this.char.attributeBase(this.name); var base = Characters.calculate.attributeBase(this.char._id, this.name);
return !valueSmallerThanBase; return value >= base || !canEditCharacter(this.char._id);
}, },
cantDecrement: function(){ cantDecrement: function(){
var valuePositive = this.char.attributeValue(this.name) > 0; var value = Characters.calculate.attributeValue(this.char._id, this.name);
return !valuePositive; return value <= 0 || !canEditCharacter(this.char._id);
},
conMod: function(){
return signedString(
Characters.calculate.abilityMod(this.char._id, "constitution")
);
}, },
}); });
Template.hitDice.events({ Template.hitDice.events({
"tap .resourceUp": function(event){ "tap .resourceUp": function(event){
if (this.char.attributeValue(this.name) < this.char.attributeBase(this.name)){ 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: {}}; var modifier = {$inc: {}};
modifier.$inc[this.name + ".adjustment"] = 1; modifier.$inc[this.name + ".adjustment"] = 1;
Characters.update(this.char._id, modifier, {validate: false}); Characters.update(this.char._id, modifier, {validate: false});
} }
}, },
"tap .resourceDown": function(event){ "tap .resourceDown": function(event){
if (this.char.attributeValue(this.name) > 0){ var value = Characters.calculate.attributeValue(this.char._id, this.name);
if (value > 0){
var modifier = {$inc: {}}; var modifier = {$inc: {}};
modifier.$inc[this.name + ".adjustment"] = -1; modifier.$inc[this.name + ".adjustment"] = -1;
Characters.update(this.char._id, modifier, {validate: false}); Characters.update(this.char._id, modifier, {validate: false});
} }
}, },
"tap .containerRight": function() { "tap .right": function() {
var charId = Template.parentData()._id; var charId = Template.parentData()._id;
var title = "d" + this.diceNum + " Hit Dice"; var title = "d" + this.diceNum + " Hit Dice";
GlobalUI.setDetail({ GlobalUI.setDetail({
template: "attributeDialog", template: "attributeDialog",
data: {name: title, statName: this.name, charId: charId}, data: {
name: title,
statName: this.name,
charId: charId,
color: "green",
},
heroId: charId + this.name, heroId: charId + this.name,
}); });
}, },

View File

@@ -1,6 +1,6 @@
<!-- needs name, char, and skillName --> <!-- needs name, charId, and skillName -->
<template name="skillDialog"> <template name="skillDialog">
{{#baseDialog title=name class=colorClass hideEdit=true}} {{#baseDialog title=name class=color hideEdit=true}}
{{> skillDialogView}} {{> skillDialogView}}
{{/baseDialog}} {{/baseDialog}}
</template> </template>
@@ -8,7 +8,7 @@
<template name="skillDialogView"> <template name="skillDialogView">
<div layout vertical center> <div layout vertical center>
<div class="display2"> <div class="display2">
{{char.skillMod skillName}} {{characterCalculate "skillMod" charId skillName}}
</div> </div>
<div class="subhead"> <div class="subhead">
<core-icon icon="{{profIcon}}" class="black54"></core-icon> <core-icon icon="{{profIcon}}" class="black54"></core-icon>
@@ -25,9 +25,9 @@
<table class="summaryTable"> <table class="summaryTable">
<tr> <tr>
<td>{{abilityName}}</td> <td>{{abilityName}}</td>
<td>{{char.abilityMod ability}}</td> <td>{{characterCalculate "abilityMod" charId ability}}</td>
</tr> </tr>
{{#if char.proficiency skillName}} {{#if characterCalculate "proficiency" charId skillName}}
<tr> <tr>
<td>{{proficiencyValue}}</td> <td>{{proficiencyValue}}</td>
<td>{{signedString profBonus}}</td> <td>{{signedString profBonus}}</td>
@@ -59,7 +59,7 @@
{{/each}} {{/each}}
<tr class="body2"> <tr class="body2">
<td>Total</td> <td>Total</td>
<td>{{char.skillMod skillName}}</td> <td>{{characterCalculate "skillMod" charId skillName}}</td>
</tr> </tr>
</table> </table>

View File

@@ -93,14 +93,20 @@ var abilities = {
charisma: {name: "Charisma"}, charisma: {name: "Charisma"},
}; };
Template.skillDialog.helpers({
color: function(){
if (this.color) return this.color + " white-text";
var char = Characters.findOne(this.charId, {fields: {color: 1}});
if (char) return getColorClass(char.color);
},
});
Template.skillDialogView.helpers({ Template.skillDialogView.helpers({
or: function(a, b, c){ or: function(a, b, c){
return a || b || c; return a || b || c;
}, },
profIcon: function(){ profIcon: function(){
var char = Characters.findOne(this.charId); var prof = Characters.calculate.proficiency(this.charId, this.skillName);
if (!char) return;
var prof = char.proficiency(this.skillName);
if (prof > 0 && prof < 1) return "image:brightness-2"; if (prof > 0 && prof < 1) return "image:brightness-2";
if (prof === 1) return "image:brightness-1"; if (prof === 1) return "image:brightness-1";
if (prof > 1) return "av:album"; if (prof > 1) return "av:album";
@@ -115,13 +121,13 @@ Template.skillDialogView.helpers({
profBonus: function(){ profBonus: function(){
var char = Characters.findOne(this.charId); var char = Characters.findOne(this.charId);
if (!char) return; if (!char) return;
return char.proficiency(this.skillName) * var prof = Characters.calculate.proficiency(this.charId, this.skillName);
char.attributeValue("proficiencyBonus"); var proficiencyBonus =
Characters.calculate.attributeValue(this.charId, "proficiencyBonus");
return prof * proficiencyBonus;
}, },
proficiencyValue: function(){ proficiencyValue: function(){
var char = Characters.findOne(this.charId); var prof = Characters.calculate.proficiency(this.charId, this.skillName);
if (!char) return;
var prof = char.proficiency(this.skillName);
if (prof == 0.5) return "Half Proficiency"; if (prof == 0.5) return "Half Proficiency";
if (prof == 1) return "Proficient"; if (prof == 1) return "Proficient";
if (prof == 2) return "Double Proficiency"; if (prof == 2) return "Double Proficiency";
@@ -132,6 +138,7 @@ Template.skillDialogView.helpers({
charId: this.charId, charId: this.charId,
stat: this.skillName, stat: this.skillName,
operation: "add", operation: "add",
enabled: true,
}); });
}, },
mulEffects: function(){ mulEffects: function(){
@@ -139,6 +146,7 @@ Template.skillDialogView.helpers({
charId: this.charId, charId: this.charId,
stat: this.skillName, stat: this.skillName,
operation: "mul", operation: "mul",
enabled: true,
}); });
}, },
minEffects: function(){ minEffects: function(){
@@ -146,6 +154,7 @@ Template.skillDialogView.helpers({
charId: this.charId, charId: this.charId,
stat: this.skillName, stat: this.skillName,
operation: "min", operation: "min",
enabled: true,
}); });
}, },
maxEffects: function(){ maxEffects: function(){
@@ -153,6 +162,7 @@ Template.skillDialogView.helpers({
charId: this.charId, charId: this.charId,
stat: this.skillName, stat: this.skillName,
operation: "max", operation: "max",
enabled: true,
}); });
}, },
advEffects: function(){ advEffects: function(){
@@ -160,6 +170,7 @@ Template.skillDialogView.helpers({
charId: this.charId, charId: this.charId,
stat: this.skillName, stat: this.skillName,
operation: "advantage", operation: "advantage",
enabled: true,
}); });
}, },
dadvEffects: function(){ dadvEffects: function(){
@@ -167,6 +178,7 @@ Template.skillDialogView.helpers({
charId: this.charId, charId: this.charId,
stat: this.skillName, stat: this.skillName,
operation: "disadvantage", operation: "disadvantage",
enabled: true,
}); });
}, },
conditionalEffects: function(){ conditionalEffects: function(){
@@ -174,6 +186,7 @@ Template.skillDialogView.helpers({
charId: this.charId, charId: this.charId,
stat: this.skillName, stat: this.skillName,
operation: "conditional", operation: "conditional",
enabled: true,
}); });
}, },
ability: function(){ ability: function(){
@@ -184,20 +197,21 @@ Template.skillDialogView.helpers({
return skill.ability; return skill.ability;
}, },
abilityName: function(){ abilityName: function(){
var opts = {fields: {}}; var skill = Characters.calculate.getField(this.charId, this.skillName);
opts.fields[this.skillName] = 1;
var char = Characters.findOne(this.charId, opts);
if (!char) return;
var skill = char[this.skillName];
if (!skill) return; if (!skill) return;
var ability = skill.ability; var ability = skill.ability;
return abilities[ability] && abilities[ability].name; return abilities[ability] && abilities[ability].name;
}, },
char: function(){
return Characters.findOne(this.charId, {fields:{_id: 1}});
},
sourceName: function(){ sourceName: function(){
if (this.parent.collection === "Characters") return "inate"; if (this.parent.collection === "Characters"){
if (this.parent.group === "racial"){
return Characters.calculate.getField(this.charId, "race") || "Race";
}
if (this.parent.group === "background"){
return "Background";
}
return "Innate";
}
return this.getParent().name; return this.getParent().name;
}, },
operationName: function(){ operationName: function(){

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