Compare commits

...

46 Commits

Author SHA1 Message Date
Stefan Zermatten
263aba596c Changed Guide styling to be more spacious 2015-05-04 09:53:29 +02:00
Stefan Zermatten
f98ed0b659 Fixed old proficiency dialog showing up after it has been depreciated 2015-04-30 07:42:59 +02:00
Stefan Zermatten
6159ce0e88 Characters menu item now shows up when no characters yet exist 2015-04-30 07:42:30 +02:00
Stefan Zermatten
e9509a3a24 Fixed containers not responding to rename or delete 2015-04-30 07:30:37 +02:00
Stefan Zermatten
aa069fd885 Removed single character subscription vulnerability 2015-04-30 07:18:42 +02:00
Stefan Zermatten
99a64667c6 Removed proficiency from effects 2015-04-29 12:31:51 +02:00
Stefan Zermatten
e05fa064d5 Added basic user guide 2015-04-29 12:25:51 +02:00
Stefan Zermatten
b95636a8a3 Added a side list for characters 2015-04-29 10:16:12 +02:00
Stefan Zermatten
bd6c7cd106 paper sliders no longer jump to points, they need to be dragged 2015-04-29 08:57:09 +02:00
Stefan Zermatten
6b1ff343c2 Persona dialogs now correctly update their input fields 2015-04-29 08:47:55 +02:00
Stefan Zermatten
9e7e027fe9 Share dialog style improvement, can delete shares 2015-04-29 08:38:47 +02:00
Stefan Zermatten
79e0f917df Gave share dialog a done button 2015-04-29 08:16:24 +02:00
Stefan Zermatten
4bddf8d5d3 Racial proficiencies now show up when inserted. 2015-04-29 08:14:01 +02:00
Stefan Zermatten
57159d6cfe Identity dialog now opens as expected 2015-04-29 08:13:09 +02:00
Stefan Zermatten
56957e0ef0 No longer showing ID of users shared to, will show username or nothing 2015-04-29 07:53:00 +02:00
Stefan Zermatten
31e7b8d610 In-progress implementing usernames for share dialog 2015-04-24 12:16:12 +02:00
Stefan Zermatten
e0209df270 Features now always enabled by default 2015-04-24 12:15:50 +02:00
Stefan Zermatten
40500517af Fixed hit dice dialog and arrow positions. 2015-04-24 12:15:32 +02:00
Stefan Zermatten
c1222ad51d Race dialog now animates properly 2015-04-24 11:26:54 +02:00
Stefan Zermatten
07890269fc Changed character menu icon from hamburger to dots 2015-04-23 10:00:50 +02:00
Stefan Zermatten
895a099b9b Added migration methods available to admins 2015-04-23 10:00:31 +02:00
Stefan Zermatten
21dfc5d086 Replaced math library include with ecwyne:mathjs 2015-04-22 14:41:15 +02:00
Stefan Zermatten
fada0f5136 Implemented a javascript code style 2015-04-22 12:44:25 +02:00
Stefan Zermatten
dce20375b5 Fixed and finished implementing attribute summary detail views 2015-04-21 15:42:10 +02:00
Thaum
6926693e9d Implemented skill and attribute summary dialogs 2015-04-21 11:30:34 +00:00
Thaum
b1e23eba9a updated test to match with new proficiency objects 2015-04-20 14:16:38 +00:00
Thaum
012aad5ae9 Prettified all remaining detail boxes to be view -> edit 2015-04-20 14:06:35 +00:00
Thaum
6ec9f09b6a Replaced languages and proficiency strings with proper proficiencies 2015-04-20 09:11:45 +00:00
Thaum
b76ac23713 Made proficiencies their own objects, added migration functionality 2015-04-17 13:56:52 +00:00
Thaum
245867dae6 Removed unneeded "type" from schemas, use parent.collection instead. 2015-04-17 11:30:44 +00:00
Thaum
1352bd1178 Made spell slot container a white-top 2015-04-17 10:41:44 +00:00
Thaum
a5ebe4f452 Fixed error setting character name as title on hot code push 2015-04-17 10:41:13 +00:00
Thaum
e94b35564b Document title now set with each route 2015-04-17 10:35:20 +00:00
Thaum
663102718d Clicking attacks now brings up the relevant item 2015-04-17 10:22:46 +00:00
Thaum
7e4c8a21aa Features detail dialog is now view first with option to edit 2015-04-17 10:13:46 +00:00
Thaum
0536a6ab01 Effect view fixed for conditional benefits 2015-04-17 10:00:06 +00:00
Thaum
e7f0bb5e05 Added favicons... lots of favicons 2015-04-17 08:26:55 +00:00
Thaum
1a1ee142c0 Fixed polymer inputs not showing their titles or their full area 2015-04-16 13:21:45 +00:00
Thaum
e6487c9416 Implemented item attack summaries 2015-04-16 11:52:17 +00:00
Thaum
7cd353894c Improved effects view 2015-04-16 09:47:13 +00:00
Thaum
49a98aec96 Fixed color-change and delete regression on item dialogs 2015-04-16 09:07:29 +00:00
Thaum
8ec3b74299 Iterated on proficiencies towards making them first class assets 2015-04-16 09:02:00 +00:00
Thaum
6aeb6635a7 whitespace cleanup 2015-04-16 09:01:25 +00:00
Thaum
638209719e Changed item detail to a detail view with the option to edit 2015-04-16 09:00:48 +00:00
Thaum
690eea4f29 Spell lists now expand horizontally if there is space. 2015-04-15 08:18:16 +00:00
Thaum
ad43a1d331 Loading screen now has hints 2015-04-15 06:53:39 +00:00
202 changed files with 3741 additions and 2332 deletions

56
.jscsrc Normal file
View File

@@ -0,0 +1,56 @@
{
"requireOperatorBeforeLineBreak": true,
"requireCamelCaseOrUpperCaseIdentifiers": true,
"maximumLineLength": {
"value": 80,
"allowComments": true,
"allowRegex": true
},
"validateIndentation": "\t",
"validateQuoteMarks": "\"",
"disallowMultipleLineStrings": true,
"disallowMixedSpacesAndTabs": "smart",
"disallowTrailingWhitespace": true,
"disallowSpaceAfterPrefixUnaryOperators": true,
"disallowMultipleVarDecl": true,
"disallowNewlineBeforeBlockStatements": true,
"disallowKeywordsOnNewLine": ["else"],
"requireSpaceAfterKeywords": [
"if",
"else",
"for",
"while",
"do",
"switch",
"return",
"try",
"catch"
],
"requireSpaceBeforeBinaryOperators": [
"=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=",
"&=", "|=", "^=", "+=",
"+", "-", "*", "/", "%", "<<", ">>", ">>>", "&",
"|", "^", "&&", "||", "===", "==", ">=",
"<=", "<", ">", "!=", "!=="
],
"requireSpaceAfterBinaryOperators": true,
"requireSpacesInConditionalExpression": true,
"requireSpacesInForStatement": true,
"requireTrailingComma": {
"ignoreSingleValue": true,
"ignoreSingleLine": true
},
"requireLineFeedAtFileEnd": true,
"disallowSpacesInAnonymousFunctionExpression": {
"beforeOpeningRoundBrace": true
},
"disallowSpacesInsideObjectBrackets": "all",
"disallowSpacesInsideArrayBrackets": "all",
"disallowSpacesInsideParentheses": true,
"disallowMultipleLineBreaks": true,
"disallowNewlineBeforeBlockStatements": true
}

3
.jshintrc Normal file
View File

@@ -0,0 +1,3 @@
{
"undef": false
}

3
rpg-docs/.gitignore vendored
View File

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

View File

@@ -18,3 +18,5 @@ zimme:collection-softremovable
momentjs:moment
mike:mocha
dburles:mongo-collection-instances
percolate:migrations
ecwyne:mathjs

View File

@@ -18,7 +18,8 @@ dburles:collection-helpers@1.0.3
dburles:mongo-collection-instances@0.3.3
ddp@1.1.0
deps@1.0.7
differential:vulcanize@0.0.4
differential:vulcanize@0.0.5
ecwyne:mathjs@0.25.0
ejson@1.0.6
email@1.0.6
fastclick@1.0.3
@@ -46,15 +47,17 @@ logging@1.0.7
matb33:collection-hooks@0.7.11
meteor@1.1.6
meteor-platform@1.2.2
mike:mocha@0.5.2
mike:mocha@0.5.3
minifiers@1.1.5
minimongo@1.0.8
mobile-status-bar@1.0.3
momentjs:moment@2.10.0
momentjs:moment@2.10.3
mongo@1.1.0
npm-bcrypt@0.7.8_2
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
random@1.0.3
@@ -63,6 +66,9 @@ 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
@@ -74,11 +80,11 @@ tracker@1.0.7
ui@1.0.6
underscore@1.0.3
url@1.0.4
velocity:core@0.4.5
velocity:html-reporter@0.3.2
velocity:node-soft-mirror@0.3.1
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
velocity:test-proxy@0.0.4
webapp@1.2.0
webapp-hashing@1.0.3
zimme:collection-behaviours@1.0.4

View File

@@ -4,4 +4,4 @@ Schemas.Instance = new SimpleSchema({
//an instance is a single flow of time all parties in an instance are in-sync time wise
});
Instances.attachSchema(Schemas.Instance);
Instances.attachSchema(Schemas.Instance);

View File

@@ -5,4 +5,4 @@ Schemas.Party = new SimpleSchema({
//each party can only be in a single instance at a time
});
Parties.attachSchema(Schemas.Party);
Parties.attachSchema(Schemas.Party);

View File

@@ -5,29 +5,32 @@ Actions = new Mongo.Collection("actions");
*/
Schemas.Action = new SimpleSchema({
charId: {
type: String,
regEx: SimpleSchema.RegEx.Id
type: String,
regEx: SimpleSchema.RegEx.Id,
},
name: {
type: String, trim: false
type: String,
trim: false,
},
description: {
type: String, trim: false
type: String,
trim: false,
},
type: {
type: String,
allowedValues: ["action, bonus, reaction, free"],
defaultValue: "action"
defaultValue: "action",
},
//the immediate impact of doing this action (eg. -1 rages)
adjustments: {
type: [Schemas.Adjustment], defaultValue: []
}
type: [Schemas.Adjustment],
defaultValue: [],
},
});
Actions.attachSchema(Schemas.Action);
Actions.attachBehaviour('softRemovable');
Actions.attachBehaviour("softRemovable");
makeChild(Actions);
Actions.allow(CHARACTER_SUBSCHEMA_ALLOW);

View File

@@ -5,68 +5,75 @@ Attacks = new Mongo.Collection("attacks");
*/
Schemas.Attack = new SimpleSchema({
charId: {
type: String,
regEx: SimpleSchema.RegEx.Id
type: String,
regEx: SimpleSchema.RegEx.Id,
},
name: {
type: String,
defaultValue: "New Attack",
trim: false
defaultValue: "New Attack",
trim: false,
},
details: {
type: String,
optional: true,
trim: false
optional: true,
trim: false,
},
attackBonus: {
type: String,
defaultValue: "strengthMod + proficiencyBonus",
optional: true,
trim: false
optional: true,
trim: false,
},
damageBonus: {
type: String,
defaultValue: "strengthMod",
optional: true,
trim: false
optional: true,
trim: false,
},
damageDice: {
type: String,
optional: true,
defaultValue: "1d8",
allowedValues: DAMAGE_DICE
allowedValues: DAMAGE_DICE,
},
damageType: {
type: String,
allowedValues: ["bludgeoning", "piercing", "slashing", "acid", "cold", "fire", "force", "lightning", "necrotic",
"poison", "psychic", "radiant", "thunder"],
defaultValue: "slashing"
},
//indicates what the attack originated from
type: {
type: String,
defaultValue: "editable",
allowedValues: ["editable", "feature", "class", "buff", "equipment", "racial", "inate"]
allowedValues: [
"bludgeoning",
"piercing",
"slashing",
"acid",
"cold",
"fire",
"force",
"lightning",
"necrotic",
"poison",
"psychic",
"radiant",
"thunder",
],
defaultValue: "slashing",
},
//the id of the feature, buff or item that created this effect
parent: {
type: Schemas.Parent
},
color: {
type: String,
allowedValues: _.pluck(colorOptions, "key"),
defaultValue: "q"
type: String,
allowedValues: _.pluck(colorOptions, "key"),
defaultValue: "q",
},
enabled: {
type: Boolean,
defaultValue: true
}
defaultValue: true,
},
});
Attacks.attachSchema(Schemas.Attack);
Attacks.attachBehaviour('softRemovable');
makeChild(Attacks, ['name', 'enabled']); //children of lots of things
Attacks.attachBehaviour("softRemovable");
makeChild(Attacks, ["name", "enabled"]); //children of lots of things
Attacks.allow(CHARACTER_SUBSCHEMA_ALLOW);
Attacks.deny(CHARACTER_SUBSCHEMA_DENY);

View File

@@ -7,21 +7,22 @@ Schemas.Buff = new SimpleSchema({
type: String,
regEx: SimpleSchema.RegEx.Id,
autoValue: function(){
if(!this.isSet) return Random.id();
}},
if (!this.isSet) return Random.id();
},
},
charId: {
type: String,
regEx: SimpleSchema.RegEx.Id
type: String,
regEx: SimpleSchema.RegEx.Id,
},
//expiry time
expiry: { type: Number, optional: true},
duration: { type: Number }
expiry: {type: Number, optional: true},
duration: {type: Number},
});
Buffs.attachSchema(Schemas.Buff);
Buffs.attachBehaviour('softRemovable');
makeParent(Buffs, 'name'); //parents of effects and attacks
Buffs.attachBehaviour("softRemovable");
makeParent(Buffs, "name"); //parents of effects and attacks
Buffs.allow(CHARACTER_SUBSCHEMA_ALLOW);
Buffs.deny(CHARACTER_SUBSCHEMA_DENY);

View File

@@ -3,18 +3,16 @@ Characters = new Mongo.Collection("characters");
Schemas.Character = new SimpleSchema({
//strings
name: { type: String, defaultValue: "", trim: false},
alignment: { type: String, defaultValue: "", trim: false},
gender: { type: String, defaultValue: "", trim: false},
race: { type: String, defaultValue: "", trim: false},
description: { type: String, defaultValue: "", trim: false},
personality: { type: String, defaultValue: "", trim: false},
ideals: { type: String, defaultValue: "", trim: false},
bonds: { type: String, defaultValue: "", trim: false},
flaws: { type: String, defaultValue: "", trim: false},
backstory: { type: String, defaultValue: "", trim: false},
proficiencies:{ type: String, defaultValue: "", trim: false},
languages: { type: String, defaultValue: "", trim: false},
name: {type: String, defaultValue: "", trim: false},
alignment: {type: String, defaultValue: "", trim: false},
gender: {type: String, defaultValue: "", trim: false},
race: {type: String, defaultValue: "", trim: false},
description: {type: String, defaultValue: "", trim: false},
personality: {type: String, defaultValue: "", trim: false},
ideals: {type: String, defaultValue: "", trim: false},
bonds: {type: String, defaultValue: "", trim: false},
flaws: {type: String, defaultValue: "", trim: false},
backstory: {type: String, defaultValue: "", trim: false},
//attributes
//ability scores
@@ -75,99 +73,100 @@ Schemas.Character = new SimpleSchema({
slashingMultiplier: {type: Schemas.Attribute},
thunderMultiplier: {type: Schemas.Attribute},
//skills
//saves
strengthSave: {type: Schemas.Skill},
"strengthSave.ability": { type: String, defaultValue: "strength" },
"strengthSave.ability": {type: String, defaultValue: "strength"},
dexteritySave: {type: Schemas.Skill},
"dexteritySave.ability": { type: String, defaultValue: "dexterity" },
"dexteritySave.ability": {type: String, defaultValue: "dexterity"},
constitutionSave:{type: Schemas.Skill},
"constitutionSave.ability": { type: String, defaultValue: "constitution" },
"constitutionSave.ability": {type: String, defaultValue: "constitution"},
intelligenceSave:{type: Schemas.Skill},
"intelligenceSave.ability": { type: String, defaultValue: "intelligence" },
"intelligenceSave.ability": {type: String, defaultValue: "intelligence"},
wisdomSave: {type: Schemas.Skill},
"wisdomSave.ability": { type: String, defaultValue: "wisdom" },
"wisdomSave.ability": {type: String, defaultValue: "wisdom"},
charismaSave: {type: Schemas.Skill},
"charismaSave.ability": { type: String, defaultValue: "charisma" },
"charismaSave.ability": {type: String, defaultValue: "charisma"},
//skill skills
acrobatics: {type: Schemas.Skill},
"acrobatics.ability": { type: String, defaultValue: "dexterity" },
acrobatics: {type: Schemas.Skill},
"acrobatics.ability": {type: String, defaultValue: "dexterity"},
animalHandling: {type: Schemas.Skill},
"animalHandling.ability": { type: String, defaultValue: "wisdom" },
animalHandling: {type: Schemas.Skill},
"animalHandling.ability": {type: String, defaultValue: "wisdom"},
arcana: {type: Schemas.Skill},
"arcana.ability": { type: String, defaultValue: "intelligence" },
arcana: {type: Schemas.Skill},
"arcana.ability": {type: String, defaultValue: "intelligence"},
athletics: {type: Schemas.Skill},
"athletics.ability": { type: String, defaultValue: "strength" },
athletics: {type: Schemas.Skill},
"athletics.ability": {type: String, defaultValue: "strength"},
deception: {type: Schemas.Skill},
"deception.ability": { type: String, defaultValue: "charisma" },
deception: {type: Schemas.Skill},
"deception.ability": {type: String, defaultValue: "charisma"},
history: {type: Schemas.Skill},
"history.ability": { type: String, defaultValue: "intelligence" },
history: {type: Schemas.Skill},
"history.ability": {type: String, defaultValue: "intelligence"},
insight: {type: Schemas.Skill},
"insight.ability": { type: String, defaultValue: "wisdom" },
insight: {type: Schemas.Skill},
"insight.ability": {type: String, defaultValue: "wisdom"},
intimidation: {type: Schemas.Skill},
"intimidation.ability": { type: String, defaultValue: "charisma" },
intimidation: {type: Schemas.Skill},
"intimidation.ability": {type: String, defaultValue: "charisma"},
investigation: {type: Schemas.Skill},
"investigation.ability": { type: String, defaultValue: "intelligence" },
"investigation.ability": {type: String, defaultValue: "intelligence"},
medicine: {type: Schemas.Skill},
"medicine.ability": { type: String, defaultValue: "wisdom" },
"medicine.ability": {type: String, defaultValue: "wisdom"},
nature: {type: Schemas.Skill},
"nature.ability": { type: String, defaultValue: "intelligence" },
"nature.ability": {type: String, defaultValue: "intelligence"},
perception: {type: Schemas.Skill},
"perception.ability": { type: String, defaultValue: "wisdom" },
"perception.ability": {type: String, defaultValue: "wisdom"},
performance: {type: Schemas.Skill},
"performance.ability": { type: String, defaultValue: "charisma" },
"performance.ability": {type: String, defaultValue: "charisma"},
persuasion: {type: Schemas.Skill},
"persuasion.ability": { type: String, defaultValue: "charisma" },
persuasion: {type: Schemas.Skill},
"persuasion.ability": {type: String, defaultValue: "charisma"},
religion: {type: Schemas.Skill},
"religion.ability": { type: String, defaultValue: "intelligence" },
religion: {type: Schemas.Skill},
"religion.ability": {type: String, defaultValue: "intelligence"},
sleightOfHand: {type: Schemas.Skill},
"sleightOfHand.ability": { type: String, defaultValue: "dexterity" },
"sleightOfHand.ability": {type: String, defaultValue: "dexterity"},
stealth: {type: Schemas.Skill},
"stealth.ability": { type: String, defaultValue: "dexterity" },
"stealth.ability": {type: String, defaultValue: "dexterity"},
survival: {type: Schemas.Skill},
"survival.ability": { type: String, defaultValue: "wisdom" },
"survival.ability": {type: String, defaultValue: "wisdom"},
//Mechanical Skills
initiative: {type: Schemas.Skill},
"initiative.ability": { type: String, defaultValue: "dexterity" },
initiative: {type: Schemas.Skill},
"initiative.ability": {type: String, defaultValue: "dexterity"},
dexterityArmor: {type: Schemas.Skill},
"dexterityArmor.ability": { type: String, defaultValue: "dexterity" },
"dexterityArmor.ability": {type: String, defaultValue: "dexterity"},
//mechanics
deathSave: { type: Schemas.DeathSave },
deathSave: {type: Schemas.DeathSave},
//permissions
owner: { type: String, regEx: SimpleSchema.RegEx.Id },
readers: { type: [String], regEx: SimpleSchema.RegEx.Id, defaultValue: [] },
writers: { type: [String], regEx: SimpleSchema.RegEx.Id, defaultValue: [] },
color: {type: String, allowedValues: _.pluck(colorOptions, "key"), defaultValue: "q"},
owner: {type: String, regEx: SimpleSchema.RegEx.Id},
readers: {type: [String], regEx: SimpleSchema.RegEx.Id, defaultValue: []},
writers: {type: [String], regEx: SimpleSchema.RegEx.Id, defaultValue: []},
color: {
type: String,
allowedValues: _.pluck(colorOptions, "key"),
defaultValue: "q",
},
//TODO add per-character settings
"settings.experiencesInc": {type: Number, defaultValue: 20}, //how many experiences to load at a time in XP table
});
@@ -176,13 +175,15 @@ Characters.attachSchema(Schemas.Character);
var attributeBase = function(charId, statName){
check(statName, String);
var effects = Effects.find({charId: charId, stat: statName, enabled: true}).fetch();
var effects = Effects.find(
{charId: charId, stat: statName, enabled: true}
).fetch();
effects = _.groupBy(effects, "operation");
var value = _.contains(DAMAGE_MULTIPLIERS, statName)? 1 : 0;
var value = _.contains(DAMAGE_MULTIPLIERS, statName) ? 1 : 0;
//start with the highest base value
_.each(effects.base, function(effect){
var efv = evaluateEffect(charId, effect)
var efv = evaluateEffect(charId, effect);
if (efv > value){
value = efv;
}
@@ -201,16 +202,16 @@ var attributeBase = function(charId, statName){
//ensure value is >= all mins
_.each(effects.min, function(effect){
var min = evaluateEffect(charId, effect);
value = value > min? value : min;
value = value > min ? value : min;
});
//ensure value is <= all maxes
_.each(effects.max, function(effect){
var max = evaluateEffect(charId, effect);
value = value < max? value : max;
value = value < max ? value : max;
});
return value;
}
};
//functions and calculated values.
//These functions can only rely on this._id since no other
@@ -223,16 +224,24 @@ Characters.helpers({
fieldSelector[fieldName] = 1;
var char = Characters.findOne(this._id, {fields: fieldSelector});
var field = char[fieldName];
if(field === undefined){
throw new Meteor.Error("getField failed",
"getField could not find field " + fieldName + " in character "+ char._id);
if (field === undefined){
throw new Meteor.Error(
"getField failed",
"getField could not find field " +
fieldName +
" in character " +
char._id
);
}
return field;
},
//returns the value of a field
fieldValue : function(fieldName){
if(!Schemas.Character.schema(fieldName)){
throw new Meteor.Error("Field not found", "Character's schema does not contain a field called: " + fieldName);
if (!Schemas.Character.schema(fieldName)){
throw new Meteor.Error(
"Field not found",
"Character's schema does not contain a field called: " + fieldName
);
}
//duck typing to get the right value function
//.ability implies skill
@@ -270,7 +279,7 @@ Characters.helpers({
var ability = this.attributeValue(skill.ability);
//base modifier
var mod = +getMod(ability)
var mod = +getMod(ability);
//multiply proficiency bonus by largest value in proficiency array
var prof = this.proficiency(skillName);
@@ -279,7 +288,9 @@ Characters.helpers({
mod += prof * this.attributeValue("proficiencyBonus");
//apply all effects
var rawEffects = Effects.find({charId: charId, stat: skillName, enabled: true}).fetch();
var rawEffects = Effects.find(
{charId: charId, stat: skillName, enabled: true}
).fetch();
var effects = _.groupBy(rawEffects, "operation");
_.forEach(effects.add, function(effect){
mod += evaluateEffect(charId, effect);
@@ -289,11 +300,11 @@ Characters.helpers({
});
_.forEach(effects.min, function(effect){
var min = evaluateEffect(charId, effect);
mod = mod > min? mod : min;
mod = mod > min ? mod : min;
});
_.forEach(effects.max, function(effect){
var max = evaluateEffect(charId, effect);
mod = mod < max? mod : max;
mod = mod < max ? mod : max;
});
return signedString(mod);
}),
@@ -302,12 +313,12 @@ Characters.helpers({
var charId = this._id;
//return largest value in proficiency array
var prof = 0;
Effects.find({charId: charId, stat: skillName, enabled: true}).forEach(function(effect){
if(effect.operation === "proficiency"){
var newProf = evaluateEffect(charId, effect);
if (newProf > prof){
prof = newProf;
}
Proficiencies.find(
{charId: charId, name: skillName, enabled: true}
).forEach(function(proficiency){
var newProf = proficiency.value;
if (newProf > prof){
prof = newProf;
}
});
return prof;
@@ -317,10 +328,12 @@ Characters.helpers({
if (_.isString(skillName)){
var skill = this.getField(skillName);
}
var charId = this._id
var charId = this._id;
var mod = +this.skillMod(skillName);
var value = 10 + mod;
Effects.find({charId: charId, stat: skillName, enabled: true, operation: "passiveAdd"}).forEach(function(effect){
Effects.find(
{charId: charId, stat: skillName, enabled: true, operation: "passiveAdd"}
).forEach(function(effect){
value += evaluateEffect(charId, effect);
});
return value;
@@ -328,11 +341,15 @@ Characters.helpers({
},
advantage: function(skillName){
var charId = this._id
var advantage = Effects.find({charId: charId, stat: skillName, enabled: true, operation: "advantage"}).count();
var disadvantage = Effects.find({charId: charId, stat: skillName, enabled: true, operation: "disadvantage"}).count();
if(advantage && !disadvantage) return 1;
if(disadvantage && !advantage) return -1;
var charId = this._id;
var advantage = Effects.find(
{charId: charId, stat: skillName, enabled: true, operation: "advantage"}
).count();
var disadvantage = Effects.find(
{charId: charId, stat: skillName, enabled: true, operation: "disadvantage"}
).count();
if (advantage && !disadvantage) return 1;
if (disadvantage && !advantage) return -1;
return 0;
},
@@ -347,15 +364,12 @@ Characters.helpers({
xpLevel: function(){
var xp = this.experience();
var xpTable = [0, 300, 900, 2700, 6500, 14000, 23000, 34000, 48000, 64000,
85000, 100000, 120000, 140000, 165000, 195000, 225000, 265000,
305000, 355000];
for(var i = 0; i < 19; i++){
if(xp < xpTable[i]){
for (var i = 0; i < 19; i++){
if (xp < XP_TABLE[i]){
return i;
}
};
if(xp > 355000) return 20;
}
if (xp > 355000) return 20;
return 0;
},
@@ -363,22 +377,25 @@ Characters.helpers({
var level = 0;
Classes.find({charId: this._id}).forEach(function(cls){
level += cls.level;
})
});
return level;
},
experience: function(){
var xp = 0;
Experiences.find({charId: this._id}, {fields: {value: 1}}).forEach(function(e){
Experiences.find(
{charId: this._id},
{fields: {value: 1}}
).forEach(function(e){
xp += e.value;
})
});
return xp;
}
},
});
//clean up all data related to that character before removing it
Characters.after.remove(function (userId, character) {
if(Meteor.isServer){
Characters.after.remove(function(userId, character) {
if (Meteor.isServer){
Actions .remove({charId: character._id});
Attacks .remove({charId: character._id});
Buffs .remove({charId: character._id});
@@ -395,25 +412,25 @@ Characters.after.remove(function (userId, character) {
});
Characters.allow({
insert: function (userId, doc) {
insert: function(userId, doc) {
// the user must be logged in, and the document must be owned by the user
return (userId && doc.owner === userId);
},
update: function (userId, doc, fields, modifier) {
update: function(userId, doc, fields, modifier) {
// can only change documents you have write access to
return doc.owner === userId ||
_.contains(doc.writers, userId);
},
remove: function (userId, doc) {
remove: function(userId, doc) {
// can only remove your own documents
return doc.owner === userId;
},
fetch: ["owner", "writers"]
fetch: ["owner", "writers"],
});
Characters.deny({
update: function (userId, docs, fields, modifier) {
update: function(userId, docs, fields, modifier) {
// can't change owners
return _.contains(fields, 'owner');
return _.contains(fields, "owner");
}
});

View File

@@ -8,21 +8,25 @@ Schemas.Class = new SimpleSchema({
type: Date,
autoValue: function() {
if (this.isInsert) {
return new Date;
return new Date();
} else if (this.isUpsert) {
return {$setOnInsert: new Date};
return {$setOnInsert: new Date()};
} else {
this.unset();
}
}
},
},
color: {
type: String,
allowedValues: _.pluck(colorOptions, "key"),
defaultValue: "q",
},
color: {type: String, allowedValues: _.pluck(colorOptions, "key"), defaultValue: "q"}
});
Classes.attachSchema(Schemas.Class);
Classes.attachBehaviour('softRemovable');
makeParent(Classes, 'name'); //parents of effects and attacks
Classes.attachBehaviour("softRemovable");
makeParent(Classes, "name"); //parents of effects and attacks
Classes.allow(CHARACTER_SUBSCHEMA_ALLOW);
Classes.deny(CHARACTER_SUBSCHEMA_DENY);

View File

@@ -7,33 +7,39 @@ Effects = new Mongo.Collection("effects");
Schemas.Effect = new SimpleSchema({
charId: {
type: String,
regEx: SimpleSchema.RegEx.Id
regEx: SimpleSchema.RegEx.Id,
},
name: {
type: String,
optional: true, //TODO make necessary if there is no owner
trim: false
trim: false,
},
operation: {
type: String,
defaultValue: "add",
allowedValues: ["base", "proficiency","add","mul","min","max","advantage","disadvantage","passiveAdd","fail","conditional"]
allowedValues: [
"base",
"proficiency",
"add",
"mul",
"min",
"max",
"advantage",
"disadvantage",
"passiveAdd",
"fail",
"conditional",
],
},
value: {
type: Number,
decimal: true,
optional: true
optional: true,
},
calculation: {
type: String,
optional: true,
trim: false
},
//indicates what the effect originated from
type: {
type: String,
defaultValue: "editable",
allowedValues: ["editable", "feature", "class", "buff", "equipment", "racial", "inate"]
trim: false,
},
//the thing that created this effect
parent: {
@@ -42,17 +48,17 @@ Schemas.Effect = new SimpleSchema({
//which stat the effect is applied to
stat: {
type: String,
optional: true
optional: true,
},
enabled: {
type: Boolean,
defaultValue: true
}
defaultValue: true,
},
});
Effects.attachSchema(Schemas.Effect);
if(Meteor.isServer) Characters.after.insert(function (userId, char) {
if (Meteor.isServer) Characters.after.insert(function(userId, char) {
Effects.insert({
charId: char._id,
type: "inate",
@@ -62,8 +68,8 @@ if(Meteor.isServer) Characters.after.insert(function (userId, char) {
calculation: "level * constitutionMod",
parent: {
id: char._id,
collection: "Characters"
}
collection: "Characters",
},
});
Effects.insert({
charId: char._id,
@@ -74,8 +80,8 @@ if(Meteor.isServer) Characters.after.insert(function (userId, char) {
calculation: "floor(level / 4 + 1.75)",
parent: {
id: char._id,
collection: "Characters"
}
collection: "Characters",
},
});
Effects.insert({
charId: char._id,
@@ -86,8 +92,8 @@ if(Meteor.isServer) Characters.after.insert(function (userId, char) {
calculation: "dexterityArmor",
parent: {
id: char._id,
collection: "Characters"
}
collection: "Characters",
},
});
Effects.insert({
charId: char._id,
@@ -98,13 +104,13 @@ if(Meteor.isServer) Characters.after.insert(function (userId, char) {
value: 10,
parent: {
id: char._id,
collection: "Characters"
}
collection: "Characters",
},
});
});
Effects.attachBehaviour('softRemovable');
makeChild(Effects, ['name', 'enabled']); //children of lots of things
Effects.attachBehaviour("softRemovable");
makeChild(Effects, ["enabled"]); //children of lots of things
Effects.allow(CHARACTER_SUBSCHEMA_ALLOW);
Effects.deny(CHARACTER_SUBSCHEMA_DENY);

View File

@@ -9,19 +9,19 @@ Schemas.Experience = new SimpleSchema({
type: Date,
autoValue: function() {
if (this.isInsert) {
return new Date;
return new Date();
} else if (this.isUpsert) {
return {$setOnInsert: new Date};
return {$setOnInsert: new Date()};
} else {
this.unset();
}
}
},
},
});
Experiences.attachSchema(Schemas.Experience);
Experiences.attachBehaviour('softRemovable');
Experiences.attachBehaviour("softRemovable");
Experiences.allow(CHARACTER_SUBSCHEMA_ALLOW);
Experiences.deny(CHARACTER_SUBSCHEMA_DENY);

View File

@@ -6,10 +6,17 @@ Schemas.Feature = new SimpleSchema({
description: {type: String, optional: true, trim: false},
uses: {type: String, optional: true, trim: false},
used: {type: Number, defaultValue: 0},
reset: {type: String, allowedValues: ["manual", "longRest", "shortRest"], defaultValue: "manual"},
reset: {
type: String,
allowedValues: ["manual", "longRest", "shortRest"],
defaultValue: "manual",
},
enabled: {type: Boolean, defaultValue: true},
alwaysEnabled:{type: Boolean, defaultValue: true},
color: {type: String, allowedValues: _.pluck(colorOptions, "key"), defaultValue: "q"}
color: {type: String,
allowedValues: _.pluck(colorOptions, "key"),
defaultValue: "q",
},
});
Features.attachSchema(Schemas.Feature);
@@ -20,11 +27,11 @@ Features.helpers({
},
usesValue: function(){
return evaluate(this.charId, this.uses);
}
},
});
Features.attachBehaviour('softRemovable');
makeParent(Features, ['name', 'enabled']); //parents of effects and attacks
Features.attachBehaviour("softRemovable");
makeParent(Features, ["name", "enabled"]); //parents of effects and attacks
Features.allow(CHARACTER_SUBSCHEMA_ALLOW);
Features.deny(CHARACTER_SUBSCHEMA_DENY);

View File

@@ -4,12 +4,16 @@ Schemas.Note = new SimpleSchema({
charId: {type: String, regEx: SimpleSchema.RegEx.Id},
name: {type: String, trim: false},
description: {type: String, optional: true, trim: false},
color: {type: String, allowedValues: _.pluck(colorOptions, "key"), defaultValue: "q"}
color: {
type: String,
allowedValues:_.pluck(colorOptions, "key"),
defaultValue: "q",
},
});
Notes.attachSchema(Schemas.Note);
Notes.attachBehaviour('softRemovable');
Notes.attachBehaviour("softRemovable");
Notes.allow(CHARACTER_SUBSCHEMA_ALLOW);
Notes.deny(CHARACTER_SUBSCHEMA_DENY);

View File

@@ -2,25 +2,35 @@ Proficiencies = new Mongo.Collection("proficiencies");
Schemas.Proficiency = new SimpleSchema({
charId: {
type: String,
regEx: SimpleSchema.RegEx.Id
},
name: {
type: String,
trim: false
regEx: SimpleSchema.RegEx.Id,
},
name: {
type: String,
trim: false,
optional: true,
},
value: {
type: Number,
allowedValues: [0, 0.5, 1, 2],
defaultValue: 1,
decimal: true,
},
//indicates what type of thing proficiency originated from
type: {
type: String,
defaultValue: "editable",
allowedValues: ["editable", "feature", "buff", "equipment", "inate"]
allowedValues: ["skill", "save", "weapon", "armor", "tool", "language"],
defaultValue: "skill",
},
enabled: {
type: Boolean,
defaultValue: true,
},
});
Proficiencies.attachSchema(Schemas.Proficiency);
Proficiencies.attachBehaviour('softRemovable');
makeChild(Proficiencies);
Proficiencies.attachBehaviour("softRemovable");
makeChild(Proficiencies);
Proficiencies.allow(CHARACTER_SUBSCHEMA_ALLOW);
Proficiencies.deny(CHARACTER_SUBSCHEMA_DENY);

View File

@@ -7,7 +7,11 @@ Schemas.SpellLists = new SimpleSchema({
saveDC: {type: String, optional: true, trim: false},
attackBonus: {type: String, optional: true, trim: false},
maxPrepared: {type: String, optional: true, trim: false},
color: {type: String, allowedValues: _.pluck(colorOptions, "key"), defaultValue: "q"},
color: {
type: String,
allowedValues: _.pluck(colorOptions, "key"),
defaultValue: "q",
},
"settings.showUnprepared": {type: Boolean, defaultValue: true},
});
@@ -16,14 +20,17 @@ SpellLists.attachSchema(Schemas.SpellLists);
SpellLists.helpers({
numPrepared: function(){
var num = 0;
Spells.find({charId: this.charId, listId: this._id, prepared: 1}, {fields: {prepareCost: 1}}).forEach(function(spell){
Spells.find(
{charId: this.charId, listId: this._id, prepared: 1},
{fields: {prepareCost: 1}}
).forEach(function(spell){
num += spell.prepareCost;
});
return num;
}
});
SpellLists.attachBehaviour('softRemovable');
SpellLists.attachBehaviour("softRemovable");
makeParent(SpellLists); //parents of spells
SpellLists.allow(CHARACTER_SUBSCHEMA_ALLOW);

View File

@@ -2,25 +2,65 @@ Spells = new Mongo.Collection("spells");
Schemas.Spell = new SimpleSchema({
charId: {type: String, regEx: SimpleSchema.RegEx.Id},
prepared: {type: String, defaultValue: "prepared", allowedValues: ["prepared","unprepared","always"]},
name: {type: String, trim: false, defaultValue: "New Spell"},
description: {type: String, optional: true, trim: false},
castingTime: {type: String, optional: true, defaultValue: "action", trim: false},
range: {type: String, optional: true, trim: false},
duration: {type: String, optional: true, trim: false, defaultValue: "Instantaneous"},
"components.verbal": {type: Boolean, defaultValue: false},
"components.somatic": {type: Boolean, defaultValue: false},
"components.material": {type: String, optional: true},
prepared: {
type: String,
defaultValue: "prepared",
allowedValues: ["prepared", "unprepared", "always"],
},
name: {
type: String,
trim: false,
defaultValue: "New Spell",
},
description: {
type: String,
optional: true,
trim: false,
},
castingTime: {
type: String,
optional: true,
defaultValue: "action",
trim: false,
},
range: {
type: String,
optional: true,
trim: false,
},
duration: {
type: String,
optional: true,
trim: false,
defaultValue: "Instantaneous",
},
"components.verbal": {type: Boolean, defaultValue: false},
"components.somatic": {type: Boolean, defaultValue: false},
"components.concentration": {type: Boolean, defaultValue: false},
ritual: {type: Boolean, defaultValue: false},
level: {type: Number, defaultValue: 1},
school: {type: String, defaultValue: "Abjuration", allowedValues: magicSchools},
color: {type: String, allowedValues: _.pluck(colorOptions, "key"), defaultValue: "q"}
"components.material": {type: String, optional: true},
ritual: {
type: Boolean,
defaultValue: false,
},
level: {
type: Number,
defaultValue: 1,
},
school: {
type: String,
defaultValue: "Abjuration",
allowedValues: magicSchools,
},
color: {
type: String,
allowedValues: _.pluck(colorOptions, "key"),
defaultValue: "q",
},
});
Spells.attachSchema(Schemas.Spell);
Spells.attachBehaviour('softRemovable');
Spells.attachBehaviour("softRemovable");
makeChild(Spells); //children of spell lists
Spells.allow(CHARACTER_SUBSCHEMA_ALLOW);

View File

@@ -5,21 +5,21 @@
Schemas.Adjustment = new SimpleSchema({
name: {
type: String,
optional: true
optional: true,
},
//which stat the adjustment is applied to
stat: {
type: String,
optional: true
optional: true,
},
//the value added to the stat
value: {
type: Number,
decimal: true,
optional: true
optional: true,
},
calculation: {
type: String,
optional: true
}
optional: true,
},
});

View File

@@ -3,11 +3,11 @@ Schemas.Attribute = new SimpleSchema({
//should be zero after reset
adjustment: {
type: Number,
defaultValue: 0
defaultValue: 0,
},
reset: {
type: String,
defaultValue: "longRest",
allowedValues: ["longRest", "shortRest"]
}
allowedValues: ["longRest", "shortRest"],
},
});

View File

@@ -3,20 +3,20 @@ Schemas.DeathSave = new SimpleSchema({
type: Number,
min: 0,
max: 3,
defaultValue: 0
defaultValue: 0,
},
fail: {
type: Number,
min: 0,
max: 3,
defaultValue: 0
defaultValue: 0,
},
canDeathSave: {
type: Boolean,
defaultValue: true
defaultValue: true,
},
stable: {
type: Boolean,
defaultValue: false
}
defaultValue: false,
},
});

View File

@@ -1,4 +1,4 @@
Schemas.Skill = new SimpleSchema({
//attribute name that this skill used as base mod for roll
ability: { type: String, defaultValue: "" },
ability: {type: String, defaultValue: ""},
});

View File

@@ -16,7 +16,7 @@ Schemas.TemporaryHitPoints = new SimpleSchema({
} else {
this.unset();
}
}
},
},
});
@@ -29,11 +29,13 @@ TemporaryHitPoints.helpers({
});
//remove the temporary hit points when they hit zero
TemporaryHitPoints.after.update(function (userId, thp, fieldNames, modifier, options) {
if(thp.used >= thp.maximum && thp.deleteOnZero){
TemporaryHitPoints.remove(thp._id);
}
}, {fetchPrevious: false});
TemporaryHitPoints.after.update(
function(userId, thp, fieldNames, modifier, options){
if (thp.used >= thp.maximum && thp.deleteOnZero){
TemporaryHitPoints.remove(thp._id);
}
}, {fetchPrevious: false}
);
TemporaryHitPoints.allow(CHARACTER_SUBSCHEMA_ALLOW);
TemporaryHitPoints.deny(CHARACTER_SUBSCHEMA_DENY);

View File

@@ -2,39 +2,55 @@
Containers = new Mongo.Collection("containers");
Schemas.Container = new SimpleSchema({
name: { type: String, trim: false },
charId: { type: String, regEx: SimpleSchema.RegEx.Id},
isCarried: { type: Boolean },
name: {type: String, trim: false},
charId: {type: String, regEx: SimpleSchema.RegEx.Id},
isCarried: {type: Boolean},
weight: {type: Number, min: 0, defaultValue: 0, decimal: true},
value: {type: Number, min: 0, defaultValue: 0, decimal: true},
description:{type: String, optional: true, trim: false},
color: {type: String, allowedValues: _.pluck(colorOptions, "key"), defaultValue: "q"}
color: {
type: String,
allowedValues: _.pluck(colorOptions, "key"),
defaultValue: "q",
},
});
Containers.attachSchema(Schemas.Container);
Containers.helpers({
totalValue: function(){
var value = this.value;
Items.find({"parent.id": this._id}, {fields: {quantity: 1, value: 1}}).forEach(function(item){
contentsValue: function(){
var value = 0;
Items.find(
{"parent.id": this._id},
{fields: {quantity: 1, value: 1}}
).forEach(function(item){
value += item.totalValue();
});
return value;
},
totalWeight: function(){
var weight = this.weight;
Items.find({"parent.id": this._id}, {fields: {quantity: 1, weight: 1}}).forEach(function(item){
totalValue: function(){
return this.contentsValue() + this.value;
},
contentsWeight: function(){
var weight = 0;
Items.find(
{"parent.id": this._id},
{fields: {quantity: 1, weight: 1}}
).forEach(function(item){
weight += item.totalWeight();
});
return weight;
},
totalWeight: function(){
return this.contentsWeight() + this.weight;
},
moveToCharacter: function(characterId){
if(this.charId === characterId) return;
if (this.charId === characterId) return;
Items.update(this._id, {$set: {charId: characterId}});
}
},
});
Containers.attachBehaviour('softRemovable');
Containers.attachBehaviour("softRemovable");
makeParent(Containers); //parents of items
Containers.allow(CHARACTER_SUBSCHEMA_ALLOW);

View File

@@ -1,4 +1,4 @@
Items = new Mongo.Collection('items');
Items = new Mongo.Collection("items");
Schemas.Item = new SimpleSchema({
name: {type: String, defaultValue: "New Item", trim: false},
@@ -10,7 +10,11 @@ Schemas.Item = new SimpleSchema({
value: {type: Number, min: 0, defaultValue: 0, decimal: true},
enabled: {type: Boolean, defaultValue: false},
requiresAttunement: {type: Boolean, defaultValue: false},
color: {type: String, allowedValues: _.pluck(colorOptions, "key"), defaultValue: "q"}
color: {
type: String,
allowedValues: _.pluck(colorOptions, "key"),
defaultValue: "q",
},
});
Items.attachSchema(Schemas.Item);
@@ -23,40 +27,77 @@ Items.helpers({
return this.weight * this.quantity;
},
pluralName: function(){
if(this.plural && this.quantity !== 1){
if (this.plural && this.quantity !== 1){
return this.plural;
} else{
} else {
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}});
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;
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}});
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}});
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 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;
if (oldStack.quantity < moveQuantity) moveQuantity = oldStack.quantity;
var oldQuantity = oldStack.quantity - moveQuantity;
var newStack = _.pick(oldStack, Schemas.Item.objectKeys());
@@ -65,31 +106,39 @@ Items.helpers({
var existingStack = Items.findOne(_.omit(newStack, "quantity"));
var updateStackSize = function(){
if(oldQuantity > 0){
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{
if (existingStack){
Items.update(
existingStack._id,
{$inc: {quantity: moveQuantity}},
{},
function(){
updateStackSize();
}
);
} else {
Items.insert(newStack, function(err, id){
if(err) throw err;
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){
if(
if (
modifier && modifier.$set && modifier.$set.enabled && //we are equipping this item
!(modifier.$set["parent.collection"] === "Characters" && modifier.$set["parent.id"]) //and we haven't specified a character to equip to
!(
modifier.$set["parent.collection"] === "Characters" &&
modifier.$set["parent.id"]
) //and we haven"t specified a character to equip to
){
//equip it to the current character
modifier.$set["parent.collection"] = "Characters";
@@ -97,21 +146,21 @@ Items.before.update(function(userId, doc, fieldNames, modifier, options){
}
});
Items.attachBehaviour('softRemovable');
Items.attachBehaviour("softRemovable");
makeChild(Items); //children of containers
makeParent(Items, ['name', 'enabled']); //parents of effects and attacks
makeParent(Items, ["name", "enabled"]); //parents of effects and attacks
Items.allow(CHARACTER_SUBSCHEMA_ALLOW);
//give characters default items
Characters.after.insert(function (userId, char) {
if(Meteor.isServer){
Characters.after.insert(function(userId, char) {
if (Meteor.isServer){
var containerId = Containers.insert({
name: "Coin Pouch",
charId: char._id,
isCarried: true,
description: "A sturdy pouch for coins",
color: "d"
color: "d",
});
Items.insert({
name: "Gold piece",
@@ -123,8 +172,8 @@ Characters.after.insert(function (userId, char) {
color: "n",
parent: {
id: containerId,
collection: "Containers"
}
collection: "Containers",
},
});
Items.insert({
name: "Silver piece",
@@ -136,8 +185,8 @@ Characters.after.insert(function (userId, char) {
color: "q",
parent: {
id: containerId,
collection: "Containers"
}
collection: "Containers",
},
});
Items.insert({
name: "Copper piece",
@@ -149,8 +198,8 @@ Characters.after.insert(function (userId, char) {
color: "s",
parent: {
id: containerId,
collection: "Containers"
}
collection: "Containers",
},
});
}
});

View File

@@ -4,17 +4,17 @@ Schema.User = new SimpleSchema({
username: {
type: String,
regEx: /^[a-z0-9A-Z_]{3,15}$/,
optional: true
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
optional: true,
},
"emails.$.address": {
type: String,
regEx: SimpleSchema.RegEx.Email
regEx: SimpleSchema.RegEx.Email,
},
"emails.$.verified": {
type: Boolean
@@ -25,20 +25,20 @@ Schema.User = new SimpleSchema({
services: {
type: Object,
optional: true,
blackbox: true
blackbox: true,
},
roles: {
type: [String],
optional: true
}
optional: true,
},
});
Meteor.users.attachSchema(Schema.User);
Meteor.users.allow({
update: function (userId, doc, fields, modifier) {
update: function(userId, doc, fields, modifier) {
return userId === doc._id &&
fields.length === 1 &&
fields[0] === 'username';
fields[0] === "username";
}
});

View File

@@ -1,71 +1,59 @@
Router.configure({
loadingTemplate: 'loading',
layoutTemplate: 'layout'
loadingTemplate: "loading",
layoutTemplate: "layout",
});
Router.map( function () {
/*
this.route('home', {
path: '/',
waitOn: function(){
return Meteor.subscribe("characterList", Meteor.userId());
},
data: {
characters: function(){
return Characters.find({}, {fields: {_id: 1}});
}
}
});*/ //add a home route and change characterList route
this.route('characterList', {
path: '/',
waitOn: function(){
return Meteor.subscribe("characterList", Meteor.userId());
},
data: {
characters: function(){
return Characters.find({}, {fields: {_id: 1}});
}
}
Router.map(function() {
this.route("/", {
name: "home",
});
this.route('characterSheet', {
path: '/character/:_id',
this.route("characterList", {
path: "/characterList",
waitOn: function(){
return Meteor.subscribe("characterList", Meteor.userId());
},
data: {
characters: function(){
return Characters.find({}, {fields: {_id: 1}});
}
},
onAfterAction: function() {
document.title = appName;
},
});
this.route("characterSheet", {
path: "/character/:_id",
waitOn: function(){
return [
Meteor.subscribe("singleCharacter", this.params._id, Meteor.userId()),
];
},
data: function() {
var data = Characters.findOne({_id: this.params._id}, {fields: {_id: 1, name: 1, color: 1}});
var data = Characters.findOne(
{_id: this.params._id},
{fields: {_id: 1, name: 1, color: 1}}
);
return data;
}
},
onAfterAction: function() {
var char = Characters.findOne({_id: this.params._id}, {fields: {name: 1}});
var name = char && char.name;
if (name){
document.title = name;
}
},
});
this.route('inventory', {
path: '/inventory/:_id',
data: {
containers: function() {
var containers = Containers.find({owner: data._id}, {fields: {_id: 1}});
return containers;
},
}
this.route("loading", {
path: "/loading"
});
this.route('item', {
path: '/item/:_id',
data: function() {
var data = Items.findOne({_id: this.params._id});
return data;
}
this.route("profile", {
path: "/account",
onAfterAction: function() {
document.title = appName + " Account";
},
});
this.route('loading', {
path: '/loading'
});
this.route('profile', {
path: '/account'
});
});
});

View File

@@ -12,10 +12,10 @@ this.GlobalUI = (function() {
return toast.show();
};
GlobalUI.deletedToast = function(id, collection, itemName){
GlobalUI.deletedToast = function(id, collection, itemName) {
GlobalUI.toast({
text: itemName? itemName + " deleted" : "Deleted item from" + collection,
template: "undoToast",
text: itemName ? itemName + " deleted" : "Deleted item from" + collection,
template: "undoToast",
data: {
id: id,
collection: collection
@@ -23,7 +23,7 @@ this.GlobalUI = (function() {
});
};
GlobalUI.setDialog = function(opts){
GlobalUI.setDialog = function(opts) {
this.dialog = $("[global-dialog]")[0];
Session.set("global.ui.dialogHeader", opts.heading);
Session.set("global.ui.dialogData", opts.data);
@@ -58,7 +58,7 @@ this.GlobalUI = (function() {
Session.set("global.ui.detailShow", true);
};
//if setting the detail rather than showing it,
//if setting the detail rather than showing it,
//the template should contain the following in template.rendered
//
//if (!this.alreadyRendered){
@@ -69,30 +69,30 @@ this.GlobalUI = (function() {
Session.set("global.ui.detailData", opts.data);
Session.set("global.ui.detailTemplate", opts.template);
Session.set("global.ui.detailHeroId", opts.heroId);
if(!!(window.history && window.history.pushState)){
if (window.history && window.history.pushState) {
history.replaceState({detail: "closed", opts: opts}, "Detail Dialog");
history.pushState({detail: "opened", opts: opts}, "Detail Dialog");
}
};
var throttleBack = _.throttle(function(){
var throttleBack = _.throttle(function() {
history.back();
}, 800, {trailing: false});
GlobalUI.closeDetail = function(){
if(!!(window.history && window.history.pushState)){
GlobalUI.closeDetail = function() {
if (!!(window.history && window.history.pushState)) {
throttleBack();
} else{
} else {
Session.set("global.ui.detailShow", false);
}
};
GlobalUI.popStateHandler = function(e){
GlobalUI.popStateHandler = function(e) {
var state = e.originalEvent.state;
if(state) {
if(state.detail === "closed"){
if (state) {
if (state.detail === "closed") {
Session.set("global.ui.detailShow", false);
} else if(state.detail === "opened"){
} else if (state.detail === "opened") {
var opts = state.opts;
Session.set("global.ui.detailData", opts.data);
Session.set("global.ui.detailTemplate", opts.template);
@@ -115,22 +115,22 @@ Template.layout.helpers({
globalDialogFullOnMobile: function() {
return Session.get("global.ui.dialogFullOnMobile");
},
globalDialogHeader: function(){
globalDialogHeader: function() {
return Session.get("global.ui.dialogHeader");
},
globalDetailSelected: function(){
globalDetailSelected: function() {
return Session.get("global.ui.detailShow") ? 1 : 0;
},
globalDetailTemplate: function(){
globalDetailTemplate: function() {
return Session.get("global.ui.detailTemplate");
},
globalDetailData: function(){
globalDetailData: function() {
return Session.get("global.ui.detailData");
},
globalToastTemplate: function(){
globalToastTemplate: function() {
return Session.get("global.ui.toastTemplate");
},
globalToastData: function(){
globalToastData: function() {
return Session.get("global.ui.toastData");
}
});
@@ -143,7 +143,7 @@ Template.layout.events({
},
"core-animated-pages-transition-end [detail-pages]": function(e) {
var detailOpened = Session.get("global.ui.detailShow");
if(!detailOpened){
if (!detailOpened) {
Session.set("global.ui.detailData", null);
Session.set("global.ui.detailTemplate", null);
Session.set("global.ui.detailHeroId", null);
@@ -151,14 +151,14 @@ Template.layout.events({
},
"core-animated-pages-transition-prepare": function(e) {
var detailOpened = Session.get("global.ui.detailShow");
if(detailOpened) {
if (detailOpened) {
//set up the transition
} else {
//undo hack
$("#mainContentSection").removeClass("fake-selected");
}
},
"tap #screenDim": function(e){
"tap #screenDim": function(e) {
GlobalUI.closeDetail();
}
});

View File

@@ -1,3 +1,3 @@
Template.registerHelper("canCast", function(){
Template.registerHelper("canCast", function() {
return Characters.find({_id: this._id, spells: {$size: 0}}).count() === 0;
});
});

View File

@@ -1,7 +1,11 @@
Template.registerHelper("colorClass", function(color){
return color? getColorClass(color) : getColorClass(this.color);
Template.registerHelper("colorClass", function(color) {
if (color) {
return getColorClass(color);
} else if (this.color) {
return getColorClass(this.color);
}
});
Template.registerHelper("hexColor", function(color){
Template.registerHelper("hexColor", function(color) {
return getHexColor(color);
});

View File

@@ -1,7 +1,9 @@
Template.registerHelper("detailHero", function(suffix, givenId){
Template.registerHelper("detailHero", function(suffix, givenId) {
var id = givenId || this._id;
if(suffix) id += suffix;
if ( Session.equals("global.ui.detailHeroId", id) ) {
if (suffix) {
id += suffix;
}
if (Session.equals("global.ui.detailHeroId", id)) {
return "hero";
}
});
});

View File

@@ -1,124 +0,0 @@
/*Template.registerHelper(function("effectList", */var disabled = function(charId, fieldName){
var obj = Characters.findOne(charId, {fields: {_id: 1}}).getField(fieldName);
var result = $("<div>");
if(_.has(obj, "conditional") && obj.conditional.length > 0){
_.each(obj.conditional, function(cond){
var c = $("<div>")
.append("* ")
.append(evaluateString(charId, cond.name));
result.append(c);
});
}
if(obj.base > 0){
var c = $("<div>")
.append($("<span>").addClass("auditValue").append(obj.base))
.append("Base");
result.append(c);
}
if(_.has(obj, "proficiency") && obj.proficiency.length > 0){
var highestProf = {};
_.each(obj.proficiency, function(prof, i){
var value = evaluateEffect(charId, prof)
if(i === 0 || value > highestProf.value){
highestProf.value = value;
highestProf.name = prof.name;
highestProf.calculation = prof.calculation;
}
});
var c = $("<div>")
.append($("<span>").addClass("auditValue").append(highestProf.value).append(" x Proficiency Bonus"))
.append(highestProf.name);
result.append(c);
}
if(_.has(obj, "add") && obj.add.length > 0){
_.each(obj.add, function(a){
var value = signedString(evaluateEffect(charId, a));
var c = $("<div>")
.append($("<span>").addClass("auditValue").append(value))
.append(a.name);
result.append(c);
});
}
if(_.has(obj, "mul") && obj.mul.length > 0){
_.each(obj.mul, function(a){
var value = signedString(evaluateEffect(charId, a));
var c = $("<div>")
.append($("<span>").addClass("auditValue").append("&times;").append(value))
.append(a.name);
result.append(c);
});
}
if(_.has(obj, "min") && obj.min.length > 0){
var highestMin = {};
_.each(obj.min, function(m, i){
var value = evaluateEffect(charId, m)
if(i === 0 || value > highestMin.value){
highestMin.value = value;
highestMin.name = m.name;
highestMin.calculation = m.calculation;
}
});
var c = $("<div>")
.append($("<span>").addClass("auditValue").append(highestMin.value).append(" Minimum"))
.append(highestMin.name);
result.append(c);
}
if(_.has(obj, "max") && obj.max.length > 0){
var lowestMax = {};
_.each(obj.max, function(m, i){
var value = evaluateEffect(charId, m)
if(i === 0 || value < lowestMax.value){
lowestMax.value = value;
lowestMax.name = m.name;
lowestMax.calculation = m.calculation;
}
});
var c = $("<div>")
.append($("<span>").addClass("auditValue").append(lowestMax.value).append(" Maximum"))
.append(lowestMax.name);
result.append(c);
}
if(obj.base < 0){
var c = $("<div>")
.append($("<span>").addClass("auditValue").append(obj.base))
.append("Damage");
result.append(c);
}
if(_.has(obj, "advantage") && obj.advantage.length > 0){
_.each(obj.advantage, function(adv){
var c = $("<div>")
.append($("<span>").addClass("auditValue").append("Advantage"))
.append(adv.name);
result.append(c);
})
}
if(_.has(obj, "disadvantage") && obj.disadvantage.length > 0){
_.each(obj.disadvantage, function(disadv){
var c = $("<div>")
.append($("<span>").addClass("auditValue").append("Disadvantage"))
.append(disadv.name);
result.append(c);
})
}
if(_.has(obj, "fail") && obj.fail.length > 0){
_.each(obj.fail, function(f){
var c = $("<div>")
.append($("<span>").addClass("auditValue").append("Fail"))
.append(f.name);
result.append(c);
})
}
var string = result.html()
if (_.isString(string)) return Spacebars.SafeString(string);
return string;
};

View File

@@ -1,25 +1,25 @@
Template.registerHelper("evaluate", function(charId, string){
Template.registerHelper("evaluate", function(charId, string) {
return evaluate(charId, string);
});
Template.registerHelper("evaluateSigned", function(charId, string){
Template.registerHelper("evaluateSigned", function(charId, string) {
var number = evaluate(charId, string);
if(_.isFinite(number)){
return number > 0? "+" + number : "" + number;
} else{
if (_.isFinite(number)) {
return number > 0 ? "+" + number : "" + number;
} else {
return number;
}
});
Template.registerHelper("evaluateSignedSpaced", function(charId, string){
Template.registerHelper("evaluateSignedSpaced", function(charId, string) {
var number = evaluate(charId, string);
if(_.isFinite(number)){
return number > 0? "+ " + number : "- " + (-1 * number);
} else{
if (_.isFinite(number)) {
return number > 0 ? "+ " + number : "- " + (-1 * number);
} else {
return number;
}
});
Template.registerHelper("evaluateString", function(charId, string){
Template.registerHelper("evaluateString", function(charId, string) {
return evaluateString(charId, string);
});

View File

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

View File

@@ -1,6 +1,6 @@
Template.registerHelper("round", function(value, decimalPlaces){
Template.registerHelper("round", function(value, decimalPlaces) {
decimalPlaces = +decimalPlaces || 2;
var num = +value;
var tens = Math.pow(10, decimalPlaces)
var tens = Math.pow(10, decimalPlaces);
return Math.round(num * tens) / tens;
});
});

View File

@@ -1,3 +1,3 @@
Template.registerHelper("session", function(key){
Template.registerHelper("session", function(key) {
return Session.get(key);
});
});

View File

@@ -1,3 +1,3 @@
Template.registerHelper("signedString", function(number){
return number > 0? "+" + number : "" + number;
});
Template.registerHelper("signedString", function(number) {
return number >= 0 ? "+" + number : "" + number;
});

View File

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

View File

@@ -33,6 +33,16 @@ 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;
@@ -53,6 +63,10 @@ paper-button {
letter-spacing: 0.010;
}
core-item {
cursor: pointer;
}
.listRow {
height: 32px;
}
@@ -94,7 +108,11 @@ paper-button {
color: rgba(0, 0, 0, 0.54);
}
.statCard, .clickable {
.clickable, .statCard, .abilityMiniCard {
cursor: pointer;
}
.skillRow {
cursor: pointer;
}
@@ -161,6 +179,14 @@ paper-button {
right: 24px;
}
.wideTable td {
padding: 4px 8px 4px 8px;
}
.wideTable table {
padding: 8px;
}
paper-fab-menu /deep/ .container {
padding: 24px !important;
}
@@ -178,6 +204,8 @@ paper-slider {
}
.whiteTop {
cursor: initial;
border-bottom: black solid 0.5px;
border-bottom: rgba(0,0,0,0.12) solid 1px;
background: white;
padding: 16px;
@@ -209,6 +237,20 @@ paper-slider {
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;
}
@@ -220,3 +262,7 @@ paper-slider {
.preline {
white-space: pre-line;
}
.prewrap{
white-space: pre-wrap;
}

View File

@@ -109,6 +109,5 @@ html /deep/ .white-text{
.white54 {
color: #eee;
color: rgba(255,255,255,0.54)
color: rgba(255,255,255,0.54);
}

View File

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

View File

@@ -1,4 +1,4 @@
<!--needs to be given charId, parentId and type-->
<!--needs to be given charId, parentId and parentCollection-->
<template name="attackEditList">
{{#if attacks.count}}
<hr style="margin: 16px 0 16px 0;">

View File

@@ -1,19 +1,18 @@
Template.attackEditList.helpers({
attacks: function(){
var cursor = Attacks.find({"parent.id": this.parentId, type: this.type, charId: this.charId});
attacks: function() {
var cursor = Attacks.find({"parent.id": this.parentId, charId: this.charId});
return cursor;
}
});
Template.attackEditList.events({
"tap #addAttackButton": function(){
"tap #addAttackButton": function() {
Attacks.insert({
charId: this.charId,
parent: {
id: this.parentId,
collection: this.parentCollection
},
type: this.type,
}
});
},
});

View File

@@ -0,0 +1,17 @@
<template name="attackView">
<div class="attackView" layout horizontal>
<div class="headline rightPadded" layout horizontal center>
{{evaluateSigned charId attackBonus}}
</div>
<div layout vertical>
<div>
{{damageDice}}&nbsp;{{{evaluateSignedSpaced charId damageBonus}}}&nbsp;{{damageType}}
</div>
{{#if details}}
<div class="caption">
{{details}}
</div>
{{/if}}
</div>
</div>
</template>

View File

@@ -0,0 +1,11 @@
<template name="attacksViewList">
{{#if attacks.count}}
<hr style="margin: 16px 0 16px 0;">
<div class="attacks">
<h2 class="spaceAfter">Attacks</h2>
{{#each attacks}}
{{> attackView}}
{{/each}}
</div>
{{/if}}
</template>

View File

@@ -0,0 +1,5 @@
Template.attacksViewList.helpers({
attacks: function() {
return Attacks.find({"parent.id": this.parentId, charId: this.charId});
}
});

View File

@@ -1,3 +1,3 @@
Template.characterSettings.events({
});

View File

@@ -1,23 +1,25 @@
Template.deleteCharacterConfirmation.onCreated(function(){
Template.deleteCharacterConfirmation.onCreated(function() {
this.canDelete = new ReactiveVar(false);
});
Template.deleteCharacterConfirmation.helpers({
cantDelete: function(){
cantDelete: function() {
return !Template.instance().canDelete.get();
},
getStyle: function(){
if(Template.instance().canDelete.get()) return "background: #d23f31; color: white;";
getStyle: function() {
if (Template.instance().canDelete.get()) {
return "background: #d23f31; color: white;";
}
}
});
Template.deleteCharacterConfirmation.events({
"change #nameInput, input #nameInput": function(event, instance){
"change #nameInput, input #nameInput": function(event, instance) {
var canDel = instance.find("#nameInput").value === this.name;
instance.canDelete.set(canDel);
},
"tap #deleteButton": function(event, instance){
if(instance.find("#nameInput").value === this.name){
"tap #deleteButton": function(event, instance) {
if (instance.find("#nameInput").value === this.name) {
GlobalUI.closeDialog();
Router.go("/");
Characters.remove(this._id);

View File

@@ -1,26 +1,42 @@
<template name="shareDialog">
<div>
<div style="width: 360px;">
<div>
<div class="subhead">
Can View
</div>
{{#each readers}}
{{this}}<br>
{{/each}}
<div class="subhead">
Can Edit
</div>
{{#each writers}}
{{this}}<br>
{{/each}}
{{#if readers.count}}
<div style="font-weight: 500;">
Can View
</div>
{{#each readers}}
<div layout horizontal center>
<div flex>{{username}}</div>
<paper-icon-button class="deleteShare" icon="delete"></paper-icon-button>
</div>
{{/each}}
{{/if}}
{{#if writers.count}}
<div style="font-weight: 500;">
Can Edit
</div>
{{#each writers}}
<div layout horizontal center>
<div flex>{{username}}</div>
<paper-icon-button class="deleteShare" icon="delete"></paper-icon-button>
</div>
{{/each}}
{{/if}}
</div>
<paper-input id="userNameOrEmailInput" label="Username or email" floatinglabel></paper-input><br>
{{#if userFindError}}<p style="color: red;">{{userFindError}}</p>{{/if}}
<div layout horizontal center>
<paper-input flex id="userNameOrEmailInput" label="Username or email" floatinglabel></paper-input>
<paper-button id="shareButton"
class="red-button"
style="width: 80px; height: 37px; margin-top: 16px;"
raised
disabled={{shareButtonDisabled}}>Share</paper-button>
</div>
<p style="color: red;">{{userFindError}}</p>
<paper-radio-group id="accessLevelMenu" selected="read">
<paper-radio-button name="read" label="View Only"></paper-radio-button>
<paper-radio-button name="write" label="Can Edit"></paper-radio-button>
</paper-radio-group>
<br>
<paper-button id="shareButton" class="red-button" raised disabled={{shareButtonDisabled}}>Share</paper-button>
</div>
<paper-button id="doneButton" affirmative> Done </paper-button>
</template>

View File

@@ -5,11 +5,11 @@ Template.shareDialog.onCreated(function(){
Template.shareDialog.helpers({
readers: function(){
var char = Characters.findOne(this._id, {fields: {readers: 1}});
return char && char.readers;
return Meteor.users.find({_id: {$in: char.readers}});
},
writers: function(){
var char = Characters.findOne(this._id, {fields: {writers: 1}});
return char && char.writers;
return Meteor.users.find({_id: {$in: char.writers}});
},
shareButtonDisabled: function(){
return !Template.instance().userId.get();
@@ -18,17 +18,18 @@ Template.shareDialog.helpers({
if (!Template.instance().userId.get()){
return "User not found";
}
}
},
});
Template.shareDialog.events({
"input #userNameOrEmailInput, change #userNameOrEmailInput": function(event, instance){
"input #userNameOrEmailInput":
function(event, instance){
var userName = instance.find("#userNameOrEmailInput").value;
instance.userId.set(undefined);
Meteor.call("getUserId", userName, function (err, result) {
if(err){
Meteor.call("getUserId", userName, function(err, result) {
if (err){
console.error(err);
} else{
} else {
console.log(result);
instance.userId.set(result);
}
@@ -37,19 +38,24 @@ Template.shareDialog.events({
"tap #shareButton": function(event, instance){
var self = this;
var permission = instance.find("#accessLevelMenu").selected;
if(!permission) throw "no permission set";
if (!permission) throw "no permission set";
var userId = instance.userId.get();
if(!userId) return;
if(permission === "write"){
if (!userId) return;
if (permission === "write"){
Characters.update(self._id, {
$addToSet: {writers: userId},
$pull: {readers: userId}
$pull: {readers: userId},
});
} else {
Characters.update(self._id, {
$addToSet: {readers: userId},
$pull: {writers: userId}
$pull: {writers: userId},
});
}
}
},
"tap .deleteShare": function(event, instance) {
Characters.update(instance.data._id, {
$pull: {writers: this._id, readers: this._id}
});
},
});

View File

@@ -1,5 +1,4 @@
paper-tabs, core-toolbar {
background-color: #795548;
box-shadow: 0px 3px 2px rgba(0, 0, 0, 0.2);
}
@@ -21,18 +20,9 @@ paper-tabs ::shadow #ink {
paper-tabs.transparent-brown {
background-color: transparent;
color: #795548;
box-shadow: none;
}
paper-tabs.transparent-brown::shadow #selectionBar {
background-color: #795548;
}
paper-tabs.transparent-brown paper-tab::shadow #ink {
color: #795548;
}
core-toolbar.medium-tall {
height: 108px;
}

View File

@@ -9,7 +9,7 @@
{{> colorDropdown}}
</div>
<paper-menu-button>
<paper-icon-button icon="menu" noink></paper-icon-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>

View File

@@ -52,15 +52,3 @@
</paper-dropdown>
</paper-dropdown-menu>
</template>
<template name="proficiencyEffectValue">
<paper-dropdown-menu class="proficiencyDropDown" label="Proficiency" flex>
<paper-dropdown layered class="dropdown">
<core-menu class="menu proficiencyMenu" selected={{value}}>
<paper-item name="1">Proficient</paper-item>
<paper-item name="0.5">Half Prof. Bonus</paper-item>
<paper-item name="2">Double Prof. Bonus</paper-item>
</core-menu>
</paper-dropdown>
</paper-dropdown-menu>
</template>

View File

@@ -82,7 +82,6 @@ var attributeOperations = [
{name: "Max", operation: "max"}
];
var skillOperations = [
{name: "Proficiency", operation: "proficiency"},
{name: "Add", operation: "add"},
{name: "Multiply", operation: "mul"},
{name: "Min", operation: "min"},
@@ -112,8 +111,8 @@ Template.effectEdit.helpers({
},
operations: function(){
var group = Template.instance().selectedStatGroup.get();
if(group === "Weakness/Resistance") return null;
if(group === "Saving Throws" || group === "Skills"){
if (group === "Weakness/Resistance") return null;
if (group === "Saving Throws" || group === "Skills"){
return skillOperations;
} else {
return attributeOperations;
@@ -122,14 +121,12 @@ Template.effectEdit.helpers({
effectValueTemplate: function(){
//resistance/vulnerability template
var group = Template.instance().selectedStatGroup.get();
if(group === "Weakness/Resistance") return "multiplierEffectValue";
if (group === "Weakness/Resistance") return "multiplierEffectValue";
var op = this.operation;
if(!op) return null;
if (!op) return null;
//operations that don't need templates
if(op === "advantage" || op === "disadvantage" || op === "fail") return null;
//proficiency template
if(op === "proficiency") return "proficiencyEffectValue";
if (op === "advantage" || op === "disadvantage" || op === "fail") return null;
//default template
return "regularEffectValue";
@@ -149,52 +146,52 @@ Template.effectEdit.events({
},
"core-select .statGroupDropDown": function(event, instance){
var detail = event.originalEvent.detail;
if(!detail.isSelected) return;
if (!detail.isSelected) return;
var groupName = detail.item.getAttribute("name");
var oldName = Template.instance().selectedStatGroup.get();
if(oldName != groupName){
if (oldName != groupName){
instance.selectedStatGroup.set(groupName);
if(groupName === "Skills" || groupName === "Saving Throws"){
Effects.update(this._id, {$set: {operation: "proficiency", value: 1, calculation: ""}, $unset: {stat: ""} });
} else if(groupName === "Weakness/Resistance"){
Effects.update(this._id, {$set: {value: 0.5, calculation: "", operation: "mul"}, $unset: {stat: ""} });
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: ""} });
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;
if (!detail.isSelected) return;
var statName = detail.item.getAttribute("name");
if (statName == this.stat) return;
Effects.update(this._id, {$set: {stat: statName}});
},
"core-select .operationDropDown": function(event){
var detail = event.originalEvent.detail;
if(!detail.isSelected) return;
if (!detail.isSelected) return;
var opName = detail.item.getAttribute("name");
if (opName == this.operation) return;
Effects.update(this._id, {$set: {operation: opName}});
},
"core-select .damageMultiplierDropDown": function(event){
var detail = event.originalEvent.detail;
if(!detail.isSelected) return;
if (!detail.isSelected) return;
var value = +detail.item.getAttribute("name");
if (value == this.value) return;
Effects.update(this._id, {$set: {value: value, calculation: "", operation: "mul"}});
},
"core-select .proficiencyDropDown": function(event){
var detail = event.originalEvent.detail;
if(!detail.isSelected) return;
var value = +detail.item.getAttribute("name");
if (value == this.value) return;
Effects.update(this._id, {$set: {value: value, calculation: ""}});
Effects.update(this._id, {$set: {
value: value,
calculation: "",
operation: "mul"
}});
},
"change .effectValueInput": function(event){
var value = event.currentTarget.value;
var numValue = +value;
if(_.isFinite(numValue)){
if (_.isFinite(numValue)){
if (this.value === numValue) return;
Effects.update(this._id, {$set: {value: numValue, calculation: ""}});
} else if (_.isString(value)){

View File

@@ -1,12 +1,6 @@
<template name="effectView">
<div class="effectView" layout horizontal center>
{{#with statName}}
<div flex>{{statName}}</div>
{{else}}
<div flex>{{sourceName}}</div>
{{/with}}
<div>{{operationName}}</div>
<div>{{statValue}}</div>
<br>
</div>
<tr>
<td>{{statName}}</td>
<td>{{operationName}}{{statValue}}</td>
</tr>
</template>

View File

@@ -6,35 +6,35 @@ var stats = {
"intelligence":{"name":"Intelligence"},
"wisdom":{"name":"Wisdom"},
"charisma":{"name":"Charisma"},
"strengthSave":{"name":"Strength Save",},
"dexteritySave":{"name":"Dexterity Save",},
"constitutionSave":{"name":"Constitution Save",},
"intelligenceSave":{"name":"Intelligence Save",},
"wisdomSave":{"name":"Wisdom Save",},
"charismaSave":{"name":"Charisma Save",},
"acrobatics":{"name":"Acrobatics",},
"animalHandling":{"name":"Animal Handling",},
"arcana":{"name":"Arcana",},
"athletics":{"name":"Athletics",},
"deception":{"name":"Deception",},
"history":{"name":"History",},
"insight":{"name":"Insight",},
"intimidation":{"name":"Intimidation",},
"investigation":{"name":"Investigation",},
"medicine":{"name":"Medicine",},
"nature":{"name":"Nature",},
"perception":{"name":"Perception",},
"performance":{"name":"Performance",},
"persuasion":{"name":"Persuasion",},
"religion":{"name":"Religion",},
"sleightOfHand":{"name":"Sleight of Hand",},
"stealth":{"name":"Stealth",},
"survival":{"name":"Survival",},
"initiative":{"name":"Initiative",},
"strengthSave":{"name":"Strength Save"},
"dexteritySave":{"name":"Dexterity Save"},
"constitutionSave":{"name":"Constitution Save"},
"intelligenceSave":{"name":"Intelligence Save"},
"wisdomSave":{"name":"Wisdom Save"},
"charismaSave":{"name":"Charisma Save"},
"acrobatics":{"name":"Acrobatics"},
"animalHandling":{"name":"Animal Handling"},
"arcana":{"name":"Arcana"},
"athletics":{"name":"Athletics"},
"deception":{"name":"Deception"},
"history":{"name":"History"},
"insight":{"name":"Insight"},
"intimidation":{"name":"Intimidation"},
"investigation":{"name":"Investigation"},
"medicine":{"name":"Medicine"},
"nature":{"name":"Nature"},
"perception":{"name":"Perception"},
"performance":{"name":"Performance"},
"persuasion":{"name":"Persuasion"},
"religion":{"name":"Religion"},
"sleightOfHand":{"name":"Sleight of Hand"},
"stealth":{"name":"Stealth"},
"survival":{"name":"Survival"},
"initiative":{"name":"Initiative"},
"hitPoints":{"name":"Hit Points"},
"armor":{"name":"Armor"},
"dexterityArmor":{"name":"Dexterity Armor Bonus"}
,"speed":{"name":"Speed"},
"dexterityArmor":{"name":"Dexterity Armor Bonus"},
"speed":{"name":"Speed"},
"proficiencyBonus":{"name":"Proficiency Bonus"},
"ki":{"name":"Ki Points"},
"sorceryPoints":{"name":"Sorcery Points"},
@@ -55,33 +55,56 @@ var stats = {
"d8HitDice":{"name":"d8 Hit Dice"},
"d10HitDice":{"name":"d10 Hit Dice"},
"d12HitDice":{"name":"d12 Hit Dice"},
"acidMultiplier":{"name":"Acid", "group": "Weakness/Resistance"},
"bludgeoningMultiplier":{"name":"Bludgeoning", "group": "Weakness/Resistance"},
"coldMultiplier":{"name":"Cold", "group": "Weakness/Resistance"},
"fireMultiplier":{"name":"Fire", "group": "Weakness/Resistance"},
"forceMultiplier":{"name":"Force", "group": "Weakness/Resistance"},
"lightningMultiplier":{"name":"Lightning", "group": "Weakness/Resistance"},
"necroticMultiplier":{"name":"Necrotic", "group": "Weakness/Resistance"},
"piercingMultiplier":{"name":"Piercing", "group": "Weakness/Resistance"},
"poisonMultiplier":{"name":"Poison", "group": "Weakness/Resistance"},
"psychicMultiplier":{"name":"Psychic", "group": "Weakness/Resistance"},
"radiantMultiplier":{"name":"Radiant", "group": "Weakness/Resistance"},
"slashingMultiplier":{"name":"Slashing", "group": "Weakness/Resistance"},
"thunderMultiplier":{"name":"Thunder", "group": "Weakness/Resistance"}
"acidMultiplier":{"name":"Acid damage", "group": "Weakness/Resistance"},
"bludgeoningMultiplier":{
"name":"Bludgeoning damage", "group": "Weakness/Resistance"
},
"coldMultiplier":{
"name":"Cold damage", "group": "Weakness/Resistance"
},
"fireMultiplier":{
"name":"Fire damage", "group": "Weakness/Resistance"
},
"forceMultiplier":{
"name":"Force damage", "group": "Weakness/Resistance"
},
"lightningMultiplier":{
"name":"Lightning damage", "group": "Weakness/Resistance"
},
"necroticMultiplier":{
"name":"Necrotic damage", "group": "Weakness/Resistance"
},
"piercingMultiplier":{
"name":"Piercing damage", "group": "Weakness/Resistance"
},
"poisonMultiplier":{
"name":"Poison damage", "group": "Weakness/Resistance"
},
"psychicMultiplier":{
"name":"Psychic damage", "group": "Weakness/Resistance"
},
"radiantMultiplier":{
"name":"Radiant damage", "group": "Weakness/Resistance"
},
"slashingMultiplier":{
"name":"Slashing damage", "group": "Weakness/Resistance"
},
"thunderMultiplier":{
"name":"Thunder damage", "group": "Weakness/Resistance"
},
};
var operations = {
base: {name: "Base Value"},
base: {name: "Base Value: "},
proficiency: {name: "Proficiency"},
add: {name: "Add"},
mul: {name: "Multiply"},
min: {name: "Min"},
max: {name: "Max"},
add: {name: "+"},
mul: {name: "x"},
min: {name: "Min: "},
max: {name: "Max: "},
advantage: {name: "Advantage"},
disadvantage: {name: "Disadvantage"},
passiveAdd: {name: "Passive Bonus"},
passiveAdd: {name: "Passive Bonus: "},
fail: {name: "Automatically Fail"},
conditional: {name: "Conditional Benefit"}
};
Template.effectView.helpers({
@@ -100,22 +123,23 @@ Template.effectView.helpers({
case "Characters":
return Characters.findOne(this.charId, {fields: {race: 1}}).race;
default:
return "Inate"
return "Inate";
}
},
statName: function(){
return stats[this.stat] && stats[this.stat].name || "No Stat"
return stats[this.stat] && stats[this.stat].name || "No Stat";
},
operationName: function(){
if(this.operation === "proficiency") return null;
if(this.operation === "proficiency" ||
this.operation === "conditional") return null;
if(stats[this.stat].group === "Weakness/Resistance") return null;
return operations[this.operation] && operations[this.operation].name || "No Operation"
if(this.operation === "add" && evaluateEffect(this.charId, this) < 0) return null;
return operations[this.operation] && operations[this.operation].name || "No Operation";
},
statValue: function(){
if(this.operation === "advantage" ||
this.operation === "disadvantage" ||
this.operation === "fail" ||
this.operation === "conditional"){
this.operation === "fail"){
return null;
}
if(this.operation === "proficiency"){
@@ -123,11 +147,16 @@ Template.effectView.helpers({
if(this.value == 1 || this.calculation == 1) return "Proficiency";
if(this.value == 2 || this.calculation == 2) return "Double Proficiency";
}
if(stats[this.stat].group === "Weakness/Resistance"){
if(this.value == 0.5 || this.calculation == 0.5) return "Resistance";
if(this.value == 2 || this.calculation == 2) return "Vulnerability";
if(this.value == 0 || this.calculation == 0) return "Immunity";
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";
}
var value = evaluateEffect(this.charId, this);
if(_.isNumber(value)) return value;
return this.calculation || this.value;
}
});

View File

@@ -1,7 +1,7 @@
<!--needs to be given charId, parentId, parentCollection and type-->
<!--needs to be given charId, parentId and parentCollection-->
<template name="effectsEditList">
{{#if effects.count}}
<hr style="margin: 16px 0 16px 0;">
<hr class="vertMargin">
<div id="effects">
<h2>Effects</h2>
{{#each effects}}

View File

@@ -1,13 +1,21 @@
Template.effectsEditList.helpers({
effects: function(){
var cursor = Effects.find({"parent.id": this.parentId, "parent.collection": this.parentCollection, type: this.type});
var selector = {
"parent.id": this.parentId,
"parent.collection": this.parentCollection,
"charId": this.charId,
};
if (this.parentGroup){
selector["parent.group"] = this.parentGroup;
}
var cursor = Effects.find(selector);
return cursor;
}
});
Template.effectsEditList.events({
"tap #addEffectButton": function(){
if ( !_.isBoolean(this.enabled) ) {
if (!_.isBoolean(this.enabled)) {
this.enabled = true;
}
Effects.insert({
@@ -15,11 +23,11 @@ Template.effectsEditList.events({
charId: this.charId,
parent: {
id: this.parentId,
collection: this.parentCollection
collection: this.parentCollection,
group: this.parentGroup,
},
operation: "add",
type: this.type,
enabled: this.enabled
enabled: this.enabled,
});
},
});
});

View File

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

View File

@@ -1,10 +1,12 @@
Template.effectsViewList.helpers({
effects: function(){
if(this.parentId){
return Effects.find({"parent.id": this.parentId, type: this.type, charId: this.charId}, {fields: {parent: 0}});
} else if(this.stat){
return Effects.find({charId: this.charId, stat: this.stat});
var selector = {
"parent.id": this.parentId,
"charId": this.charId
};
if (this.parentGroup){
selector["parent.group"] = this.parentGroup;
}
return Effects.find(selector, {fields: {parent: 0}});
}
});

View File

@@ -1,40 +1,85 @@
<template name="featureDialog">
{{#with feature}}
{{#baseDialog title=name class=colorClass}}
<!--name-->
<paper-input id="featureNameInput" label="Name" floatinglabel value={{name}}></paper-input>
<hr style="margin: 16px 0 16px 0;">
<!--description-->
<paper-input-decorator label="Description" floatinglabel layout vertical>
<paper-autogrow-textarea>
<textarea id="featureDescriptionInput" placeholder aria-label="Description" value={{description}}></textarea>
</paper-autogrow-textarea>
</paper-input-decorator>
<hr style="margin: 16px 0 16px 0;">
<div layout horizontal center style="height: 60px;">
<div class="caption">Limit Uses</div>
<paper-toggle-button id="limitUseCheck"
checked={{usesSet}}
role="button"
aria-pressed="false"
tabindex="0"
touch-action="pan-y"
style="margin-left: 8px; margin-right:8px;">
</paper-toggle-button>
{{#if usesSet}}
<paper-input id="usesInput" label="Uses" floatinglabel value={{uses}}></paper-input>
{{/if}}
<paper-dropdown-menu id="enabledDropdown" label="Enable Feature">
<paper-dropdown layered class="dropdown">
<core-menu id="enabledMenu" class="menu" selected={{enabled}} on-tap="onStatMenuTap">
<paper-item name="alwaysEnabled"> Always Enabled </paper-item>
<paper-item name="enabled"> Enabled </paper-item>
<paper-item name="disabled"> Disabled </paper-item>
</core-menu>
</paper-dropdown>
</paper-dropdown-menu>
</div>
{{> effectsEditList parentId=_id parentCollection="Features" charId=charId type="feature" name=name enabled=isEnabled}}
{{#baseDialog title=name class=colorClass startEditing=../startEditing}}
{{> featureDetails}}
{{else}}
{{> featureEdit}}
{{/baseDialog}}
{{/with}}
</template>
</template>
<template name="featureDetails">
{{#if or canEnable hasUses}}
<div layout horizontal center justified wrap>
{{#if canEnable}}
<div>enabled:</div>
<paper-checkbox class="sideMargin" checked={{enabled}}></paper-checkbox>
{{/if}}
{{#if hasUses}}
<div class="subhead" style="margin-right: 16px">
Uses: {{usesLeft}}/{{usesValue}}
</div>
{{/if}}
{{#if hasUses}}
<div layout horizontal>
<paper-button class="useFeature" disabled={{noUsesLeft}}>Use</paper-button>
<paper-button class="resetFeature" disabled={{usesFull}}>Reset</paper-button>
</div>
{{/if}}
</div>
<hr class="vertMargin">
{{/if}}
{{#if description}}
<div class="prewrap">{{description}}</div>
{{/if}}
{{> effectsViewList charId=charId parentId=_id}}
{{> proficiencyViewList charId=charId parentId=_id}}
</template>
<template name="featureEdit">
<!--name-->
<paper-input id="featureNameInput" class="fullwidth" label="Name" floatinglabel value={{name}}></paper-input>
<hr class="vertMargin">
<div layout horizontal center style="height: 60px;">
<paper-dropdown-menu id="enabledDropdown" label="Enable Feature">
<paper-dropdown layered class="dropdown">
<core-menu id="enabledMenu" class="menu" selected={{enabledSelection}} on-tap="onStatMenuTap">
<paper-item name="alwaysEnabled"> Always Enabled </paper-item>
<paper-item name="enabled"> Enabled </paper-item>
<paper-item name="disabled"> Disabled </paper-item>
</core-menu>
</paper-dropdown>
</paper-dropdown-menu>
<div layout horizontal center class="sideMargin">
<div>Limit Uses: </div>
<paper-toggle-button id="limitUseCheck"
class="sideMargin"
checked={{usesSet}}
role="button"
aria-pressed="false"
tabindex="0"
touch-action="pan-y">
</paper-toggle-button>
</div>
{{#if usesSet}}
<paper-input flex id="usesInput" label="Uses" floatinglabel value={{uses}}></paper-input>
{{/if}}
</div>
<hr class="vertMargin">
<!--description-->
<paper-input-decorator label="Description" floatinglabel layout vertical>
<paper-autogrow-textarea>
<textarea id="featureDescriptionInput" placeholder aria-label="Description" value={{description}}></textarea>
</paper-autogrow-textarea>
</paper-input-decorator>
{{> effectsEditList parentId=_id parentCollection="Features" charId=charId name=name enabled=enabled}}
{{> proficiencyEditList parentId=_id parentCollection="Features" charId=charId enabled=enabled}}
</template>

View File

@@ -1,3 +1,9 @@
Template.featureDialog.helpers({
feature: function(){
return Features.findOne(this.featureId);
},
});
Template.featureDialog.events({
"color-change": function(event, instance){
Features.update(instance.data.featureId, {$set: {color: event.color}});
@@ -7,6 +13,61 @@ Template.featureDialog.events({
GlobalUI.deletedToast(instance.data.featureId, "Features", "Feature");
GlobalUI.closeDetail();
},
});
Template.featureDetails.helpers({
or: function(a, b){
return a || b;
},
hasUses: function(){
return this.usesValue() > 0;
},
noUsesLeft: function(){
return this.usesLeft() <= 0;
},
usesFull: function(){
return this.usesLeft() >= this.usesValue();
},
});
Template.featureDetails.events({
"tap .useFeature": function(event){
var featureId = this._id;
Features.update(featureId, {$inc: {used: 1}});
},
"tap .resetFeature": function(event){
var featureId = this._id;
Features.update(featureId, {$set: {used: 0}});
},
"change .enabledCheckbox": function(event){
var enabled = !this.enabled;
Features.update(this._id, {$set: {enabled: enabled}});
},
});
Template.featureEdit.onRendered(function(){
updatePolymerInputs(this);
});
Template.featureEdit.helpers({
usesSet: function(){
return _.isString(this.uses);
},
enabledSelection: function(){
if (this.enabled){
if (this.alwaysEnabled){
return "alwaysEnabled";
} else {
return "enabled";
}
} else if (this.enabled === false){ //make sure it is false, not just falsey
return "disabled";
}
},
});
Template.featureEdit.events({
"change #featureNameInput": function(event){
var name = Template.instance().find("#featureNameInput").value;
Features.update(this._id, {$set: {name: name}});
@@ -18,7 +79,7 @@ Template.featureDialog.events({
"change #limitUseCheck": function(event){
var currentUses = this.uses;
var featureId = this._id;
if( event.target.checked && !_.isString(currentUses) ){
if (event.target.checked && !_.isString(currentUses)){
Features.update(featureId, {$set: {uses: ""}}, {removeEmptyStrings: false});
} else if (!event.target.checked && _.isString(currentUses)){
Features.update(featureId, {$unset: {uses: ""}});
@@ -31,29 +92,18 @@ Template.featureDialog.events({
},
"core-select #enabledDropdown": function(event){
var detail = event.originalEvent.detail;
if(!detail.isSelected) return;
if (!detail.isSelected) return;
var value = detail.item.getAttribute("name");
var setter;
if(value === "enabled"){
if (value === "enabled"){
setter = {enabled: true, alwaysEnabled: false};
} else if (value === "disabled"){
setter = {enabled: false, alwaysEnabled: false};
} else{
} else {
setter = {enabled: true, alwaysEnabled: true};
}
if (setter.enabled === this.enabled && setter.alwaysEnabled === this.alwaysEnabled) return;
if (setter.enabled === this.enabled &&
setter.alwaysEnabled === this.alwaysEnabled) return;
Features.update(this._id, {$set: setter});
},
});
Template.featureDialog.helpers({
feature: function(){
return Features.findOne(this.featureId);
},
usesSet: function(){
return _.isString(this.uses);
},
isEnabled: function(){
return this.enabled !== "disabled";
}
});

View File

@@ -48,19 +48,36 @@
{{/each}}
</div>
</paper-shadow>
<!--Proficiencies-->
<paper-shadow class="card container" hero-id="main" {{detailHero "proficiencies"}}>
<div id="proficiencies"
class="whiteTop"
hero-id="toolbar"
layout horizontal center
{{detailHero "proficiencies"}}>
<div id="proficiencies"
class="whiteTop"
layout horizontal center>
<div class="containerName subhead">Proficiencies</div>
</div>
<div flex class="containerMain padded preline">{{characterProficiencies}}</div>
<div flex class="containerMain listPadded">
{{#if weaponProfs.count}}
<div class="list-subhead" layout horizontal center>Weapons</div>
{{/if}}
{{#each weaponProfs}}
{{> proficiencyListItem}}
{{/each}}
{{#if armorProfs.count}}
<div class="list-subhead" layout horizontal center>Armor</div>
{{/if}}
{{#each armorProfs}}
{{> proficiencyListItem}}
{{/each}}
{{#if toolProfs.count}}
<div class="list-subhead" layout horizontal center>Tools</div>
{{/if}}
{{#each toolProfs}}
{{> proficiencyListItem}}
{{/each}}
</div>
</paper-shadow>
<!--features-->
{{#each features}}
<paper-shadow class="card container featureCard" hero-id="main" {{detailHero}}>
@@ -100,16 +117,16 @@
<template name="resource">
{{#if char.attributeBase name}}
<paper-shadow class="card container" hero-id="main" {{detailHero}} layout horizontal>
<div class="containerLeft {{getColor}}">
<div class="resourceValue">{{char.attributeValue name}}</div>
<!--<div class="resourceMax">{{char.attributeBase name}}</div>-->
<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>
</div>
<div class="resourceValue">{{char.attributeValue name}}</div>
<!--<div class="resourceMax">{{char.attributeBase name}}</div>-->
</div>
<div class="containerRight" flex relative horizontal layout center>
<div class="containerRight clickable" flex relative horizontal layout center>
{{title}}
<paper-ripple fit></paper-ripple>
</div>

View File

@@ -13,42 +13,54 @@ Template.features.helpers({
return this.usesLeft() >= this.usesValue();
},
colorClass: function(){
return getColorClass(this.color)
return getColorClass(this.color);
},
featureOrder: function(){
return _.indexOf(_.keys(colorOptions), this.color);
},
attacks: function(){
return Attacks.find({charId: this._id, enabled: true}, {sort: {color: 1, name: 1}});
},
characterProficiencies: function(){
var char = Characters.findOne(this._id);
return char && char.proficiencies;
return Attacks.find(
{charId: this._id, enabled: true},
{sort: {color: 1, name: 1}});
},
canEnable: function(){
return !this.alwaysEnabled;
}
},
weaponProfs: function(){
return Proficiencies.find({charId: this._id, type: "weapon"});
},
armorProfs: function(){
return Proficiencies.find({charId: this._id, type: "armor"});
},
toolProfs: function(){
return Proficiencies.find({charId: this._id, type: "tool"});
},
});
Template.features.events({
"tap #addFeature": function(event){
var featureId = Features.insert({name: "New Feature", charId: this._id});
var featureId = Features.insert({
name: "New Feature",
charId: this._id,
enabled: true,
alwaysEnabled: true,
});
GlobalUI.setDetail({
template: "featureDialog",
data: {featureId: featureId, charId: this._id},
heroId: featureId
})
data: {featureId: featureId, charId: this._id, startEditing: true},
heroId: featureId,
});
},
"tap #addAttackButton": function(event){
var charId = this._id;
Attacks.insert({
charId: charId
}, function(error, id){
if(!error){
if (!error){
GlobalUI.setDetail({
template: "attackDialog",
data: {attackId: id, charId: charId},
heroId: id
heroId: id,
});
}
});
@@ -59,17 +71,11 @@ Template.features.events({
GlobalUI.setDetail({
template: "featureDialog",
data: {featureId: featureId, charId: charId},
heroId: featureId
heroId: featureId,
});
},
"tap .attack": function(event){
var attackId = this._id;
var charId = Template.parentData()._id;
GlobalUI.setDetail({
template: "attackDialog",
data: {attackId: attackId, charId: charId},
heroId: attackId
});
openParentDialog(this.parent, this.charId, this._id);
},
"tap .useFeature": function(event){
var featureId = this._id;
@@ -79,52 +85,54 @@ Template.features.events({
var featureId = this._id;
Features.update(featureId, {$set: {used: 0}});
},
"tap #proficiencies": function(event){
var charId = this._id;
GlobalUI.setDetail({
template: "textDialog",
data: {charId: charId, field: "proficiencies", title: "Proficiencies", color: "q"},
heroId: this._id + "proficiencies"
});
},
"tap .enabledCheckbox": function(event){
event.stopPropagation();
},
"change .enabledCheckbox": function(event){
var enabled = !this.enabled;
Features.update(this._id, {$set: {enabled: enabled}});
}
},
});
Template.resource.helpers({
cantIncrement: function(){
return !(this.char.attributeValue(this.name) < this.char.attributeBase(this.name));
var baseBigger = this.char.attributeValue(this.name) <
this.char.attributeBase(this.name);
return !baseBigger;
},
cantDecrement: function(){
return !(this.char.attributeValue(this.name) > 0);
var valuePositive = this.char.attributeValue(this.name) > 0;
return !valuePositive;
},
getColor: function(){
if(this.char.attributeValue(this.name) > 0){
if (this.char.attributeValue(this.name) > 0){
return this.color;
} else {
return "grey";
}
}
},
});
Template.resource.events({
"tap .resourceUp": function(event){
if(this.char.attributeValue(this.name) < this.char.attributeBase(this.name)){
if (this.char.attributeValue(this.name) < this.char.attributeBase(this.name)){
var modifier = {$inc: {}};
modifier.$inc[this.name + ".adjustment"] = 1;
Characters.update(this.char._id, modifier, {validate: false});
}
},
"tap .resourceDown": function(event){
if(this.char.attributeValue(this.name) > 0){
if (this.char.attributeValue(this.name) > 0){
var modifier = {$inc: {}};
modifier.$inc[this.name + ".adjustment"] = -1;
Characters.update(this.char._id, modifier, {validate: false});
}
}
},
"tap .containerRight": function(event, instance) {
GlobalUI.setDetail({
template: "attributeDialog",
data: {name: this.title, statName: this.name, charId: this.char._id},
heroId: this.char._id + this.name,
});
},
});

View File

@@ -1,22 +1,46 @@
<template name="containerDialog">
{{#with container}}
{{#baseDialog title=name class=colorClass}}
<!--Name and plural name-->
<paper-input id="containerNameInput" label="Name" floatinglabel value={{name}}></paper-input>
<!--Weight-->
<paper-input-decorator label="Weight" floatinglabel>
<input id="weightInput" type="number" value={{weight}}>
</paper-input-decorator>
<!--Value-->
<paper-input-decorator label="Value" floatinglabel>
<input id="valueInput" type="number" value={{value}}>
</paper-input-decorator>
<!--Description-->
<paper-input-decorator label="Description" floatinglabel layout vertical>
<paper-autogrow-textarea>
<textarea id="containerDescriptionInput" placeholder aria-label="Description" value={{description}}></textarea>
</paper-autogrow-textarea>
</paper-input-decorator>
{{#baseDialog title=name class=colorClass startEditing=../startEditing}}
{{> containerView}}
{{else}}
{{> containerEdit}}
{{/baseDialog}}
{{/with}}
</template>
</template>
<template name="containerEdit">
<paper-input id="containerNameInput"
label="Name"
floatinglabel
value={{name}}></paper-input>
<div layout horizontal around-justified wrap>
<paper-input-decorator label="Weight" floatinglabel>
<input id="weightInput" type="number" value={{weight}}>
</paper-input-decorator>
<paper-input-decorator label="Value" floatinglabel>
<input id="valueInput" type="number" value={{value}}>
</paper-input-decorator>
</div>
<hr class="vertMargin">
<paper-input-decorator label="Description" floatinglabel layout vertical>
<paper-autogrow-textarea>
<textarea id="containerDescriptionInput" placeholder aria-label="Description" value={{description}}></textarea>
</paper-autogrow-textarea>
</paper-input-decorator>
</template>
<template name="containerView">
<div layout horizontal wrap center justified>
<table class="summaryTable fullwidth">
<tr><td>Container</td><td>{{weight}}lbs</td><td>{{longValueString value}}</td></tr>
<tr><td>Contents</td><td>{{contentsWeight}}lbs</td><td>{{longValueString contentsValue}}</td></tr>
<tr class="body2"><td>Total</td><td>{{totalWeight}}lbs</td><td>{{longValueString totalValue}}</td></tr>
</table>
</div>
{{#if description}}
<hr class="vertMargin">
<div class="prewrap">{{description}}</div>
{{/if}}
</template>

View File

@@ -10,11 +10,21 @@ Template.containerDialog.events({
},
"tap #deleteButton": function(event, instance){
Containers.softRemoveNode(instance.data.containerId);
GlobalUI.deletedToast(instance.data.containerId, "Containers", "Container and contents");
GlobalUI.deletedToast(
instance.data.containerId,
"Containers", "Container and contents"
);
GlobalUI.closeDetail();
},
});
Template.containerEdit.onRendered(function(){
updatePolymerInputs(this);
});
Template.containerEdit.events({
//TODO validate input (integer, non-negative, etc) for these inputs and give validation errors
"change #containerNameInput, input #containerNameInput": function(event){
"change #containerNameInput": function(event){
var name = Template.instance().find("#containerNameInput").value;
Containers.update(this._id, {$set: {name: name}});
},
@@ -26,8 +36,8 @@ Template.containerDialog.events({
var value = +Template.instance().find("#valueInput").value;
Containers.update(this._id, {$set: {value: value}});
},
"change #containerDescriptionInput": function(event){
var description = Template.instance().find("#containerDescriptionInput").value;
"change #containerDescriptionInput": function(event, instance){
var description = instance.find("#containerDescriptionInput").value;
Containers.update(this._id, {$set: {description: description}});
}
},
});

View File

@@ -40,7 +40,6 @@ div#stats {
.containerRight {
padding: 16px;
cursor: pointer;
/* same style as subhead */
font-size: 16px;
@@ -73,7 +72,7 @@ div#stats {
}
.resourceButtons {
margin: -16px -16px -16px 8px;
margin: -16px 8px -16px -16px;
align-self: center;
}

View File

@@ -7,16 +7,28 @@ Template.inventory.helpers({
return Containers.find({charId: this._id}, {sort: {color: 1, name: 1}});
},
items: function(charId, containerId){
return Items.find({charId: charId, "parent.id": containerId }, {sort: {color: 1, name: 1}});
return Items.find(
{charId: charId, "parent.id": containerId},
{sort: {color: 1, name: 1}}
);
},
attuned: function(){
return Items.find({ charId: this._id, enabled: true, requiresAttunement: true }, {sort: {color: 1, name: 1}});
return Items.find(
{charId: this._id, enabled: true, requiresAttunement: true},
{sort: {color: 1, name: 1}}
);
},
equipment: function(){
return Items.find({ charId: this._id, enabled: true, requiresAttunement: false }, {sort: {color: 1, name: 1}});
return Items.find(
{charId: this._id, enabled: true, requiresAttunement: false},
{sort: {color: 1, name: 1}}
);
},
carriedItems: function(){
return Items.find({charId: this._id, enabled: false, "parent.id": this._id}, {sort: {color: 1, name: 1}});
return Items.find(
{charId: this._id, enabled: false, "parent.id": this._id},
{sort: {color: 1, name: 1}}
);
},
showAddButtons: function(){
return Template.instance().showAddButtons.get();
@@ -26,45 +38,64 @@ Template.inventory.helpers({
},
netWorth: function(){
var worth = 0;
Items.find({charId: this._id}, {fields: {value : 1, quantity: 1}}).forEach(function(item){
Items.find(
{charId: this._id},
{fields: {value : 1, quantity: 1}}
).forEach(function(item){
worth += item.totalValue();
});
return worth;
},
weightCarried: function(){
var weight = 0;
Containers.find({charId: this._id, isCarried: true}).forEach(function(container){
Containers.find(
{charId: this._id, isCarried: true}
).forEach(function(container){
weight += container.totalWeight();
});
Items.find({charId: this._id, "parent.id": this._id}, {fields: {weight : 1, quantity: 1}}).forEach(function(item){
Items.find(
{charId: this._id, "parent.id": this._id},
{fields: {weight : 1, quantity: 1}}
).forEach(function(item){
weight += item.totalWeight();
});
return weight;
},
equipmentValue: function(){
var value = 0;
Items.find({charId: this._id, enabled: true}, {fields: {value : 1, quantity: 1}}).forEach(function(item){
Items.find(
{charId: this._id, enabled: true},
{fields: {value : 1, quantity: 1}}
).forEach(function(item){
value += item.totalValue();
});
return value;
},
equipmentWeight: function(){
var weight = 0;
Items.find({charId: this._id, enabled: true}, {fields: {weight : 1, quantity: 1}}).forEach(function(item){
Items.find({charId: this._id, enabled: true},
{fields: {weight : 1, quantity: 1}}
).forEach(function(item){
weight += item.totalWeight();
});
return weight;
},
carriedValue: function(){
var value = 0;
Items.find({charId: this._id, enabled: false, "parent.id": this._id}, {fields: {value : 1, quantity: 1}}).forEach(function(item){
Items.find(
{charId: this._id, enabled: false, "parent.id": this._id},
{fields: {value : 1, quantity: 1}}
).forEach(function(item){
value += item.totalValue();
});
return value;
},
carriedWeight: function(){
var weight = 0;
Items.find({charId: this._id, enabled: false, "parent.id": this._id}, {fields: {weight : 1, quantity: 1}}).forEach(function(item){
Items.find(
{charId: this._id, enabled: false, "parent.id": this._id},
{fields: {weight : 1, quantity: 1}}
).forEach(function(item){
weight += item.totalWeight();
});
return weight;
@@ -78,23 +109,31 @@ Template.inventory.events({
charId: charId,
parent:{
id: charId,
collection: "Characters"
}
collection: "Characters",
},
}, function(err, itemId){
if(err) throw err;
if (err) throw err;
GlobalUI.setDetail({
template: "itemDialog",
data: {itemId: itemId, charId: charId},
heroId: itemId
data: {itemId: itemId, charId: charId, startEditing: true},
heroId: itemId,
});
});
},
"tap #addContainer": function(event){
var containerId = Containers.insert({name: "New Container", isCarried: true, charId: this._id});
var containerId = Containers.insert({
name: "New Container",
isCarried: true,
charId: this._id,
});
GlobalUI.setDetail({
template: "containerDialog",
data: {containerId: containerId, charId: this.charId},
heroId: containerId
data: {
containerId: containerId,
charId: this.charId,
startEditing: true,
},
heroId: containerId,
});
},
"tap .inventoryItem": function(event){
@@ -103,14 +142,14 @@ Template.inventory.events({
GlobalUI.setDetail({
template: "itemDialog",
data: {itemId: itemId, charId: charId},
heroId: itemId
heroId: itemId,
});
},
"tap .containerTop": function(event){
GlobalUI.setDetail({
template: "containerDialog",
data: {containerId: this._id, charId: this.charId},
heroId: this._id
heroId: this._id,
});
},
"tap .carriedCheckbox": function(event){
@@ -118,10 +157,10 @@ Template.inventory.events({
},
"change .carriedCheckbox": function(event){
var carried;
if(this.isCarried) carried = false;
if (this.isCarried) carried = false;
else carried = true;
Containers.update(this._id, {$set: {isCarried: carried}});
}
},
});
Template.inventoryItem.helpers({
@@ -129,8 +168,8 @@ Template.inventoryItem.helpers({
return num !== 1;
},
hidden: function(){
return Session.equals("inventory.dragItemId", this._id)? "hidden" : null;
}
return Session.equals("inventory.dragItemId", this._id) ? "hidden" : null;
},
});
Template.layout.events({
@@ -153,17 +192,17 @@ Template.layout.events({
},
"drop .itemContainer": function(event, instacne){
var item = Items.findOne(Session.get("inventory.dragItemId"));
if(event.ctrlKey){
if (event.ctrlKey){
//split the stack to the container
GlobalUI.showDialog({
template: "splitStackDialog",
data: {
id: item._id,
parentCollection: "Containers",
parentId: this._id
}
parentId: this._id,
},
});
} else{
} else {
//move item to the container
item.moveToContainer(this._id);
}
@@ -178,22 +217,22 @@ Template.layout.events({
"drop .carriedContainer": function(event, instance){
var charId = Session.get("inventory.dragItemOriginalCharacter");
var item = Items.findOne(Session.get("inventory.dragItemId"));
if(event.ctrlKey){
if (event.ctrlKey){
//split the stack to the container
GlobalUI.showDialog({
template: "splitStackDialog",
data: {
id: item._id,
parentCollection: "Characters",
parentId: this._id
}
parentId: this._id,
},
});
} else{
} else {
//move item to the character
item.moveToCharacter(this._id);
}
resetInvetorySession();
}
},
});
var resetInvetorySession = function(){

View File

@@ -1,57 +1,96 @@
<template name="itemDialog">
{{#with item}}
{{#baseDialog title=name class=colorClass}}
<!--Name and plural name-->
<paper-input id="itemNameInput" class="fullwidth" label="Name" floatinglabel value={{name}}></paper-input>
{{# if ne1 quantity}}<paper-input id="itemPluralInput" label="Plural Name" floatinglabel value={{plural}}></paper-input>{{/if}}
<!--Container dropdown-->
<paper-dropdown-menu id="containerDropDown" label="Container">
<paper-dropdown layered class="dropdown">
<core-menu class="menu" selected={{parent.id}}>
{{#each containers}}
<paper-item name={{_id}} class="containerMenuItem">{{name}}</paper-item>
{{/each}}
</core-menu>
</paper-dropdown>
</paper-dropdown-menu>
<!--Equipped-->
<div center horizontal layout>
<div flex>Equipped</div>
<paper-toggle-button id="equippedInput"
checked={{enabled}}
role="button"
aria-pressed="false"
tabindex="0"
touch-action="pan-y">
</paper-toggle-button>
</div>
<!--Equipped-->
<div center horizontal layout>
<div flex>Requires Attunement</div>
<paper-checkbox id="attunementCheckbox" checked={{requiresAttunement}}></paper-checkbox>
</div>
<!--Quantity-->
<paper-input-decorator label="Quantity" floatinglabel>
<input id="quantityInput" type="number" value={{quantity}}>
</paper-input-decorator>
<!--Weight-->
<paper-input-decorator label="Weight Each (lbs)" floatinglabel>
<input id="weightInput" type="number" value={{weight}}>
</paper-input-decorator>
<!--Value-->
<paper-input-decorator label="Value Each (GP)" floatinglabel>
<input id="valueInput" type="number" value={{value}}>
</paper-input-decorator>
<!--Description-->
<paper-input-decorator label="Description" floatinglabel layout vertical>
<paper-autogrow-textarea>
<textarea id="itemDescriptionInput" placeholder aria-label="Description" value={{description}}></textarea>
</paper-autogrow-textarea>
</paper-input-decorator>
<!--Effects-->
{{> effectsEditList parentId=_id parentCollection="Items" charId=charId type="equipment" enabled=equipped name=name}}
<!--Attacks-->
{{> attackEditList parentId=_id parentCollection="Items" charId=charId type="equipment" enabled=equipped name=name}}
{{#baseDialog title=itemHeading class=colorClass startEditing=../startEditing}}
{{> itemDetails}}
{{else}}
{{> itemEdit}}
{{/baseDialog}}
{{/with}}
</template>
</template>
<template name="itemDetails">
<div layout horizontal wrap center justified class="headline">
{{#if weight}}<div class="sideMargin">{{totalWeight}}lbs</div>{{/if}}
{{#if value}}<div>{{valueString totalValue}}</div>{{/if}}
</div>
<div layout horizontal wrap class="caption">
{{#if enabled}}<div class="vertMargin" style="margin-right: 16px">Equipped</div>{{/if}}
{{#if requiresAttunement}}<div class="vertMargin">Requires Attunement</div>{{/if}}
</div>
{{#if description}}
<hr class="vertMargin">
<div class="prewrap">{{description}}</div>
{{/if}}
{{> effectsViewList charId=charId parentId=_id}}
{{> attacksViewList charId=charId parentId=_id}}
</template>
<template name="itemEdit">
<paper-input class="fullwidth" id="itemNameInput" label="Name" floatinglabel value={{name}}></paper-input>
<div layout horizontal wrap>
<paper-input-decorator label="Quantity" floatinglabel>
<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}}
</div>
<hr class="vertMargin">
<div layout horizontal wrap justified>
<div center horizontal layout>
<div class="padded">Container</div>
{{> containerDropdown}}
</div>
<div center horizontal layout>
<div class="padded">Equipped</div>
<paper-toggle-button id="equippedInput"
checked={{enabled}}
role="button"
aria-pressed="false"
tabindex="0"
touch-action="pan-y">
</paper-toggle-button>
</div>
<div center horizontal layout>
<div class="padded">Requires Attunement</div>
<paper-checkbox id="attunementCheckbox" checked={{requiresAttunement}}></paper-checkbox>
</div>
</div>
<hr class="vertMargin">
<div layout horizontal around-justified>
<paper-input-decorator label="Weight Each (lbs)" floatinglabel>
<input id="weightInput" type="number" value={{weight}}>
</paper-input-decorator>
<!--Value-->
<paper-input-decorator label="Value Each (GP)" floatinglabel>
<input id="valueInput" type="number" value={{value}}>
</paper-input-decorator>
</div>
<hr class="vertMargin">
<!--Description-->
<paper-input-decorator label="Description" floatinglabel layout vertical>
<paper-autogrow-textarea>
<textarea id="itemDescriptionInput" placeholder aria-label="Description" value={{description}}></textarea>
</paper-autogrow-textarea>
</paper-input-decorator>
<!--Effects-->
{{> effectsEditList parentId=_id parentCollection="Items" charId=charId enabled=equipped name=name}}
<!--Attacks-->
{{> attackEditList parentId=_id parentCollection="Items" charId=charId enabled=equipped name=name}}
</template>
<template name="containerDropdown">
<paper-dropdown-menu id="containerDropDown" label="Container">
<paper-dropdown layered class="dropdown">
<core-menu class="menu" selected={{parent.id}}>
{{#each containers}}
<paper-item name={{_id}} class="containerMenuItem">{{name}}</paper-item>
{{/each}}
</core-menu>
</paper-dropdown>
</paper-dropdown-menu>
</template>

View File

@@ -1,20 +1,38 @@
var getContainers = function(charId){
return Containers.find({charId: charId}, {sort: {name: 1, _id: 1}, fields: {name: 1}}).fetch();
return Containers.find(
{charId: charId},
{sort: {name: 1, _id: 1}, fields: {name: 1}}
);
};
Template.itemDialog.onCreated(function(){
this.editing = new ReactiveVar(!!this.data.startEditing);
});
Template.itemDialog.helpers({
item: function(){
return Items.findOne(this.itemId);
},
containers: function(){
return getContainers(this.charId);
editing: function(){
return Template.instance().editing.get();
},
itemHeading: function(){
if (this.quantity === 1){
return this.name;
} else {
var pName = this.plural || this.name;
return this.quantity + " " + pName;
}
},
ne1: function(num){
return num != 1;
}
});
Template.itemDialog.events({
"tap #editButton": function(event, instance){
instance.editing.set(true);
},
"tap #doneEditingButton": function(event, instance){
instance.editing.set(false);
},
"color-change": function(event, instance){
Items.update(instance.data.itemId, {$set: {color: event.color}});
},
@@ -23,6 +41,19 @@ Template.itemDialog.events({
GlobalUI.deletedToast(instance.data.itemId, "Items", "Item");
GlobalUI.closeDetail();
},
});
Template.itemEdit.onRendered(function(){
updatePolymerInputs(this);
});
Template.itemEdit.helpers({
ne1: function(num){
return num != 1;
}
});
Template.itemEdit.events({
//TODO validate input (integer, non-negative, etc) for these inputs and give validation errors
"change #itemNameInput": function(event){
var name = Template.instance().find("#itemNameInput").value;
@@ -51,8 +82,8 @@ Template.itemDialog.events({
"change #equippedInput": function(event){
var equipped = Template.instance().find("#equippedInput").checked;
var item = Items.findOne(this._id);
if(item){
if(equipped){
if (item){
if (equipped){
item.equip();
} else {
item.unequip();
@@ -63,11 +94,20 @@ Template.itemDialog.events({
var value = event.currentTarget.checked;
Items.update(this._id, {$set: {requiresAttunement: value}});
},
});
Template.containerDropdown.helpers({
containers: function(){
return getContainers(this.charId);
}
});
Template.containerDropdown.events({
"core-select #containerDropDown": function(event){
var detail = event.originalEvent.detail;
if(!detail.isSelected) return;
if (!detail.isSelected) return;
var containerId = detail.item.getAttribute("name");
var item = Items.findOne(Template.currentData().itemId);
var item = Items.findOne(Template.currentData()._id);
item.moveToContainer(containerId);
}
});

View File

@@ -1,27 +1,28 @@
Template.splitStackDialog.helpers({
quantity: function(){
var item = Items.findOne(this.id);
if(item) return Math.round(item.quantity/2);
if (item) return Math.round(item.quantity / 2);
}
});
Template.splitStackDialog.events({
'tap #moveButton': function(event, instance){
"tap #moveButton": function(event, instance){
var item = Items.findOne(this.id);
if(item){
if (item){
item.splitToParent(
{collection: this.parentCollection , id: this.parentId},
+instance.find('#quantityInput').value
{collection: this.parentCollection , id: this.parentId},
+instance.find("#quantityInput").value
);
}
},
'tap #oneButton':function(event, instance){
instance.find('#quantityInput').value = 1;
"tap #oneButton":function(event, instance){
instance.find("#quantityInput").value = 1;
},
'tap #halfButton':function(event, instance){
instance.find('#quantityInput').value = Math.round(Items.findOne(this.id).quantity/2);
"tap #halfButton":function(event, instance){
var val = Math.round(Items.findOne(this.id).quantity / 2);
instance.find("#quantityInput").value = val;
},
"tap #allButton":function(event, instance){
instance.find("#quantityInput").value = Items.findOne(this.id).quantity;
},
'tap #allButton':function(event, instance){
instance.find('#quantityInput').value = Items.findOne(this.id).quantity;
}
});

View File

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

View File

@@ -1,3 +1,7 @@
Template.classDialog.onRendered(function(){
updatePolymerInputs(this);
});
Template.classDialog.events({
"tap #deleteButton": function(event, instance){
Classes.softRemoveNode(instance.data.classId);
@@ -11,11 +15,11 @@ Template.classDialog.events({
"change #levelValueInput": function(event){
var value = event.currentTarget.value;
Classes.update(this._id, {$set: {level: value}});
}
},
});
Template.classDialog.helpers({
class: function(){
return Classes.findOne(this.classId);
}
});
});

View File

@@ -1,6 +1,14 @@
<template name="experienceDialog">
{{#with experience}}
{{#baseDialog title=name class=colorClass hideColor="true"}}
{{#baseDialog title=name class=colorClass hideColor="true" startEditing=../startEditing}}
<div horizontal layout center-justified>
{{value}}
</div>
{{#if description}}
<hr class="vertMargin">
<div class="prewrap">{{description}}</div>
{{/if}}
{{else}}
<div horizontal layout>
<!--Name-->
<paper-input id="experienceNameInput" label="Name" floatinglabel value={{name}} flex></paper-input>

View File

@@ -1,7 +1,16 @@
Template.experienceDialog.helpers({
feature: function(){
return Features.findOne(this.featureId);
},
});
Template.experienceDialog.events({
"tap #deleteButton": function(event, instance){
Experiences.softRemove(instance.data.experienceId);
GlobalUI.deletedToast(instance.data.experienceId, "Experiences", "Experience");
GlobalUI.deletedToast(
instance.data.experienceId,
"Experiences", "Experience"
);
GlobalUI.closeDetail();
},
//TODO validate input (integer, non-negative, etc) for these inputs and give validation errors
@@ -16,7 +25,7 @@ Template.experienceDialog.events({
"change #experienceDescriptionInput": function(event){
var value = event.currentTarget.value;
Experiences.update(this._id, {$set: {description: value}});
}
},
});
Template.experienceDialog.helpers({

View File

@@ -40,7 +40,7 @@
</div>
<div class="containerMain experiences">
<div class="itemSlot">
<paper-item class="inventoryItem race" hero-id="main" {{detailHero "race"}} layout horizontal>
<paper-item class="inventoryItem race" hero-id="main" {{detailHero "race" _id}} layout horizontal>
{{race}}
</paper-item>
</div>

View File

@@ -1,6 +1,8 @@
Template.journal.created = function(){
var self = this;
self.experiencesLimit = new ReactiveVar(self.data.settings && self.data.settings.experiencesInc || 10);
self.experiencesLimit = new ReactiveVar(
self.data.settings && self.data.settings.experiencesInc || 10
);
};
Template.journal.helpers({
@@ -8,17 +10,29 @@ Template.journal.helpers({
return Notes.find({charId: this._id}, {sort: {color: 1, name: 1}});
},
experiences: function(){
return Experiences.find({charId: this._id}, {sort: {dateAdded: -1}, limit: Template.instance().experiencesLimit.get()});
return Experiences.find(
{charId: this._id},
{
sort: {dateAdded: -1},
limit: Template.instance().experiencesLimit.get(),
}
);
},
notMoreExperiences: function(){
return Experiences.find({charId: this._id}).count() < Template.instance().experiencesLimit.get();
return Experiences.find(
{charId: this._id}
).count() < Template.instance().experiencesLimit.get();
},
cantCollapse: function(){
return Template.instance().experiencesLimit.get() <= (this.settings && this.settings.experiencesInc || 10);
return Template.instance().experiencesLimit.get() <=
(this.settings && this.settings.experiencesInc || 10);
},
moreExperiencesOrCollapse: function(){
return (!(Experiences.find({charId: this._id}).count() < Template.instance().experiencesLimit.get())) ||
Template.instance().experiencesLimit.get() > (this.settings && this.settings.experiencesInc || 10);
var allShown = Experiences.find({charId: this._id}).count() <
Template.instance().experiencesLimit.get();
var canCollapse = Template.instance().experiencesLimit.get() >
(this.settings && this.settings.experiencesInc || 10);
return !allShown || canCollapse;
},
classes: function(){
return Classes.find({charId: this._id}, {sort: {createdAt: 1}});
@@ -29,13 +43,13 @@ Template.journal.helpers({
nextLevelXP: function(){
var currentLevel = this.level();
if (currentLevel < 20){
return xpTable[currentLevel];
return XP_TABLE[currentLevel];
}
},
race: function(){
var char = Characters.findOne(this._id, {fields: {race: 1}});
return char && char.race;
}
},
});
Template.journal.events({
@@ -43,41 +57,41 @@ Template.journal.events({
GlobalUI.setDetail({
template: "noteDialog",
data: {noteId: this._id, charId: this.charId},
heroId: this._id
heroId: this._id,
});
},
"tap .experience": function(event){
GlobalUI.setDetail({
template: "experienceDialog",
data: {experienceId: this._id, charId: this.charId},
heroId: this._id
heroId: this._id,
});
},
"tap .class": function(event){
GlobalUI.setDetail({
template: "classDialog",
data: {classId: this._id, charId: this.charId},
heroId: this._id
heroId: this._id,
});
},
"tap .race": function(event){
GlobalUI.setDetail({
template: "raceDialog",
data: {charId: this._id},
heroId: this._id + "race"
heroId: this._id + "race",
});
},
"tap #addNote": function(event){
var charId = this._id;
Notes.insert({
name: "New Note",
charId: charId
name: "New Note",
charId: charId,
}, function(error, id){
if(!error){
if (!error){
GlobalUI.setDetail({
template: "noteDialog",
data: {noteId: id, charId: charId},
heroId: id
data: {noteId: id, charId: charId, startEditing: true},
heroId: id,
});
}
});
@@ -87,44 +101,53 @@ Template.journal.events({
Experiences.insert({
charId: charId
}, function(error, id){
if(!error){
if (!error){
GlobalUI.setDetail({
template: "experienceDialog",
data: {experienceId: id, charId: charId},
heroId: id
data: {experienceId: id, charId: charId, startEditing: true},
heroId: id,
});
}
})
});
},
"tap #addClassButton":function(event){
var charId = this._id;
Classes.insert({
charId: charId,
name: "new Class",
level: 1
level: 1,
}, function(error, id){
if(!error){
if (!error){
GlobalUI.setDetail({
template: "classDialog",
data: {classId: id, charId: charId},
heroId: id
data: {classId: id, charId: charId, startEditing: true},
heroId: id,
});
}
})
});
},
"tap #moreExperiences": function(event){
var inst = Template.instance();
inst.experiencesLimit.set(inst.experiencesLimit.get() + (this.settings && this.settings.experiencesInc || 10));
inst.experiencesLimit.set(
inst.experiencesLimit.get() +
(this.settings && this.settings.experiencesInc || 10)
);
},
"tap #lessExperiences": function(event){
var inst = Template.instance();
inst.experiencesLimit.set(this.settings && this.settings.experiencesInc || 10);
inst.experiencesLimit.set(
this.settings && this.settings.experiencesInc || 10
);
//scroll to the top of the div
inst.$(".scroll-y").animate({
scrollTop: inst.$(".scroll-y").scrollTop() + inst.$(".experiencesCard").position().top - 8
scrollTop: (
inst.$(".scroll-y").scrollTop() +
inst.$(".experiencesCard").position().top -
8
)
}, 300);
//HACK giggle the columns :( to workaround chrome bug that stops .containers height from updating
var cs = inst.$(".containers").removeClass("containers");
_.defer(function(){cs.addClass("containers")});
}
_.defer(function(){cs.addClass("containers");});
},
});

View File

@@ -1,6 +1,8 @@
<template name="noteDialog">
{{#with note}}
{{#baseDialog title=name class=colorClass}}
{{#baseDialog title=name class=colorClass startEditing=../startEditing}}
<div class="prewrap">{{description}}</div>
{{else}}
<!--Name-->
<div horizontal layout>
<paper-input id="noteNameInput" label="Name" floatinglabel value={{name}} flex></paper-input>
@@ -8,7 +10,7 @@
<!--Description-->
<paper-input-decorator label="Description" floatinglabel layout vertical>
<paper-autogrow-textarea>
<textarea id="noteDescriptionInput" placeholder value={{description}}></textarea>
<textarea id="noteDescriptionInput" value={{description}}></textarea>
</paper-autogrow-textarea>
</paper-input-decorator>
{{/baseDialog}}

View File

@@ -1,3 +1,7 @@
Template.noteDialog.onRendered(function(){
updatePolymerInputs(this);
});
Template.noteDialog.events({
"color-change": function(event, instance){
Notes.update(instance.data.noteId, {$set: {color: event.color}});
@@ -14,11 +18,11 @@ Template.noteDialog.events({
"change #noteDescriptionInput": function(event){
var value = event.currentTarget.value;
Notes.update(this._id, {$set: {description: value}});
}
},
});
Template.noteDialog.helpers({
note: function(){
return Notes.findOne(this.noteId);
}
});
});

View File

@@ -1,6 +1,10 @@
<template name="raceDialog">
{{#baseDialog title="Race" class=colorClass hideColor="true" hideDelete="true"}}
{{#baseDialog title="Race" class=colorClass hideColor="true" hideDelete="true" startEditing=startEditing}}
{{> effectsViewList charId=charId parentId=charId parentGroup="racial"}}
{{> proficiencyViewList charId=charId parentId=charId parentGroup="racial"}}
{{else}}
<paper-input id="raceInput" label="Race" floatinglabel value={{race}}></paper-input>
{{> effectsEditList parentId=charId parentCollection="Characters" charId=charId type="racial"}}
{{> effectsEditList parentId=charId parentCollection="Characters" charId=charId parentGroup="racial"}}
{{> proficiencyEditList parentId=charId parentCollection="Characters" charId=charId parentGroup="racial"}}
{{/baseDialog}}
</template>

View File

@@ -1,3 +1,7 @@
Template.raceDialog.onRendered(function(){
updatePolymerInputs(this);
});
Template.raceDialog.events({
"change #raceInput": function(event){
var value = event.currentTarget.value;
@@ -10,4 +14,4 @@ Template.raceDialog.helpers({
var char = Characters.findOne(this.charId, {fields: {race: 1}});
return char && char.race;
}
});
});

View File

@@ -4,9 +4,9 @@ Template.newCharacterDialog.events({
name: instance.find("#nameInput").value,
gender: instance.find("#genderInput").value,
race: instance.find("#raceInput").value,
owner: Meteor.userId()
owner: Meteor.userId(),
}, function(err, id){
if(err) throw err;
if (err) throw err;
Router.go("characterSheet", {_id: id});
});
}

View File

@@ -1,12 +1,18 @@
<template name="personaDetailsDialog">
{{#baseDialog title=name class="deep-purple white-text" hideColor="true" hideDelete="true"}}
<!--Name-->
<paper-input id="nameInput" label="Name" floatinglabel value={{name}}></paper-input><br>
<!--Alignment-->
<paper-input id="alignmentInput" label="Alignment" floatinglabel value={{alignment}}></paper-input><br>
<!--Gender-->
<paper-input id="genderInput" label="Gender" floatinglabel value={{gender}}></paper-input><br>
<!--Race-->
<paper-input id="raceInput" label="Race" floatinglabel value={{race}}></paper-input><br>
{{#baseDialog title=name class="deep-purple white-text" hideColor="true" hideDelete="true" startEditing=startEditing}}
{{alignment}} {{gender}} {{race}}
{{else}}
{{> personaDetailsEdit}}
{{/baseDialog}}
</template>
</template>
<template name="personaDetailsEdit">
<!--Name-->
<paper-input id="nameInput" label="Name" floatinglabel value={{name}}></paper-input><br>
<!--Alignment-->
<paper-input id="alignmentInput" label="Alignment" floatinglabel value={{alignment}}></paper-input><br>
<!--Gender-->
<paper-input id="genderInput" label="Gender" floatinglabel value={{gender}}></paper-input><br>
<!--Race-->
<paper-input id="raceInput" label="Race" floatinglabel value={{race}}></paper-input><br>
</template>

View File

@@ -1,18 +1,22 @@
Template.personaDetailsDialog.events({
Template.personaDetailsEdit.onRendered(function(){
updatePolymerInputs(this);
});
Template.personaDetailsEdit.events({
"change #nameInput": function(event){
var input = event.currentTarget.value;
Characters.update( this.charId, {$set: {name: input}} );
Characters.update(this.charId, {$set: {name: input}});
},
"change #alignmentInput": function(event){
var input = event.currentTarget.value;
Characters.update( this.charId, {$set: {alignment: input}} );
Characters.update(this.charId, {$set: {alignment: input}});
},
"change #genderInput": function(event){
var input = event.currentTarget.value;
Characters.update( this.charId, {$set: {gender: input}} );
Characters.update(this.charId, {$set: {gender: input}});
},
"change #raceInput": function(event){
var input = event.currentTarget.value;
Characters.update( this.charId, {$set: {race: input}} );
}
Characters.update(this.charId, {$set: {race: input}});
},
});

View File

@@ -1,3 +0,0 @@
#persona .containerMain{
white-space: pre-line;
}

View File

@@ -11,7 +11,16 @@
{{> containerCard characterField "bonds" "Bonds"}}
{{> containerCard characterField "flaws" "Flaws"}}
{{> containerCard characterField "backstory" "Background"}}
{{> containerCard characterField "languages" "Languages"}}
<paper-shadow class="card container">
<div class="containerTop whiteTop" layout horizontal center>
<div class="containerName subhead" flex>Languages</div>
</div>
<div flex class="containerMain listPadded">
{{#each languages}}
{{> proficiencyListItem}}
{{/each}}
</div>
</paper-shadow>
</div>
</div>
</div>
@@ -23,9 +32,9 @@
<template name="containerCardHelper">
<paper-shadow class="card container {{class}}" hero-id="main" {{detailHero field ../_id}}>
<div class="containerTop {{colorClass}}" hero-id="toolbar" layout horizontal center {{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>
</div>
<div flex class="containerMain">{{> UI.contentBlock}}</div>
<div flex class="containerMain prewrap">{{> UI.contentBlock}}</div>
</paper-shadow>
</template>

View File

@@ -5,15 +5,18 @@ var colorMap = {
bonds: "h",
flaws: "i",
backstory: "j",
languages: "k"
}
};
Template.persona.helpers({
characterDetails: function(){
var char = Characters.findOne(this._id, {fields: {name: 1, gender: 1, alignment: 1, race:1}})
var char = Characters.findOne(
this._id,
{fields: {name: 1, gender: 1, alignment: 1, race:1}}
);
char.field = "details";
char.title = char.name;
char.color = "d";
char.topClass = "characterField";
return char;
},
characterField: function(field, title){
@@ -21,26 +24,41 @@ Template.persona.helpers({
fieldSelector.fields[field] = 1;
var char = Characters.findOne(this._id, fieldSelector);
var color = colorMap[field];
return {_id: char._id, title: title, field: field, color: color, body: char[field]};
}
return {
_id: char._id,
title: title,
field: field,
color: color,
body: char[field],
topClass: "characterField",
};
},
languages: function(){
return Proficiencies.find({charId: this._id, type: "language"});
},
});
Template.persona.events({
"tap .containerTop": function(event){
if(this.field !== "details"){
"tap .characterField": function(event){
if (this.field !== "details"){
var charId = Template.parentData()._id;
GlobalUI.setDetail({
template: "textDialog",
data: {charId: charId, field: this.field, title: this.title, color: this.color},
heroId: this._id + this.field
data: {
charId: charId,
field: this.field,
title: this.title,
color: this.color,
},
heroId: this._id + this.field,
});
} else{
} else {
this.charId = Template.parentData()._id;
GlobalUI.setDetail({
template: "personaDetailsDialog",
data: this,
heroId: this._id + "details"
heroId: this._id + this.field,
});
}
}
}
});
});

View File

@@ -1,9 +1,15 @@
<template name="textDialog">
{{#baseDialog title=title class=colorClass hideColor="true" hideDelete="true"}}
<paper-input-decorator label={{title}} floatinglabel layout vertical>
<paper-autogrow-textarea>
<textarea id="textInput" placeholder value={{value}}></textarea>
</paper-autogrow-textarea>
</paper-input-decorator>
{{#baseDialog title=title class=colorClass hideColor="true" hideDelete="true" startEditing=startEditing}}
<div class="prewrap">{{value}}</div>
{{else}}
{{> textDialogEdit}}
{{/baseDialog}}
</template>
</template>
<template name="textDialogEdit">
<paper-input-decorator label={{title}} floatinglabel layout vertical>
<paper-autogrow-textarea>
<textarea id="textInput" placeholder value={{value}}></textarea>
</paper-autogrow-textarea>
</paper-input-decorator>
</template>

View File

@@ -7,11 +7,24 @@ Template.textDialog.helpers({
}
});
Template.textDialog.events({
Template.textDialogEdit.onRendered(function(){
updatePolymerInputs(this);
});
Template.textDialogEdit.helpers({
value: function(){
var fieldSelector = {fields: {}};
fieldSelector.fields[this.field] = 1;
var char = Characters.findOne(this.charId, fieldSelector);
return char[this.field];
}
});
Template.textDialogEdit.events({
"change #textInput": function(event){
var input = event.currentTarget.value;
var setter = {$set: {}};
setter.$set[this.field] = input;
Characters.update(this.charId, setter);
}
});
});

View File

@@ -0,0 +1,46 @@
<template name="proficiencyEdit">
<div layout horizontal around-justified>
<paper-dropdown-menu class="typeDropDown" label="Stat Group" flex>
<paper-dropdown layered class="dropdown">
<core-menu class="menu typeMenu" selected={{type}}>
{{#each proficiencyTypes}}
<paper-item class="statGroupSelect" name={{type}}>{{name}}</paper-item>
{{/each}}
</core-menu>
</paper-dropdown>
</paper-dropdown-menu>
{{> UI.dynamic template=nameInputTemplate}}
<paper-dropdown-menu class="valueDropDown" label="Proficiency" flex>
<paper-dropdown layered class="dropdown">
<core-menu class="menu valueMenu" selected={{value}}>
<paper-item name="1">Proficient</paper-item>
<paper-item name="0.5">Half Prof. Bonus</paper-item>
<paper-item name="2">Double Prof. Bonus</paper-item>
</core-menu>
</paper-dropdown>
</paper-dropdown-menu>
<paper-icon-button class="deleteProficiency"
icon="delete">
</paper-icon-button>
</div>
</template>
<template name="nameDropdown">
<paper-dropdown-menu class="nameDropDown sideMargin" label="Proficiency" flex>
<paper-dropdown layered class="dropdown">
<core-menu class="menu nameMenu" selected={{name}}>
{{#each nameDropdownItems}}
<paper-item name={{stat}}>{{name}}</paper-item>
{{/each}}
</core-menu>
</paper-dropdown>
</paper-dropdown-menu>
</template>
<template name="nameInput">
<paper-input class="nameInput sideMargin"
label="Name"
floatinglabel
value={{name}}
flex></paper-input>
</template>

View File

@@ -0,0 +1,90 @@
var profTypes = [
{type: "skill", name: "Skill"},
{type: "save", name: "Saving Throw"},
{type: "weapon", name: "Weapon"},
{type: "armor", name: "Armor"},
{type: "tool", name: "Tool"},
{type: "language", name: "Language"},
];
var saves = [
{name: "Strength Save", stat: "strengthSave"},
{name: "Dexterity Save", stat: "dexteritySave"},
{name: "Constitution Save", stat: "constitutionSave"},
{name: "Intelligence Save", stat: "intelligenceSave"},
{name: "Wisdom Save", stat: "wisdomSave"},
{name: "Charisma Save", stat: "charismaSave"},
];
var skills = [
{name: "Acrobatics", stat: "acrobatics"},
{name: "Animal Handling", stat: "animalHandling"},
{name: "Arcana", stat: "arcana"},
{name: "Athletics", stat: "athletics"},
{name: "Deception", stat: "deception"},
{name: "History", stat: "history"},
{name: "Insight", stat: "insight"},
{name: "Intimidation", stat: "intimidation"},
{name: "Investigation", stat: "investigation"},
{name: "Medicine", stat: "medicine"},
{name: "Nature", stat: "nature"},
{name: "Perception", stat: "perception"},
{name: "Performance", stat: "performance"},
{name: "Persuasion", stat: "persuasion"},
{name: "Religion", stat: "religion"},
{name: "Sleight of Hand", stat: "sleightOfHand"},
{name: "Stealth", stat: "stealth"},
{name: "Survival", stat: "survival"},
{name: "Initiative", stat: "initiative"},
];
Template.proficiencyEdit.helpers({
proficiencyTypes: function(){
return profTypes;
},
nameInputTemplate: function(){
if (!this.type) return null;
if (this.type === "skill" ||
this.type === "save") return "nameDropdown";
return "nameInput";
},
});
Template.proficiencyEdit.events({
"tap .deleteProficiency": function(event){
Proficiencies.softRemoveNode(this._id);
GlobalUI.deletedToast(this._id, "Proficiencies", "Proficiency");
},
"core-select .typeDropDown": function(event){
var detail = event.originalEvent.detail;
if (!detail.isSelected) return;
var type = detail.item.getAttribute("name");
if (type == this.type) return;
Proficiencies.update(this._id, {$set: {type: type}});
},
"core-select .valueDropDown": function(event){
var detail = event.originalEvent.detail;
if (!detail.isSelected) return;
var value = +detail.item.getAttribute("name");
if (value == this.value) return;
Proficiencies.update(this._id, {$set: {value: value}});
},
"core-select .nameDropDown": function(event){
var detail = event.originalEvent.detail;
if (!detail.isSelected) return;
var name = detail.item.getAttribute("name");
if (name == this.name) return;
Proficiencies.update(this._id, {$set: {name: name}});
},
"change .nameInput": function(event){
var name = event.currentTarget.value;
Proficiencies.update(this._id, {$set: {name: name}});
},
});
Template.nameDropdown.helpers({
nameDropdownItems: function(){
if (this.type === "skill") return skills;
if (this.type === "save") return saves;
}
});

View File

@@ -0,0 +1,18 @@
<!--needs to be given charId, parentId and parentCollection-->
<template name="proficiencyEditList">
{{#if proficiencies.count}}
<hr class="vertMargin">
<div id="proficiencies">
<h2>Proficiencies</h2>
{{#each proficiencies}}
{{>proficiencyEdit}}
{{/each}}
</div>
{{/if}}
<paper-button id="addProficiencyButton"
class="red-button"
raised>
Add Proficiency
</paper-button>
</template>

View File

@@ -0,0 +1,31 @@
Template.proficiencyEditList.helpers({
proficiencies: function(){
var selector = {
"parent.id": this.parentId,
"charId": this.charId,
};
if (this.parentGroup){
selector["parent.group"] = this.parentGroup;
}
return Proficiencies.find(selector);
}
});
Template.proficiencyEditList.events({
"tap #addProficiencyButton": function(){
if (!_.isBoolean(this.enabled)) {
this.enabled = true;
}
Proficiencies.insert({
charId: this.charId,
parent: {
id: this.parentId,
collection: this.parentCollection,
group: this.parentGroup,
},
enabled: this.enabled,
value: 1,
type: "skill",
});
},
});

View File

@@ -0,0 +1,8 @@
<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>
</template>

View File

@@ -0,0 +1,20 @@
Template.proficiencyListItem.helpers({
profIcon: function(){
var prof = this.value;
if (prof > 0 && prof < 1) return "image:brightness-2";
if (prof === 1) return "image:brightness-1";
if (prof > 1) return "av:album";
return "radio-button-off";
},
getName: function(){
if (this.type === "skill") return skills[this.name];
if (this.type === "save") return saves[this.name];
return this.name;
},
});
Template.proficiencyListItem.events({
"tap .proficiencyItem": function(event, instance){
openParentDialog(this.parent, this.charId, this._id);
}
});

View File

@@ -0,0 +1,6 @@
<template name="proficiencyView">
<div class="proficiencyView" layout horizontal center>
<core-icon icon="{{profIcon}}"></core-icon>
<div class="sideMargin">{{getName}}</div>
</div>
</template>

View File

@@ -0,0 +1,45 @@
var saves = {
strengthSave: "Strength Save",
dexteritySave: "Dexterity Save",
constitutionSave: "Constitution Save",
intelligenceSave: "Intelligence Save",
wisdomSave: "Wisdom Save",
charismaSave: "Charisma Save",
};
var skills = {
acrobatics: "Acrobatics",
animalHandling: "Animal Handling",
arcana: "Arcana",
athletics: "Athletics",
deception: "Deception",
history: "History",
insight: "Insight",
intimidation: "Intimidation",
investigation: "Investigation",
medicine: "Medicine",
nature: "Nature",
perception: "Perception",
performance: "Performance",
persuasion: "Persuasion",
religion: "Religion",
sleightOfHand: "Sleight of Hand",
stealth: "Stealth",
survival: "Survival",
initiative: "Initiative",
};
Template.proficiencyView.helpers({
profIcon: function(){
var prof = this.value;
if (prof > 0 && prof < 1) return "image:brightness-2";
if (prof === 1) return "image:brightness-1";
if (prof > 1) return "av:album";
return "radio-button-off";
},
getName: function(){
if (this.type === "skill") return skills[this.name];
if (this.type === "save") return saves[this.name];
return this.name;
},
});

View File

@@ -0,0 +1,11 @@
<template name="proficiencyViewList">
{{#if proficiencies.count}}
<hr class="vertMargin">
<div class="proficiencies">
<h2 class="spaceAfter">Proficiencies</h2>
{{#each proficiencies}}
{{> proficiencyView}}
{{/each}}
</div>
{{/if}}
</template>

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