Compare commits

...

69 Commits
0.2.4 ... 0.5.6

Author SHA1 Message Date
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
Stefan Zermatten
43c4122fe3 Fixed stack dragging within the same container 2015-05-11 17:10:58 +02:00
Stefan Zermatten
3f4dcc146a fixed charId's being out of date after re-parenting 2015-05-11 17:10:28 +02:00
Stefan Zermatten
e4600decd0 Added more things to except list for need sign in 2015-05-11 16:51:17 +02:00
Stefan Zermatten
f6df716870 Rewrote how item drag and drop works. Need to update charId's to keep up. 2015-05-11 16:51:02 +02:00
Stefan Zermatten
b99da301cd Updated meteor 2015-05-11 12:16:38 +02:00
Stefan Zermatten
0a01885300 Finished implementing useraccounts 2015-05-11 12:15:00 +02:00
Stefan Zermatten
5cb1515235 Began implementing useraccounts and permissions properly 2015-05-08 12:59:38 +02:00
132 changed files with 3363 additions and 1540 deletions

View File

@@ -16,7 +16,14 @@ differential:vulcanize
matb33:collection-hooks
zimme:collection-softremovable
momentjs:moment
mike:mocha
dburles:mongo-collection-instances
percolate:migrations
ecwyne:mathjs
useraccounts:polymer
accounts-google
splendido:accounts-meld
email
fourseven:scss@2.1.1
wolves:bourbon
meteorhacks:subs-manager
meteorhacks:kadira

View File

@@ -1,10 +1,11 @@
accounts-base@1.2.0
accounts-google@1.0.4
accounts-oauth@1.1.5
accounts-password@1.1.1
accounts-ui@1.1.5
accounts-ui-unstyled@1.1.7
aldeed:collection2@2.3.3
aldeed:simple-schema@1.3.2
amplify@1.0.0
aldeed:simple-schema@1.3.3
autoupdate@1.2.1
base64@1.0.3
binary-heap@1.0.3
@@ -23,7 +24,9 @@ ecwyne:mathjs@0.25.0
ejson@1.0.6
email@1.0.6
fastclick@1.0.3
fourseven:scss@2.1.1
geojson-utils@1.0.3
google@1.1.5
html-tools@1.0.4
htmljs@1.0.4
http@1.1.0
@@ -44,48 +47,48 @@ less@1.0.14
livedata@1.0.13
localstorage@1.0.3
logging@1.0.7
matb33:collection-hooks@0.7.11
matb33:collection-hooks@0.7.13
meteor@1.1.6
meteor-platform@1.2.2
mike:mocha@0.5.3
meteorhacks:kadira@2.21.0
meteorhacks:meteorx@1.3.1
meteorhacks:subs-manager@1.3.0
minifiers@1.1.5
minimongo@1.0.8
mobile-status-bar@1.0.3
momentjs:moment@2.10.3
mongo@1.1.0
mongo-livedata@1.0.8
npm-bcrypt@0.7.8_2
oauth@1.1.4
oauth2@1.1.3
observe-sequence@1.0.6
ordered-dict@1.0.3
package-version-parser@3.0.3
percolate:migrations@0.7.3
practicalmeteor:chai@1.9.2_3
practicalmeteor:loglevel@1.1.0_3
percolate:migrations@0.7.5
random@1.0.3
reactive-dict@1.1.0
reactive-var@1.0.5
reload@1.1.3
retry@1.0.3
routepolicy@1.0.5
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
session@1.1.0
sha@1.0.3
softwarerero:accounts-t9n@1.0.9
spacebars@1.0.6
spacebars-compiler@1.0.6
splendido:accounts-emails-field@1.2.0
splendido:accounts-meld@1.3.0
srp@1.0.3
templating@1.1.1
tracker@1.0.7
ui@1.0.6
underscore@1.0.3
url@1.0.4
velocity:chokidar@0.12.6_1
velocity:core@0.6.0
velocity:html-reporter@0.5.3
velocity:meteor-internals@1.1.0_7
velocity:shim@0.1.0
useraccounts:core@1.9.1
useraccounts:polymer@1.9.1
webapp@1.2.0
webapp-hashing@1.0.3
wolves:bourbon@1.0.0
zimme:collection-behaviours@1.0.4
zimme:collection-softremovable@1.0.4

View File

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

View File

@@ -1,28 +1,51 @@
Buffs = new Mongo.Collection("buffs");
//buffs are temporary once applied and store things which expire and their expiry time
Schemas.Buff = new SimpleSchema({
//buff id
_id: {
type: String,
regEx: SimpleSchema.RegEx.Id,
autoValue: function(){
if (!this.isSet) return Random.id();
},
},
charId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
//expiry time
expiry: {type: Number, optional: true},
duration: {type: Number},
name: {
type: String,
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.attachBehaviour("softRemovable");
makeParent(Buffs, "name"); //parents of effects and attacks
makeParent(Buffs, ["name", "enabled"]); //parents of effects
Buffs.allow(CHARACTER_SUBSCHEMA_ALLOW);
Buffs.deny(CHARACTER_SUBSCHEMA_DENY);

View File

@@ -159,6 +159,7 @@ Schemas.Character = new SimpleSchema({
deathSave: {type: Schemas.DeathSave},
//permissions
party: {type: String, regEx: SimpleSchema.RegEx.Id, optional: true},
owner: {type: String, regEx: SimpleSchema.RegEx.Id},
readers: {type: [String], regEx: SimpleSchema.RegEx.Id, defaultValue: []},
writers: {type: [String], regEx: SimpleSchema.RegEx.Id, defaultValue: []},
@@ -168,21 +169,64 @@ Schemas.Character = new SimpleSchema({
defaultValue: "q",
},
//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);
var attributeBase = function(charId, statName){
check(statName, String);
var effects = Effects.find(
{charId: charId, stat: statName, enabled: true}
).fetch();
effects = _.groupBy(effects, "operation");
var value = _.contains(DAMAGE_MULTIPLIERS, statName) ? 1 : 0;
//if it's a damage multiplier, we treat it specially
if (_.contains(DAMAGE_MULTIPLIERS, statName)){
var effects = Effects.find(
{charId: charId, stat: statName, enabled: true, operation: "mul"}
).fetch();
var resistCount = 0;
var vulnCount = 0;
var multiplierEvaluationFail = false;
_.each(effects, function(effect){
var val = evaluateEffect(charId, effect);
if (val === 0.5){ //resistance
resistCount += 1;
} else if (val === 2){ //vulnerability
vulnCount += 1;
} else if (val === 0){ //imunity
return 0; //imunity is absolute
} else {
multiplierEvaluationFail = true;
}
});
if (multiplierEvaluationFail){
//we can't work it out correctly, set the value to 1
//and try work it out using regular maths below
value = 1;
} else if (resistCount && !vulnCount){
return 0.5;
} else if (!resistCount && vulnCount){
return 2;
} else {
return 1;
}
}
var value = 0;
//start with the highest base value
_.each(effects.base, function(effect){
Effects.find(
{charId: charId, stat: statName, enabled: true, operation: "base"}
).forEach(function(effect){
var efv = evaluateEffect(charId, effect);
if (efv > value){
value = efv;
@@ -190,23 +234,31 @@ var attributeBase = function(charId, statName){
});
//add all the add values
_.each(effects.add, function(effect){
Effects.find(
{charId: charId, stat: statName, enabled: true, operation: "add"}
).forEach(function(effect){
value += evaluateEffect(charId, effect);
});
//multiply all the mul values
_.each(effects.mul, function(effect){
Effects.find(
{charId: charId, stat: statName, enabled: true, operation: "mul"}
).forEach(function(effect){
value *= evaluateEffect(charId, effect);
});
//ensure value is >= all mins
_.each(effects.min, function(effect){
Effects.find(
{charId: charId, stat: statName, enabled: true, operation: "min"}
).forEach(function(effect){
var min = evaluateEffect(charId, effect);
value = value > min ? value : min;
});
//ensure value is <= all maxes
_.each(effects.max, function(effect){
Effects.find(
{charId: charId, stat: statName, enabled: true, operation: "max"}
).forEach(function(effect){
var max = evaluateEffect(charId, effect);
value = value < max ? value : max;
});

View File

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

View File

@@ -10,6 +10,7 @@ Schemas.Item = new SimpleSchema({
value: {type: Number, min: 0, defaultValue: 0, decimal: true},
enabled: {type: Boolean, defaultValue: false},
requiresAttunement: {type: Boolean, defaultValue: false},
"settings.showIncrement": {type: Boolean, defaultValue: false},
color: {
type: String,
allowedValues: _.pluck(colorOptions, "key"),
@@ -19,6 +20,156 @@ Schemas.Item = new SimpleSchema({
Items.attachSchema(Schemas.Item);
var checkMovePermission = function(itemId, parent) {
var item = Items.findOne(itemId);
if (!item)
throw new Meteor.Error("No such item",
"An item could not be found to move");
//handle permissions
var permission = Meteor.call("canWriteCharacter", item.charId);
if (!permission){
throw new Meteor.Error("Access denied",
"Not permitted to move items from this character");
}
if (parent.collection === "Characters"){
permission = Meteor.call("canWriteCharacter", parent.id);
if (!permission){
throw new Meteor.Error("Access denied",
"Not permitted to move items to this character");
}
} else {
var parentCollectionObject = global[parent.collection];
var parentObject = null;
if (parentCollectionObject)
parentObject = parentCollectionObject.findOne(
parent.id, {fields: {_id: 1, charId: 1}}
);
if (!parentObject) throw new Meteor.Error(
"Invalid parent",
"The destination parent " + parent.id +
" does not exist in the collection " + parent.collection
);
if (parentObject.charId){
permission = Meteor.call("canWriteCharacter", parentObject.charId);
if (!permission){
throw new Meteor.Error("Access denied",
"Not permitted to move items to this character");
}
}
}
};
var moveItem = function(itemId, enable, parentCollection, parentId) {
var item = Items.findOne(itemId);
if (!item) return;
parentCollection = parentCollection || item.parent.collection;
parentId = parentId || item.parent.id;
if (Meteor.isServer) {
checkMovePermission(itemId, {collection: parentCollection, id: parentId});
}
//update the item provided the update will actually change something
if (
item.parent.collection !== parentCollection ||
item.parent.id !== parentId ||
item.enabled !== enable
){
Items.update(
itemId,
{$set: {
"parent.collection": parentCollection,
"parent.id": parentId,
enabled: enable,
}}
);
}
};
Meteor.methods({
moveItemToParent: function(itemId, parent) {
check(itemId, String);
check(parent, {collection: String, id: String});
moveItem(itemId, false, parent.collection, parent.id);
},
moveItemToCharacter: function(itemId, charId) {
check(itemId, String);
check(charId, String);
moveItem(itemId, false, "Characters", charId);
},
moveItemToContainer: function(itemId, containerId) {
check(itemId, String);
check(containerId, String);
moveItem(itemId, false, "Containers", containerId);
},
equipItem: function(itemId, charId){
check(itemId, String);
check(charId, String);
moveItem(itemId, true, "Characters", charId);
},
unequipItem: function(itemId, charId){
check(itemId, String);
check(charId, String);
moveItem(itemId, false, "Characters", charId);
},
splitItemToParent: function(itemId, moveQuantity, parent){
check(itemId, String);
check(moveQuantity, Number);
check(parent, {id: String, collection: String});
//get the item
var item = Items.findOne(itemId);
if (!item) return;
//don't bother moving nothing
if (moveQuantity <= 0 || item.quantity <= 0){
return;
}
//ensure we are only moving up to the current stack size
if (item.quantity < moveQuantity){
moveQuantity = this.quantity;
}
if (Meteor.isServer) {
checkMovePermission(itemId, parent);
}
//create a new item stack
var newStack = _.omit(EJSON.clone(item), "_id");
newStack.parent = parent;
newStack.quantity = moveQuantity;
//find out if we have an exact replica in the destination
var query = _.omit(newStack, ["parent", "quantity"]);
query["parent.collection"] = newStack.parent.collection;
query["parent.id"] = newStack.parent.id;
query._id = {$ne: itemId}; //make sure we don't join it to itself
var existingStack = Items.findOne(query);
if (existingStack){
//increase the existing stack's size
Items.update(
existingStack._id,
{$inc: {quantity: moveQuantity}}
);
} else {
//insert the new stack
Items.insert(newStack, function(err, id){
if (err) throw err;
//copy the children also
Meteor.call("cloneChildren", item._id, {collection: "Items", id: id});
});
}
//reduce the old stack's size
var oldQuantity = item.quantity - moveQuantity;
if (oldQuantity === 0){
Items.remove(itemId);
} else {
Items.update(itemId, {$set: {quantity: oldQuantity}});
}
},
});
Items.helpers({
totalValue: function(){
return this.value * this.quantity;
@@ -33,103 +184,6 @@ Items.helpers({
return this.name;
}
},
equip: function(characterId){
var charId = characterId || this.charId;
if (!charId || !Characters.findOne(charId)) throw "Invalid character";
if (this.parent.collection === "Characters" &&
this.parent.id === charId &&
this.enabled) {
return;
}
Items.update(
this._id,
{$set: {
"parent.collection": "Characters",
"parent.id": charId,
enabled: true,
}}
);
},
unequip: function(){
if (!this.enabled) return;
Items.update(this._id, {$set: {enabled: false}});
},
moveToContainer: function(containerId){
if (!containerId || !Containers.findOne(containerId)){
throw "Invalid container";
}
if (this.parent.collection === "Containers" &&
this.parent.id === containerId &&
!this.enabled) return;
Items.update(
this._id,
{$set: {
"parent.collection": "Containers",
"parent.id": containerId,
enabled: false,
}}
);
},
moveToCharacter: function(characterId){
if (!characterId || !Characters.findOne(characterId)) {
throw "Invalid character";
}
if (this.parent.collection === "Characters" &&
this.parent.id === characterId &&
!this.enabled) return;
Items.update(
this._id,
{$set: {
"parent.collection": "Characters",
"parent.id": characterId,
charId: characterId,
enabled: false,
}}
);
},
splitToParent: function(parent, moveQuantity){
check(parent, {id: String, collection: String});
check(moveQuantity, Number);
var parentCollection = Meteor.isClient ?
window[parent.collection] : global[parent.collection];
if (!parent.id || !parentCollection.findOne(parent.id)){
throw "Invalid parent";
}
var oldStack = this;
//we can only move as much as we have, leaving 0 behind at worst
if (oldStack.quantity < moveQuantity) moveQuantity = oldStack.quantity;
var oldQuantity = oldStack.quantity - moveQuantity;
var newStack = _.pick(oldStack, Schemas.Item.objectKeys());
newStack.parent = parent;
newStack.quantity = moveQuantity;
var existingStack = Items.findOne(_.omit(newStack, "quantity"));
var updateStackSize = function(){
if (oldQuantity > 0){
Items.update(oldStack._id, {$set: {quantity: oldQuantity}});
} else {
Items.remove(oldStack._id);
}
};
if (existingStack){
Items.update(
existingStack._id,
{$inc: {quantity: moveQuantity}},
{},
function(){
updateStackSize();
}
);
} else {
Items.insert(newStack, function(err, id){
if (err) throw err;
updateStackSize();
//copy the children also
Meteor.call("cloneChildren", oldStack._id, {collection: "Items", id: id});
});
}
},
});
Items.before.update(function(userId, doc, fieldNames, modifier, options){

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,44 +1,23 @@
Schema = {};
Schema.User = new SimpleSchema({
username: {
type: String,
regEx: /^[a-z0-9A-Z_]{3,15}$/,
optional: true,
},
emails: {
type: [Object],
// this must be optional if you also use other login services like facebook,
// but if you use only accounts-password, then it can be required
optional: true,
},
"emails.$.address": {
type: String,
regEx: SimpleSchema.RegEx.Email,
},
"emails.$.verified": {
type: Boolean
},
createdAt: {
type: Date
},
services: {
type: Object,
optional: true,
blackbox: true,
},
roles: {
type: [String],
optional: true,
},
});
Meteor.users.attachSchema(Schema.User);
Meteor.users.allow({
update: function(userId, doc, fields, modifier) {
return userId === doc._id &&
fields.length === 1 &&
fields[0] === "username";
if (
doc._id === userId &&
_.contains(fields, "username") &&
_.contains(fields, "profile") &&
fields.length === 2 &&
_.keys(modifier).length === 1 &&
modifier.$set &&
modifier.$set["profile.username"] &&
modifier.$set.username &&
_.keys(modifier.$set).length === 2
){
var expectedUsername = modifier.$set["profile.username"];
expectedUsername = expectedUsername.toLowerCase().replace(/\s+/gm, "");
if (modifier.$set.username !== expectedUsername){
return false;
}
var foundUser = Meteor.call("getUserId", expectedUsername);
return !foundUser || foundUser === userId;
}
}
});

View File

@@ -3,15 +3,27 @@ Router.configure({
layoutTemplate: "layout",
});
Router.plugin("ensureSignedIn", {
only: [
"profile",
"characterList",
]
});
Router.plugin("dataNotFound", {notFoundTemplate: "notFound"});
Router.map(function() {
this.route("/", {
name: "home",
onAfterAction: function() {
document.title = appName;
},
});
this.route("characterList", {
path: "/characterList",
waitOn: function(){
return Meteor.subscribe("characterList", Meteor.userId());
return subsManager.subscribe("characterList", Meteor.userId());
},
data: {
characters: function(){
@@ -27,13 +39,13 @@ Router.map(function() {
path: "/character/:_id",
waitOn: function(){
return [
Meteor.subscribe("singleCharacter", this.params._id, Meteor.userId()),
subsManager.subscribe("singleCharacter", this.params._id, Meteor.userId()),
];
},
data: function() {
var data = Characters.findOne(
{_id: this.params._id},
{fields: {_id: 1, name: 1, color: 1}}
{fields: {_id: 1, name: 1, color: 1, writers: 1, readers: 1}}
);
return data;
},
@@ -56,4 +68,28 @@ Router.map(function() {
document.title = appName + " Account";
},
});
this.route("/changelog", {
name: "changeLog",
waitOn: function() {
return [
subsManager.subscribe("changeLog"),
]
},
data: {
changeLogs: function() {
return ChangeLogs.find({}, {sort: {version: -1}});
}
},
onAfterAction: function() {
document.title = appName;
},
});
this.route("/guide", {
name: "guide",
onAfterAction: function() {
document.title = appName;
},
});
});

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,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;
}
.blue-grey {
background-color: #607D8B;
.app-grey {
background-color: #424242;
}
.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,84 @@
@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);
}
//Horizontal rule
hr {
background-color: #444;
opacity: 0.12;
border-width: 0;
color: #444;
height: 1px;
line-height: 0;
margin: 16px -16px;
text-align: center;
}
//FABs
.floatyButton {
position: absolute;
bottom: 24px;
right: 24px;
}
//Buttons
paper-button {
color: #000;
color: rgba(0,0,0,0.87);
font-size: 14px;
font-weight: 400;
letter-spacing: 0.010;
text-transform: uppercase;
}
//Style shortcuts
.scroll-y {
overflow-y: auto;
}
.clickable, core-item, paper-tab {
cursor: pointer;
}
.pre-wrap, .prewrap{
white-space: pre-wrap;
}
.padded {
padding: 8px;
}
.fullwidth {
width: 100%;
}
.fab-buffer {
height: 100px;
}

View File

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

View File

@@ -1,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;
}
.white-text .display1{
.white-text .display1, .white-text.display1{
color: rgba(255,255,255,0.54);
}

View File

@@ -15,21 +15,11 @@
value={{details}}></paper-input>
</div>
<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-->
<paper-input id="damageInput"
label="Damage Bonus"
label="Damage"
floatinglabel
value={{damageBonus}}
value={{damage}}
flex></paper-input>
<!--DamageType-->
<paper-dropdown-menu id="damageTypeDropdown" label="Damage Type">

View File

@@ -1,6 +1,18 @@
var damageTypes = ["bludgeoning", "piercing", "slashing",
"acid", "cold", "fire", "force", "lightning", "necrotic",
"poison", "psychic", "radiant", "thunder"];
var damageTypes = [
"bludgeoning",
"piercing",
"slashing",
"acid",
"cold",
"fire",
"force",
"lightning",
"necrotic",
"poison",
"psychic",
"radiant",
"thunder",
];
Template.attackEdit.events({
"tap #deleteAttack": function(event, instance) {
@@ -13,7 +25,7 @@ Template.attackEdit.events({
},
"change #damageInput": function(event) {
var value = event.currentTarget.value;
Attacks.update(this._id, {$set: {damageBonus: value}});
Attacks.update(this._id, {$set: {damage: value}});
},
"change #detailInput": function(event) {
var value = event.currentTarget.value;
@@ -26,13 +38,6 @@ Template.attackEdit.events({
if (value == this.damageType) return;
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({
@@ -41,5 +46,5 @@ Template.attackEdit.helpers({
},
DAMAGE_DICE: function() {
return DAMAGE_DICE;
}
},
});

View File

@@ -1,11 +1,11 @@
<template name="attackView">
<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}}
</div>
<div layout vertical>
<div>
{{damageDice}}&nbsp;{{{evaluateSignedSpaced charId damageBonus}}}&nbsp;{{damageType}}
{{evaluateString charId damage}}&nbsp;{{damageType}}
</div>
{{#if details}}
<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,26 @@
<template name="characterSettings">
{{#with character}}
<div>
<table>
<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}}
</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">
<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>
{{#if readers.count}}
<div style="font-weight: 500;">
@@ -7,7 +19,7 @@
</div>
{{#each readers}}
<div layout horizontal center>
<div flex>{{username}}</div>
<div flex>{{getUserName}}</div>
<paper-icon-button class="deleteShare" icon="delete"></paper-icon-button>
</div>
{{/each}}

View File

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

View File

@@ -5,24 +5,35 @@
<div flex>
{{name}}
</div>
<div>
{{> colorDropdown}}
</div>
<paper-menu-button>
<paper-icon-button icon="more-vert" noink></paper-icon-button>
<paper-dropdown class="dropdown" halign="right">
<core-menu class="menu" style="color: black; color: rgba(0,0,0,0.87);">
<paper-item id="deleteCharacter"><core-icon icon="delete"></core-icon>Delete</paper-item>
<paper-item id="shareCharacter"><core-icon icon="social:share"></core-icon>Share</paper-item>
</core-menu>
</paper-dropdown>
</paper-menu-button>
{{#if canEditCharacter _id}}
<div>
{{> colorDropdown}}
</div>
<paper-menu-button>
<paper-icon-button icon="more-vert" noink></paper-icon-button>
<paper-dropdown class="dropdown" halign="right">
<core-menu class="menu" style="color: black; color: rgba(0,0,0,0.87);">
<paper-item id="deleteCharacter">
<core-icon icon="delete"></core-icon>Delete
</paper-item>
<paper-item id="shareCharacter">
<core-icon icon="social:share"></core-icon>Share
</paper-item>
<paper-item id="characterSettings">
<core-icon icon="settings"></core-icon>Settings
</paper-item>
</core-menu>
</paper-dropdown>
</paper-menu-button>
{{/if}}
<div class="bottom fit" horizontal layout>
<paper-tabs flex horizontal center layout id="characterSheetTabs" selected={{selectedTab}} class="{{colorClass}}">
<paper-tab name="stats">Stats</paper-tab>
<paper-tab name="features">Features</paper-tab>
<paper-tab name="inventory">Inventory</paper-tab>
{{#unless hideSpellcasting}}
<paper-tab name="spells">Spells</paper-tab>
{{/unless}}
<paper-tab name="persona">Persona</paper-tab>
<paper-tab name="journal">Journal</paper-tab>
</paper-tabs>
@@ -33,7 +44,9 @@
<section flex name="stats">{{> stats}}</section>
<section flex name="features">{{> features}}</section>
<section flex name="inventory">{{> inventory}}</section>
{{#unless hideSpellcasting}}
<section flex name="spells">{{> spells}}</section>
{{/unless}}
<section flex name="persona">{{> persona}}</section>
<section flex name="journal">{{> journal}}</section>
</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");
};
//watch this character and make sure their encumbrance is updated
trackEncumbranceConditions(this.data._id, this);
});
var setTab = function(charId, tab){
return Session.set(charId + ".selectedTab", tab);
@@ -13,6 +16,10 @@ var getTab = function(charId){
Template.characterSheet.helpers({
selectedTab: function(){
return getTab(this._id);
},
hideSpellcasting: function() {
var char = Characters.findOne(this._id);
return char && char.settings.hideSpellcasting;
}
});
@@ -40,4 +47,11 @@ Template.characterSheet.events({
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 {
width: 120px;
html /deep/ .operationDropDown {
width: 152px;
}
body /deep/ #statDropDown {
width: 120px;
html /deep/ .statDropDown {
width: 152px;
}
body /deep/ #operationDropDown {
width: 100px;
}
body /deep/ #damageMultiplierDropDown {
width: 120px;
}
body /deep/ #proficiencyDropDown {
width: 120px;
html /deep/ .damageMultiplierDropDown {
width: 152px;
}
html /deep/ .effectEdit paper-input {
@@ -24,6 +16,7 @@ html /deep/ .effectEdit paper-input {
}
html /deep/ .effectEdit {
height: 64px;
display: flex;
align-items: flex-end;
}

View File

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

View File

@@ -93,24 +93,17 @@ var skillOperations = [
{name: "Conditional Benefit", operation: "conditional"}
];
Template.effectEdit.created = function(){
var statGroup = statsDict[this.data.stat] && statsDict[this.data.stat].group;
this.selectedStatGroup = new ReactiveVar(statGroup);
};
Template.effectEdit.helpers({
selectedStatGroup: function(){
return Template.instance().selectedStatGroup.get();
},
statGroups: function(){
return statGroupNames;
},
stats: function(){
var group = Template.instance().selectedStatGroup.get();
var group = this;
return statGroups[group];
},
operations: function(){
var group = Template.instance().selectedStatGroup.get();
var stat = statsDict[this.stat];
var group = stat && stat.group;
if (group === "Weakness/Resistance") return null;
if (group === "Saving Throws" || group === "Skills"){
return skillOperations;
@@ -120,7 +113,8 @@ Template.effectEdit.helpers({
},
effectValueTemplate: function(){
//resistance/vulnerability template
var group = Template.instance().selectedStatGroup.get();
var stat = statsDict[this.stat];
var group = stat && stat.group;
if (group === "Weakness/Resistance") return "multiplierEffectValue";
var op = this.operation;
@@ -144,25 +138,6 @@ Template.effectEdit.events({
Effects.softRemoveNode(this._id);
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){
var detail = event.originalEvent.detail;
if (!detail.isSelected) return;

View File

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

View File

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

View File

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

View File

@@ -65,7 +65,7 @@ Template.features.events({
}
});
},
"tap .featureCard .containerTop": function(event){
"tap .featureCard .top": function(event){
var featureId = this._id;
var charId = Template.parentData()._id;
GlobalUI.setDetail({
@@ -128,7 +128,7 @@ Template.resource.events({
Characters.update(this.char._id, modifier, {validate: false});
}
},
"tap .containerRight": function(event, instance) {
"tap .right": function(event, instance) {
GlobalUI.setDetail({
template: "attributeDialog",
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,65 @@
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 = char.attributeValue("strength");
var capacity = strength * 15;
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

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

View File

@@ -61,6 +61,18 @@ Template.inventory.helpers({
});
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(){
var value = 0;
Items.find(
@@ -103,7 +115,7 @@ Template.inventory.helpers({
});
Template.inventory.events({
"tap #addItem": function(event){
"tap .addItem": function(event){
var charId = this._id;
Items.insert({
charId: charId,
@@ -120,7 +132,7 @@ Template.inventory.events({
});
});
},
"tap #addContainer": function(event){
"tap .addContainer": function(event){
var containerId = Containers.insert({
name: "New Container",
isCarried: true,
@@ -136,6 +148,23 @@ Template.inventory.events({
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){
var itemId = this._id;
var charId = Template.parentData()._id;
@@ -145,7 +174,18 @@ Template.inventory.events({
heroId: itemId,
});
},
"tap .containerTop": function(event){
"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({
template: "containerDialog",
data: {containerId: this._id, charId: this.charId},
@@ -167,6 +207,9 @@ Template.inventoryItem.helpers({
ne1: function(num){
return num !== 1;
},
lt1: function(num) {
return num < 1;
},
hidden: function(){
return Session.equals("inventory.dragItemId", this._id) ? "hidden" : null;
},
@@ -174,71 +217,93 @@ Template.inventoryItem.helpers({
Template.layout.events({
"dragstart .inventoryItem": function(event, instance){
event.originalEvent.dataTransfer.setData("dicecloud-id/items", this._id);
Session.set("inventory.dragItemId", this._id);
Session.set("inventory.dragItemOriginalContainer", this.container);
Session.set("inventory.dragItemOriginalCharacter", this.charId);
},
"dragover .itemContainer, dragenter .itemContainer":
function(event, instance){
if (_.contains(event.originalEvent.dataTransfer.types, "dicecloud-id/items")){
event.preventDefault();
}
},
"dragover .equipmentContainer, dragenter .equipmentContainer":
function(event, instance){
if (_.contains(event.originalEvent.dataTransfer.types, "dicecloud-id/items")){
event.preventDefault();
}
},
"dragover .carriedContainer, dragenter .carriedContainer":
function(event, instance){
if (_.contains(event.originalEvent.dataTransfer.types, "dicecloud-id/items")){
event.preventDefault();
}
},
"dragover .characterRepresentative, dragenter .characterRepresentative":
function(event, instance){
if (_.contains(event.originalEvent.dataTransfer.types, "dicecloud-id/items")){
event.preventDefault();
}
},
"dragend .inventoryItem": function(event, instance){
resetInvetorySession(); //this is a valid drop zone
Session.set("inventory.dragItemId", null);
},
"dragover .itemContainer": function(event, instance){
event.preventDefault();
},
"dragover .equipmentContainer": function(event, instance){
event.preventDefault();
},
"dragover .carriedContainer": function(event, instance){
event.preventDefault();
},
"drop .itemContainer": function(event, instacne){
var item = Items.findOne(Session.get("inventory.dragItemId"));
"drop .itemContainer": function(event, instance){
var itemId = event.originalEvent.dataTransfer.getData("dicecloud-id/items");
if (event.ctrlKey){
//split the stack to the container
GlobalUI.showDialog({
template: "splitStackDialog",
data: {
id: item._id,
id: itemId,
parentCollection: "Containers",
parentId: this._id,
},
});
} else {
//move item to the container
item.moveToContainer(this._id);
Meteor.call("moveItemToContainer", itemId, this._id);
}
resetInvetorySession();
Session.set("inventory.dragItemId", null);
},
"drop .equipmentContainer": function(event, instance){
var charId = Session.get("inventory.dragItemOriginalCharacter");
var item = Items.findOne(Session.get("inventory.dragItemId"));
item.equip(charId);
resetInvetorySession();
var itemId = event.originalEvent.dataTransfer.getData("dicecloud-id/items");
Meteor.call("equipItem", itemId, this._id);
Session.set("inventory.dragItemId", null);
},
"drop .carriedContainer": function(event, instance){
var charId = Session.get("inventory.dragItemOriginalCharacter");
var item = Items.findOne(Session.get("inventory.dragItemId"));
var itemId = event.originalEvent.dataTransfer.getData("dicecloud-id/items");
if (event.ctrlKey){
//split the stack to the container
GlobalUI.showDialog({
template: "splitStackDialog",
data: {
id: item._id,
id: itemId,
parentCollection: "Characters",
parentId: this._id,
},
});
} else {
//move item to the character
item.moveToCharacter(this._id);
Meteor.call("moveItemToCharacter", itemId, this._id);
}
resetInvetorySession();
Session.set("inventory.dragItemId", null);
},
"drop .characterRepresentative": function(event, instance) {
var itemId = event.originalEvent.dataTransfer.getData("dicecloud-id/items");
if (event.ctrlKey){
//split the stack to the container
GlobalUI.showDialog({
template: "splitStackDialog",
data: {
id: itemId,
parentCollection: "Characters",
parentId: this._id,
},
});
} else {
//move item to the character
Meteor.call("moveItemToCharacter", itemId, this._id);
}
Session.set("inventory.dragItemId", null);
},
});
var resetInvetorySession = function(){
_.defer(function(){
Session.set("inventory.dragItemId", null);
Session.set("inventory.dragItemOriginalContainer", null);
Session.set("inventory.dragItemOriginalCharacter", null);
});
};

View File

@@ -10,7 +10,7 @@
<template name="itemDetails">
<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}}
</div>
<div layout horizontal wrap class="caption">
@@ -19,7 +19,7 @@
</div>
{{#if description}}
<hr class="vertMargin">
<div class="prewrap">{{description}}</div>
<div class="pre-wrap">{{evaluateString charId description}}</div>
{{/if}}
{{> effectsViewList charId=charId parentId=_id}}
{{> attacksViewList charId=charId parentId=_id}}
@@ -28,10 +28,25 @@
<template name="itemEdit">
<paper-input class="fullwidth" id="itemNameInput" label="Name" floatinglabel value={{name}}></paper-input>
<div layout horizontal wrap>
<paper-input-decorator label="Quantity" floatinglabel>
<input id="quantityInput" type="number" value={{quantity}}>
<paper-input-decorator label="Quantity"
floatinglabel
style="width: 80px">
<input id="quantityInput"
type="number"
value={{quantity}}>
</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>
<hr class="vertMargin">
@@ -53,7 +68,9 @@
</div>
<div center horizontal layout>
<div class="padded">Requires Attunement</div>
<paper-checkbox id="attunementCheckbox" checked={{requiresAttunement}}></paper-checkbox>
<paper-checkbox id="attunementCheckbox"
checked={{requiresAttunement}}>
</paper-checkbox>
</div>
</div>

View File

@@ -50,7 +50,7 @@ Template.itemEdit.onRendered(function(){
Template.itemEdit.helpers({
ne1: function(num){
return num != 1;
}
},
});
Template.itemEdit.events({
@@ -81,15 +81,16 @@ Template.itemEdit.events({
},
"change #equippedInput": function(event){
var equipped = Template.instance().find("#equippedInput").checked;
var item = Items.findOne(this._id);
if (item){
if (equipped){
item.equip();
} else {
item.unequip();
}
if (equipped){
Meteor.call("equipItem", this._id, this.charId);
} else {
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){
var value = event.currentTarget.checked;
Items.update(this._id, {$set: {requiresAttunement: value}});
@@ -107,7 +108,6 @@ Template.containerDropdown.events({
var detail = event.originalEvent.detail;
if (!detail.isSelected) return;
var containerId = detail.item.getAttribute("name");
var item = Items.findOne(Template.currentData()._id);
item.moveToContainer(containerId);
Meteor.call("moveItemToContainer", Template.currentData()._id, containerId);
}
});

View File

@@ -7,13 +7,12 @@ Template.splitStackDialog.helpers({
Template.splitStackDialog.events({
"tap #moveButton": function(event, instance){
var item = Items.findOne(this.id);
if (item){
item.splitToParent(
{collection: this.parentCollection , id: this.parentId},
+instance.find("#quantityInput").value
);
}
Meteor.call(
"splitItemToParent",
this.id,
+instance.find("#quantityInput").value,
{collection: this.parentCollection , id: this.parentId}
);
},
"tap #oneButton":function(event, instance){
instance.find("#quantityInput").value = 1;

View File

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

View File

@@ -1,12 +1,12 @@
<template name="experienceDialog">
{{#with experience}}
{{#baseDialog title=name class=colorClass hideColor="true" startEditing=../startEditing}}
<div horizontal layout center-justified>
{{#baseDialog title=name class=color hideColor="true" startEditing=../startEditing}}
<div horizontal layout center-justified class= "display2">
{{value}}
</div>
{{#if description}}
<hr class="vertMargin">
<div class="prewrap">{{description}}</div>
<div class="pre-wrap">{{description}}</div>
{{/if}}
{{else}}
<div horizontal layout>

View File

@@ -1,6 +1,11 @@
Template.experienceDialog.helpers({
feature: function(){
return Features.findOne(this.featureId);
experience: function(){
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);
},
});
@@ -27,10 +32,3 @@ Template.experienceDialog.events({
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,98 @@
<template name="journal">
<div fit>
<div id="journal" class="scroll-y" fit>
<div class="containers">
<div class="column-container">
<!--Experience Table-->
<paper-shadow class="card container experiencesCard" hero-id="main" {{detailHero}}>
<div class="whiteTop" hero-id="toolbar" layout horizontal center {{detailHero}}>
<div class="containerName subhead" flex>Experience</div>
<div class="subhead">{{experience}} XP</div>
<paper-shadow class="card experiencesCard"
hero-id="main" {{detailHero}}>
<div class="top white subhead"
hero-id="toolbar" {{detailHero}}
layout horizontal center>
<div flex>Experience</div>
<div >{{experience}} XP</div>
<paper-icon-button class="black54" id="addXP" icon="add"></paper-icon-button>
</div>
<div class="containerMain experiences">
<div class="bottom list">
{{#each experiences}}
<div class="itemSlot">
<paper-item class="inventoryItem experience" hero-id="main" {{detailHero}} layout horizontal>
<div flex>{{name}}</div><div class="xpValue">{{value}}</div>
</paper-item>
<div class="item-slot">
<div class="item experience"
hero-id="main" {{detailHero}}
layout horizontal center>
<div flex>{{name}}</div>
<div class="xpValue">{{value}}</div>
</div>
</div>
{{/each}}
</div>
{{#if moreExperiencesOrCollapse}}
<div class="containerFoot" layout="" horizontal="" center="" end-justified="">
<paper-button id="moreExperiences" disabled={{notMoreExperiences}}>Load More</paper-button>
<paper-button id="lessExperiences" disabled={{cantCollapse}}>Collapse</paper-button>
<div layout horizontal center end-justified>
<paper-button id="moreExperiences"
disabled={{notMoreExperiences}}>
Load More
</paper-button>
<paper-button id="lessExperiences"
disabled={{cantCollapse}}>
Collapse
</paper-button>
</div>
{{/if}}
</paper-shadow>
<!--Class Table-->
<paper-shadow class="card container" hero-id="main" {{detailHero}}>
<div class="whiteTop" hero-id="toolbar" layout horizontal center {{detailHero}}>
<paper-shadow class="card"
hero-id="main" {{detailHero}}>
<div class="white top"
hero-id="toolbar" {{detailHero}}
layout horizontal center>
<div flex>
<div class="containerName subhead">Level {{level}}</div>
<div class="containerName subhead">
Level {{level}}
</div>
{{#if nextLevelXP}}
<div class="caption">
Next Level: {{nextLevelXP}}XP
</div>
{{/if}}
</div>
<paper-icon-button class="black54" id="addClassButton" icon="add"></paper-icon-button>
<paper-icon-button class="black54"
id="addClassButton"
icon="add">
</paper-icon-button>
</div>
<div class="containerMain experiences">
<div class="itemSlot">
<paper-item class="inventoryItem race" hero-id="main" {{detailHero "race" _id}} layout horizontal>
<div class="bottom list">
<div class="item-slot">
<div class="item race"
hero-id="main" {{detailHero "race" _id}}
layout horizontal center>
{{race}}
</paper-item>
</div>
</div>
{{#each classes}}
<div class="itemSlot">
<paper-item class="inventoryItem class" hero-id="main" {{detailHero}} layout horizontal>
<div class="item-slot">
<div class="item class"
hero-id="main" {{detailHero}}
layout horizontal center>
{{name}}&nbsp;{{level}}
</paper-item>
</div>
</div>
{{/each}}
</div>
</paper-shadow>
<!--Notes-->
{{#each notes}}
<paper-shadow class="card container" hero-id="main" {{detailHero}}>
<div class="containerTop {{colorClass}} noteTop" hero-id="toolbar" layout horizontal center {{detailHero}}>
<div flex>
<div class="containerName subhead">{{name}}</div>
</div>
<paper-shadow class="card" hero-id="main" {{detailHero}}>
<div class="top {{colorClass}} noteTop subhead"
hero-id="toolbar" {{detailHero}}
layout horizontal center>
{{name}}
</div>
<div class="containerMain preline">{{description}}</div>
<div class="bottom text">{{description}}</div>
</paper-shadow>
{{/each}}
</div>
<div class="fab-buffer"></div>
</div>
</div>
{{#if canEditCharacter _id}}
<paper-fab id="addNote"
class="floatyButton"
icon="add"
@@ -75,4 +100,5 @@
role="button"
tabindex="0"
hero-id="main"></paper-fab>
{{/if}}
</template>

View File

@@ -1,18 +1,30 @@
<template name="noteDialog">
{{#with note}}
{{#baseDialog title=name class=colorClass startEditing=../startEditing}}
<div class="prewrap">{{description}}</div>
<div class="pre-wrap">{{description}}</div>
{{else}}
<!--Name-->
<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>
{{> noteDialogEdit}}
{{/baseDialog}}
{{/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>

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
<template name="persona">
<div fit>
<div id="persona" class="scroll-y" fit>
<div class="containers">
<div class="column-container">
{{#with characterDetails}}
{{#containerCardHelper this}}{{alignment}} {{gender}} {{race}}{{/containerCardHelper}}
{{/with}}
@@ -11,11 +11,11 @@
{{> containerCard characterField "bonds" "Bonds"}}
{{> containerCard characterField "flaws" "Flaws"}}
{{> containerCard characterField "backstory" "Background"}}
<paper-shadow class="card container">
<div class="containerTop whiteTop" layout horizontal center>
<div class="containerName subhead" flex>Languages</div>
<paper-shadow class="card">
<div class="white top subhead">
Languages
</div>
<div flex class="containerMain listPadded">
<div class="bottom list">
{{#each languages}}
{{> proficiencyListItem}}
{{/each}}
@@ -27,14 +27,19 @@
</template>
<template name="containerCard">
{{#containerCardHelper this}}{{body}}{{/containerCardHelper}}
{{#containerCardHelper this}}{{evaluateString _id body}}{{/containerCardHelper}}
</template>
<template name="containerCardHelper">
<paper-shadow class="card container {{class}}" hero-id="main" {{detailHero field ../_id}}>
<div class="containerTop {{colorClass}} {{topClass}}" hero-id="toolbar" layout horizontal center {{detailHero field ../_id}}>
<div class="containerName subhead" hero-id="title" flex {{detailHero field ../_id}}>{{title}}</div>
<paper-shadow class="card {{class}}"
hero-id="main" {{detailHero field ../_id}}>
<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 flex class="containerMain prewrap">{{> UI.contentBlock}}</div>
<div class="bottom text">{{> UI.contentBlock}}</div>
</paper-shadow>
</template>

View File

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

View File

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

View File

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

View File

@@ -26,7 +26,7 @@
<span class="body2">Duration: </span><span>{{duration}}</span>
</div>
</div>
<div class="prewrap">{{description}}</div>
<div class="pre-wrap">{{evaluateString charId description}}</div>
</template>
<template name="spellEdit">

View File

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

View File

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

View File

@@ -170,7 +170,7 @@ Template.spells.events({
heroId: charId + stat,
});
},
"tap .containerTop": function(event){
"tap .spellList .top": function(event){
GlobalUI.setDetail({
template: "spellListDialog",
data: {spellListId: this._id, charId: this.charId},
@@ -184,7 +184,7 @@ Template.spells.events({
heroId: this._id,
});
},
"tap #addSpellList": function(event){
"tap .addSpellList": function(event){
var charId = this.charId;
SpellLists.insert({
name: "New SpellList",
@@ -201,7 +201,7 @@ Template.spells.events({
}
});
},
"tap #addSpell": function(event){
"tap .addSpell": function(event){
var charId = this.charId;
var listId = SpellLists.findOne({charId: this._id})._id;
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">
<paper-shadow class="card double abilityMiniCard"
hero-id="main" {{detailHero ability ../_id}}>
<div class="abilityScore white-text {{color}}"
<paper-shadow class="card abilityMiniCard clickable"
hero-id="main" {{detailHero ability ../_id}}
layout horizontal>
<div class="left white-text {{color}}"
hero-id="toolbar" {{detailHero ability ../_id}}>
<h1 class="display1">{{../attributeValue ability}}</h1>
<h2>{{../abilityMod ability}}</h2>
<div class="display1">{{../attributeValue ability}}</div>
<div class="title">{{../abilityMod ability}}</div>
</div>
<div class="abilityCardRight subhead" layout horizontal center>
<div class="right subhead" layout horizontal center>
{{title}}
</div>
<paper-ripple fit></paper-ripple>
</paper-shadow>
</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

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

View File

@@ -93,6 +93,14 @@ var abilities = {
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({
or: function(a, b, c){
return a || b || c;
@@ -106,27 +114,27 @@ Template.attributeDialogView.helpers({
},
baseEffects: function(){
return Effects.find(
{charId: this.charId, stat: this.statName, operation: "base"}
{charId: this.charId, stat: this.statName, operation: "base", enabled: true}
);
},
addEffects: function(){
return Effects.find(
{charId: this.charId, stat: this.statName, operation: "add"}
{charId: this.charId, stat: this.statName, operation: "add", enabled: true}
);
},
mulEffects: function(){
return Effects.find(
{charId: this.charId, stat: this.statName, operation: "mul"}
{charId: this.charId, stat: this.statName, operation: "mul", enabled: true}
);
},
minEffects: function(){
return Effects.find(
{charId: this.charId, stat: this.statName, operation: "min"}
{charId: this.charId, stat: this.statName, operation: "min", enabled: true}
);
},
maxEffects: function(){
return Effects.find(
{charId: this.charId, stat: this.statName, operation: "max"}
{charId: this.charId, stat: this.statName, operation: "max", enabled: true}
);
},
attributeBase: function(){
@@ -140,7 +148,12 @@ Template.attributeDialogView.helpers({
return char.attributeValue(this.statName);
},
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;
},
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"}}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"}}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"}}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"}}lbs</td>
<td class="caption"></td>
</tr>
</table>
</template>

View File

@@ -2,19 +2,21 @@
<paper-shadow class="card container healthCard"
hero-id="main" {{detailHero "hitPoints" _id}}
layout horizontal wrap>
<div class="green white-text subhead padded leftRound"
<div class="green white-text subhead left"
hero-id="toolbar" {{detailHero "hitPoints" _id}}
layout vertical center>
layout vertical center center-justified>
<div class="hitPointTitle clickable">Hit Points</div>
<paper-icon-button class="white54" id="addTempHP" icon="add"></paper-icon-button>
</div>
<div class="padded" flex layout vertical center-justified style="min-width: 180px;">
<paper-slider id="hitPointSlider"
value={{attributeValue "hitPoints"}}
max={{attributeBase "hitPoints"}}
editable pin
role="slider"
></paper-slider>
<div class="right" flex layout vertical center-justified style="min-width: 180px;">
<div layout horizontal>
<paper-slider id="hitPointSlider"
value={{attributeValue "hitPoints"}}
max={{attributeBase "hitPoints"}}
editable pin
role="slider"
></paper-slider>
</div>
{{#each tempHitPoints}}
<div>
{{name}}

View File

@@ -1,10 +1,20 @@
<template name="hitDice">
{{#if ../attributeBase name}}
<paper-shadow class="card container" hero-id="main" {{detailHero name ../_id}} layout horizontal>
<div class="containerLeft green" layout horizontal hero-id="toolbar" {{detailHero name ../_id}}>
<div class="resourceButtons">
<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>
<paper-shadow class="card hit-dice" hero-id="main"
{{detailHero name ../_id}}
layout horizontal>
<div class="left green display1 white-text"
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 class="resourceValue" layout vertical center>
<div>
@@ -15,9 +25,8 @@
</div>
</div>
</div>
<div class="containerRight clickable" flex relative horizontal layout center>
<div class="right clickable" flex relative horizontal layout center>
Hit Dice
<paper-ripple fit></paper-ripple>
</div>
</paper-shadow>
{{/if}}

View File

@@ -25,12 +25,17 @@ Template.hitDice.events({
Characters.update(this.char._id, modifier, {validate: false});
}
},
"tap .containerRight": function() {
"tap .right": function() {
var charId = Template.parentData()._id;
var title = "d" + this.diceNum + " Hit Dice";
GlobalUI.setDetail({
template: "attributeDialog",
data: {name: title, statName: this.name, charId: charId},
data: {
name: title,
statName: this.name,
charId: charId,
color: "green",
},
heroId: charId + this.name,
});
},

View File

@@ -1,6 +1,6 @@
<!-- needs name, char, and skillName -->
<template name="skillDialog">
{{#baseDialog title=name class=colorClass hideEdit=true}}
{{#baseDialog title=name class=color hideEdit=true}}
{{> skillDialogView}}
{{/baseDialog}}
</template>

View File

@@ -93,6 +93,14 @@ var abilities = {
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({
or: function(a, b, c){
return a || b || c;
@@ -132,6 +140,7 @@ Template.skillDialogView.helpers({
charId: this.charId,
stat: this.skillName,
operation: "add",
enabled: true,
});
},
mulEffects: function(){
@@ -139,6 +148,7 @@ Template.skillDialogView.helpers({
charId: this.charId,
stat: this.skillName,
operation: "mul",
enabled: true,
});
},
minEffects: function(){
@@ -146,6 +156,7 @@ Template.skillDialogView.helpers({
charId: this.charId,
stat: this.skillName,
operation: "min",
enabled: true,
});
},
maxEffects: function(){
@@ -153,6 +164,7 @@ Template.skillDialogView.helpers({
charId: this.charId,
stat: this.skillName,
operation: "max",
enabled: true,
});
},
advEffects: function(){
@@ -160,6 +172,7 @@ Template.skillDialogView.helpers({
charId: this.charId,
stat: this.skillName,
operation: "advantage",
enabled: true,
});
},
dadvEffects: function(){
@@ -167,6 +180,7 @@ Template.skillDialogView.helpers({
charId: this.charId,
stat: this.skillName,
operation: "disadvantage",
enabled: true,
});
},
conditionalEffects: function(){
@@ -174,6 +188,7 @@ Template.skillDialogView.helpers({
charId: this.charId,
stat: this.skillName,
operation: "conditional",
enabled: true,
});
},
ability: function(){

View File

@@ -1,40 +0,0 @@
.skillRow {
height: 32px;
margin: 0 -16px 0 -16px;
background: white;
}
.skillRow core-icon {
color: rgba(0,0,0,0.54);
}
.skillMod {
width: 42px;
text-align: center;
}
.skillName, .skillMod{
}
.fail.skillMod {
color: #D50000;
}
.advantage{
background-image: url(/png/advantage/greenUp.png);
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.disadvantage{
background-image: url(/png/advantage/redDown.png);
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.hasConditionals::after{
content: "*";
}

View File

@@ -1,16 +1,24 @@
<template name="skillRow">
<paper-item class="skillRow" layout horizontal hero-id="main" {{detailHero skill ../_id}}>
<core-icon icon="{{profIcon}}"></core-icon>
{{#if failSkill}}
<div class="fail skillMod">fail</div>
{{else}}
<div class="{{advantage}} skillMod">{{../skillMod skill}}</div>
{{/if}}
<div class="{{#if conditionalCount}}hasConditionals{{/if}} skillName">
{{name}}
{{#if showPassive}}
({{../passiveSkill skill}})
<div class="item-slot">
<div class="skill-row item small"
hero-id="main"
{{detailHero skill ../_id}}
layout horizontal center>
<core-icon icon="{{profIcon}}"></core-icon>
{{#if failSkill}}
<div class="fail skill-mod">fail</div>
{{else}}
<div class="{{advantage}} skill-mod">{{../skillMod skill}}</div>
{{/if}}
<div flex>
{{name}}
{{#if conditionalCount}}
*
{{/if}}
{{#if showPassive}}
({{../passiveSkill skill}})
{{/if}}
</div>
</div>
</paper-item>
</div>
</template>

View File

@@ -0,0 +1,21 @@
.skill-row {
.skill-mod{
width: 45px;
text-align: center;
&.fail {
color: #D50000;
}
&.advantage{
background-image: url(/png/advantage/greenUp.png);
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
&.disadvantage{
background-image: url(/png/advantage/redDown.png);
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
}
}

View File

@@ -1,9 +1,9 @@
<template name="stats">
<div class="scroll-y" fit>
<div class="resourceCards" layout horizontal wrap>
<div style="padding: 8px 8px 0 8px">
{{> healthCard}}
</div>
<div id="stats" class="containers" >
<div class="column-container thin-columns" >
<!--Ability Scores-->
{{> abilityMiniCard ability="strength" title="Strength" color="red"}}
{{> abilityMiniCard ability="dexterity" title="Dexterity" color="indigo"}}
@@ -27,11 +27,11 @@
{{>hitDice name="d10HitDice" diceNum="10" char=this}}
{{>hitDice name="d12HitDice" diceNum="12" char=this}}
<!--Saving Throws-->
<paper-shadow class="card container" hero-id="main" {{detailHero}}>
<div class="containerTop whiteTop" layout horizontal center>
<div class="containerName subhead" hero-id="title" flex>Saving Throws</div>
<paper-shadow class="card">
<div class="top white subhead">
Saving Throws
</div>
<div flex class="containerMain">
<div flex class="bottom list">
{{> skillRow name="Strength" skill="strengthSave"}}
{{> skillRow name="Dexterity" skill="dexteritySave"}}
{{> skillRow name="Constitution" skill="constitutionSave"}}
@@ -41,11 +41,11 @@
</div>
</paper-shadow>
<!--Skills-->
<paper-shadow class="card container" hero-id="main" {{detailHero}}>
<div class="containerTop whiteTop" layout horizontal center>
<div class="containerName subhead" hero-id="title" flex>Skills</div>
<paper-shadow class="card">
<div class="top white subhead">
Skills
</div>
<div flex class="containerMain">
<div flex class="bottom list">
{{> skillRow name="Acrobatics" skill="acrobatics"}}
{{> skillRow name="Animal Handling" skill="animalHandling"}}
{{> skillRow name="Arcana" skill="arcana"}}
@@ -71,8 +71,8 @@
</template>
<template name="statCard">
<paper-shadow class="card container statCard" hero-id="main" {{detailHero stat ../_id}} layout horizontal>
<div class="containerLeft {{color}}"
<paper-shadow class="card statCard clickable" hero-id="main" {{detailHero stat ../_id}} layout horizontal>
<div class="left display1 white-text {{color}}"
hero-id="toolbar" {{detailHero stat ../_id}}>
{{#if isSkill}}
{{../skillMod stat}}
@@ -80,9 +80,8 @@
{{prefix}}{{../attributeValue stat}}
{{/if}}
</div>
<div class="containerRight" flex horizontal layout center>
<div class="right subhead" flex horizontal layout center>
{{name}}
</div>
<paper-ripple fit></paper-ripple>
</paper-shadow>
</template>

View File

@@ -4,38 +4,64 @@ Template.stats.events({
if (this.isSkill){
GlobalUI.setDetail({
template: "skillDialog",
data: {name: this.name, skillName: this.stat, charId: charId},
data: {
name: this.name,
skillName: this.stat,
charId: charId,
color: this.color,
},
heroId: charId + this.stat,
});
} else {
GlobalUI.setDetail({
template: "attributeDialog",
data: {name: this.name, statName: this.stat, charId: charId},
data: {
name: this.name,
statName: this.stat,
charId: charId,
color: this.color,
},
heroId: charId + this.stat,
});
}
},
"tap .abilityMiniCard": function(event, instance){
var charId = Template.parentData()._id;
var template = "attributeDialog";
if (this.ability === "strength") template = "strengthDialog";
GlobalUI.setDetail({
template: "attributeDialog",
data: {name: this.title, statName: this.ability, charId: charId},
template: template,
data: {
name: this.title,
statName: this.ability,
charId: charId,
color: this.color,
},
heroId: charId + this.ability,
});
},
"tap .skillRow": function(event, instance){
"tap .skill-row": function(event, instance){
var skill = this.skill;
var charId = instance.data._id;
GlobalUI.setDetail({
template: "skillDialog",
data: {name: this.name, skillName: skill, charId: charId},
heroId: charId + skill,
});
template: "skillDialog",
data: {
name: this.name,
skillName: skill,
charId: charId,
},
heroId: charId + skill,
});
},
"tap .hitPointTitle": function(event, instance) {
GlobalUI.setDetail({
template: "attributeDialog",
data: {name: "Hit Points", statName: "hitPoints", charId: this._id},
data: {
name: "Hit Points",
statName: "hitPoints",
charId: this._id,
color: "green",
},
heroId: this._id + "hitPoints",
});
},

View File

@@ -1,5 +1,5 @@
<template name="characterList">
<core-toolbar class="blue-grey white-text">
<core-toolbar class="app-grey white-text">
<core-icon-button icon="menu" core-drawer-toggle></core-icon-button>
<div flex>
Characters

View File

@@ -2,10 +2,21 @@
color: black;
color: rgba(0, 0, 0, 0.870588);
cursor: pointer;
font-size: 16px;
height: 40px;
overflow: hidden;
padding: 12px 0 12px 16px;
padding: 8px 0 8px 16px;
text-overflow: ellipsis;
white-space: nowrap;
}
.singleLineItem core-icon {
height: 8px;
margin-right: 8px;
width: 8px;
}
.singleLineItem div {
text-overflow: ellipsis;
/* Required for text-overflow to do anything */
white-space: nowrap;
overflow: hidden;
}

View File

@@ -3,7 +3,11 @@
{{#if characters.count}}
<div>
{{#each characters}}
<div class="singleLineItem">{{name}}</div>
<div class="singleLineItem characterRepresentative"
layout horizontal center>
<core-icon icon="image:brightness-1"></core-icon>
<div>{{name}}</div>
</div>
{{/each}}
</div>
{{/if}}

View File

@@ -21,8 +21,10 @@ Template.characterSideList.helpers({
Template.characterSideList.events({
"tap .singleLineItem": function(event, instance) {
Router.go("characterSheet", {_id: this._id});
$("core-drawer-panel").get(0).closeDrawer();
},
"tap core-item": function() {
Router.go("characterList");
$("core-drawer-panel").get(0).closeDrawer();
},
});

View File

@@ -0,0 +1,37 @@
<template name="feedback">
<div class="feedback" style="min-width: 300px; min-height: 370px">
<div>
<paper-input id="feedbackTitle" label="Title" floatinglabel></paper-input>
</div>
<div>
<paper-dropdown-menu class="typeDropdown" label="Operation" flex>
<paper-dropdown layered class="dropdown">
<core-menu class="menu typeMenu" selected="general">
<paper-item name="general">General Feedback</paper-item>
<paper-item name="bug">Bug</paper-item>
<paper-item name="change">Suggested Change</paper-item>
<paper-item name="feature">Feature Request</paper-item>
</core-menu>
</paper-dropdown>
</paper-dropdown-menu>
</div>
<div layout horizontal center>
<div>Importance</div>
<paper-slider id="severity" max=10 min=1 value=5 snap></paper-slider>
</div>
<paper-input-decorator label="Description" floatinglabel layout vertical>
<paper-autogrow-textarea rows=10 maxRows=10>
<textarea id="feedbackDescription"></textarea>
</paper-autogrow-textarea>
</paper-input-decorator>
</div>
<paper-button id="cancelButton"
affirmative>
Cancel
</paper-button>
<paper-button id="sendButton"
affirmative
disabled={{invalid}}>
Send
</paper-button>
</template>

View File

@@ -0,0 +1,33 @@
Template.feedback.onCreated(function() {
this.title = new ReactiveVar("");
this.description = new ReactiveVar("");
});
Template.feedback.helpers({
invalid: function() {
var inst = Template.instance();
return !inst.title.get() ||
!inst.description.get();
}
});
Template.feedback.events({
"input #feedbackTitle": function(event, instance) {
instance.title.set(instance.find("#feedbackTitle").value);
},
"input #feedbackDescription": function(event, instance) {
instance.description.set(instance.find("#feedbackDescription").value);
},
"tap #sendButton": function(event, instance) {
var report = {};
report.title = instance.find("#feedbackTitle").value;
report.severity = instance.find("#severity").value;
report.type = instance.find(".typeMenu").selected;
report.description = instance.find("#feedbackDescription").value;
report.metaData = {
url: window.location.href,
session: _.pairs(Session.keys),
};
Meteor.call("insertReport", report);
}
});

View File

@@ -1,92 +1,94 @@
<template name="guide">
<div class="wallOfText">
<h1>Dicecloud Beta</h1>
<p>Welcome to the Dicecloud beta. Please don't share the link with people you don't actively play with, since the beta is intended to be small, and your experience will probably get laggy if it gets more traffic than I'm expecting.</p>
<p>The beta is going to start with just the character sheet. You can play D&amp;D without minis and maps, without a pre-written adventure, you can play without a lot of things, but the character sheet is necessary. So I'm starting here and working my way outwards.</p>
<p>I will eventually have public bug tracking and feature requests going, but for now I'm going to track comments, feedback and suggestions on <a href="http://reddit.com/r/dicecloud">this subreddit</a>. If you've never used reddit before, all you need is a username and password to sign up. So it should be pretty accessible.</p>
<h2>Character Sheet Philosophy</h2>
<p>Setting up your character on Dicecloud is going to take you a little longer than just filling it in on a paper character sheet would have. The goal of using an online sheet is to make actually playing the game more streamlined, and ultimately more fun. So putting a little extra effort into setting up your character now will pay off over and over again once you're playing.</p>
<p>The idea is to track where each number comes from, and allow you to easily make changes on the fly.</p>
<p>Lets look at a hypothetical example.</p>
<p>You need to swim through a sunken section of dungeon to fetch the quest's Thing.<br>You'll need to take off your magical Plate Armor of +1 Constitution to swim without sinking, of course. Taking it off will change your armor class, your speed and your constitution, which in turn changes your hitpoints and your constitution saving throw. Working out all those changes in the middle of a game will drag the game to a hault. <br> Fortunately you have a digital character sheet, so it's a matter of dragging your Plate Armor +1 Con from your "equipment" box to your "backpack" box and you're done. Your hitpoints change correctly, your saving throws are up to date, your armor class goes back to reflecting the fact that you have natural armor from being a dragonborn. Your character sheet keeps up and you ultimately get more time to play the game. Huzzah!</p>
<h2>Creating a Character</h2>
<ul>
<li>In the <a href={{pathFor route="characterList"}}>character list</a>, click the plus button, floating in the bottom right corner.</li>
<li>Give your character a name, gender and race, these can be changed later if you change your mind. Then click the Add button</li>
<li>Your new character should open, with most of its attributes and abilities at zero.</li>
</ul>
<core-toolbar class="app-grey white-text">
<core-icon-button icon="menu" core-drawer-toggle></core-icon-button>
<div flex>Guide</div>
</core-toolbar>
<div layout vertical center>
<paper-shadow class="wallOfText card" style="padding: 32px; max-width: 800px;">
<h2>Character Sheet Philosophy</h2>
<p>Setting up your character on Dicecloud is going to take you a little longer than just filling it in on a paper character sheet would have. The goal of using an online sheet is to make actually playing the game more streamlined, and ultimately more fun. So putting a little extra effort into setting up your character now will pay off over and over again once you're playing.</p>
<p>The idea is to track where each number comes from, and allow you to easily make changes on the fly.</p>
<p>Lets look at a hypothetical example.</p>
<p>You need to swim through a sunken section of dungeon to fetch the quest's Thing.<br>You'll need to take off your magical Plate Armor of +1 Constitution to swim without sinking, of course. Taking it off will change your armor class, your speed and your constitution, which in turn changes your hitpoints and your constitution saving throw. Working out all those changes in the middle of a game will drag the game to a hault. <br> Fortunately you have a digital character sheet, so it's a matter of dragging your Plate Armor +1 Con from your "equipment" box to your "backpack" box and you're done. Your hitpoints change correctly, your saving throws are up to date, your armor class goes back to reflecting the fact that you have natural armor from being a dragonborn. Your character sheet keeps up and you ultimately get more time to play the game. Huzzah!</p>
<h2>Creating a Character</h2>
<ul>
<li>In the <a href={{pathFor route="characterList"}}>character list</a>, click the plus button, floating in the bottom right corner.</li>
<li>Give your character a name, gender and race, these can be changed later if you change your mind. Then click the Add button</li>
<li>Your new character should open, with most of its attributes and abilities at zero.</li>
</ul>
<h2>Adding Racial Effects</h2>
<p>You have already given your character a race, but you haven't yet specified what that race does for your character, so lets do that.</p>
<ul>
<li>Click the Journal tab</li>
<li>In the card that displays your level, click on your race to open the racial dialog box</li>
<li>Click the edit button in the top corner of the racial dialog</li>
</ul>
<p>In the edit mode of the racial dialog you can change your race's name and add effects and proficiencies your race gives you. We will only be adding the base traits our race gives us, specific features can go in the features tab so we can more easily reference them later.</p>
<p>Lets add some of the effects all races will give.</p>
<ul>
<li>Click the Add Effect button, a new effect should appear</li>
<li>In the Stat Group dropdown box, choose "Stats"</li>
<li>The second dropdown lets us choose which stat to effect, choose "Speed"</li>
<li>The third dropdown lets us choose how to effect that stat, choose "Base Value", since our character's base speed comes from their race</li>
<li>Finally, input the value for our characters speed, it'll probably be 30 unless you chose a slower race, such as a dwarf</li>
<li>Close the Race dialog and navigate to the Stats tab</li>
<li>The speed card should now correctly display the character's speed</li>
<li>Click the speed card to see how that value was calculated</li>
<li>Currently there is only one number effecting the total, the speed from our race, but as more effects from different sources start impacting our character's speed, they will show up here.</li>
</ul>
<h2>Adding Racial Effects</h2>
<p>You have already given your character a race, but you haven't yet specified what that race does for your character, so lets do that.</p>
<ul>
<li>Click the Journal tab</li>
<li>In the card that displays your level, click on your race to open the racial dialog box</li>
<li>Click the edit button in the top corner of the racial dialog</li>
</ul>
<p>In the edit mode of the racial dialog you can change your race's name and add effects and proficiencies your race gives you. We will only be adding the base traits our race gives us, specific features can go in the features tab so we can more easily reference them later.</p>
<p>Lets add some of the effects all races will give.</p>
<ul>
<li>Click the Add Effect button, a new effect should appear</li>
<li>In the Stat Group dropdown box, choose "Stats"</li>
<li>The second dropdown lets us choose which stat to effect, choose "Speed"</li>
<li>The third dropdown lets us choose how to effect that stat, choose "Base Value", since our character's base speed comes from their race</li>
<li>Finally, input the value for our characters speed, it'll probably be 30 unless you chose a slower race, such as a dwarf</li>
<li>Close the Race dialog and navigate to the Stats tab</li>
<li>The speed card should now correctly display the character's speed</li>
<li>Click the speed card to see how that value was calculated</li>
<li>Currently there is only one number effecting the total, the speed from our race, but as more effects from different sources start impacting our character's speed, they will show up here.</li>
</ul>
<h2>Adding your ability scores</h2>
<p>Your character currently doesn't have any ability scores, so lets fix that. Whether you roll your abilities or point-buy them, lets add a feature to represent where they came from</p>
<ul>
<li>Navigate to the <emd>Features</emd> tab</li>
<li>Click the plus button in the bottom right to add a new feature</li>
<li>Give the Feature a name, like <em>Point Buy</em></li>
<li>Leave the feature as always enabled, don't limit its uses, and leave the description blank</li>
<li>Click the <em>Add Effect</em> button</li>
<li>For <em>Stat Group</em> choose <em>Ability Scores</em></li>
<li>For <em>Stat</em> choose <em>Strength</em></li>
<li>For the operation choose <em>Base Value</em></li>
<li>Input your character's rolled or point-bought strength, without the racial modfier</li>
<li>Repeat for the rest of your ability scores</li>
</ul>
<p>You can now check that your ability scores appear on your <em>Stats</em> page and that your skills that use them have their values calculated accordingly.</p>
<p>We didn't include your character's racial ability modifiers in the feature, so you should go back to your character's racial dialog and add them in there as effects. Remember to use the add operation, rather than base value, since your race adds to your ability scores.</p>
<p>By separating the source of your character's stats you can easily check how your character got their ability scores and stats, even after 20 levels, without getting confused or making mistakes.</p>
<h2>Adding your ability scores</h2>
<p>Your character currently doesn't have any ability scores, so lets fix that. Whether you roll your abilities or point-buy them, lets add a feature to represent where they came from</p>
<ul>
<li>Navigate to the <emd>Features</emd> tab</li>
<li>Click the plus button in the bottom right to add a new feature</li>
<li>Give the Feature a name, like <em>Point Buy</em></li>
<li>Leave the feature as always enabled, don't limit its uses, and leave the description blank</li>
<li>Click the <em>Add Effect</em> button</li>
<li>For <em>Stat Group</em> choose <em>Ability Scores</em></li>
<li>For <em>Stat</em> choose <em>Strength</em></li>
<li>For the operation choose <em>Base Value</em></li>
<li>Input your character's rolled or point-bought strength, without the racial modfier</li>
<li>Repeat for the rest of your ability scores</li>
</ul>
<p>You can now check that your ability scores appear on your <em>Stats</em> page and that your skills that use them have their values calculated accordingly.</p>
<p>We didn't include your character's racial ability modifiers in the feature, so you should go back to your character's racial dialog and add them in there as effects. Remember to use the add operation, rather than base value, since your race adds to your ability scores.</p>
<p>By separating the source of your character's stats you can easily check how your character got their ability scores and stats, even after 20 levels, without getting confused or making mistakes.</p>
<h2>Adding a Class</h2>
<p>Currently your character is at level 0, because they don't have any class levels. Let's fix that.</p>
<ul>
<li>Click the plus button in the card that currently says "Level 0"</li>
<li>A new class has now been added, name the class in the Class Name input and leave the level as 1</li>
</ul>
<p>We now have a class, lets add the saving throw proficiencies it gives us.</p>
<ul>
<li>Click the Add Proficiency button</li>
<li>Click the dropdown box that currently has "Skill" selected, and choose "Saving Throw" instead</li>
<li>In the second dropdown choose the first saving throw your class gives you</li>
<li>The third dropdown lets us specify if we have half or double our proficiency bonus for this proficiency, leave it at the default "proficient" for now</li>
</ul>
<p>If you navigate back to the stat page, you will see that you now have a proficiency bonus, based on your class level, and the saving throw you are proficienct in will take your proficiency bonus into account.</p>
<p>One of the most important things your class gives you is your hitpoints, so lets go add those now.</p>
<ul>
<li>Navigate to the class dialog box by clicking on your class name in the journal tab and hitting the edit button</li>
<li>Click the Add Effect button</li>
<li>Choose the <em>Stats</em> stat group, and choose the <em>Hitpoints</em> stat</li>
<li>Choose the <em>Base Value</em> operation</li>
</ul>
<p>Now we need to decide how many hitpoints our class gives us. We will assume that we take the constant hitpoints per level, since it's both the rule used for league play and it's statistically advantageous over rolling for hitpoints every level.</p>
<p>We could work out our hit points every level and change the effect each time, but we can do one better, we can input the calculation directly into the value field and have the character sheet figure it out for us</p>
<p>Let's assume we are rolling a fighter, so in the class name you typed in "Fighter" (with the capital F, but without the quote marks). A fighter gets 10 hp at first level and 6 hitpoints every level after that.</p>
<p>Lets rather split that into 4 bonus hitpoints at first level, and 6 hitpoints for every fighter level your character has. We can the write this as <em>4 + 6*FighterLevel</em> where the * represents multiplication.</p>
<p><em>Note, we don't add the constitution modifier here, that's already taken care of by default, since all characters add their constitution modifier to their hit points</em></p>
<ul>
<li>In the value field input <em>4 + 6 * FighterLevel</em>, the spaces aren't needed, but you must spell your class name exactly as it is spelt in the class name input box, capital letters and all, in our case "Fighter"</li>
<li>Create a new effect that effects the base value of <em>d10 Hit Dice</em> with the value of <em>FighterLevel</em>, since we also get our fighters level worth of hit dice</li>
<li>Check how your changes are reflected in the <em>Stats</em> tab</li>
<li>Change your level and check that the <em>Stats</em> tab gets updated accordingly</li>
</ul>
<p>You can try all sorts of calculations in your effects and in certain other places too. For example if you had some feature that is used a number of times equal to your wisdom modifier or 1, whichever is lower, you could limit its uses to <em>min(1, wisdomMod)</em> and the character sheet will figure it out for you, and update itself if you wisdom modifier happens to change later.</p>
<h2>Adding a Class</h2>
<p>Currently your character is at level 0, because they don't have any class levels. Let's fix that.</p>
<ul>
<li>Click the plus button in the card that currently says "Level 0"</li>
<li>A new class has now been added, name the class in the Class Name input and leave the level as 1</li>
</ul>
<p>We now have a class, lets add the saving throw proficiencies it gives us.</p>
<ul>
<li>Click the Add Proficiency button</li>
<li>Click the dropdown box that currently has "Skill" selected, and choose "Saving Throw" instead</li>
<li>In the second dropdown choose the first saving throw your class gives you</li>
<li>The third dropdown lets us specify if we have half or double our proficiency bonus for this proficiency, leave it at the default "proficient" for now</li>
</ul>
<p>If you navigate back to the stat page, you will see that you now have a proficiency bonus, based on your class level, and the saving throw you are proficienct in will take your proficiency bonus into account.</p>
<p>One of the most important things your class gives you is your hitpoints, so lets go add those now.</p>
<ul>
<li>Navigate to the class dialog box by clicking on your class name in the journal tab and hitting the edit button</li>
<li>Click the Add Effect button</li>
<li>Choose the <em>Stats</em> stat group, and choose the <em>Hitpoints</em> stat</li>
<li>Choose the <em>Base Value</em> operation</li>
</ul>
<p>Now we need to decide how many hitpoints our class gives us. We will assume that we take the constant hitpoints per level, since it's both the rule used for league play and it's statistically advantageous over rolling for hitpoints every level.</p>
<p>We could work out our hit points every level and change the effect each time, but we can do one better, we can input the calculation directly into the value field and have the character sheet figure it out for us</p>
<p>Let's assume we are rolling a fighter, so in the class name you typed in "Fighter" (with the capital F, but without the quote marks). A fighter gets 10 hp at first level and 6 hitpoints every level after that.</p>
<p>Lets rather split that into 4 bonus hitpoints at first level, and 6 hitpoints for every fighter level your character has. We can the write this as <em>4 + 6*FighterLevel</em> where the * represents multiplication.</p>
<p><em>Note, we don't add the constitution modifier here, that's already taken care of by default, since all characters add their constitution modifier to their hit points</em></p>
<ul>
<li>In the value field input <em>4 + 6 * FighterLevel</em>, the spaces aren't needed, but you must spell your class name exactly as it is spelt in the class name input box, capital letters and all, in our case "Fighter"</li>
<li>Create a new effect that effects the base value of <em>d10 Hit Dice</em> with the value of <em>FighterLevel</em>, since we also get our fighters level worth of hit dice</li>
<li>Check how your changes are reflected in the <em>Stats</em> tab</li>
<li>Change your level and check that the <em>Stats</em> tab gets updated accordingly</li>
</ul>
<p>You can try all sorts of calculations in your effects and in certain other places too. For example if you had some feature that is used a number of times equal to your wisdom modifier or 1, whichever is lower, you could limit its uses to <em>min(1, wisdomMod)</em> and the character sheet will figure it out for you, and update itself if you wisdom modifier happens to change later.</p>
</paper-shadow>
</div>
</template>

View File

@@ -1,11 +1,9 @@
<template name="home">
<core-toolbar class="blue-grey white-text">
<core-toolbar class="app-grey white-text">
<core-icon-button icon="menu" core-drawer-toggle></core-icon-button>
<div flex>
DiceCloud
Home
</div>
</core-toolbar>
<div class="padded scroll-y white" fit>
{{> guide}}
</div>
{{> intro}}
</template>

View File

@@ -1,25 +1,15 @@
Template.home.helpers({
characterDetails: function(){
var char = Characters.findOne(
this._id,
{fields: {name: 1, gender: 1, alignment: 1, race:1}}
);
char.title = char.name;
char.field = "base";
char.color = "d";
char.class = "characterCard";
return char;
}
selectedTab: function(){
return Session.get("homePage.selectedTab");
},
});
Template.home.events({
"tap .characterCard": function(event, instance){
Router.go("characterSheet", {_id: this._id});
"core-animated-pages-transition-end .tabPages": function(event) {
event.stopPropagation();
},
"tap #addCharacter": function(event, template) {
Characters.insert({owner: Meteor.userId()});
},
"tap #deleteChar": function(event, template){
Characters.remove(this._id);
"tap .homeTabs paper-tab": function(event, instance){
Session.set("homePage.selectedTab",
event.currentTarget.getAttribute("name"));
},
});

View File

@@ -0,0 +1,89 @@
<template name="intro">
<div class="intro">
<div class="section white-text" style="background: #282828">
<div class="display2">
Dice Cloud
</div>
<img style="width:130px; height:130px; background-color: #282828;"
src="/crown-dice-logo-cropped-transparent.png">
<div class="display1">
Unofficial Online Realtime D&amp;D 5e App
</div>
<h2>
Spend less time shuffling paper and more time playing the game
</h2>
{{#unless currentUser}}
<div layout horizontal around-justified wrap>
<paper-button class="red white-text signInButton"
style="margin: 16px;"
raised>
Sign In
</paper-button>
<paper-button class="red white-text signUpButton"
style="margin: 16px;"
raised>
Sign Up
</paper-button>
</div>
{{else}}
<div style="padding-bottom: 0;"></div>
{{/unless}}
</div>
<div class="section" style="background-color: #e9e9e9;">
<div>
<div class="display1">Character Sheet Open Beta</div>
<h2 style="margin-bottom: 16px;">
Check out the example characters
</h2>
<div layout horizontal around-justified wrap>
<paper-shadow class="card characterCard ssArcher clickable"
z="2">
<div class="top subhead green white-text">
<div class="subhead" flex>
Starter Set Archer
</div>
</div>
<div class="bottom text">Lawful Good Human</div>
</paper-shadow>
</div>
</div>
</div>
<div class="section white-text" style="background: #282828">
<div class="columns" layout horizontal around-justified wrap>
<div>
<h1>Check out the guide</h1>
<p>
Learn how your class gives you features, those features have effects,
and those effects determine your stats.
<paper-button class="guideButton">View Guide</paper-button>
</p>
</div>
<div>
<h1>
Discuss
</h1>
<p>
On the official subreddit
<paper-button class="redditButton">
<a href="http://www.reddit.com/r/dicecloud/">
/r/dicecloud
</a>
</paper-button>
</p>
</div>
<div>
<h1>
Get involved
</h1>
<p>Shape upcoming features and track bugs on the Dice Cloud Trello board
<paper-button class="trelloButton">
<a href="https://trello.com/b/94M0SCnq/dicecloud-roadmap">
Trello Roadmap
</a>
</paper-button>
</p>
</div>
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,14 @@
Template.intro.events({
"tap .signInButton": function() {
Router.go("/sign-in");
},
"tap .signUpButton": function() {
Router.go("/sign-up");
},
"tap .ssArcher": function() {
Router.go("/character/yBWwt5XQTTHZiRQxq");
},
"tap .guideButton": function() {
Router.go("/guide");
},
});

View File

@@ -0,0 +1,33 @@
.intro {
.section {
width: 100%;
min-height: 200px;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
padding-top: 24px;
padding-bottom: 24px;
& > div, & > h2 {
padding: 32px;
.display1 {
margin-bottom: 16px;
}
}
.columns > div{
max-width: 300px;
padding: 16px;
text-align: center;
paper-button {
color: #FF5252;
}
}
}
paper-button {
min-width: 200px;
a {
color: inherit;
text-decoration: inherit;
}
}
}

View File

@@ -8,6 +8,7 @@
<link rel="import" href="/components/core-icons/editor-icons.html">
<link rel="import" href="/components/core-icons/image-icons.html">
<link rel="import" href="/components/core-icons/social-icons.html">
<link rel="import" href="/components/core-image/core-image.html">
<link rel="import" href="/components/core-item/core-item.html">
<link rel="import" href="/components/core-menu/core-menu.html">
<link rel="import" href="/components/core-scaffold/core-scaffold.html">

View File

@@ -1,18 +1,22 @@
<template name="layout">
<core-drawer-panel>
<core-drawer-panel responsiveWidth="905px">
<core-header-panel drawer navigation flex mode="seamed" class="white">
<div id="accountSummary">
{{> loginButtons}}
{{#if currentUser}}
<div id="profileLink" style="text-decoration: underline; cursor: pointer;">
My account
<div id="profileLink"
style="text-decoration: underline; cursor: pointer; font-size: 16px;">
{{profileLink}}
</div>
{{else}}
<a href="/sign-in" style="color: white;">Sign in</a>
{{/if}}
</div>
<div id="navPanel">
<core-item id="homeNav" icon="home" label="Home"></core-item>
{{> characterSideList}}
<core-item id="guide" icon="social:school" label="Guide"></core-item>
<core-item id="feedback" icon="bug-report" label="Send Feedback"></core-item>
<core-item id="changeLog" icon="list" label="Change Log"></core-item>
</div>
</core-header-panel>
<core-animated-pages main
@@ -21,7 +25,7 @@
transitions="hero-transition cross-fade"
selected={{globalDetailSelected}}>
<section id="mainContentSection" class={{notSelected}}>
<core-header-panel fit mode="seamed">
<core-header-panel fit mode="standard">
{{> yield}}
</core-header-panel>
<div>
@@ -31,7 +35,9 @@
<section id="detailSection">
<div id="detailScreenFiller">
<div id="screenDim" cross-fade></div>
<paper-shadow id="globalDetail" z="5" animated hero hero-id="main">
<paper-shadow id="globalDetail" class="card" z="5"
animated hero hero-id="main"
layout vertical>
{{#if globalDetailTemplate}}
{{> UI.dynamic template=globalDetailTemplate data=globalDetailData}}
{{/if}}

View File

@@ -1,3 +1,7 @@
Template.layout.onCreated(function() {
this.subscribe("user");
});
Template.layout.rendered = function() {
$(window).on("popstate", GlobalUI.popStateHandler);
};
@@ -9,14 +13,36 @@ Template.layout.destroyed = function() {
Template.layout.helpers({
notSelected: function(){
return Session.get("global.ui.detailShow") ? "not-selected" : null;
}
},
profileLink: function() {
var user = Meteor.user();
return user.profile && user.profile.username || user.username || "My Account";
},
});
Template.layout.events({
"tap #homeNav": function(event, instance){
Router.go("/");
instance.find("core-drawer-panel").closeDrawer();
},
"tap #profileLink": function(event, instance){
Router.go("profile");
instance.find("core-drawer-panel").closeDrawer();
},
"tap #feedback": function(event, instance) {
GlobalUI.showDialog({
heading: "Feedback",
template: "feedback",
fullOnMobile: true,
});
instance.find("core-drawer-panel").closeDrawer();
},
"tap #changeLog": function(event, instance) {
Router.go("changeLog");
instance.find("core-drawer-panel").closeDrawer();
},
"tap #guide": function(event, instance) {
Router.go("guide");
instance.find("core-drawer-panel").closeDrawer();
},
});

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