dopairb 0.1.0 → 0.4.0

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/lib/dopairb.rb CHANGED
@@ -19,18 +19,24 @@ require_relative "dopairb/fx"
19
19
  require_relative "dopairb/stage"
20
20
  require_relative "dopairb/game"
21
21
  require_relative "dopairb/probe"
22
+ require_relative "dopairb/profile"
23
+ require_relative "dopairb/gallery"
22
24
  require_relative "dopairb/scene"
23
25
  require_relative "dopairb/scenes/success"
24
26
  require_relative "dopairb/scenes/failure"
25
27
  require_relative "dopairb/scenes/session"
28
+ require_relative "dopairb/scenes/finale"
29
+ require_relative "dopairb/scenes/bonus"
26
30
  require_relative "dopairb/director"
27
31
  require_relative "dopairb/output_tap"
28
32
  require_relative "dopairb/charge_line"
33
+ require_relative "dopairb/sound"
29
34
  require_relative "dopairb/input_fx"
30
35
  require_relative "dopairb/reline_adapter"
31
36
  require_relative "dopairb/irb_adapter"
32
37
  require_relative "dopairb/session"
33
38
  require_relative "dopairb/demo"
39
+ require_relative "dopairb/tour"
34
40
 
35
41
  # dopairb: IRB where every keystroke, result and exception gets a show.
36
42
  #
@@ -42,7 +48,10 @@ module Dopairb
42
48
  def config
43
49
  @config ||= begin
44
50
  c = Config.new
45
- c.apply_string(ENV["DOPAIRB"]) if ENV["DOPAIRB"]
51
+ if ENV["DOPAIRB"]
52
+ c.apply_string(ENV["DOPAIRB"])
53
+ overrides << [:string, ENV["DOPAIRB"]]
54
+ end
46
55
  c
47
56
  rescue ArgumentError => e
48
57
  warn "dopairb: ignoring DOPAIRB=#{ENV['DOPAIRB'].inspect}: #{e.message}"
@@ -50,14 +59,29 @@ module Dopairb
50
59
  end
51
60
  end
52
61
 
62
+ # Settings for this run only ($DOPAIRB, dopairb options). They win over
63
+ # Dopairb.enable(...) in .irbrc, which IRB loads after they are applied.
64
+ def overrides = (@overrides ||= [])
65
+
66
+ def override(name, value)
67
+ name == :string ? config.apply_string(value) : config.set(name, value)
68
+ overrides << [name, value]
69
+ end
70
+
53
71
  # Hook into IRB. Safe to call more than once; options are config settings.
54
72
  def enable(intro: nil, **settings)
55
73
  settings.each { |k, v| config.set(k, v) }
56
74
  config.set(:intro, intro) unless intro.nil?
75
+ overrides.each { |k, v| k == :string ? config.apply_string(v) : config.set(k, v) }
57
76
  @session ||= Session.new(config)
58
77
  if IrbAdapter.install
59
78
  RelineAdapter.install(@session.fx)
60
79
  OutputTap.install
80
+ if active?
81
+ # decided before warming up: the first run shows the loading screen
82
+ @session.cold_start = Sound.available? && !Sound.ready?(:intro, config.duration)
83
+ Sound.warm_up(config.duration)
84
+ end
61
85
  else
62
86
  warn "dopairb: IRB #{defined?(IRB::VERSION) ? IRB::VERSION : '?'} is not supported; running plain IRB"
63
87
  end
@@ -81,33 +105,78 @@ module Dopairb
81
105
  "normal" => { intensity: :normal },
82
106
  "max" => { intensity: :max },
83
107
  "calm" => { intensity: :low, flash: :off, duration: 0.7 },
84
- "party" => { intensity: :max, flash: :full, sound: true, duration: 1.0 },
108
+ "party" => { intensity: :max, flash: :full, sound: :sfx, duration: 1.0 },
85
109
  }.freeze
86
110
 
87
111
  HELP = <<~TXT
88
112
  dopa show settings and session stats
89
113
  dopa off|low|normal|max
90
114
  dopa calm low intensity, no flash, shorter
91
- dopa party max intensity, full-screen flash, bell
115
+ dopa party max intensity, full-screen flash, sound effects
92
116
  dopa demo play every effect once
117
+ dopa loading show the loading screen
118
+ dopa gallery the bonus art you have collected (dopa gallery NAME shows one)
119
+ dopa gallery tour a slideshow of all 100 (uncollected ones as silhouettes)
93
120
  dopa KEY=VALUE ... change one setting, e.g. `dopa flash=off duration=0.5`
94
121
 
95
122
  settings:
96
123
  TXT
97
124
 
125
+ def gallery(name)
126
+ owned = ((@session&.profile&.gallery || []) + (@session&.collected || [])).uniq
127
+ if name == "tour"
128
+ return puts("dopairb: the tour needs a terminal") unless Term.interactive?
129
+ return Tour.run(config, owned)
130
+ end
131
+ if name.nil?
132
+ puts "dopairb gallery: #{owned.size}/#{Gallery::PIECES.size} (level-ups, jackpots and lucky ART DROPs bring more)"
133
+ Gallery::PIECES.each do |p|
134
+ puts format(" %-32s %s -- %s, %s", p.id, p.title, p.artist, p.year) if owned.include?(p.id)
135
+ end
136
+ rest = Gallery::PIECES.size - owned.size
137
+ puts " ... #{rest} more to discover" if rest > 0
138
+ puts " `dopa gallery NAME` shows one; `dopa gallery tour` walks through all #{Gallery::PIECES.size}"
139
+ return
140
+ end
141
+ piece = Gallery.find(name.to_sym) if Gallery.ids.include?(name.to_sym)
142
+ return puts("dopairb: no such piece #{name.inspect}") unless piece
143
+ return puts("dopairb: not unlocked yet -- keep playing!") unless owned.include?(piece.id)
144
+ cols = [Term.cols - 1, 100].min
145
+ w, h = Gallery.fit(piece, cols, [Term.rows - 4, 30].min)
146
+ c = Canvas.new(w, h / 2)
147
+ Gallery.draw(c, piece, 0, 0, w, h, depth: Term.depth(config))
148
+ puts c.render(Term.depth(config))
149
+ puts "\"#{piece.title}\" #{piece.artist}, #{piece.year}"
150
+ end
151
+
98
152
  def command(arg)
99
153
  words = arg.split
100
154
  if words.empty? || words == ["status"]
101
155
  s = @session&.game
102
156
  puts "dopairb #{VERSION} (#{active? ? 'active' : 'inactive: not a terminal or intensity=off'})"
103
157
  puts config.describe
158
+ puts " sound player: #{Sound.backend ? Sound.backend[1].first : '(none; bell only)'} cache: #{Sound.dir}"
104
159
  puts " session: #{s.evals} evals, #{s.successes} hits, #{s.failures} errors, max combo #{s.max_combo}, score #{Fx.number_with_commas(s.score)}" if s
160
+ prof = @session&.profile
161
+ if prof&.persistent?
162
+ lv = s.leveling? ? s.level : prof.level
163
+ xp = s.leveling? ? s.xp : prof.xp
164
+ puts " career: LV #{lv} (#{Fx.number_with_commas(xp)} / #{Fx.number_with_commas(Game.xp_for(lv + 1))} XP), " \
165
+ "best score #{Fx.number_with_commas(prof.best_score)}, best combo #{prof.best_combo}, day streak #{prof.streak}, " \
166
+ "gallery #{prof.gallery.size}/#{Gallery::PIECES.size} (#{prof.path})"
167
+ end
105
168
  elsif words == ["help"]
106
169
  puts HELP + config.describe
107
170
  elsif words == ["demo"]
108
171
  Demo.run(self)
172
+ elsif words[0] == "gallery"
173
+ gallery(words[1])
174
+ elsif words == ["loading"]
175
+ t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
176
+ @session&.director&.loading { Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0 > 2.5 } if active?
109
177
  elsif words.size == 1 && PRESETS.key?(words[0])
110
178
  PRESETS[words[0]].each { |k, v| config.set(k, v) }
179
+ Sound.warm_up(config.duration) if active?
111
180
  puts "dopairb: #{words[0]}"
112
181
  else
113
182
  words.each do |w|
@@ -115,6 +184,7 @@ module Dopairb
115
184
  raise ArgumentError, "expected KEY=VALUE, got #{w.inspect}" unless v
116
185
  config.set(k, v)
117
186
  end
187
+ Sound.warm_up(config.duration) if active?
118
188
  puts "dopairb: " + words.join(" ")
119
189
  end
120
190
  nil
data/spec.md CHANGED
@@ -127,10 +127,10 @@
127
127
 
128
128
  | 設定 | 内容 |
129
129
  | --- | --- |
130
- | `intensity` | `off` / `low` / `normal` / `max`。初期値 `normal` |
130
+ | `intensity` | `off` / `low` / `normal` / `max`。初期値 `max`(v0.4.0 で `normal` から変更) |
131
131
  | `motion` | アニメーションの有無と長さ |
132
132
  | `flash` | 全画面フラッシュの有無。初期値は控えめ |
133
- | `sound` | 端末ベル等を使う場合の明示的な有効化。初期値 `off` |
133
+ | `sound` | `off` / `bell` / `sfx`。初期値 `sfx`(v0.4.0 で `off` から変更。プレイヤーがなければベル) |
134
134
  | `hud` | COMBO・CHARGE・SCORE の表示 |
135
135
  | `duration` | 各演出時間の倍率 |
136
136
  | `color` | 色数と配色。モノクロでも意味が分かる表示 |
@@ -196,7 +196,7 @@ stateDiagram-v2
196
196
 
197
197
  - 常時 HUD を画面上部に固定するか、プロンプト周辺の一時表示にするか。
198
198
  - `puts` などの評価途中の出力をどの範囲まで捕捉・装飾するか。
199
- - スコアをセッション内だけにするか、記録を保存するか。
199
+ - ~~スコアをセッション内だけにするか、記録を保存するか。~~ → 生涯 XP・レベル・ベスト記録・連続日数・ボーナス絵を `~/.local/state/dopairb/profile.json` に保存する(`DOPAIRB_PROFILE=off` で無効)。
200
200
  - 演出素材を Unicode 文字中心にするか、端末によって別の表現を選ぶか。
201
201
  - gem の起動コマンドと、通常の `irb` や `.irbrc` から有効化する方法。
202
202
  - IRB/Reline の対象バージョンと、毎キー描画に必要な拡張点。
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dopairb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Koichi Sasada
@@ -72,19 +72,30 @@ files:
72
72
  - lib/dopairb/director.rb
73
73
  - lib/dopairb/font.rb
74
74
  - lib/dopairb/fx.rb
75
+ - lib/dopairb/gallery.rb
76
+ - lib/dopairb/gallery/impressionism.rb
77
+ - lib/dopairb/gallery/japan.rb
78
+ - lib/dopairb/gallery/modern_ancient.rb
79
+ - lib/dopairb/gallery/renaissance.rb
80
+ - lib/dopairb/gallery/romantic.rb
75
81
  - lib/dopairb/game.rb
76
82
  - lib/dopairb/input_fx.rb
77
83
  - lib/dopairb/irb_adapter.rb
78
84
  - lib/dopairb/output_tap.rb
79
85
  - lib/dopairb/probe.rb
86
+ - lib/dopairb/profile.rb
80
87
  - lib/dopairb/reline_adapter.rb
81
88
  - lib/dopairb/scene.rb
89
+ - lib/dopairb/scenes/bonus.rb
82
90
  - lib/dopairb/scenes/failure.rb
91
+ - lib/dopairb/scenes/finale.rb
83
92
  - lib/dopairb/scenes/session.rb
84
93
  - lib/dopairb/scenes/success.rb
85
94
  - lib/dopairb/session.rb
95
+ - lib/dopairb/sound.rb
86
96
  - lib/dopairb/stage.rb
87
97
  - lib/dopairb/term.rb
98
+ - lib/dopairb/tour.rb
88
99
  - lib/dopairb/version.rb
89
100
  - spec.md
90
101
  homepage: https://github.com/ko1/dopairb