Merge pull request 'Custom stock files and data' (#11) from custom-stocks into main

Reviewed-on: #11
pull/17/head
commit 32c91f437d
  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. 7
      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. 7
      src/data/gold/resources/dwarf.json
  15. 7
      src/data/gold/resources/elf.json
  16. 7
      src/data/gold/resources/man.json
  17. 7
      src/data/gold/resources/orc.json
  18. 7
      src/data/gold/resources/roden.json
  19. 7
      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. 7
      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. 194
      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 - Custom upload for your own data files
- Updates to Roden and Great Wolves files (currently the data comes from Monster Burner and not Codex) - Updates to Roden and Great Wolves files (currently the data comes from Monster Burner and not Codex)
## [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 ## [2.3.0] - 2019-07-28
### Added ### Added
- Display emotional attribute traits on PDF - Display emotional attribute traits on PDF

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

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

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

@ -1,4 +1,6 @@
{ {
"stock": "elf",
"settings": {
"Path Of Spite Subsetting": { "Path Of Spite Subsetting": {
"Griever": { "Griever": {
"time": 3, "time": 3,
@ -293,4 +295,5 @@
] ]
} }
} }
}
} }

@ -1,4 +1,6 @@
[ {
"stock": "elf",
"resources": [
{ {
"name": "Bitter Poison", "name": "Bitter Poison",
"type": "gear", "type": "gear",
@ -111,4 +113,5 @@
} }
] ]
} }
] ]
}

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

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

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

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

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

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

@ -1,4 +1,6 @@
[ {
"stock": "dwarf",
"resources": [
{ {
"name": "Shoddy Arms", "name": "Shoddy Arms",
"rp": 5, "rp": 5,
@ -149,4 +151,5 @@
"rp": 15, "rp": 15,
"type": "gear" "type": "gear"
} }
] ]
}

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

@ -1,4 +1,6 @@
[ {
"stock": "man",
"resources": [
{ {
"name": "Arms", "name": "Arms",
"type": "gear", "type": "gear",
@ -431,4 +433,5 @@
} }
] ]
} }
] ]
}

@ -1,4 +1,6 @@
[ {
"stock": "orc",
"resources": [
{ {
"name": "Rags", "name": "Rags",
"rp": 1, "rp": 1,
@ -208,4 +210,5 @@
"rp": 7, "rp": 7,
"type": "gear" "type": "gear"
} }
] ]
}

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

@ -1,4 +1,6 @@
[ {
"stock": "wolf",
"resources": [
{ {
"name": "Territory", "name": "Territory",
"type": "property", "type": "property",
@ -39,4 +41,5 @@
} }
] ]
} }
] ]
}

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

@ -1,4 +1,6 @@
[ {
"stock": "troll",
"resources": [
{ {
"name": "Rags", "name": "Rags",
"type": "gear", "type": "gear",
@ -54,4 +56,5 @@
"type": "property", "type": "property",
"rp": 5 "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": { "Special Gifted Lifepaths": {
"Gifted Child": { "Gifted Child": {
"born": true,
"time": 9, "time": 9,
"res": 4, "res": 4,
"leads": [ "leads": [
@ -1221,4 +1224,5 @@
] ]
} }
} }
}
} }

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

@ -1,4 +1,5 @@
require 'json' require 'json'
require_relative '../stock'
module Charred module Charred
module Gold module Gold
@ -11,28 +12,29 @@ module Charred
lifepaths = {} lifepaths = {}
resources = {} 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") 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") 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") file = File.read("data/gold/stocks/#{stock}.json")
stat_pts[stock] = JSON.parse(file) stocks[stock] = Stock.new(JSON.parse(file))
end end
data.merge!({ data.merge!({
:stocks => stocks,
:skills => skills, :skills => skills,
:traits => traits, :traits => traits,
:lifepaths => lifepaths, :lifepaths => lifepaths,
:resources => resources, :resources => resources,
:stat_pts => stat_pts :stocks => stocks
}) })
end end
end end

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

@ -4,7 +4,8 @@ module Charred
module Wizard module Wizard
def load_wizard(data) def load_wizard(data)
file = File.read('data/wizard/lifepaths.json') 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') file = File.read('data/wizard/skills.json')
wizard_skills = JSON.parse(file) 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.name = charStruct.name;
$scope.gender = charStruct.gender; $scope.gender = charStruct.gender;
$scope.stock = charStruct.stock; $scope.stock = charStruct.stock;
$scope.ensureStockLoaded($scope.stock).then(() => {
// Appropriate weapons must be loaded before calculateLifepathSkills is called. // Appropriate weapons must be loaded before calculateLifepathSkills is called.
if(serverSettings.storageType != 'server'){ if(serverSettings.storageType != 'server'){
appropriateWeapons.appropriateWeapons = charStruct.approp_weapons; appropriateWeapons.appropriateWeapons = charStruct.approp_weapons;
@ -173,7 +173,8 @@ function loadCurrentCharacterFromStruct($scope, charStruct, burningData, appropr
$scope.attributeModifierQuestionResults = loadAttributeModifierQuestionResultsFromSave($scope, charStruct.attr_mod_questions); $scope.attributeModifierQuestionResults = loadAttributeModifierQuestionResultsFromSave($scope, charStruct.attr_mod_questions);
$scope.brutalLifeWithdrawn = charStruct.brutal_life_withdrawn; $scope.brutalLifeWithdrawn = charStruct.brutal_life_withdrawn;
$scope.$digest();
});
} }
function convertCurrentCharacterToStruct($scope, appropriateWeapons) { function convertCurrentCharacterToStruct($scope, appropriateWeapons) {

@ -1,4 +1,4 @@
var DEBUG = false;
/**** Class Settings (Angular Service) ****/ /**** Class Settings (Angular Service) ****/
function Settings() { function Settings() {
this.enforceLifepathReqts = true; this.enforceLifepathReqts = true;
@ -218,13 +218,14 @@ function CharacterStorageService($http) {
/* Load character names from server */ /* Load character names from server */
this.loadCharacterNames = function(){ this.loadCharacterNames = function(){
$http.get("/list_chars/user1", {'timeout': 3000} ). fetch("/list_chars/user1")
success(function(data,status,headers,config){ .then((response) => response.json())
.then((data) => {
myself.characterIdAndNames = data; myself.characterIdAndNames = data;
console.log("Loaded saved character names"); console.log("Loaded saved character names");
}). })
error(function(data,status,headers,config){ .catch((error) => {
console.log("Error: Loading saved character names from server failed: HTTP code " + status + ": " + data); console.log("Error: Loading saved character names from server failed: "+error);
}); });
} }
@ -236,6 +237,10 @@ function CharacterStorageService($http) {
/**** Class BurningDataService (Angular Service) ****/ /**** Class BurningDataService (Angular Service) ****/
// This service is used to load the lifepaths, skills, traits, etc. from the server. // This service is used to load the lifepaths, skills, traits, etc. from the server.
function BurningDataService($http) { function BurningDataService($http) {
// Used to reference the object from within functions and callbacks
var myself = this;
/* JSON Data structure representing lifepaths. The structure is: /* JSON Data structure representing lifepaths. The structure is:
stock: stock:
setting_name: setting_name:
@ -263,122 +268,88 @@ function BurningDataService($http) {
// A hash of StartingStatPoints objects keyed by stock. // A hash of StartingStatPoints objects keyed by stock.
this.startingStatPts = {}; this.startingStatPts = {};
this.dataSetsLoaded = 0; /* Loading of stocks, skills, and traits begins on initializing the service
// 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;
}
this.datasetLoaded = function(){ /* Load stocks from server */
this.dataSetsLoaded += 1; this.whenStocksLoaded = fetch("/stocks")
if ( this.onAllDatasetsLoaded && (this.dataSetsLoaded >= this.totalDataSets) ){ .then((response) => response.json())
this.onAllDatasetsLoaded(); .then((data) => {
} if(DEBUG) {
if ( this.dataSetsLoaded > this.totalDataSets){ console.log("Loaded stock data:");
console.log("Error: the totalDataSets setting in BurningDataService is too low! This will cause wierd errors. Please adjust it"); console.log(data);
}
} }
myself.stocks = data;
var stocks = ["man", "dwarf", "elf", "orc", "roden", "wolf", "troll"]; for (var stock of Object.keys(data)) {
var myself = this; myself.startingStatPts[stock] = new StartingStatPoints(myself.stocks[stock].starting_stats);
/* 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} ). .catch((error) => {
success(function(data,status,headers,config){ console.log("Error: Getting stocks from server failed: "+error);
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 */ /* Load skills from server */
var loadStartingStatPtsForStock = function(stock){ this.whenSkillsLoaded = fetch("/skills")
if( ! isValidStock(stock) ){ .then((response) => response.json())
console.log("Loading starting stat points failed: asked to load pts for invalid stock " + stock); .then((data) => {
return myself.skills = data;
if(DEBUG) {
console.log("Loaded skill data:");
console.log(data);
} }
})
$http.get("/starting_stat_pts/" + stock, {'timeout': 3000} ). .catch((error) => {
success(function(data,status,headers,config){ console.log("Error: Getting skills from server failed: "+error);
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 */ /* Load traits from server */
var loadResourcesForStock = function(stock){ this.whenTraitsLoaded = fetch("/traits")
if( ! isValidStock(stock) ){ .then((response) => response.json())
console.log("Loading resources failed: asked to load for invalid stock " + stock); .then((data) => {
return myself.traits = data;
if(DEBUG) {
console.log("Loaded trait data:");
console.log(data);
} }
})
$http.get("/resources/" + stock, {'timeout': 3000} ). .catch((error) => {
success(function(data,status,headers,config){ console.log("Error: Getting traits from server failed: "+error);
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++) { /* Lifepaths and resources defer until their stock is selected */
loadLifepathsForStock(stocks[i]);
loadStartingStatPtsForStock(stocks[i]);
loadResourcesForStock(stocks[i]);
}
/* Load skills from server */ this.whenLifePathsLoadedForStock = {};
$http.get("/skills", {'timeout': 3000} ). /* Load lifepaths from server */
success(function(data,status,headers,config){ this.loadLifepathsForStock = function(stock){
myself.skills = data; return myself.whenLifePathsLoadedForStock[stock] = fetch("/lifepaths/" + stock)
myself.datasetLoaded(); .then((response) => response.json())
console.log("Loaded skills. "); .then((data) => {
}). myself.lifepaths[stock] = data;
error(function(data,status,headers,config){ if(DEBUG) {
myself.datasetLoaded(); console.log("Loaded "+stock+" lifepaths:");
console.log("Error: Getting skills from server failed: HTTP code " + status + ": " + data); console.log(data);
}
})
.catch((error) => {
console.log("Error: Getting "+stock+" lifepaths from server failed: "+error);
}); });
};
/* Load traits from server */
$http.get("/traits", {'timeout': 3000} ). /* Load resources from server */
success(function(data,status,headers,config){ this.whenResourcesLoadedForStock = {};
myself.traits = data; this.loadResourcesForStock = function(stock){
myself.datasetLoaded(); return myself.whenResourcesLoadedForStock[stock] = fetch("/resources/" + stock)
console.log("Loaded traits. "); .then((response) => response.json())
}). .then((data) => {
error(function(data,status,headers,config){ myself.resources[stock] = data;
myself.datasetLoaded(); if(DEBUG) {
console.log("Error: Getting traits from server failed: HTTP code " + status + ": " + data); console.log("Loaded "+stock+" resources:");
console.log(data);
}
})
.catch((error) => {
console.log("Error: Getting "+stock+" resources from server failed: "+error);
}); });
};
} }
/**** End BurningDataService ****/ /**** End BurningDataService ****/

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

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

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