coop_al 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +7 -0
- data/.rubocop.yml +9 -0
- data/Gemfile +3 -0
- data/README.md +89 -0
- data/Rakefile +10 -0
- data/coop.gemspec +29 -0
- data/examples/filler.rb +9 -0
- data/examples/test.rb +15 -0
- data/exe/coop +131 -0
- data/lib/coop_al/adventure.rb +52 -0
- data/lib/coop_al/adventure_generator.rb +35 -0
- data/lib/coop_al/bestiary.rb +51 -0
- data/lib/coop_al/bestiary_generator.rb +3 -0
- data/lib/coop_al/bestiary_populator.rb +14 -0
- data/lib/coop_al/chapter.rb +51 -0
- data/lib/coop_al/chapter_generator.rb +32 -0
- data/lib/coop_al/encounter.rb +148 -0
- data/lib/coop_al/encounter_generator.rb +67 -0
- data/lib/coop_al/exception.rb +7 -0
- data/lib/coop_al/item.rb +30 -0
- data/lib/coop_al/library.rb +54 -0
- data/lib/coop_al/library_generator.rb +4 -0
- data/lib/coop_al/loot.rb +49 -0
- data/lib/coop_al/loot_generator.rb +42 -0
- data/lib/coop_al/monster.rb +19 -0
- data/lib/coop_al/monster_definition.rb +18 -0
- data/lib/coop_al/path.rb +65 -0
- data/lib/coop_al/path_follower.rb +34 -0
- data/lib/coop_al/random_encounter.rb +30 -0
- data/lib/coop_al/random_encounter_generator.rb +37 -0
- data/lib/coop_al/session.rb +55 -0
- data/lib/coop_al/session_date_generator.rb +43 -0
- data/lib/coop_al/session_encounter.rb +76 -0
- data/lib/coop_al/session_log.rb +51 -0
- data/lib/coop_al/state.rb +85 -0
- data/lib/coop_al/state_reporter.rb +72 -0
- data/lib/coop_al/trace.rb +29 -0
- data/lib/coop_al/treasure.rb +21 -0
- data/lib/coop_al/treasure_tables.rb +28 -0
- data/lib/coop_al/value.rb +167 -0
- data/lib/coop_al/version.rb +3 -0
- data/lib/coop_al/xp.rb +86 -0
- data/lib/coop_al.rb +34 -0
- data/res/srd.rb +400 -0
- metadata +188 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
module CoopAl
|
2
|
+
##
|
3
|
+
# SessionDateGenerator
|
4
|
+
#
|
5
|
+
class SessionDateGenerator
|
6
|
+
def initialize(options)
|
7
|
+
@next_date = options[:end_date]
|
8
|
+
@session_frequency = options[:session_frequency]
|
9
|
+
@skip_frequency = options[:skip_frequency]
|
10
|
+
@sessions = []
|
11
|
+
@blackout_dates = options[:blackout_dates]
|
12
|
+
end
|
13
|
+
|
14
|
+
def add_session
|
15
|
+
@sessions.push(@next_date)
|
16
|
+
advance_next_date
|
17
|
+
end
|
18
|
+
|
19
|
+
def session(number)
|
20
|
+
@sessions[-number]
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def advance_next_date
|
26
|
+
loop do
|
27
|
+
@next_date -= @session_frequency
|
28
|
+
break if ok?(@next_date)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def ok?(date)
|
33
|
+
return false if @blackout_dates.include?(date)
|
34
|
+
return true if @skip_frequency.nil?
|
35
|
+
return false if roll_dice("d#{@skip_frequency}") == 1
|
36
|
+
true
|
37
|
+
end
|
38
|
+
|
39
|
+
def blackout?(date)
|
40
|
+
@blackout_dates.include?(date)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module CoopAl
|
2
|
+
##
|
3
|
+
# SessionEncounter
|
4
|
+
#
|
5
|
+
class SessionEncounter
|
6
|
+
attr_reader :xp, :treasure
|
7
|
+
def initialize(name, monsters, xp, treasure, items)
|
8
|
+
@name = name
|
9
|
+
@monster_counts = monster_counts(monsters)
|
10
|
+
@monsters = monsters.uniq.sort
|
11
|
+
@xp = xp
|
12
|
+
@treasure = treasure
|
13
|
+
@items = items
|
14
|
+
end
|
15
|
+
|
16
|
+
def counts?
|
17
|
+
@xp.nonzero?
|
18
|
+
end
|
19
|
+
|
20
|
+
def dump(s)
|
21
|
+
s.puts "Encounter: #{@name}"
|
22
|
+
s.puts " Monsters: #{monsters_s}"
|
23
|
+
s.puts " XP: #{@xp}"
|
24
|
+
s.puts " Treasure: #{@treasure}" unless @treasure.zero?
|
25
|
+
s.puts " Items: #{items_s}" unless @items.empty?
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def monster_counts(monsters)
|
31
|
+
results = {}
|
32
|
+
monsters.each do |m|
|
33
|
+
if results.key?(m)
|
34
|
+
results[m] += 1
|
35
|
+
else
|
36
|
+
results[m] = 1
|
37
|
+
end
|
38
|
+
end
|
39
|
+
results
|
40
|
+
end
|
41
|
+
|
42
|
+
def monsters_s
|
43
|
+
@monsters.map { |m| pluralize(m, @monster_counts[m]) }.join(', ')
|
44
|
+
end
|
45
|
+
|
46
|
+
def pluralize(monster, count)
|
47
|
+
return monster if count == 1
|
48
|
+
"#{monster} (#{count})"
|
49
|
+
end
|
50
|
+
|
51
|
+
def items_s
|
52
|
+
@items.map(&:to_s).join(', ')
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# Downtime
|
58
|
+
#
|
59
|
+
class Downtime
|
60
|
+
def xp
|
61
|
+
0
|
62
|
+
end
|
63
|
+
|
64
|
+
def treasure
|
65
|
+
Value.new
|
66
|
+
end
|
67
|
+
|
68
|
+
def counts?
|
69
|
+
true
|
70
|
+
end
|
71
|
+
|
72
|
+
def dump(s)
|
73
|
+
s.puts 'Downtime'
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module CoopAl
|
2
|
+
##
|
3
|
+
# SessionLog
|
4
|
+
#
|
5
|
+
class SessionLog
|
6
|
+
def initialize(date_generator, options)
|
7
|
+
@date_generator = date_generator
|
8
|
+
@encounter_count = options.key?(:encounter_count) ? options[:encounter_count] : 10
|
9
|
+
@party_size = options.key?(:party_size) ? options[:party_size] : 6
|
10
|
+
@dm_name = options.key?(:dm_name) ? options[:dm_name] : 'Cameron'
|
11
|
+
@accumulated_xp = 0
|
12
|
+
@accumulated_treasure = Value.new
|
13
|
+
@sessions = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def record_encounter(adventure_name, encounter_name, monsters, xp, loot)
|
17
|
+
ensure_active_session(adventure_name)
|
18
|
+
@accumulated_xp += xp / @party_size
|
19
|
+
@accumulated_treasure += loot.treasure_value / @party_size
|
20
|
+
active_session.add_encounter(SessionEncounter.new(encounter_name, monsters, xp / @party_size, loot.treasure_value / @party_size, loot.items.dup))
|
21
|
+
end
|
22
|
+
|
23
|
+
def record_downtime(adventure_name)
|
24
|
+
ensure_active_session(adventure_name)
|
25
|
+
active_session.add_encounter(Downtime.new)
|
26
|
+
end
|
27
|
+
|
28
|
+
def dump(s)
|
29
|
+
@sessions.each { |e| e.dump(s) }
|
30
|
+
s.puts "Session Count: #{@sessions.size}"
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def ensure_active_session(adventure_name)
|
36
|
+
start_new_session(adventure_name) if @sessions.empty? || active_session.done?
|
37
|
+
end
|
38
|
+
|
39
|
+
def active_session
|
40
|
+
@sessions.last
|
41
|
+
end
|
42
|
+
|
43
|
+
def start_new_session(adventure_name)
|
44
|
+
@sessions.push(Session.new(session_number, @date_generator, adventure_name, @dm_name, @accumulated_xp, @accumulated_treasure.dup, @encounter_count))
|
45
|
+
end
|
46
|
+
|
47
|
+
def session_number
|
48
|
+
@sessions.size + 1
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module CoopAl
|
2
|
+
##
|
3
|
+
# State
|
4
|
+
#
|
5
|
+
class State
|
6
|
+
attr_reader :xp, :loot, :items
|
7
|
+
attr_accessor :current_path
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@path_stack = [Path.root]
|
11
|
+
@xp = 0
|
12
|
+
@loot = Loot.empty
|
13
|
+
@visited_paths = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def current_path
|
17
|
+
@path_stack.last
|
18
|
+
end
|
19
|
+
|
20
|
+
def path_depth
|
21
|
+
@path_stack.size
|
22
|
+
end
|
23
|
+
|
24
|
+
def in_downtime?
|
25
|
+
current_path.root?
|
26
|
+
end
|
27
|
+
|
28
|
+
def available_paths(library)
|
29
|
+
all_available_paths(library).select { |p| !@visited_paths.include?(p) }
|
30
|
+
end
|
31
|
+
|
32
|
+
def all_available_paths(library)
|
33
|
+
if path_depth == 1
|
34
|
+
if in_downtime?
|
35
|
+
library.all_entries
|
36
|
+
else
|
37
|
+
chapter = library.resolve(current_path)
|
38
|
+
chapter.links + downtime_if_available(chapter)
|
39
|
+
end
|
40
|
+
else
|
41
|
+
chapter = library.resolve(@path_stack[0])
|
42
|
+
library.all_entries + chapter.links + downtime_if_available(chapter)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def downtime_if_available(chapter)
|
47
|
+
return [Path.root] if chapter.links_to_downtime? && !in_downtime?
|
48
|
+
[]
|
49
|
+
end
|
50
|
+
|
51
|
+
def apply_path(path)
|
52
|
+
if path_depth == 1
|
53
|
+
if path.root?
|
54
|
+
@path_stack.push(path)
|
55
|
+
else
|
56
|
+
@path_stack = [current_path + path]
|
57
|
+
@visited_paths << current_path
|
58
|
+
end
|
59
|
+
else
|
60
|
+
@path_stack = [path]
|
61
|
+
@visited_paths << path
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def add_xp(xp)
|
66
|
+
@xp += xp
|
67
|
+
end
|
68
|
+
|
69
|
+
def add_loot(loot)
|
70
|
+
@loot += loot
|
71
|
+
end
|
72
|
+
|
73
|
+
def add_items(items)
|
74
|
+
@items.concat(items)
|
75
|
+
end
|
76
|
+
|
77
|
+
def treasure_value
|
78
|
+
@treasure.inject(Value.new) { |a, e| a + e.value }
|
79
|
+
end
|
80
|
+
|
81
|
+
def history_includes?(path)
|
82
|
+
@visited_paths.include?(path)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module CoopAl
|
2
|
+
##
|
3
|
+
# StateReporter
|
4
|
+
#
|
5
|
+
class StateReporter
|
6
|
+
def initialize(state, library, options = {})
|
7
|
+
@state = state
|
8
|
+
@library = library
|
9
|
+
@party_size = options.key?(:party_size) ? options[:party_size] : 6
|
10
|
+
@show_xp = options.key?(:show_xp) ? options[:show_xp] : true
|
11
|
+
@show_loot = options.key?(:show_loot) ? options[:show_loot] : true
|
12
|
+
@show_paths = options.key?(:show_paths) ? options[:show_paths] : true
|
13
|
+
end
|
14
|
+
|
15
|
+
def report(s)
|
16
|
+
report_xp(s) if @show_xp
|
17
|
+
report_loot(s) if @show_loot
|
18
|
+
report_paths(s) if @show_paths
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def report_xp(s)
|
24
|
+
s.puts "Character Level: #{character_level} (#{character_xp} XP - #{@state.xp} XP total)"
|
25
|
+
end
|
26
|
+
|
27
|
+
def character_xp
|
28
|
+
@state.xp / @party_size
|
29
|
+
end
|
30
|
+
|
31
|
+
def character_level
|
32
|
+
Coop::XpRequirementTable.new.level_from_xp(character_xp)
|
33
|
+
end
|
34
|
+
|
35
|
+
def report_loot(s)
|
36
|
+
report_treasure(s)
|
37
|
+
report_items(s)
|
38
|
+
end
|
39
|
+
|
40
|
+
def report_treasure(s)
|
41
|
+
return if @state.loot.treasures.empty?
|
42
|
+
s.puts "Treasure: #{character_treasure} (#{@state.loot.treasure_value} total)"
|
43
|
+
end
|
44
|
+
|
45
|
+
def report_items(s)
|
46
|
+
items = @state.loot.items
|
47
|
+
return if items.empty?
|
48
|
+
s.puts 'Items:'
|
49
|
+
s.puts items.map { |item| " #{item.description_with_origin}" }
|
50
|
+
end
|
51
|
+
|
52
|
+
def character_treasure
|
53
|
+
@state.loot.treasure_value / @party_size
|
54
|
+
end
|
55
|
+
|
56
|
+
def report_paths(s)
|
57
|
+
s.puts 'Paths:'
|
58
|
+
if available_paths.empty?
|
59
|
+
s.puts ' None'
|
60
|
+
else
|
61
|
+
s.puts available_paths.map { |p| " #{p}" }.join("\n")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def available_paths
|
66
|
+
@state.available_paths(@library).map do |p|
|
67
|
+
return ['Downtime'] if p.root?
|
68
|
+
"#{@library.resolve(p).description} (#{p})"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module CoopAl
|
4
|
+
##
|
5
|
+
# Trace
|
6
|
+
#
|
7
|
+
class Trace
|
8
|
+
include Singleton
|
9
|
+
|
10
|
+
attr_writer :tracing, :stream
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@tracing = false
|
14
|
+
@stream = STDOUT
|
15
|
+
end
|
16
|
+
|
17
|
+
def info(s)
|
18
|
+
@stream.puts(s) if @tracing
|
19
|
+
end
|
20
|
+
|
21
|
+
def warn(s)
|
22
|
+
@stream.puts("[WARNING] #{s}") if @tracing
|
23
|
+
end
|
24
|
+
|
25
|
+
def error(s)
|
26
|
+
@stream.puts("[ERROR] #{s}") if @tracing
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module CoopAl
|
2
|
+
##
|
3
|
+
# Treasure
|
4
|
+
#
|
5
|
+
class Treasure
|
6
|
+
attr_reader :value
|
7
|
+
|
8
|
+
def initialize(value, description = nil)
|
9
|
+
@value = value
|
10
|
+
@description = description
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
if @description.nil?
|
15
|
+
"coins (#{@value})"
|
16
|
+
else
|
17
|
+
"#{@description} (#{@value})"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
table :individual_treasure_cr_0_4 do
|
2
|
+
f(1..30) { '5d6'.cp }
|
3
|
+
f(31..60) { '4d6'.sp }
|
4
|
+
f(61..70) { '3d6'.ep }
|
5
|
+
f(71..95) { '3d6'.gp }
|
6
|
+
f(96..100) { '1d6'.pp }
|
7
|
+
end
|
8
|
+
|
9
|
+
table :individual_treasure_cr_5_10 do
|
10
|
+
f(1..30) { '4d6*100'.cp + '1d6*10'.ep }
|
11
|
+
f(31..60) { '6d6*10'.sp + '2d6*10'.gp }
|
12
|
+
f(61..70) { '3d6*10'.ep + '2d6*10'.gp }
|
13
|
+
f(71..95) { '4d6*10'.gp }
|
14
|
+
f(96..100) { '2d6*10'.gp + '3d6'.pp }
|
15
|
+
end
|
16
|
+
|
17
|
+
table :individual_treasure_cr_11_16 do
|
18
|
+
f(1..20) { '4d6*100'.sp + '1d6*100'.gp }
|
19
|
+
f(21..35) { '1d6*100'.ep + '1d6*100'.gp }
|
20
|
+
f(36..75) { '2d6*100'.gp + '1d6*10'.pp }
|
21
|
+
f(76..100) { '2d6*100'.gp + '2d6*10'.pp }
|
22
|
+
end
|
23
|
+
|
24
|
+
table :individual_treasure_cr_17_ do
|
25
|
+
f(1..15) { '2d6*1000'.ep + '8d6*100'.gp }
|
26
|
+
f(16..55) { '1d6*1000'.gp + '1d6*100'.pp }
|
27
|
+
f(56..100) { '1d6*1000'.gp + '2d6*100'.pp }
|
28
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
module CoopAl
|
2
|
+
##
|
3
|
+
# Value
|
4
|
+
#
|
5
|
+
class Value
|
6
|
+
attr_reader :platinum, :electrum, :gold, :silver, :copper
|
7
|
+
|
8
|
+
def initialize(amounts = {})
|
9
|
+
@platinum = amounts[:platinum] || 0
|
10
|
+
@electrum = amounts[:electrum] || 0
|
11
|
+
@gold = amounts[:gold] || 0
|
12
|
+
@silver = amounts[:silver] || 0
|
13
|
+
@copper = amounts[:copper] || 0
|
14
|
+
normalize
|
15
|
+
end
|
16
|
+
|
17
|
+
alias pp platinum
|
18
|
+
alias ep electrum
|
19
|
+
alias gp gold
|
20
|
+
alias sp silver
|
21
|
+
alias cp copper
|
22
|
+
|
23
|
+
def zero?
|
24
|
+
raw_value.zero?
|
25
|
+
end
|
26
|
+
|
27
|
+
def nonzero?
|
28
|
+
!zero?
|
29
|
+
end
|
30
|
+
|
31
|
+
def raw_value
|
32
|
+
@platinum * 10 + @gold + @silver / 10.0 + @copper / 100.0 + @electrum / 2.0
|
33
|
+
end
|
34
|
+
|
35
|
+
def normalize_from(value)
|
36
|
+
@platinum = 0
|
37
|
+
@electrum = 0
|
38
|
+
@gold = value.floor
|
39
|
+
value -= @gold
|
40
|
+
value *= 10
|
41
|
+
@silver = value.floor
|
42
|
+
value -= @silver
|
43
|
+
value *= 10
|
44
|
+
@copper = value.floor
|
45
|
+
end
|
46
|
+
|
47
|
+
def normalize
|
48
|
+
normalize_from(raw_value)
|
49
|
+
end
|
50
|
+
|
51
|
+
def +(other)
|
52
|
+
@platinum += other.pp
|
53
|
+
@electrum += other.ep
|
54
|
+
@gold += other.gp
|
55
|
+
@silver += other.sp
|
56
|
+
@copper += other.cp
|
57
|
+
normalize
|
58
|
+
self
|
59
|
+
end
|
60
|
+
|
61
|
+
def *(other)
|
62
|
+
normalize_from(raw_value * other)
|
63
|
+
self
|
64
|
+
end
|
65
|
+
|
66
|
+
def /(other)
|
67
|
+
normalize_from(raw_value / other)
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.platinum(amount)
|
72
|
+
Value.new(platinum: amount)
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.gold(amount)
|
76
|
+
Value.new(gold: amount)
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.silver(amount)
|
80
|
+
Value.new(silver: amount)
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.copper(amount)
|
84
|
+
Value.new(copper: amount)
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.electrum(amount)
|
88
|
+
Value.new(electrum: amount)
|
89
|
+
end
|
90
|
+
|
91
|
+
def to_a
|
92
|
+
[@platinum, @electrum, @gold, @silver, @copper]
|
93
|
+
end
|
94
|
+
|
95
|
+
def to_s
|
96
|
+
[
|
97
|
+
[@platinum, 'pp'],
|
98
|
+
[@gold, 'gp'],
|
99
|
+
[@silver, 'sp'],
|
100
|
+
[@copper, 'cp'],
|
101
|
+
[@electrum, 'ep']
|
102
|
+
].select { |v| v[0].nonzero? }.map { |v| "#{v[0]}#{v[1]}" }.join(', ')
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
##
|
108
|
+
# Integer monkey-patch
|
109
|
+
#
|
110
|
+
class Integer
|
111
|
+
def platinum
|
112
|
+
Coop::Value.platinum(to_i)
|
113
|
+
end
|
114
|
+
|
115
|
+
def gold
|
116
|
+
Coop::Value.gold(to_i)
|
117
|
+
end
|
118
|
+
|
119
|
+
def silver
|
120
|
+
Coop::Value.silver(to_i)
|
121
|
+
end
|
122
|
+
|
123
|
+
def copper
|
124
|
+
Coop::Value.copper(to_i)
|
125
|
+
end
|
126
|
+
|
127
|
+
def electrum
|
128
|
+
Coop::Value.electrum(to_i)
|
129
|
+
end
|
130
|
+
|
131
|
+
alias gp gold
|
132
|
+
alias sp silver
|
133
|
+
alias cp copper
|
134
|
+
alias pp platinum
|
135
|
+
alias ep electrum
|
136
|
+
end
|
137
|
+
|
138
|
+
##
|
139
|
+
# String monkey-patch
|
140
|
+
#
|
141
|
+
class String
|
142
|
+
def platinum
|
143
|
+
Coop::Value.platinum(roll_dice(self))
|
144
|
+
end
|
145
|
+
|
146
|
+
def gold
|
147
|
+
Coop::Value.gold(roll_dice(self))
|
148
|
+
end
|
149
|
+
|
150
|
+
def silver
|
151
|
+
Coop::Value.silver(roll_dice(self))
|
152
|
+
end
|
153
|
+
|
154
|
+
def copper
|
155
|
+
Coop::Value.copper(roll_dice(self))
|
156
|
+
end
|
157
|
+
|
158
|
+
def electrum
|
159
|
+
Coop::Value.electrum(roll_dice(self))
|
160
|
+
end
|
161
|
+
|
162
|
+
alias gp gold
|
163
|
+
alias sp silver
|
164
|
+
alias cp copper
|
165
|
+
alias pp platinum
|
166
|
+
alias ep electrum
|
167
|
+
end
|
data/lib/coop_al/xp.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
module CoopAl
|
2
|
+
##
|
3
|
+
# XpRewardTable
|
4
|
+
#
|
5
|
+
class XpRewardTable
|
6
|
+
def initialize
|
7
|
+
@xp_by_cr = {
|
8
|
+
cr0: 10,
|
9
|
+
cr1_8: 25,
|
10
|
+
cr1_4: 50,
|
11
|
+
cr1_2: 100,
|
12
|
+
cr1: 200,
|
13
|
+
cr2: 450,
|
14
|
+
cr3: 700,
|
15
|
+
cr4: 1_100,
|
16
|
+
cr5: 1_800,
|
17
|
+
cr6: 2_300,
|
18
|
+
cr7: 2_900,
|
19
|
+
cr8: 3_900,
|
20
|
+
cr9: 5_000,
|
21
|
+
cr10: 5_900,
|
22
|
+
cr11: 7_200,
|
23
|
+
cr12: 8_400,
|
24
|
+
cr13: 10_000,
|
25
|
+
cr14: 11_500,
|
26
|
+
cr15: 13_000,
|
27
|
+
cr16: 15_000,
|
28
|
+
cr17: 18_000,
|
29
|
+
cr18: 20_000,
|
30
|
+
cr19: 22_000,
|
31
|
+
cr20: 25_000,
|
32
|
+
cr21: 33_000,
|
33
|
+
cr22: 41_000,
|
34
|
+
cr23: 50_000,
|
35
|
+
cr24: 62_000,
|
36
|
+
cr25: 75_000,
|
37
|
+
cr26: 90_000,
|
38
|
+
cr27: 105_000,
|
39
|
+
cr28: 120_000,
|
40
|
+
cr29: 135_000,
|
41
|
+
cr30: 155_000
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
def [](cr)
|
46
|
+
@xp_by_cr[cr]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# XpRequirementTable
|
52
|
+
#
|
53
|
+
class XpRequirementTable
|
54
|
+
def initialize
|
55
|
+
@xp_by_level = {
|
56
|
+
1 => 0,
|
57
|
+
2 => 300,
|
58
|
+
3 => 900,
|
59
|
+
4 => 2_700,
|
60
|
+
5 => 6_500,
|
61
|
+
6 => 14_000,
|
62
|
+
7 => 23_000,
|
63
|
+
8 => 34_000,
|
64
|
+
9 => 48_000,
|
65
|
+
10 => 64_000,
|
66
|
+
11 => 85_000,
|
67
|
+
12 => 100_000,
|
68
|
+
13 => 120_000,
|
69
|
+
14 => 140_000,
|
70
|
+
15 => 165_000,
|
71
|
+
16 => 195_000,
|
72
|
+
17 => 225_000,
|
73
|
+
18 => 265_000,
|
74
|
+
19 => 305_000,
|
75
|
+
20 => 355_000
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
def level_from_xp(xp)
|
80
|
+
20.downto(1) do |i|
|
81
|
+
return i if xp >= @xp_by_level[i]
|
82
|
+
end
|
83
|
+
raise 'Invalid xp value (#{xp})'
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
data/lib/coop_al.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'tablescript'
|
2
|
+
require 'coop_al/treasure_tables'
|
3
|
+
require 'coop_al/trace'
|
4
|
+
require 'coop_al/version'
|
5
|
+
require 'coop_al/exception'
|
6
|
+
require 'coop_al/monster_definition'
|
7
|
+
require 'coop_al/loot_generator'
|
8
|
+
require 'coop_al/bestiary'
|
9
|
+
require 'coop_al/treasure'
|
10
|
+
require 'coop_al/value'
|
11
|
+
require 'coop_al/path'
|
12
|
+
require 'coop_al/state'
|
13
|
+
require 'coop_al/chapter'
|
14
|
+
require 'coop_al/monster'
|
15
|
+
require 'coop_al/item'
|
16
|
+
require 'coop_al/encounter'
|
17
|
+
require 'coop_al/random_encounter'
|
18
|
+
require 'coop_al/library_generator'
|
19
|
+
require 'coop_al/random_encounter_generator'
|
20
|
+
require 'coop_al/encounter_generator'
|
21
|
+
require 'coop_al/chapter_generator'
|
22
|
+
require 'coop_al/path_follower'
|
23
|
+
require 'coop_al/session_encounter'
|
24
|
+
require 'coop_al/session_date_generator'
|
25
|
+
require 'coop_al/session'
|
26
|
+
require 'coop_al/session_log'
|
27
|
+
require 'coop_al/xp'
|
28
|
+
require 'coop_al/adventure'
|
29
|
+
require 'coop_al/adventure_generator'
|
30
|
+
require 'coop_al/library'
|
31
|
+
require 'coop_al/loot'
|
32
|
+
require 'coop_al/state_reporter'
|
33
|
+
require 'coop_al/bestiary_populator'
|
34
|
+
require 'coop_al/bestiary_generator'
|