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

pull/11/head
Daniel Asher Resnick 2 years ago
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] 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

@ -15,15 +15,6 @@
"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"
] ]
@ -353,15 +344,6 @@
"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"
] ]
@ -663,15 +645,6 @@
"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"
] ]
@ -953,15 +926,6 @@
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"

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

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

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

@ -16,15 +16,6 @@
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"
] ]
@ -572,15 +563,6 @@
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"
] ]
@ -1737,4 +1719,4 @@
] ]
} }
} }
} }

@ -19,16 +19,6 @@
"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"
] ]
@ -335,16 +325,6 @@
"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"
] ]
@ -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": [ "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"
], ],
@ -253,17 +242,6 @@
"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"
], ],
@ -397,17 +375,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"
@ -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/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' require_relative 'data/custom'
module Charred module Charred
@ -16,6 +17,7 @@ module Charred
def initialize def initialize
@data = {} @data = {}
@data[:stocks] = {}
puts 'loading gold' puts 'loading gold'
load_gold(@data) load_gold(@data)

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

@ -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) ****/ /**** Class AppropriateWeaponsService (Angular Service) ****/
function AppropriateWeaponsService($modal, $http) { 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. // weapons that are appropriate for that lifepath.
this.appropriateWeapons = {}; this.appropriateWeapons = {};
@ -167,7 +167,7 @@ function WeaponOfChoiceService($modal, $http) {
} }
return has; return has;
} }
this.selectWeaponOfChoice = function (displayLp, onSelect){ this.selectWeaponOfChoice = function (displayLp, onSelect){
if( this.hasWeaponOfChoice(displayLp) ){ if( this.hasWeaponOfChoice(displayLp) ){
this.selectWeaponOfChoiceByModal(displayLp.name, function(selected){ this.selectWeaponOfChoiceByModal(displayLp.name, function(selected){
@ -250,121 +250,125 @@ function BurningDataService($http) {
roots: [root1, root2, ...] roots: [root1, root2, ...]
skill_name: skill_name:
roots: [root1, root2, ...] roots: [root1, root2, ...]
*/ */
this.skills = {}; this.skills = {};
/* JSON data structure representing all available traits */ /* JSON data structure representing all available traits */
this.traits = {}; this.traits = {};
/* JSON data structure representing all available resources (gear/property) */ /* JSON data structure representing all available resources (gear/property) */
this.resources = {}; this.resources = {};
// A hash of StartingStatPoints objects keyed by stock. // A hash of StartingStatPoints objects keyed by stock.
this.startingStatPts = {}; this.startingStatPts = {};
this.dataSetsLoaded = 0; this.events = {
// Total data sets: stocksLoaded: { triggered : false, callbacks : [] },
// lifepaths: 7 (man, dwarf, elf, orc, roden, wolf, troll) traitsLoaded: { triggered : false, callbacks : [] },
// stat points: 7 (man, dwarf, elf, orc, roden, wolf, troll) skillsLoaded: { triggered : false, callbacks : [] },
// skills }
// traits
// resources: 7 (man, dwarf, elf, orc, roden, wolf. troll) this.registerEvent = function(eventName, callback){
// TOTAL: 23 if(this.events[eventName].triggered)
this.totalDataSets = 23; callback();
this.onAllDatasetsLoaded = null; this.events[eventName].callbacks.push(callback);
this.registerOnAllDatasetsLoaded = function(callback){ }
if ( this.dataSetsLoaded >= this.totalDataSets ){ this.triggerEvent = function(eventName) {
myself.events[eventName].triggered = true;
for(var callback of myself.events[eventName].callbacks) {
callback(); callback();
} }
this.onAllDatasetsLoaded = callback;
} }
this.datasetLoaded = function(){ this.stockEvents = {};
this.dataSetsLoaded += 1; this.defineStockEvent = function(stockName, eventName) {
if ( this.onAllDatasetsLoaded && (this.dataSetsLoaded >= this.totalDataSets) ){ if(!(stockName in myself.stockEvents)) myself.stockEvents[stockName] = {};
this.onAllDatasetsLoaded(); myself.stockEvents[stockName][eventName] = { triggered : false, callbacks : [] } ;
} }
if ( this.dataSetsLoaded > this.totalDataSets){ this.registerStockEvent = function(stockName, eventName, callback){
console.log("Error: the totalDataSets setting in BurningDataService is too low! This will cause wierd errors. Please adjust it"); 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; var myself = this;
/* Load lifepaths from server */ // Log events
var loadLifepathsForStock = function(stock){ this.registerEvent("stocksLoaded", function() {
if( ! isValidStock(stock) ){ console.log("Stocks fetched from server: ", Object.keys(myself.stocks))
console.log("Loading lifepaths failed: asked to load lifepaths for invalid stock " + stock); // DEBUG:
return // 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} ). $http.get("/lifepaths/" + stock, {'timeout': 3000} ).
success(function(data,status,headers,config){ success(function(data,status,headers,config){
myself.lifepaths[stock] = data; myself.lifepaths[stock] = data;
myself.datasetLoaded(); myself.triggerStockEvent(stock, "lifepathsLoaded");
console.log("Loaded "+stock+" lifepaths. " + Object.keys(myself.lifepaths).length + " settings"); console.log("Loaded "+stock+" lifepaths. " + Object.keys(myself.lifepaths[stock]).length + " settings");
}). }).
error(function(data,status,headers,config){ error(function(data,status,headers,config){
myself.datasetLoaded();
console.log("Error: Getting "+stock+" lifepaths from server failed: HTTP code " + status + ": " + data); console.log("Error: Getting "+stock+" lifepaths from server failed: HTTP code " + status + ": " + data);
}); });
} }
/* Load starting stat points table from server */ /* Load resources from server */
var loadStartingStatPtsForStock = function(stock){ this.loadResourcesForStock = function(stock){
if( ! isValidStock(stock) ){
console.log("Loading starting stat points failed: asked to load pts for invalid stock " + stock);
return
}
$http.get("/starting_stat_pts/" + stock, {'timeout': 3000} ).
success(function(data,status,headers,config){
myself.startingStatPts[stock] = new StartingStatPoints(data);
myself.datasetLoaded();
console.log("Loaded "+stock+" starting stat points. ");
}).
error(function(data,status,headers,config){
myself.datasetLoaded();
console.log("Error: Getting "+stock+" stat points from server failed: HTTP code " + status + ": " + data);
});
}
/* Load starting stat points table from server */
var loadResourcesForStock = function(stock){
if( ! isValidStock(stock) ){
console.log("Loading resources failed: asked to load for invalid stock " + stock);
return
}
$http.get("/resources/" + stock, {'timeout': 3000} ). $http.get("/resources/" + stock, {'timeout': 3000} ).
success(function(data,status,headers,config){ success(function(data,status,headers,config){
myself.resources[stock] = data; myself.resources[stock] = data;
myself.datasetLoaded(); myself.triggerStockEvent(stock, "resourcesLoaded");
console.log("Loaded "+stock+" resources. "); console.log("Loaded "+stock+" resources. ");
}). }).
error(function(data,status,headers,config){ error(function(data,status,headers,config){
myself.datasetLoaded();
console.log("Error: Getting "+stock+" stat points from server failed: HTTP code " + status + ": " + data); 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 */ /* Load skills from server */
$http.get("/skills", {'timeout': 3000} ). $http.get("/skills", {'timeout': 3000} ).
success(function(data,status,headers,config){ success(function(data,status,headers,config){
myself.skills = data; myself.skills = data;
myself.datasetLoaded(); myself.triggerEvent("skillsLoaded");
console.log("Loaded skills. ");
}). }).
error(function(data,status,headers,config){ error(function(data,status,headers,config){
myself.datasetLoaded();
console.log("Error: Getting skills from server failed: HTTP code " + status + ": " + data); console.log("Error: Getting skills from server failed: HTTP code " + status + ": " + data);
}); });
@ -372,13 +376,22 @@ function BurningDataService($http) {
$http.get("/traits", {'timeout': 3000} ). $http.get("/traits", {'timeout': 3000} ).
success(function(data,status,headers,config){ success(function(data,status,headers,config){
myself.traits = data; myself.traits = data;
myself.datasetLoaded(); myself.triggerEvent("traitsLoaded");
console.log("Loaded traits. ");
}). }).
error(function(data,status,headers,config){ error(function(data,status,headers,config){
myself.datasetLoaded();
console.log("Error: Getting traits from server failed: HTTP code " + status + ": " + data); 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 ****/ /**** End BurningDataService ****/

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

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

Loading…
Cancel
Save