diff --git a/rpg-docs/.meteor/packages b/rpg-docs/.meteor/packages
index 0928a7e4..89495d83 100644
--- a/rpg-docs/.meteor/packages
+++ b/rpg-docs/.meteor/packages
@@ -48,3 +48,4 @@ es5-shim@4.6.15
differential:vulcanize
reactive-dict
percolate:synced-cron
+ongoworks:speakingurl
diff --git a/rpg-docs/.meteor/versions b/rpg-docs/.meteor/versions
index 04f53178..c2254df9 100644
--- a/rpg-docs/.meteor/versions
+++ b/rpg-docs/.meteor/versions
@@ -85,6 +85,7 @@ npm-mongo@2.2.16_1
oauth@1.1.12
oauth2@1.1.11
observe-sequence@1.0.14
+ongoworks:speakingurl@9.0.0
ordered-dict@1.0.9
percolate:migrations@0.9.8
percolate:synced-cron@1.3.2
diff --git a/rpg-docs/Model/Campaign/Party.js b/rpg-docs/Model/Campaign/Party.js
index 990e8e11..d48dbb97 100644
--- a/rpg-docs/Model/Campaign/Party.js
+++ b/rpg-docs/Model/Campaign/Party.js
@@ -1,8 +1,42 @@
Parties = new Mongo.Collection("parties");
Schemas.Party = new SimpleSchema({
- //each character/monster can only be in one party at a time
- //each party can only be in a single instance at a time
+ name: {
+ type: String,
+ defaultValue: "New Party",
+ trim: false,
+ optional: true,
+ },
+ characters: {
+ type: [String],
+ regEx: SimpleSchema.RegEx.Id,
+ index: 1,
+ defaultValue: [],
+ },
+ owner: {
+ type: String,
+ regEx: SimpleSchema.RegEx.Id,
+ },
});
Parties.attachSchema(Schemas.Party);
+
+Parties.allow({
+ insert: function(userId, doc) {
+ return userId && doc.owner === userId;
+ },
+ update: function(userId, doc, fields, modifier) {
+ return userId && doc.owner === userId;
+ },
+ remove: function(userId, doc) {
+ return userId && doc.owner === userId;
+ },
+ fetch: ["owner"],
+});
+
+Parties.deny({
+ update: function(userId, docs, fields, modifier) {
+ // can't change owners
+ return _.contains(fields, "owner");
+ }
+});
diff --git a/rpg-docs/Model/Character/Actions.js b/rpg-docs/Model/Character/Actions.js
index a4e2e236..31caa805 100644
--- a/rpg-docs/Model/Character/Actions.js
+++ b/rpg-docs/Model/Character/Actions.js
@@ -11,10 +11,12 @@ Schemas.Action = new SimpleSchema({
},
name: {
type: String,
+ optional: true,
trim: false,
},
description: {
type: String,
+ optional: true,
trim: false,
},
type: {
diff --git a/rpg-docs/Model/Character/Attacks.js b/rpg-docs/Model/Character/Attacks.js
index 2450439a..4a2dcc72 100644
--- a/rpg-docs/Model/Character/Attacks.js
+++ b/rpg-docs/Model/Character/Attacks.js
@@ -12,6 +12,7 @@ Schemas.Attack = new SimpleSchema({
name: {
type: String,
defaultValue: "New Attack",
+ optional: true,
trim: false,
},
details: {
diff --git a/rpg-docs/Model/Character/Buffs.js b/rpg-docs/Model/Character/Buffs.js
index 596679cd..dd16c39b 100644
--- a/rpg-docs/Model/Character/Buffs.js
+++ b/rpg-docs/Model/Character/Buffs.js
@@ -8,6 +8,7 @@ Schemas.Buff = new SimpleSchema({
},
name: {
type: String,
+ optional: true,
trim: false,
},
description: {
diff --git a/rpg-docs/Model/Character/Characters.js b/rpg-docs/Model/Character/Characters.js
index 20e717e1..e75430b2 100644
--- a/rpg-docs/Model/Character/Characters.js
+++ b/rpg-docs/Model/Character/Characters.js
@@ -4,6 +4,7 @@ Characters = new Mongo.Collection("characters");
Schemas.Character = new SimpleSchema({
//strings
name: {type: String, defaultValue: "", trim: false, optional: true},
+ urlName: {type: String, defaultValue: "-", trim: false, optional: true},
alignment: {type: String, defaultValue: "", trim: false, optional: true},
gender: {type: String, defaultValue: "", trim: false, optional: true},
race: {type: String, defaultValue: "", trim: false, optional: true},
@@ -184,6 +185,9 @@ Schemas.Character = new SimpleSchema({
defaultValue: "whitelist",
allowedValues: ["whitelist", "public"],
},
+ "settings.exportFeatures": {type: Boolean, defaultValue: true},
+ "settings.exportAttacks": {type: Boolean, defaultValue: true},
+ "settings.exportDescription": {type: Boolean, defaultValue: true},
});
Characters.attachSchema(Schemas.Character);
@@ -254,7 +258,10 @@ var attributeBase = preventLoop(function(charId, statName){
var result = (base + add) * mul;
if (result < min) result = min;
if (result > max) result = max;
-
+ // Don't round carry multiplier
+ if (statName === "carryMultiplier"){
+ return result;
+ }
return Math.floor(result);
});
@@ -534,6 +541,15 @@ if (Meteor.isServer){
Items .remove({charId: character._id});
Containers .remove({charId: character._id});
});
+ Characters.after.update(function(userId, doc, fieldNames, modifier, options) {
+ if (_.contains(fieldNames, "name")){
+ var urlName = getSlug(doc.name, {maintainCase: true}) || "-";
+ Characters.update(doc._id, {$set: {urlName}});
+ }
+ });
+ Characters.before.insert(function(userId, doc) {
+ doc.urlName = getSlug(doc.name, {maintainCase: true}) || "-";
+ });
}
Characters.allow({
diff --git a/rpg-docs/Model/Character/Classes.js b/rpg-docs/Model/Character/Classes.js
index ac036230..52e4df9e 100644
--- a/rpg-docs/Model/Character/Classes.js
+++ b/rpg-docs/Model/Character/Classes.js
@@ -2,7 +2,7 @@ Classes = new Mongo.Collection("classes");
Schemas.Class = new SimpleSchema({
charId: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
- name: {type: String, trim: false},
+ name: {type: String, optional: true, trim: false},
level: {type: Number},
createdAt: {
type: Date,
diff --git a/rpg-docs/Model/Character/Experience.js b/rpg-docs/Model/Character/Experience.js
index ff6d5932..4dc22b2a 100644
--- a/rpg-docs/Model/Character/Experience.js
+++ b/rpg-docs/Model/Character/Experience.js
@@ -2,7 +2,7 @@ Experiences = new Mongo.Collection("experience");
Schemas.Experience = new SimpleSchema({
charId: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
- name: {type: String, defaultValue: "New Experience", trim: false},
+ name: {type: String, optional: true, trim: false, defaultValue: "New Experience"},
description: {type: String, optional: true, trim: false},
value: {type: Number, defaultValue: 0},
dateAdded: {
diff --git a/rpg-docs/Model/Character/Features.js b/rpg-docs/Model/Character/Features.js
index 29b8c7d5..468d6fe3 100644
--- a/rpg-docs/Model/Character/Features.js
+++ b/rpg-docs/Model/Character/Features.js
@@ -2,7 +2,7 @@ Features = new Mongo.Collection("features");
Schemas.Feature = new SimpleSchema({
charId: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
- name: {type: String, trim: false},
+ name: {type: String, optional: true, trim: false},
description: {type: String, optional: true, trim: false},
uses: {type: String, optional: true, trim: false},
used: {type: Number, defaultValue: 0},
diff --git a/rpg-docs/Model/Character/Notes.js b/rpg-docs/Model/Character/Notes.js
index 8d1afdf5..f1ce7aa4 100644
--- a/rpg-docs/Model/Character/Notes.js
+++ b/rpg-docs/Model/Character/Notes.js
@@ -2,7 +2,7 @@ Notes = new Mongo.Collection("notes");
Schemas.Note = new SimpleSchema({
charId: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
- name: {type: String, trim: false},
+ name: {type: String, optional: true, trim: false},
description: {type: String, optional: true, trim: false},
color: {
type: String,
diff --git a/rpg-docs/Model/Character/SpellLists.js b/rpg-docs/Model/Character/SpellLists.js
index 16aa2dab..982be871 100644
--- a/rpg-docs/Model/Character/SpellLists.js
+++ b/rpg-docs/Model/Character/SpellLists.js
@@ -2,7 +2,7 @@ SpellLists = new Mongo.Collection("spellLists");
Schemas.SpellLists = new SimpleSchema({
charId: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
- name: {type: String, trim: false},
+ name: {type: String, optional: true, trim: false},
description: {type: String, optional: true, trim: false},
saveDC: {type: String, optional: true, trim: false},
attackBonus: {type: String, optional: true, trim: false},
diff --git a/rpg-docs/Model/Character/Spells.js b/rpg-docs/Model/Character/Spells.js
index f04e2fa7..58f21cd2 100644
--- a/rpg-docs/Model/Character/Spells.js
+++ b/rpg-docs/Model/Character/Spells.js
@@ -9,6 +9,7 @@ Schemas.Spell = new SimpleSchema({
},
name: {
type: String,
+ optional: true,
trim: false,
defaultValue: "New Spell",
},
diff --git a/rpg-docs/Model/Inventory/Containers.js b/rpg-docs/Model/Inventory/Containers.js
index 94761c58..2318c930 100644
--- a/rpg-docs/Model/Inventory/Containers.js
+++ b/rpg-docs/Model/Inventory/Containers.js
@@ -2,7 +2,7 @@
Containers = new Mongo.Collection("containers");
Schemas.Container = new SimpleSchema({
- name: {type: String, trim: false},
+ name: {type: String, optional: true, trim: false},
charId: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
isCarried: {type: Boolean},
weight: {type: Number, min: 0, defaultValue: 0, decimal: true},
diff --git a/rpg-docs/Model/Inventory/Items.js b/rpg-docs/Model/Inventory/Items.js
index c626c16f..6c5c00e1 100644
--- a/rpg-docs/Model/Inventory/Items.js
+++ b/rpg-docs/Model/Inventory/Items.js
@@ -1,7 +1,7 @@
Items = new Mongo.Collection("items");
Schemas.Item = new SimpleSchema({
- name: {type: String, defaultValue: "New Item", trim: false},
+ name: {type: String, optional: true, trim: false, defaultValue: "New Item"},
plural: {type: String, optional: true, trim: false},
description:{type: String, optional: true, trim: false},
charId: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1}, //id of owner
diff --git a/rpg-docs/Routes/Routes.js b/rpg-docs/Routes/Routes.js
index 5f2906c2..90676c79 100644
--- a/rpg-docs/Routes/Routes.js
+++ b/rpg-docs/Routes/Routes.js
@@ -24,7 +24,7 @@ Router.map(function() {
this.route("characterList", {
path: "/characterList",
waitOn: function(){
- return subsManager.subscribe("characterList", Meteor.userId());
+ return subsManager.subscribe("characterList");
},
onAfterAction: function() {
document.title = appName + " - Characters";
@@ -32,11 +32,27 @@ Router.map(function() {
fastRender: true,
});
- this.route("characterSheet", {
- path: "/character/:_id",
+ this.route("characterSheetNaked", {
+ path: "/character/:_id/",
waitOn: function(){
return [
- subsManager.subscribe("singleCharacter", this.params._id, Meteor.userId()),
+ subsManager.subscribe("singleCharacter", this.params._id),
+ ];
+ },
+ action: function(){
+ var _id = this.params._id
+ var character = Characters.findOne(_id);
+ var urlName = character && character.urlName;
+ var path = `\/character\/${_id}\/${urlName || "-"}`;
+ Router.go(path,{},{replaceState:true});
+ },
+ });
+
+ this.route("characterSheet", {
+ path: "/character/:_id/:urlName",
+ waitOn: function(){
+ return [
+ subsManager.subscribe("singleCharacter", this.params._id),
];
},
data: function() {
diff --git a/rpg-docs/client/globalHelpers/characterPath.js b/rpg-docs/client/globalHelpers/characterPath.js
new file mode 100644
index 00000000..723ee07b
--- /dev/null
+++ b/rpg-docs/client/globalHelpers/characterPath.js
@@ -0,0 +1,3 @@
+Template.registerHelper("characterPath", function(char) {
+ return `\/character\/${char._id}\/${char.urlName || "-"}`;
+});
diff --git a/rpg-docs/client/globalHelpers/evaluate.js b/rpg-docs/client/globalHelpers/evaluate.js
index d986b1f0..65ad0240 100644
--- a/rpg-docs/client/globalHelpers/evaluate.js
+++ b/rpg-docs/client/globalHelpers/evaluate.js
@@ -24,6 +24,10 @@ Template.registerHelper("evaluateString", function(charId, string) {
return evaluateString(charId, string);
});
+Template.registerHelper("evaluateSpellString", function(charId, spellListId, string) {
+ return evaluateSpellString(charId, spellListId, string);
+});
+
Template.registerHelper("evaluateShortString", function(charId, string) {
if (_.isString(string)){
return evaluateString(
diff --git a/rpg-docs/client/lib/improvedInitiativeJson.js b/rpg-docs/client/lib/improvedInitiativeJson.js
new file mode 100644
index 00000000..eb1502e8
--- /dev/null
+++ b/rpg-docs/client/lib/improvedInitiativeJson.js
@@ -0,0 +1,195 @@
+improvedInitiativeJson = function(charId, options){
+ options = options || {
+ features: true,
+ attacks: true,
+ description: true,
+ };
+ var char = Characters.findOne(charId);
+ if (!char) return;
+ var baseValue = function(attributeName){
+ return Characters.calculate.attributeBase(charId, attributeName);
+ };
+ var skillMod = function(skillName){
+ return Characters.calculate.skillMod(charId, skillName);
+ };
+ var damageMods = getDamageMods(charId);
+ return JSON.stringify({
+ "Id": char._id,
+ "Name": char.name,
+ "Source": "DiceCloud",
+ "Type": char.race,
+ "HP": {
+ "Value": baseValue("hitPoints"),
+ "Notes": getHitDiceString(charId) || "",
+ },
+ "AC": {
+ "Value": baseValue("armor"),
+ "Notes": getArmorString(charId) || "",
+ },
+ "InitiativeModifier": skillMod("initiative"),
+ "Speed": ["" + baseValue("speed")],
+ "Abilities": {
+ "Str": baseValue("strength"),
+ "Dex": baseValue("dexterity"),
+ "Con": baseValue("constitution"),
+ "Cha": baseValue("charisma"),
+ "Int": baseValue("intelligence"),
+ "Wis": baseValue("wisdom"),
+ },
+ "DamageVulnerabilities": damageMods.vulnerabilities,
+ "DamageResistances": damageMods.resistances,
+ "DamageImmunities": damageMods.immunities,
+ "ConditionImmunities": [],
+ "Saves": [
+ {"Name": "Str", "Modifier": skillMod("strengthSave")},
+ {"Name": "Dex", "Modifier": skillMod("dexteritySave")},
+ {"Name": "Con", "Modifier": skillMod("constitutionSave")},
+ {"Name": "Int", "Modifier": skillMod("intelligenceSave")},
+ {"Name": "Wis", "Modifier": skillMod("wisdomSave")},
+ {"Name": "Cha", "Modifier": skillMod("charismaSave")},
+ ],
+ "Skills": getSkills(charId),
+ "Senses": [
+ "passive Perception " +
+ Characters.calculate.passiveSkill(charId, "perception")
+ ],
+ "Languages": getLanguages(charId),
+ "Challenge": "",
+ "Traits": options.features ? getTraits(charId) : [],
+ "Actions": options.attacks ? getActions(charId) : [],
+ "Reactions": [],
+ "LegendaryActions": [],
+ "Description": options.description ? char.description : "",
+ "Player": Meteor.user().username,
+ }, null, 2);
+}
+
+var getHitDiceString = function(charId){
+ var d6 = Characters.calculate.attributeBase(charId, "d6HitDice");
+ var d8 = Characters.calculate.attributeBase(charId, "d8HitDice");
+ var d10 = Characters.calculate.attributeBase(charId, "d10HitDice");
+ var d12 = Characters.calculate.attributeBase(charId, "d12HitDice");
+ var con = Characters.calculate.abilityMod(charId,"constitution");
+ var string = "" +
+ (d6 ? `${d6}d6 + ` : "") +
+ (d8 ? `${d8}d8 + ` : "") +
+ (d10 ? `${d10}d10 + ` : "") +
+ (d12 ? `${d12}d12 + ` : "") +
+ con;
+}
+
+var getArmorString = function(charId){
+ var bases = Effects.find({
+ charId: charId,
+ stat: "armor",
+ operation: "base",
+ enabled: true,
+ }).map(e => ({
+ ame: e.name,
+ value: evaluateEffect(charId, e),
+ }));
+ var base = bases.length && _.max(bases, b => b.value).name || "";
+ var effects = Effects.find({
+ charId: charId,
+ stat: "armor",
+ operation: {$ne: "base"},
+ enabled: true,
+ }).map(e => e.name);
+ var strings = base ? [base] : [];
+ strings = strings.concat(effects);
+ return strings.join(", ");
+}
+
+var getDamageMods = function(charId){
+ // jscs:disable maximumLineLength
+ var multipliers = [
+ {name: "Acid", value: Characters.calculate.attributeValue(charId, "acidMultiplier")},
+ {name: "Bludgeoning", value: Characters.calculate.attributeValue(charId, "bludgeoningMultiplier")},
+ {name: "Cold", value: Characters.calculate.attributeValue(charId, "coldMultiplier")},
+ {name: "Fire", value: Characters.calculate.attributeValue(charId, "fireMultiplier")},
+ {name: "Force", value: Characters.calculate.attributeValue(charId, "forceMultiplier")},
+ {name: "Lightning", value: Characters.calculate.attributeValue(charId, "lightningMultiplier")},
+ {name: "Necrotic", value: Characters.calculate.attributeValue(charId, "necroticMultiplier")},
+ {name: "Piercing", value: Characters.calculate.attributeValue(charId, "piercingMultiplier")},
+ {name: "Poison", value: Characters.calculate.attributeValue(charId, "poisonMultiplier")},
+ {name: "Psychic", value: Characters.calculate.attributeValue(charId, "psychicMultiplier")},
+ {name: "Radiant", value: Characters.calculate.attributeValue(charId, "radiantMultiplier")},
+ {name: "Slashing", value: Characters.calculate.attributeValue(charId, "slashingMultiplier")},
+ {name: "Thunder", value: Characters.calculate.attributeValue(charId, "thunderMultiplier")},
+ ];
+ // jscs:enable maximumLineLength
+ multipliers = _.groupBy(multipliers, "value");
+ var names = o => o.name;
+ return {
+ "immunities": _.map(multipliers["0"], names),
+ "resistances": _.map(multipliers["0.5"], names),
+ "vulnerabilities": _.map(multipliers["2"], names),
+ };
+}
+
+var getSkills = function(charId){
+ var allSkills = [
+ {name: "acrobatics", attribute: "dexterity"},
+ {name: "animalHandling", attribute: "wisdom"},
+ {name: "arcana", attribute: "intelligence"},
+ {name: "athletics", attribute: "strength"},
+ {name: "deception", attribute: "charisma"},
+ {name: "history", attribute: "intelligence"},
+ {name: "insight", attribute: "wisdom"},
+ {name: "intimidation", attribute: "charisma"},
+ {name: "investigation", attribute: "intelligence"},
+ {name: "medicine", attribute: "wisdom"},
+ {name: "nature", attribute: "intelligence"},
+ {name: "perception", attribute: "wisdom"},
+ {name: "performance", attribute: "charisma"},
+ {name: "persuasion", attribute: "charisma"},
+ {name: "religion", attribute: "intelligence"},
+ {name: "sleightOfHand", attribute: "dexterity"},
+ {name: "stealth", attribute: "dexterity"},
+ {name: "survival", attribute: "wisdom"},
+ ];
+ var skills = [];
+ _.each(allSkills, skill => {
+ var value = Characters.calculate.skillMod(charId, skill.name);
+ var mod = Characters.calculate.abilityMod(charId, skill.attribute);
+ if (value !== mod){
+ skills.push({Name: skill.name, Modifier: value});
+ }
+ });
+ return skills;
+};
+
+var getLanguages = function(charId){
+ return Proficiencies.find({
+ charId,
+ enabled: true,
+ type: "language",
+ }).map(l => l.name);
+};
+
+var getTraits = function(charId){
+ return Features.find(
+ {charId: charId},
+ {sort: {color: 1, name: 1}}
+ ).map(f => ({
+ Name: f.name,
+ // evaluateShortString helper
+ Content: evaluateString(
+ charId, f.description && f.description.split(/^( *[-*_]){3,} *(?:\n+|$)/m)[0]
+ ) || "",
+ Usage: "",
+ }));
+}
+
+var getActions = function(charId){
+ return Attacks.find(
+ {charId, enabled: true},
+ {sort: {color: 1, name: 1}}
+ ).map(a => ({
+ Name: a.name,
+ Content: `+${evaluate(charId, a.attackBonus)} to hit, ` +
+ `${evaluateString(charId, a.damage)} ${a.damageType} damage, ` +
+ `${a.details}`,
+ Usage: "",
+ }));
+}
diff --git a/rpg-docs/client/lib/requestAnimationFramePolyfill.js b/rpg-docs/client/lib/requestAnimationFramePolyfill.js
new file mode 100644
index 00000000..0fb801e6
--- /dev/null
+++ b/rpg-docs/client/lib/requestAnimationFramePolyfill.js
@@ -0,0 +1,28 @@
+// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
+// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
+
+// requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel
+
+// MIT license
+var lastTime = 0;
+var vendors = ["ms", "moz", "webkit", "o"];
+for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
+ window.requestAnimationFrame = window[vendors[x] + "RequestAnimationFrame"];
+ window.cancelAnimationFrame = window[vendors[x] + "CancelAnimationFrame"] ||
+ window[vendors[x] + "CancelRequestAnimationFrame"];
+}
+
+if (!window.requestAnimationFrame)
+window.requestAnimationFrame = function(callback, element) {
+ var currTime = new Date().getTime();
+ var timeToCall = Math.max(0, 16 - (currTime - lastTime));
+ var id = window.setTimeout(function() { callback(currTime + timeToCall); },
+ timeToCall);
+ lastTime = currTime + timeToCall;
+ return id;
+};
+
+if (!window.cancelAnimationFrame)
+window.cancelAnimationFrame = function(id) {
+ clearTimeout(id);
+};
diff --git a/rpg-docs/client/views/character/characterSheet.html b/rpg-docs/client/views/character/characterSheet.html
index bb9ce825..cc6f11e3 100644
--- a/rpg-docs/client/views/character/characterSheet.html
+++ b/rpg-docs/client/views/character/characterSheet.html
@@ -24,6 +24,10 @@
Settings
+
+
+ Export to Improved Initiative
+
{{/if}}
diff --git a/rpg-docs/client/views/character/characterSheet.js b/rpg-docs/client/views/character/characterSheet.js
index 006c42f4..c7052c44 100644
--- a/rpg-docs/client/views/character/characterSheet.js
+++ b/rpg-docs/client/views/character/characterSheet.js
@@ -203,4 +203,11 @@ Template.characterSheet.events({
element: event.currentTarget.parentElement.parentElement,
});
},
+ "click #characterExport": function(event, instance){
+ pushDialogStack({
+ data: this,
+ template: "exportDialog",
+ element: event.currentTarget.parentElement.parentElement,
+ });
+ },
});
diff --git a/rpg-docs/client/views/character/effects/effectEdit/effectEdit.html b/rpg-docs/client/views/character/effects/effectEdit/effectEdit.html
index 3ae51b94..078cd946 100644
--- a/rpg-docs/client/views/character/effects/effectEdit/effectEdit.html
+++ b/rpg-docs/client/views/character/effects/effectEdit/effectEdit.html
@@ -20,26 +20,28 @@
- {{#markdown}}{{evaluateString charId description}}{{/markdown}}
+ {{#markdown}}{{evaluateSpellString charId parent.id description}}{{/markdown}}
{{> attacksViewList charId=charId parentId=_id}}
diff --git a/rpg-docs/client/views/character/stats/skillDialog/skillDialog.html b/rpg-docs/client/views/character/stats/skillDialog/skillDialog.html
index 8edf4caa..a0918755 100644
--- a/rpg-docs/client/views/character/stats/skillDialog/skillDialog.html
+++ b/rpg-docs/client/views/character/stats/skillDialog/skillDialog.html
@@ -61,6 +61,18 @@
Total |
{{characterCalculate "skillMod" charId skillName}} |
+ {{#each passiveEffects}}
+
+ | {{sourceName}} |
+ Passive Bonus: {{statValue}} |
+
+ {{/each}}
+ {{#if showPassiveTotal}}
+
+ | Passive Score |
+ {{characterCalculate "passiveSkill" charId skillName}} |
+
+ {{/if}}
diff --git a/rpg-docs/client/views/character/stats/skillDialog/skillDialog.js b/rpg-docs/client/views/character/stats/skillDialog/skillDialog.js
index 759c1f11..d3b5f6cd 100644
--- a/rpg-docs/client/views/character/stats/skillDialog/skillDialog.js
+++ b/rpg-docs/client/views/character/stats/skillDialog/skillDialog.js
@@ -122,7 +122,7 @@ Template.skillDialogView.helpers({
var char = Characters.findOne(this.charId);
if (!char) return;
var prof = Characters.calculate.proficiency(this.charId, this.skillName);
- var proficiencyBonus =
+ var proficiencyBonus =
Characters.calculate.attributeValue(this.charId, "proficiencyBonus");
return prof * proficiencyBonus;
},
@@ -189,6 +189,23 @@ Template.skillDialogView.helpers({
enabled: true,
});
},
+ passiveEffects: function(){
+ return Effects.find({
+ charId: this.charId,
+ stat: this.skillName,
+ operation: "passiveAdd",
+ enabled: true,
+ });
+ },
+ showPassiveTotal: function(){
+ if (this.skillName === "perception") return true;
+ return Effects.find({
+ charId: this.charId,
+ stat: this.skillName,
+ operation: "passiveAdd",
+ enabled: true,
+ }).count();
+ },
ability: function(){
var opts = {fields: {}};
opts.fields[this.skillName] = 1;
diff --git a/rpg-docs/client/views/character/stats/skillRow/skillRow.html b/rpg-docs/client/views/character/stats/skillRow/skillRow.html
index 0b01f919..207dbac2 100644
--- a/rpg-docs/client/views/character/stats/skillRow/skillRow.html
+++ b/rpg-docs/client/views/character/stats/skillRow/skillRow.html
@@ -14,7 +14,7 @@
{{#if conditionalCount}}
*
{{/if}}
- {{#if showPassive}}
+ {{#if isPassiveShown}}
({{characterCalculate "passiveSkill" ../_id skill}})
{{/if}}
diff --git a/rpg-docs/client/views/character/stats/skillRow/skillRow.js b/rpg-docs/client/views/character/stats/skillRow/skillRow.js
index 20915294..25a15d6c 100644
--- a/rpg-docs/client/views/character/stats/skillRow/skillRow.js
+++ b/rpg-docs/client/views/character/stats/skillRow/skillRow.js
@@ -38,4 +38,16 @@ Template.skillRow.helpers({
operation: "conditional",
}).count();
},
+ isPassiveShown: function(){
+ if (this.showPassive === "forced") return true;
+ if (this.showPassive === "ifNeeded"){
+ var charId = Template.parentData()._id;
+ return Effects.find({
+ charId,
+ stat: this.skill,
+ operation: "passiveAdd",
+ enabled: true,
+ }).count();
+ }
+ },
});
diff --git a/rpg-docs/client/views/character/stats/stats.html b/rpg-docs/client/views/character/stats/stats.html
index 4884e337..74d0c8d6 100644
--- a/rpg-docs/client/views/character/stats/stats.html
+++ b/rpg-docs/client/views/character/stats/stats.html
@@ -51,24 +51,24 @@
Skills
- {{> skillRow name="Acrobatics" skill="acrobatics"}}
- {{> skillRow name="Animal Handling" skill="animalHandling"}}
- {{> skillRow name="Arcana" skill="arcana"}}
- {{> skillRow name="Athletics" skill="athletics"}}
- {{> skillRow name="Deception" skill="deception"}}
- {{> skillRow name="History" skill="history"}}
- {{> skillRow name="Insight" skill="insight"}}
- {{> skillRow name="Intimidation" skill="intimidation"}}
- {{> skillRow name="Investigation" skill="investigation"}}
- {{> skillRow name="Medicine" skill="medicine"}}
- {{> skillRow name="Nature" skill="nature"}}
- {{> skillRow name="Perception" skill="perception" showPassive="true"}}
- {{> skillRow name="Performance" skill="performance"}}
- {{> skillRow name="Persuasion" skill="persuasion"}}
- {{> skillRow name="Religion" skill="religion"}}
- {{> skillRow name="Sleight of Hand" skill="sleightOfHand"}}
- {{> skillRow name="Stealth" skill="stealth"}}
- {{> skillRow name="Survival" skill="survival"}}
+ {{> skillRow name="Acrobatics" skill="acrobatics" showPassive="ifNeeded"}}
+ {{> skillRow name="Animal Handling" skill="animalHandling" showPassive="ifNeeded"}}
+ {{> skillRow name="Arcana" skill="arcana" showPassive="ifNeeded"}}
+ {{> skillRow name="Athletics" skill="athletics" showPassive="ifNeeded"}}
+ {{> skillRow name="Deception" skill="deception" showPassive="ifNeeded"}}
+ {{> skillRow name="History" skill="history" showPassive="ifNeeded"}}
+ {{> skillRow name="Insight" skill="insight" showPassive="ifNeeded"}}
+ {{> skillRow name="Intimidation" skill="intimidation" showPassive="ifNeeded"}}
+ {{> skillRow name="Investigation" skill="investigation" showPassive="ifNeeded"}}
+ {{> skillRow name="Medicine" skill="medicine" showPassive="ifNeeded"}}
+ {{> skillRow name="Nature" skill="nature" showPassive="ifNeeded"}}
+ {{> skillRow name="Perception" skill="perception" showPassive="forced"}}
+ {{> skillRow name="Performance" skill="performance" showPassive="ifNeeded"}}
+ {{> skillRow name="Persuasion" skill="persuasion" showPassive="ifNeeded"}}
+ {{> skillRow name="Religion" skill="religion" showPassive="ifNeeded"}}
+ {{> skillRow name="Sleight of Hand" skill="sleightOfHand" showPassive="ifNeeded"}}
+ {{> skillRow name="Stealth" skill="stealth" showPassive="ifNeeded"}}
+ {{> skillRow name="Survival" skill="survival" showPassive="ifNeeded"}}
diff --git a/rpg-docs/client/views/characterList/characterList.css b/rpg-docs/client/views/characterList/characterList.css
index f25313b8..f33acebb 100644
--- a/rpg-docs/client/views/characterList/characterList.css
+++ b/rpg-docs/client/views/characterList/characterList.css
@@ -8,8 +8,16 @@
position: relative;
}
-.character-card .image {
+.partyHeader {
+ display: inline-block;
+}
+.partyHeader iron-icon {
+ visibility: hidden;
+}
+
+.partyHeader:hover iron-icon{
+ visibility: initial;
}
.character-card .initials {
diff --git a/rpg-docs/client/views/characterList/characterList.html b/rpg-docs/client/views/characterList/characterList.html
index 1603eaa5..a5b00dd2 100644
--- a/rpg-docs/client/views/characterList/characterList.html
+++ b/rpg-docs/client/views/characterList/characterList.html
@@ -10,31 +10,27 @@
{{#if currentUser}}
{{#if characters.count}}
+ {{# each party in parties}}
+
+ {{#with party}}
+
+ {{/with}}
+
+ {{# each charactersInParty party._id}}
+ {{> characterCard}}
+ {{/each}}
+ {{> gridPadding class="character-card flex layout vertical" num=12}}
+
+
+ {{/each}}
{{else}}
You don't seem to have any characters yet
@@ -47,9 +43,46 @@
{{/if}}
-
+ {{#fabMenu}}
+
+
+ {{/fabMenu}}
+
+
+
+
+
+ {{#unless picture}}
+
+ {{initials name}}
+
+ {{/unless}}
+
+
+
+ {{name}}
+
+
+ {{alignment}} {{gender}} {{race}}
+
+
+
+
+
+
diff --git a/rpg-docs/client/views/characterList/characterList.js b/rpg-docs/client/views/characterList/characterList.js
index c5971a03..e35a5571 100644
--- a/rpg-docs/client/views/characterList/characterList.js
+++ b/rpg-docs/client/views/characterList/characterList.js
@@ -1,27 +1,57 @@
Template.characterList.helpers({
- characters(){
+ characters() {
var userId = Meteor.userId();
return Characters.find(
- {
- $or: [
- {readers: userId},
- {writers: userId},
- {owner: userId},
- ]
- },
- {
- fields: {name: 1, picture: 1, color: 1, race: 1, alignment: 1, gender: 1},
- sort: {name: 1},
- }
+ {$or: [{readers: userId}, {writers: userId}, {owner: userId}]},
+ {sort: {name: 1}}
);
},
+ parties() {
+ return Parties.find({owner: Meteor.userId()});
+ },
+ charactersInParty(partyId) {
+ var userId = Meteor.userId();
+ var party = Parties.findOne(partyId);
+ return Characters.find(
+ {
+ _id: {$in: party.characters},
+ $or: [{readers: userId}, {writers: userId}, {owner: userId}],
+ },
+ {sort: {name: 1}}
+ );
+ },
+ charactersWithNoParty() {
+ var userId = Meteor.userId();
+ var charArrays = Parties.find({owner: userId}).map(p => p.characters);
+ var partyChars = _.uniq(_.flatten(charArrays));
+ return Characters.find(
+ {
+ _id: {$nin: partyChars},
+ $or: [{readers: userId}, {writers: userId}, {owner: userId}],
+ },
+ {sort: {name: 1}}
+ );
+ },
+});
+
+Template.characterCard.helpers({
initials(name){
return name.replace(/[^A-Z]/g, "");
},
-})
+});
Template.characterList.events({
- "tap .addCharacter": function(event, template) {
+ "click .partyHeader": function(event, instance){
+ pushDialogStack({
+ template: "partyDialog",
+ data: {
+ _id: this._id,
+ startEditing: true,
+ },
+ element: event.currentTarget.parentElement,
+ });
+ },
+ "click .addCharacter": function(event, instance) {
pushDialogStack({
template: "newCharacterDialog",
element: event.currentTarget,
@@ -29,8 +59,23 @@ Template.characterList.events({
if (!character) return;
character.owner = Meteor.userId();
let _id = Characters.insert(character);
- Router.go("characterSheet", {_id});
+ let urlName = getSlug(character.name, {maintainCase: true}) || "-"
+ Router.go("characterSheet", {_id, urlName});
},
})
},
+ "click .addParty": function(event, instance) {
+ var partyId = Parties.insert({
+ owner: Meteor.userId(),
+ });
+ pushDialogStack({
+ template: "partyDialog",
+ data: {
+ _id: partyId,
+ startEditing: true,
+ },
+ element: event.currentTarget,
+ returnElement: instance.find(`.party[data-id='${partyId}']`),
+ });
+ },
});
diff --git a/rpg-docs/client/views/characterList/characterSideList.css b/rpg-docs/client/views/characterList/characterSideList.css
index e48e0b94..1b08159b 100644
--- a/rpg-docs/client/views/characterList/characterSideList.css
+++ b/rpg-docs/client/views/characterList/characterSideList.css
@@ -2,8 +2,21 @@
prevent character names from wrapping
*/
-.character-name {
+.side-list .character-name {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
+
+.side-list .partyHead {
+ font-weight: 500;
+ cursor: pointer;
+}
+
+.side-list .partyHead iron-icon {
+ transition: transform 0.3s ease;
+}
+
+.side-list .partyHead iron-icon.open {
+ transform: rotate(90deg);
+}
diff --git a/rpg-docs/client/views/characterList/characterSideList.html b/rpg-docs/client/views/characterList/characterSideList.html
index e65d7804..8a3fe6fd 100644
--- a/rpg-docs/client/views/characterList/characterSideList.html
+++ b/rpg-docs/client/views/characterList/characterSideList.html
@@ -1,15 +1,31 @@
- {{#if characters.count}}
-
- {{/if}}
+
diff --git a/rpg-docs/client/views/characterList/characterSideList.js b/rpg-docs/client/views/characterList/characterSideList.js
index 915bf87a..18e4e800 100644
--- a/rpg-docs/client/views/characterList/characterSideList.js
+++ b/rpg-docs/client/views/characterList/characterSideList.js
@@ -1,33 +1,50 @@
Template.characterSideList.onCreated(function() {
this.subscribe("characterList");
+ this.openedParties = new ReactiveVar(new Set());
});
Template.characterSideList.helpers({
- characters: function() {
+ parties() {
+ var userId = Meteor.userId();
+ return Parties.find({owner: userId});
+ },
+ charactersInParty() {
var userId = Meteor.userId();
return Characters.find(
{
- $or: [
- {readers: userId},
- {writers: userId},
- {owner: userId},
- ]
+ _id: {$in: this.characters},
+ $or: [{readers: userId}, {writers: userId}, {owner: userId}],
},
- {
- fields: {name: 1},
- sort: {name: 1},
- }
+ {sort: {name: 1}}
);
},
+ charactersWithNoParty() {
+ var userId = Meteor.userId();
+ var charArrays = Parties.find({owner: userId}).map(p => p.characters);
+ var partyChars = _.uniq(_.flatten(charArrays));
+ return Characters.find(
+ {
+ _id: {$nin: partyChars},
+ $or: [{readers: userId}, {writers: userId}, {owner: userId}],
+ },
+ {sort: {name: 1}}
+ );
+ },
+ isOpen(id) {
+ var openedParties = Template.instance().openedParties.get();
+ console.log(openedParties);
+ return openedParties.has(id);
+ },
});
Template.characterSideList.events({
- "tap .singleLineItem": function(event, instance) {
- //Router.go("characterSheet", {_id: this._id});
- $("core-drawer-panel").get(0).closeDrawer();
- },
- "tap core-item": function() {
- Router.go("characterList");
- $("core-drawer-panel").get(0).closeDrawer();
+ "click .partyHead": function(event, instance){
+ var openedParties = instance.openedParties.get();
+ if (openedParties.has(this._id)){
+ openedParties.delete(this._id);
+ } else {
+ openedParties.add(this._id);
+ }
+ instance.openedParties.set(openedParties);
},
});
diff --git a/rpg-docs/client/views/characterList/partyDialog/partyDialog.css b/rpg-docs/client/views/characterList/partyDialog/partyDialog.css
new file mode 100644
index 00000000..f4ddf6ed
--- /dev/null
+++ b/rpg-docs/client/views/characterList/partyDialog/partyDialog.css
@@ -0,0 +1,3 @@
+.partyEdit .inPartyCheckbox {
+ margin-bottom: 8px;
+}
diff --git a/rpg-docs/client/views/characterList/partyDialog/partyDialog.html b/rpg-docs/client/views/characterList/partyDialog/partyDialog.html
new file mode 100644
index 00000000..9c6d1b86
--- /dev/null
+++ b/rpg-docs/client/views/characterList/partyDialog/partyDialog.html
@@ -0,0 +1,32 @@
+
+ {{#with party}}
+ {{#baseDialog title=name hideColor=true startEditing=true}}
+ {{> partyDetails}}
+ {{else}}
+ {{> partyEdit}}
+ {{/baseDialog}}
+ {{/with}}
+
+
+
+
+
+ {{#each character in getCharacters}}
+
{{character.name}}
+ {{/each}}
+
+
+
+
+
+
+
+
+ {{#each allCharacters}}
+
+ {{name}}
+
+ {{/each}}
+
+
diff --git a/rpg-docs/client/views/characterList/partyDialog/partyDialog.js b/rpg-docs/client/views/characterList/partyDialog/partyDialog.js
new file mode 100644
index 00000000..cb1c8e97
--- /dev/null
+++ b/rpg-docs/client/views/characterList/partyDialog/partyDialog.js
@@ -0,0 +1,62 @@
+Template.partyDialog.helpers({
+ party(){
+ return Parties.findOne(this._id);
+ }
+});
+
+Template.partyDetails.helpers({
+ getCharacters (){
+ var userId = Meteor.userId();
+ return Characters.find(
+ {
+ _id: {$in: this.characters},
+ $or: [{readers: userId}, {writers: userId}, {owner: userId}],
+ },
+ {sort: {name: 1}}
+ );
+ }
+});
+
+Template.partyEdit.helpers({
+ allCharacters() {
+ var userId = Meteor.userId();
+ return Characters.find(
+ {$or: [{readers: userId}, {writers: userId}, {owner: userId}]},
+ {sort: {name: 1}}
+ );
+ },
+ charInParty(charId) {
+ return _.contains(Template.parentData().characters, charId);
+ },
+});
+
+Template.partyDialog.events({
+ "click #deleteButton": function(event, instance){
+ Parties.remove(instance.data._id);
+ popDialogStack();
+ },
+ "click #doneEditingButton": function(event, instance){
+ popDialogStack();
+ },
+});
+
+Template.partyEdit.events({
+ "change .inPartyCheckbox": function(event, instance){
+ var currentCharacters = this.characters;
+ var checked = event.currentTarget.checked;
+ var charId = this._id;
+ var partyId = instance.data._id;
+ if (checked){
+ Parties.update(partyId, {$addToSet: {characters: charId}});
+ } else {
+ Parties.update(partyId, {$pull: {characters: charId}});
+ }
+ },
+ "input .partyNameInput": function(event, instance){
+ var name = event.currentTarget.value;
+ Parties.update(this._id, {$set: {name}}, {
+ removeEmptyStrings: false,
+ trimStrings: false,
+ });
+ },
+});
diff --git a/rpg-docs/client/views/paperTemplates/baseDialog/baseDialog.js b/rpg-docs/client/views/paperTemplates/baseDialog/baseDialog.js
index 09526a48..2d51ead3 100644
--- a/rpg-docs/client/views/paperTemplates/baseDialog/baseDialog.js
+++ b/rpg-docs/client/views/paperTemplates/baseDialog/baseDialog.js
@@ -4,11 +4,13 @@ Template.baseDialog.onCreated(function(){
Template.baseDialog.helpers({
editing: function(){
+ if (!Template.parentData() || !Template.parentData().charId) return true;
return Template.instance().editing.get() &&
canEditCharacter(Template.parentData().charId);
},
showEdit: function() {
if (this.hideEdit) return false;
+ if (!Template.parentData() || !Template.parentData().charId) return true;
return canEditCharacter(Template.parentData().charId);
},
});
diff --git a/rpg-docs/lib/functions/evaluate.js b/rpg-docs/lib/functions/evaluate.js
index 9fce8c79..a66eed27 100644
--- a/rpg-docs/lib/functions/evaluate.js
+++ b/rpg-docs/lib/functions/evaluate.js
@@ -8,9 +8,10 @@
})();
//evaluates a calculation string
-evaluate = function(charId, string){
+evaluate = function(charId, string, opts){
+ var spellListId = opts && opts.spellListId;
if (!string) return string;
- string = string.replace(/\b[a-z]+\b/gi, function(sub){
+ string = string.replace(/\b[a-z,1-9]+\b/gi, function(sub){
//fields
if (Schemas.Character.schema(sub)){
return Characters.calculate.fieldValue(charId, sub);
@@ -43,6 +44,12 @@ evaluate = function(charId, string){
if (sub.toUpperCase() === "LEVEL"){
return Characters.calculate.level(charId);
}
+ if (spellListId && sub.toUpperCase() === "DC") {
+ var list = SpellLists.findOne(spellListId);
+ if (list && list.saveDC){
+ return evaluate(charId, list.saveDC);
+ }
+ }
return sub;
});
try {
@@ -66,6 +73,17 @@ evaluateString = function(charId, string){
return result;
};
+evaluateSpellString = function (charId, spellListId, string) {
+ //define brackets as curly brackets around anything that isn't a curly bracket
+ if (!string) return string;
+ var brackets = /\{[^\{\}]*\}/g;
+ var result = string.replace(brackets, function(exp){
+ exp = exp.replace(/(\{|\})/g, ""); //remove curly brackets
+ return evaluate(charId, exp, {spellListId});
+ });
+ return result;
+}
+
//returns the value of the effect if it exists,
//otherwise returns the result of the calculation if it exists,
//otherwise returns 0
diff --git a/rpg-docs/public/custom_components/dicecloud-selector/dicecloud-selectable.html b/rpg-docs/public/custom_components/dicecloud-selector/dicecloud-selectable.html
index fceeba7e..c88c3a30 100644
--- a/rpg-docs/public/custom_components/dicecloud-selector/dicecloud-selectable.html
+++ b/rpg-docs/public/custom_components/dicecloud-selector/dicecloud-selectable.html
@@ -249,7 +249,9 @@
},
_updateItems: function() {
- var nodes = Polymer.dom(this).queryDistributedElements(this.selectable || '*');
+ var nodes = this.selectable
+ ? Polymer.dom(this).querySelectorAll(this.selectable)
+ : Polymer.dom(this).queryDistributedElements('*');
nodes = Array.prototype.filter.call(nodes, this._bindFilterItem);
this._setItems(nodes);
},
diff --git a/rpg-docs/server/migrations/migrations.js b/rpg-docs/server/migrations/migrations.js
index 9eee2347..34e73414 100644
--- a/rpg-docs/server/migrations/migrations.js
+++ b/rpg-docs/server/migrations/migrations.js
@@ -23,127 +23,13 @@ Meteor.methods({
Migrations.add({
version: 1,
- name: "converts effect proficiencies to proficiency objects, removes types from assets",
- up: function() {
- //convert proficiency effects to proficiency objects
- Effects.find({operation: "proficiency"}).forEach(function(effect){
- var type = "skill";
- if (_.contains(SAVES, effect.stat)) type = "save";
- Proficiencies.insert({
- charId: effect.charId,
- name: effect.stat,
- value: effect.value,
- parent: _.clone(effect.parent),
- type: type,
- enabled: effect.enabled,
- }, function(err){
- if (!err) Effects.remove(effect._id);
- });
- });
- //store type as a parent group if it's needed
- Effects.find({"parent.collection": "Characters"}).forEach(function(e){
- Effects.update(e._id, {$set: {"parent.group": e.type}});
- });
- Attacks.find({"parent.collection": "Characters"}).forEach(function(a){
- Attacks.update(a._id, {$set: {"parent.group": a.type}});
- });
- //remove type
- Effects.update({}, {$unset: {type: ""}}, {validate: false, multi: true});
- Attacks.update({}, {$unset: {type: ""}}, {validate: false, multi: true});
- //remove languages and proficiencies
- Characters.update(
- {},
- {$unset: {languages: "", proficiencies: ""}},
- {validate: false, multi: true}
- );
- },
-});
-
-Migrations.add({
- version: 2,
- name: "Converts attacks from damage dice and damage bonus to a string with curly bracket calculations, adds settings.showIncrement to items",
- up: function() {
- //update attacks
- Attacks.find({}).forEach(function(attack) {
- if (!attack.damage && attack.damageDice && attack.damageBonus){
- var newDamage = attack.damageDice +
- " + {" + attack.damageBonus + "}";
- Attacks.update(
- attack._id,
- {
- $unset: {
- damageBonus: "",
- damageDice: "",
- },
- $set: {
- damage: newDamage
- },
- },
- {validate: false});
- }
- });
- //update Items
- Items.update(
- {settings: undefined},
- {$set: {"settings.showIncrement" : false}},
- {validate: false, multi: true}
- );
- },
-});
-
-Migrations.add({
- version: 3,
- name: "Converts attacks from damage dice and damage bonus to a string with curly bracket calculations, adds settings.showIncrement to items",
+ name: "Gives all characters a URL name",
up: function() {
//update characters
- Characters.update(
- {"settings.useVariantEncumbrance": undefined},
- {$set: {"settings.useVariantEncumbrance" : false}},
- {validate: false, multi: true}
- );
- Characters.update(
- {"settings.useStandardEncumbrance": undefined},
- {$set: {"settings.useStandardEncumbrance" : true}},
- {validate: false, multi: true}
- );
- },
-});
-
-Migrations.add({
- version: 4,
- name: "Adds an effect to give characters a base carry capacity",
- up: function() {
- //update characters
-
Characters.find({}).forEach(function(char){
- Characters.update(char._id, {
- $set: {
- carryMultiplier: {
- adjustment: 0,
- reset: "longRest",
- }
- }
- });
- var effect = Effects.findOne({
- charId: char._id, name: "Natural Carrying Capacity"
- });
- if (effect) return;
- Effects.insert({
- charId: char._id,
- name: "Natural Carrying Capacity",
- stat: "carryMultiplier",
- operation: "base",
- value: "1",
- parent: {
- id: char._id,
- collection: "Characters",
- group: "Inate",
- },
- });
- effect = Effects.findOne({
- charId: char._id, name: "Natural Carrying Capacity"
- });
- if (!effect) throw "Carry capacity effect should be set by now."
+ if (char.urlName) return;
+ var urlName = getSlug(char.name, {maintainCase: true}) || "-";
+ Characters.update(char._id, {$set: {urlName}});
});
},
down: function(){
diff --git a/rpg-docs/server/publications/characterList.js b/rpg-docs/server/publications/characterList.js
index b8c24b83..a33dd71d 100644
--- a/rpg-docs/server/publications/characterList.js
+++ b/rpg-docs/server/publications/characterList.js
@@ -4,26 +4,24 @@ Meteor.publish("characterList", function(){
this.ready();
return;
}
- return Characters.find(
- {
- $or: [
- {readers: userId},
- {writers: userId},
- {owner: userId},
- ]
- },
- {
- fields: {
- name: 1,
- race: 1,
- alignment: 1,
- gender: 1,
- readers: 1,
- writers:1,
- owner: 1,
- color: 1,
- picture: 1,
+ return [
+ Characters.find(
+ {$or: [{readers: userId}, {writers: userId}, {owner: userId}]},
+ {
+ fields: {
+ name: 1,
+ urlName: 1,
+ race: 1,
+ alignment: 1,
+ gender: 1,
+ readers: 1,
+ writers:1,
+ owner: 1,
+ color: 1,
+ picture: 1,
+ }
}
- }
- );
+ ),
+ Parties.find({owner: userId}),
+ ];
});