Trolls, Bitter Reminders, cleanup

pull/1/head
Michael Hansen 5 years ago
parent b501259633
commit 03dd07fc9e
  1. 11
      CHANGELOG.md
  2. 11
      DEPLOY.md
  3. 2
      src/data/dark_elf/resources.json
  4. 2
      src/data/dark_elf/skills.json
  5. 617
      src/data/troll/lifepaths.json
  6. 57
      src/data/troll/resources.json
  7. 27
      src/data/troll/skills.json
  8. 82
      src/data/troll/starting_stat_pts.json
  9. 399
      src/data/troll/traits.json
  10. 2
      src/data/wizard/lifepaths.json
  11. 2
      src/data/wizard/traits.json
  12. 17
      src/lib/data.rb
  13. 4
      src/lib/data/dark_elf.rb
  14. 29
      src/lib/data/troll.rb
  15. 6
      src/lib/data/wizard.rb
  16. 4
      src/public/js/burning-classes.js
  17. 36
      src/public/js/burning-service.js
  18. 73
      src/public/js/burning.js
  19. 2
      src/public/js/server_settings.js
  20. 8
      src/views/partials/help.erb
  21. 1
      src/views/partials/main.erb

@ -5,8 +5,6 @@ The format is based on [Keep a Changelog](changelog),
and this project adheres to [Semantic Versioning](semver). and this project adheres to [Semantic Versioning](semver).
## [Unreleased] ## [Unreleased]
- Dark Elf - Bitter Reminders rules
- Troll stock
- Add emotional attributes to the PDF - Add emotional attributes to the PDF
- Add a page to the pdf for Health + Steel questions - Add a page to the pdf for Health + Steel questions
- Add a page to the pdf for trait descriptions - Add a page to the pdf for trait descriptions
@ -15,6 +13,15 @@ and this project adheres to [Semantic Versioning](semver).
- Custom upload for your own data files - Custom upload for your own data files
- Updates to Roden and Great Wolves files (currently the data comes from Monster Burner and not Codex) - Updates to Roden and Great Wolves files (currently the data comes from Monster Burner and not Codex)
## [2.2.0] - 2019-07-14
### Added
- Added the troll stock.
- Dark Elf - Spite calculations for Bitter Reminders
### Changed
- Wizard trait "Misunderstood" was renamed to "Outsider" to avoid conflict with Roden trait of the same name
- Added a `verbose_merge` method to surface trait and skill conflicts.
## [2.1.1] - 2019-07-14 ## [2.1.1] - 2019-07-14
### Changed ### Changed
- Traits on PDF are now just a comma-delimited list (instead of newline) and shrink to fit in the appropriate space - Traits on PDF are now just a comma-delimited list (instead of newline) and shrink to fit in the appropriate space

@ -0,0 +1,11 @@
## Deploy Steps
See: [Heroku - Container Registry and Runtime](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
```

@ -25,7 +25,7 @@
"rp": 3 "rp": 3
}, },
{ {
"name": "Garotte", "name": "Garrote",
"type": "gear", "type": "gear",
"rp": 3 "rp": 3
}, },

@ -41,7 +41,7 @@
"Spite" "Spite"
] ]
}, },
"Rhyme Of The Unraveler": { "Rhyme Of The Unraveller": {
"stock": "elven", "stock": "elven",
"magic": 1, "magic": 1,
"roots": [ "roots": [

@ -0,0 +1,617 @@
{
"Wild Setting": {
"Born Wild": {
"time": 5,
"res": 2,
"skills": [
[
2,
"General"
]
],
"traits": [
1
],
"common_traits": [
"Black Nails",
"Fangs",
"Night Blooded",
"Night Eyed (Troll)",
"Massive Stature (Troll)",
"Stone's Age",
"Tough (Troll)",
"Troll Skin",
"Voracious Carnivore"
],
"leads": [
"Pit"
],
"key_leads": [
"Pit Setting"
]
},
"Bogey": {
"time": 7,
"res": 1,
"stat": [
[
1,
"p"
]
],
"skills": [
[
3,
"Brawling",
"Forest-wise",
"Stealthy"
]
],
"traits": [
2
],
"leads": [
"Cave"
],
"key_leads": [
"Cavedweller Setting"
]
},
"Bridgehaunt": {
"time": 10,
"res": 2,
"stat": [
[
1,
"p"
]
],
"skills": [
[
2,
"Bridge-wise",
"Intimidation"
]
],
"traits": [
1,
"Stubborn"
]
},
"Manhunter": {
"time": 7,
"res": 2,
"stat": [
[
1,
"pm"
]
],
"skills": [
[
2,
"Man-wise",
"Village-wise"
]
],
"traits": [
1
],
"leads": [
"Pit"
],
"key_leads": [
"Pit Setting"
]
},
"Forlorn": {
"time": 13,
"res": 1,
"stat": [
[
1,
"m"
]
],
"skills": [
[
1,
"General"
]
],
"traits": [
2,
"Solitary",
"Addled",
"Slow"
]
},
"Brute": {
"time": 15,
"res": 2,
"stat": [
[
2,
"p"
]
],
"skills": [
[
2,
"Intimidation",
"Brawling"
]
],
"traits": [
1,
"Brute"
],
"requires": "Bridgehaunt, Mine Haunt, Manhunter, Dwarf Hunter, Battering Ram or Mattock",
"requires_expr": [
"bridgehaunt",
"mine haunt",
"manhunter",
"dwarf hunter",
"battering ram",
"mattock"
],
"leads": [
"Pit"
],
"key_leads": [
"Pit Setting"
]
},
"Brood Mother": {
"time": 33,
"res": 10,
"stat": [
[
1,
"m"
],
[
1,
"p"
]
],
"skills": [
[
3,
"Nursing",
"Ugly Truth"
]
],
"traits": [
2,
"Misshapen",
"Grotesque",
"Witch Flesh"
],
"requires": "Brute, Dread Stone or Bellower",
"requires_expr": [
"brute",
"dread stone",
"bellower"
],
"leads": [
"Pit",
"Cave"
],
"key_leads": [
"Pit Setting",
"Cavedweller Setting"
]
},
"Bull": {
"time": 45,
"res": 5,
"stat": [
[
1,
"m"
],
[
1,
"p"
]
],
"skills": [
[
3,
"Torture",
"Extortion"
]
],
"traits": [
2,
"Boor"
],
"requires": "Brute or Ironshield",
"requires_expr": [
"brute",
"ironshield"
],
"leads": [
"Pit"
],
"key_leads": [
"Pit Setting"
]
}
},
"Cavedweller Setting": {
"Born To Dark Caves": {
"time": 5,
"res": 2,
"skills": [
[
2,
"General"
]
],
"traits": [
2
],
"common_traits": [
"Black Nails",
"Fangs",
"Night Blooded",
"Night Eyed (Troll)",
"Massive Stature (Troll)",
"Stone's Age",
"Tough (Troll)",
"Troll Skin",
"Voracious Carnivore"
],
"leads": [
"Pit"
],
"key_leads": [
"Pit Setting"
]
},
"Dweller": {
"time": 4,
"res": 1,
"stat": [
[
1,
"p"
]
],
"skills": [
[
2,
"Climbing",
"Rock-wise",
"Brawling"
]
],
"traits": [
2
],
"leads": [
"Wild"
],
"key_leads": [
"Wild Setting"
]
},
"Mine Haunt": {
"time": 12,
"res": 1,
"stat": [
[
1,
"pm"
]
],
"skills": [
[
2,
"Mine-wise",
"Brawling"
]
],
"traits": [
1
],
"leads": [
"Pit",
"Wild"
],
"key_leads": [
"Pit Setting",
"Wild Setting"
]
},
"Dwarf Hunter": {
"time": 6,
"res": 3,
"stat": [
[
1,
"p"
],
[
1,
"m"
]
],
"skills": [
[
3,
"Dwarf-wise",
"Throwing"
]
],
"traits": [
1,
"Stubborn"
],
"leads": [
"Pit"
],
"key_leads": [
"Pit Setting"
]
},
"Dread Stone": {
"time": 30,
"res": 4,
"stat": [
[
2,
"p"
]
],
"skills": [
[
3,
"Camouflage",
"Stealthy"
]
],
"traits": [
3
],
"requires": "Mine Haunt or Dwarf Hunter",
"requires_expr": [
"mine haunt",
"dwarf hunter"
]
}
},
"Pit Setting": {
"Born To The Pit": {
"time": 4,
"res": 1,
"skills": [
[
1,
"General"
]
],
"traits": [
2,
"Vile Language"
],
"common_traits": [
"Black Nails",
"Fangs",
"Night Blooded",
"Night Eyed (Troll)",
"Massive Stature (Troll)",
"Stone's Age",
"Tough (Troll)",
"Troll Skin",
"Voracious Carnivore"
],
"leads": [
"Pit",
"Cave"
],
"key_leads": [
"Pit Setting",
"Cavedweller Setting"
]
},
"Tortured": {
"time": 3,
"skills": [
[
1
]
],
"traits": [
3,
"Tasting The Lash",
"Numb"
]
},
"Ox": {
"time": 9,
"res": 1,
"stat": [
[
1,
"p"
]
],
"skills": [
[
2,
"Ditch Digging",
"Hauling",
"Mining"
]
],
"traits": [
1,
"Back-breaking Labor",
"Tasting The Lash"
]
},
"Battering Ram": {
"time": 6,
"res": 1,
"stat": [
[
1,
"p"
]
],
"skills": [
[
2,
"Brawling",
"Throwing"
]
],
"traits": [
1,
"Hardened"
],
"leads": [
"Wild"
],
"key_leads": [
"Wild Setting"
]
},
"Mattock": {
"time": 7,
"res": 7,
"stat": [
[
1,
"p"
]
],
"skills": [
[
3,
"Hammer",
"Formation Fighting"
]
],
"traits": [
1,
"Brutal"
],
"requires": "Ox, Battering Ram, Dwarf Hunter, Manhunter or Brute",
"requires_expr": [
"ox",
"battering ram",
"dwarf hunter",
"manhunter",
"brute"
],
"leads": [
"Wild"
],
"key_leads": [
"Wild Setting"
]
},
"Ironshield": {
"time": 8,
"res": 6,
"stat": [
[
1,
"pm"
]
],
"skills": [
[
1,
"Shield Training",
"Intimidation"
]
],
"traits": [
1,
"Unflinching"
],
"requires": "Mattock or Brute",
"requires_expr": [
"mattock",
"brute"
],
"leads": [
"Wild"
],
"key_leads": [
"Wild Setting"
]
},
"Bellower": {
"time": 16,
"res": 4,
"stat": [
[
1,
"m"
],
[
1,
"p"
]
],
"skills": [
[
3,
"Torture",
"Lash"
]
],
"traits": [
1,
"Where There's A Whip, There's A Way",
"Tough As Nails",
"Bellowing"
],
"requires": "Bull or Ironshield",
"requires_expr": [
"bull",
"ironshield"
]
},
"Warlord": {
"time": 56,
"res": 10,
"stat": [
[
1,
"m"
],
[
1,
"p"
]
],
"skills": [
[
3,
"Command",
"Strategy"
],
[
1,
"General"
]
],
"traits": [
2,
"Insidious Cruelty",
"Wickedly Clever"
],
"requires": "Bellower",
"requires_expr": [
"bellower"
]
}
}
}

@ -0,0 +1,57 @@
[
{
"name": "Rags",
"type": "gear",
"rp": 1
},
{
"name": "Troll Shoes",
"type": "gear",
"rp": 1
},
{
"name": "Sack",
"type": "gear",
"rp": 1
},
{
"name": "Chest or Footlocker",
"type": "gear",
"rp": 3
},
{
"name": "Trophies",
"type": "gear",
"rp": 3
},
{
"name": "Shiny Trophies",
"type": "gear",
"rp": 7
},
{
"name": "Pile of Rocks",
"type": "gear",
"rp": 2
},
{
"name": "Troll Lash",
"type": "gear",
"rp": 5
},
{
"name": "Mattock",
"type": "gear",
"rp": 10
},
{
"name": "Black Iron Shield",
"type": "gear",
"rp": 5
},
{
"name": "Cave Hole",
"type": "property",
"rp": 5
}
]

@ -0,0 +1,27 @@
{
"Bridge-wise": {
"stock": "trollish",
"roots": [
"Perception"
]
},
"Camouflage": {
"stock": "trollish",
"roots": [
"Perception",
"Agility"
]
},
"Lash": {
"stock": "trollish",
"roots": [
"Agility"
]
},
"Mine-wise": {
"stock": "trollish",
"roots": [
"Perception"
]
}
}

@ -0,0 +1,82 @@
[
{
"range": [
1,
5
],
"m": 3,
"p": 11
},
{
"range": [
6,
12
],
"m": 4,
"p": 14
},
{
"range": [
13,
19
],
"m": 4,
"p": 17
},
{
"range": [
20,
27
],
"m": 4,
"p": 19
},
{
"range": [
28,
57
],
"m": 4,
"p": 20
},
{
"range": [
58,
80
],
"m": 4,
"p": 19
},
{
"range": [
81,
124
],
"m": 4,
"p": 18
},
{
"range": [
125,
213
],
"m": 5,
"p": 17
},
{
"range": [
214,
390
],
"m": 5,
"p": 16
},
{
"range": [
391,
712
],
"m": 6,
"p": 15
}
]

@ -0,0 +1,399 @@
{
"Black Nails": {
"cost": 0,
"type": "die",
"restrict": [
"trollish",
"common"
],
"desc": "Trolls typically have massive, craggy claws used for digging and tearing. They don't often use their claws as a weapon, favoring the use of their stony fists to subdue prey. However if a Troll has another character in a Lock and chooses to do damage (rather than increase the Lock), add +1 Power to the damage of the attack due to the claws gouging into the victim."
},
"Fangs": {
"cost": 0,
"type": "die",
"restrict": [
"trollish",
"common"
],
"desc": "A Troll's mouth is a mass of misshapen teeth and fangs. They may bite. Pow 1, Add 2, VA -, WS: 2, Shortest"
},
"Night Blooded": {
"cost": 0,
"type": "die",
"restrict": [
"trollish",
"common"
],
"desc": "Nocturnal by nature, Trolls typically shun the sunlight, for it will undo the sorcery which gave their night-stalking ancestors life. Exposure to the sun causes the Troll's stony roots to consume them and crush them from within."
},
"Night Eyed (Troll)": {
"cost": 1,
"type": "die",
"restrict": [
"trollish",
"common"
],
"desc": "Born to the stuff of night, Trolls can see perfectly well in the darkness and suffer no penalties for nighttime conditions. However rain and haze do affect them like other characters, and bright light imposes a +1 Ob penalty."
},
"Massive Stature (Troll)": {
"cost": 0,
"type": "die",
"restrict": [
"trollish",
"common"
],
"desc": "Trolls are the definition of Massive Stature: barrel-chested, hulking beasts with long thick limbs and shoulders like towering cliffs. The largest of their number grow twice as tall as humans, but even the smallest adults are rarely less than seven feet in height. Increase all weapon lengths by one for the troll. These greate lumbering beasts must have minimum Power and Forte of 4. The maximum exponents for Power and Forte are 9. The maximum exponents for Agility and Speed are 5. Stride is 7."
},
"Stone's Age": {
"cost": 0,
"type": "die",
"restrict": [
"trollish",
"common"
],
"desc": "Trolls were born of stone and sorcery and to these roots they remain closely tied. Though not immortal, these creatures age very slowly and are capable of living for long centuries–if they can survive their own violent tendencies. However, the weight of granite in their blood has detrimental effects. Their view of the world is dim and ponderous and their ability to intuit or empathize is minimal. A Troll may not have a Perception or Will higher than 6."
},
"Tough (Troll)": {
"cost": 0,
"type": "die",
"restrict": [
"trollish",
"common"
],
"desc": "Trolls are the embodiment of tough. Round up when factoring Mortal Wound."
},
"Troll Skin": {
"cost": 0,
"type": "die",
"restrict": [
"trollish",
"common"
],
"desc": "Troll Skin is resilient and leathery. It provides 1D of armor to the limbs and head, 2D to the chest. As they get older, a Troll's flesh often hardens into a stony mass. Thus the Troll Skin trait may be upgraded to Scaly Skin or Stone Skin for just a few trait points. See the Troll Special Traits list. This skin cannot \"fail\" and become damaged like armor: 1s are discounted in this case. VA works as normal against this armor."
},
"Voracious Carnivore": {
"cost": 0,
"restrict": [
"trollish",
"common"
],
"type": "character"
},
"Addled": {
"cost": 1,
"type": "character",
"restrict": [
"trollish",
"lifepath"
]
},
"Bellowing": {
"cost": 1,
"type": "call_on",
"restrict": [
"trollish",
"lifepath"
],
"desc": "The sound of this Troll's voice is unimaginably loud. Use as a call-on for Intimidation and Command."
},
"Boor": {
"cost": 1,
"type": "character",
"restrict": [
"trollish",
"lifepath"
],
"desc": "Bull Trolls have forceful personalities, perhaps verging on the barest of leadership qualities, but mitigated by ill moods, bad jokes and a predilection for overbearing violence."
},
"Brute": {
"cost": 4,
"type": "die",
"restrict": [
"trollish",
"lifepath"
],
"desc": "This Troll is so tough (or alternately, stupid) that it doesn't know when to give up. WHen taking die penalties from wounds, mental stats do not count for the purposes of incapacitation."
},
"Grotesque": {
"cost": 1,
"type": "call_on",
"restrict": [
"trollish",
"lifepath"
],
"desc": "Call-on for Intimidation and Conspicuous when displaying your deformity."
},
"Hauling": {
"cost": 1,
"type": "call_on",
"restrict": [
"trollish",
"lifepath"
],
"desc": "Ox Trolls are the beasts of burden in the Troll legions. They haul impossible loads for interminable journeys. Use this trait as a call-on for Power when pushing, pulling or hauling loads."
},
"Heaving": {
"cost": 1,
"type": "call_on",
"restrict": [
"trollish",
"lifepath"
],
"desc": "The lowest rung for Troll soldiers in their great legions is that of the Battering Ram. These massive brutes haul forth great war engines to the walls of the citadels of their enemies and even use their own bodies to batter down walls. Use this trait as a call-on for Power when trying to break, smash or knock over something."
},
"Insidious Cruelty": {
"cost": 1,
"type": "character",
"restrict": [
"trollish",
"lifepath"
]
},
"Mind Numbing Work": {
"cost": 1,
"type": "character",
"restrict": [
"trollish",
"lifepath"
]
},
"Misshapen": {
"cost": 1,
"type": "character",
"restrict": [
"trollish",
"lifepath"
]
},
"Solitary": {
"cost": 1,
"type": "character",
"restrict": [
"trollish",
"lifepath"
]
},
"Unflinching": {
"cost": 1,
"type": "die",
"restrict": [
"trollish",
"lifepath"
],
"desc": "Ironshields are the bodyguards and retinues of the Troll legions. They are trained to stand guard and lay down their lives for their Warlord master. This trait reduces hesitation by 4 for fear and pain."
},
"Wickedly Clever": {
"cost": 1,
"type": "call_on",
"restrict": [
"trollish",
"lifepath"
],
"desc": "Warlords quickly grow to understand that the world sees Trolls as mindless brutes, good only for absorbing and dealing devastation. Outsiders forget that Trolls can evince intelligence. Troll Warlords use this prejudice to their advantage–playing the part of the dumb automaton while scheming and plotting behind their thick masks. Call-on for Strategy and Tactics when unveiling a surprising stratagem or ploy."
},
"Witch Flesh": {
"cost": 1,
"type": "die",
"restrict": [
"trollish",
"lifepath"
],
"desc": "The Brood Mother knows a bit of magic, rumored to have been handed down from an old giantess in ages past. This magic, called Witch Flesh, grants the ability to transform into a handsome young man or woman. The disguise is impenetrable. The brood mother retains her stats and attributes, but her character traits are replaced with Beautiful, Wholesome, Apple-Cheeked, etc. Any other traits requiring physical expression (like Horns or Claws) are temporarily lost to the human form. No test is required to transform, but the change requires a full scene, during which the magic slowly takes effect. Once the Brood Mother reveals her true nature to her victim (or is perhaps accidentally spotted transforming) the victim will never see her the same way again–and can never bee fooled by the Witch Flesh of this Brood Mother again."
},
"Berzerker": {
"cost": 3,
"type": "die",
"restrict": [
"trollish",
"special"
],
"desc": "When this Troll fails a Steel test or suffers a wound (that they can feel–Numb and Dead To Pain work against this trait), the monster immediately and automatically shifts into aggressive stance. There is no hesitation. While enraged they will (and must) attack any living (or seemingly living) thing around them–starting with that which just hurt/enraged them. They then move on to the next closest target, friend or foe. The Troll remains in aggressive stance even if they sprint. The rage lasts for exchanges equal to 10 minus the character's Will exponent. If the monster is already in aggressive stance when the above conditions are met, they spend two actions roaring and bellowing (which count as an Intimidation test against their opponents)."
},
"Bull-legged": {
"cost": 4,
"type": "die",
"restrict": [
"trollish",
"special"
],
"desc": "In place of their clawed feet, this Troll possesses a pair of bony hooves that give them a kicking weapon (Pow 1, VA 1, WS 1). The Troll also walks with a distinctive gait and makes quite a racket when treading on hard surfaces. Any Speed or Stealthy tests made while on stone, ice or really hard, smooth wood are at a +1 Ob. (May not be combined with Webbed Fingers And Toes.)"
},
"Crystalline Spines": {
"cost": 3,
"type": "die",
"restrict": [
"trollish",
"special"
],
"desc": "A growth of crystalline spines sprouts from teh back and shoulders of this Troll. The spines are sharp to the touch and break off and lodge themselves in the flesh of the unwary, causing great discomfort. Any creature that grapples or otherwise comes into contact with the Troll's spines suffers a +1 Ob to all tests until the spines are removed. This takes effect if the Troll grapples you, too. The penalty is cumulative for each contact. Spines can be plucked out in about a half hour. If the Surgery or Field dressing skill is used, they can be treated in the time it takes to tend to a Superficial wound. If the troll is forced onto their back–if they fall or are pushed–it is possible that the spines are crushed and flattened. Roll the die of fate. On a 1 the spines are crushed and unusable. It takes about a month for the crystals to regrow. (They always regrow.)"
},
"Devourer": {
"cost": 3,
"type": "die",
"restrict": [
"trollish",
"special"
],
"desc": "This Troll eats anything–soil, stone, wood, metal and flesh. They are indiscriminate in their choices and in fact cannot help themselves from sampling their surroundings. Add +1 VA to bite attacks."
},
"Earth Blood": {
"cost": 3,
"type": "die",
"restrict": [
"trollish",
"special"
],
"desc": "No matter how badly wounded, this Troll will always heal. Cuts will close, bones will knit and even lost limbs will regrow. This Troll can never fail a Health test for Recovery (though they can roll poorly, in which case maximum healing time is required for the wound to close). The only way to destroy this beast is to put them to the fire."
},
"Enemy Of The Sun (Troll)": {
"cost": 4,
"type": "die",
"restrict": [
"trollish",
"special"
],
"desc": "The greatest, most fearsome of Trolls have been bred to resist the burning rays of the sun. They may travel by day and not be turned to stone as with the Night Blooded trait. Enemies of the Sun do not suffer penalties for dim or twilit light, but they can no longer see in complete darkness (+4 Ob in pitch dark). This trait replaces the Night Blooded trait."
},
"Hammer Hands": {
"cost": 2,
"type": "die",
"restrict": [
"trollish",
"special"
],
"desc": "With hands like great mattocks, this Troll is best suited to brawling and pummeling. Add +1D to the Troll's Brawling skill for the purposes of bare-fisted Block, Strike, Beat and Push actions. The Troll suffers a +1 Ob to any other skill that requires manual dexterity (including Throwing and weapons skills)."
},
"Hook Hand": {
"cost": 4,
"type": "die",
"restrict": [
"trollish",
"special"
],
"desc": "One of this Trolls hands has formed into a massive bony hook. This hook can be used as a weapon: Pow 2, VA 1, WS 2, Short; may Great Strike. In fact, it is really to big and clumsy to be used for anything else."
},
"Horns": {
"cost": 5,
"type": "die",
"restrict": [
"trollish",
"special"
],
"desc": "Great stony horns protrude from the Troll's skull. In addition to being terribly intimidating, they also serve a somewhat practical purpose. When the Troll charges another creature of relatively similar size, they may choose to add +1D to their Charge Power or to do damage on the Charge as if they had been simultaneously Striking. Damage is Pow 2, VA 2, WS 1. The first two successes of the Charge attack count as a superficial wound to the victim. Four successes over equals a Mark hit. Six over is a Superb hit."
},
"Iron Hide": {
"cost": 6,
"type": "die",
"restrict": [
"trollish",
"special"
],
"desc": "This skin is as tough as iron and scaled like a shark. All incoming damage is reudced by -1 pip on the PTGS: B8 damage would be reduced to B7, etc."
},
"Malformed": {
"cost": 1,
"type": "character",
"restrict": [
"trollish",
"special"
]
},
"Mossy Coat": {
"cost": 2,
"type": "call_on",
"restrict": [
"trollish",
"special"
],
"desc": "Covered in a coat of moss and slime, this Troll blends in quite well to forest or earthy surroundings. Call-on for Stealthy or Camouflage in forest or mines."
},
"Oafishly Charming": {
"cost": 1,
"type": "character",
"restrict": [
"trollish",
"special"
]
},
"Repulsive Blood": {
"cost": 2,
"type": "die",
"restrict": [
"trollish",
"special"
],
"desc": "This Troll's blood is noxious and caustic. If the Troll should be bitten for effect, the horrid liquid runs into the mouth of the attacking creature and causes great discomfort and nausea. Any creature that bites the Troll, penetrates its armor and achieves a Mark result, must make an Ob 4 Forte test. Any successes missed equal an obstacle penalty that lasts for three exchanges."
},
"Ridged Brow": {
"cost": 3,
"type": "die",
"restrict": [
"trollish",
"special"
],
"desc": "Protruding out over this Troll's eyes is a stony brow like unto a craggy cliff. This provides +1D of armor to the Troll's head."
},
"Scaly Skin": {
"cost": 4,
"type": "die",
"restrict": [
"trollish",
"special"
],
"desc": "Scaly Skin provides 2D of armor to the limbs and head, and 3D to the chest. This skin cannot \"fail\" and become damaged like normal armor: 1s are ignored in this case. VA works as normal against this armor."
},
"Stone Skin": {
"cost": 5,
"type": "die",
"restrict": [
"trollish",
"special"
],
"desc": "The hide of this creature is impossibly tough. Stone Skin provides 3D of armor the head and limbs, and 4D to the chest. This skin cannot \"fail\" and become damaged like normal armor: 1s are ignored in this case. VA works as normal against this armor."
},
"Toothy Maw": {
"cost": 3,
"type": "die",
"restrict": [
"trollish",
"special"
],
"desc": "This Troll's jaws are massive, distended and terrible. The Toothy Maw counts as: Pow 3, Add 2, VA 1, WS 1, Shortest. It replaces the Fanged trait."
},
"Troll Savant": {
"cost": 1,
"type": "character",
"restrict": [
"trollish",
"special"
],
"desc": "This Troll has the uncanny ability to form complete sentences, understand what is said to them and possibly even comprehend what a written language, map or glyph could represent–if it were possible in the first place for scratchings on stone or leather to mean anything. They are a mental giant among their peers and are appropriately hated and feared."
},
"Tusks": {
"cost": 2,
"type": "die",
"restrict": [
"trollish",
"special"
],
"desc": "This Troll's lower canine teeth protrude into formidable tusks. The beast may use these sharpened weapons to gore the unwary. Add +1 Power to the Troll's bite attack when it is at the hands fighting distance. This trait may be combined with Fangs or Toothy Maw."
},
"Vestigial Wings": {
"cost": 1,
"type": "character",
"restrict": [
"trollish",
"special"
],
"desc": "Two small, bat-like wings protrude from the Troll's shoulders. Flap, flap."
},
"Webbed Fingers And Toes": {
"cost": 1,
"type": "die",
"restrict": [
"trollish",
"special"
],
"desc": "Stride 5 when swimming."
}
}

@ -16,7 +16,7 @@
], ],
"traits": [ "traits": [
1, 1,
"Misunderstood", "Outsider",
"Gifted", "Gifted",
"Abused", "Abused",
"Spirit Familiar" "Spirit Familiar"

@ -108,7 +108,7 @@
"type": "die", "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." "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": { "Outsider": {
"cost": 2, "cost": 2,
"type": "die", "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." "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."

@ -1,21 +1,38 @@
require_relative 'data/gold' require_relative 'data/gold'
require_relative 'data/wizard' require_relative 'data/wizard'
require_relative 'data/dark_elf' require_relative 'data/dark_elf'
require_relative 'data/troll'
module Charred module Charred
class Data class Data
include Charred::Gold include Charred::Gold
include Charred::Wizard include Charred::Wizard
include Charred::DarkElf include Charred::DarkElf
include Charred::Troll
attr :data attr :data
def initialize def initialize
@data = {} @data = {}
puts 'loading gold'
load_gold(@data) load_gold(@data)
puts 'loading wizard burner'
load_wizard(@data) load_wizard(@data)
puts 'loading dark elves'
load_dark_elf(@data) load_dark_elf(@data)
puts 'loading trolls'
load_troll(@data)
end
def verbose_merge(to, from)
from.keys.each do |k|
puts "warning: destination contains #{k}" if to.include? k
to[k] = from[k]
end
end end
def lifepath_requirements(expr) def lifepath_requirements(expr)

@ -5,11 +5,11 @@ module Charred
def load_dark_elf(data) def load_dark_elf(data)
file = File.read('data/dark_elf/skills.json') file = File.read('data/dark_elf/skills.json')
skills = JSON.parse(file) skills = JSON.parse(file)
data[:skills].merge! skills verbose_merge data[:skills], skills
file = File.read('data/dark_elf/traits.json') file = File.read('data/dark_elf/traits.json')
traits = JSON.parse(file) traits = JSON.parse(file)
data[:traits].merge! traits verbose_merge data[:traits], traits
file = File.read('data/dark_elf/lifepaths.json') file = File.read('data/dark_elf/lifepaths.json')
lifepaths = JSON.parse(file) lifepaths = JSON.parse(file)

@ -0,0 +1,29 @@
require 'json'
module Charred
module Troll
def load_troll(data)
data[:stocks] << 'troll'
file = File.read('data/troll/skills.json')
skills = JSON.parse(file)
verbose_merge data[:skills], skills
file = File.read('data/troll/traits.json')
traits = JSON.parse(file)
verbose_merge data[:traits], traits
file = File.read('data/troll/lifepaths.json')
lifepaths = JSON.parse(file)
data[:lifepaths]['troll'] = lifepaths
file = File.read("data/troll/resources.json")
resources = JSON.parse(file)
data[:resources]['troll'] = resources
file = File.read("data/troll/starting_stat_pts.json")
stats = JSON.parse(file)
data[:stat_pts]['troll'] = stats
end
end
end

@ -3,17 +3,16 @@ require 'json'
module Charred module Charred
module Wizard module Wizard
def load_wizard(data) def load_wizard(data)
puts 'Loading wizard burner'
file = File.read('data/wizard/lifepaths.json') file = File.read('data/wizard/lifepaths.json')
wizard_data = JSON.parse(file) wizard_data = JSON.parse(file)
file = File.read('data/wizard/skills.json') file = File.read('data/wizard/skills.json')
wizard_skills = JSON.parse(file) wizard_skills = JSON.parse(file)
data[:skills].merge! wizard_skills verbose_merge data[:skills], wizard_skills
file = File.read('data/wizard/traits.json') file = File.read('data/wizard/traits.json')
wizard_traits = JSON.parse(file) wizard_traits = JSON.parse(file)
data[:traits].merge! wizard_traits verbose_merge data[:traits], wizard_traits
man = data[:lifepaths]['man'] man = data[:lifepaths]['man']
@ -111,7 +110,6 @@ module Charred
end end
data[:lifepaths]['man'] = man data[:lifepaths]['man'] = man
puts 'loaded!'
data data
end end
end end

@ -499,7 +499,7 @@ function DisplayTrait(name, traitdata){
this.bonus = data["bonus"]; this.bonus = data["bonus"];
var rawRestrict = data["restrict"] var rawRestrict = data["restrict"]
// Stock is one of mannish, dwarven, elven, orcish. If there is no restriction, it is set to null. // Stock is one of mannish, dwarven, elven, orcish, trollish. If there is no restriction, it is set to null.
this.stock = null; this.stock = null;
// Category is common, lifepath, or special // Category is common, lifepath, or special
this.category = null; this.category = null;
@ -512,7 +512,7 @@ function DisplayTrait(name, traitdata){
this.category = []; this.category = [];
this.category.push(flag); this.category.push(flag);
} }
else if ( flag == "mannish" || flag == "dwarven" || flag == "elven" || flag == "orcish" ){ else if ( flag == "mannish" || flag == "dwarven" || flag == "elven" || flag == "orcish" || flag == "trollish" ){
if ( !this.stock ) if ( !this.stock )
this.stock = []; this.stock = [];
this.stock.push(flag); this.stock.push(flag);

@ -265,13 +265,13 @@ function BurningDataService($http) {
this.dataSetsLoaded = 0; this.dataSetsLoaded = 0;
// Total data sets: // Total data sets:
// lifepaths: 6 (man, dwarf, elf, orc, roden, wolf) // lifepaths: 7 (man, dwarf, elf, orc, roden, wolf, troll)
// stat points: 6 (man, dwarf, elf, orc, roden, wolf) // stat points: 7 (man, dwarf, elf, orc, roden, wolf, troll)
// skills // skills
// traits // traits
// resources: 6 (man, dwarf, elf, orc, roden, wolf) // resources: 7 (man, dwarf, elf, orc, roden, wolf. troll)
// TOTAL: 20 // TOTAL: 23
this.totalDataSets = 20; this.totalDataSets = 23;
this.onAllDatasetsLoaded = null; this.onAllDatasetsLoaded = null;
this.registerOnAllDatasetsLoaded = function(callback){ this.registerOnAllDatasetsLoaded = function(callback){
if ( this.dataSetsLoaded >= this.totalDataSets ){ if ( this.dataSetsLoaded >= this.totalDataSets ){
@ -290,6 +290,7 @@ function BurningDataService($http) {
} }
} }
var stocks = ["man", "dwarf", "elf", "orc", "roden", "wolf", "troll"];
var myself = this; var myself = this;
/* Load lifepaths from server */ /* Load lifepaths from server */
@ -311,13 +312,6 @@ function BurningDataService($http) {
}); });
} }
loadLifepathsForStock("man");
loadLifepathsForStock("dwarf");
loadLifepathsForStock("elf");
loadLifepathsForStock("orc");
loadLifepathsForStock("roden");
loadLifepathsForStock("wolf");
/* Load starting stat points table from server */ /* Load starting stat points table from server */
var loadStartingStatPtsForStock = function(stock){ var loadStartingStatPtsForStock = function(stock){
if( ! isValidStock(stock) ){ if( ! isValidStock(stock) ){
@ -337,13 +331,6 @@ function BurningDataService($http) {
}); });
} }
loadStartingStatPtsForStock("man");
loadStartingStatPtsForStock("dwarf");
loadStartingStatPtsForStock("elf");
loadStartingStatPtsForStock("orc");
loadStartingStatPtsForStock("roden");
loadStartingStatPtsForStock("wolf");
/* Load starting stat points table from server */ /* Load starting stat points table from server */
var loadResourcesForStock = function(stock){ var loadResourcesForStock = function(stock){
if( ! isValidStock(stock) ){ if( ! isValidStock(stock) ){
@ -363,12 +350,11 @@ function BurningDataService($http) {
}); });
} }
loadResourcesForStock("man"); for (var i = 0; i < stocks.length; i++) {
loadResourcesForStock("dwarf"); loadLifepathsForStock(stocks[i]);
loadResourcesForStock("elf"); loadStartingStatPtsForStock(stocks[i]);
loadResourcesForStock("orc"); loadResourcesForStock(stocks[i]);
loadResourcesForStock("roden"); }
loadResourcesForStock("wolf");
/* Load skills from server */ /* Load skills from server */
$http.get("/skills", {'timeout': 3000} ). $http.get("/skills", {'timeout': 3000} ).

@ -555,8 +555,13 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
$scope.incrementStat = function(stat){ $scope.incrementStat = function(stat){
// Man stock has max 8 pts in any stat // Man stock has max 8 pts in any stat
if ( stat.exp() == 8 ) if ( $scope.stock == "troll" && (stat.name == "Power" || stat.name == "Forte" ) ) {
return; if ( stat.exp() == 9 )
return;
} else {
if ( stat.exp() == 8 )
return;
}
var specificStatPoints = 0; var specificStatPoints = 0;
var eitherStatPoints = $scope.unspentStatPoints.either; var eitherStatPoints = $scope.unspentStatPoints.either;
@ -991,7 +996,12 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
var bonus = computeBonus(name) var bonus = computeBonus(name)
if( "Mortal Wound" == name ){ if( "Mortal Wound" == name ){
var shadeAndExp = computeStatAverage($scope.statsByName, ["Power", "Forte"]); var shadeAndExp;
if ( $scope.stock == 'troll' ) {
shadeAndExp = computeStatAverage($scope.statsByName, ["Power", "Forte"], true);
} else {
shadeAndExp = computeStatAverage($scope.statsByName, ["Power", "Forte"]);
}
return {"shade" : shadeAndExp[0], "exp" : shadeAndExp[1] + 6 + bonus}; return {"shade" : shadeAndExp[0], "exp" : shadeAndExp[1] + 6 + bonus};
} }
else if ( "Reflexes" == name ){ else if ( "Reflexes" == name ){
@ -2767,7 +2777,7 @@ function calculateUnspentResourcePoints($scope){
} }
function isValidStock(stock){ function isValidStock(stock){
return stock == "man" || stock == "elf" || stock == "orc" || stock == "dwarf" || stock == "roden" || stock == "wolf"; return stock == "man" || stock == "elf" || stock == "orc" || stock == "dwarf" || stock == "roden" || stock == "wolf" || stock =="troll";
} }
function restrictionStockToValidStock(stock){ function restrictionStockToValidStock(stock){
@ -2784,6 +2794,8 @@ function restrictionStockToValidStock(stock){
return "wolf"; return "wolf";
else if ( stock == "roden" ) else if ( stock == "roden" )
return "roden"; return "roden";
else if ( stock == "trollish" )
return "troll";
} }
function validStockToRestrictionStock(stock){ function validStockToRestrictionStock(stock){
@ -2800,6 +2812,8 @@ function validStockToRestrictionStock(stock){
return "roden"; return "roden";
else if ( stock == "wolf" ) else if ( stock == "wolf" )
return "wolfish"; return "wolfish";
else if ( stock == "troll" )
return "trollish";
} }
function attributeModifyingQuestions($scope, attribute) function attributeModifyingQuestions($scope, attribute)
@ -3127,15 +3141,61 @@ function attributeModifyingQuestions($scope, attribute)
return val; return val;
} }
var bitterMod = function() {
var darkElfGear = [
'Bitter Poison',
'Spiteful Poison',
'Lock Picks',
'Long Knife',
'Barbed Javelins',
'Garrote',
'Caltrops',
'Tools Of The Trade',
'Cloak Of Darkness',
'Climbing Claws',
'Remote Refuge',
'Morlin Armor',
'Morlin Weapons'
]
var bitterRps = 0;
var isBitter = function(gear) {
for (var i = 0; i < darkElfGear.length; i++) {
if (gear.desc.startsWith(darkElfGear[i])) {
return false;
}
}
return true;
}
for (var k in $scope.gear) {
var gear = $scope.gear[k];
if (isBitter(gear)) {
bitterRps += gear.cost;
}
}
for (var k in $scope.property) {
var prop = $scope.property[k];
if (isBitter(prop)) {
bitterRps += prop.cost;
}
}
return Math.floor(bitterRps/10);
}
result.push( result.push(
{question: "+1 Spite for every point of Grief", computed: true, compute: griefMod}, {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: "+1 Spite for each of several spiteful traits", computed: true, compute: traitsMod},
{question: "+1 Spite for every 10 rps spent on Elven resources", computed: true, compute: bitterMod},
{question: "Has the character been betrayed by their friends?", math_label: "(+1 Spite)", modifier: 1}, {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: "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 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: "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 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} {question: "Does the character still love someone on the other side?", math_label: "(-2 Spite)", modifier: -2}
); );
} }
else if ( attribute == "Hatred" ) else if ( attribute == "Hatred" )
@ -3380,7 +3440,7 @@ function getGeneralSkillNameFor(skillName){
} }
// Given n stat names, average the stats taking into account shade. Return the resulting [shade, exp] tuple. // Given n stat names, average the stats taking into account shade. Return the resulting [shade, exp] tuple.
var computeStatAverage = function(statsByName, statNames){ var computeStatAverage = function(statsByName, statNames, roundUp){
var sum = 0; var sum = 0;
var shade = 'B'; var shade = 'B';
var allGray = true; var allGray = true;
@ -3423,6 +3483,9 @@ var computeStatAverage = function(statsByName, statNames){
} }
var exp = Math.floor( sum / stats.length ); var exp = Math.floor( sum / stats.length );
if ( roundUp ) {
exp = Math.ceil( sum / stats.length );
}
return [shade, exp]; return [shade, exp];
} }

@ -1,5 +1,5 @@
var serverSettings = { var serverSettings = {
'versionString' : '2.1.0', 'versionString' : '2.2.0',
'storageType' : 'client', 'storageType' : 'client',
'displayAttrMath' : 'false' 'displayAttrMath' : 'false'
} }

@ -209,6 +209,10 @@
The wizard content is taken from the 'Wizard Burner' chapter of Burning Wheel Codex. Some liberties were taken to prevent The wizard content is taken from the 'Wizard Burner' chapter of Burning Wheel Codex. Some liberties were taken to prevent
collision with existing traits. collision with existing traits.
</p> </p>
<p>
The Gifted Child lifepath trait 'Misunderstood' was changed to 'Outsider' to avoid a conflict with Roden trait of the
same name.
</p>
<p> <p>
The Demented Inventor lifepath trait 'Demented' was changed to 'Unhinged' to avoid a conflict with Great Wolf trait of the 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. same name and to preserve the mechanical flavor.
@ -225,6 +229,10 @@
<p> <p>
The Wastrel lifepath trait 'Feral' was changed to 'Feral Elf' to avoid a conflict with the character trait of the same name. The Wastrel lifepath trait 'Feral' was changed to 'Feral Elf' to avoid a conflict with the character trait of the same name.
</p> </p>
<h2 id='trolls'>Trolls</h2>
<p>
Trolls have several traits which conflict with Roden and Orc traits. They are: 'Night Eyed', 'Tough', 'Massive Stature' and 'Enemy Of The Sun'. The troll versions are appended with '(Troll)'.
</p>
</div> </div>
</div> </div>
</div> </div>

@ -91,6 +91,7 @@
<option value='orc'>Orc</option> <option value='orc'>Orc</option>
<option value='roden'>Roden</option> <option value='roden'>Roden</option>
<option value='wolf'>Great Wolf</option> <option value='wolf'>Great Wolf</option>
<option value='troll'>Troll</option>
</select> </select>
</div> </div>
<div class='col-md-1'> <div class='col-md-1'>

Loading…
Cancel
Save