traveller_rpg 0.0.1.1 → 0.1.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -42,3 +42,289 @@ task :chargen do
42
42
  exit 1
43
43
  end
44
44
  end
45
+
46
+ task :career_stats do
47
+ $LOAD_PATH.unshift(File.join(__dir__, 'lib'))
48
+ require 'traveller_rpg/careers'
49
+
50
+ by_stat = {
51
+ strength: {},
52
+ dexterity: {},
53
+ endurance: {},
54
+ intelligence: {},
55
+ education: {},
56
+ social_status: {},
57
+ }
58
+
59
+ # for each career, show relevant stats
60
+ ObjectSpace.each_object(Class) { |klass|
61
+ next unless klass < TravellerRPG::Career
62
+ next if klass == TravellerRPG::MilitaryCareer
63
+
64
+ qual = klass.const_get('QUALIFICATION')
65
+ qual = qual[:choose] if qual and qual[:choose]
66
+
67
+ if qual
68
+ qual.each { |stat, check|
69
+ by_stat[stat][:qualification] ||= {}
70
+ by_stat[stat][:qualification][name] = check
71
+ }
72
+ end
73
+
74
+ puts "#{name} #{qual}"
75
+ spec = klass.const_get('SPECIALIST')
76
+ spec.each { |asg, cfg|
77
+ puts "\t#{asg} survival: #{cfg[:survival]}"
78
+ puts "\t#{asg} advancement: #{cfg[:advancement]}"
79
+
80
+ cfg[:survival].each { |stat, check|
81
+ by_stat[stat][:survival] ||= {}
82
+ by_stat[stat][:survival][name] ||= {}
83
+ by_stat[stat][:survival][name][asg] = check
84
+ }
85
+ cfg[:advancement].each { |stat, check|
86
+ by_stat[stat][:advancement] ||= {}
87
+ by_stat[stat][:advancement][name] ||= {}
88
+ by_stat[stat][:advancement][name][asg] = check
89
+ }
90
+ }
91
+ }
92
+
93
+ scores = {}
94
+
95
+ def score(num, check_type)
96
+ c = case check_type
97
+ when :qualification then 1
98
+ when :survival then 0.9
99
+ when :advancement then 0.8
100
+ end
101
+ c * (10 - num)
102
+ end
103
+
104
+ by_stat.each { |stat, hsh|
105
+ scores[stat] = {}
106
+ [:qualification, :survival, :advancement].each { |check_type|
107
+ next unless hsh.key?(check_type)
108
+ hsh[check_type].each { |kls, cfg|
109
+ scores[stat][kls] ||= {}
110
+ scores[stat][kls][:total] ||= 0
111
+ case check_type
112
+ when :qualification
113
+ scores[stat][kls][:total] += score(cfg, check_type)
114
+ when :advancement, :survival
115
+ cfg.each { |asg, check|
116
+ scores[stat][kls][:total] += score(check, check_type)
117
+ scores[stat][kls][asg] ||= 0
118
+ scores[stat][kls][asg] += score(check, check_type)
119
+ }
120
+ end
121
+ }
122
+ }
123
+ }
124
+
125
+ scores.each { |stat, hsh|
126
+ puts
127
+ puts stat
128
+ puts "==="
129
+ hsh.sort_by { |kls, score_hsh|
130
+ score_hsh[:total]
131
+ }.reverse.each { |kls, score_hsh|
132
+ puts format("%s %0.1f", kls, score_hsh[:total])
133
+ score_hsh.sort_by { |k, v| v }.reverse.each { |k, v|
134
+ next if k == :total
135
+ puts format("\t%s %0.1f", k, v)
136
+ }
137
+ }
138
+ }
139
+ end
140
+
141
+ task :choose_report do
142
+ $LOAD_PATH.unshift(File.join(__dir__, 'lib'))
143
+ require 'traveller_rpg/careers'
144
+
145
+ report = {
146
+ skills: {},
147
+ stat_checks: {},
148
+ ranks: {},
149
+ benefits: {},
150
+ }
151
+
152
+ # for each career, show relevant stats
153
+ ObjectSpace.each_object(Class) { |klass|
154
+ next unless klass < TravellerRPG::Career
155
+ next if klass == TravellerRPG::MilitaryCareer
156
+ # next if klass == TravellerRPG::Drifter
157
+
158
+ name = klass.name.split('::').last
159
+ qual = klass.const_get('QUALIFICATION')
160
+ if qual and qual[:choose]
161
+ report[:stat_checks][name] ||= []
162
+ report[:stat_checks][name] << :qualification
163
+ end
164
+ %w{PERSONAL_SKILLS SERVICE_SKILLS ADVANCED_SKILLS}.each { |sk|
165
+ skills = klass.const_get(sk) rescue nil
166
+ next unless skills
167
+ skills.each { |item|
168
+ if item.is_a?(Hash) and item[:choose]
169
+ report[:skills][name] ||= []
170
+ report[:skills][name] << sk
171
+ end
172
+ }
173
+ }
174
+ spec = klass.const_get('SPECIALIST')
175
+ if spec
176
+ spec.each { |asg, cfg|
177
+ # ranks, skills, stat_checks
178
+ ranks = cfg.fetch(:ranks)
179
+ ranks.each { |rank, hsh|
180
+ if hsh[:choose] or
181
+ (hsh[:skill].is_a?(Hash) and hsh[:skill][:choose]) or
182
+ (hsh[:stat].is_a?(Hash) and hsh[:stat][:choose])
183
+ report[:ranks][name] ||= []
184
+ report[:ranks][name] << "[#{asg}]"
185
+ else
186
+ # p hsh
187
+ end
188
+ }
189
+ skills = cfg.fetch(:skills)
190
+ skills.each { |item|
191
+ if item.is_a?(Hash) and item[:choose]
192
+ report[:skills][name] ||= []
193
+ report[:skills][name] << "[#{asg}]"
194
+ end
195
+ }
196
+ [:survival, :advancement].each { |sc|
197
+ stat_check = cfg.fetch(sc)
198
+ if stat_check[:choose]
199
+ report[:stat_checks][name] ||= []
200
+ report[:stat_checks][name] << sc
201
+ end
202
+ }
203
+ }
204
+ end
205
+
206
+ ben = klass.const_get('BENEFITS')
207
+ if ben
208
+ ben.each { |roll, hsh|
209
+ if hsh.is_a?(Hash) and hsh[:choose]
210
+ report[:benefits][name] ||= []
211
+ report[:benefits][name] << roll
212
+ end
213
+ }
214
+ end
215
+
216
+ if klass < TravellerRPG::MilitaryCareer
217
+ oranks = klass.const_get('OFFICER_RANKS')
218
+ oranks.each { |rank, hsh|
219
+ if hsh[:choose] or
220
+ (hsh[:skill].is_a?(Hash) and hsh[:skill][:choose]) or
221
+ (hsh[:stat].is_a?(Hash) and hsh[:stat][:choose])
222
+ report[:ranks][name] ||= []
223
+ report[:ranks][name] << 'Officer'
224
+ else
225
+ # p hsh
226
+ end
227
+ }
228
+ end
229
+ }
230
+
231
+ report.each { |item, hsh|
232
+ puts
233
+ puts item
234
+ puts "==="
235
+
236
+ hsh.each { |name, stuff|
237
+ puts "#{name}: #{stuff.join(' ')}"
238
+ }
239
+ }
240
+ end
241
+
242
+ task :military_compare do
243
+ $LOAD_PATH.unshift(File.join(__dir__, 'lib'))
244
+ require 'traveller_rpg/careers'
245
+ require 'traveller_rpg/generator'
246
+
247
+ include TravellerRPG
248
+
249
+ def const_get(class_name, const_name)
250
+ TravellerRPG.const_get(class_name).const_get(const_name)
251
+ end
252
+
253
+ char = Generator.character
254
+ scalars = %w{ADVANCED_EDUCATION AGE_PENALTY}
255
+ hashes = %w{QUALIFICATION}
256
+ arrays = %w{PERSONAL_SKILLS SERVICE_SKILLS ADVANCED_SKILLS OFFICER_SKILLS
257
+ CREDITS}
258
+ complex = %w{BENEFITS OFFICER_RANKS SPECIALIST}
259
+ %w{Army Marines Navy}.each { |cname|
260
+ cnamex = cname + 'x'
261
+ (scalars + arrays + hashes).each { |const_name|
262
+ if const_get(cname, const_name) != const_get(cnamex, const_name)
263
+ warn "#{cname}::#{const_name}"
264
+ end
265
+ }
266
+ complex.each { |const_name|
267
+ if const_get(cname, const_name) != const_get(cnamex, const_name)
268
+ warn "#{cname}::#{const_name}"
269
+ old = const_get(cnamex, const_name)
270
+ new = const_get(cname, const_name)
271
+ case old
272
+ when Hash
273
+ warn "mismatch: #{old.class} #{new.class}" unless new.is_a?(Hash)
274
+ if old.size != new.size
275
+ warn "size mismatch: #{old.size} #{new.size}"
276
+ end
277
+ old.each { |k, v|
278
+ if old[k] != new[k]
279
+ warn "mismatched values for #{k}"
280
+ if v.is_a?(Hash)
281
+ v.each { |kk, vv|
282
+ if vv != new[k][kk]
283
+ warn "mismatched values for #{kk}"
284
+ end
285
+ if vv.is_a?(Hash)
286
+ vv.each { |kkk, vvv|
287
+ if vvv != new[k][kk][kkk]
288
+ warn "mismatched values for #{kkk}"
289
+ end
290
+ }
291
+ else
292
+ warn "#{kk}: #{vv.class} #{new[k][kk].class}"
293
+ end
294
+ }
295
+ else
296
+ warn "#{v.class} #{new[k].class}"
297
+ end
298
+ end
299
+ }
300
+ when Array
301
+ warn "ARRAY!"
302
+ else
303
+ warn old.class.name
304
+ end
305
+ end
306
+ }
307
+ }
308
+ end
309
+
310
+ task :naval_officer do
311
+ $LOAD_PATH.unshift(File.join(__dir__, 'lib'))
312
+ require 'traveller_rpg/careers'
313
+ require 'traveller_rpg/generator'
314
+
315
+ include TravellerRPG
316
+
317
+ char = Generator.character
318
+ char.stats.education = 12
319
+ char.stats.social_status = 12
320
+
321
+ career = Navy.new(char)
322
+ career.activate
323
+ career.advancement_roll while !career.officer?
324
+ puts char.report
325
+ 4.times { career.run_term if career.active? }
326
+ puts char.report
327
+ career.muster_out
328
+ puts char.report
329
+ puts career.report
330
+ end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.1.1
1
+ 0.1.0.1
@@ -1,7 +1,21 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ require 'traveller_rpg/generator'
3
4
  require 'traveller_rpg/career_path'
4
5
 
5
- careers = %w{Agent Army Marines Navy Scout}
6
- TravellerRPG::CareerPath.run(careers)
7
- puts "DONE"
6
+ include TravellerRPG
7
+
8
+ careers = %w{Agent Army Citizen Entertainer Marines Merchant
9
+ Navy Noble Rogue Scholar Scout}
10
+ begin
11
+ path = CareerPath.run(careers, character: Generator.character)
12
+ rescue => e
13
+ puts [e.class, e.message].join(': ')
14
+ begin
15
+ require 'pry'
16
+ binding.pry
17
+ rescue LoadError
18
+ # ok
19
+ end
20
+ raise
21
+ end
@@ -9,20 +9,27 @@ module TravellerRPG
9
9
  \z # end str
10
10
  }x
11
11
 
12
- def self.roll(str = nil, faces: 6, dice: 2, count: 1)
13
- return self.roll_str(str) if str
14
- rolln = -> (faces, dice) { Array.new(dice) { rand(faces) + 1 } }
15
- (Array.new(count) { rolln.(faces, dice).sum }.sum.to_f / count).round
12
+ def self.roll(spec = 'd6', label: nil, dm: 0)
13
+ matches = spec.match(ROLL_RGX) or raise("bad roll spec: #{spec}")
14
+ roll = self.roll_int(dice: matches[1].empty? ? 1 : matches[1].to_i,
15
+ faces: matches[2].to_i)
16
+ puts "#{label} roll (#{spec}): #{roll} (DM #{dm})" if label
17
+ roll + dm
16
18
  end
17
19
 
18
- def self.roll_str(str)
19
- matches = str.match(ROLL_RGX) or raise("bad roll: #{str}")
20
- dice, faces = matches[1], matches[2]
21
- self.roll(dice: dice.empty? ? 1 : dice.to_i, faces: faces.to_i)
20
+ # this is the fastest, but won't print individual die values
21
+ def self.roll_int(dice: 1, faces: 6)
22
+ val = 0
23
+ dice.times { val += rand(faces) + 1 }
24
+ val
25
+ end
26
+
27
+ def self.roll_ary(dice: 1, faces: 6)
28
+ Array.new(dice) { rand(faces) + 1 }.sum
22
29
  end
23
30
 
24
31
  def self.choose(msg, *args)
25
- puts msg + ' (' + args.join(' ') + ')'
32
+ puts msg + ' (' + args.join(' | ') + ')'
26
33
  raise "no choices" if args.empty?
27
34
  return self.player_choose(msg, *args) if PLAYER_CHOOSE
28
35
  choice = args.sample
@@ -38,176 +45,31 @@ module TravellerRPG
38
45
  chosen = choice
39
46
  elsif args.include?(choice.to_sym)
40
47
  chosen = choice.to_sym
41
- else
48
+ elsif choice.empty?
42
49
  puts "Try again.\n"
50
+ else
51
+ matches = args.grep %r{\A#{choice}}i
52
+ case matches.size
53
+ when 0
54
+ # ok
55
+ puts "Try again.\n"
56
+ when 1
57
+ chosen = matches.first
58
+ else
59
+ puts "Ambiguous: #{matches.join(' | ')}"
60
+ puts "Try again.\n"
61
+ end
43
62
  end
44
63
  end
64
+ puts "Accepted: #{chosen}"
45
65
  chosen
46
66
  end
47
67
 
48
68
  def self.player_prompt(msg = nil)
49
69
  print msg + ' ' if msg
50
70
  print '> '
51
- $stdin.gets(chomp: true)
71
+ $stdin.gets.chomp
52
72
  end
53
-
54
- # per http://www.traveller-srd.com/core-rules/skills/
55
- SKILLS = {
56
- admin: nil,
57
- advocate: nil,
58
-
59
- animals_group: nil,
60
- animals_riding: nil,
61
- animals_veterinary: nil,
62
- animals_training: nil,
63
- animals_farming: nil,
64
-
65
- athletics_group: nil,
66
- athletics_coordination: nil,
67
- athletics_endurance: nil,
68
- athletics_strength: nil,
69
- athletics_flying: nil,
70
-
71
- art_group: nil,
72
- art_acting: nil,
73
- art_dance: nil,
74
- art_holography: nil,
75
- art_instrument: nil,
76
- art_sculpting: nil,
77
- art_writing: nil,
78
-
79
- astrogation: nil,
80
- battle_dress: nil,
81
- broker: nil,
82
- carouse: nil,
83
- comms: nil,
84
- computers: nil,
85
- deception: nil,
86
- diplomat: nil,
87
-
88
- drive_group: nil,
89
- drive_hovercraft: nil,
90
- drive_mole: nil,
91
- drive_tracked: nil,
92
- drive_walker: nil,
93
- drive_wheeled: nil,
94
-
95
- engineer_group: nil,
96
- engineer_manoeuvre: nil,
97
- engineer_jump_drive: nil,
98
- engineer_electronics: nil,
99
- engineer_life_support: nil,
100
- engineer_power: nil,
101
-
102
- explosives: nil,
103
-
104
- flyer_group: nil,
105
- flyer_airship: nil,
106
- flyer_grav: nil,
107
- flyer_rotor: nil,
108
- flyer_wing: nil,
109
-
110
- gambler: nil,
111
-
112
- gunner_group: nil,
113
- gunner_turrets: nil,
114
- gunner_ortillery: nil,
115
- gunner_screens: nil,
116
- gunner_capital_weapons: nil,
117
-
118
- gun_combat_group: nil,
119
- gun_combat_slug_rifle: nil,
120
- gun_combat_slug_pistol: nil,
121
- gun_combat_shotgun: nil,
122
- gun_combat_energy_rifle: nil,
123
- gun_combat_energy_pistol: nil,
124
-
125
- heavy_weapons_group: nil,
126
- heavy_weapons_launchers: nil,
127
- heavy_weapons_man_portable_artillery: nil,
128
- heavy_weapons_field_artillery: nil,
129
-
130
- investigate: nil,
131
- jack_of_all_trades: nil,
132
-
133
- language_group: nil,
134
- language_anglic: nil,
135
-
136
- leadership: nil,
137
-
138
- life_sciences_group: nil,
139
- life_sciences_biology: nil,
140
- life_sciences_cybernetics: nil,
141
- life_sciences_genetics: nil,
142
- life_sciences_psionicology: nil,
143
-
144
- mechanic: nil,
145
- medic: nil,
146
-
147
- melee_group: nil,
148
- melee_unarmed_combat: nil,
149
- melee_blade: nil,
150
- melee_bludgeon: nil,
151
- melee_natural_weapons: nil,
152
-
153
- navigation: nil,
154
- persuade: nil,
155
-
156
- pilot_group: nil,
157
- pilot_small_craft: nil,
158
- pilot_spacecraft: nil,
159
- pilot_capital_ships: nil,
160
-
161
- physical_sciences_group: nil,
162
- physical_sciences_physics: nil,
163
- physical_sciences_chemistry: nil,
164
- physical_sciences_electronics: nil,
165
-
166
- recon: nil,
167
- remote_operations: nil,
168
-
169
- seafarer_group: nil,
170
- seafarer_sail: nil,
171
- seafarer_submarine: nil,
172
- seafarer_ocean_ships: nil,
173
- seafarer_motorboats: nil,
174
-
175
- sensors: nil,
176
-
177
- social_sciences_group: nil,
178
- social_sciences_archeology: nil,
179
- social_sciences_economics: nil,
180
- social_sciences_history: nil,
181
- social_sciences_linguistics: nil,
182
- social_sciences_philosophy: nil,
183
- social_sciences_psychology: nil,
184
- social_sciences_sophontology: nil,
185
-
186
- space_sciences_group: nil,
187
- space_sciences_planetology: nil,
188
- space_sciences_robotics: nil,
189
- space_sciences_xenology: nil,
190
-
191
- stealth: nil,
192
- steward: nil,
193
- streetwise: nil,
194
- survival: nil,
195
-
196
- tactics_group: nil,
197
- tactics_military: nil,
198
- tactics_ground: nil,
199
- tactics_naval: nil,
200
-
201
- trade_group: nil,
202
- trade_biologicals: nil,
203
- trade_civil_engineering: nil,
204
- trade_space_construction: nil,
205
- trade_hydroponics: nil,
206
- trade_polymers: nil,
207
-
208
- vacc_suit: nil,
209
- zero_g: nil,
210
- }
211
73
  end
212
74
 
213
75
  # compatibility stuff