Compare commits

..

10 Commits

  1. 12
      CHANGELOG.md
  2. 6
      Gemfile
  3. 84
      Gemfile.lock
  4. 47
      README.md
  5. 32
      src/app.rb
  6. 1
      src/data/custom/.gitignore
  7. 551
      src/data/dark_elf/lifepaths.json
  8. 231
      src/data/dark_elf/resources.json
  9. 4
      src/data/dark_elf/traits.json
  10. 3837
      src/data/gold/lifepaths/dwarf.json
  11. 3970
      src/data/gold/lifepaths/elf.json
  12. 23311
      src/data/gold/lifepaths/man.json
  13. 3061
      src/data/gold/lifepaths/orc.json
  14. 3325
      src/data/gold/lifepaths/roden.json
  15. 1835
      src/data/gold/lifepaths/wolf.json
  16. 307
      src/data/gold/resources/dwarf.json
  17. 355
      src/data/gold/resources/elf.json
  18. 871
      src/data/gold/resources/man.json
  19. 425
      src/data/gold/resources/orc.json
  20. 281
      src/data/gold/resources/roden.json
  21. 87
      src/data/gold/resources/wolf.json
  22. 114
      src/data/gold/starting_stat_pts/dwarf.json
  23. 146
      src/data/gold/starting_stat_pts/elf.json
  24. 90
      src/data/gold/starting_stat_pts/man.json
  25. 98
      src/data/gold/starting_stat_pts/orc.json
  26. 74
      src/data/gold/starting_stat_pts/roden.json
  27. 58
      src/data/gold/starting_stat_pts/wolf.json
  28. 130
      src/data/gold/stocks/dwarf.json
  29. 161
      src/data/gold/stocks/elf.json
  30. 99
      src/data/gold/stocks/man.json
  31. 114
      src/data/gold/stocks/orc.json
  32. 90
      src/data/gold/stocks/roden.json
  33. 75
      src/data/gold/stocks/wolf.json
  34. 10
      src/data/gold/traits.json
  35. 1151
      src/data/troll/lifepaths.json
  36. 117
      src/data/troll/resources.json
  37. 82
      src/data/troll/starting_stat_pts.json
  38. 100
      src/data/troll/stock.json
  39. 2330
      src/data/wizard/lifepaths.json
  40. 15
      src/lib/data.rb
  41. 27
      src/lib/data/custom.rb
  42. 8
      src/lib/data/dark_elf.rb
  43. 22
      src/lib/data/gold.rb
  44. 17
      src/lib/data/troll.rb
  45. 5
      src/lib/data/wizard.rb
  46. 34
      src/lib/stock.rb
  47. 67
      src/public/css/stocked.css
  48. 47
      src/public/js/burning-serialize.js
  49. 205
      src/public/js/burning-service.js
  50. 463
      src/public/js/burning.js
  51. 4
      src/public/js/server_settings.js
  52. 388
      src/public/js/stocked.js
  53. 42
      src/public/js/stocked/editable-input.js
  54. 1796
      src/public/js/stocked/test/data-archive.js
  55. 23
      src/public/js/stocked/test/data_1.json
  56. 7
      src/views/index.erb
  57. 4
      src/views/partials/help.erb
  58. 9
      src/views/partials/main.erb
  59. 377
      src/views/partials/stocked.erb
  60. 6
      src/views/partials/stocked/editableInput.erb
  61. 1
      tests/Algoric the Apologist Character Sheet.char
  62. 1
      tests/Algoric the Apologist Character Sheet.model
  63. 1
      tests/Tyastanarphen Character Sheet.char
  64. 1
      tests/Tyastanarphen Character Sheet.model
  65. 133
      tests/data/custom/test.lifepaths
  66. 25
      tests/data/custom/test.resources
  67. 39
      tests/data/custom/test.skills
  68. 95
      tests/data/custom/test.stock
  69. 55
      tests/data/custom/test.traits

@ -11,16 +11,6 @@ and this project adheres to [Semantic Versioning](semver).
- Custom upload for your own data files
- Updates to Roden and Great Wolves files (currently the data comes from Monster Burner and not Codex)
## [3.0.0] - 2023-09-03
### Added
- Custom stocks can now be added to a server
- Stock data moved from code to dedicated data file
- Data files split into stock, lifepath, skills, and traits files, with designated extensions
- "Born" lifepaths with arbitrary names
### Fixed
- Spite shade calculation
## [2.3.0] - 2019-07-28
### Added
- Display emotional attribute traits on PDF
@ -69,4 +59,4 @@ and this project adheres to [Semantic Versioning](semver).
[changelog]: https://keepachangelog.com/en/1.0.0/
[semver]: https://semver.org/spec/v2.0.0.html
[semver]: https://semver.org/spec/v2.0.0.html

@ -3,9 +3,9 @@ source 'https://rubygems.org'
gem 'thin'
gem 'sinatra'
gem 'sinatra-contrib'
gem 'prawn'
gem 'prawn-templates'
gem 'deep_merge', '~> 1.2', '>= 1.2.1'
gem 'prawn', '2.2.2'
gem 'prawn-templates', '0.1.1'
gem 'rubyzip'
group :development do
gem 'rerun'

@ -1,71 +1,73 @@
GEM
remote: https://rubygems.org/
specs:
Ascii85 (1.1.0)
Ascii85 (1.0.3)
afm (0.2.2)
daemons (1.4.1)
deep_merge (1.2.2)
backports (3.15.0)
daemons (1.3.1)
eventmachine (1.2.7)
ffi (1.15.5)
ffi (1.11.1)
hashery (2.1.2)
listen (3.8.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
multi_json (1.15.0)
mustermann (3.0.0)
ruby2_keywords (~> 0.0.1)
pdf-core (0.9.0)
pdf-reader (2.11.0)
Ascii85 (~> 1.0)
listen (3.1.5)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
ruby_dep (~> 1.2)
multi_json (1.13.1)
mustermann (1.0.3)
pdf-core (0.7.0)
pdf-reader (2.2.0)
Ascii85 (~> 1.0.0)
afm (~> 0.2.1)
hashery (~> 2.0)
ruby-rc4
ttfunk
prawn (2.4.0)
pdf-core (~> 0.9.0)
ttfunk (~> 1.7)
prawn-templates (0.1.2)
prawn (2.2.2)
pdf-core (~> 0.7.0)
ttfunk (~> 1.5)
prawn-templates (0.1.1)
pdf-reader (~> 2.0)
prawn (~> 2.2)
rack (2.2.8)
rack-protection (3.1.0)
rack (~> 2.2, >= 2.2.4)
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
rack (2.0.7)
rack-protection (2.0.5)
rack
rb-fsevent (0.10.3)
rb-inotify (0.10.0)
ffi (~> 1.0)
rerun (0.14.0)
rerun (0.13.0)
listen (~> 3.0)
ruby-rc4 (0.1.5)
ruby2_keywords (0.0.5)
sinatra (3.1.0)
mustermann (~> 3.0)
rack (~> 2.2, >= 2.2.4)
rack-protection (= 3.1.0)
ruby_dep (1.5.0)
rubyzip (2.3.2)
sinatra (2.0.5)
mustermann (~> 1.0)
rack (~> 2.0)
rack-protection (= 2.0.5)
tilt (~> 2.0)
sinatra-contrib (3.1.0)
sinatra-contrib (2.0.5)
backports (>= 2.8.2)
multi_json
mustermann (~> 3.0)
rack-protection (= 3.1.0)
sinatra (= 3.1.0)
tilt (~> 2.0)
thin (1.8.2)
mustermann (~> 1.0)
rack-protection (= 2.0.5)
sinatra (= 2.0.5)
tilt (>= 1.3, < 3)
thin (1.7.2)
daemons (~> 1.0, >= 1.0.9)
eventmachine (~> 1.0, >= 1.0.4)
rack (>= 1, < 3)
tilt (2.2.0)
ttfunk (1.7.0)
tilt (2.0.9)
ttfunk (1.5.1)
PLATFORMS
x86_64-linux
ruby
DEPENDENCIES
deep_merge (~> 1.2, >= 1.2.1)
prawn
prawn-templates
prawn (= 2.2.2)
prawn-templates (= 0.1.1)
rerun
rubyzip
sinatra
sinatra-contrib
thin
BUNDLED WITH
2.3.7
1.17.2

@ -1,10 +1,14 @@
# Charred Gold
This is a fork of [Charred Black](https://github.com/modality/charred-black) Below is a partial fork of the original README of Charred Black; especially since there is a departure from some of the original values. The unofficial, online, Burning Wheel Gold (+Codex) character burner. Adapted from [Charred](https://charred.herokuapp.com/). Later adapted from [Charred Black](https://github.com/modality/charred-black)
This is a fork of [https://github.com/modality/charred-black](Charred Black)
Below is the original README of Charred Black, possibly to be updated in the future;
especially since there is a departure from some of the original values.
The unofficial, online, Burning Wheel Gold (+Codex) character burner. Adapted from [Charred](https://charred.herokuapp.com/).
## Project Structure
* `Dockerfile` and `Dockerfile.dev` - Container definition files. The dev container has automated reloading if you mount the container's filesystem to the host machine. These mostly serve as examples to launch the app yourself, and are no longer supported. The app uses Sinatra to hope, and you should just be able to `bundle install` and then run `ruby app.rb` from the source directory. I will try to publish a SysV initscript soon.
* `Dockerfile` and `Dockerfile.dev` - Container definition files. The dev container has automated reloading if you mount the container's filesystem to the host machine. If you are unfamiliar with Docker, this app uses Sinatra and you can _probably_ get by with a bundle install and `ruby ./src/app.rb`.
* `src/data` - Binaries and data files for lifepaths live here, see `dark_elf` and `wizard` directory for well-defined examples
* `src/lib` - Ruby scripts for caching, PDF generation, and data loading
* `src/public` - Javascript and CSS
@ -13,27 +17,38 @@ This is a fork of [Charred Black](https://github.com/modality/charred-black) Bel
## Beliefs
We 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.
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 Gold 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.
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 Gold's startup time and memory requirements. The design of your lifepath requirements and emotional attributes may not be supported by the editor, or may be convoluted to implement. Most importantly, deciding to include any community-made content makes me an arbiter of quality, and I'd prefer not to have the Enmity Clause invoked because I rejected someone's homebrew.
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. The design of your lifepath
requirements and emotional attributes may not be supported by the editor, or may be convoluted to implement. Most importantly, deciding to include any community-made content makes me an arbiter of quality, and I'd prefer not to have the Enmity Clause invoked because I rejected
someone's homebrew.
The current maintainers of Charred Gold are working on some tooling to allow "easy" creation of new settings, stocks, LPs, and so on. The exact method for handling has not been decided precisely.
**However**, 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 create data files to use with this tool and share them with your friends. If someone else
wants to keep a repository or forum thread of data files known to work with Charred Black, I am happy 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 Gold 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:
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.
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:
@ -126,19 +141,3 @@ Some notes:
}
}
```
# Building
Requires Ruby 3.1 or better for support.
On Debian or derivatives this requires `apt-get install ruby-dev` in order to allow eventmachine to work (which is a hard dependency for thin, the webserver we're using for charred.
In order to run in normal use, simply run:
```
# sudo apt-get install ruby-dev bundler
# bundler install # on some distros this might be bundler3.1
# cd src
# HOST=0.0.0.0 PORT=7878 RACK_ENV=production ruby app.rb
```

@ -33,6 +33,11 @@ get /\/([\w]+)_partial/ do
erb "partials/#{partial}".to_sym
end
get /\/stocked\/([\w]+)_partial/ do
partial = params['captures'].first
erb "partials/stocked/#{partial}".to_sym
end
get '/namegen/:gender' do
if params['gender'] == 'female'
['Ada', 'Belle', 'Carmen', 'Desdemona', 'Edie'].sample
@ -49,20 +54,24 @@ get '/traits' do
json DATA[:traits]
end
get '/stocks' do
json DATA[:stocks]
end
get '/lifepaths/:stock' do
if DATA[:stocks].keys.include? params['stock']
if DATA[:stocks].include? params['stock']
json DATA[:lifepaths][params['stock']]
else
404
end
end
get '/starting_stat_pts/:stock' do
if DATA[:stocks].include? params['stock']
json DATA[:stat_pts][params['stock']]
else
404
end
end
get '/resources/:stock' do
if DATA[:stocks].keys.include? params['stock']
if DATA[:stocks].include? params['stock']
json DATA[:resources][params['stock']]
else
404
@ -114,6 +123,17 @@ post '/wiki' do
"/get_file?file=#{key}&download_name=#{data['name']} Character Sheet.wiki"
end
post '/stocked_download' do
request.body.rewind
raw = request.body.readpartial(16 * 1024)
puts raw
data = JSON.parse(raw)
key = "stock-#{Time.now.strftime('%Y%m%d%H%M%S%L')}-#{rand(1...10000)}"
CACHE.store key, data
"/get_file?file=#{key}&download_name=#{data['Name']}.stock"
end
get '/get_file' do
data = nil
if params['download_name'].match(/\.pdf$/)

@ -1,299 +1,296 @@
{
"stock": "elf",
"settings": {
"Path Of Spite Subsetting": {
"Griever": {
"time": 3,
"res": 0,
"skills": [
[
3,
"Sorrow Of Truth",
"Dark Elf-wise"
]
],
"traits": [
"Path Of Spite Subsetting": {
"Griever": {
"time": 3,
"res": 0,
"skills": [
[
3,
"Sorrow Of Truth",
"Dark Elf-wise"
]
],
"traits": [
1,
"Spite"
]
},
"Wastrel": {
"time": 25,
"res": 3,
"skills": [
[
6,
"Scavenging",
"Brawling",
"Forest-wise",
"Wasteland-wise",
"Animal-wise"
]
],
"traits": [
2,
"Filthy",
"Feral Elf"
],
"requires": "Griever",
"requires_expr": [
"griever"
]
},
"Thief": {
"time": 18,
"res": 6,
"stat": [
[
1,
"Spite"
"pm"
]
},
"Wastrel": {
"time": 25,
"res": 3,
"skills": [
[
6,
"Scavenging",
"Brawling",
"Forest-wise",
"Wasteland-wise",
"Animal-wise"
]
],
"traits": [
2,
"Filthy",
"Feral Elf"
],
"requires": "Griever",
"requires_expr": [
"griever"
],
"skills": [
[
6,
"Lock Pick",
"Stealthy",
"Climbing",
"Sleight Of Hand",
"Dirge Of Night"
]
},
"Thief": {
"time": 18,
"res": 6,
"stat": [
[
1,
"pm"
]
],
"skills": [
[
6,
"Lock Pick",
"Stealthy",
"Climbing",
"Sleight Of Hand",
"Dirge Of Night"
]
],
"traits": [
],
"traits": [
1,
"Thief"
],
"requires": "Griever",
"requires_expr": [
"griever"
]
},
"Assassin": {
"time": 15,
"res": 6,
"stat": [
[
1,
"Thief"
],
"requires": "Griever",
"requires_expr": [
"griever"
"p"
]
},
"Assassin": {
"time": 15,
"res": 6,
"stat": [
[
1,
"p"
]
],
"skills": [
[
7,
"Intimidation",
"Poisons",
"Escape Artist",
"Knives",
"Garotte",
"Keen Of Terror"
]
],
"traits": [
],
"skills": [
[
7,
"Intimidation",
"Poisons",
"Escape Artist",
"Knives",
"Garotte",
"Keen Of Terror"
]
],
"traits": [
1,
"Murderous",
"Callous",
"Cold Blooded"
],
"requires": "Griever",
"requires_expr": [
"griever"
]
},
"Stalker": {
"time": 20,
"res": 8,
"skills": [
[
8,
"Hunting",
"Elf-wise",
"Tracking",
"Trapper",
"Observation",
"Stealthy",
"Throwing",
"Javelin",
"Supplication To Shadows"
]
],
"traits": [
1,
"Saturnine"
],
"requires": "Griever",
"requires_expr": [
"griever"
]
},
"Deceiver": {
"time": 35,
"res": 10,
"stat": [
[
1,
"Murderous",
"Callous",
"Cold Blooded"
],
"requires": "Griever",
"requires_expr": [
"griever"
"m"
]
},
"Stalker": {
"time": 20,
"res": 8,
"skills": [
[
8,
"Hunting",
"Elf-wise",
"Tracking",
"Trapper",
"Observation",
"Stealthy",
"Throwing",
"Javelin",
"Supplication To Shadows"
]
],
"traits": [
],
"skills": [
[
7,
"Sleight Of Hand",
"Disguise",
"Inconspicuous",
"Rhyme Of The Unraveller"
]
],
"traits": [
1,
"Deceptive"
],
"requires": "Griever",
"requires_expr": [
"griever"
]
},
"Liar": {
"time": 25,
"res": 8,
"stat": [
[
1,
"Saturnine"
],
"requires": "Griever",
"requires_expr": [
"griever"
"m"
]
},
"Deceiver": {
"time": 35,
"res": 10,
"stat": [
[
1,
"m"
]
],
"skills": [
[
7,
"Sleight Of Hand",
"Disguise",
"Inconspicuous",
"Rhyme Of The Unraveller"
]
],
"traits": [
],
"skills": [
[
6,
"Falsehood",
"Soothing Platitudes",
"Persuasion",
"Twisted Tongue"
]
],
"traits": [
1,
"Compulsive Liar"
],
"requires": "Griever",
"requires_expr": [
"griever"
]
},
"Siren": {
"time": 55,
"res": 20,
"stat": [
[
1,
"Deceptive"
],
"requires": "Griever",
"requires_expr": [
"griever"
"pm"
]
},
"Liar": {
"time": 25,
"res": 8,
"stat": [
[
1,
"m"
]
],
"skills": [
[
6,
"Falsehood",
"Soothing Platitudes",
"Persuasion",
"Twisted Tongue"
]
],
"traits": [
],
"skills": [
[
9,
"Seduction",
"Etiquette",
"Persuasion",
"Soothing Platitudes",
"Gossip-wise",
"Fugue Of Discord",
"Litany Of Fools"
]
],
"traits": [
2,
"Charismatic",
"Femme Fatale/Homme Fatale"
],
"requires": "Assassin, Deceiver or Liar",
"requires_expr": [
"assassin",
"deceiver",
"liar"
]
},
"Eremite": {
"time": 150,
"res": 15,
"stat": [
[
1,
"Compulsive Liar"
],
"requires": "Griever",
"requires_expr": [
"griever"
"m"
]
},
"Siren": {
"time": 55,
"res": 20,
"stat": [
[
1,
"pm"
]
],
"skills": [
[
9,
"Seduction",
"Etiquette",
"Persuasion",
"Soothing Platitudes",
"Gossip-wise",
"Fugue Of Discord",
"Litany Of Fools"
]
],
"traits": [
2,
"Charismatic",
"Femme Fatale/Homme Fatale"
],
"requires": "Assassin, Deceiver or Liar",
"requires_expr": [
"assassin",
"deceiver",
"liar"
],
"skills": [
[
12,
"Philosophy",
"Ugly Truth",
"Obscure History",
"Symbology",
"Strategy",
"Elf-wise",
"Orc-wise",
"Dwarf-wise",
"Man-wise",
"Paean To The Dark Fire"
]
},
"Eremite": {
"time": 150,
"res": 15,
"stat": [
[
1,
"m"
]
],
"skills": [
[
12,
"Philosophy",
"Ugly Truth",
"Obscure History",
"Symbology",
"Strategy",
"Elf-wise",
"Orc-wise",
"Dwarf-wise",
"Man-wise",
"Paean To The Dark Fire"
]
],
"traits": [
],
"traits": [
2,
"Remote",
"Cold"
],
"requires": "Siren, Wastrel or Stalker",
"requires_expr": [
"siren",
"wastrel",
"stalker"
]
},
"Recluse": {
"time": 225,
"res": 25,
"stat": [
[
1,
"p"
]
],
"skills": [
[
2,
"Remote",
"Cold"
"Ancient History",
"Dwarf-wise",
"Elven Politics-wise",
"Cut Of The Quickened Mind"
],
"requires": "Siren, Wastrel or Stalker",
"requires_expr": [
"siren",
"wastrel",
"stalker"
[
5,
"General"
]
},
"Recluse": {
"time": 225,
"res": 25,
"stat": [
[
1,
"p"
]
],
"skills": [
[
2,
"Ancient History",
"Dwarf-wise",
"Elven Politics-wise",
"Cut Of The Quickened Mind"
],
[
5,
"General"
]
],
"traits": [
1,
"Vengeful"
],
"traits": [
1,
"Vengeful"
],
"requires": "Eremite or any three Dark Elf lifepaths",
"requires_expr": [
"+or",
[
"eremite"
],
"requires": "Eremite or any three Dark Elf lifepaths",
"requires_expr": [
"+or",
[
"eremite"
],
[
"+has_n_lifepaths_in",
3,
"path of spite subsetting:griever",
"path of spite subsetting:wastrel",
"path of spite subsetting:thief",
"path of spite subsetting:assassin",
"path of spite subsetting:stalker",
"path of spite subsetting:deceiver",
"path of spite subsetting:liar",
"path of spite subsetting:siren"
]
[
"+has_n_lifepaths_in",
3,
"path of spite subsetting:griever",
"path of spite subsetting:wastrel",
"path of spite subsetting:thief",
"path of spite subsetting:assassin",
"path of spite subsetting:stalker",
"path of spite subsetting:deceiver",
"path of spite subsetting:liar",
"path of spite subsetting:siren"
]
}
]
}
}
}
}

@ -1,117 +1,114 @@
{
"stock": "elf",
"resources": [
{
"name": "Bitter Poison",
"type": "gear",
"rp": 10
},
{
"name": "Spiteful Poison",
"type": "gear",
"rp": 20
},
{
"name": "Lock Picks",
"type": "gear",
"rp": 10
},
{
"name": "Long Knife",
"type": "gear",
"rp": 5
},
{
"name": "Barbed Javelins",
"type": "gear",
"rp": 3
},
{
"name": "Garrote",
"type": "gear",
"rp": 3
},
{
"name": "Caltrops",
"type": "gear",
"rp": 3
},
{
"name": "Tools Of The Trade",
"type": "gear",
"rp": 9
},
{
"name": "Cloak Of Darkness",
"type": "gear",
"rp": 30
},
{
"name": "Climbing Claws",
"type": "gear",
"rp": 5
},
{
"name": "Remote Refuge",
"type": "property",
"resources": [
{
"name": "Wasteland",
"rp": 20
},
{
"name": "Isolated Manor And Moorland",
"rp": 50
},
{
"name": "Hidden Fortress",
"rp": 100
},
{
"name": "Dark Forest, Cove Or Lonely Mountain",
"rp": 150
},
{
"name": "Safe House",
"rp": 25
}
]
},
{
"name": "Morlin Armor",
"type": "gear",
"resources": [
{
"name": "Light Mail",
"rp": 30
},
{
"name": "Heavy Mail",
"rp": 40
},
{
"name": "Plated Mail",
"rp": 100
}
]
},
{
"name": "Morlin Weapons",
"type": "gear",
"resources": [
{
"name": "+1 speed",
"rp": 15
},
{
"name": "+1 VA",
"rp": 15
},
{
"name": "+1 Power",
"rp": 30
}
]
}
]
}
[
{
"name": "Bitter Poison",
"type": "gear",
"rp": 10
},
{
"name": "Spiteful Poison",
"type": "gear",
"rp": 20
},
{
"name": "Lock Picks",
"type": "gear",
"rp": 10
},
{
"name": "Long Knife",
"type": "gear",
"rp": 5
},
{
"name": "Barbed Javelins",
"type": "gear",
"rp": 3
},
{
"name": "Garrote",
"type": "gear",
"rp": 3
},
{
"name": "Caltrops",
"type": "gear",
"rp": 3
},
{
"name": "Tools Of The Trade",
"type": "gear",
"rp": 9
},
{
"name": "Cloak Of Darkness",
"type": "gear",
"rp": 30
},
{
"name": "Climbing Claws",
"type": "gear",
"rp": 5
},
{
"name": "Remote Refuge",
"type": "property",
"resources": [
{
"name": "Wasteland",
"rp": 20
},
{
"name": "Isolated Manor And Moorland",
"rp": 50
},
{
"name": "Hidden Fortress",
"rp": 100
},
{
"name": "Dark Forest, Cove Or Lonely Mountain",
"rp": 150
},
{
"name": "Safe House",
"rp": 25
}
]
},
{
"name": "Morlin Armor",
"type": "gear",
"resources": [
{
"name": "Light Mail",
"rp": 30
},
{
"name": "Heavy Mail",
"rp": 40
},
{
"name": "Plated Mail",
"rp": 100
}
]
},
{
"name": "Morlin Weapons",
"type": "gear",
"resources": [
{
"name": "+1 speed",
"rp": 15
},
{
"name": "+1 VA",
"rp": 15
},
{
"name": "+1 Power",
"rp": 30
}
]
}
]

@ -10,7 +10,7 @@
"desc": "The Deceptive trait acts as a call-on for Sleight of Hand and Falsehood. However this trait so infuses the liar, he has a hard time telling the truth. Deceptive also incurs a +1 Ob penalty to Oratory, Command tests and a +2 Ob to Ugly Truth or Litany of Fools."
},
"Femme Fatale/Homme Fatale": {
"cost": 2,
"cost": 1,
"type": "call_on",
"desc": "Call-on for Seduction."
},
@ -55,4 +55,4 @@
"type": "die",
"desc": "If the Dark Elf character is defeated in a Duel of Wits or a Fight and lives to tell the tale, they may FoRK their Spite into all rolls bent on avenging themselves against their enemy. Humiliating or murdering the enemy–getting revenge upon them–immediately earns the player a bonus persona point, for a total of two: one for a personal goal and one for Vengeful. The Vengeful trait also requires that the Dark Elf have a close relationship with a non-spiteful Elf or a mortal human. It can be familial, romantic or fraternal in nature–but it must be close. It must rankle the Dark Elf that they still live that way."
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1,155 +1,152 @@
{
"stock": "dwarf",
"resources": [
{
"name": "Shoddy Arms",
"rp": 5,
"type": "gear"
},
{
"name": "Dwarven Arms",
"rp": 20,
"type": "gear"
},
{
"name": "Shoddy Crossbow",
"rp": 6,
"type": "gear"
},
{
"name": "Dwarven Arbalest",
"rp": 20,
"type": "gear"
},
{
"name": "Dwarven-made Light Mail",
"rp": 9,
"type": "gear"
},
{
"name": "Dwarven-made Heavy Mail",
"rp": 10,
"type": "gear"
},
{
"name": "Dwarven-made Plated Mail",
"rp": 20,
"type": "gear"
},
{
"name": "Dwarven Mail",
"rp": 100,
"type": "gear"
},
{
"name": "Forge Mask",
"rp": 40,
"type": "gear"
},
{
"name": "Dwarven Shield",
"rp": 20,
"type": "gear"
},
{
"name": "Riding Mount Or Pack Animal",
"rp": 8,
"type": "gear"
},
{
"name": "Clothes",
"rp": 1,
"type": "gear"
},
{
"name": "Traveling Gear",
"rp": 1,
"type": "gear"
},
{
"name": "Sturdy Shoes",
"rp": 1,
"type": "gear"
},
{
"name": "Finery",
"rp": 5,
"type": "gear"
},
{
"name": "Chronicles",
"rp": 15,
"type": "gear"
},
{
"name": "Keg O' Nog",
"rp": 20,
"type": "gear"
},
{
"name": "Small Dwarven House",
"rp": 10,
"type": "property"
},
{
"name": "Large Dwarven House",
"rp": 15,
"type": "property"
},
{
"name": "A Dwarven Hall",
"rp": 30,
"type": "property"
},
{
"name": "A Graybeard's Hold",
"rp": 40,
"type": "property"
},
{
"name": "An Engineer's Hold",
"rp": 45,
"type": "property"
},
{
"name": "A Master Artificer's Hold",
"rp": 60,
"type": "property"
},
{
"name": "A Warden's Hold",
"rp": 75,
"type": "property"
},
{
"name": "A High Captain's Hold",
"rp": 90,
"type": "property"
},
{
"name": "A Prince's Hold",
"rp": 105,
"type": "property"
},
{
"name": "A Workshop",
"rp": 60,
"type": "property"
},
{
"name": "Dwarven Tools",
"rp": 10,
"type": "gear"
},
{
"name": "Shoddy Tools",
"rp": 5,
"type": "gear"
},
{
"name": "Carts And Baggage",
"rp": 15,
"type": "gear"
}
]
}
[
{
"name": "Shoddy Arms",
"rp": 5,
"type": "gear"
},
{
"name": "Dwarven Arms",
"rp": 20,
"type": "gear"
},
{
"name": "Shoddy Crossbow",
"rp": 6,
"type": "gear"
},
{
"name": "Dwarven Arbalest",
"rp": 20,
"type": "gear"
},
{
"name": "Dwarven-made Light Mail",
"rp": 9,
"type": "gear"
},
{
"name": "Dwarven-made Heavy Mail",
"rp": 10,
"type": "gear"
},
{
"name": "Dwarven-made Plated Mail",
"rp": 20,
"type": "gear"
},
{
"name": "Dwarven Mail",
"rp": 100,
"type": "gear"
},
{
"name": "Forge Mask",
"rp": 40,
"type": "gear"
},
{
"name": "Dwarven Shield",
"rp": 20,
"type": "gear"
},
{
"name": "Riding Mount Or Pack Animal",
"rp": 8,
"type": "gear"
},
{
"name": "Clothes",
"rp": 1,
"type": "gear"
},
{
"name": "Traveling Gear",
"rp": 1,
"type": "gear"
},
{
"name": "Sturdy Shoes",
"rp": 1,
"type": "gear"
},
{
"name": "Finery",
"rp": 5,
"type": "gear"
},
{
"name": "Chronicles",
"rp": 15,
"type": "gear"
},
{
"name": "Keg O' Nog",
"rp": 20,
"type": "gear"
},
{
"name": "Small Dwarven House",
"rp": 10,
"type": "property"
},
{
"name": "Large Dwarven House",
"rp": 15,
"type": "property"
},
{
"name": "A Dwarven Hall",
"rp": 30,
"type": "property"
},
{
"name": "A Graybeard's Hold",
"rp": 40,
"type": "property"
},
{
"name": "An Engineer's Hold",
"rp": 45,
"type": "property"
},
{
"name": "A Master Artificer's Hold",
"rp": 60,
"type": "property"
},
{
"name": "A Warden's Hold",
"rp": 75,
"type": "property"
},
{
"name": "A High Captain's Hold",
"rp": 90,
"type": "property"
},
{
"name": "A Prince's Hold",
"rp": 105,
"type": "property"
},
{
"name": "A Workshop",
"rp": 60,
"type": "property"
},
{
"name": "Dwarven Tools",
"rp": 10,
"type": "gear"
},
{
"name": "Shoddy Tools",
"rp": 5,
"type": "gear"
},
{
"name": "Carts And Baggage",
"rp": 15,
"type": "gear"
}
]

@ -1,179 +1,176 @@
{
"stock": "elf",
"resources": [
{
"name": "Run Of The Mill Bow",
"type": "gear",
"rp": 5
},
{
"name": "Run Of The Mill Arms",
"type": "gear",
"rp": 5
},
{
"name": "Reinforced Leather",
"type": "gear",
"rp": 3
},
{
"name": "Light Mail",
"type": "gear",
"rp": 6
},
{
"name": "Heavy Mail",
"type": "gear",
"rp": 10
},
{
"name": "Plated Mail",
"type": "gear",
"rp": 20
},
{
"name": "Elven Armor",
"type": "gear",
"resources": [
{
"name": "Gambeson",
"rp": 9
},
{
"name": "Reinforced Leather",
"resources": null,
"rp": 20
},
{
"name": "Light Mail",
"resources": null,
"rp": 30
},
{
"name": "Heavy Mail",
"rp": 40
},
{
"name": "Plated Mail",
"rp": 75
}
]
},
{
"name": "Elven Arms",
"type": "gear",
"rp": 15
},
{
"name": "Elven Bow",
"type": "gear",
"rp": 25
},
{
"name": "Elven Cloak",
"type": "gear",
"rp": 30
},
{
"name": "Elven Steed",
"type": "gear",
"rp": 8
},
{
"name": "Elven Clothes",
"type": "gear",
"rp": 2
},
{
"name": "Elven Shoes",
"type": "gear",
"rp": 1
},
{
"name": "Elven Finery",
"type": "gear",
"rp": 5
},
{
"name": "Elven Rope",
"type": "gear",
"rp": 12
},
{
"name": "Elven Bread",
"type": "gear",
"rp": 10
},
{
"name": "Elven Mirrorwine",
"type": "gear",
"rp": 8
},
{
"name": "Starlight",
"type": "gear",
"rp": 50
},
{
"name": "Tome Of Lore",
"type": "gear",
"rp": 20
},
{
"name": "Elven Instrument",
"type": "gear",
"rp": 6
},
{
"name": "Personal Effects",
"type": "gear",
"rp": 1
},
{
"name": "Elven Smithy",
"type": "property",
"rp": 50
},
{
"name": "Artisan's Shop",
"type": "property",
"rp": 60
},
{
"name": "Skill Tools",
"type": "gear",
"rp": 9
},
{
"name": "Elven Ship",
"type": "gear",
"rp": 80
},
{
"name": "Elven Land",
"type": "property",
"resources": [
{
"name": "Pastoral",
"rp": 20
},
{
"name": "Large Country Manor And Land",
"rp": 50
},
{
"name": "Palace",
"rp": 100
},
{
"name": "A Forest, Bay Or Mountain",
"rp": 150
},
{
"name": "Apartment In The Citadel",
"rp": 25
}
]
}
]
}
[
{
"name": "Run Of The Mill Bow",
"type": "gear",
"rp": 5
},
{
"name": "Run Of The Mill Arms",
"type": "gear",
"rp": 5
},
{
"name": "Reinforced Leather",
"type": "gear",
"rp": 3
},
{
"name": "Light Mail",
"type": "gear",
"rp": 6
},
{
"name": "Heavy Mail",
"type": "gear",
"rp": 10
},
{
"name": "Plated Mail",
"type": "gear",
"rp": 20
},
{
"name": "Elven Armor",
"type": "gear",
"resources": [
{
"name": "Gambeson",
"rp": 9
},
{
"name": "Reinforced Leather",
"resources": null,
"rp": 20
},
{
"name": "Light Mail",
"resources": null,
"rp": 30
},
{
"name": "Heavy Mail",
"rp": 40
},
{
"name": "Plated Mail",
"rp": 75
}
]
},
{
"name": "Elven Arms",
"type": "gear",
"rp": 15
},
{
"name": "Elven Bow",
"type": "gear",
"rp": 25
},
{
"name": "Elven Cloak",
"type": "gear",
"rp": 30
},
{
"name": "Elven Steed",
"type": "gear",
"rp": 8
},
{
"name": "Elven Clothes",
"type": "gear",
"rp": 2
},
{
"name": "Elven Shoes",
"type": "gear",
"rp": 1
},
{
"name": "Elven Finery",
"type": "gear",
"rp": 5
},
{
"name": "Elven Rope",
"type": "gear",
"rp": 12
},
{
"name": "Elven Bread",
"type": "gear",
"rp": 10
},
{
"name": "Elven Mirrorwine",
"type": "gear",
"rp": 8
},
{
"name": "Starlight",
"type": "gear",
"rp": 50
},
{
"name": "Tome Of Lore",
"type": "gear",
"rp": 20
},
{
"name": "Elven Instrument",
"type": "gear",
"rp": 6
},
{
"name": "Personal Effects",
"type": "gear",
"rp": 1
},
{
"name": "Elven Smithy",
"type": "property",
"rp": 50
},
{
"name": "Artisan's Shop",
"type": "property",
"rp": 60
},
{
"name": "Skill Tools",
"type": "gear",
"rp": 9
},
{
"name": "Elven Ship",
"type": "gear",
"rp": 80
},
{
"name": "Elven Land",
"type": "property",
"resources": [
{
"name": "Pastoral",
"rp": 20
},
{
"name": "Large Country Manor And Land",
"rp": 50
},
{
"name": "Palace",
"rp": 100
},
{
"name": "A Forest, Bay Or Mountain",
"rp": 150
},
{
"name": "Apartment In The Citadel",
"rp": 25
}
]
}
]

@ -1,437 +1,434 @@
{
"stock": "man",
"resources": [
{
"name": "Arms",
"type": "gear",
"resources": [
{
"name": "Poor Quality",
"rp": 3
},
{
"name": "Run Of The Mill Quality",
"rp": 5
},
{
"name": "Superior Quality",
"rp": 20
}
]
},
{
"name": "Missile Weapons",
"type": "gear",
"resources": [
{
"name": "Throwing Weapons",
"resources": [
{
"name": "Poor Quality",
"rp": 2
},
{
"name": "Run Of The Mill Quality",
"rp": 3
},
{
"name": "Superior Quality",
"rp": 9
}
]
},
{
"name": "Hunting Bow",
"resources": [
{
"name": "Poor Quality",
"rp": 3
},
{
"name": "Run Of The Mill Quality",
"rp": 5
},
{
"name": "Superior Quality",
"rp": 15
}
]
},
{
"name": "Great Bow",
"resources": [
{
"name": "Poor Quality",
"rp": 5
},
{
"name": "Run Of The Mill Quality",
"rp": 10
},
{
"name": "Superior Quality",
"rp": 30
}
]
},
{
"name": "Crossbow",
"resources": [
{
"name": "Poor Quality",
"rp": 4
},
{
"name": "Run Of The Mill Quality",
"rp": 7
},
{
"name": "Superior Quality",
"rp": 21
}
]
},
{
"name": "Heavy Crossbow",
"resources": [
{
"name": "Poor Quality",
"rp": 6
},
{
"name": "Run Of The Mill Quality",
"rp": 12
},
{
"name": "Superior Quality",
"rp": 36
}
]
},
{
"name": "Pistol",
"resources": [
{
"name": "Poor Quality",
"rp": 8
},
{
"name": "Run Of The Mill Quality",
"rp": 15
},
{
"name": "Superior Quality",
"rp": 45
}
]
},
{
"name": "Arquebus",
"resources": [
{
"name": "Poor Quality",
"rp": 10
},
{
"name": "Run Of The Mill Quality",
"rp": 20
},
{
"name": "Superior Quality",
"rp": 60
}
]
}
]
},
{
"name": "Armor",
"type": "gear",
"resources": [
{
"name": "Gambeson",
"resources": [
{
"name": "Poor Quality",
"rp": 2
},
{
"name": "Run Of The Mill Quality",
"rp": 3
},
{
"name": "Superior Quality",
"rp": 12
}
]
},
{
"name": "Reinforced Leather",
"resources": [
{
"name": "Poor Quality",
"rp": 3
},
{
"name": "Run Of The Mill Quality",
"rp": 6
},
{
"name": "Superior Quality",
"rp": 24
}
]
},
{
"name": "Light Mail",
"resources": [
{
"name": "Poor Quality",
"rp": 5
},
{
"name": "Run Of The Mill Quality",
"rp": 10
},
{
"name": "Superior Quality",
"rp": 40
}
]
},
{
"name": "Heavy Mail",
"resources": [
{
"name": "Poor Quality",
"rp": 8
},
{
"name": "Run Of The Mill Quality",
"rp": 15
},
{
"name": "Superior Quality",
"rp": 60
}
]
},
{
"name": "Plated Mail",
"resources": [
{
"name": "Poor Quality",
"rp": 10
},
{
"name": "Run Of The Mill Quality",
"rp": 20
},
{
"name": "Superior Quality",
"rp": 80
}
]
},
{
"name": "Full Plated Mail",
"resources": [
{
"name": "Poor Quality",
"rp": 25
},
{
"name": "Run Of The Mill Quality",
"rp": 50
},
{
"name": "Superior Quality",
"rp": 200
}
]
}
]
},
{
"name": "Riding Mount Or Pack Animal",
"rp": 5,
"type": "gear"
},
{
"name": "Warhorse",
"rp": 12,
"type": "gear"
},
{
"name": "Clothes",
"rp": 1,
"type": "gear"
},
{
"name": "Traveling Gear",
"rp": 1,
"type": "gear"
},
{
"name": "Shoes",
"rp": 1,
"type": "gear"
},
{
"name": "Personal Effects",
"rp": 1,
"type": "gear"
},
{
"name": "Finery",
"rp": 5,
"type": "gear"
},
{
"name": "Cash",
"rp": 6,
"type": "gear"
},
{
"name": "Skill Toolkit",
"rp": 8,
"type": "gear"
},
{
"name": "Workshop",
"rp": 20,
"type": "property"
},
{
"name": "Companion Animal",
"rp": 3,
"type": "gear"
},
{
"name": "Herd Of Animals",
"rp": 20,
"type": "property"
},
{
"name": "Rent",
"rp": 5,
"type": "property"
},
{
"name": "Property",
"type": "property",
"resources": [
{
"name": "A Leaky Shack",
"rp": 1
},
{
"name": "A Small Cottage",
"rp": 3
},
{
"name": "A House",
"rp": 10
},
{
"name": "A \"Cottage Industry\" Like A Weaver",
"rp": 10
},
{
"name": "A Villa Or Farm",
"rp": 15
},
{
"name": "A Small Business",
"rp": 20
},
{
"name": "Moderate-sized Business",
"rp": 30
},
{
"name": "A Manor Or Small Estate",
"rp": 40
},
{
"name": "An Urban Hotel",
"rp": 40
},
{
"name": "A Well-paid Position (Like Mayor)",
"rp": 45
},
{
"name": "A Successful Small Business",
"rp": 60
},
{
"name": "A Large Business",
"rp": 60
},
{
"name": "A Keep",
"rp": 60
},
{
"name": "A Fortress",
"rp": 75
},
{
"name": "A Moderate-sized Estate",
"rp": 75
},
{
"name": "A Castle With Attendant Town",
"rp": 90
},
{
"name": "A Large Estate",
"rp": 90
},
{
"name": "A Palace",
"rp": 105
},
{
"name": "A Government Position In A Prosperous Town",
"rp": 105
}
]
},
{
"name": "Boat",
"type": "property",
"resources": [
{
"name": "A Rowboat Or Skiff",
"rp": 5
},
{
"name": "A Longboat",
"rp": 10
},
{
"name": "A Junk",
"rp": 15
},
{
"name": "A Felucca",
"rp": 30
},
{
"name": "A Carrack",
"rp": 60
},
{
"name": "A Caravel",
"rp": 75
},
{
"name": "Treasure Ship",
"rp": 105
}
]
}
]
}
[
{
"name": "Arms",
"type": "gear",
"resources": [
{
"name": "Poor Quality",
"rp": 3
},
{
"name": "Run Of The Mill Quality",
"rp": 5
},
{
"name": "Superior Quality",
"rp": 20
}
]
},
{
"name": "Missile Weapons",
"type": "gear",
"resources": [
{
"name": "Throwing Weapons",
"resources": [
{
"name": "Poor Quality",
"rp": 2
},
{
"name": "Run Of The Mill Quality",
"rp": 3
},
{
"name": "Superior Quality",
"rp": 9
}
]
},
{
"name": "Hunting Bow",
"resources": [
{
"name": "Poor Quality",
"rp": 3
},
{
"name": "Run Of The Mill Quality",
"rp": 5
},
{
"name": "Superior Quality",
"rp": 15
}
]
},
{
"name": "Great Bow",
"resources": [
{
"name": "Poor Quality",
"rp": 5
},
{
"name": "Run Of The Mill Quality",
"rp": 10
},
{
"name": "Superior Quality",
"rp": 30
}
]
},
{
"name": "Crossbow",
"resources": [
{
"name": "Poor Quality",
"rp": 4
},
{
"name": "Run Of The Mill Quality",
"rp": 7
},
{
"name": "Superior Quality",
"rp": 21
}
]
},
{
"name": "Heavy Crossbow",
"resources": [
{
"name": "Poor Quality",
"rp": 6
},
{
"name": "Run Of The Mill Quality",
"rp": 12
},
{
"name": "Superior Quality",
"rp": 36
}
]
},
{
"name": "Pistol",
"resources": [
{
"name": "Poor Quality",
"rp": 8
},
{
"name": "Run Of The Mill Quality",
"rp": 15
},
{
"name": "Superior Quality",
"rp": 45
}
]
},
{
"name": "Arquebus",
"resources": [
{
"name": "Poor Quality",
"rp": 10
},
{
"name": "Run Of The Mill Quality",
"rp": 20
},
{
"name": "Superior Quality",
"rp": 60
}
]
}
]
},
{
"name": "Armor",
"type": "gear",
"resources": [
{
"name": "Gambeson",
"resources": [
{
"name": "Poor Quality",
"rp": 2
},
{
"name": "Run Of The Mill Quality",
"rp": 3
},
{
"name": "Superior Quality",
"rp": 12
}
]
},
{
"name": "Reinforced Leather",
"resources": [
{
"name": "Poor Quality",
"rp": 3
},
{
"name": "Run Of The Mill Quality",
"rp": 6
},
{
"name": "Superior Quality",
"rp": 24
}
]
},
{
"name": "Light Mail",
"resources": [
{
"name": "Poor Quality",
"rp": 5
},
{
"name": "Run Of The Mill Quality",
"rp": 10
},
{
"name": "Superior Quality",
"rp": 40
}
]
},
{
"name": "Heavy Mail",
"resources": [
{
"name": "Poor Quality",
"rp": 8
},
{
"name": "Run Of The Mill Quality",
"rp": 15
},
{
"name": "Superior Quality",
"rp": 60
}
]
},
{
"name": "Plated Mail",
"resources": [
{
"name": "Poor Quality",
"rp": 10
},
{
"name": "Run Of The Mill Quality",
"rp": 20
},
{
"name": "Superior Quality",
"rp": 80
}
]
},
{
"name": "Full Plated Mail",
"resources": [
{
"name": "Poor Quality",
"rp": 25
},
{
"name": "Run Of The Mill Quality",
"rp": 50
},
{
"name": "Superior Quality",
"rp": 200
}
]
}
]
},
{
"name": "Riding Mount Or Pack Animal",
"rp": 5,
"type": "gear"
},
{
"name": "Warhorse",
"rp": 12,
"type": "gear"
},
{
"name": "Clothes",
"rp": 1,
"type": "gear"
},
{
"name": "Traveling Gear",
"rp": 1,
"type": "gear"
},
{
"name": "Shoes",
"rp": 1,
"type": "gear"
},
{
"name": "Personal Effects",
"rp": 1,
"type": "gear"
},
{
"name": "Finery",
"rp": 5,
"type": "gear"
},
{
"name": "Cash",
"rp": 6,
"type": "gear"
},
{
"name": "Skill Toolkit",
"rp": 8,
"type": "gear"
},
{
"name": "Workshop",
"rp": 20,
"type": "property"
},
{
"name": "Companion Animal",
"rp": 3,
"type": "gear"
},
{
"name": "Herd Of Animals",
"rp": 20,
"type": "property"
},
{
"name": "Rent",
"rp": 5,
"type": "property"
},
{
"name": "Property",
"type": "property",
"resources": [
{
"name": "A Leaky Shack",
"rp": 1
},
{
"name": "A Small Cottage",
"rp": 3
},
{
"name": "A House",
"rp": 10
},
{
"name": "A \"Cottage Industry\" Like A Weaver",
"rp": 10
},
{
"name": "A Villa Or Farm",
"rp": 15
},
{
"name": "A Small Business",
"rp": 20
},
{
"name": "Moderate-sized Business",
"rp": 30
},
{
"name": "A Manor Or Small Estate",
"rp": 40
},
{
"name": "An Urban Hotel",
"rp": 40
},
{
"name": "A Well-paid Position (Like Mayor)",
"rp": 45
},
{
"name": "A Successful Small Business",
"rp": 60
},
{
"name": "A Large Business",
"rp": 60
},
{
"name": "A Keep",
"rp": 60
},
{
"name": "A Fortress",
"rp": 75
},
{
"name": "A Moderate-sized Estate",
"rp": 75
},
{
"name": "A Castle With Attendant Town",
"rp": 90
},
{
"name": "A Large Estate",
"rp": 90
},
{
"name": "A Palace",
"rp": 105
},
{
"name": "A Government Position In A Prosperous Town",
"rp": 105
}
]
},
{
"name": "Boat",
"type": "property",
"resources": [
{
"name": "A Rowboat Or Skiff",
"rp": 5
},
{
"name": "A Longboat",
"rp": 10
},
{
"name": "A Junk",
"rp": 15
},
{
"name": "A Felucca",
"rp": 30
},
{
"name": "A Carrack",
"rp": 60
},
{
"name": "A Caravel",
"rp": 75
},
{
"name": "Treasure Ship",
"rp": 105
}
]
}
]

@ -1,214 +1,211 @@
{
"stock": "orc",
"resources": [
{
"name": "Rags",
"rp": 1,
"type": "gear"
},
{
"name": "Traveling Gear",
"rp": 3,
"type": "gear"
},
{
"name": "Hobnailed Boots",
"rp": 1,
"type": "gear"
},
{
"name": "Orc Arms",
"type": "gear",
"resources": [
{
"name": "Poor Quality",
"rp": 3
},
{
"name": "Run Of The Mill Quality",
"rp": 5
}
]
},
{
"name": "Armor",
"type": "gear",
"resources": [
{
"name": "Hides",
"resources": [
{
"name": "Poor Quality",
"rp": 1
}
]
},
{
"name": "Reinforced Hides",
"resources": [
{
"name": "Poor Quality",
"rp": 3
}
]
},
{
"name": "Reinforced Leather",
"resources": [
{
"name": "Run Of The Mill Quality",
"rp": 8
},
{
"name": "Superior Quality",
"rp": 25
}
]
},
{
"name": "Light Mail",
"resources": [
{
"name": "Poor Quality",
"rp": 5
},
{
"name": "Run Of The Mill Quality",
"rp": 12
}
]
},
{
"name": "Heavy Mail",
"resources": [
{
"name": "Poor Quality",
"rp": 8
},
{
"name": "Run Of The Mill Quality",
"rp": 20
}
]
}
]
},
{
"name": "Missile Weapons",
"type": "gear",
"resources": [
{
"name": "Bow",
"resources": [
{
"name": "Poor Quality",
"rp": 3
},
{
"name": "Run Of The Mill Quality",
"rp": 5
}
]
},
{
"name": "Crossbow",
"resources": [
{
"name": "Poor Quality",
"rp": 4
},
{
"name": "Run Of The Mill Quality",
"rp": 6
}
]
},
{
"name": "Iron-cased Bombs",
"resources": null,
"rp": 10
}
]
},
{
"name": "Black Iron Helmet",
"rp": 5,
"type": "gear"
},
{
"name": "Black Iron Shield",
"rp": 4,
"type": "gear"
},
{
"name": "Riding Mount Or Pack Animal",
"rp": 5,
"type": "gear"
},
{
"name": "Great Wolf Mount",
"rp": 15,
"type": "gear"
},
{
"name": "Whip",
"rp": 2,
"type": "gear"
},
{
"name": "Poison",
"rp": 5,
"type": "gear"
},
{
"name": "Brazen Horn",
"rp": 9,
"type": "gear"
},
{
"name": "Clan Banner",
"rp": 9,
"type": "gear"
},
{
"name": "Skill Tools",
"rp": 9,
"type": "gear"
},
{
"name": "Riding Harness For Wolf",
"rp": 5,
"type": "gear"
},
{
"name": "Servant's Black Robes",
"rp": 1,
"type": "gear"
},
{
"name": "Servant's Leather Apron",
"rp": 1,
"type": "gear"
},
{
"name": "Ceremonial Knives",
"rp": 3,
"type": "gear"
},
{
"name": "Ceremonial Axe Or Sword",
"rp": 7,
"type": "gear"
},
{
"name": "Servant's Tools Of The Trade",
"rp": 7,
"type": "gear"
},
{
"name": "Poisoner's Toolkit",
"rp": 7,
"type": "gear"
}
]
}
[
{
"name": "Rags",
"rp": 1,
"type": "gear"
},
{
"name": "Traveling Gear",
"rp": 3,
"type": "gear"
},
{
"name": "Hobnailed Boots",
"rp": 1,
"type": "gear"
},
{
"name": "Orc Arms",
"type": "gear",
"resources": [
{
"name": "Poor Quality",
"rp": 3
},
{
"name": "Run Of The Mill Quality",
"rp": 5
}
]
},
{
"name": "Armor",
"type": "gear",
"resources": [
{
"name": "Hides",
"resources": [
{
"name": "Poor Quality",
"rp": 1
}
]
},
{
"name": "Reinforced Hides",
"resources": [
{
"name": "Poor Quality",
"rp": 3
}
]
},
{
"name": "Reinforced Leather",
"resources": [
{
"name": "Run Of The Mill Quality",
"rp": 8
},
{
"name": "Superior Quality",
"rp": 25
}
]
},
{
"name": "Light Mail",
"resources": [
{
"name": "Poor Quality",
"rp": 5
},
{
"name": "Run Of The Mill Quality",
"rp": 12
}
]
},
{
"name": "Heavy Mail",
"resources": [
{
"name": "Poor Quality",
"rp": 8
},
{
"name": "Run Of The Mill Quality",
"rp": 20
}
]
}
]
},
{
"name": "Missile Weapons",
"type": "gear",
"resources": [
{
"name": "Bow",
"resources": [
{
"name": "Poor Quality",
"rp": 3
},
{
"name": "Run Of The Mill Quality",
"rp": 5
}
]
},
{
"name": "Crossbow",
"resources": [
{
"name": "Poor Quality",
"rp": 4
},
{
"name": "Run Of The Mill Quality",
"rp": 6
}
]
},
{
"name": "Iron-cased Bombs",
"resources": null,
"rp": 10
}
]
},
{
"name": "Black Iron Helmet",
"rp": 5,
"type": "gear"
},
{
"name": "Black Iron Shield",
"rp": 4,
"type": "gear"
},
{
"name": "Riding Mount Or Pack Animal",
"rp": 5,
"type": "gear"
},
{
"name": "Great Wolf Mount",
"rp": 15,
"type": "gear"
},
{
"name": "Whip",
"rp": 2,
"type": "gear"
},
{
"name": "Poison",
"rp": 5,
"type": "gear"
},
{
"name": "Brazen Horn",
"rp": 9,
"type": "gear"
},
{
"name": "Clan Banner",
"rp": 9,
"type": "gear"
},
{
"name": "Skill Tools",
"rp": 9,
"type": "gear"
},
{
"name": "Riding Harness For Wolf",
"rp": 5,
"type": "gear"
},
{
"name": "Servant's Black Robes",
"rp": 1,
"type": "gear"
},
{
"name": "Servant's Leather Apron",
"rp": 1,
"type": "gear"
},
{
"name": "Ceremonial Knives",
"rp": 3,
"type": "gear"
},
{
"name": "Ceremonial Axe Or Sword",
"rp": 7,
"type": "gear"
},
{
"name": "Servant's Tools Of The Trade",
"rp": 7,
"type": "gear"
},
{
"name": "Poisoner's Toolkit",
"rp": 7,
"type": "gear"
}
]

@ -1,142 +1,139 @@
{
"stock": "roden",
"resources": [
{
"name": "Arms",
"rp": 5,
"type": "gear"
},
{
"name": "Roden Throwing Blades",
"rp": 15,
"type": "gear"
},
{
"name": "Wooden Shield",
"rp": 2,
"type": "gear"
},
{
"name": "Armor",
"type": "gear",
"resources": [
{
"name": "Gambeson",
"rp": 5
},
{
"name": "Reinforced Leather",
"rp": 10
},
{
"name": "Light Mail",
"rp": 15
},
{
"name": "Heavy Mail",
"rp": 20
}
]
},
{
"name": "Property",
"type": "property",
"resources": [
{
"name": "Rat's Nest Property",
"resources": [
{
"name": "Den",
"rp": 7
},
{
"name": "Nest",
"rp": 10
},
{
"name": "Apiary",
"rp": 10
},
{
"name": "Fields",
"rp": 15
}
]
}
]
},
{
"name": "Workshop",
"type": "property",
"rp": 20
},
{
"name": "Animal Herd",
"type": "property",
"rp": 10
},
{
"name": "Clothes",
"type": "gear",
"rp": 1
},
{
"name": "Traveling Gear",
"type": "gear",
"rp": 1
},
{
"name": "Shoes",
"type": "gear",
"rp": 3
},
{
"name": "Tool Kit",
"type": "gear",
"rp": 9
},
{
"name": "Firebombs",
"type": "gear",
"rp": 20
},
{
"name": "Robes Of The Ordained",
"type": "gear",
"rp": 1
},
{
"name": "Honeyed Oatcakes",
"type": "gear",
"rp": 5
},
{
"name": "Dandewine",
"type": "gear",
"rp": 5
},
{
"name": "Blood Blossom",
"type": "gear",
"rp": 5
},
{
"name": "Visionary Cult",
"type": "affiliation",
"resources": [
{
"name": "1d Cult",
"rp": 10
},
{
"name": "2d Cult",
"rp": 25
},
{
"name": "3d Cult",
"rp": 50
}
]
}
]
}
[
{
"name": "Arms",
"rp": 5,
"type": "gear"
},
{
"name": "Roden Throwing Blades",
"rp": 15,
"type": "gear"
},
{
"name": "Wooden Shield",
"rp": 2,
"type": "gear"
},
{
"name": "Armor",
"type": "gear",
"resources": [
{
"name": "Gambeson",
"rp": 5
},
{
"name": "Reinforced Leather",
"rp": 10
},
{
"name": "Light Mail",
"rp": 15
},
{
"name": "Heavy Mail",
"rp": 20
}
]
},
{
"name": "Property",
"type": "property",
"resources": [
{
"name": "Rat's Nest Property",
"resources": [
{
"name": "Den",
"rp": 7
},
{
"name": "Nest",
"rp": 10
},
{
"name": "Apiary",
"rp": 10
},
{
"name": "Fields",
"rp": 15
}
]
}
]
},
{
"name": "Workshop",
"type": "property",
"rp": 20
},
{
"name": "Animal Herd",
"type": "property",
"rp": 10
},
{
"name": "Clothes",
"type": "gear",
"rp": 1
},
{
"name": "Traveling Gear",
"type": "gear",
"rp": 1
},
{
"name": "Shoes",
"type": "gear",
"rp": 3
},
{
"name": "Tool Kit",
"type": "gear",
"rp": 9
},
{
"name": "Firebombs",
"type": "gear",
"rp": 20
},
{
"name": "Robes Of The Ordained",
"type": "gear",
"rp": 1
},
{
"name": "Honeyed Oatcakes",
"type": "gear",
"rp": 5
},
{
"name": "Dandewine",
"type": "gear",
"rp": 5
},
{
"name": "Blood Blossom",
"type": "gear",
"rp": 5
},
{
"name": "Visionary Cult",
"type": "affiliation",
"resources": [
{
"name": "1d Cult",
"rp": 10
},
{
"name": "2d Cult",
"rp": 25
},
{
"name": "3d Cult",
"rp": 50
}
]
}
]

@ -1,45 +1,42 @@
{
"stock": "wolf",
"resources": [
{
"name": "Territory",
"type": "property",
"resources": [
{
"name": "Barren",
"rp": 5
},
{
"name": "Wild Grounds",
"rp": 10
},
{
"name": "Plentiful Range",
"rp": 15
}
]
},
{
"name": "Armor",
"type": "gear",
"resources": [
{
"name": "Plated Leather Chanfron",
"rp": 3
},
{
"name": "Leather Collar",
"rp": 2
},
{
"name": "Plated Body Armor",
"rp": 6
},
{
"name": "Leather Greaves And Cuissarts",
"rp": 2
}
]
}
]
}
[
{
"name": "Territory",
"type": "property",
"resources": [
{
"name": "Barren",
"rp": 5
},
{
"name": "Wild Grounds",
"rp": 10
},
{
"name": "Plentiful Range",
"rp": 15
}
]
},
{
"name": "Armor",
"type": "gear",
"resources": [
{
"name": "Plated Leather Chanfron",
"rp": 3
},
{
"name": "Leather Collar",
"rp": 2
},
{
"name": "Plated Body Armor",
"rp": 6
},
{
"name": "Leather Greaves And Cuissarts",
"rp": 2
}
]
}
]

@ -0,0 +1,114 @@
[
{
"range": [
1,
20
],
"m": 6,
"p": 13
},
{
"range": [
21,
30
],
"m": 7,
"p": 13
},
{
"range": [
31,
50
],
"m": 7,
"p": 14
},
{
"range": [
51,
76
],
"m": 8,
"p": 15
},
{
"range": [
77,
111
],
"m": 8,
"p": 16
},
{
"range": [
112,
151
],
"m": 9,
"p": 16
},
{
"range": [
152,
199
],
"m": 9,
"p": 17
},
{
"range": [
200,
245
],
"m": 10,
"p": 18
},
{
"range": [
246,
300
],
"m": 11,
"p": 17
},
{
"range": [
301,
345
],
"m": 11,
"p": 16
},
{
"range": [
346,
396
],
"m": 12,
"p": 15
},
{
"range": [
397,
445
],
"m": 11,
"p": 14
},
{
"range": [
446,
525
],
"m": 11,
"p": 13
},
{
"range": [
526,
600
],
"m": 10,
"p": 12
}
]

@ -0,0 +1,146 @@
[
{
"range": [
1,
25
],
"m": 7,
"p": 13
},
{
"range": [
26,
60
],
"m": 8,
"p": 13
},
{
"range": [
61,
100
],
"m": 9,
"p": 14
},
{
"range": [
101,
125
],
"m": 9,
"p": 15
},
{
"range": [
126,
160
],
"m": 10,
"p": 16
},
{
"range": [
161,
225
],
"m": 10,
"p": 17
},
{
"range": [
226,
325
],
"m": 11,
"p": 17
},
{
"range": [
326,
425
],
"m": 12,
"p": 17
},
{
"range": [
426,
525
],
"m": 13,
"p": 18
},
{
"range": [
526,
625
],
"m": 13,
"p": 19
},
{
"range": [
626,
725
],
"m": 14,
"p": 19
},
{
"range": [
726,
825
],
"m": 14,
"p": 20
},
{
"range": [
826,
925
],
"m": 15,
"p": 20
},
{
"range": [
926,
1025
],
"m": 15,
"p": 21
},
{
"range": [
1026,
1125
],
"m": 15,
"p": 22
},
{
"range": [
1126,
1225
],
"m": 15,
"p": 23
},
{
"range": [
1226,
1325
],
"m": 15,
"p": 24
},
{
"range": [
1326,
9999
],
"m": 16,
"p": 24
}
]

@ -0,0 +1,90 @@
[
{
"range": [
1,
10
],
"m": 5,
"p": 10
},
{
"range": [
11,
14
],
"m": 6,
"p": 13
},
{
"range": [
15,
16
],
"m": 6,
"p": 16
},
{
"range": [
17,
25
],
"m": 7,
"p": 16
},
{
"range": [
26,
29
],
"m": 7,
"p": 15
},
{
"range": [
30,
35
],
"m": 7,
"p": 14
},
{
"range": [
36,
40
],
"m": 7,
"p": 13
},
{
"range": [
41,
55
],
"m": 7,
"p": 12
},
{
"range": [
56,
65
],
"m": 7,
"p": 11
},
{
"range": [
66,
79
],
"m": 7,
"p": 10
},
{
"range": [
80,
100
],
"m": 6,
"p": 9
}
]

@ -0,0 +1,98 @@
[
{
"range": [
1,
10
],
"m": 3,
"p": 10
},
{
"range": [
11,
16
],
"m": 4,
"p": 11
},
{
"range": [
17,
22
],
"m": 5,
"p": 12
},
{
"range": [
23,
30
],
"m": 5,
"p": 13
},
{
"range": [
31,
40
],
"m": 6,
"p": 14
},
{
"range": [
41,
50
],
"m": 6,
"p": 15
},
{
"range": [
51,
60
],
"m": 7,
"p": 16
},
{
"range": [
61,
80
],
"m": 7,
"p": 17
},
{
"range": [
81,
99
],
"m": 8,
"p": 17
},
{
"range": [
100,
125
],
"m": 8,
"p": 18
},
{
"range": [
126,
150
],
"m": 9,
"p": 18
},
{
"range": [
151,
9999
],
"m": 9,
"p": 19
}
]

@ -0,0 +1,74 @@
[
{
"range": [
1,
5
],
"m": 6,
"p": 10
},
{
"range": [
6,
9
],
"m": 7,
"p": 13
},
{
"range": [
10,
15
],
"m": 7,
"p": 14
},
{
"range": [
16,
24
],
"m": 8,
"p": 15
},
{
"range": [
25,
30
],
"m": 8,
"p": 14
},
{
"range": [
31,
36
],
"m": 7,
"p": 13
},
{
"range": [
37,
40
],
"m": 7,
"p": 12
},
{
"range": [
41,
45
],
"m": 7,
"p": 11
},
{
"range": [
46,
49
],
"m": 6,
"p": 10
}
]

@ -0,0 +1,58 @@
[
{
"range": [
1,
1.5
],
"m": 6,
"p": 12
},
{
"range": [
2,
3.5
],
"m": 7,
"p": 16
},
{
"range": [
4,
5.5
],
"m": 7,
"p": 17
},
{
"range": [
6,
7.5
],
"m": 7,
"p": 16
},
{
"range": [
8,
9.5
],
"m": 6,
"p": 14
},
{
"range": [
10,
11.5
],
"m": 6,
"p": 12
},
{
"range": [
12,
15.5
],
"m": 5,
"p": 10
}
]

@ -1,130 +0,0 @@
{
"key": "dwarf",
"name": "Dwarf",
"stride": 6,
"adjective": "dwarven",
"common_traits": [
"Accustomed To The Dark",
"Bearded",
"Greed",
"Oathsworn",
"Shaped From Earth And Stone",
"Stout",
"Tough"
],
"starting_stats":
[
{
"range": [
1,
20
],
"m": 6,
"p": 13
},
{
"range": [
21,
30
],
"m": 7,
"p": 13
},
{
"range": [
31,
50
],
"m": 7,
"p": 14
},
{
"range": [
51,
76
],
"m": 8,
"p": 15
},
{
"range": [
77,
111
],
"m": 8,
"p": 16
},
{
"range": [
112,
151
],
"m": 9,
"p": 16
},
{
"range": [
152,
199
],
"m": 9,
"p": 17
},
{
"range": [
200,
245
],
"m": 10,
"p": 18
},
{
"range": [
246,
300
],
"m": 11,
"p": 17
},
{
"range": [
301,
345
],
"m": 11,
"p": 16
},
{
"range": [
346,
396
],
"m": 12,
"p": 15
},
{
"range": [
397,
445
],
"m": 11,
"p": 14
},
{
"range": [
446,
525
],
"m": 11,
"p": 13
},
{
"range": [
526,
600
],
"m": 10,
"p": 12
}
]
}

@ -1,161 +0,0 @@
{
"key": "elf",
"name": "Elf",
"stride": 8,
"adjective": "elven",
"common_traits": [
"Born Under The Silver Stars",
"Essence Of The Earth",
"Fair And Statuesque",
"First Born",
"Grief",
"Keen Sight"
],
"starting_stats":
[
{
"range": [
1,
25
],
"m": 7,
"p": 13
},
{
"range": [
26,
60
],
"m": 8,
"p": 13
},
{
"range": [
61,
100
],
"m": 9,
"p": 14
},
{
"range": [
101,
125
],
"m": 9,
"p": 15
},
{
"range": [
126,
160
],
"m": 10,
"p": 16
},
{
"range": [
161,
225
],
"m": 10,
"p": 17
},
{
"range": [
226,
325
],
"m": 11,
"p": 17
},
{
"range": [
326,
425
],
"m": 12,
"p": 17
},
{
"range": [
426,
525
],
"m": 13,
"p": 18
},
{
"range": [
526,
625
],
"m": 13,
"p": 19
},
{
"range": [
626,
725
],
"m": 14,
"p": 19
},
{
"range": [
726,
825
],
"m": 14,
"p": 20
},
{
"range": [
826,
925
],
"m": 15,
"p": 20
},
{
"range": [
926,
1025
],
"m": 15,
"p": 21
},
{
"range": [
1026,
1125
],
"m": 15,
"p": 22
},
{
"range": [
1126,
1225
],
"m": 15,
"p": 23
},
{
"range": [
1226,
1325
],
"m": 15,
"p": 24
},
{
"range": [
1326,
9999
],
"m": 16,
"p": 24
}
]
}

@ -1,99 +0,0 @@
{
"key": "man",
"name": "Man",
"stride": 7,
"adjective": "mannish",
"common_traits": [
],
"starting_stats":
[
{
"range": [
1,
10
],
"m": 5,
"p": 10
},
{
"range": [
11,
14
],
"m": 6,
"p": 13
},
{
"range": [
15,
16
],
"m": 6,
"p": 16
},
{
"range": [
17,
25
],
"m": 7,
"p": 16
},
{
"range": [
26,
29
],
"m": 7,
"p": 15
},
{
"range": [
30,
35
],
"m": 7,
"p": 14
},
{
"range": [
36,
40
],
"m": 7,
"p": 13
},
{
"range": [
41,
55
],
"m": 7,
"p": 12
},
{
"range": [
56,
65
],
"m": 7,
"p": 11
},
{
"range": [
66,
79
],
"m": 7,
"p": 10
},
{
"range": [
80,
100
],
"m": 6,
"p": 9
}
]
}

@ -1,114 +0,0 @@
{
"key": "orc",
"name": "Orc",
"stride": 7,
"adjective": "orcish",
"common_traits": [
"Cannibal",
"Cold Black Blood",
"Breeder",
"Fanged And Clawed",
"Loathsome And Twisted",
"Lynx-eyed, Like Burning Coals",
"Vile Language"
],
"starting_stats":
[
{
"range": [
1,
10
],
"m": 3,
"p": 10
},
{
"range": [
11,
16
],
"m": 4,
"p": 11
},
{
"range": [
17,
22
],
"m": 5,
"p": 12
},
{
"range": [
23,
30
],
"m": 5,
"p": 13
},
{
"range": [
31,
40
],
"m": 6,
"p": 14
},
{
"range": [
41,
50
],
"m": 6,
"p": 15
},
{
"range": [
51,
60
],
"m": 7,
"p": 16
},
{
"range": [
61,
80
],
"m": 7,
"p": 17
},
{
"range": [
81,
99
],
"m": 8,
"p": 17
},
{
"range": [
100,
125
],
"m": 8,
"p": 18
},
{
"range": [
126,
150
],
"m": 9,
"p": 18
},
{
"range": [
151,
9999
],
"m": 9,
"p": 19
}
]
}

@ -1,90 +0,0 @@
{
"key": "roden",
"name": "Roden",
"stride": 8,
"adjective": "roden",
"common_traits": [
"Aecer's Likeness",
"Coat Of Fur",
"Communal",
"Enlarged Incisors",
"Quick-blooded",
"Tail",
"Large Ears"
],
"starting_stats":
[
{
"range": [
1,
5
],
"m": 6,
"p": 10
},
{
"range": [
6,
9
],
"m": 7,
"p": 13
},
{
"range": [
10,
15
],
"m": 7,
"p": 14
},
{
"range": [
16,
24
],
"m": 8,
"p": 15
},
{
"range": [
25,
30
],
"m": 8,
"p": 14
},
{
"range": [
31,
36
],
"m": 7,
"p": 13
},
{
"range": [
37,
40
],
"m": 7,
"p": 12
},
{
"range": [
41,
45
],
"m": 7,
"p": 11
},
{
"range": [
46,
49
],
"m": 6,
"p": 10
}
]
}

@ -1,75 +0,0 @@
{
"key": "wolf",
"name": "Wolf",
"stride": 11,
"adjective": "wolfish",
"common_traits": [
"Crushing Jaws",
"Deep Fur",
"Great Lupine Form",
"Lupine Intellect",
"Long-legged",
"Wolf's Eyes",
"Wolf's Snout",
"Woodland Ear"
],
"starting_stats":
[
{
"range": [
1,
1.5
],
"m": 6,
"p": 12
},
{
"range": [
2,
3.5
],
"m": 7,
"p": 16
},
{
"range": [
4,
5.5
],
"m": 7,
"p": 17
},
{
"range": [
6,
7.5
],
"m": 7,
"p": 16
},
{
"range": [
8,
9.5
],
"m": 6,
"p": 14
},
{
"range": [
10,
11.5
],
"m": 6,
"p": 12
},
{
"range": [
12,
15.5
],
"m": 5,
"p": 10
}
]
}

@ -632,14 +632,14 @@
],
"desc": "So vile are they, Orcs will not hesitate to slay and eat their companions."
},
"Catalyst": {
"cost": 3,
"type": "die",
"Catamite": {
"cost": 1,
"type": "character",
"restrict": [
"mannish",
"special"
],
"desc": "You are the center of something great and terrible. Earn a persona point each time your actions cause a conflict between two powerful personas or organizations."
"desc": "Catamite has been chosen as the trait name to represent the openly homosexual characters in the Burning Wheel. Honestly it was a pejorative medieval term -- a slur. It is how society would refer to them, not necessarily how they refer to themselves. Any Character may be homosexual via the player's choice, but by taking the Catamite trait the player is acknowledging that his character is open about his orientation. The ramifications of such a decision in a conservative medieval society are grist for great game situations"
},
"Charging Blindly": {
"cost": 0,
@ -4730,4 +4730,4 @@
],
"desc": "The form of this wolf is perfectly suited to stalking in the shadows of the woods. Use this trait as a call-on for Stealthy when stalking the forests and valleys."
}
}
}

File diff suppressed because it is too large Load Diff

@ -1,60 +1,57 @@
{
"stock": "troll",
"resources": [
{
"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
}
]
}
[
{
"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,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
}
]

@ -1,100 +0,0 @@
{
"key": "troll",
"name": "Troll",
"stride": 7,
"adjective": "trollish",
"common_traits": [
"Black Nails",
"Fangs",
"Night Blooded",
"Night Eyed (Troll)",
"Massive Stature (Troll)",
"Stone's Age",
"Tough (Troll)",
"Troll Skin",
"Voracious Carnivore"
],
"starting_stats":
[
{
"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
}
]
}

File diff suppressed because it is too large Load Diff

@ -1,11 +1,7 @@
require 'deep_merge'
require_relative 'data/gold'
require_relative 'data/wizard'
require_relative 'data/dark_elf'
require_relative 'data/troll'
require_relative 'stock'
require_relative 'data/custom'
module Charred
class Data
@ -13,13 +9,11 @@ module Charred
include Charred::Wizard
include Charred::DarkElf
include Charred::Troll
include Charred::Custom
attr :data
def initialize
@data = {}
@data[:stocks] = {}
puts 'loading gold'
load_gold(@data)
@ -33,9 +27,6 @@ module Charred
puts 'loading trolls'
load_troll(@data)
puts 'loading custom stocks'
load_custom(@data)
@data[:traits] = @data[:traits].sort.to_h
@data[:skills] = @data[:skills].sort.to_h
end
@ -62,9 +53,5 @@ module Charred
[]
end
end
def json_get(filename)
JSON.parse(File.read(filename))
end
end
end
end

@ -1,27 +0,0 @@
require 'json'
module Charred
module Custom
def load_custom(data)
Dir.glob("data/custom/**/*") { |file|
if File.file?(file)
case File.extname(file)
when ".skills"
verbose_merge data[:skills], json_get(file)
when ".traits"
verbose_merge data[:traits], json_get(file)
when ".stock"
stock = Stock.new(json_get(file))
data[:stocks].deep_merge!({ stock.key => stock })
when ".lifepaths"
contents = json_get(file)
data[:lifepaths].ko_deep_merge!({ contents["stock"] => contents["settings"]})
when ".resources"
contents = json_get(file)
data[:resources].ko_deep_merge!({ contents["stock"] => contents["resources"]})
end
end
}
end
end
end

@ -12,12 +12,10 @@ module Charred
verbose_merge data[:traits], traits
file = File.read('data/dark_elf/lifepaths.json')
contents = JSON.parse(file)
lifepaths = contents["settings"]
lifepaths = JSON.parse(file)
file = File.read("data/dark_elf/resources.json")
contents = JSON.parse(file)
resources = contents["resources"]
resources = JSON.parse(file)
data[:resources]['elf'] += resources
elf = data[:lifepaths]['elf']
@ -34,4 +32,4 @@ module Charred
data[:lifepaths]['elf'] = elf
end
end
end
end

@ -1,5 +1,4 @@
require 'json'
require_relative '../stock'
module Charred
module Gold
@ -12,30 +11,29 @@ module Charred
lifepaths = {}
resources = {}
stocks = {}
stat_pts = {}
gold_stocks = ['dwarf', 'elf', 'man', 'orc', 'roden', 'wolf']
stocks = ['dwarf', 'elf', 'man', 'orc', 'roden', 'wolf']
gold_stocks.each do |stock|
stocks.each do |stock|
file = File.read("data/gold/lifepaths/#{stock}.json")
contents = JSON.parse(file)
lifepaths[stock] = contents["settings"]
lifepaths[stock] = JSON.parse(file)
file = File.read("data/gold/resources/#{stock}.json")
contents = JSON.parse(file)
resources[stock] = contents["resources"]
resources[stock] = JSON.parse(file)
file = File.read("data/gold/stocks/#{stock}.json")
stocks[stock] = Stock.new(JSON.parse(file))
file = File.read("data/gold/starting_stat_pts/#{stock}.json")
stat_pts[stock] = JSON.parse(file)
end
data.merge!({
:stocks => stocks,
:skills => skills,
:traits => traits,
:lifepaths => lifepaths,
:resources => resources,
:stocks => stocks
:stat_pts => stat_pts
})
end
end
end
end

@ -1,9 +1,10 @@
require 'json'
require_relative '../stock'
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
@ -13,18 +14,16 @@ module Charred
verbose_merge data[:traits], traits
file = File.read('data/troll/lifepaths.json')
contents = JSON.parse(file)
lifepaths = contents["settings"]
lifepaths = JSON.parse(file)
data[:lifepaths]['troll'] = lifepaths
file = File.read("data/troll/resources.json")
contents = JSON.parse(file)
resources = contents["resources"]
resources = JSON.parse(file)
data[:resources]['troll'] = resources
file = File.read("data/troll/stock.json")
stock = JSON.parse(file)
data[:stocks]['troll'] = Stock.new(stock)
file = File.read("data/troll/starting_stat_pts.json")
stats = JSON.parse(file)
data[:stat_pts]['troll'] = stats
end
end
end
end

@ -4,8 +4,7 @@ module Charred
module Wizard
def load_wizard(data)
file = File.read('data/wizard/lifepaths.json')
contents = JSON.parse(file)
wizard_data = contents["settings"]
wizard_data = JSON.parse(file)
file = File.read('data/wizard/skills.json')
wizard_skills = JSON.parse(file)
@ -117,4 +116,4 @@ module Charred
data
end
end
end
end

@ -1,34 +0,0 @@
module Charred
class Stock
@@default_stride = 7
attr :key
attr :name
attr :stride
attr :common_traits
attr :starting_stats
def initialize(h)
@key = h["key"]
@name = h["name"] || @key
@stride = h["stride"] || @@default_stride
@adjective = h["adjective"] || @key+"ish"
@common_traits = h["common_traits"]
@starting_stats = h["starting_stats"]
end
def as_json(options = {})
{
"key" => @key,
"name" => @name,
"stride" => @stride,
"adjective" => @adjective,
"common_traits" => @common_traits,
"starting_stats" => @starting_stats
}
end
def to_json(*a)
as_json.to_json(*a)
end
end
end

@ -0,0 +1,67 @@
a.panel-title:link,.panel-title a:link { text-decoration: none; }
a.panel-title:visited,.panel-title a:visited { text-decoration: none; }
a.panel-title:hover,.panel-title a:hover { text-decoration: underline; }
a.panel-title:active,.panel-title a:active { text-decoration: underline; }
input.editable-name {
color: #333;
display: inline;
width: 12em;
}
input.editable-line {
color: #333;
}
input.editable-num {
color: #333;
display: inline;
width: 3em;
}
input.editable-name.not-editing {
background: #F5F5F5;
font-weight: bold;
}
input.editable-line.not-editing {
background: #F5F5F5;
}
input.editable-num.not-editing {
background: #F5F5F5;
}
.add-skills-traits-container {
margin-top: 1em;
margin-bottom: 1em;
}
.horizontal-input-pair {
display: flex;
align-items: center;
gap: 0.5em;
}
.horizontal-input-pair label {
margin-bottom: 0;
}
.horizontal-input-pair input {
flex-grow: 1;
}
div.skill-even {
}
div.skill-odd {
background: #F5F5F5;
}
tr.trait-even {
background: lightgray;
}
tr.trait-odd {
background: #F5F5F5;
}
textarea.trait-desc {
height: 5em;
width: 98%;
margin: 1em;
}
table.traits{
width: 100%;
}
table.traits input.editable-name {
width: 100%;
}

@ -1,8 +1,8 @@
function loadCurrentCharacterFromStruct($scope, charStruct, burningData, appropriateWeapons){
$scope.name = charStruct.name;
$scope.gender = charStruct.gender;
$scope.stock = charStruct.stock;
$scope.ensureStockLoaded($scope.stock).then(() => {
$scope.name = charStruct.name;
$scope.gender = charStruct.gender;
$scope.stock = charStruct.stock;
// Appropriate weapons must be loaded before calculateLifepathSkills is called.
if(serverSettings.storageType != 'server'){
appropriateWeapons.appropriateWeapons = charStruct.approp_weapons;
@ -15,8 +15,8 @@ function loadCurrentCharacterFromStruct($scope, charStruct, burningData, appropr
var selectedLifepaths = [];
for(var i = 0; i < charStruct.lifepaths.length; i++){
var lp = charStruct.lifepaths[i];
// lp[0] is setting name, lp[1] is lifepath name.
// lp[2] is brutalLifeDOF, lp[3] is brutalLifeTraitName,
// lp[0] is setting name, lp[1] is lifepath name.
// lp[2] is brutalLifeDOF, lp[3] is brutalLifeTraitName,
// lp[4] is lifepath time, if it's variable and the user selected a value
// lp[5] is the replacement skill for 'Weapon Of Choice' if it's present.
// lp[6] is the replacement stat array, if present. This is needed if the
@ -128,19 +128,19 @@ function loadCurrentCharacterFromStruct($scope, charStruct, burningData, appropr
// Load Resources
$scope.gear = {};
for(var i = 0; i < charStruct.gear.length; i++){
for(var i = 0; i < charStruct.gear.length; i++){
var gear = charStruct.gear[i];
$scope.gear[gear.desc] = new DisplayGear(gear.desc, gear.cost);
}
$scope.property = {};
for(var i = 0; i < charStruct.property.length; i++){
for(var i = 0; i < charStruct.property.length; i++){
var property = charStruct.property[i];
$scope.property[property.desc] = new DisplayGear(property.desc, property.cost);
}
$scope.relationships = {};
for(var i = 0; i < charStruct.relationships.length; i++){
for(var i = 0; i < charStruct.relationships.length; i++){
var rel = charStruct.relationships[i];
$scope.relationships[rel.desc] = new DisplayRelationship(
rel.desc,
@ -154,13 +154,13 @@ function loadCurrentCharacterFromStruct($scope, charStruct, burningData, appropr
}
$scope.affiliations = {};
for(var i = 0; i < charStruct.affiliations.length; i++){
for(var i = 0; i < charStruct.affiliations.length; i++){
var affil = charStruct.affiliations[i];
$scope.affiliations[affil.desc] = new DisplayAffiliation(affil.desc, affil.importance);
}
$scope.reputations = {};
for(var i = 0; i < charStruct.reputations.length; i++){
for(var i = 0; i < charStruct.reputations.length; i++){
var rep = charStruct.reputations[i];
$scope.reputations[rep.desc] = new DisplayReputation(rep.desc, rep.importance);
}
@ -173,15 +173,14 @@ function loadCurrentCharacterFromStruct($scope, charStruct, burningData, appropr
$scope.attributeModifierQuestionResults = loadAttributeModifierQuestionResultsFromSave($scope, charStruct.attr_mod_questions);
$scope.brutalLifeWithdrawn = charStruct.brutal_life_withdrawn;
$scope.$digest();
});
}
function convertCurrentCharacterToStruct($scope, appropriateWeapons) {
// To serialize:
// - Serialized version
// - Character name
// - Stock
// - Stock
// - Gender
// - A list Lifepath names, with setting: [setting, lifepath]
// - How many points were spent on which stat
@ -226,10 +225,10 @@ function convertCurrentCharacterToStruct($scope, appropriateWeapons) {
var displayStat = $scope.stats[i];
stats.push({
"name" : displayStat.name,
"shade" : displayStat.shade,
"mentalPoints" : displayStat.mentalPointsSpent,
"physicalPoints" : displayStat.physicalPointsSpent,
"name" : displayStat.name,
"shade" : displayStat.shade,
"mentalPoints" : displayStat.mentalPointsSpent,
"physicalPoints" : displayStat.physicalPointsSpent,
"eitherPoints" : displayStat.eitherPointsSpent
});
}
@ -278,7 +277,7 @@ function convertCurrentCharacterToStruct($scope, appropriateWeapons) {
}
var res = serializeResource( $scope.gear, function(display){
return {
return {
"cost" : display.cost,
"desc" : display.desc
};
@ -286,7 +285,7 @@ function convertCurrentCharacterToStruct($scope, appropriateWeapons) {
chardata.gear = res;
var res = serializeResource( $scope.property, function(display){
return {
return {
"cost" : display.cost,
"desc" : display.desc
};
@ -294,7 +293,7 @@ function convertCurrentCharacterToStruct($scope, appropriateWeapons) {
chardata.property = res;
var res = serializeResource( $scope.relationships, function(display){
return {
return {
"desc" : display.desc,
"importance" : display.importance,
"isImmedFam" : display.isImmedFam,
@ -307,7 +306,7 @@ function convertCurrentCharacterToStruct($scope, appropriateWeapons) {
chardata.relationships = res;
var res = serializeResource( $scope.affiliations, function(display){
return {
return {
"desc" : display.desc,
"importance" : display.importance
};
@ -315,7 +314,7 @@ function convertCurrentCharacterToStruct($scope, appropriateWeapons) {
chardata.affiliations = res;
var res = serializeResource( $scope.reputations, function(display){
return {
return {
"desc" : display.desc,
"importance" : display.importance
};

@ -1,4 +1,4 @@
var DEBUG = false;
/**** Class Settings (Angular Service) ****/
function Settings() {
this.enforceLifepathReqts = true;
@ -9,7 +9,7 @@ function Settings() {
/**** Class AppropriateWeaponsService (Angular Service) ****/
function AppropriateWeaponsService($modal, $http) {
// This class will store a hash which maps lifepath names to a list of
// This class will store a hash which maps lifepath names to a list of
// weapons that are appropriate for that lifepath.
this.appropriateWeapons = {};
@ -167,7 +167,7 @@ function WeaponOfChoiceService($modal, $http) {
}
return has;
}
this.selectWeaponOfChoice = function (displayLp, onSelect){
if( this.hasWeaponOfChoice(displayLp) ){
this.selectWeaponOfChoiceByModal(displayLp.name, function(selected){
@ -218,14 +218,13 @@ function CharacterStorageService($http) {
/* Load character names from server */
this.loadCharacterNames = function(){
fetch("/list_chars/user1")
.then((response) => response.json())
.then((data) => {
$http.get("/list_chars/user1", {'timeout': 3000} ).
success(function(data,status,headers,config){
myself.characterIdAndNames = data;
console.log("Loaded saved character names");
})
.catch((error) => {
console.log("Error: Loading saved character names from server failed: "+error);
}).
error(function(data,status,headers,config){
console.log("Error: Loading saved character names from server failed: HTTP code " + status + ": " + data);
});
}
@ -237,10 +236,6 @@ function CharacterStorageService($http) {
/**** Class BurningDataService (Angular Service) ****/
// This service is used to load the lifepaths, skills, traits, etc. from the server.
function BurningDataService($http) {
// Used to reference the object from within functions and callbacks
var myself = this;
/* JSON Data structure representing lifepaths. The structure is:
stock:
setting_name:
@ -255,101 +250,135 @@ function BurningDataService($http) {
roots: [root1, root2, ...]
skill_name:
roots: [root1, root2, ...]
*/
this.skills = {};
/* JSON data structure representing all available traits */
this.traits = {};
/* JSON data structure representing all available resources (gear/property) */
this.resources = {};
// A hash of StartingStatPoints objects keyed by stock.
this.startingStatPts = {};
/* Loading of stocks, skills, and traits begins on initializing the service
this.dataSetsLoaded = 0;
// Total data sets:
// lifepaths: 7 (man, dwarf, elf, orc, roden, wolf, troll)
// stat points: 7 (man, dwarf, elf, orc, roden, wolf, troll)
// skills
// traits
// resources: 7 (man, dwarf, elf, orc, roden, wolf. troll)
// TOTAL: 23
this.totalDataSets = 23;
this.onAllDatasetsLoaded = null;
this.registerOnAllDatasetsLoaded = function(callback){
if ( this.dataSetsLoaded >= this.totalDataSets ){
callback();
}
this.onAllDatasetsLoaded = callback;
}
/* Load stocks from server */
this.whenStocksLoaded = fetch("/stocks")
.then((response) => response.json())
.then((data) => {
if(DEBUG) {
console.log("Loaded stock data:");
console.log(data);
}
myself.stocks = data;
for (var stock of Object.keys(data)) {
myself.startingStatPts[stock] = new StartingStatPoints(myself.stocks[stock].starting_stats);
}
})
.catch((error) => {
console.log("Error: Getting stocks from server failed: "+error);
});
this.datasetLoaded = function(){
this.dataSetsLoaded += 1;
if ( this.onAllDatasetsLoaded && (this.dataSetsLoaded >= this.totalDataSets) ){
this.onAllDatasetsLoaded();
}
if ( this.dataSetsLoaded > this.totalDataSets){
console.log("Error: the totalDataSets setting in BurningDataService is too low! This will cause wierd errors. Please adjust it");
}
}
var stocks = ["man", "dwarf", "elf", "orc", "roden", "wolf", "troll"];
var myself = this;
/* Load lifepaths from server */
var loadLifepathsForStock = function(stock){
if( ! isValidStock(stock) ){
console.log("Loading lifepaths failed: asked to load lifepaths for invalid stock " + stock);
return
}
$http.get("/lifepaths/" + stock, {'timeout': 3000} ).
success(function(data,status,headers,config){
myself.lifepaths[stock] = data;
myself.datasetLoaded();
console.log("Loaded "+stock+" lifepaths. " + Object.keys(myself.lifepaths).length + " settings");
}).
error(function(data,status,headers,config){
myself.datasetLoaded();
console.log("Error: Getting "+stock+" lifepaths from server failed: HTTP code " + status + ": " + data);
});
}
/* Load starting stat points table from server */
var loadStartingStatPtsForStock = function(stock){
if( ! isValidStock(stock) ){
console.log("Loading starting stat points failed: asked to load pts for invalid stock " + stock);
return
}
$http.get("/starting_stat_pts/" + stock, {'timeout': 3000} ).
success(function(data,status,headers,config){
myself.startingStatPts[stock] = new StartingStatPoints(data);
myself.datasetLoaded();
console.log("Loaded "+stock+" starting stat points. ");
}).
error(function(data,status,headers,config){
myself.datasetLoaded();
console.log("Error: Getting "+stock+" stat points from server failed: HTTP code " + status + ": " + data);
});
}
/* Load starting stat points table from server */
var loadResourcesForStock = function(stock){
if( ! isValidStock(stock) ){
console.log("Loading resources failed: asked to load for invalid stock " + stock);
return
}
$http.get("/resources/" + stock, {'timeout': 3000} ).
success(function(data,status,headers,config){
myself.resources[stock] = data;
myself.datasetLoaded();
console.log("Loaded "+stock+" resources. ");
}).
error(function(data,status,headers,config){
myself.datasetLoaded();
console.log("Error: Getting "+stock+" stat points from server failed: HTTP code " + status + ": " + data);
});
}
for (var i = 0; i < stocks.length; i++) {
loadLifepathsForStock(stocks[i]);
loadStartingStatPtsForStock(stocks[i]);
loadResourcesForStock(stocks[i]);
}
/* Load skills from server */
this.whenSkillsLoaded = fetch("/skills")
.then((response) => response.json())
.then((data) => {
$http.get("/skills", {'timeout': 3000} ).
success(function(data,status,headers,config){
myself.skills = data;
if(DEBUG) {
console.log("Loaded skill data:");
console.log(data);
}
})
.catch((error) => {
console.log("Error: Getting skills from server failed: "+error);
myself.datasetLoaded();
console.log("Loaded skills. ");
}).
error(function(data,status,headers,config){
myself.datasetLoaded();
console.log("Error: Getting skills from server failed: HTTP code " + status + ": " + data);
});
/* Load traits from server */
this.whenTraitsLoaded = fetch("/traits")
.then((response) => response.json())
.then((data) => {
$http.get("/traits", {'timeout': 3000} ).
success(function(data,status,headers,config){
myself.traits = data;
if(DEBUG) {
console.log("Loaded trait data:");
console.log(data);
}
})
.catch((error) => {
console.log("Error: Getting traits from server failed: "+error);
myself.datasetLoaded();
console.log("Loaded traits. ");
}).
error(function(data,status,headers,config){
myself.datasetLoaded();
console.log("Error: Getting traits from server failed: HTTP code " + status + ": " + data);
});
/* Lifepaths and resources defer until their stock is selected */
this.whenLifePathsLoadedForStock = {};
/* Load lifepaths from server */
this.loadLifepathsForStock = function(stock){
return myself.whenLifePathsLoadedForStock[stock] = fetch("/lifepaths/" + stock)
.then((response) => response.json())
.then((data) => {
myself.lifepaths[stock] = data;
if(DEBUG) {
console.log("Loaded "+stock+" lifepaths:");
console.log(data);
}
})
.catch((error) => {
console.log("Error: Getting "+stock+" lifepaths from server failed: "+error);
});
};
/* Load resources from server */
this.whenResourcesLoadedForStock = {};
this.loadResourcesForStock = function(stock){
return myself.whenResourcesLoadedForStock[stock] = fetch("/resources/" + stock)
.then((response) => response.json())
.then((data) => {
myself.resources[stock] = data;
if(DEBUG) {
console.log("Loaded "+stock+" resources:");
console.log(data);
}
})
.catch((error) => {
console.log("Error: Getting "+stock+" resources from server failed: "+error);
});
};
}
/**** End BurningDataService ****/

File diff suppressed because it is too large Load Diff

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

@ -0,0 +1,388 @@
// class TimeLogger {
// constructor() {
// this.logs = [];
// this.unlabelled_id = 0;
// this.start = this.previous = Date.now();
// this.log('start');
// }
// log(label) {
// var now = Date.now();
// var log = {
// 'when': now,
// 'label': label,
// 'elapsed': now - this.previous,
// 'total': now - this.start
// };
// this.logs.push(log);
// this.previous = now;
// return log;
// }
// }
// const timelogger = new TimeLogger;
// console.log(timelogger.logs[0]);
//
// console.log(timelogger.log("post-data"));
// Settings
function StockedSetting(name, charredSettingData) {
this.isSubsetting = false;
if(result = name.match(/(.*) setting/i)) {
this.name = result[1];
} else if(result = name.match(/(.*) subsetting/i)) {
this.name = result[1];
this.isSubsetting = true;
} else {
this.name = name;
}
this.lifepaths = [];
for (let name in charredSettingData) {
this.lifepaths.push(new StockedLifePath(name, charredSettingData[name]));
}
}
StockedSetting.prototype.addLifepath = function() {
this.lifepaths.push(new StockedLifePath(this.newLifepathName, {}));
this.newLifepathName = "";
}
StockedSetting.prototype.removeLifepath = function(index) {
this.lifepaths.splice(index, 1);
}
StockedSetting.prototype.toCharred = function() {
// let name = this.name + (this.isSubsetting ? " Subsetting" : " Setting";
let charred = {};
for(let lp of this.lifepaths) {
charred[lp.name] = lp.toCharred();
}
// common traits
// stride
return charred;
}
function StockedLifePath(name, charredPathData) {
if(charredPathData) {
this.name = name;
this.time = charredPathData.time;
this.res = charredPathData.res;
this.requires = charredPathData.requires;
this.restrict = charredPathData.restrict;
this.stat = new StockedLifePathStats(charredPathData.stat);
this.skills = new StockedLifePathSkills(charredPathData.skills);
this.traits = new StockedLifePathTraits(charredPathData.traits);
} else {
this.stat = new StockedLifePathStats();
this.skills = new StockedLifePathSkills();
this.traits = new StockedLifePathTraits();
}
this.leads_dict = {};
if(charredPathData.leads) {
for(let lead of charredPathData.leads) { this.leads_dict[lead] = true; }
}
this.leads = () => Object.keys(this.leads_dict).filter(l => this.leads_dict[l]);
}
StockedLifePath.prototype.toCharred = function() {
let charred = {};
charred.time = this.time;
charred.res = this.res;
charred.requires = this.requires;
charred.restrict = this.restrict;
charred.stat = this.stat.toCharred();
charred.skills = this.skills.toCharred();
charred.traits = this.traits.toCharred();
charred.leads = this.leads();
return charred;
}
function StockedLifePathStats(charredStatData) {
this.P = 0;
this.M = 0;
this.PM = 0;
if(charredStatData) {
for (let stat of charredStatData) {
if(stat[1].toUpperCase() == 'P') {
this.P += stat[0];
}
else if(stat[1].toUpperCase() == 'M') {
this.M += stat[0];
}
else if(stat[1].toUpperCase() == 'PM' || stat[0].toUpperCase() == 'MP') {
this.PM += stat[0];
}
}
}
}
StockedLifePathStats.prototype.toString = function() {
let strs = [];
if (this.P) { strs.push("+" + this.P + "P"); }
if (this.M) { strs.push("+" + this.M + "M"); }
if (this.PM) { strs.push("+" + this.PM + "P/M"); }
return strs.join(",");
};
StockedLifePathStats.prototype.toCharred = function() {
let charred = [];
if(this.P > 0) charred.push([this.P, "p"]);
if(this.M > 0) charred.push([this.M, "m"]);
if(this.PM > 0) charred.push([this.PM, "pm"]);
return charred;
}
function StockedLifePathSkills(charredLpSkillData) {
this.lpPoints = 0;
this.generalPoints = 0;
this.lpSkills = [];
if (charredLpSkillData) {
for (let skill of charredLpSkillData) {
if(skill.length == 1) {
this.lpPoints = skill[0];
}
if(skill.length >= 2) {
if(skill[1].toLowerCase() == "general") {
this.generalPoints += skill[0];
} else {
this.lpPoints += skill[0];
this.lpSkills = this.lpSkills.concat(skill.slice(1));
}
}
}
}
}
StockedLifePathSkills.prototype.removeSkill = function(index) {
this.lpSkills.splice(index, 1);
}
StockedLifePathSkills.prototype.addSkill = function(skill) {
this.lpSkills.push(skill);
}
StockedLifePathSkills.prototype.toString = function() {
let strs = [];
if (this.lpPoints) { strs.push(StockedUtil.pts(this.lpPoints, this.lpSkills)); }
if (this.generalPoints) { strs.push(StockedUtil.pts(this.generalPoints, ["General"])); }
return strs.join("; ");
};
StockedLifePathSkills.prototype.toCharred = function() {
let charred = [];
if(this.lpSkills.length > 0 || this.lpPoints > 0){
charred.push([this.lpPoints].concat(this.lpSkills));
}
if(this.generalPoints > 0)
charred.push([this.generalPoints, "General"]);
return charred;
}
function StockedLifePathTraits(charredTraitData) {
this.lpTraits = [];
if(charredTraitData) {
this.points = charredTraitData[0];
this.lpTraits = charredTraitData.slice(1);
}
}
StockedLifePathTraits.prototype.removeTrait = function(index) {
this.lpTraits.splice(index, 1);
}
StockedLifePathTraits.prototype.addTrait = function(skill) {
this.lpTraits.push(skill);
}
StockedLifePathTraits.prototype.toString = function() {
return StockedUtil.pts(this.points, this.lpTraits);
}
StockedLifePathTraits.prototype.toCharred = function() {
return [this.points].concat(this.lpTraits);
}
// Skills
function StockedSkill(name, charredSkillData) {
this.name = name;
this.magic = charredSkillData.magic ? true : false;
this.roots = {
Perception: false,
Will: false,
Forte: false,
Power: false,
Agility: false,
Speed: false,
};
if(charredSkillData.roots){
charredSkillData.roots.map(rootName => this.roots[rootName] = true);
}
this.stockSpecific = "TODO";
}
// Traits
function StockedTrait(name, charredTraitData) {
this.name = name;
this.cost = charredTraitData.cost;
this.type = charredTraitData.type;
this.desc = charredTraitData.desc;
// No logic for these yet
this.bonus = charredTraitData.bonus;
this.restrict = charredTraitData.restrict;
}
//Ctrl
var testscope;
function StockedCtrl($scope, $http, burningData) {
$scope.to_id = function(input) { return input.replaceAll(/\W/g, '_'); };
$scope.StockedUtil = StockedUtil;
$scope.TRAIT_TYPES = ["character", "die", "call_on"];
$scope.settings = [];
/* testing */
testscope = $scope;
$scope.general = {
'Name': 'Foo',
'Stride': 7,
'CommonTraits': [],
'selectedTrait': ''
};
// $scope.parseStock = function (stockData){
// let settings = [];
// for (let name in stockData) {
// settings.push(new StockedSetting(name, stockData[name]));
// }
// return settings;
// };
// $scope.parseSkills = function (skillsData){
// let skills = [];
// for (let name in skillsData) {
// skills.push(new StockedSkill(name, skillsData[name]));
// }
// return skills;
// };
// $scope.parseTraits = function (traitsData){
// let traits = [];
// for (let name in traitsData) {
// traits.push(new StockedTrait(name, traitsData[name]));
// }
// return traits;
// };
/* end testing */
/* Input/Output */
/* end Input/Output */
$scope.addSetting = function (){
this.settings.push(new StockedSetting(this.newSettingName, {}));
this.newSettingName = "";
};
$scope.removeSetting = function (index) {
this.settings.splice(index, 1);
};
$scope.addSkill = function (){
this.skills.push(new StockedSkill("New", {}));
};
$scope.removeSkill = function (index) {
this.skills.splice(index, 1);
};
$scope.addTrait = function (){
this.traits.push(new StockedTrait("New", {}));
};
$scope.removeTrait = function (index) {
this.traits.splice(index, 1);
};
$scope.collapseBody = function(data_target, $event) {
$(data_target).collapse("toggle");
if($event) { $event.stopPropagation(); }
};
$scope.editField = function($event, edit) {
$($event.target).toggleClass("not-editing");
};
burningData.registerOnAllDatasetsLoaded(function() {
onLifepathsLoad_Stocked($scope, burningData);
});
$scope.editLeads = function($event) {
let container = $($event.target).closest('.path-leads');
container.find(".path-leads-read").toggle(false);
container.find(".path-leads-write").toggle(true);
};
$scope.readLeads = function($event) {
let container = $($event.target).closest('.path-leads');
container.find(".path-leads-read").toggle(true);
container.find(".path-leads-write").toggle(false);
};
$scope.stocked_loadCharredModel = function() {
file = document.getElementById("stocked_charred_file");
file.files[0].text().then((text) => loadCharredModel(this, JSON.parse(text)));
};
$scope.stocked_downLoadCharredModel = function() {
model = serializeToCharredModel(this);
$http.post("/stocked_download", model).
success(function(data,status,headers,config){
console.log("huzzah, converting stocked model to charred succeeded. File URL: " + data);
var frame = document.getElementById("downloadframe");
if ( frame ){
frame.src = data;
}
}).
error(function(data,status,headers,config){
console.log("boo, converting stocked model to charred failed. File URL: " + data);
$scope.addAlert('tools', "converting stocked model to charred failed: " + data);
});
}
}
// Accepts an object, JSON text should be parsed first
function loadCharredModel($scope, model) {
let settings = [];
for (let name in model) {
settings.push(new StockedSetting(name, model[name]));
}
$scope.settings = settings;
$scope.$apply();
}
function serializeToCharredModel($scope) {
let model = {};
model.settings = {};
model.Name = $scope.general.Name;
$scope.settings.forEach((s) =>
model.settings[s.name + (s.isSubsetting ? " Subsetting" : " Setting")] = s.toCharred())
return JSON.stringify(model);
}
function onLifepathsLoad_Stocked($scope, burningData) {
// $scope.settings = $scope.parseStock(test_data);
// $scope.skills = $scope.parseSkills(test_skills_data);
// $scope.traits = $scope.parseTraits(test_traits_data);
$scope.charredTraits = Object.keys(burningData.traits);
$scope.charredSkills = Object.keys(burningData.skills);
}
var StockedUtil = {
"pluralize": function(num, thing, withE, nospace) {
str = "";
str += num;
if (!nospace) { str += ' '; }
str += thing;
if(num != 1) {
if (withE) { str += 'e'; }
str += 's';
}
return str;
},
"pts": function(num, entries) {
let str = StockedUtil.pluralize(num, "pt") + ": ";
if(Array.isArray(entries) && entries.length > 0) { str += entries.join(", "); }
else { str += "—" }
return str;
}//, "filter$properties": (key, value) => (key.startsWith('$') ? undefined : value)
};

@ -0,0 +1,42 @@
function EditableInputController($scope, $element, $attrs) {
console.log($scope);
console.log($element);
var ctrl = this;
ctrl.isEditing = false;
ctrl.handleModeChange = function() {
if (ctrl.isEditing) {
ctrl.onUpdate({value: ctrl.modelValue});
ctrl.modelValueCopy = ctrl.modelValue;
}
ctrl.isEditing = !ctrl.isEditing;
};
ctrl.reset = function() {
ctrl.modelValue = ctrl.modelValueCopy;
};
ctrl.$onInit = function() {
// Make a copy of the initial value to be able to reset it later
ctrl.modelValueCopy = ctrl.modelValue;
// Set a default inputType
if (!ctrl.inputType) {
ctrl.inputType = 'text';
}
};
}
export function register(module) {
console.log(module);
module.component('editableInput', {
templateUrl: '/stocked/editableInput_partial',
controller: EditableInputController,
bindings: {
modelValue: '<',
inputID: '<',
inputType: '@?',
onUpdate: '&'
}
});
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,23 @@
{
"Foo Setting": {
"Born Foo": {
"time": 1,
"res": 5,
"stat": [
[1, "pm"]
],
"leads": [
"Bar"
],
"key_leads": [
"Bar Setting"
],
"skills": [
[3, "General"]
],
"traits": [1],
"common_traits": []
}
},
"Bar Setting": {}
}

@ -3,6 +3,7 @@
<head>
<link href='/css/bootstrap.min.css' rel='stylesheet' type='text/css'>
<link href='/css/style.css' rel='stylesheet' type='text/css'>
<link href='/css/stocked.css' rel='stylesheet' type='text/css'>
<script src='/js/server_settings.js'></script>
<script src='/js/angular.min.js'></script>
<script src='/js/angular-resource.js'></script>
@ -14,11 +15,12 @@
<script src='/js/burning-service.js'></script>
<script src='/js/burning-modal.js'></script>
<script src='/js/burning-serialize.js'></script>
<script src='/js/stocked.js'></script>
<script src='/js/burning.js'></script>
<title>Charred - The Burning Wheel Gold Character Burner</title>
</head>
<body>
<a href="https://git.obscuritus.ca:3000/danwizard208/charred-gold/" class="github-corner" target="_blank" aria-label="View source on GitTea"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:#151513; color:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
<a href="https://github.com/modality/charred-black" class="github-corner" target="_blank" aria-label="View source on GitHub"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:#151513; color:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
<div class='container' ng-controller='BurningCtrl'>
<nav class='navbar navbar-default' role='navigation'>
<div class='navbar-header'>
@ -34,6 +36,9 @@
<li>
<a target='_blank' href='https://forums.burningwheel.com/t/charred-an-unofficial-online-bwg-character-burner/14299'>Forum Thread</a>
</li>
<li>
<a href='#/stocked'>Stocked</a>
</li>
</ul>
</nav>
<div ng-view=''></div>

@ -58,7 +58,7 @@
in the main view. At the end is a description of some tricks for specific stocks.
</p>
<p>
The source code for this version can be found on Gittea: <a href="https://git.obscuritus.ca:3000/danwizard208/charred-gold" target="_blank">Charred Gold source</a>.
The source code for this version can be found on Github: <a href="https://github.com/modality/charred-black" target="_blank">Charred Black source</a>.
</p>
<h2 id='tools'>Tools</h2>
<p>
@ -235,4 +235,4 @@
</p>
</div>
</div>
</div>
</div>

@ -94,7 +94,14 @@
</strong>
</div>
<div class='col-md-2'>
<select class='form-control' ng-change='onStockChange()' ng-model='stock' ng-options='s.key as s.name for s in stocks'>
<select class='form-control' ng-change='onStockChange()' ng-model='stock'>
<option value='man'>Man</option>
<option value='dwarf'>Dwarf</option>
<option value='elf'>Elf</option>
<option value='orc'>Orc</option>
<option value='roden'>Roden</option>
<option value='wolf'>Great Wolf</option>
<option value='troll'>Troll</option>
</select>
</div>
<div class='col-md-1'>

@ -0,0 +1,377 @@
<div class='container' ng-controller='StockedCtrl'>
<h1 id='intro'>Stocked</h1>
<div class='well'>
Stocked (Stock&middot;ed)
<br>
<br>
<ol>
<li><i>adj.</i> Furnished with more than enough. <i>(ref https://www.vocabulary.com/dictionary/stocked)</i></li>
<li><i>n.</i> A portmanteau of <i>stock</i> and <i>editor</i>.</li>
</ol>
</div>
<div class='panel panel-default'>
<div class='panel-heading' >
<h4 class='panel-title'>
<a href='' ng-click='collapseBody("#collapse_tools")'>▸</a>
Tools
</h4>
</div>
<div class='panel-collapse collapse in' id='collapse_tools'>
<div class='panel-body'>
<div class='alert alert-danger alert-dismissable' ng-repeat="warn in alertsOfType('tools', 'warn')">
<button aria-hidden='true' class='close' ng-click="removeAlert('tools',warn)" type='button'>&times;</button>
{{warn}}
</div>
<div class='alert alert-success alert-dismissable' ng-repeat="warn in alertsOfType('tools', 'succ')">
<button aria-hidden='true' class='close' ng-click="removeAlert('tools',warn)" type='button'>&times;</button>
{{warn}}
</div>
<div class='container'>
<div class='row'>
<div class='col-md-3'>
<input type="file" id="stocked_charred_file"/>
<a href='' ng-click='stocked_loadCharredModel()'>
TODO: Load charred stock model
</a>
</div>
<div class='col-md-3'>
<a href='' ng-click='stocked_downloadCharredModel()'>
TODO: Download charred stock model
</a>
</div>
<div class='col-md-3'>
<a href='' ng-click='stocked_generateMarkdown()'>
TODO: Generate Markdown
</a>
</div>
</div>
</div>
</div>
</div>
<div class='panel-heading'>
<h4 class='panel-title'>
<a href='' ng-click='collapseBody("#collapse_general")'>▸</a>
General
</h4>
</div>
<div class='panel-collapse collapse in' id='collapse_general'>
<div class='panel-body'>
<label for='stock-name'>Name:</label>
<input class='form-control input-lg not-editing editable-name' name='stock-name' id='stock-name'
ng-model="general.Name" ng-click="$event.stopPropagation()"
ng-focus='editField($event, true)' ng-blur='editField($event, false)' />
<label for='stock-stride'>Stride:</label>
<input type="number" class='not-editing editable-num' name='stock-stride' id='stock-stride'
ng-model="general.Stride" ng-click="$event.stopPropagation()"
ng-focus='editField($event, true)' ng-blur='editField($event, false)' />
<br />
<div>
<span>Common traits: {{general.CommonTraits.join(", ")}}</span>
<select class='form-control' ng-model='general.selectedTrait' ng-options='t for t in charredTraits'></select>
</div>
<div>
<a href='' ng-click='general.CommonTraits.push(general.selectedTrait)'>Add trait</a>
</div>
<div>
<a href='' ng-click=''>TODO: Add new trait</a>
</div>
<br />
TODO: other things in General section?
</div>
</div>
<div class='panel-heading'>
<h4 class='panel-title'>
<a href='' ng-click='collapseBody("#collapse_settings")'>▸</a>
Settings
</h4>
</div>
<div class='panel-collapse collapse in' id='collapse_settings'>
<div class='panel-body'>
<span class="note-label">Note:</span>
<span class="note-content">
Settings will have " Setting" (or " Subsetting" for subsettings)
appended to the name in the generated charred model, which is how charred will display them.
</span>
<br />
<a ng-click='collapseBody(".collapse_all_settings", $event)' href=''>
collapse/expand all settings
</a>
<div class='list-group'>
<div ng-repeat="setting in settings" class='list-group-item'>
<div class='panel-heading'>
<a href='' class="panel-title" ng-click='collapseBody("#collapse_" + to_id(setting.name))'>▸</a>
<input class='form-control input-lg not-editing editable-name'
ng-model="setting.name" ng-click="$event.stopPropagation()"
ng-focus='editField($event, true)' ng-blur='editField($event, false)' />
<span class="panel-title" style="font-weight: bold;">
<input type="checkbox" ng-model="setting.isSubsetting" /> Subsetting?
</span>
<a href='' ng-click='removeSetting($index)'>[X]</a>
</div>
<div class='panel-collapse collapse in collapse_all_settings' id='collapse_{{to_id(setting.name)}}'>
<div class='panel-body'>
<div class="panel">
<div class="panel-body">
<span class="note-label">Note:</span>
<span class="note-content">
Prefix a lifepath's name with "Born " to have charred consider it a born lifepath;
i.e. selectable if and only if it is the first lifepath.
</span>
<br />
<a ng-click='collapseBody(".collapse_all_"+to_id(setting.name), $event)' href=''>
collapse/expand all paths in setting
</a>
<div class='container-fluid'>
<div class='row'>
<div class='h4 col-md-3'>Lifepath</div>
<div class='h4 col-md-1'>Time</div>
<div class='h4 col-md-1'>Res</div>
<div class='h4 col-md-3'>Stat</div>
<div class='h4 col-md-4'>Leads</div>
</div>
</div>
</div>
<div ng-repeat="path in setting.lifepaths">
<div class='panel-heading'>
<div class='container-fluid'>
<div class='row'>
<div class="col-md-3">
<a href='' class="panel-title" ng-click='collapseBody("#collapse_" + to_id(path.name))'>▸</a>
<input ng-model="path.name"
class='form-control input-lg not-editing editable-name'
ng-click="$event.stopPropagation()"
ng-focus='editField($event, true)' ng-blur='editField($event, false)' />
<a href='' ng-click='setting.removeLifepath($index)'>[X]</a>
</div>
<div class="col-md-1">
<input type="number" ng-model="path.time"
class='not-editing editable-num'
ng-click="$event.stopPropagation()"
ng-focus='editField($event, true)' ng-blur='editField($event, false)' />
<span>yrs</span>
</div>
<div class="col-md-1">
<input type="number" ng-model="path.res"
class='not-editing editable-num'
ng-click="$event.stopPropagation()"
ng-focus='editField($event, true)' ng-blur='editField($event, false)' />
</div>
<div class="col-md-3">
<label for="{{to_id(path.name)}}_M">M: </label>
<input type="number" ng-model="path.stat.M" id="{{to_id(path.name)}}_M"
class='not-editing editable-num'
ng-click="$event.stopPropagation()"
ng-focus='editField($event, true)' ng-blur='editField($event, false)' />
<label for="{{to_id(path.name)}}_P">P: </label>
<input type="number" ng-model="path.stat.P" id="{{to_id(path.name)}}_M"
class='not-editing editable-num'
ng-click="$event.stopPropagation()"
ng-focus='editField($event, true)' ng-blur='editField($event, false)' />
<label for="{{to_id(path.name)}}_PM">P/M: </label>
<input type="number" ng-model="path.stat.PM" id="{{to_id(path.name)}}_M"
class='not-editing editable-num'
ng-click="$event.stopPropagation()"
ng-focus='editField($event, true)' ng-blur='editField($event, false)' />
</div>
<div class='h5 col-md-4 path-leads'>
<div class="path-leads-read">
<span>Leads: </span><i>{{path.leads().join(", ")}}</i>
<a href='' ng-click='editLeads($event)'>Edit</a>
</div>
<div class="path-leads-write" hidden="hidden">
<div ng-repeat="setting in settings">
<input type="checkbox" id='{{to_id(path.name)}}-to-{{to_id(setting.name)}}'
ng-model='path.leads_dict[setting.name]' value='{{to_id(setting.name)}}' />
<label for='{{to_id(path.name)}}-to-{{to_id(setting.name)}}'>
{{setting.name}}
</label>
</div>
<a href='' ng-click='readLeads($event)'>Done</a>
</div>
</div>
</div>
</div>
</div>
<div class='panel-collapse collapse in collapse_all_{{to_id(setting.name)}}' id='collapse_{{to_id(path.name)}}'>
<div class='panel-body'>
<span ng-if='$first'>TODO: Born/common traits</span>
<div>
<b><i>Skills:</i></b>
<span ng-if='path.skills.lpSkills.length > 0' ng-click='editPoints($event)'>
{{StockedUtil.pluralize(path.skills.lpPoints, "pt")}}:
</span>
<div style="display: inline;" ng-repeat='skill in path.skills.lpSkills track by $index'>
{{skill}}<a href='' ng-click='path.skills.removeSkill($index)'>[X]</a><!--
--><span ng-if='!$last'>,</span></div><!--
--><span ng-if='path.skills.lpSkills.length > 0 && path.skills.generalPoints > 0'>;</span>
<span ng-if='path.skills.generalPoints > 0'>{{StockedUtil.pluralize(path.skills.generalPoints, "pt")}}: General</span>
</div>
<div>
<b><i>Traits:</i></b>
<span ng-if='path.traits.lpTraits.length == 0'>—</span>
<span ng-if='path.traits.lpTraits.length > 0' ng-click='editPoints($event)'>
{{StockedUtil.pluralize(path.traits.points, "pt")}}:
</span>
<div style="display: inline;" ng-repeat='trait in path.traits.lpTraits track by $index'>
{{trait}}<a href='' ng-click='path.traits.removeTrait($index)'>[X]</a><!--
--><span ng-if='!$last'>,</span></div>
</div>
<div ng-if="path.requires" class="horizontal-input-pair">
<label for="{{to_id(path.name)}}-requires"><b><i>Requires: </i></b></label>
<input ng-model="path.requires" id="{{to_id(path.name)}}-requires"
class='form-control not-editing editable-line'
ng-click="$event.stopPropagation()"
ng-focus='editField($event, true)' ng-blur='editField($event, false)' />
</div>
<div ng-if="path.restrict" class="horizontal-input-pair">
<label for="{{to_id(path.name)}}-restrict"><b><i>Restrictions: </i></b></label>
<input ng-model="path.restrict" id="{{to_id(path.name)}}-restrict"
class='form-control not-editing editable-line'
ng-click="$event.stopPropagation()"
ng-focus='editField($event, true)' ng-blur='editField($event, false)' />
</div>
<div class="add-skills-traits-container" class="container">
<div class='row'>
<div class='col-md-3'>
<select class='form-control' ng-model='path.selectedSkill' ng-options='s for s in charredSkills'></select>
</div>
<div class='col-md-1'>
<a href='' ng-click='path.skills.addSkill(path.selectedSkill)'>Add skill</a>
</div>
<div class='col-md-2'>
<a href='' ng-click=''>TODO: Add new skill</a>
</div>
<div class='col-md-3'>
<select class='form-control' ng-model='path.selectedTrait' ng-options='t for t in charredTraits'></select>
</div>
<div class='col-md-1'>
<a href='' ng-click='path.traits.addTrait(path.selectedTrait)'>Add trait</a>
</div>
<div class='col-md-2'>
<a href='' ng-click=''>TODO: Add new trait</a>
</div>
</div>
</div>
<span>WISHLIST: requires expression</span>
</div>
</div>
</div>
<div class='panel panel-default'>
<input class='form-control input-lg not-editing editable-name'
name='new-lifepath-name' id='new-lifepath-name'
ng-model="setting.newLifepathName" ng-click="$event.stopPropagation()"
ng-focus='editField($event, true)' ng-blur='editField($event, false)' />
<a href='' ng-click='setting.addLifepath()'>Add new lifepath</a>
</div>
</div>
</div>
</div>
</div>
<div class='panel panel-default'>
<input class='form-control input-lg not-editing editable-name'
name='new-setting-name' id='new-setting-name'
ng-model="newSettingName" ng-click="$event.stopPropagation()"
ng-focus='editField($event, true)' ng-blur='editField($event, false)' />
<a href='' ng-click='addSetting()'>Add new setting</a>
</div>
</div>
</div>
</div>
<div class='panel-heading'>
<h4 class='panel-title'>
<a href='' ng-click='collapseBody("#collapse_skills")'>▸</a>
Skills
</h4>
</div>
<div class='panel-collapse collapse in' id='collapse_skills'>
<div class='panel-body'>
<div class='container-fluid'>
<div class='row'>
<div class='h4 col-md-3'>Skill</div>
<div class='h4 col-md-1'>Sorcerous?</div>
<div class='h4 col-md-8'>Roots</div>
<%# <div class='h4 col-md-2'>Stock specific?</div> %>
</div>
</div>
<div ng-repeat='skill in skills' class='row' ng-class-even="'skill-even'" ng-class-odd="'skill-odd'">
<div class='h4 col-md-3'>
<input ng-model="skill.name"
class='form-control input-lg not-editing editable-name'
ng-click="$event.stopPropagation()"
ng-focus='editField($event, true)' ng-blur='editField($event, false)' />
</div>
<div class='h4 col-md-1'><input type="checkbox" ng-model='skill.magic'></div>
<div class='h4 col-md-8'>
<div class="skill-roots-write">
<span ng-repeat="(root,is) in skill.roots">
<input type="checkbox" id='{{to_id(skill.name)-to_id(root)}}' ng-model='is'/>
<label for='{{to_id(skill.name)-to_id(root)}}'> {{root}} </label>
</div>
</div>
</div>
</div>
<div class='row'>
<div class='h4 col-md-12'><a href='' ng-click='addSkill()'>Add new skill</a></div>
</div>
</div>
</div>
<div class='panel-heading'>
<h4 class='panel-title'>
<a href='' ng-click='collapseBody("#collapse_traits")'>▸</a>
Traits
</h4>
</div>
<div class='panel-collapse collapse in' id='collapse_traits'>
<div class='panel-body'>
<table class="traits">
<tr>
<th>Trait</th>
<th>Cost</th>
<th>Type</th>
<%# <div class='h4 col-md-2'>Bonus</div> %>
<%# <div class='h4 col-md-2'>Restrictions</div> %>
</tr>
<%# </div> %>
<tr ng-repeat-start='trait in traits' ng-class-even="'trait-even'" ng-class-odd="'trait-odd'">
<td>
<input ng-model="trait.name"
class='form-control input-lg not-editing editable-name'
ng-click="$event.stopPropagation()"
ng-focus='editField($event, true)' ng-blur='editField($event, false)' />
</td>
<td>
<input type="number" class='not-editing editable-num'
ng-model="trait.cost" ng-click="$event.stopPropagation()"
ng-focus='editField($event, true)' ng-blur='editField($event, false)' />
</td>
<td>
<select class='form-control' ng-model='trait.type' ng-options='t for t in TRAIT_TYPES'></select>
</td>
<td><a href='' ng-click='removeTrait($index)'>[X]</a></td>
</tr>
<%# <div class='h4 col-md-2'>WISHLIST</div> %>
<%# <div class='h4 col-md-2'>WISHLIST</div> %>
<tr ng-repeat-end ng-class-even="'trait-even'" ng-class-odd="'trait-odd'">
<td colspan="99"><textarea class='trait-desc' ng-model='trait.desc'></textarea></td>
</tr>
</table>
<div class='row'>
<div class='h4 col-md-12'><a href='' ng-click='addTrait()'>Add new trait</a></div>
</div>
</div>
</div>
</div>

@ -0,0 +1,6 @@
<span ng-if='$ctrl.label' for='{{$ctrl.inputID}}'>{{$ctrl.label}}</span>
<span ng-switch='$ctrl.isEditing'>
<input ng-switch-when="true" ng-model='$ctrl.modelValue' ng-blur='$ctrl.handleModeChange()'
id='{{$ctrl.inputID}}' type='{{$ctrl.inputType}}' class="editable-input editing">
<span ng-switch-default class="editable-input">{{$ctrl.modelValue}}</span>
</span>

@ -1 +0,0 @@
{"serialize_version":1,"name":"Algoric the Apologist","gender":"male","stock":"man","lifepaths":[["Peasant Setting","Born Peasant",0,null,null,null,[]],["College of Magic Setting","Supplicant",0,null,null,null,[]],["College of Magic Setting","Junior Student",0,null,null,null,[[1,"p"]]],["Outcast Subsetting","Rogue Wizard",0,null,null,null,[[1,"p"],[1,"m"]]],["Servitude And Captive Setting","Captive Of War",0,null,null,null,[]],["City Dweller Setting","Criminal",0,null,null,null,[[1,"pm"]]],["City Dweller Setting","Temple Acolyte",0,null,null,null,[[1,"m"]]],["Religious Subsetting","Priest",0,null,null,null,[[1,"m"]]]],"stats":[{"name":"Will","shade":"B","mentalPoints":5,"physicalPoints":0,"eitherPoints":1},{"name":"Perception","shade":"B","mentalPoints":5,"physicalPoints":0,"eitherPoints":0},{"name":"Power","shade":"B","mentalPoints":0,"physicalPoints":3,"eitherPoints":0},{"name":"Forte","shade":"B","mentalPoints":0,"physicalPoints":4,"eitherPoints":0},{"name":"Agility","shade":"B","mentalPoints":0,"physicalPoints":4,"eitherPoints":0},{"name":"Speed","shade":"B","mentalPoints":0,"physicalPoints":3,"eitherPoints":0}],"skills":{"lifepath":[{"name":"Animal Husbandry","lifepathPoints":1,"generalPoints":0},{"name":"Firebuilding","lifepathPoints":1,"generalPoints":0},{"name":"Read","lifepathPoints":1,"generalPoints":0},{"name":"Write","lifepathPoints":1,"generalPoints":0},{"name":"Ancient History","lifepathPoints":1,"generalPoints":0},{"name":"Circination","lifepathPoints":2,"generalPoints":0},{"name":"Illuminations","lifepathPoints":1,"generalPoints":0},{"name":"Astrology","lifepathPoints":2,"generalPoints":0},{"name":"Symbology","lifepathPoints":1,"generalPoints":0},{"name":"Sorcery","lifepathPoints":5,"generalPoints":0},{"name":"Inconspicuous","lifepathPoints":1,"generalPoints":0},{"name":"Graveyard-wise","lifepathPoints":1,"generalPoints":0},{"name":"Bloodletting","lifepathPoints":1,"generalPoints":0},{"name":"Ugly Truth","lifepathPoints":1,"generalPoints":0},{"name":"Apocalypse-wise","lifepathPoints":1,"generalPoints":0},{"name":"Enchanting","lifepathPoints":2,"generalPoints":0},{"name":"Alchemy","lifepathPoints":1,"generalPoints":0},{"name":"Cell-wise","lifepathPoints":1,"generalPoints":0},{"name":"Chain-wise","lifepathPoints":1,"generalPoints":0},{"name":"Streetwise","lifepathPoints":1,"generalPoints":0},{"name":"Intimidation","lifepathPoints":1,"generalPoints":0},{"name":"Knives","lifepathPoints":1,"generalPoints":0},{"name":"Climbing","lifepathPoints":1,"generalPoints":0},{"name":"Doctrine","lifepathPoints":1,"generalPoints":0},{"name":"Bureaucracy","lifepathPoints":1,"generalPoints":0},{"name":"Temple-wise","lifepathPoints":1,"generalPoints":0},{"name":"Oratory","lifepathPoints":1,"generalPoints":0},{"name":"Suasion","lifepathPoints":2,"generalPoints":0},{"name":"Ritual","lifepathPoints":1,"generalPoints":0},{"name":"Religious History","lifepathPoints":1,"generalPoints":0}],"general":[{"name":"Whip-wise","lifepathPoints":0,"generalPoints":1},{"name":"Herbalism","lifepathPoints":0,"generalPoints":2}]},"traits":[{"name":"Gifted"},{"name":"Second Sight"},{"name":"Fey Blood"},{"name":"Aura Of Fear"},{"name":"Obscure Aura"},{"name":"Poker Face"},{"name":"Faithful"}],"gear":[{"cost":1,"desc":"Clothes"},{"cost":1,"desc":"Traveling Gear"},{"cost":1,"desc":"Personal Effects"},{"cost":5,"desc":"Riding Mount Or Pack Animal"},{"cost":10,"desc":"Armor, Light Mail, Run Of The Mill Quality"},{"cost":14,"desc":"Spells"}],"property":[{"cost":10,"desc":"Property, A House"}],"relationships":[],"affiliations":[{"desc":"The Scurrilous Foes","importance":"small"}],"reputations":[{"desc":"An Escaped Prisoner","importance":"regional"}],"attr_mod_questions":{"Health":[{"question":"Was the character severely wounded in the past?","answer":true},{"question":"Is the character athletic and active?","answer":true}],"Steel":[{"question":"Has the character ever been severely wounded?","answer":true},{"question":"Has the character ever murdered or killed with his own hand more than once?","answer":true},{"question":"Has the character been raised in a competitive (but non-violent) culture - sports, debate, strategy games, courting?","answer":true}],"Faith":[{"question":"Is it only through God that you best serve your allies?","answer":false}]},"brutal_life_withdrawn":false,"approp_weapons":{}}

@ -1 +0,0 @@
{"name":"Algoric the Apologist","age":41,"stock":"man","lifepaths":["Born Peasant","Supplicant","Junior Student","Rogue Wizard","Captive Of War","Criminal","Temple Acolyte","Priest"],"stats":{"will":["B",6],"perception":["B",5],"power":["B",3],"forte":["B",4],"agility":["B",4],"speed":["B",3]},"attributes":{"mortal wound":["B",9],"reflexes":["B",4],"health":["B",5],"steel":["B",6],"hesitation":["",4],"stride":["",7],"circles":["B",3],"resources":["B",3],"faith":["B",3]},"skills":[["Animal Husbandry","B",3,false],["Firebuilding","B",2,false],["Read","B",2,false],["Write","B",2,false],["Ancient History","B",2,false],["Circination","B",2,false],["Illuminations","B",2,false],["Astrology","B",3,false],["Symbology","B",2,false],["Sorcery","B",5,false],["Inconspicuous","B",3,false],["Graveyard-wise","B",2,null],["Bloodletting","B",2,false],["Ugly Truth","B",2,false],["Apocalypse-wise","B",2,null],["Enchanting","B",2,false],["Alchemy","B",2,false],["Cell-wise","B",2,null],["Chain-wise","B",2,null],["Streetwise","B",2,false],["Intimidation","B",3,false],["Knives","B",2,false],["Climbing","B",1,false],["Doctrine","B",2,false],["Bureaucracy","B",3,false],["Temple-wise","B",2,null],["Oratory","B",3,false],["Suasion","B",4,false],["Ritual","B",2,false],["Religious History","B",2,false],["Whip-wise","B",2,false],["Herbalism","B",3,false]],"traits":[["Gifted","die"],["Second Sight","die"],["Fey Blood","die"],["Aura Of Fear","die"],["Obscure Aura","die"],["Poker Face","call_on"],["Faithful","die"],["Broken In","die"],["Hazed","die"],["Spooky","character"],["Claustrophobic","die"],["Cynical","character"],["Believer","die"],["Vested","die"]],"gear":["Clothes","Traveling Gear","Personal Effects","Riding Mount Or Pack Animal","Armor, Light Mail, Run Of The Mill Quality","Spells"],"property":["Property, A House"],"relationships":[],"reputations":["An Escaped Prisoner 2D"],"affiliations":["The Scurrilous Foes 1D"],"ptgs":{"su":3,"li":5,"mi":6,"se":7,"tr":8,"mo":9},"attr_mod_questions":{"Health":[{"question":"Does the character live in squalor and filth?","math_label":"(-1 Health)","modifier":-1},{"question":"Is the character frail or sickly?","math_label":"(-1 Health)","modifier":-1},{"question":"Was the character severely wounded in the past?","math_label":"(-1 Health)","modifier":-1,"answer":true},{"question":"Has the character been tortured and enslaved?","math_label":"(-1 Health)","modifier":-1},{"question":"Is the character athletic and active?","math_label":"(+1 Health)","modifier":1,"answer":true},{"question":"Does the character live in a really clean and happy place, like the hills in the Sound of Music?","math_label":"(+1 Health)","modifier":1}],"Steel":[{"question":"Has the character ever been severely wounded?","math_label":"(+1 Steel if combat lifepath taken/-1 Steel if not)","computeModifier":true,"answer":true},{"question":"Has the character ever murdered or killed with his own hand more than once?","math_label":"(+1 Steel)","modifier":1,"answer":true},{"question":"Has the character been tortured, enslaved or beaten terribly over time?","math_label":"(+1 Steel if Will is > 4, -1 Steel if Will < 4, +0 if Will is 4)","computeModifier":true},{"question":"Has the character lead a sheltered life, free of violence and pain?","math_label":"(-1 Steel)","modifier":-1},{"question":"Has the character been raised in a competitive (but non-violent) culture - sports, debate, strategy games, courting?","math_label":"(+1 Steel)","modifier":1,"answer":true},{"question":"Has the character given birth to a child?","math_label":"(+1 Steel)","modifier":1}],"Faith":[{"question":"Is God who you trust the most?","math_label":"(+1 Faith)","modifier":1},{"question":"When in danger, do you consult God for aid?","math_label":"(+1 Faith)","modifier":1},{"question":"Is it only through God that you best serve your allies?","math_label":"(+1 Faith)","modifier":1,"answer":false}]}}

File diff suppressed because one or more lines are too long

@ -1 +0,0 @@
{"name":"Tyastanarphen","age":676,"stock":"elf","lifepaths":["Citadel Born","Wanderer","Song Singer","Bard","Loremaster","Griever","Liar","Deceiver","Recluse"],"stats":{"will":["B",8],"perception":["G",6],"power":["B",6],"forte":["B",5],"agility":["B",5],"speed":["B",5]},"attributes":{"mortal wound":["B",11],"reflexes":["B",6],"health":["B",5],"steel":["B",9],"hesitation":["",2],"stride":["",8],"circles":["B",4],"resources":["B",1],"grief":["G",4],"spite":["B",9]},"skills":[["Elven Script","G",4,false],["Sing","B",4,false],["Song Of Paths And Ways","G",3,false],["Air Of Gates","B",4,false],["Foraging","G",3,false],["Road-wise","G",3,false],["Song Of Songs","B",4,false],["Song Of Soothing","B",4,false],["Lament Of Stars","B",4,false],["Song Of Merriment","B",4,false],["Lament Of Mourning","B",4,false],["Tract Of Enmity","B",4,false],["Oratory","B",6,false],["Conspicuous","B",4,false],["Lyre","B",3,false],["Flute","B",3,false],["Ancient And Obscure History","G",3,false],["Research","G",3,false],["Ballad Of History","G",3,false],["Lyric Of Law","G",3,false],["Canticle Of Years","B",4,false],["Rhyme Of Tongues","B",4,false],["Sorrow Of Truth","B",4,false],["Dark Elf-wise","G",3,null],["Falsehood","B",4,false],["Soothing Platitudes","B",4,false],["Persuasion","B",6,false],["Twisted Tongue","B",4,false],["Sleight Of Hand","B",4,false],["Disguise","B",3,false],["Inconspicuous","B",4,false],["Rhyme Of The Unraveller","B",4,false],["Ancient History","G",3,false],["Dwarf-wise","G",3,false],["Elven Politics-wise","G",3,null],["Cut Of The Quickened Mind","B",4,false],["Antiphon Union Training","B",5,true],["Almanac","G",3,false]],"traits":[["Grim","character"],["Glib","call_on"],["Oikofugic","character"],["Voice In The Crowd","character"],["Spite","character"],["Compulsive Liar","character"],["Deceptive","call_on"],["Vengeful","die"],["Born Under The Silver Stars","character"],["Essence Of The Earth","die"],["Fair And Statuesque","character"],["First Born","die"],["Grief","die"],["Keen Sight","die"]],"gear":["Morlin Armor, Light Mail","Long Knife","Tome Of Lore","Cloak Of Darkness"],"property":["Remote Refuge, Safe House"],"relationships":[],"reputations":[],"affiliations":[],"ptgs":{"su":3,"li":6,"mi":8,"se":9,"tr":10,"mo":11},"attr_mod_questions":{"Health":[{"question":"Does the character live in squalor and filth?","math_label":"(-1 Health)","modifier":-1,"answer":true},{"question":"Is the character frail or sickly?","math_label":"(-1 Health)","modifier":-1},{"question":"Was the character severely wounded in the past?","math_label":"(-1 Health)","modifier":-1,"answer":true},{"question":"Has the character been tortured and enslaved?","math_label":"(-1 Health)","modifier":-1,"answer":true},{"question":"Is the character athletic and active?","math_label":"(+1 Health)","modifier":1,"answer":true},{"question":"Does the character live in a really clean and happy place, like the hills in the Sound of Music?","math_label":"(+1 Health)","modifier":1}],"Steel":[{"question":"Has the character ever been severely wounded?","math_label":"(+1 Steel if combat lifepath taken/-1 Steel if not)","computeModifier":true,"answer":true},{"question":"Has the character ever murdered or killed with his own hand more than once?","math_label":"(+1 Steel)","modifier":1,"answer":true},{"question":"Has the character been tortured, enslaved or beaten terribly over time?","math_label":"(+1 Steel if Will is > 4, -1 Steel if Will < 4, +0 if Will is 4)","computeModifier":true,"answer":true},{"question":"Has the character lead a sheltered life, free of violence and pain?","math_label":"(-1 Steel)","modifier":-1},{"question":"Has the character been raised in a competitive (but non-violent) culture - sports, debate, strategy games, courting?","math_label":"(+1 Steel)","modifier":1,"answer":true},{"question":"Has the character given birth to a child?","math_label":"(+1 Steel)","modifier":1,"answer":true}],"Grief":[{"question":"Does the character's history include tragedy?","math_label":"(+1 Grief)","modifier":1,"answer":true},{"question":"Has the character lived among non-Elven people?","math_label":"(+1 Grief)","modifier":1,"answer":true}],"Spite":[{"question":"Has the character been betrayed by their friends?","math_label":"(+1 Spite)","modifier":1,"answer":true},{"question":"Is the character lovesick or broken hearted?","math_label":"(+1 Spite)","modifier":1,"answer":true},{"question":"Has the character been abandoned by those they held dear?","math_label":"(+1 Spite)","modifier":1,"answer":true},{"question":"Has the character been abused or tortured?","math_label":"(+1 Spite)","modifier":1,"answer":true},{"question":"Does the character still respect or admire someone on the other side?","math_label":"(-1 Spite)","modifier":-1,"answer":true},{"question":"Does the character still love someone on the other side?","math_label":"(-2 Spite)","modifier":-2}]}}

@ -1,133 +0,0 @@
{
"stock": "test",
"settings": {
"Test Setting": {
"Born Test": {
"time": 7,
"res": 5,
"skills": [
[
1,
"General"
]
],
"traits": [
2
],
"leads": [
"Nowhere"
],
"key_leads": [
"Nowhere Setting"
]
},
"A thing": {
"time": 5,
"res": 2,
"stat": [
[
2,
"p"
]
],
"skills": [
[
4,
"Testing",
"Forest-wise",
"Stealthy"
]
],
"traits": [
1
],
"leads": [
"Nowhere"
],
"key_leads": [
"Nowhere Setting"
]
}
},
"Nowhere Setting": {
"Born Nothing": {
"time": 0,
"res": 0,
"skills": [
],
"traits": [
],
"leads": [
],
"key_leads": [
]
},
"Not A Thing": {
"time": 15,
"res": 1,
"stat": [
[
1,
"pm"
]
],
"skills": [
[
2,
"Nothinging",
"Voiding"
]
],
"traits": [
3
],
"leads": [
"Somewhere"
],
"key_leads": [
"Somewhere Subsetting"
]
}
},
"Somewhere Subsetting": {
"Something Special": {
"time": 30,
"res": 21,
"stat": [
[
1,
"m"
],[
1,
"p"
],[
1,
"pm"
]
],
"skills": [
[
7,
"Specializing",
"Soothing",
"History",
"Sorcery",
"Spirit Binding",
"Harming"
]
],
"traits": [
3,
"Stubborn",
"Shy"
],
"leads": [
"Nowhere"
],
"key_leads": [
"Nowhere Setting"
]
}
}
}
}

@ -1,25 +0,0 @@
{
"stock": "test",
"resources": [
{
"name": "Rags",
"type": "gear",
"rp": 1
},
{
"name": "Riches",
"type": "gear",
"rp": 50
},
{
"name": "A firey soul",
"type": "gear",
"rp": 3
},
{
"name": "Chest or Footlocker",
"type": "gear",
"rp": 3
}
]
}

@ -1,39 +0,0 @@
{
"Testing": {
"stock": "testish",
"roots": [
"Perception"
]
},
"Nothinging": {
"stock": "testish",
"roots": [
"Will",
"Agility"
]
},
"Voiding": {
"stock": "testish",
"roots": [
"Speed"
]
},
"Specializing": {
"stock": "testish",
"roots": [
"Forte"
]
},
"Soothing": {
"stock": "testish",
"roots": [
"Power"
]
},
"Harming": {
"stock": "testish",
"roots": [
"Will"
]
}
}

@ -1,95 +0,0 @@
{
"key": "test",
"name": "Test",
"stride": 9,
"adjective": "testish",
"common_traits": [
"Trait 1",
"testy",
"Trait #3",
"Night Eyed (Test)"
],
"starting_stats":
[
{
"range": [
1,
10
],
"m": 1,
"p": 2
},
{
"range": [
11,
20
],
"m": 2,
"p": 4
},
{
"range": [
21,
30
],
"m": 3,
"p": 6
},
{
"range": [
31,
40
],
"m": 4,
"p": 8
},
{
"range": [
41,
50
],
"m": 5,
"p": 10
},
{
"range": [
51,
60
],
"m": 6,
"p": 12
},
{
"range": [
61,
70
],
"m": 7,
"p": 14
},
{
"range": [
71,
80
],
"m": 8,
"p": 16
},
{
"range": [
81,
90
],
"m": 9,
"p": 18
},
{
"range": [
91,
100
],
"m": 10,
"p": 20
}
]
}

@ -1,55 +0,0 @@
{
"Trait 1": {
"cost": 0,
"type": "die",
"restrict": [
"testish",
"common"
],
"desc": ""
},
"testy": {
"cost": 0,
"type": "die",
"restrict": [
"testish",
"common"
],
"desc": ""
},
"Trait #3": {
"cost": 0,
"type": "die",
"restrict": [
"testish",
"common"
],
"desc": ""
},
"Night Eyed (Test)": {
"cost": 1,
"type": "die",
"restrict": [
"testish",
"common"
],
"desc": "The test version"
},
"Shy": {
"cost": 1,
"type": "character",
"restrict": [
"testish",
"lifepath"
]
},
"Special": {
"cost": 3,
"type": "die",
"restrict": [
"testish",
"special"
],
"desc": "Snowflake"
}
}
Loading…
Cancel
Save