traveller_rpg 0.0.1.1 → 0.1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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