deploy charred

pull/1/head
Michael Hansen 5 years ago
parent 9def2a15ba
commit 29aa8c8329
  1. 7
      Dockerfile
  2. 22
      Dockerfile.dev
  3. 10
      deploy.md
  4. 810
      src/data/lifepaths/wizard.json
  5. 52
      src/data/wizard_skills.json
  6. 149
      src/data/wizard_traits.json
  7. 130
      src/lib/data.rb
  8. 9
      src/lib/pdf.rb
  9. 1
      src/public/js/burning-serialize.js
  10. 10
      src/public/js/burning.js
  11. 20
      src/views/partials/help.erb
  12. 15
      src/views/partials/main.erb

@ -1,8 +1,6 @@
FROM ruby:2.6.3
EXPOSE 7878
ENV HOST=0.0.0.0 PORT=7878 RACK_ENV=development
ENV HOST=0.0.0.0 PORT=7878 RACK_ENV=production
# throw errors if Gemfile has been modified since Gemfile.lock
RUN bundle config --global frozen 1
@ -18,5 +16,4 @@ COPY . /app
WORKDIR /app/src
CMD ["rerun", "-b", "--", "ruby", "./app.rb"]
#CMD ["ruby", "./app.rb"]
CMD ["ruby", "./app.rb"]

@ -0,0 +1,22 @@
FROM ruby:2.6.3
EXPOSE 7878
ENV HOST=0.0.0.0 PORT=7878 RACK_ENV=development
# throw errors if Gemfile has been modified since Gemfile.lock
RUN bundle config --global frozen 1
WORKDIR /app
# to generate Gemfile.lock, run this in service dir:
# $ docker run --rm -v "$PWD":/app -w /app ruby:2.6.3 bundle install
COPY Gemfile Gemfile.lock /app/
RUN bundle install
COPY . /app
WORKDIR /app/src
CMD ["rerun", "-b", "--", "ruby", "./app.rb"]
#CMD ["ruby", "./app.rb"]

@ -0,0 +1,10 @@
Deploy
------
https://devcenter.heroku.com/articles/container-registry-and-runtime
heroku login
heroku container:login
heroku container:push web -a charred-black
heroku container:release web -a charred-black
heroku open

File diff suppressed because it is too large Load Diff

@ -0,0 +1,52 @@
{
"Alchemy": {
"roots": [
"Perception"
]
},
"Bargaining": {
"roots": [
"Will",
"Perception"
]
},
"Circination": {
"magic": 1,
"roots": [
"Perception",
"Agility"
]
},
"Death Art": {
"magic": 1,
"roots": [
"Will",
"Forte"
]
},
"Enchanting": {
"magic": 1,
"roots": [
"Perception",
"Agility"
]
},
"Sorcery": {
"magic": 1,
"roots": [
"Perception"
]
},
"Spirit Binding": {
"magic": 1,
"roots": [
"Will"
]
},
"Summoning": {
"magic": 1,
"roots": [
"Perception"
]
}
}

@ -0,0 +1,149 @@
{
"Always in the Way": {
"cost": 1,
"type": "character"
},
"Assistant Pig-Keeper": {
"cost": 1,
"type": "character"
},
"Brook No Fools": {
"cost": 7,
"type": "die",
"desc": "The spirit binder's art is not something to be trifled with! Their secrets are well kept because meddling fools will only cause irreparable harm to themselves and the domains with which they tamper. A summoner with this trait receives a fate point every time they rebuff an offer of help from a potential student (so that character does not earn a test toward learning the skill). The summoner receives a persona point when they actively thwart another character from learning the secrets of Spirit Binding, Summoning, or Circination."
},
"Bully": {
"cost": 1,
"type": "character"
},
"Corrupted": {
"cost": 3,
"type": "die",
"desc": "This character has opened their body and soul to the dark powers. They must open the Corrupted emotional attribute. Increase their starting rank by one."
},
"Council of Mages": {
"cost": 6,
"type": "die",
"desc": "The Council of Mages is a small, secretive, yet powerful entity. This trait grants a 1D reputation and a 1D affiliation with this organization. This combines with other appropriate reputations and affiliations."
},
"Creepy": {
"cost": 1,
"type": "character"
},
"Cult Leader": {
"cost": 8,
"type": "die",
"desc": "Cult Leaders are charismatic and eccentric. This trait grants a 1D reputation and 1D affiliation with the Death Cult. This combines with other appropriate reputations and affiliations."
},
"Unhinged": {
"cost": 1,
"type": "character"
},
"Disfigured": {
"cost": 3,
"type": "die",
"desc": "This character has led a life of hardship and pain. They've been cut up, broken, and beaten down. This trait grants +1D to shrug off or grit teeth for any injury."
},
"Evil": {
"cost": 1,
"type": "character"
},
"Faust": {
"cost": 3,
"type": "die",
"desc": "When bargaining with the higher powers for something pure and innocent like love, gain a bonus persona point for each agreement you strike on behalf of the one you seek to protect or affect."
},
"Feared Above All": {
"cost": 5,
"type": "die",
"desc": "For good reason or not, this character is feared by all who know them. +1D to Intimidation. Choose a 1D infamous reputation."
},
"Fiery": {
"cost": 1,
"type": "character"
},
"Disgruntled": {
"cost": 1,
"type": "character"
},
"Furtive": {
"cost": 1,
"type": "character"
},
"Graduate": {
"cost": 3,
"type": "die",
"desc": "This trait grants a 1D affiliation with a College of Magic. This combines with other appropriate and applicable affiliations."
},
"Harried": {
"cost": 1,
"type": "character"
},
"Hazed": {
"cost": 2,
"type": "die",
"desc": "This character may take a free inimical relationship with another character who was their senior in the College of Magic."
},
"Impressive Hat": {
"cost": 3,
"type": "die",
"desc": "This character's hat is mightily impressive. It can be seen for miles! It's so impressive that people often remember the hat more than the person. This gives a 1D Reputation among the people of a given locale as the wearer of the really impressive hat, so long as the hat is being worn, of course. Wearing the hat also gives a +2 Ob to any Disguise or Inconspicuous test."
},
"Invocations of the Damned": {
"cost": 5,
"type": "call_on",
"desc": "The Dark Priest swears their soul to fell powers. Like other priests, they must have a Belief that ties to their Faith in order to maintain the emotional attribute. If the player writes a second Belief tied to their faith in the dark gods and their service to them, the priest may use this trait as a call-on for Faith when that Belief comes into question."
},
"Late": {
"cost": 1,
"type": "character"
},
"Low Born": {
"cost": 2,
"type": "die",
"desc": "Low Born characters are born of unremarkable parents. They cannot trace their lineage to any nobility or to any historic or notable figures. They have a 1D infamous reputation in noble or elite circles due to their obvious lack of heritage. It also adds a +1 Ob disadvantage to any Inconspicuous tests in noble or courtly settings."
},
"Master of Mages": {
"cost": 0,
"type": "die",
"desc": "The Headmaster of a College of Magic attained their position by rigorous pursuit of excellence and expertise in sorcery and magic. This trait grants a 2D repuation among mages and the halls of power as a Master of Mages."
},
"Misunderstood": {
"cost": 2,
"type": "die",
"desc": "Choose a 1D infamous reputation. And suffer -1 to your body of argument whenever you're trying to explain your nature, position, meaning, or special insight."
},
"Never A Moment Of Peace": {
"cost": 3,
"type": "die",
"desc": "This trait grants the character a 5 rps relationship. This relationship must be with a character who is always bothering or interrupting him. If that character is forced out of his life, someone new will come along. This character just attracts these sort of people! You may invest additional resource points and increase the value of this relationship."
},
"Polite": {
"cost": 1,
"type": "character"
},
"Speaker Of The Secret Language": {
"cost": 1,
"type": "character"
},
"Spirit Familiar": {
"cost": 5,
"type": "die",
"desc": "This character has a familiar whose spirit is linked to their own. They can see through the familiar's eyes using the familiar's Perception stat. However, when using their familiar's eyes, a character with this trait cannot look through their own though other senses may still be employed. It takes two actions of concentration to engage their familiar's eyes, and two more actions to return their sight to their own eyes. This trait does not grant Low Speech. However the creature does understand its companion's wishes and will go where its master directs it to go, look at what its master directs it to look at, etc. A character with this trait feels the pain of their familiar. If the familiar suffers a light or greater wound, the master suffers a light wound. If the familiar is ever killed, the master suffers a traumatic wound. In the game, the character is physically unharmed, but mentally traumatized. Recovery and Treatment are as per a standard traumatic wound. This wound does not bleed out. A familiar uses the following stats: Wi G2, Pe G3, Ag B4, Sp B5, Po B3, Fo B3, Hea B4, Ref B4, Ste B5, MW B9. Choose its form: cat, doc, snake, bird, pig, rabbit, fox or similar. No large predators or beasts of burden. The animal must show some mark or quality that indicates it is not entirely of this world. For example, it must be hairless, one eyed, two-tailed, of large size or even just have a disturbing aura."
},
"Suicidal": {
"cost": 5,
"type": "die",
"desc": "This character (instantly) earns a persona point every time he takes a mortal wound."
},
"Versatile": {
"cost": 4,
"type": "die",
"desc": "The Master Sorcerer places their confidence in the art of sorcery. It is a versatile and potent art, and they know it intimately. If using Art Magic, reduce by one the number of tests required for Weaving Magic into the Fiber of my Being. If using the standard Sorcery rules, reduce their practicals aptitude by one. If using Practical Magic, they may take an additional category of magic. In addition, the player earns a persona point for pushing their character's magic in a dangerous or untried direction."
},
"Well Traveled": {
"cost": 4,
"type": "die",
"desc": "The Speaker of Names wanders the lands and seas, communing with the spirits. This trait allows the player to take a new domain when their character travels to a new locale in play. The player may describe a memory their character has of this place or a place like it. They may describe when they traveled here before. The player then adds a new domain to their spirit binder at its base level (0). Once used, this trait becomes a character trait. To be clear, the domain is added during play at a time of the player's choosing, not during character burning."
}
}

@ -1,8 +1,138 @@
require 'json'
module CharredData
def self.merge_data!(to, from)
from.keys.each do |k|
to[k] = from[k]
end
end
def self.lifepath_requirements(expr)
if expr.is_a? Numeric
[]
elsif expr.is_a? String
if expr.start_with? "+"
[]
else
expr
end
elsif expr.is_a? Array
(expr.map { |e| self.lifepath_requirements(e) }).flatten
else
[]
end
end
def self.load_wizard_burner(data)
puts 'Loading wizard burner'
file = File.read('data/lifepaths/wizard.json')
wizard_data = JSON.parse(file)
file = File.read('data/wizard_skills.json')
wizard_skills = JSON.parse(file)
self.merge_data!(data[:skills], wizard_skills)
file = File.read('data/wizard_traits.json')
wizard_traits = JSON.parse(file)
self.merge_data!(data[:traits], wizard_traits)
man = data[:lifepaths]['man']
man['College of Magic Setting'] = wizard_data['College of Magic Setting']
man['Death Cult Setting'] = wizard_data['Death Cult Setting']
self.merge_data!(data[:lifepaths]['orc']['Servant Of The Dark Blood Subsetting'], wizard_data['Servant Of The Dark Blood Subsetting'])
[
'Peasant Setting',
'Villager Setting',
'City Dweller Setting',
'Noble Court Subsetting',
'Outcast Subsetting'
].each do |setting|
self.merge_data!(man[setting], wizard_data[setting])
end
[
'Peasant Setting',
'Villager Setting',
'City Dweller Setting',
'Seafaring Setting',
'Servitude And Captive Setting'
].each do |setting|
self.merge_data!(man[setting], {
'Gifted Child' => wizard_data['Special Gifted Lifepaths']['Gifted Child']
})
end
[
'Noble Court Subsetting',
'Religious Subsetting',
'Outcast Subsetting',
'Professional Soldier Subsetting'
].each do |setting|
self.merge_data!(man[setting], {
'Apt Pupil' => wizard_data['Special Gifted Lifepaths']['Apt Pupil']
})
end
leads_short = {
'College of Magic Setting' => 'College',
'Death Cult Setting' => 'Death',
}
# backfill leads
leads_short.keys.each do |wiz_set|
wizard_data[wiz_set].keys.each do |wiz_lp|
lifepath = wizard_data[wiz_set][wiz_lp]
next if !lifepath['requires_expr']
requirements = self.lifepath_requirements(lifepath['requires_expr'])
requirements.each do |req|
setting = nil
req_lp = req
setting, req_lp = req.split(':') if req.include? ':'
man.keys.each do |man_set|
next if wiz_set.downcase == man_set.downcase
next if setting && setting != man_set.downcase
man[man_set].keys.each do |man_lp|
next if req_lp != man_lp.downcase
#puts "adding #{wiz_set}:#{wiz_lp} lead to #{man_set}:#{man_lp}"
man[man_set][man_lp]['key_leads'] |= [wiz_set]
man[man_set][man_lp]['leads'] |= [leads_short[wiz_set]]
end
end
end
end
end
# backfill Apt Pupil == Neophyte Sorcerer connection
man.keys.each do |man_set|
man[man_set].keys.each do |man_lp|
requirements = self.lifepath_requirements(man[man_set][man_lp]['requires_expr'])
next if requirements.include? 'apt pupil'
requirements.each do |req|
setting = nil
req_lp = req
setting, req_lp = req.split(':') if req.include? ':'
next unless req_lp.downcase == 'neophyte sorcerer'
if man[man_set][man_lp]['requires_expr'].include? 'neophyte sorcerer'
man[man_set][man_lp]['requires_expr'] |= ['apt pupil']
else
puts "warning, could not add apt pupil to deep requirements array at #{man_set}:#{man_lp}"
end
end
end
end
data[:lifepaths]['man'] = man
puts 'loaded!'
data
end

@ -29,7 +29,7 @@ class CharSheet
pdf.font 'data/caliban.ttf'
pdf.default_leading -4
logger.info @character
# logger.info @character
<<-GRID
(0...40).each do |x|
@ -123,10 +123,15 @@ class CharSheet
self.render_shade_exponent(pdf, ref_attr[0], ref_attr[1], [279, 396.5])
self.render_shade_exponent(pdf, mor_attr[0], mor_attr[1], [278.5, 346])
ptgs = @character['ptgs']
["Su", "Li", "Mi", "Se", "Tr", "Mo"].each do |tol|
tol_c = ptgs[tol.downcase]
pdf.draw_text "#{tol}", :at => [29 + (19.5 * tol_c), 190], :size => @t2
end
pdf.draw_text str_attr[1], :at => [264, 428], :size => @t2
pdf.draw_text hes_attr[1], :at => [53, 318], :size => @t2
skills_left = @character['skills'][0...13]
skills_right = @character['skills'][13...26]

@ -234,7 +234,6 @@ function convertCurrentCharacterToStruct($scope, appropriateWeapons) {
}
chardata.stats = stats;
var lifepathSkills = [];
var generalSkills = [];
var addSkillTo = function(list, displaySkill){

@ -1245,6 +1245,7 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
// :traits => [
// [name, type]
// ]
// :ptgs => { :su => ["B1"], ... }
// }
@ -1343,6 +1344,15 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
});
chardata.affiliations = res;
chardata.ptgs = {
"su": $scope.ptgs.su,
"li": $scope.ptgs.li,
"mi": $scope.ptgs.mi,
"se": $scope.ptgs.se,
"tr": $scope.ptgs.tr,
"mo": $scope.ptgs.mo,
};
chardata.attr_mod_questions = convertAttributeModifierQuestionResultsForCharsheet($scope);
return chardata;

@ -33,6 +33,9 @@
<li>
<a href='#/help?scrollTo=great_wolves'>Hints for Great Wolves</a>
</li>
<li>
<a href='#/help?scrollTo=wizards'>Hints for Wizards</a>
</li>
</ul>
</div>
<div class='col-md-9'>
@ -195,6 +198,23 @@
The revised edition defined a 'Broken' trait for Great Wolves; in Charred it is named 'Broken Wolf'. Similarly the previous
Hunting skill for wolves has been renamed to 'Wolfish Hunting'.
</p>
<h2 id='wizards'>Wizards</h2>
<p>
The wizard content is taken from the 'Wizard Burner' chapter of Burning Wheel Codex. Some liberties were taken to prevent
collision with existing traits.
</p>
<p>
The Demented Inventor lifepath trait 'Demented' was changed to 'Unhinged' to avoid a conflict with Great Wolf trait of the
same name and to preserve the mechanical flavor.
</p>
<p>
The Master Of The Weak lifepath trait 'Feared' was changed to 'Feared Above All' to avoid a conflict with the Roden trait
of the same name.
</p>
<p>
The Court Summoner lifepath trait 'Frustrated' was changed to 'Disgruntled' to avoid a conflict with the Roden trait of the
same name.
</p>
</div>
</div>
</div>

@ -448,6 +448,10 @@
<div class='col-md-2'>
<strong>Type</strong>
</div>
<div class='col-md-1'></div>
<div class='col-md-5'>
<strong>Description</strong>
</div>
</div>
</div>
</li>
@ -460,6 +464,10 @@
<div class='col-md-2'>
{{trait.typeForDisplay()}}
</div>
<div class='col-md-1'></div>
<div class='col-md-5'>
<p>{{trait.desc}}</p>
</div>
</div>
</div>
</li>
@ -475,6 +483,9 @@
<div class='col-md-1'>
<button class='btn btn-default btn-xs' ng-click='removeTrait(trait)'>remove</button>
</div>
<div class='col-md-5'>
<p>{{trait.desc}}</p>
</div>
</div>
</div>
</li>
@ -487,6 +498,10 @@
<div class='col-md-2'>
{{trait.typeForDisplay()}}
</div>
<div class='col-md-1'></div>
<div class='col-md-5'>
<p>{{trait.desc}}</p>
</div>
</div>
</div>
</li>

Loading…
Cancel
Save