Merge branch 'stock-ruby-class' into custom-stocks

pull/11/head
commit 5091396638
  1. 16
      src/app.rb
  2. 36
      src/data/gold/lifepaths/dwarf.json
  3. 26
      src/data/gold/lifepaths/elf.json
  4. 2
      src/data/gold/lifepaths/man.json
  5. 20
      src/data/gold/lifepaths/orc.json
  6. 20
      src/data/gold/lifepaths/roden.json
  7. 22
      src/data/gold/lifepaths/wolf.json
  8. 114
      src/data/gold/starting_stat_pts/dwarf.json
  9. 146
      src/data/gold/starting_stat_pts/elf.json
  10. 90
      src/data/gold/starting_stat_pts/man.json
  11. 98
      src/data/gold/starting_stat_pts/orc.json
  12. 74
      src/data/gold/starting_stat_pts/roden.json
  13. 58
      src/data/gold/starting_stat_pts/wolf.json
  14. 130
      src/data/gold/stocks/dwarf.json
  15. 161
      src/data/gold/stocks/elf.json
  16. 99
      src/data/gold/stocks/man.json
  17. 114
      src/data/gold/stocks/orc.json
  18. 90
      src/data/gold/stocks/roden.json
  19. 75
      src/data/gold/stocks/wolf.json
  20. 35
      src/data/troll/lifepaths.json
  21. 82
      src/data/troll/starting_stat_pts.json
  22. 100
      src/data/troll/stock.json
  23. 2
      src/lib/data.rb
  24. 16
      src/lib/data/gold.rb
  25. 11
      src/lib/data/troll.rb
  26. 34
      src/lib/stock.rb
  27. 159
      src/public/js/burning-service.js
  28. 369
      src/public/js/burning.js
  29. 9
      src/views/partials/main.erb

@ -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

@ -15,15 +15,6 @@
"traits": [
1
],
"common_traits": [
"Accustomed To The Dark",
"Bearded",
"Greed",
"Oathsworn",
"Shaped From Earth And Stone",
"Stout",
"Tough"
],
"key_leads": [
"Guilder Setting"
]
@ -353,15 +344,6 @@
"traits": [
1
],
"common_traits": [
"Accustomed To The Dark",
"Bearded",
"Greed",
"Oathsworn",
"Shaped From Earth And Stone",
"Stout",
"Tough"
],
"key_leads": [
"Clansman Setting"
]
@ -663,15 +645,6 @@
"traits": [
1
],
"common_traits": [
"Accustomed To The Dark",
"Bearded",
"Greed",
"Oathsworn",
"Shaped From Earth And Stone",
"Stout",
"Tough"
],
"key_leads": [
"Clansman Setting"
]
@ -953,15 +926,6 @@
2,
"Dvergar"
],
"common_traits": [
"Accustomed To The Dark",
"Bearded",
"Greed",
"Oathsworn",
"Shaped From Earth And Stone",
"Stout",
"Tough"
],
"key_leads": [
"Guilder Setting",
"Artificer Setting"

@ -20,14 +20,6 @@
"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"
]
@ -517,14 +509,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,14 +1173,6 @@
"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"
@ -2045,4 +2021,4 @@
]
}
}
}
}

@ -11811,4 +11811,4 @@
]
}
}
}
}

@ -22,15 +22,6 @@
"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"
@ -432,15 +423,6 @@
"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"
]
@ -1592,4 +1574,4 @@
]
}
}
}
}

@ -16,15 +16,6 @@
3,
"Vegetarian"
],
"common_traits": [
"Aecer's Likeness",
"Coat Of Fur",
"Communal",
"Enlarged Incisors",
"Quick-blooded",
"Tail",
"Large Ears"
],
"key_leads": [
"Society Subsetting"
]
@ -572,15 +563,6 @@
3,
"Tunnel Vision"
],
"common_traits": [
"Aecer's Likeness",
"Coat Of Fur",
"Communal",
"Enlarged Incisors",
"Quick-blooded",
"Tail",
"Large Ears"
],
"key_leads": [
"Society Subsetting"
]
@ -1737,4 +1719,4 @@
]
}
}
}
}

@ -19,16 +19,6 @@
"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"
]
@ -335,16 +325,6 @@
"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"
]
@ -968,4 +948,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
}
]
}

@ -12,17 +12,6 @@
"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"
],
@ -253,17 +242,6 @@
"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"
],
@ -397,17 +375,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"
@ -614,4 +581,4 @@
]
}
}
}
}

@ -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
}
]
}

@ -2,6 +2,7 @@ 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
@ -16,6 +17,7 @@ module Charred
def initialize
@data = {}
@data[:stocks] = {}
puts 'loading gold'
load_gold(@data)

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

@ -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

@ -9,7 +9,7 @@ function Settings() {
/**** Class AppropriateWeaponsService (Angular Service) ****/
function AppropriateWeaponsService($modal, $http) {
// This class will store a hash which maps lifepath names to a list of
// This class will store a hash which maps lifepath names to a list of
// weapons that are appropriate for that lifepath.
this.appropriateWeapons = {};
@ -167,7 +167,7 @@ function WeaponOfChoiceService($modal, $http) {
}
return has;
}
this.selectWeaponOfChoice = function (displayLp, onSelect){
if( this.hasWeaponOfChoice(displayLp) ){
this.selectWeaponOfChoiceByModal(displayLp.name, function(selected){
@ -250,121 +250,125 @@ function BurningDataService($http) {
roots: [root1, root2, ...]
skill_name:
roots: [root1, root2, ...]
*/
this.skills = {};
/* JSON data structure representing all available traits */
this.traits = {};
/* JSON data structure representing all available resources (gear/property) */
this.resources = {};
// A hash of StartingStatPoints objects keyed by stock.
this.startingStatPts = {};
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 ){
this.events = {
stocksLoaded: { triggered : false, callbacks : [] },
traitsLoaded: { triggered : false, callbacks : [] },
skillsLoaded: { triggered : false, callbacks : [] },
}
this.registerEvent = function(eventName, callback){
if(this.events[eventName].triggered)
callback();
this.events[eventName].callbacks.push(callback);
}
this.triggerEvent = function(eventName) {
myself.events[eventName].triggered = true;
for(var callback of myself.events[eventName].callbacks) {
callback();
}
this.onAllDatasetsLoaded = callback;
}
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");
this.stockEvents = {};
this.defineStockEvent = function(stockName, eventName) {
if(!(stockName in myself.stockEvents)) myself.stockEvents[stockName] = {};
myself.stockEvents[stockName][eventName] = { triggered : false, callbacks : [] } ;
}
this.registerStockEvent = function(stockName, eventName, callback){
if(myself.stockEvents[stockName][eventName].triggered)
callback();
myself.stockEvents[stockName][eventName].callbacks.push(callback);
}
this.triggerStockEvent = function(stockName, eventName) {
myself.stockEvents[stockName][eventName].triggered = true;
for(var callback of myself.stockEvents[stockName][eventName].callbacks) {
callback();
}
}
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
}
// Log events
this.registerEvent("stocksLoaded", function() {
console.log("Stocks fetched from server: ", Object.keys(myself.stocks))
// DEBUG:
// console.log("Stocks fetched from server: ", myself.stocks)
});
this.registerEvent("skillsLoaded", function() {
console.log("Loaded " + Object.keys(myself.skills).length + " skills.");
// DEBUG:
// console.log("Loaded skills: ", myself.skills);
});
this.registerEvent("traitsLoaded", function() {
console.log("Loaded " + Object.keys(myself.traits).length + " traits.");
// DEBUG:
// console.log("Loaded traits: ", myself.traits);
});
$http.get("/stocks", {'timeout': 3000}).
success(function(data,status,headers,config){
// DEBUG:
// console.log(data);
myself.stocks = data;
for (var stock of Object.keys(data)) {
myself.startingStatPts[stock] = new StartingStatPoints(myself.stocks[stock].starting_stats);
myself.defineStockEvent(stock, "lifepathsLoaded");
myself.defineStockEvent(stock, "resourcesLoaded");
}
myself.triggerEvent("stocksLoaded");
}).
error(function(data,status,headers,config){
console.log("Error: Getting stocks from server failed: HTTP code " + status + ": " + data);
});
/* Load lifepaths from server */
this.loadLifepathsForStock = function(stock){
$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");
myself.triggerStockEvent(stock, "lifepathsLoaded");
console.log("Loaded "+stock+" lifepaths. " + Object.keys(myself.lifepaths[stock]).length + " settings");
}).
error(function(data,status,headers,config){
myself.datasetLoaded();
console.log("Error: Getting "+stock+" lifepaths from server failed: HTTP code " + status + ": " + data);
});
}
/* Load starting stat points table from server */
var loadStartingStatPtsForStock = function(stock){
if( ! isValidStock(stock) ){
console.log("Loading starting stat points failed: asked to load pts for invalid stock " + stock);
return
}
$http.get("/starting_stat_pts/" + stock, {'timeout': 3000} ).
success(function(data,status,headers,config){
myself.startingStatPts[stock] = new StartingStatPoints(data);
myself.datasetLoaded();
console.log("Loaded "+stock+" starting stat points. ");
}).
error(function(data,status,headers,config){
myself.datasetLoaded();
console.log("Error: Getting "+stock+" stat points from server failed: HTTP code " + status + ": " + data);
});
}
/* Load starting stat points table from server */
var loadResourcesForStock = function(stock){
if( ! isValidStock(stock) ){
console.log("Loading resources failed: asked to load for invalid stock " + stock);
return
}
/* Load resources from server */
this.loadResourcesForStock = function(stock){
$http.get("/resources/" + stock, {'timeout': 3000} ).
success(function(data,status,headers,config){
myself.resources[stock] = data;
myself.datasetLoaded();
myself.triggerStockEvent(stock, "resourcesLoaded");
console.log("Loaded "+stock+" resources. ");
}).
error(function(data,status,headers,config){
myself.datasetLoaded();
console.log("Error: Getting "+stock+" stat points from server failed: HTTP code " + status + ": " + data);
});
}
for (var i = 0; i < stocks.length; i++) {
loadLifepathsForStock(stocks[i]);
loadStartingStatPtsForStock(stocks[i]);
loadResourcesForStock(stocks[i]);
}
/* Load skills from server */
$http.get("/skills", {'timeout': 3000} ).
success(function(data,status,headers,config){
myself.skills = data;
myself.datasetLoaded();
console.log("Loaded skills. ");
myself.triggerEvent("skillsLoaded");
}).
error(function(data,status,headers,config){
myself.datasetLoaded();
console.log("Error: Getting skills from server failed: HTTP code " + status + ": " + data);
});
@ -372,13 +376,22 @@ function BurningDataService($http) {
$http.get("/traits", {'timeout': 3000} ).
success(function(data,status,headers,config){
myself.traits = data;
myself.datasetLoaded();
console.log("Loaded traits. ");
myself.triggerEvent("traitsLoaded");
}).
error(function(data,status,headers,config){
myself.datasetLoaded();
console.log("Error: Getting traits from server failed: HTTP code " + status + ": " + data);
});
// DEBUG:
// setTimeout(() => testStockLoading(myself), (2 * 1000));
}
function testStockLoading(dataService) {
console.log(dataService);
for (var stock in dataService.stocks) {
console.log(stock);
dataService.loadLifepathsForStock(stock);
dataService.loadResourcesForStock(stock);
}
}
/**** End BurningDataService ****/

@ -12,7 +12,7 @@ function handleIframeLoad(frameName)
if ( frame != null )
{
result = frame.document.getElementsByTagName("pre")[0].innerHTML;
// The form's onload handler gets called when the main page is first loaded as well.
// We detect this condition by checking if the iframes contents are not empty.
if ( result.length > 0 ){
@ -32,7 +32,7 @@ function handleIframeLoad(frameName)
scope.loadCurrentCharacterFromStruct(charStruct);
}
);
}
}
catch(e){
console.log("Loading character failed: " + e);
scope.$apply(
@ -72,7 +72,7 @@ burningModule.run(function($rootScope, $location, $anchorScroll, $routeParams) {
$rootScope.$on('$routeChangeSuccess', function(newRoute, oldRoute) {
if($routeParams.scrollTo){
$location.hash($routeParams.scrollTo);
$anchorScroll();
$anchorScroll();
}
});
});
@ -92,7 +92,7 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
selected item at that level.
index: The starting level in the hierarchy for which we should recalculate the lower levels
of the lists.
*/
*/
$scope.calculateHierarchyListForSelectN = function(listForSelect, currentItem, index){
if(index < 1)
return;
@ -100,7 +100,7 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
while(index < 3){
if(!currentItem[index-1] || !currentItem[index-1].resources) {
listForSelect[index] = [];
listForSelect[index] = [];
currentItem[index] = {};
}
else {
@ -118,7 +118,7 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
$scope.enforcePointLimits = settings.enforcePointLimits
/* A list of DisplayLifepath. */
$scope.selectedLifepaths = [];
$scope.selectedLifepaths = [];
$scope.statNames = ["Will", "Perception", "Power", "Forte", "Agility", "Speed"];
$scope.stats = [];
@ -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...";
@ -195,7 +195,7 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
$scope.reputations = {};
// Hash containing total stat points categorized by
// Hash containing total stat points categorized by
// type (physical, mental, either)
$scope.totalStatPoints = {"physical" : 0, "mental" : 0, "either" : 0}
$scope.unspentStatPoints = {"physical" : 0, "mental" : 0, "either" : 0}
@ -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)
@ -247,7 +241,7 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
/* Traits that are on the character's lifepaths, but not necessarily taken */
$scope.lifepathTraits = {};
/* Modifiers to attributes based on the answers to questions. This applies to Greed, Steel, etc.
/* Modifiers to attributes based on the answers to questions. This applies to Greed, Steel, etc.
The hash is keyed by attribute name, and the value is a list of yes/no questions, their answers,
and the modifier applied for a yes answer.
*/
@ -255,11 +249,11 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
$scope.attributeBonuses = {};
/* Used to keep track of whether the user shade-shifted an attribute, for those attributes that
/* Used to keep track of whether the user shade-shifted an attribute, for those attributes that
allow shade shifting */
$scope.attributeShade = {'Steel': 'B', 'Grief' : 'B', 'Greed' : 'B', 'Hatred' : 'B', 'Spite' : 'B'};
$scope.ptgs = new PTGS();
$scope.ptgs = new PTGS();
/* List of traits to show in the Choose Special Trait dropdown */
$scope.specialTraitsForDisplay = [];
@ -287,22 +281,22 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
};
$scope.characterStorage = characterStorage
$scope.resourceAdderToShow = 'gear';
// If this is true, then the user had added a lifepath to an Orc character that added a
// If this is true, then the user had added a lifepath to an Orc character that added a
// brutal life trait, and then the character removed that lifepath. According to the rules
// they can never gain more lifepaths after this action.
$scope.brutalLifeWithdrawn = false;
calculateGearSelectionLists($scope, burningData);
calculatePropertySelectionLists($scope, burningData);
$scope.serverSettings = serverSettings;
}
$scope.initialize("man");
$scope.initialize();
if ( characterStorage.currentCharacter ){
//console.log("Loading current character");
@ -354,7 +348,7 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
}
return result;
}
$scope.onGenderChange = function(){
if ($scope.name.length == 0) {
@ -364,20 +358,32 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
}
$scope.onStockChange = function(){
if(!$scope.stock) return;
if(!$scope.stockSelected) {
$scope.stocks.shift();
$scope.stockSelected = true;
}
var oldName = $scope.name;
// Make a blank character sheet
$scope.initialize($scope.stock);
if ( oldName.length == 0 ){
$scope.generateName();
} else {
$scope.name = oldName;
if(!burningData.lifepaths[$scope.stock]) {
burningData.loadLifepathsForStock($scope.stock);
}
if(!burningData.resources[$scope.stock]) {
burningData.loadResourcesForStock($scope.stock);
}
// TODO: technically a bug — only want this registered once per stock...
burningData.registerStockEvent($scope.stock, "lifepathsLoaded", function () {
// Make a blank character sheet
$scope.initialize($scope.stock);
calculateSettingNames($scope, burningData);
calculateCurrentSettingLifepathNames($scope, burningData);
calculateSpecialTraitsForDisplay($scope, burningData);
if ( oldName.length == 0 ){
$scope.generateName();
} else {
$scope.name = oldName;
}
calculateSettingNames($scope, burningData);
calculateCurrentSettingLifepathNames($scope, burningData);
calculateSpecialTraitsForDisplay($scope, burningData);
});
}
$scope.onSettingChange = function(){
@ -403,8 +409,10 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
calculateUnspentSkillPoints($scope);
}
burningData.registerOnAllDatasetsLoaded(function(){
onLifepathsLoad($scope, burningData);
burningData.registerEvent("stocksLoaded", function() {
$scope.stocks = [{ name: "Select a stock" }]
$scope.stocks = $scope.stocks.concat(Object.values(burningData.stocks));
$scope.stockSelected = false;
});
$scope.$on('$locationChangeStart', function(event, nextUrl, currentUrl) {
@ -426,11 +434,11 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
$scope.onAddLifepathClick = function(){
// Find the current lifepath info in the lifepaths
var setting = burningData.lifepaths[$scope.stock][$scope.currentSetting];
if (!setting)
if (!setting)
return;
var lifepath = setting[$scope.currentSettingLifepath];
if (!lifepath)
if (!lifepath)
return;
displayLp = new DisplayLifepath($scope.currentSetting, $scope.currentSettingLifepath, lifepath);
@ -449,7 +457,7 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
$scope.chooseStatPenalties(displayLp, -penalty);
}
// If the lifepath contains 'Appropriate Weapons', ask the
// If the lifepath contains 'Appropriate Weapons', ask the
// user to choose those weapons.
if( appropriateWeapons.hasAppropriateWeapons(displayLp) ){
var appropriate = appropriateWeapons.appropriateWeapons[displayLp.name];
@ -466,7 +474,7 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
}
}
// If the lifepath contains 'Weapon Of Choice', ask the
// If the lifepath contains 'Weapon Of Choice', ask the
// user to choose the weapon.
if( weaponOfChoice.hasWeaponOfChoice(displayLp) ){
weaponOfChoice.selectWeaponOfChoice(displayLp, function(){
@ -487,7 +495,7 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
displayLp.calculateResourcePoints(prevLifepath);
displayLp.calculateGeneralSkillPoints(prevLifepath);
displayLp.modifyForDiminishingReturns($scope.selectedLifepaths);
displayLp.modifyForDiminishingReturns($scope.selectedLifepaths);
if($scope.stock == "orc"){
displayLp.applyBrutalLife($scope.selectedLifepaths);
}
@ -551,7 +559,7 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
calculateUnspentResourcePoints($scope);
applyBonusesFromTraits($scope);
}
$scope.incrementStat = function(stat){
// Man stock has max 8 pts in any stat
@ -578,7 +586,7 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
if(specificStatPoints <= 0 && eitherStatPoints <= 0 && $scope.enforcePointLimits)
return;
if(specificStatPoints > 0){
specificStatPoints -= 1;
stat.setSpecificPointsSpent(stat.specificPointsSpent() + 1);
@ -591,10 +599,10 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
$scope.unspentStatPoints.either = eitherStatPoints;
if("m" == stat.type){
$scope.unspentStatPoints.mental = specificStatPoints;
}
}
else if ("p" == stat.type){
$scope.unspentStatPoints.physical = specificStatPoints;
}
}
calculatePTGS($scope);
}
@ -611,15 +619,15 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
}
else {
var specificStatPoints = 0;
stat.setSpecificPointsSpent(stat.specificPointsSpent() - 1);
if("m" == stat.type){
$scope.unspentStatPoints.mental += 1;
}
}
else if ("p" == stat.type){
$scope.unspentStatPoints.physical += 1;
}
}
else{
console.log("Error: Unknown stat type " + stat.type + " passed to decrementStat for stat " + stat.name);
// Undo the decrement
@ -672,10 +680,10 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
$scope.unspentStatPoints.either = eitherStatPoints;
if("m" == stat.type){
$scope.unspentStatPoints.mental = specificStatPoints;
}
}
else if ("p" == stat.type){
$scope.unspentStatPoints.physical = specificStatPoints;
}
}
}
var toBlack = function(stat){
@ -690,7 +698,7 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
else{
console.log("Error: Unknown stat type " + stat.type + " passed to incrementStat for stat " + stat.name);
return;
}
}
var cost = 5;
@ -773,7 +781,7 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
cost -= toTake;
}
if ( cost > 0 && ($scope.unspentSkillPoints["general"] > 0 || ! $scope.enforcePointLimits))
{
$scope.unspentSkillPoints["general"] -= cost;
@ -789,11 +797,11 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
// If this skill is required to be open, do not allow
// unchecking
var required = skillsRequiredToBeOpened($scope.selectedLifepaths)
if (skill.name in required){
if (skill.name in required){
checkbox.checked = true;
return;
}
if ( skill.generalPointsSpent > 0 ){
$scope.unspentSkillPoints.general += skill.generalPointsSpent;
skill.generalPointsSpent = 0;
@ -889,7 +897,7 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
if(endsWith(wiseName, "-wise")){
wiseName = wiseName.substring(0, wiseName.length-5);
}
wiseName = capitalizeEachWord(wiseName) + "-wise";
$scope.generalSkills[wiseName] = new DisplaySkill(wiseName, burningData.skills);
@ -899,7 +907,7 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
general skills selected by the user */
$scope.isGeneralSkill = function(displaySkill){
return (displaySkill.name in $scope.generalSkills);
}
/* Given the passed display skill, remove it from the list of general skills */
@ -908,7 +916,7 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
calculateUnspentSkillPoints($scope);
}
// Return a hash containing all skills
// Return a hash containing all skills
$scope.allSelectedSkills = function(){
var result = {}
for(var key in $scope.lifepathSkills){
@ -921,15 +929,15 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
}
// Return a list of skill names that the character can choose
// from to add a general skill. This is all skills less the
// skills the character already has, and less the skills that
// from to add a general skill. This is all skills less the
// skills the character already has, and less the skills that
// are not allowed for the character's stock.
$scope.selectableGeneralSkills = function(){
var result = [];
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);
}
}
@ -1024,20 +1032,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 ){
@ -1054,13 +1051,13 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
for(var i = 0; i < property.length; i++){
sum += property[i].cost;
}
if ( sum >= 50 ){
v += 1;
}
v += bonus;
return {"shade" : "B", "exp" : v};
}
else if ( "Resources" == name ){
@ -1140,7 +1137,7 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
// Divide physical between each physical stat
var physicalBuckets = divideIntoBuckets($scope.totalStatPoints.physical, 4);
var physicalEitherBuckets = divideIntoBuckets(eitherBuckets[1], 4);
$scope.statsByName.Will.mentalPointsSpent = mentalBuckets[0];
$scope.statsByName.Will.eitherPointsSpent = mentalEitherBuckets[1];
$scope.statsByName.Perception.mentalPointsSpent = mentalBuckets[1];
@ -1162,7 +1159,7 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
}
if ( sum != $scope.totalStatPoints.either + $scope.totalStatPoints.mental + $scope.totalStatPoints.physical ) {
console.log("Error: Calculation in distributeStats is incorrect.");
for(var i = 0; i < $scope.stats.length; i++){
$scope.stats[i].physicalPointsSpent = 0;
$scope.stats[i].mentalPointsSpent = 0;
@ -1189,30 +1186,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 )
@ -1403,7 +1376,7 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
});
}
// Launch a download for the current character. Since Javascript can't really
// Launch a download for the current character. Since Javascript can't really
// launch a download using data from javascript, we need to pass the current character
// to the server which sends a filename back to a hidden iframe which then launches the download.
$scope.downloadCurrentCharacter = function(){
@ -1534,7 +1507,7 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
}
$scope.addResource = function(type){
var resource = null;
var resourceHash = null;
if(type == 'relationship'){
@ -1603,7 +1576,7 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
calculateUnspentResourcePoints($scope);
}
$scope.addSelectListGear = function(){
var name = "";
var cost = 0;
@ -1623,7 +1596,7 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
}
}
}
$scope.currentGearDesc = name;
$scope.currentGearCost = cost;
$scope.addResource('gear');
@ -1648,7 +1621,7 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
}
}
}
$scope.currentPropertyDesc = name;
$scope.currentPropertyCost = cost;
$scope.addResource('property');
@ -1713,7 +1686,7 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
}
$scope.answerEmotionalAttributeQuestions = function (attributeName){
// If the character already has some or all of the questions answered, pass those in.
// Otherwise generate new ones.
@ -1738,9 +1711,9 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
displayEmotionalMath: function () {
return serverSettings.displayAttrMath;
},
}
}
});
modalInstance.result.then(function (selected) {
$scope.attributeModifierQuestionResults[attributeName] = selected;
}, function () {
@ -1838,12 +1811,12 @@ function BurningCtrl($scope, $http, $modal, $timeout, settings, appropriateWeapo
}
$scope.showUploadCharacterModal = function (){
var modalInstance = $modal.open({
templateUrl: '/upload_character_partial',
controller: UploadCharacterModalCtrl
});
modalInstance.result.then(function () {
console.log("Modal: Uploaded character");
}, function () {
@ -1953,7 +1926,7 @@ function calculateAge($scope){
function calculateSettingNames($scope, burningData){
var settingNames = null;
var lastCurrentSetting = $scope.currentSetting;
var lastCurrentSetting = $scope.currentSetting;
if ( ! $scope.enforceLifepathReqts ) {
// Display all settings and subsettings
@ -2030,8 +2003,8 @@ function calculateCurrentSettingLifepathNames($scope, burningData){
var currentSettingLifepathNames = null;
if($scope.enforceLifepathReqts){
//console.log("calculateCurrentSettingLifepathNames: enforce lifepath requirements is enabled");
currentSettingLifepathNames = [];
//console.log("calculateCurrentSettingLifepathNames: enforce lifepath requirements is enabled");
currentSettingLifepathNames = [];
var all = Object.keys(burningData.lifepaths[$scope.stock][$scope.currentSetting])
// Filter out the names that are not allowed based on the character's lifepaths.
if ( $scope.selectedLifepaths.length == 0 ){
@ -2056,11 +2029,11 @@ function calculateCurrentSettingLifepathNames($scope, burningData){
//console.log(settingName + ":" + lifepathName + " allowed: " + (result[0] ? "yes" : "no"));
//console.log("rexpr: " + rexpr);
if(result[0]){
//console.log("calculateCurrentSettingLifepathNames: added because lifepath has reqts, which are met: " + lifepathName);
//console.log("calculateCurrentSettingLifepathNames: added because lifepath has reqts, which are met: " + lifepathName);
currentSettingLifepathNames.push(lifepathName);
}
else {
//console.log("calculateCurrentSettingLifepathNames: not added because lifepath has reqts, which not are met: " + lifepathName);
//console.log("calculateCurrentSettingLifepathNames: not added because lifepath has reqts, which not are met: " + lifepathName);
}
}
else {
@ -2069,7 +2042,7 @@ function calculateCurrentSettingLifepathNames($scope, burningData){
}
}
}
}
else {
currentSettingLifepathNames = Object.keys(burningData.lifepaths[$scope.stock][$scope.currentSetting]);
}
@ -2092,7 +2065,7 @@ function calculateCurrentSettingLifepathNames($scope, burningData){
}
function calculateTotalStatPoints($scope, burningData){
var totalStatPoints = {"physical" : 0, "mental" : 0, "either" : 0};
$scope.totalStatPoints = {"physical" : 0, "mental" : 0, "either" : 0};
@ -2123,10 +2096,10 @@ function calculateTotalStatPoints($scope, burningData){
Preconditions: totalStatPoints is up to date.
*/
function calculateUnspentStatPoints($scope){
var unspentStatPoints = {
"physical" : $scope.totalStatPoints.physical,
"mental" : $scope.totalStatPoints.mental,
"physical" : $scope.totalStatPoints.physical,
"mental" : $scope.totalStatPoints.mental,
"either" : $scope.totalStatPoints.either
}
@ -2149,7 +2122,7 @@ function calculateLifepathSkills($scope, burningData, appropriateWeapons){
for(var i = 0; i < $scope.selectedLifepaths.length; i++){
var displayLp = $scope.selectedLifepaths[i];
appropriateWeapons.replaceAppropriateWeaponsUsingSaved(displayLp);
displayLp.replaceWeaponOfChoice();
@ -2158,7 +2131,7 @@ function calculateLifepathSkills($scope, burningData, appropriateWeapons){
if ( name != "General"){
lifepathSkills[name] = new DisplaySkill(name, burningData.skills);
}
}
}
}
$scope.lifepathSkills = lifepathSkills;
@ -2178,20 +2151,20 @@ function calculateTotalSkillPoints($scope){
function calculateUnspentSkillPoints($scope){
var unspentSkillPoints = {
"lifepath" : $scope.totalSkillPoints.lifepath,
"lifepath" : $scope.totalSkillPoints.lifepath,
"general" : $scope.totalSkillPoints.general
}
for(var key in $scope.lifepathSkills){
var skill = $scope.lifepathSkills[key]
unspentSkillPoints.lifepath -= skill.lifepathPointsSpent;
unspentSkillPoints.general -= skill.generalPointsSpent;
}
for(var key in $scope.generalSkills){
var skill = $scope.generalSkills[key]
unspentSkillPoints.lifepath -= skill.lifepathPointsSpent;
unspentSkillPoints.general -= skill.generalPointsSpent;
}
@ -2203,7 +2176,7 @@ function openRequiredSkills($scope){
var required = skillsRequiredToBeOpened($scope.selectedLifepaths);
var unspentSkillPoints = {
"lifepath" : $scope.unspentSkillPoints.lifepath,
"lifepath" : $scope.unspentSkillPoints.lifepath,
"general" : $scope.unspentSkillPoints.general
}
@ -2241,13 +2214,13 @@ function skillsRequiredToBeOpened(lifepaths){
break;
}
}
}
}
return skillHash;
}
/*
When a lifepath is removed, the stat points spent may be more than the available,
When a lifepath is removed, the stat points spent may be more than the available,
leading to a negative amount available. This method attempts to correct the situation
by lowering the spent points.
*/
@ -2258,10 +2231,10 @@ function correctStatPoints($scope){
}
/*
Helper function used by correctStatPoints. This function tries to correct the deficit in
Helper function used by correctStatPoints. This function tries to correct the deficit in
$scope.unspentStatPoints for the specified 'unspentStatField' (one of physical, mental, or either)
by unspending points from the stats, using the field 'displayStatField'
(one of physicalPointsSpent, mentalPointsSpent, eitherPointsSpent)
by unspending points from the stats, using the field 'displayStatField'
(one of physicalPointsSpent, mentalPointsSpent, eitherPointsSpent)
*/
function correctStatPointsHelperLowerPointsOfType($scope, unspentStatField, displayStatField){
if ( $scope.unspentStatPoints[unspentStatField] < 0 ){
@ -2284,7 +2257,7 @@ function correctStatPointsHelperLowerPointsOfType($scope, unspentStatField, disp
}
}
}
}
/* If the user adds some general skills to the character and then adds a lifepath that has those skills,
@ -2307,14 +2280,14 @@ function calculateLifepathTraits($scope, burningData){
for(var i = 0; i < $scope.selectedLifepaths.length; i++){
var displayLp = $scope.selectedLifepaths[i];
totalTraitPoints += displayLp.traitPts;
for(var j = 0; j < displayLp.traits.length; j++){
var name = displayLp.traits[j];
lifepathTraits[name] = new DisplayTrait(name, burningData.traits);
}
}
}
$scope.lifepathTraits = lifepathTraits;
@ -2328,7 +2301,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];
@ -2377,7 +2350,7 @@ function traitsRequiredToBeOpened(lifepaths){
break;
}
}
}
}
return traitHash;
}
@ -2404,17 +2377,17 @@ function calculateUnspentTraitPoints($scope){
/*
rexpr are the requires_expr. Returns a two-element list.
The first element is true if the requirements are satisifed, false
otherwise. The second element are any extra conditions if the first
rexpr are the requires_expr. Returns a two-element list.
The first element is true if the requirements are satisifed, false
otherwise. The second element are any extra conditions if the first
element is true. These extra conditions semantically descibe extra conditions
that must _later_ be met, for example "the requirements are satisfied as long as
that must _later_ be met, for example "the requirements are satisfied as long as
the character takes the trait 'your grace' "
The extra conditions supported so far are only a list of trait names.
*/
function areLifepathRequirementsSatisfied($scope, rexpr){
// make lookup tables
var selectedLifepathsByName = {}
for(var i = 0; i < $scope.selectedLifepaths.length; i++) {
@ -2424,7 +2397,7 @@ function areLifepathRequirementsSatisfied($scope, rexpr){
for(var i = 0; i < $scope.selectedLifepaths.length; i++) {
selectedLifepathsBySettingAndName[$scope.selectedLifepaths[i].setting.toLowerCase() + ":" + $scope.selectedLifepaths[i].name.toLowerCase()] = true;
}
var checkHasLifepathIn = function(rexpr){
// This is a [+has_lifepath_in, lp1, lp2, ...] OR [lifepath, lifepath] array.
@ -2509,7 +2482,7 @@ function areLifepathRequirementsSatisfied($scope, rexpr){
console.log("Error in areLifepathRequirementsSatisfied when evaluating expression: age_less_than predicate is length < 2 when it must be 2");
return [false, []];
}
return [$scope.age < rexpr[1], []];
}
@ -2518,7 +2491,7 @@ function areLifepathRequirementsSatisfied($scope, rexpr){
console.log("Error in areLifepathRequirementsSatisfied when evaluating expression: age_greater_than predicate is length < 2 when it must be 2");
return [false, []];
}
return [$scope.age > rexpr[1], []];
}
@ -2586,7 +2559,7 @@ function areLifepathRequirementsSatisfied($scope, rexpr){
console.log("Error in areLifepathRequirementsSatisfied when evaluating expression: '"+type+"' expression is length "+rexpr.length+" when it must exactly 2");
return [false, []];
}
var evalResult = areLifepathRequirementsSatisfied($scope, rexpr[1]);
return [!evalResult[0], evalResult[1]];
@ -2664,7 +2637,7 @@ function characterStructValid(charStruct){
}
function calculateTraitWarnings($scope, burningData){
// Make lookup maps of traits using lower-case trait names
var allTakenTraitNames = {};
for(var key in $scope.purchasedTraits){
@ -2686,7 +2659,7 @@ function calculateTraitWarnings($scope, burningData){
var result = areLifepathRequirementsSatisfied($scope, rexpr);
for(var k = 0; k < result[1].length; k++){
var trait = result[1][k];
if( ! (trait in allTakenTraitNames) ){
traitWarnings.push("You must take the '"+trait+"' trait to satisfy the '"+selectedLifepath.name+"' lifepath requirements.");
}
@ -2704,12 +2677,12 @@ function applyBonusesFromTraits($scope) {
for(var key in $scope.purchasedTraits){
var displayTrait = $scope.purchasedTraits[key];
traitBonuses.addTrait(key, displayTrait);
}
}
for(var key in $scope.requiredTraits){
var displayTrait = $scope.requiredTraits[key];
traitBonuses.addTrait(key, displayTrait);
}
}
for(var key in $scope.commonTraits){
var displayTrait = $scope.commonTraits[key];
@ -2725,13 +2698,13 @@ function applyBonusesFromTraits($scope) {
var displaySkill = $scope.lifepathSkills[key];
displaySkill.bonus = traitBonuses.getAddBonusesForSkill(key);
displaySkill.roundUp = traitBonuses.getRoundUpBonusForSkill(displaySkill);
}
}
for(var key in $scope.generalSkills) {
var displaySkill = $scope.generalSkills[key];
displaySkill.bonus = traitBonuses.getAddBonusesForSkill(key);
displaySkill.roundUp = traitBonuses.getRoundUpBonusForSkill(displaySkill);
}
}
//** Attributes
@ -2750,7 +2723,7 @@ function applyBonusesFromTraits($scope) {
}
/*
Compute which traits the user can add as special traits. This value depends on character stock so
Compute which traits the user can add as special traits. This value depends on character stock so
this function should be called when stock changes.
*/
function calculateSpecialTraitsForDisplay($scope, burningData){
@ -2760,7 +2733,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));
}
@ -2808,48 +2781,16 @@ 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 restrictionStockToValidStock(stocks, stockAdjective){
return Object.values(stocks).findLast(s => s.adjective == stockAdjective).key;
}
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 validStockToRestrictionStock(stocks, stockName){
return stocks[stockName].adjective;
}
function attributeModifyingQuestions($scope, attribute)
{
{
var result = [];
var ageMod = function(age){
@ -2919,8 +2860,8 @@ function attributeModifyingQuestions($scope, attribute)
{question: "+1 Greed if the character is over 200 years old.", computed: true, compute: ageMod(200)},
{question: "+1 Greed if the character is over 400 years old.", computed: true, compute: ageMod(400)},
{question: "Each romantic relationship is -1 Greed. Each hateful relationship is +1 Greed. A hateful immediate family member is +2 Greed.", computed: true, compute: relMod}
);
}
);
}
else if ( attribute == "Health" )
{
var stockMod = function(){
@ -2944,7 +2885,7 @@ function attributeModifyingQuestions($scope, attribute)
// "herald":1, "bannerman":1, "scout":1, "sergeant":1, "veteran":1, "cavalryman":1, "captain":1, "military order":1}
var steelyLps = {"conscript":1, "squire":1, "knight":1, "bandit":1, "pirate":1, "military order":1, "sword singer":1};
var steelySettings = {"professional soldier subsetting":1, "black legion subsetting":1, "dwarven host subsetting":1, "protector subsetting":1};
var steel = 0;
for(var i = 0; i < $scope.selectedLifepaths.length; i++){
if($scope.selectedLifepaths[i].name.toLowerCase() in steelyLps || $scope.selectedLifepaths[i].setting.toLowerCase() in steelySettings){
@ -3051,23 +2992,23 @@ function attributeModifyingQuestions($scope, attribute)
var lpMod1 = function(){
var lps = {"lancer":1, "lieutenant":1, "captain":1};
var lps2 = {"lord protector":1, "soother":1};
var grief1 = 0;
var grief2 = 0;
for(var i = 0; i < $scope.selectedLifepaths.length; i++){
if($scope.selectedLifepaths[i].name.toLowerCase() in lps)
{
grief1 = 1;
if( grief2 > 0)
if( grief2 > 0)
break;
}
if($scope.selectedLifepaths[i].name.toLowerCase() in lps2)
{
grief2 = 1;
if( grief1 > 0)
if( grief1 > 0)
break;
}
}
}
return grief1 + grief2;
}
@ -3084,11 +3025,11 @@ function attributeModifyingQuestions($scope, attribute)
var lpMod2 = function(){
var lps = {"loremaster":1, "adjutant":1, "althing":1};
for(var i = 0; i < $scope.selectedLifepaths.length; i++){
if($scope.selectedLifepaths[i].name.toLowerCase() in lps)
return 1;
}
}
return 0;
}
@ -3128,7 +3069,7 @@ function attributeModifyingQuestions($scope, attribute)
var percMod = function(){
return ($scope.statsByName['Perception'].exp() > 5 ? 1 : 0);
}
result.push(
{question: "+1 Grief if the character has taken any Protector lifepath.", computed: true, compute: protectMod},
{question: "+1 Grief if the character has been a Lancer, Lieutenant or Captain; Additional +1 if the character has been a Lord Protector or Soother", computed: true, compute: lpMod1},
@ -3235,7 +3176,7 @@ function attributeModifyingQuestions($scope, attribute)
var brutalMod = function(){
var count = 0;
for(var i = 0; i < $scope.selectedLifepaths.length; i++){
if ( $scope.selectedLifepaths[i].brutalLifeTraitName ){
if ( $scope.selectedLifepaths[i].brutalLifeTraitName ){
count++;
}
}
@ -3354,7 +3295,7 @@ function convertAttributeModifierQuestionResultsForSave($scope){
// Save only the non-computed questions that were answered.
var result = {};
for (key in $scope.attributeModifierQuestionResults){
var list = [];
@ -3384,7 +3325,7 @@ function convertAttributeModifierQuestionResultsForCharsheet($scope){
var attribute = attributeNames[j];
var fullQuestions = attributeModifyingQuestions($scope, attribute)
if(! fullQuestions){
// Not an attribute with questions
continue;
@ -3423,7 +3364,7 @@ function loadAttributeModifierQuestionResultsFromSave($scope, questions)
{
var result = {};
// For each attribute for which questions were saved, generate the
// For each attribute for which questions were saved, generate the
// full set of questions, then add in the answers from the save.
// The reason we do this rather than save all questions and answers
// is 1) to save space, and 2) because we can't save the compute function
@ -3452,7 +3393,7 @@ function loadAttributeModifierQuestionResultsFromSave($scope, questions)
/**
A number of skills are not defined because they are a specific instance of a general skill; for example
ancient history is a type of history, and has the same roots. This function returns the parent skill for
ancient history is a type of history, and has the same roots. This function returns the parent skill for
a specific skill.
*/
function getGeneralSkillNameFor(skillName){
@ -3510,7 +3451,7 @@ var computeStatAverage = function(statsByName, statNames, roundUp){
if(getShade(stats[i]) == 'G' && !allGray){
sum += 2;
}
sum += stats[i].exp();
}
@ -3557,11 +3498,11 @@ function calculateGearSelectionLists($scope, burningData) {
$scope.gearListForSelect = [];
for(var i = 0; i < 3; i++)
$scope.currentSelectListGear.push({});
$scope.gearListForSelect[0] = calculateHierarchyListForSelect($scope, burningData, 'gear');
if($scope.gearListForSelect[0].length > 0)
$scope.currentSelectListGear[0] = $scope.gearListForSelect[0][0];
$scope.calculateHierarchyListForSelectN($scope.gearListForSelect, $scope.currentSelectListGear, 1);
}
@ -3570,11 +3511,11 @@ function calculatePropertySelectionLists($scope, burningData) {
$scope.propertyListForSelect = [];
for(var i = 0; i < 3; i++)
$scope.currentSelectListProperty.push({});
$scope.propertyListForSelect[0] = calculateHierarchyListForSelect($scope, burningData, 'property');
if($scope.propertyListForSelect[0].length > 0)
$scope.currentSelectListProperty[0] = $scope.propertyListForSelect[0][0];
$scope.calculateHierarchyListForSelectN($scope.propertyListForSelect, $scope.currentSelectListProperty, 1);
}

@ -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'>

Loading…
Cancel
Save