This commit is contained in:
Thaum
2014-11-03 06:17:35 +00:00
commit 2a82e39cf0
34 changed files with 1133 additions and 0 deletions

16
.codio Normal file
View File

@@ -0,0 +1,16 @@
{
// Configure your Run and Preview buttons here.
// Run button configuration
"commands": {
"Node version": "node --version"
},
// Preview button configuration
"preview": {
"Project Index (static)": "https://{{domain}}/{{index}}",
"Current File (static)": "https://{{domain}}/{{filepath}}",
"Box URL": "http://{{domain}}:3000/",
"Box URL SSL": "https://{{domain}}:9500/"
}
}

26
.guides/sections.md Normal file
View File

@@ -0,0 +1,26 @@
---
title: Example section
files: []
editable: true
layout: 2-panels-tree
---
Some **awesome** content 1
---
title: Example section 3
files: []
editable: true
layout: ""
---
Some **awesome** content 3
---
title: Example section 4
files: []
editable: true
layout: ""
---
Some **awesome** content 4

1
.guides/styles.css Normal file
View File

@@ -0,0 +1 @@
/* Place you styles here */

37
TODO.md Normal file
View File

@@ -0,0 +1,37 @@
Character.js is under construction... expect broken character sheets
issues
------
* hot code pushes don't apply transforms correctly
Characters attributes and buffs
-------------------------------
Characters currently have attributes and skills that can take bonuses and multipliers.
When a feature is activated or enabled on a characer or a piece of equipment is equipped
the buffs and effects should be applied to the correct attribute or skill. When the
equipment or feature is removed or deactivated, the effects should be removed as well.
Effects need the following data as a bare minimum:
* attribute or skill to effect
* name
* value
For example, plate would be an object like this:
{
name: "Plate Armor" ,
effects: [
{stat: "skills.dexArmor.min", value: 0},
{stat: "skills.dexArmor.max", value: 0},
{stat: "attributes.armor.min", value: 18},
{stat: "skills.stealth.disadvantage", value 1} //disadvantage doesn't need a value
]
}
See Conditions for this implemented.
The effects ultimately need to be pushed to the correct array when applied: `attributes.armor.min.push({name: "Plate Armor", type: "Equpiment", value: 18})`
They will also need to be removed correctly by the thing that applied them, or after some time.

View File

@@ -0,0 +1,6 @@
# This file contains information which helps Meteor properly upgrade your
# app when you run 'meteor update'. You should check it into version control
# with your project.
notices-for-0.9.0
notices-for-0.9.1

1
rpg-docs/.meteor/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
local

7
rpg-docs/.meteor/.id Normal file
View File

@@ -0,0 +1,7 @@
# This file contains a token that is unique to your project.
# Check it into your repository along with the rest of this directory.
# It can be used for purposes such as:
# - ensuring you don't accidentally deploy one app on top of another
# - providing package authors with aggregated statistics
1xg0ir21aq4e081rfkzg3

View File

@@ -0,0 +1 @@

13
rpg-docs/.meteor/packages Normal file
View File

@@ -0,0 +1,13 @@
# Meteor packages used by this project, one per line.
#
# 'meteor add' and 'meteor remove' will edit this file for you,
# but you can also edit it by hand.
meteor-platform
autopublish
insecure
iron:router
accounts-password
accounts-ui
random

1
rpg-docs/.meteor/release Normal file
View File

@@ -0,0 +1 @@
METEOR@0.9.2.2

66
rpg-docs/.meteor/versions Normal file
View File

@@ -0,0 +1,66 @@
accounts-base@1.1.0
accounts-password@1.0.1
accounts-ui-unstyled@1.1.1
accounts-ui@1.1.0
application-configuration@1.0.2
autopublish@1.0.0
autoupdate@1.1.0
base64@1.0.0
binary-heap@1.0.0
blaze-tools@1.0.0
blaze@2.0.0
boilerplate-generator@1.0.0
callback-hook@1.0.0
check@1.0.0
ctl-helper@1.0.3
ctl@1.0.1
ddp@1.0.8
deps@1.0.3
ejson@1.0.2
email@1.0.2
fastclick@1.0.0
follower-livedata@1.0.1
geojson-utils@1.0.0
html-tools@1.0.0
htmljs@1.0.1
http@1.0.5
id-map@1.0.0
insecure@1.0.0
iron:core@0.3.4
iron:dynamic-template@0.4.1
iron:layout@0.4.1
iron:router@0.9.4
jquery@1.0.0
json@1.0.0
less@1.0.8
livedata@1.0.9
localstorage@1.0.0
logging@1.0.3
meteor-platform@1.1.0
meteor@1.1.0
minifiers@1.1.0
minimongo@1.0.3
mobile-status-bar@1.0.0
mongo@1.0.5
npm-bcrypt@0.7.7
observe-sequence@1.0.2
ordered-dict@1.0.0
random@1.0.0
reactive-dict@1.0.2
reactive-var@1.0.1
reload@1.1.0
retry@1.0.0
routepolicy@1.0.1
service-configuration@1.0.1
session@1.0.1
sha@1.0.0
spacebars-compiler@1.0.2
spacebars@1.0.1
srp@1.0.0
templating@1.0.6
tracker@1.0.2
ui@1.0.2
underscore@1.0.0
url@1.0.0
webapp-hashing@1.0.0
webapp@1.1.1

View File

@@ -0,0 +1,9 @@
Armor = function(name, value){
this.name = name;
this.value = value;
this.equipped = true;
this.dexModMax = 20;
this.dexModMin = -20;
this.strengthNeeded = 0;
this.stealthDisadvantage = false;
}

View File

@@ -0,0 +1,324 @@
//set up the collection for characters
Characters = new Meteor.Collection("characters", {
//transform function alters the object returned by the database
transform: function (doc) {
//extend character with its protoypal functions
var newInstance = Object.create(protoCharacter);
doc = _.extend(newInstance, doc);
return doc;
}
});
//Attributes are numerical values
Attribute = function(base){
this.base = base; //the unmodified value of the attribute
//effects of the form {name: "Ring of Protection", value: 1}
this.add = []; //bonuses added to the attribute
this.mul = []; //multipliers to the attribute (after adding bonuses)
this.min = []; //effects setting the minimum value of the attribute
this.max = []; //effects setting the maximum value of the attribute
this.conditional = []; //conditional modifiers
}
var attributes = [
"strength",
"dexterity",
"constitution",
"intelligence",
"wisdom",
"charisma",
"hitPoints",
"proficiencyBonus",
"speed",
"armor",
"weight",
"weightCarried",
"age",
"ageRate",
"level1SpellSlots",
"level2SpellSlots",
"level3SpellSlots",
"level4SpellSlots",
"level5SpellSlots",
"level6SpellSlots",
"level7SpellSlots",
"level8SpellSlots",
"level9SpellSlots",
"ki",
"sorceryPoints",
"rages"
];
//Skills are bonuses to rolls: "+2" etc.
//They are based off of some ability
Skill = function(ability){
//proficiencies of the form {name: "Jack of all Trades", value: 0.5}
//only the highest is used
this.proficiency = [];
//ability name that this skill uses as base for roll
this.ability = ability;
this.add = [];
this.mul = [];
this.min = [];
this.max = [];
this.advantage = []; //effects granting advantage
this.disadvantage = [];
this.passiveAdd = []; //only added to passive checks
this.fail = []; //all checks are failed
this.conditional = []; //conditional modifiers
}
var skills = [
{skill: "strengthSave", ability: "strength"},
{skill: "dexteritySave", ability: "dexterity"},
{skill: "constitutionSave", ability: "constitution"},
{skill: "intelligenceSave", ability: "intelligence"},
{skill: "wisdomSave", ability: "wisdom"},
{skill: "charismaSave", ability: "charisma"},
{skill: "acrobatics", ability: "dexterity"},
{skill: "animalHandling", ability: "wisdom"},
{skill: "arcana",ability: "intelligence"},
{skill: "athletics", ability: "strength"},
{skill: "deception", ability: "charisma"},
{skill: "history", ability: "intelligence"},
{skill: "insight", ability: "wisdom"},
{skill: "intimidation", ability: "charisma"},
{skill: "investigation", ability: "intelligence"},
{skill: "medicine", ability: "wisdom"},
{skill: "nature", ability: "intelligence"},
{skill: "perception", ability: "wisdom"},
{skill: "performance", ability: "charisma"},
{skill: "persuasion", ability: "charisma"},
{skill: "religion", ability: "intelligence"},
{skill: "sleightOfHand", ability: "dexterity"},
{skill: "stealth", ability: "dexterity"},
{skill: "survival", ability: "wisdom"},
{skill: "initiative", ability: "dexterity"},
{skill: "strengthAttack", ability: "strength"},
{skill: "dexterityAttack", ability: "dexterity"},
{skill: "rangedAttack", ability: "dexterity"},
{skill: "dexterityArmor", ability: "dexterity"}
];
//Plain text fields for the character
var strings = [
"name",
"alignment",
"gender",
"race",
"description",
"personality",
"ideals",
"bonds",
"flaws"
];
//Data structure for the character
//no functions can be added to this constructor
Character = function(owner){
//attributes
this.attributes = {};
for(var i = 0, l = attributes.length; i < l; i++){
this.attributes[attributes[i]] = new Attribute(0);
}
//add 10 and dex bonus to armor
this.attributes.armor.add.push({name: "Base Armor Class", value: 10});
this.attributes.armor.add.push({name: "Dexterity Modifier", value: "skillMod skills.dexterityArmor"});
//skills
this.skills = {};
for(var i = 0, l = skills.length; i < l; i++){
this.skills[skills[i].skill] = new Skill(skills[i].ability);
}
this.deathSave = {
success : 0,
fail: 0
};
this.hitDice = [];
this.weaponProficiencies = [];
this.toolProficiencies = [];
this.languages = [];
this.features = [];
this.spells = [];
this.classes = [];
this.experience = new Experience();
this.vulnerability = {};
for(var i = 0, l = DamageTypes.length; i < l; i++){
this.vulnerability[DamageTypes[i]] = new Attribute(1);
this.vulnerability[DamageTypes[i]].min.push({name: "Resistance doesn't stack", value: 0.5});
this.vulnerability[DamageTypes[i]].max.push({name: "Vulnerability doesn't stack", value: 2});
}
//admin
this.owner = owner;
this.readers = [];
this.writers = [];
}
//functions and calculated values go here
var protoCharacter = {
attributeValue: function(attribute){
if (attribute === undefined) return;
//base value
var value = attribute.base;
//add all values in add array
for(var i = 0, l = attribute.add.length; i < l; i++){
var add = pop(attribute.add[i].value, this);
value += add ;
}
//multiply all values in mul array
for(var i = 0, l = attribute.mul.length; i < l; i++){
var mul = pop(attribute.mul[i], this);
value *= mul;
}
//largest min
for(var i = 0, l = attribute.min.length; i < l; i++){
var min = pop(attribute.min[i], this);
value = value > min? value : min;
}
//smallest max
for(var i = 0, l = attribute.max.length; i < l; i++){
var max = pop(attribute.max[i], this);
value = value < max? value : max;
}
return value;
},
skillMod: function(skill){
//get the final value of the ability score
var ability = this.attributeValue(this.attributes[skill.ability]);
//base modifier
var mod = +getMod(ability)
//multiply proficiency bonus by largest value in proficiency array
var prof = 0;
for(var i = 0, l = skill.proficiency.length; i < l; i++){
var newProf = pop(skill.proficiency[i].value, this);
if (newProf > prof){
prof = newProf;
}
}
//add multiplied proficiency bonus to modifier
mod += prof * this.attributeValue(this.attributes.proficiencyBonus);
//add all values in add array
for(var i = 0, l = skill.add.length; i < l; i++){
mod += pop(skill.add[i].value, this);
}
//multiply all values in mul array
for(var i = 0, l = skill.mul.length; i < l; i++){
mod *= pop(skill.mul[i].value, this);
}
//largest min
for(var i = 0, l = skill.min.length; i < l; i++){
var min = pop(skill.min[i], this);
mod = mod > min? mod : min;
}
//smallest max
for(var i = 0, l = skill.max.length; i < l; i++){
var max = pop(skill.max[i], this);
mod = mod < max? mod : max;
}
return signedString(mod);
},
passiveSkill: function(skill){
var mod = +this.skillMod(skill);
var value = 10 + mod;
for(var i = 0, l = skill.passiveAdd.length; i < l; i++){
value += pop(skill.passiveAdd[i].value, this);
}
return value;
//TODO decide whether (dis)advantage gives (-)+5 to passive checks
},
abilityMod: function(attribute){
return signedString(getMod(this.attributeValue(attribute)));
},
passiveAbility: function(attribute){
var mod = +getMod(this.attributeValue(attribute));
return 10 + mod;
}
}
getMod = function(score){
return Math.floor((score-10)/2);
}
signedString = function(number){
return number > 0? "+" + number : "" + number;
}
// turns dot notation strings into keys of root
// argument formats:
// 157, anything -> 157
// "some.number", object -> object.some.number
// "some.function", object -> object.some.function()
// "some.function arg1 arg2", object -> object.some.function(arg1, arg2)
pop = function(input, root){
if(typeof(input) === "string"){
//we need root for this part
if(root === undefined) return;
//this is a likely to fail if the string is malformed
try{
//split over spaces
var parts = input.split(" ");
//for each word
for (var i = 0; i < parts.length; i++){
//split over dots
var str = parts[i].split(".");
//start at root
parts[i] = root;
//for each word between dots
for (var j = 0; j < str.length; j++){
parts[i] = parts[i][str[j]];
}
}
//pull the first word out, might be a function
var func = parts.splice(0, 1)[0];
//if it's a function, apply the arguments to it
if(_.isFunction(func)) return +func.apply(root, parts);
//if it's a number, return it
if(!isNaN(func)) return +func;
} catch (err) {
//TODO pokemon catch statement is bad
//"gotta catch em all"
console.log(err);
return;
}
}
return +input;
}

View File

@@ -0,0 +1,118 @@
DamageTypes = [
"acid", "bludgeoning", "cold", "fire", "force",
"lightning", "necrotic", "piercing", "poison", "psychic",
"radiant", "slashing", "thunder"
]
Conditions = {};
Conditions.Blinded = {
description: ["a blinded creature can't see and automatically fails any ability check that requires sight.",
"Attack rolls against the creature have advantage, and the creature's attack rolls have disadvantage."]
}
Conditions.Charmed = {
description: ["A charmed creature can't attack the charmer or target the charmer with harmful abilities or magical effects",
"The charmer has advantage on any ability check to interact socially with the creature."]
}
Conditions.Deafened = {
description: ["A deafened creature can't hear and automatically fails any ability check that requires hearing"]
}
Conditions.Frightened = {
description: []
}
Conditions.Grappled = {
description: [
"A grappled creature's speed becomes 0, and it can't benefit from any bonuses to its speed",
"The condition ends if the grappler is incapacitated",
"The conditions also ends if if an effect removes the\
grappled creature from the reach of the grappler or grappling\
effect, such as when a creature is hurled\
away by the thunderwave spell."
],
effects: [
{stat: "attributes.speed.maximum", value: 0}
]
}
Conditions.Incapacitated = {
effects: [
{stat: "attributes.actions.maximum", value: 0},
{stat: "attributes.reactions.maximum", value: 0},
{stat: "attributes.bonusActions.maximum", value: 0}
]
}
Conditions.Invisible = {
}
Conditions.Paralyzed = {
//implies incapacitated
effects: [
{stat: "skills.strengthSave.fail", value: 1},
{stat: "skills.dexteritySave.fail", value: 1},
{stat: "attributes.speed.maximum", value: 0}
]
}
_.extend(Conditions.Paralyzed, Conditions.Incapacitated);
Conditions.Petrified = {
effects: [
{stat: "attributes.weight.mul", value: 10},
{stat: "attributes.ageRate.max", value: 0},
{stat: "attributes.ageRate.min", value: 0},
{stat: "skills.strengthSave.fail", value: 1},
{stat: "skills.dexteritySave.fail", value: 1},
{stat: "attributes.speed.maximum", value: 0}
]
}
for(var i = 0, l = DamageTypes.length; i < l; i++){
var str = "vulnerability." + DamageTypes[i] + ".mul"
Conditions.Petrified.effects.push({stat: str, value: 0.5});
}
_.extend(Conditions.Petrified, Conditions.Incapacitated);
Conditions.Poisoned = {
description: []
}
Conditions.Prone = {
description: [],
effects: [
{stat: "skills.strengthAttack.disadvantage", value: 1},
{stat: "skills.dexterityAttack.disadvantage", value: 1},
{stat: "skills.rangedAttack.disadvantage", value: 1}
]
}
Conditions.Restrained = {
effects: [
{stat: "attributes.speed.maximum", value: 0}
]
}
Conditions.Stunned = {
//implies incapacitated
effects: [
{stat: "attributes.speed.maximum", value: 0},
{stat: "skills.strengthSave.fail", value: 1},
{stat: "skills.dexteritySave.fail", value: 1}
]
}
_.extend(Conditions.Stunned, Conditions.Incapacitated);
Conditions.Unconscious = {
//implies incapacitated
//implies prone
effects: [
{stat: "attributes.speed.maximum", value: 0},
{stat: "skills.strengthSave.fail", value: 1},
{stat: "skills.dexteritySave.fail", value: 1}
]
}
_.extend(Conditions.Unconscious, Conditions.Incapacitated);
_.extend(Conditions.Unconscious, Conditions.Prone);

View File

@@ -0,0 +1,52 @@
Experience = function(){
this.total = 0;
this.events = [];
this.level = 0;
}
Experience.prototype.addEvent = function(description, value){
this.events.push({
"description": description,
"value": value
})
this.total += value;
this.level = this.getLevel();
}
Experience.prototype.removeEvent = function(index){
this.total -= this.events[index].value;
this.events.splice(index,1);
this.level = this.getLevel();
}
Experience.prototype.refreshTotal = function(){
this.total = 0;
for(var i = 0, length = this.events.length; i < length; i++){
this.total += this.events[i].value;
}
this.level = this.getLevel();
}
Experience.prototype.getLevel = function(){
var xp = this.total;
if(xp > 355000) return 20;
if(xp > 305000) return 19;
if(xp > 265000) return 18;
if(xp > 225000) return 17;
if(xp > 195000) return 16;
if(xp > 165000) return 15;
if(xp > 140000) return 14;
if(xp > 120000) return 13;
if(xp > 100000) return 12;
if(xp > 85000) return 11;
if(xp > 64000) return 10;
if(xp > 48000) return 9;
if(xp > 34000) return 8;
if(xp > 23000) return 7;
if(xp > 14000) return 6;
if(xp > 6500) return 5;
if(xp > 2700) return 4;
if(xp > 900) return 3;
if(xp > 300) return 2;
return 1;
}

View File

@@ -0,0 +1,4 @@
Feature = function(name){
this.name = name;
this.description = description;
}

View File

@@ -0,0 +1,6 @@
HitDice = function(sides){
this.sides = sides;
this.bonus = 0;
this.number = 0;
this.max = 0;
}

View File

@@ -0,0 +1,11 @@
Spell = function(name){
this.name = name;
this.level = 0;
this.school = "";
this.range = "";
this.verbal = false;
this.somatic = false;
this.material = false;
this.duration = "";
this.description = "";
}

View File

@@ -0,0 +1,7 @@
Containers = new Meteor.Collection('containers');
Container = function(name, owner){
this.name = name;
this.owner = owner;
this.isCarried = true;
}

View File

@@ -0,0 +1,10 @@
Items = new Meteor.Collection('items');
Item = function(name, container){
this.name = name;
this.container = container;
this.quantity = 1;
this.weight = 0.0;
this.value = 0;//value in gold pieces
this.description = "";
}

View File

@@ -0,0 +1,44 @@
roll = function (n, d){
if(!isNaN(n)){
//first digit is a number
if(d === undefined){
d = n;
n = 1;
}
if(n > 500){
console.log(n + " > 500, cannot lift that many dice to roll them");
return;
}
var result = {sum: 0, rolls: []};
for (var i = 0; i < n; i++){
var roll = Math.floor(Random.fraction() * d + 1)
result.sum += roll;
result.rolls.push(roll);
}
return result;
}
console.log("rolling dice failed for inputs: ", n, d);
return {sum: 0, rolls: []};
}
rollDropLow = function(n, d, drop){
var r = roll(n,d)
r.rolls.sort(function(a, b){return a-b}); //sort ascending
r.rolls.splice(0, drop); //remove the lowest elements
r.sum = 0;
for (var i = 0, l = r.rolls.length; i , l ; i++){
sum += r.rolls[i];
}
return r;
}
rollDropHigh = function(n, d, drop){
var r = roll(n,d)
r.rolls.sort(function(a, b){return b-a}); //sort descending
r.rolls.splice(0, drop); //remove the highest elements
r.sum = 0;
for (var i = 0, l = r.rolls.length; i , l ; i++){
sum += r.rolls[i];
}
return r;
}

20
rpg-docs/Routes/Routes.js Normal file
View File

@@ -0,0 +1,20 @@
Router.map( function () {
this.route('home', {
path: '/',
data: { characters: function(){
console.log('id ' + Meteor.userId());
return Characters.find({owner: Meteor.userId()})
}
}
});
this.route('character', {
path: '/character/:_id',
notFoundTemplate: 'characterNotFound',
data: function() {
character = Characters.findOne({_id: this.params._id});
if (character) character.items = Items.find({owner: this.params._id});
return character;
}
});
});

View File

@@ -0,0 +1,81 @@
.flexContainer {
display: flex;
flex-wrap: wrap;
}
#abilityScores {
text-align: center;
flex-basis: 120px;
flex-grow: 1;
max-width: 340px;
}
/*Float boxes are indivisble, have shadows*/
.floatBox{
border: 2px solid black;
border-radius: 0px 0px 10px 10px;
padding: 5px;
margin: 5px;
background: white;
}
/* headings in floatboxes */
.floatBox h2{
background: black;
color: white;
font-size: 1em;
margin: 0px -5px 0px -5px;
padding: 2px 15px;
}
.floatBox h2:first-child{
margin: -5px -5px 0px -5px;
}
.floatBox.rounded {
border-radius: 10px;
display: inline-block;
text-align: center;
}
/* Ability scores */
.floatBox.ability {
border-radius: 10px;
width: 85px;
text-align: center;
margin: 5px 5px 20px 5px;
display: inline-block;
}
.abilityName {
}
.abilityScore {
font-size: 2em;
}
.abilityMod {
margin: 0 auto -15px;
padding: 2px;
border: 2px solid black;
border-radius: 5px;
background: white;
width: 40px;
}
/* Stats */
#armorClassBox {
width: 90px;
height: 100px;
background-image: url('/svg/ac.svg');
background-repeat: no-repeat;
text-align: center;
display: flex;
flex-direction: column;
justify-content: center;
}
.statValue {
font-size: 2em;
}

View File

@@ -0,0 +1,7 @@
root {
display: block;
}
body {
font-family: sans-serif;
}

View File

@@ -0,0 +1,68 @@
<template name = "abilities">
<div class="ability floatBox">
<div class ="abilityName">
Strength
</div>
<div class="abilityScore">
{{attributeValue attributes.strength}}
</div>
<div class="abilityMod">
{{abilityMod attributes.strength}}
</div>
</div>
<div class="ability floatBox">
<div class ="abilityName">
Dexterity
</div>
<div class="abilityScore">
{{attributeValue attributes.dexterity}}
</div>
<div class="abilityMod">
{{abilityMod attributes.dexterity}}
</div>
</div>
<div class="ability floatBox">
<div class ="abilityName">
Constitution
</div>
<div class="abilityScore">
{{attributeValue attributes.constitution}}
</div>
<div class="abilityMod">
{{abilityMod attributes.constitution}}
</div>
</div>
<div class="ability floatBox">
<div class ="abilityName">
Intelligence
</div>
<div class="abilityScore">
{{attributeValue attributes.intelligence}}
</div>
<div class="abilityMod">
{{abilityMod attributes.intelligence}}
</div>
</div>
<div class="ability floatBox">
<div class ="abilityName">
Wisdom
</div>
<div class="abilityScore">
{{attributeValue attributes.wisdom}}
</div>
<div class="abilityMod">
{{abilityMod attributes.wisdom}}
</div>
</div>
<div class="ability floatBox">
<div class ="abilityName">
Charisma
</div>
<div class="abilityScore">
{{attributeValue attributes.charisma}}
</div>
<div class="abilityMod">
{{abilityMod attributes.charisma}}
</div>
</div>
</template>

View File

@@ -0,0 +1,21 @@
<template name="character">
<div>
{{> characterName}}
</div>
<div class="flexContainer">
<div id="abilityScores" class="flexItem">
{{> abilities}}
</div>
<div class="flexItem">
<div>
Proficiency Bonus {{proficiencyBonus}}
</div>
<div id="savesAndSkills" class="floatBox">
{{> savesAndSkills}}
</div>
</div>
<div id="stats" class="flexItem">
{{> characterStats}}
</div>
</div>
</template>

View File

@@ -0,0 +1,11 @@
<template name="characterName">
<div id="level">
{{experience.level}}
</div>
<div id="name">
{{name}}
</div>
<div id="role">
{{alignment}} {{gender}} {{race}} {{#each class}} {{this}} {{/each}}
</div>
</template>

View File

@@ -0,0 +1,50 @@
<template name="characterStats">
<div id="hitPointBox">
Hit Points
<div id="hitPoints">
<input type="number" value={{attributeValue attributes.hitPoints}}>
</div>
<div id="maxHitPoints">
{{attributeValue attributes.maxHitPoints}}
</div>
</div>
<div id="armorClassBox">
<div id="armorClass">
<div id="armorClassValue" class="statValue">
{{attributeValue attributes.armor}}
</div>
<div id="armorClassLabel">
Armor<br>Class
</div>
</div>
</div>
<div id="amorBox">
</div>
<div id="initiativeBox" class="floatBox rounded">
<div class="statValue">
{{skillMod skills.initiative}}
</div>
Initiative
</div>
<div id="speedBox" class="floatBox rounded">
<div class="statValue">
{{attributeValue attributes.speed}}
</div>
Speed
</div>
<div id="passivePerceptionBox" class="floatBox rounded">
<div class="statValue">
{{passiveSkill skills.perception}}
</div>
Passive Perception
</div>
<div>
<div>
Death Saves
</div><div>
Successes {{deathSaveSuccess}}
</div><div>
Failures {{deathSaveFail}}
</div>
</div>
</template>

View File

@@ -0,0 +1,52 @@
<template name="savesAndSkills">
<h2>Saving Throws</h2>
<div>
Strength Save {{skillMod skills.strengthSave}}
</div><div>
Dexterity Save {{skillMod skills.dexteritySave}}
</div><div>
Constitution Save {{skillMod skills.constitutionSave}}
</div><div>
Intelligence Save {{skillMod skills.intelligenceSave}}
</div><div>
Wisdom Save {{skillMod skills.wisdomSave}}
</div><div>
Charisma Save {{skillMod skills.charismaSave}}
</div>
<h2>Skills</h2>
<div>
Acrobatics {{skillMod skills.acrobatics}}
</div><div>
Animal Handling {{skillMod skills.animalHandling}}
</div><div>
Arcana {{skillMod skills.arcana}}
</div><div>
Athletics {{skillMod skills.athletics}}
</div><div>
Deception {{skillMod skills.deception}}
</div><div>
History {{skillMod skills.history}}
</div><div>
Insight {{skillMod skills.insight}}
</div><div>
Intimidation {{skillMod skills.intimidation}}
</div><div>
Investigation {{skillMod skills.investigation}}
</div><div>
Medicine {{skillMod skills.medicine}}
</div><div>
Nature {{skillMod skills.nature}}
</div><div>
Perception {{skillMod skills.perception}}
</div><div>
Persuasion {{skillMod skills.persuasion}}
</div><div>
Religion {{skillMod skills.religion}}
</div><div>
Sleight of Hand {{skillMod skills.sleightOfHand}}
</div><div>
Stealth {{skillMod skills.stealth}}
</div><div>
Survival {{skillMod skills.survival}}
</div>
</template>

View File

@@ -0,0 +1,10 @@
<template name="home">
{{>loginButtons}}
<div>
{{# each characters}}
<li><a href="{{ pathFor 'character' }} ">{{_id}}</a></li>
{{/each}}
</div>
<input id="addCharacter" type="button" value="Add Character">
<input id="nukeCharacters" type="button" value="Clear all characters">
</template>

View File

@@ -0,0 +1,11 @@
Template.home.events({
"click #addCharacter": function (event, template) {
Characters.insert(new Character(Meteor.userId()));
},
"click #nukeCharacters": function(event, template){
while(true){
if(!Characters.findOne()) break;
Characters.remove(Characters.findOne()._id);
}
}
});

View File

@@ -0,0 +1,4 @@
<head>
<title>RPG Docs</title>
<meta name="viewport" content="width=device-width">
</head>

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
width="90"
height="100"
id="svg2">
<defs
id="defs4" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-952.36218)"
id="layer1">
<path
d="m 15,962.36218 c -0.870551,10.48175 -8.4321358,17.45735 -13,18 l 0,23.00002 c 5.0277793,16.7056 22.831231,42.1683 43,47 19.444592,-3.432 35.858861,-26.1733 43,-47 l 0,-23.00002 c -7.815979,-1.16093 -10.356092,-11.69154 -12,-18 l -31,-8 z"
id="path2985"
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

2
startup.sh Normal file
View File

@@ -0,0 +1,2 @@
cd rpg-docs
export MONGO_URL=mongodb://thaum:pandas@linus.mongohq.com:10050/green_meteor