diff --git a/Dockerfile b/Dockerfile old mode 100755 new mode 100644 diff --git a/Dockerfile.dev b/Dockerfile.dev old mode 100755 new mode 100644 diff --git a/Gemfile b/Gemfile old mode 100755 new mode 100644 diff --git a/Gemfile.lock b/Gemfile.lock old mode 100755 new mode 100644 diff --git a/deploy.md b/deploy.md old mode 100755 new mode 100644 index ec2f7d0..e4e7ed2 --- a/deploy.md +++ b/deploy.md @@ -7,4 +7,136 @@ heroku login heroku container:login heroku container:push web -a charred-black heroku container:release web -a charred-black -heroku open \ No newline at end of file +heroku open + +## Changelog + +2.0.1 - update + +First Mate LP should be selectable with 2 seafaring LPs. If Son of a Gun is the first LP, it is not selectable. +Example: Son of a Gun -> Officer's Mate -> First Mate. + +2.0.0 - Charred Black release +gold character pdf +wizard burner lifepaths + + +## Beliefs + +I welcome community contributions, and you are welcome to fork this source code if you want to go your own way. As the maintainer, +here's what you can expect from me when I judge contributions. + +### Do One Thing Well + +Charred Black is a character creation utility. You are welcome to use the data, source code, or character files in the creation +of other gaming tools, but let's keep this tool focused on one thing and do it really well. + +### Stick To Published Material + +In order to keep the scope of my maintainership finite, I'm not planning to accept community-made lifepaths et al. for inclusion +in this codebase. Each additional data set increases Charred Black's startup time and memory requirements. Additionally, that +makes me an arbiter of quality, and I'd prefer not to have the Enmity Clause invoked because I rejected someone's homebrew content. + +I am working on a solution for uploading lifepaths et. al which would be stored locally in your browser and not permanently on +a server. This way, you can make your data files and share them around with your friends for use with this tool. If someone else +wants to keep a repository or forum thread of data files known to work with Charred Black, I'd happily to link to it in this +documentation and from the website itself. I don't want to discourage contributions, I just want do one thing well. + +### Keep It Mostly Stateless + +Charred Black uses an in-memory cache to allow users to upload JSON and then download .char and .pdf files. I don't know how the +original Charred handled this, but the tradeoffs of this approach are: + +1. Works as expected without an update to the frontend +2. PDF generation happens entirely in one process, limiting the amount of futzing you have to do with distributed systems +3. Because the cache is in memory, you can't scale processes horizontally + +The cache has a limited number of keys, and only the first 16kb of data are used, with the aim of making this this app useless +for nefarious purposes. The average size of a 4-lifepath character is around 4kb, so this should be more than enough. If you're +trying to do something weird and your character file is bigger than this, consider using a pencil and paper. + +More guidelines: + +* Only JSON should be stored in the cache. +* Cached items should be invalidated upon access (by using the delete method to get the data) to restrict the usefulness of this app + to bad actors. +* Only .pdf and .char file formats should be returned as responses when getting data out of the cache. I'll consider other formats + on a case-by-case basis: for example, I'd be open to a format which could be used with Roll20. + +## Guide + +### Best Practices + +No matter what's in the book, always Capital Case for skills, traits, lifepaths and settings: + +e.g. +Path Of Spite Subsetting +Never A Moment Of Peace +Ages Of The Etharch +Reeks Of Alcohol + +Hyphens: the word after the hyphen is not capitalized: + +Rabble-rouser +Burden Of The Crown-wise + +Anatomy of a lifepath +``` +{ + "Example Setting": { + "Example Lifepath": { + "time": 1, + "res": 1, + "stat": [ + [ + 1, + "m" + ], + [ + 1, + "p" + ] + ], + "skills": [ + [ + 3, + "Inconspicuous" + ], + [ + 1, + "General" + ] + ], + "traits": [ + 1, + "" + ], + "requires": "", + "requires_expr": [ + ], + "leads": [ + "Peasant", + "Villager", + "City", + "Court", + "Servitude", + "Outcast", + "Soldier", + "Seafaring", + "Religious" + ], + "key_leads": [ + "Peasant Setting", + "Villager Setting", + "City Dweller Setting", + "Noble Court Subsetting", + "Servitude And Captive Setting", + "Outcast Subsetting", + "Professional Soldier Subsetting", + "Seafaring Setting", + "Religious Subsetting" + ] + } + } +} +``` \ No newline at end of file diff --git a/src/app.rb b/src/app.rb old mode 100755 new mode 100644 diff --git a/src/data/caliban.ttf b/src/data/caliban.ttf old mode 100755 new mode 100644 diff --git a/src/data/dark_elf/lifepaths.json b/src/data/dark_elf/lifepaths.json old mode 100755 new mode 100644 diff --git a/src/data/dark_elf/resources.json b/src/data/dark_elf/resources.json old mode 100755 new mode 100644 diff --git a/src/data/dark_elf/skills.json b/src/data/dark_elf/skills.json old mode 100755 new mode 100644 index e14ee41..048e835 --- a/src/data/dark_elf/skills.json +++ b/src/data/dark_elf/skills.json @@ -48,7 +48,7 @@ "Will" ] }, - "Surrow Of Truth": { + "Sorrow Of Truth": { "stock": "elven", "magic": 1, "roots": [ diff --git a/src/data/dark_elf/traits.json b/src/data/dark_elf/traits.json old mode 100755 new mode 100644 diff --git a/src/data/gold.pdf b/src/data/gold.pdf old mode 100755 new mode 100644 diff --git a/src/data/gold/lifepaths/dwarf.json b/src/data/gold/lifepaths/dwarf.json old mode 100755 new mode 100644 diff --git a/src/data/gold/lifepaths/elf.json b/src/data/gold/lifepaths/elf.json old mode 100755 new mode 100644 diff --git a/src/data/gold/lifepaths/man.json b/src/data/gold/lifepaths/man.json old mode 100755 new mode 100644 diff --git a/src/data/gold/lifepaths/orc.json b/src/data/gold/lifepaths/orc.json old mode 100755 new mode 100644 diff --git a/src/data/gold/lifepaths/roden.json b/src/data/gold/lifepaths/roden.json old mode 100755 new mode 100644 diff --git a/src/data/gold/lifepaths/wolf.json b/src/data/gold/lifepaths/wolf.json old mode 100755 new mode 100644 diff --git a/src/data/gold/resources/dwarf.json b/src/data/gold/resources/dwarf.json old mode 100755 new mode 100644 diff --git a/src/data/gold/resources/elf.json b/src/data/gold/resources/elf.json old mode 100755 new mode 100644 diff --git a/src/data/gold/resources/man.json b/src/data/gold/resources/man.json old mode 100755 new mode 100644 diff --git a/src/data/gold/resources/orc.json b/src/data/gold/resources/orc.json old mode 100755 new mode 100644 diff --git a/src/data/gold/resources/roden.json b/src/data/gold/resources/roden.json old mode 100755 new mode 100644 diff --git a/src/data/gold/resources/wolf.json b/src/data/gold/resources/wolf.json old mode 100755 new mode 100644 diff --git a/src/data/gold/skills.json b/src/data/gold/skills.json old mode 100755 new mode 100644 diff --git a/src/data/gold/starting_stat_pts/dwarf.json b/src/data/gold/starting_stat_pts/dwarf.json old mode 100755 new mode 100644 diff --git a/src/data/gold/starting_stat_pts/elf.json b/src/data/gold/starting_stat_pts/elf.json old mode 100755 new mode 100644 diff --git a/src/data/gold/starting_stat_pts/man.json b/src/data/gold/starting_stat_pts/man.json old mode 100755 new mode 100644 diff --git a/src/data/gold/starting_stat_pts/orc.json b/src/data/gold/starting_stat_pts/orc.json old mode 100755 new mode 100644 diff --git a/src/data/gold/starting_stat_pts/roden.json b/src/data/gold/starting_stat_pts/roden.json old mode 100755 new mode 100644 diff --git a/src/data/gold/starting_stat_pts/wolf.json b/src/data/gold/starting_stat_pts/wolf.json old mode 100755 new mode 100644 diff --git a/src/data/gold/traits.json b/src/data/gold/traits.json old mode 100755 new mode 100644 diff --git a/src/data/wizard/lifepaths.json b/src/data/wizard/lifepaths.json old mode 100755 new mode 100644 diff --git a/src/data/wizard/skills.json b/src/data/wizard/skills.json old mode 100755 new mode 100644 diff --git a/src/data/wizard/traits.json b/src/data/wizard/traits.json old mode 100755 new mode 100644 diff --git a/src/lib/cache.rb b/src/lib/cache.rb old mode 100755 new mode 100644 diff --git a/src/lib/data.rb b/src/lib/data.rb old mode 100755 new mode 100644 diff --git a/src/lib/data/dark_elf.rb b/src/lib/data/dark_elf.rb old mode 100755 new mode 100644 diff --git a/src/lib/data/gold.rb b/src/lib/data/gold.rb old mode 100755 new mode 100644 diff --git a/src/lib/data/wizard.rb b/src/lib/data/wizard.rb old mode 100755 new mode 100644 diff --git a/src/lib/pdf.rb b/src/lib/pdf.rb old mode 100755 new mode 100644 diff --git a/src/public/css/bootstrap.min.css b/src/public/css/bootstrap.min.css old mode 100755 new mode 100644 diff --git a/src/public/css/style.css b/src/public/css/style.css old mode 100755 new mode 100644 diff --git a/src/public/fonts/post-mediaeval.ttf b/src/public/fonts/post-mediaeval.ttf old mode 100755 new mode 100644 diff --git a/src/public/js/angular-resource.js b/src/public/js/angular-resource.js old mode 100755 new mode 100644 diff --git a/src/public/js/angular-route.js b/src/public/js/angular-route.js old mode 100755 new mode 100644 diff --git a/src/public/js/angular.min.js b/src/public/js/angular.min.js old mode 100755 new mode 100644 diff --git a/src/public/js/bootstrap.min.js b/src/public/js/bootstrap.min.js old mode 100755 new mode 100644 diff --git a/src/public/js/burning-classes.js b/src/public/js/burning-classes.js old mode 100755 new mode 100644 diff --git a/src/public/js/burning-modal.js b/src/public/js/burning-modal.js old mode 100755 new mode 100644 diff --git a/src/public/js/burning-serialize.js b/src/public/js/burning-serialize.js old mode 100755 new mode 100644 diff --git a/src/public/js/burning-service.js b/src/public/js/burning-service.js old mode 100755 new mode 100644 diff --git a/src/public/js/burning-util.js b/src/public/js/burning-util.js old mode 100755 new mode 100644 diff --git a/src/public/js/burning.js b/src/public/js/burning.js old mode 100755 new mode 100644 index 1ea389d..b2cebd1 --- a/src/public/js/burning.js +++ b/src/public/js/burning.js @@ -150,6 +150,15 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo return $scope.attribute("Ancestral Taint").shade; } }; + $scope.statsForSkillCalc["Spite"] = { + "exp": function(){ + // Note: this function can't be called until $scope.attribute is defined! + return $scope.attribute("Spite").exp; + }, + "calcshade": function(){ + return $scope.attribute("Spite").shade; + } + }; // Setting names for use in the Add Lifepath section $scope.settingNames = ["Loading..."] $scope.currentSettingLifepathNames = []; @@ -248,7 +257,7 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo /* Used to keep track of whether the user shade-shifted an attribute, for those attributes that allow shade shifting */ - $scope.attributeShade = {'Steel': 'B', 'Grief' : 'B', 'Greed' : 'B', 'Hatred' : 'B'}; + $scope.attributeShade = {'Steel': 'B', 'Grief' : 'B', 'Greed' : 'B', 'Hatred' : 'B', 'Spite' : 'B'}; $scope.ptgs = new PTGS(); @@ -329,6 +338,10 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo } else if ( $scope.stock == "elf" ){ result.push("Grief"); + // Spite must be calculated after Grief + if ( $scope.hasTrait("Spite") ) { + result.push("Spite"); + } } else if ( $scope.stock == "dwarf" ){ result.push("Greed"); @@ -933,7 +946,6 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo } } - // Return the value of the attribute with the specified name as a hash of [shade : S, exp : E, modifyable : flag] $scope.attribute = function(name){ @@ -1081,6 +1093,14 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo grief += bonus; return { "shade" : $scope.attributeShade[name], "exp" : grief, "modifyable" : true}; } + else if ( "Spite" == name ){ + var spite = computeModifiers(name); + if($scope.attributeShade[name] == 'G'){ + spite -= 5; + } + spite += bonus; + return { "shade" : $scope.attributeShade[name], "exp" : spite, "modifyable" : true}; + } else if ( "Faith" == name ){ var faith = 3 + computeModifiers(name); faith += bonus; @@ -1092,7 +1112,6 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo taint += bonus; return { "shade" : $scope.statsByName["Will"].shade, "exp" : taint, "modifyable" : true}; } - } $scope.distributeStats = function(){ @@ -3043,14 +3062,12 @@ function attributeModifyingQuestions($scope, attribute) for(key in skills) { var skill = skills[key]; - if( skill.exp($scope.statsForSkillCalc) > 0 && beginsWith(key.toLowerCase(), "lament") ) - return 0; - } - for(var i = 0; i < skills.length; i++){ - if( beginsWith(skills[i].name.toLowerCase(), "lament") ) + // Dark Elves have no laments, therefore we check if the skill is a lament BEFORE we calculate + // the exponent because we don't want to it to get sent into an infinite loop, i.e. + // calculate spite -> calc grief -> calc spite skill exponent -> spite -> grief -> ... + if( beginsWith(key.toLowerCase(), "lament") && skill.exp($scope.statsForSkillCalc) > 0 ) return 0; } - return 1; } @@ -3077,9 +3094,48 @@ function attributeModifyingQuestions($scope, attribute) {question: "Has the character lived among non-Elven people?", math_label: "(+1 Grief)", modifier: 1}, {question: "+1 Grief for each point of Steel above 5.", computed: true, compute: steelMod}, {question: "+1 Grief if the characters Perception is above 5.", computed: true, compute: percMod}, - {question: "+1 Greed if the character is over 500 years old.", computed: true, compute: ageMod(500)}, - {question: "+1 Greed if the character is over 750 years old.", computed: true, compute: ageMod(750)}, - {question: "+1 Greed if the character is over 1000 years old.", computed: true, compute: ageMod(1000)} + {question: "+1 Grief if the character is over 500 years old.", computed: true, compute: ageMod(500)}, + {question: "+1 Grief if the character is over 750 years old.", computed: true, compute: ageMod(750)}, + {question: "+1 Grief if the character is over 1000 years old.", computed: true, compute: ageMod(1000)} + ); + } + else if ( attribute == "Spite" ) + { + var griefMod = function() { + var grief = $scope.attribute("Grief").exp + return grief; + } + + var traitsMod = function(){ + var val = 0; + if($scope.hasTrait('Slayer')) + val++; + if($scope.hasTrait('Exile')) + val++; + if($scope.hasTrait('Feral')) + val++; + if($scope.hasTrait('Murderous')) + val++; + if($scope.hasTrait('Saturnine')) + val++; + if($scope.hasTrait('Femme Fatale/Homme Fatale')) + val++; + if($scope.hasTrait('Cold')) + val++; + if($scope.hasTrait('Bitter')) + val++; + return val; + } + + result.push( + {question: "+1 Spite for every point of Grief", computed: true, compute: griefMod}, + {question: "+1 Spite for each of several spiteful traits", computed: true, compute: traitsMod}, + {question: "Has the character been betrayed by their friends?", math_label: "(+1 Spite)", modifier: 1}, + {question: "Is the character lovesick or broken hearted?", math_label: "(+1 Spite)", modifier: 1}, + {question: "Has the character been abandoned by those they held dear?", math_label: "(+1 Spite)", modifier: 1}, + {question: "Has the character been abused or tortured?", math_label: "(+1 Spite)", modifier: 1}, + {question: "Does the character still respect or admire someone on the other side?", math_label: "(-1 Spite)", modifier: -1}, + {question: "Does the character still love someone on the other side?", math_label: "(-2 Spite)", modifier: -2} ); } else if ( attribute == "Hatred" ) diff --git a/src/public/js/jquery-1.10.2.min.js b/src/public/js/jquery-1.10.2.min.js old mode 100755 new mode 100644 diff --git a/src/public/js/server_settings.js b/src/public/js/server_settings.js old mode 100755 new mode 100644 diff --git a/src/public/js/ui-bootstrap-tpls-0.11.0.js b/src/public/js/ui-bootstrap-tpls-0.11.0.js old mode 100755 new mode 100644 diff --git a/src/views/index.erb b/src/views/index.erb old mode 100755 new mode 100644 diff --git a/src/views/partials/choose_appropriate_weapons.erb b/src/views/partials/choose_appropriate_weapons.erb old mode 100755 new mode 100644 diff --git a/src/views/partials/choose_character.erb b/src/views/partials/choose_character.erb old mode 100755 new mode 100644 diff --git a/src/views/partials/choose_stat_penalty.erb b/src/views/partials/choose_stat_penalty.erb old mode 100755 new mode 100644 diff --git a/src/views/partials/choose_trait.erb b/src/views/partials/choose_trait.erb old mode 100755 new mode 100644 diff --git a/src/views/partials/choose_weapon_of_choice.erb b/src/views/partials/choose_weapon_of_choice.erb old mode 100755 new mode 100644 diff --git a/src/views/partials/config.erb b/src/views/partials/config.erb old mode 100755 new mode 100644 diff --git a/src/views/partials/emotional_attr_questions.erb b/src/views/partials/emotional_attr_questions.erb old mode 100755 new mode 100644 diff --git a/src/views/partials/help.erb b/src/views/partials/help.erb old mode 100755 new mode 100644 diff --git a/src/views/partials/main.erb b/src/views/partials/main.erb old mode 100755 new mode 100644 diff --git a/src/views/partials/upload_character.erb b/src/views/partials/upload_character.erb old mode 100755 new mode 100644