Custom stock files and data #11

Merged
danwizard208 merged 50 commits from custom-stocks into main 1 year ago
  1. 10
      CHANGELOG.md
  2. 1
      Gemfile
  3. 2
      Gemfile.lock
  4. 16
      src/app.rb
  5. 1
      src/data/custom/.gitignore
  6. 3
      src/data/dark_elf/lifepaths.json
  7. 5
      src/data/dark_elf/resources.json
  8. 51
      src/data/gold/lifepaths/dwarf.json
  9. 33
      src/data/gold/lifepaths/elf.json
  10. 15
      src/data/gold/lifepaths/man.json
  11. 27
      src/data/gold/lifepaths/orc.json
  12. 27
      src/data/gold/lifepaths/roden.json
  13. 29
      src/data/gold/lifepaths/wolf.json
  14. 5
      src/data/gold/resources/dwarf.json
  15. 5
      src/data/gold/resources/elf.json
  16. 5
      src/data/gold/resources/man.json
  17. 5
      src/data/gold/resources/orc.json
  18. 5
      src/data/gold/resources/roden.json
  19. 5
      src/data/gold/resources/wolf.json
  20. 114
      src/data/gold/starting_stat_pts/dwarf.json
  21. 146
      src/data/gold/starting_stat_pts/elf.json
  22. 90
      src/data/gold/starting_stat_pts/man.json
  23. 98
      src/data/gold/starting_stat_pts/orc.json
  24. 74
      src/data/gold/starting_stat_pts/roden.json
  25. 58
      src/data/gold/starting_stat_pts/wolf.json
  26. 130
      src/data/gold/stocks/dwarf.json
  27. 161
      src/data/gold/stocks/elf.json
  28. 99
      src/data/gold/stocks/man.json
  29. 114
      src/data/gold/stocks/orc.json
  30. 90
      src/data/gold/stocks/roden.json
  31. 75
      src/data/gold/stocks/wolf.json
  32. 45
      src/data/troll/lifepaths.json
  33. 5
      src/data/troll/resources.json
  34. 82
      src/data/troll/starting_stat_pts.json
  35. 100
      src/data/troll/stock.json
  36. 4
      src/data/wizard/lifepaths.json
  37. 13
      src/lib/data.rb
  38. 27
      src/lib/data/custom.rb
  39. 6
      src/lib/data/dark_elf.rb
  40. 20
      src/lib/data/gold.rb
  41. 15
      src/lib/data/troll.rb
  42. 3
      src/lib/data/wizard.rb
  43. 34
      src/lib/stock.rb
  44. 5
      src/public/js/burning-serialize.js
  45. 187
      src/public/js/burning-service.js
  46. 198
      src/public/js/burning.js
  47. 2
      src/public/js/server_settings.js
  48. 9
      src/views/partials/main.erb
  49. 133
      tests/data/custom/test.lifepaths
  50. 25
      tests/data/custom/test.resources
  51. 39
      tests/data/custom/test.skills
  52. 95
      tests/data/custom/test.stock
  53. 55
      tests/data/custom/test.traits

@ -11,6 +11,16 @@ 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

@ -5,6 +5,7 @@ gem 'sinatra'
gem 'sinatra-contrib'
gem 'prawn'
gem 'prawn-templates'
gem 'deep_merge', '~> 1.2', '>= 1.2.1'
group :development do
gem 'rerun'

@ -4,6 +4,7 @@ GEM
Ascii85 (1.1.0)
afm (0.2.2)
daemons (1.4.1)
deep_merge (1.2.2)
eventmachine (1.2.7)
ffi (1.15.5)
hashery (2.1.2)
@ -58,6 +59,7 @@ PLATFORMS
x86_64-linux
DEPENDENCIES
deep_merge (~> 1.2, >= 1.2.1)
prawn
prawn-templates
rerun

@ -49,24 +49,20 @@ get '/traits' do
json DATA[:traits]
end
get '/lifepaths/:stock' do
if DATA[:stocks].include? params['stock']
json DATA[:lifepaths][params['stock']]
else
404
end
get '/stocks' do
json DATA[:stocks]
end
get '/starting_stat_pts/:stock' do
if DATA[:stocks].include? params['stock']
json DATA[:stat_pts][params['stock']]
get '/lifepaths/:stock' do
if DATA[:stocks].keys.include? params['stock']
json DATA[:lifepaths][params['stock']]
else
404
end
end
get '/resources/:stock' do
if DATA[:stocks].include? params['stock']
if DATA[:stocks].keys.include? params['stock']
json DATA[:resources][params['stock']]
else
404

@ -1,4 +1,6 @@
{
"stock": "elf",
"settings": {
"Path Of Spite Subsetting": {
"Griever": {
"time": 3,
@ -294,3 +296,4 @@
}
}
}
}

@ -1,4 +1,6 @@
[
{
"stock": "elf",
"resources": [
{
"name": "Bitter Poison",
"type": "gear",
@ -112,3 +114,4 @@
]
}
]
}

@ -1,4 +1,6 @@
{
"stock": "dwarf",
"settings": {
"Clansman Setting": {
"Born Clansman": {
"time": 20,
@ -15,18 +17,10 @@
"traits": [
1
],
"common_traits": [
"Accustomed To The Dark",
"Bearded",
"Greed",
"Oathsworn",
"Shaped From Earth And Stone",
"Stout",
"Tough"
],
"key_leads": [
"Guilder Setting"
]
],
"born": true
},
"Tender": {
"time": 20,
@ -353,18 +347,10 @@
"traits": [
1
],
"common_traits": [
"Accustomed To The Dark",
"Bearded",
"Greed",
"Oathsworn",
"Shaped From Earth And Stone",
"Stout",
"Tough"
],
"key_leads": [
"Clansman Setting"
]
],
"born": true
},
"Wordbearer": {
"time": 15,
@ -663,18 +649,10 @@
"traits": [
1
],
"common_traits": [
"Accustomed To The Dark",
"Bearded",
"Greed",
"Oathsworn",
"Shaped From Earth And Stone",
"Stout",
"Tough"
],
"key_leads": [
"Clansman Setting"
]
],
"born": true
},
"Ardent": {
"time": 21,
@ -953,19 +931,11 @@
2,
"Dvergar"
],
"common_traits": [
"Accustomed To The Dark",
"Bearded",
"Greed",
"Oathsworn",
"Shaped From Earth And Stone",
"Stout",
"Tough"
],
"key_leads": [
"Guilder Setting",
"Artificer Setting"
]
],
"born": true
},
"Abecedart": {
"time": 20,
@ -1973,3 +1943,4 @@
}
}
}
}

@ -1,4 +1,6 @@
{
"stock": "elf",
"settings": {
"Wilderlands Setting": {
"Born Wilder Elf": {
"time": 20,
@ -20,17 +22,10 @@
"traits": [
1
],
"common_traits": [
"Born Under The Silver Stars",
"Essence Of The Earth",
"Fair And Statuesque",
"First Born",
"Grief",
"Keen Sight"
],
"key_leads": [
"Citadel Setting"
]
],
"born": true
},
"Rider": {
"time": 20,
@ -517,14 +512,6 @@
"traits": [
1
],
"common_traits": [
"Born Under The Silver Stars",
"Essence Of The Earth",
"Fair And Statuesque",
"First Born",
"Grief",
"Keen Sight"
],
"key_leads": [
"Wilderlands Setting"
]
@ -1189,18 +1176,11 @@
"Fea",
"Aman"
],
"common_traits": [
"Born Under The Silver Stars",
"Essence Of The Earth",
"Fair And Statuesque",
"First Born",
"Grief",
"Keen Sight"
],
"key_leads": [
"Wilderlands Setting",
"Citadel Setting"
]
],
"born": true
},
"Student": {
"time": 25,
@ -2046,3 +2026,4 @@
}
}
}
}

@ -1,4 +1,6 @@
{
"stock": "man",
"settings": {
"Peasant Setting": {
"Born Peasant": {
"time": 8,
@ -23,7 +25,8 @@
"Professional Soldier Subsetting",
"Seafaring Setting",
"Religious Subsetting"
]
],
"born": true
},
"Farmer": {
"time": 8,
@ -699,6 +702,7 @@
},
"Villager Setting": {
"Village Born": {
"born": true,
"time": 10,
"res": 4,
"leads": [
@ -2406,6 +2410,7 @@
},
"City Dweller Setting": {
"City Born": {
"born": true,
"time": 12,
"res": 10,
"leads": [
@ -5134,7 +5139,8 @@
"Professional Soldier Subsetting",
"Seafaring Setting",
"Religious Subsetting"
]
],
"born": true
},
"Bastard": {
"time": 6,
@ -7615,7 +7621,8 @@
"key_leads": [
"Professional Soldier Subsetting",
"Outcast Subsetting"
]
],
"born": true
},
"Ditch Digging": {
"time": 4,
@ -9872,6 +9879,7 @@
},
"Seafaring Setting": {
"Son Of A Gun": {
"born": true,
"time": 8,
"res": 3,
"leads": [
@ -11812,3 +11820,4 @@
}
}
}
}

@ -1,4 +1,6 @@
{
"stock": "orc",
"settings": {
"Chattel Setting": {
"Born Chattel": {
"time": 10,
@ -22,19 +24,11 @@
"traits": [
1
],
"common_traits": [
"Cannibal",
"Cold Black Blood",
"Breeder",
"Fanged And Clawed",
"Loathsome And Twisted",
"Lynx-eyed, Like Burning Coals",
"Vile Language"
],
"key_leads": [
"Black Legion Subsetting",
"Servant Of The Dark Blood Subsetting"
]
],
"born": true
},
"Cattle Slave": {
"time": 5,
@ -432,18 +426,10 @@
"Born To Rule Them All",
"Enemy Of The Sun"
],
"common_traits": [
"Cannibal",
"Cold Black Blood",
"Breeder",
"Fanged And Clawed",
"Loathsome And Twisted",
"Lynx-eyed, Like Burning Coals",
"Vile Language"
],
"key_leads": [
"Servant Of The Dark Blood Subsetting"
]
],
"born": true
},
"The Rites": {
"time": 3,
@ -1593,3 +1579,4 @@
}
}
}
}

@ -1,4 +1,6 @@
{
"stock": "roden",
"settings": {
"Field Setting": {
"Born To The Fields": {
"time": 8,
@ -16,18 +18,10 @@
3,
"Vegetarian"
],
"common_traits": [
"Aecer's Likeness",
"Coat Of Fur",
"Communal",
"Enlarged Incisors",
"Quick-blooded",
"Tail",
"Large Ears"
],
"key_leads": [
"Society Subsetting"
]
],
"born": true
},
"Hand": {
"time": 3,
@ -572,18 +566,10 @@
3,
"Tunnel Vision"
],
"common_traits": [
"Aecer's Likeness",
"Coat Of Fur",
"Communal",
"Enlarged Incisors",
"Quick-blooded",
"Tail",
"Large Ears"
],
"key_leads": [
"Society Subsetting"
]
],
"born": true
},
"Pinky": {
"time": 2,
@ -1738,3 +1724,4 @@
}
}
}
}

@ -1,4 +1,6 @@
{
"stock": "wolf",
"settings": {
"Wild Pack Setting": {
"Born To The Pack": {
"time": 1,
@ -19,19 +21,10 @@
"traits": [
2
],
"common_traits": [
"Crushing Jaws",
"Deep Fur",
"Great Lupine Form",
"Lupine Intellect",
"Long-legged",
"Wolf's Eyes",
"Wolf's Snout",
"Woodland Ear"
],
"key_leads": [
"Captive Subsetting"
]
],
"born": true
},
"Yearling": {
"time": 1,
@ -335,19 +328,10 @@
"Vile Language",
"Demented"
],
"common_traits": [
"Crushing Jaws",
"Deep Fur",
"Great Lupine Form",
"Lupine Intellect",
"Long-legged",
"Wolf's Eyes",
"Wolf's Snout",
"Woodland Ear"
],
"key_leads": [
"Captive Subsetting"
]
],
"born": true
},
"Caged And Beaten": {
"time": 0.5,
@ -969,3 +953,4 @@
}
}
}
}

@ -1,4 +1,6 @@
[
{
"stock": "dwarf",
"resources": [
{
"name": "Shoddy Arms",
"rp": 5,
@ -150,3 +152,4 @@
"type": "gear"
}
]
}

@ -1,4 +1,6 @@
[
{
"stock": "elf",
"resources": [
{
"name": "Run Of The Mill Bow",
"type": "gear",
@ -174,3 +176,4 @@
]
}
]
}

@ -1,4 +1,6 @@
[
{
"stock": "man",
"resources": [
{
"name": "Arms",
"type": "gear",
@ -432,3 +434,4 @@
]
}
]
}

@ -1,4 +1,6 @@
[
{
"stock": "orc",
"resources": [
{
"name": "Rags",
"rp": 1,
@ -209,3 +211,4 @@
"type": "gear"
}
]
}

@ -1,4 +1,6 @@
[
{
"stock": "roden",
"resources": [
{
"name": "Arms",
"rp": 5,
@ -137,3 +139,4 @@
]
}
]
}

@ -1,4 +1,6 @@
[
{
"stock": "wolf",
"resources": [
{
"name": "Territory",
"type": "property",
@ -40,3 +42,4 @@
]
}
]
}

@ -1,114 +0,0 @@
[
{
"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,146 +0,0 @@
[
{
"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,90 +0,0 @@
[
{
"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,98 +0,0 @@
[
{
"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,74 +0,0 @@
[
{
"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,58 +0,0 @@
[
{
"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
}
]

@ -0,0 +1,130 @@
{
"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
}
]
}

@ -0,0 +1,161 @@
{
"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
}
]
}

@ -0,0 +1,99 @@
{
"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
}
]
}

@ -0,0 +1,114 @@
{
"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
}
]
}

@ -0,0 +1,90 @@
{
"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
}
]
}

@ -0,0 +1,75 @@
{
"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
}
]
}

@ -1,4 +1,6 @@
{
"stock": "troll",
"settings": {
"Wild Setting": {
"Born Wild": {
"time": 5,
@ -12,23 +14,13 @@
"traits": [
1
],
"common_traits": [
"Black Nails",
"Fangs",
"Night Blooded",
"Night Eyed (Troll)",
"Massive Stature (Troll)",
"Stone's Age",
"Tough (Troll)",
"Troll Skin",
"Voracious Carnivore"
],
"leads": [
"Pit"
],
"key_leads": [
"Pit Setting"
]
],
"born": true
},
"Bogey": {
"time": 7,
@ -253,23 +245,13 @@
"traits": [
2
],
"common_traits": [
"Black Nails",
"Fangs",
"Night Blooded",
"Night Eyed (Troll)",
"Massive Stature (Troll)",
"Stone's Age",
"Tough (Troll)",
"Troll Skin",
"Voracious Carnivore"
],
"leads": [
"Pit"
],
"key_leads": [
"Pit Setting"
]
],
"born": true
},
"Dweller": {
"time": 4,
@ -397,17 +379,6 @@
2,
"Vile Language"
],
"common_traits": [
"Black Nails",
"Fangs",
"Night Blooded",
"Night Eyed (Troll)",
"Massive Stature (Troll)",
"Stone's Age",
"Tough (Troll)",
"Troll Skin",
"Voracious Carnivore"
],
"leads": [
"Pit",
"Cave"
@ -415,7 +386,8 @@
"key_leads": [
"Pit Setting",
"Cavedweller Setting"
]
],
"born": true
},
"Tortured": {
"time": 3,
@ -615,3 +587,4 @@
}
}
}
}

@ -1,4 +1,6 @@
[
{
"stock": "troll",
"resources": [
{
"name": "Rags",
"type": "gear",
@ -55,3 +57,4 @@
"rp": 5
}
]
}

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

@ -0,0 +1,100 @@
{
"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
}
]
}

@ -1,6 +1,9 @@
{
"stock": "man",
"settings": {
"Special Gifted Lifepaths": {
"Gifted Child": {
"born": true,
"time": 9,
"res": 4,
"leads": [
@ -1222,3 +1225,4 @@
}
}
}
}

@ -1,7 +1,11 @@
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
@ -9,11 +13,13 @@ 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)
@ -27,6 +33,9 @@ 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
@ -53,5 +62,9 @@ module Charred
[]
end
end
def json_get(filename)
JSON.parse(File.read(filename))
end
end
end

@ -0,0 +1,27 @@
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,10 +12,12 @@ module Charred
verbose_merge data[:traits], traits
file = File.read('data/dark_elf/lifepaths.json')
lifepaths = JSON.parse(file)
contents = JSON.parse(file)
lifepaths = contents["settings"]
file = File.read("data/dark_elf/resources.json")
resources = JSON.parse(file)
contents = JSON.parse(file)
resources = contents["resources"]
data[:resources]['elf'] += resources
elf = data[:lifepaths]['elf']

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

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

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

@ -0,0 +1,34 @@
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

@ -2,7 +2,7 @@ function loadCurrentCharacterFromStruct($scope, charStruct, burningData, appropr
$scope.name = charStruct.name;
$scope.gender = charStruct.gender;
$scope.stock = charStruct.stock;
$scope.ensureStockLoaded($scope.stock).then(() => {
// Appropriate weapons must be loaded before calculateLifepathSkills is called.
if(serverSettings.storageType != 'server'){
appropriateWeapons.appropriateWeapons = charStruct.approp_weapons;
@ -173,7 +173,8 @@ 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) {

@ -1,4 +1,4 @@
var DEBUG = false;
/**** Class Settings (Angular Service) ****/
function Settings() {
this.enforceLifepathReqts = true;
@ -218,13 +218,14 @@ function CharacterStorageService($http) {
/* Load character names from server */
this.loadCharacterNames = function(){
$http.get("/list_chars/user1", {'timeout': 3000} ).
success(function(data,status,headers,config){
fetch("/list_chars/user1")
.then((response) => response.json())
.then((data) => {
myself.characterIdAndNames = data;
console.log("Loaded saved character names");
}).
error(function(data,status,headers,config){
console.log("Error: Loading saved character names from server failed: HTTP code " + status + ": " + data);
})
.catch((error) => {
console.log("Error: Loading saved character names from server failed: "+error);
});
}
@ -236,6 +237,10 @@ 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:
@ -263,122 +268,88 @@ function BurningDataService($http) {
// A hash of StartingStatPoints objects keyed by stock.
this.startingStatPts = {};
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;
}
/* Loading of stocks, skills, and traits begins on initializing the service
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");
}
/* Load stocks from server */
this.whenStocksLoaded = fetch("/stocks")
.then((response) => response.json())
.then((data) => {
if(DEBUG) {
console.log("Loaded stock data:");
console.log(data);
}
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
myself.stocks = data;
for (var stock of Object.keys(data)) {
myself.startingStatPts[stock] = new StartingStatPoints(myself.stocks[stock].starting_stats);
}
$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);
})
.catch((error) => {
console.log("Error: Getting stocks from server failed: "+error);
});
}
/* 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
/* Load skills from server */
this.whenSkillsLoaded = fetch("/skills")
.then((response) => response.json())
.then((data) => {
myself.skills = data;
if(DEBUG) {
console.log("Loaded skill data:");
console.log(data);
}
$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);
})
.catch((error) => {
console.log("Error: Getting skills from server failed: "+error);
});
}
/* 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
/* Load traits from server */
this.whenTraitsLoaded = fetch("/traits")
.then((response) => response.json())
.then((data) => {
myself.traits = data;
if(DEBUG) {
console.log("Loaded trait data:");
console.log(data);
}
$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);
})
.catch((error) => {
console.log("Error: Getting traits from server failed: "+error);
});
}
for (var i = 0; i < stocks.length; i++) {
loadLifepathsForStock(stocks[i]);
loadStartingStatPtsForStock(stocks[i]);
loadResourcesForStock(stocks[i]);
}
/* Lifepaths and resources defer until their stock is selected */
/* Load skills from server */
$http.get("/skills", {'timeout': 3000} ).
success(function(data,status,headers,config){
myself.skills = data;
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);
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 traits from server */
$http.get("/traits", {'timeout': 3000} ).
success(function(data,status,headers,config){
myself.traits = data;
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);
};
/* 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 ****/

@ -160,7 +160,7 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
}
};
// Setting names for use in the Add Lifepath section
$scope.settingNames = ["Loading..."]
$scope.settingNames = [];
$scope.currentSettingLifepathNames = [];
// The currently selected lifepath
$scope.currentSettingLifepath = "Loading...";
@ -206,12 +206,6 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
// Character name
$scope.name = "";
// Character stock. One of man, dwarf, orc, elf
if ( ! isValidStock(stock) ){
console.log("Invalid stock '"+stock+"' passed to BurningCtrl.initialize. Defaulting to man");
stock = "man";
}
$scope.stock = stock;
// Character id (server side id)
@ -302,7 +296,7 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
}
$scope.initialize("man");
$scope.initialize();
if ( characterStorage.currentCharacter ){
//console.log("Loading current character");
@ -355,7 +349,6 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
return result;
}
$scope.onGenderChange = function(){
if ($scope.name.length == 0) {
$scope.generateName();
@ -364,8 +357,15 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
}
$scope.onStockChange = function(){
var oldName = $scope.name;
if(!$scope.stock) return;
if(!$scope.stockSelected) { // Removes 'select a stock' after the first selection
$scope.stocks.shift();
$scope.stockSelected = true;
}
$scope.ensureStockLoaded($scope.stock).then(() => {
var oldName = $scope.name;
// Make a blank character sheet
$scope.initialize($scope.stock);
@ -374,11 +374,25 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
} else {
$scope.name = oldName;
}
calculateSettingNames($scope, burningData);
calculateCurrentSettingLifepathNames($scope, burningData);
calculateSpecialTraitsForDisplay($scope, burningData);
calculateGearSelectionLists($scope, burningData);
calculatePropertySelectionLists($scope, burningData);
$scope.$digest();
});
}
$scope.ensureStockLoaded = function(stock) {
let loadPromises = [];
if(!burningData.lifepaths[stock]) {
loadPromises.push(burningData.loadLifepathsForStock(stock));
}
if(!burningData.resources[stock]) {
loadPromises.push(burningData.loadResourcesForStock(stock));
}
return Promise.all(loadPromises);
};
$scope.onSettingChange = function(){
@ -403,8 +417,10 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
calculateUnspentSkillPoints($scope);
}
burningData.registerOnAllDatasetsLoaded(function(){
onLifepathsLoad($scope, burningData);
burningData.whenStocksLoaded.then(() => {
$scope.stocks = [{ name: "Select a stock" }, ...Object.values(burningData.stocks)];
$scope.stockSelected = false;
$scope.$digest();
});
$scope.$on('$locationChangeStart', function(event, nextUrl, currentUrl) {
@ -934,7 +950,7 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
for(var key in burningData.skills){
if ( !(key in $scope.lifepathSkills) && !(key in $scope.generalSkills) ){
var displaySkill = burningData.skills[key];
if ( !displaySkill.stock || restrictionStockToValidStock(displaySkill.stock) == $scope.stock ) {
if ( !displaySkill.stock || restrictionStockToValidStock(burningData.stocks, displaySkill.stock) == $scope.stock ) {
result.push(key);
}
}
@ -1029,20 +1045,9 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
return {"shade" : "", "exp" : 10 - $scope.statsByName["Will"].exp() + bonus};
}
else if ( "Stride" == name ){
var stride = 0;
if( $scope.stock == 'dwarf' )
stride = 6;
else if( $scope.stock == 'elf' )
stride = 8;
else if( $scope.stock == 'roden' )
stride = 8;
else if( $scope.stock == 'wolf' )
stride = 11;
else
stride = 7;
// This is a hack: if stock is unselected, use 0 for stride to not throw error; it shouldn't be displayed anyway
var stride = $scope.stock ? burningData.stocks[$scope.stock].stride : 0;
stride += bonus;
return {"shade" : "", "exp" : stride};
}
else if ( "Circles" == name ){
@ -1195,30 +1200,6 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
return list;
}
/*
$scope.specialTraitsForDisplay = function(){
var list = [];
for(var traitName in burningData.traits) {
var trait = burningData.traits[traitName];
if ('restrict' in trait){
if ( trait.restrict.indexOf(validStockToRestrictionStock($scope.stock)) >= 0 &&
(trait.restrict.indexOf("special") >= 0 || trait.restrict.indexOf("character") >= 0) ){
list.push(new DisplayTrait(traitName, burningData.traits));
}
} else {
// No restriction! As long as cost > 0 (cost 0 is for traits with no cost; not purchaseable)
if ( trait.cost > 0 ) {
list.push(new DisplayTrait(traitName, burningData.traits));
}
}
}
return list;
}
*/
$scope.addLifepathTrait = function(traitName){
if ( $scope.unspentTraitPoints < 1 && $scope.enforcePointLimits )
@ -1956,63 +1937,31 @@ function calculateAge($scope){
$scope.age = age;
}
function isSubsetting(setting) { return !Object.values(setting).some((lp) => lp.born); }
function calculateSettingNames($scope, burningData){
var settingNames = null;
let stockSettings = burningData.lifepaths[$scope.stock];
$scope.settingNames = Object.keys(stockSettings);
var lastCurrentSetting = $scope.currentSetting;
if ( ! $scope.enforceLifepathReqts ) {
// Display all settings and subsettings
settingNames = [];
for(key in burningData.lifepaths[$scope.stock]){
settingNames.push(key);
}
}
else if ( $scope.selectedLifepaths.length == 0 ){
if ($scope.enforceLifepathReqts){
if ( $scope.selectedLifepaths.length == 0 ){
// All settings are allowed. Subsettings have no Born lifepath so don't include them.
settingNames = [];
for(key in burningData.lifepaths[$scope.stock]){
if( key.toLowerCase().indexOf("subsetting") < 0 ){
settingNames.push(key);
}
}
$scope.settingNames = $scope.settingNames.filter(s => !isSubsetting(stockSettings[s]));
}
else {
// Only settings that are leads from the last lifepath are allowed
var lastLifepath = $scope.selectedLifepaths[$scope.selectedLifepaths.length-1];
settingNames = [];
var all = Object.keys(burningData.lifepaths[$scope.stock]);
for(var i = 0; i < all.length; i++){
//console.log("calculateSettingNames: checking if '"+all[i]+"' is allowed");
var setting = all[i];
if ( lastLifepath.setting == setting ){
settingNames.push(setting);
continue;
}
if ( lastLifepath.leads ){
for(var j = 0; j < lastLifepath.keyLeads.length; j++){
var lead = lastLifepath.keyLeads[j];
//console.log("calculateSettingNames: checking lead: '"+lead+"' is allowed");
if( setting == lead ){
settingNames.push(setting);
// Doing this filtering instead of [lastLifepath.setting, ...lastLifepath.keyLeads] for two reasons:
// only presents settings present in this server and maintains relative order.
$scope.settingNames = $scope.settingNames.filter(s => s == lastLifepath.setting
|| (Array.isArray(lastLifepath.keyLeads) && lastLifepath.keyLeads.includes(s)));
}
}
}
}
}
$scope.settingNames = settingNames;
var currentSettingNeedsUpdate = true;
for(var i = 0; i < $scope.settingNames.length; i++){
if( $scope.settingNames[i] == lastCurrentSetting){
currentSettingNeedsUpdate = false;
break;
}
}
var currentSettingNeedsUpdate = !$scope.settingNames.includes(lastCurrentSetting);
if ( currentSettingNeedsUpdate && $scope.settingNames.length > 0 ){
$scope.currentSetting = $scope.settingNames[0];
@ -2025,13 +1974,6 @@ function calculatePTGS($scope) {
}
function isBornLifepath(lifepathName) {
return lifepathName.indexOf("Born") >= 0 ||
lifepathName == "Son Of A Gun" ||
lifepathName == "Gifted Child";
}
function calculateCurrentSettingLifepathNames($scope, burningData){
var currentSettingLifepathNames = null;
@ -2043,7 +1985,7 @@ function calculateCurrentSettingLifepathNames($scope, burningData){
if ( $scope.selectedLifepaths.length == 0 ){
// Only "Born" lifepaths are allowed
for(var i = 0; i < all.length; i++){
if ( isBornLifepath(all[i]) ){
if ( burningData.lifepaths[$scope.stock][$scope.currentSetting][all[i]].born ){
currentSettingLifepathNames.push(all[i]);
}
}
@ -2053,7 +1995,7 @@ function calculateCurrentSettingLifepathNames($scope, burningData){
for(var j = 0; j < lifepathNames.length; j++){
var lifepathName = lifepathNames[j];
if ( isBornLifepath(lifepathName) )
if ( burningData.lifepaths[$scope.stock][$scope.currentSetting][lifepathName].born )
continue;
var rexpr = burningData.lifepaths[$scope.stock][$scope.currentSetting][lifepathName].requires_expr
@ -2334,7 +2276,7 @@ function setCommonTraits($scope, burningData){
if( $scope.selectedLifepaths.length == 0 )
return;
var common = $scope.selectedLifepaths[0].commonTraits;
var common = burningData.stocks[$scope.stock].common_traits;
if(common.length > 0){
for(var j = 0; j < common.length; j++){
var name = common[j];
@ -2766,7 +2708,7 @@ function calculateSpecialTraitsForDisplay($scope, burningData){
var trait = burningData.traits[traitName];
if ('restrict' in trait){
if ( trait.restrict.indexOf(validStockToRestrictionStock($scope.stock)) >= 0 &&
if ( trait.restrict.indexOf(validStockToRestrictionStock(burningData.stocks, $scope.stock)) >= 0 &&
(trait.restrict.indexOf("special") >= 0 || trait.restrict.indexOf("character") >= 0) ){
list.push(new DisplayTrait(traitName, burningData.traits));
}
@ -2814,44 +2756,12 @@ function calculateUnspentResourcePoints($scope){
$scope.unspentResourcePoints = unspentResourcePoints;
}
function isValidStock(stock){
return stock == "man" || stock == "elf" || stock == "orc" || stock == "dwarf" || stock == "roden" || stock == "wolf" || stock =="troll";
}
function restrictionStockToValidStock(stock){
if ( stock == "mannish" )
return "man";
else if ( stock == "elven" )
return "elf";
else if ( stock == "orcish" )
return "orc";
else if ( stock == "dwarven" )
return "dwarf";
else if ( stock == "wolfish" )
return "wolf";
else if ( stock == "roden" )
return "roden";
else if ( stock == "trollish" )
return "troll";
}
function validStockToRestrictionStock(stock){
if ( stock == "man" )
return "mannish";
else if ( stock == "elf" )
return "elven";
else if ( stock == "orc" )
return "orcish";
else if ( stock == "dwarf" )
return "dwarven";
else if ( stock == "roden" )
return "roden";
else if ( stock == "wolf" )
return "wolfish";
else if ( stock == "troll" )
return "trollish";
function restrictionStockToValidStock(stocks, stockAdjective){
return Object.values(stocks).findLast(s => s.adjective == stockAdjective).key;
}
function validStockToRestrictionStock(stocks, stockName){
return stocks[stockName].adjective;
}
function attributeModifyingQuestions($scope, attribute)

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

@ -94,14 +94,7 @@
</strong>
</div>
<div class='col-md-2'>
<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 class='form-control' ng-change='onStockChange()' ng-model='stock' ng-options='s.key as s.name for s in stocks'>
</select>
</div>
<div class='col-md-1'>

@ -0,0 +1,133 @@
{
"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"
]
}
}
}
}

@ -0,0 +1,25 @@
{
"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
}
]
}

@ -0,0 +1,39 @@
{
"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"
]
}
}

@ -0,0 +1,95 @@
{
"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
}
]
}

@ -0,0 +1,55 @@
{
"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