dopairb 0.2.0 → 0.5.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.
- checksums.yaml +4 -4
- data/README.md +77 -9
- data/exe/dopacc +84 -0
- data/lib/dopairb/claude_code.rb +330 -0
- data/lib/dopairb/cli.rb +4 -6
- data/lib/dopairb/config.rb +6 -4
- data/lib/dopairb/demo.rb +9 -2
- data/lib/dopairb/director.rb +35 -5
- data/lib/dopairb/fx.rb +23 -0
- data/lib/dopairb/gallery/impressionism.rb +818 -0
- data/lib/dopairb/gallery/japan.rb +693 -0
- data/lib/dopairb/gallery/modern_ancient.rb +774 -0
- data/lib/dopairb/gallery/renaissance.rb +829 -0
- data/lib/dopairb/gallery/romantic.rb +703 -0
- data/lib/dopairb/gallery.rb +349 -0
- data/lib/dopairb/game.rb +84 -7
- data/lib/dopairb/input_fx.rb +63 -9
- data/lib/dopairb/irb_adapter.rb +8 -0
- data/lib/dopairb/profile.rb +106 -0
- data/lib/dopairb/reline_adapter.rb +1 -0
- data/lib/dopairb/scene.rb +5 -0
- data/lib/dopairb/scenes/bonus.rb +279 -0
- data/lib/dopairb/scenes/failure.rb +1 -0
- data/lib/dopairb/scenes/finale.rb +42 -2
- data/lib/dopairb/scenes/session.rb +21 -3
- data/lib/dopairb/scenes/success.rb +11 -4
- data/lib/dopairb/session.rb +53 -7
- data/lib/dopairb/sound.rb +57 -2
- data/lib/dopairb/term.rb +3 -1
- data/lib/dopairb/tour.rb +89 -0
- data/lib/dopairb/version.rb +1 -1
- data/lib/dopairb.rb +101 -12
- data/spec.md +3 -3
- metadata +13 -1
data/lib/dopairb/demo.rb
CHANGED
|
@@ -7,8 +7,9 @@ module Dopairb
|
|
|
7
7
|
|
|
8
8
|
module_function
|
|
9
9
|
|
|
10
|
-
def ev(kind, info, flags: [], gain: 120, combo: 3, streak: 0)
|
|
11
|
-
E.new(kind: kind, tier: nil, info: info, flags: flags, gain: gain, combo: combo, streak: streak, score: 12_480
|
|
10
|
+
def ev(kind, info, flags: [], gain: 120, combo: 3, streak: 0, mult: 1, level: 5)
|
|
11
|
+
E.new(kind: kind, tier: nil, info: info, flags: flags, gain: gain, combo: combo, streak: streak, score: 12_480,
|
|
12
|
+
mult: mult, level: level)
|
|
12
13
|
end
|
|
13
14
|
|
|
14
15
|
def cases
|
|
@@ -33,6 +34,12 @@ module Dopairb
|
|
|
33
34
|
["1 + 1", ev(:success, { type: :integer, value: 2 }, flags: [:comeback], combo: 1, streak: 5, gain: 600)],
|
|
34
35
|
["loop {}", ev(:interrupt, { charge: 60 })],
|
|
35
36
|
["2**128", ev(:success, { type: :integer, value: 2**128 }, flags: [:new_record, :record_jump], gain: 420)],
|
|
37
|
+
["[3, 1, 2].sort", ev(:success, { type: :array, size: 3 }, flags: [:critical], gain: 960, mult: 8)],
|
|
38
|
+
["12.fdiv(5)", ev(:success, { type: :float, value: 2.4 }, flags: [:fever], combo: 12, gain: 1100, mult: 2)],
|
|
39
|
+
["21.0 * 2", ev(:success, { type: :float, value: 42.0 }, flags: [:fever, :combo_best], combo: 13, gain: 1200, mult: 2)],
|
|
40
|
+
["rand(777)", ev(:success, { type: :integer, value: 777 }, flags: [:jackpot], gain: 6_400, mult: 16)],
|
|
41
|
+
["'level' * 2", ev(:success, { type: :string, length: 10, head: "levellevel", art: :great_wave, art_new: true, gallery: [1, 5] },
|
|
42
|
+
flags: [:level_up], gain: 200, level: 6)],
|
|
36
43
|
]
|
|
37
44
|
end
|
|
38
45
|
|
data/lib/dopairb/director.rb
CHANGED
|
@@ -6,7 +6,7 @@ module Dopairb
|
|
|
6
6
|
class Director
|
|
7
7
|
FPS = 40.0
|
|
8
8
|
|
|
9
|
-
attr_reader :config
|
|
9
|
+
attr_reader :config, :rng
|
|
10
10
|
|
|
11
11
|
def initialize(config, rng: Random.new)
|
|
12
12
|
@config = config
|
|
@@ -50,20 +50,35 @@ module Dopairb
|
|
|
50
50
|
Scenes::Banner.new(ctx, event, text: text, palette: palette, mega: true, rainbow: rainbow, sub: sub)
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
|
-
|
|
53
|
+
full = lambda { self.ctx(rows: ctx.rows, cols: ctx.w + 1).tap { |c| c.depth = ctx.depth } }
|
|
54
|
+
if event.flag?(:jackpot)
|
|
55
|
+
Scenes::Jackpot.new(ctx, event)
|
|
56
|
+
elsif event.flag?(:level_up) || (event.flag?(:art_drop) && info[:art])
|
|
57
|
+
if @config.motion && @config.level >= 2 && info[:art] && Scenes::Masterpiece.fits?(ctx.rows, ctx.w + 1)
|
|
58
|
+
Scenes::Masterpiece.new(full.(), event)
|
|
59
|
+
elsif event.flag?(:level_up)
|
|
60
|
+
mega.("LEVEL UP!", Color::NEON, rainbow: true, sub: "LV #{event.level}")
|
|
61
|
+
else
|
|
62
|
+
Scenes::Banner.new(ctx, event, text: "ART DROP!", palette: Color::GOLD, sub: Gallery.find(info[:art])&.title)
|
|
63
|
+
end
|
|
64
|
+
elsif event.flag?(:comeback)
|
|
54
65
|
mega.(event.streak >= 2 ? "COMEBACK!" : "FIXED!", Color::GOLD, rainbow: event.streak >= 2)
|
|
55
66
|
elsif event.flag?(:eval_milestone)
|
|
56
67
|
mega.("#{Fx.number_with_commas(event_evals(event))} EVALS", Color::NEON, rainbow: true)
|
|
57
68
|
elsif event.flag?(:record_jump)
|
|
58
69
|
mega.("NEW RECORD", Color::GOLD, rainbow: true, sub: number_text(info))
|
|
59
70
|
elsif event.flag?(:combo_mega)
|
|
60
|
-
mega.("COMBO #{event.combo}!", Color::FIRE, rainbow: event.combo >= 50)
|
|
71
|
+
mega.("COMBO #{event.combo}!", Color::FIRE, rainbow: event.combo >= 50, sub: event.flag?(:fever_start) ? "FEVER TIME! ALL POINTS x2" : nil)
|
|
72
|
+
elsif event.flag?(:combo_best)
|
|
73
|
+
Scenes::Banner.new(ctx, event, text: "BEST COMBO!", palette: Color::FIRE, sub: "ALL-TIME BEST #{event.combo}")
|
|
61
74
|
elsif info[:type] == :definition
|
|
62
75
|
Scenes::Unlock.new(ctx, event)
|
|
63
76
|
elsif event.flag?(:combo_milestone) && event.combo >= 5
|
|
64
77
|
Scenes::Banner.new(ctx, event, text: "COMBO #{event.combo}", palette: Color::FIRE)
|
|
65
78
|
elsif event.flag?(:first_hit)
|
|
66
79
|
Scenes::Banner.new(ctx, event, text: "FIRST HIT!", palette: Color::ICE)
|
|
80
|
+
elsif event.flag?(:critical) && !counter?(event)
|
|
81
|
+
Scenes::Critical.new(ctx, event)
|
|
67
82
|
elsif event.flag?(:output_rain)
|
|
68
83
|
Scenes::Rain.new(ctx, event)
|
|
69
84
|
else
|
|
@@ -71,6 +86,11 @@ module Dopairb
|
|
|
71
86
|
end
|
|
72
87
|
end
|
|
73
88
|
|
|
89
|
+
def counter?(event)
|
|
90
|
+
v = event.info[:value]
|
|
91
|
+
%i[integer float].include?(event.info[:type]) && (v.abs >= 10_000 || event.flag?(:new_record))
|
|
92
|
+
end
|
|
93
|
+
|
|
74
94
|
def value_scene(event, ctx)
|
|
75
95
|
info = event.info
|
|
76
96
|
case info[:type]
|
|
@@ -92,9 +112,18 @@ module Dopairb
|
|
|
92
112
|
c.input = nil if input && input.size + 12 > c.rows
|
|
93
113
|
scene = scene_for(event, c)
|
|
94
114
|
play(scene)
|
|
115
|
+
encore(event, c) if Scenes::Jackpot === scene
|
|
95
116
|
scene
|
|
96
117
|
end
|
|
97
118
|
|
|
119
|
+
# A jackpot pays out a masterpiece too: too good to fold away.
|
|
120
|
+
def encore(event, c)
|
|
121
|
+
return unless event.info[:art] && @config.motion && @config.level >= 2
|
|
122
|
+
return if Term.input_pending? || !Scenes::Masterpiece.fits?(c.rows, c.w + 1)
|
|
123
|
+
full = ctx(rows: c.rows, cols: c.w + 1).tap { |x| x.depth = c.depth }
|
|
124
|
+
play(Scenes::Masterpiece.new(full, event))
|
|
125
|
+
end
|
|
126
|
+
|
|
98
127
|
# Cute loading screen until ready.call is true (e.g. the intro sound exists).
|
|
99
128
|
def loading(&ready)
|
|
100
129
|
return if @config.off?
|
|
@@ -105,9 +134,9 @@ module Dopairb
|
|
|
105
134
|
raise if SystemExit === e || (SignalException === e && !(Interrupt === e))
|
|
106
135
|
end
|
|
107
136
|
|
|
108
|
-
def intro
|
|
137
|
+
def intro(career = nil)
|
|
109
138
|
return if @config.off? || !@config.intro
|
|
110
|
-
play(Scenes::Intro.new(ctx), force_trail: true)
|
|
139
|
+
play(Scenes::Intro.new(ctx, career), force_trail: true)
|
|
111
140
|
end
|
|
112
141
|
|
|
113
142
|
def outro(stats)
|
|
@@ -132,6 +161,7 @@ module Dopairb
|
|
|
132
161
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
|
133
162
|
raise if SystemExit === e || (SignalException === e && !(Interrupt === e))
|
|
134
163
|
# Never let an effect break the REPL; Ctrl-C during an effect just skips it.
|
|
164
|
+
Dopairb.debug(e) unless Interrupt === e
|
|
135
165
|
end
|
|
136
166
|
|
|
137
167
|
private
|
data/lib/dopairb/fx.rb
CHANGED
|
@@ -111,6 +111,29 @@ module Dopairb
|
|
|
111
111
|
width * scale
|
|
112
112
|
end
|
|
113
113
|
|
|
114
|
+
# The big font squeezed into three rows with half blocks (▀ ▄ █).
|
|
115
|
+
# Returns the width drawn, or nil when the terminal lacks the glyphs.
|
|
116
|
+
def half_text(canvas, text, x, y, color_fn)
|
|
117
|
+
up = Term.glyph("▀", nil)
|
|
118
|
+
down = Term.glyph("▄", nil)
|
|
119
|
+
full = Term.glyph("█", nil)
|
|
120
|
+
return nil unless up && down && full
|
|
121
|
+
bm = Font.bitmap(text)
|
|
122
|
+
width = bm[0].size
|
|
123
|
+
3.times do |r|
|
|
124
|
+
top = bm[r * 2]
|
|
125
|
+
bot = bm[r * 2 + 1] || []
|
|
126
|
+
width.times do |cx|
|
|
127
|
+
a = top[cx]
|
|
128
|
+
b = bot[cx]
|
|
129
|
+
next unless a || b
|
|
130
|
+
ch = a && b ? full : (a ? up : down)
|
|
131
|
+
canvas.put(x + cx, y + r, ch, color_fn.(cx, r, cx.fdiv([width - 1, 1].max)))
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
width
|
|
135
|
+
end
|
|
136
|
+
|
|
114
137
|
def text_fits?(text, w, scale = 1)
|
|
115
138
|
Font.width(text, scale: scale) <= w
|
|
116
139
|
end
|