dopairb 0.2.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.
- checksums.yaml +4 -4
- data/README.md +41 -9
- 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 +50 -6
- 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 +57 -1
- data/spec.md +3 -3
- metadata +10 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cbc57c42e0e25caed6e783bf761e9edda9822c1d853fa4d55c144ce6704a875e
|
|
4
|
+
data.tar.gz: a3d81e85abe05052cbbf99907162215b4d27796fd0ed14827d83190c0252672e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9522cb64cb142280f6ed6070e0e92884206fb0a7eccd1d289202c64ed9cb28a2cbb14d291a686362ce5797654910dec25f91ab2383193e82782cbd9bcceb7943
|
|
7
|
+
data.tar.gz: 6cae99bcfcdcf7419a2e943fda49cbb97f8603a7e7a09d17f23bf5beb165b8ab85e6cea0f99c6d20f0daa6304dcb13341c5ef9f2781e635642003a4e6dd4f11f
|
data/README.md
CHANGED
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
An interactive Ruby shell that answers every keystroke, evaluation, result and exception with loud terminal effects.
|
|
4
4
|
It is built as an IRB extension. The design spec is [spec.md](spec.md) (in Japanese).
|
|
5
5
|
|
|
6
|
+
https://github.com/user-attachments/assets/b45e4a97-ffde-4fb2-be73-ef69d5c8af42
|
|
7
|
+
|
|
8
|
+
A 70-second session with sound (`dopairb`, the defaults); also in [docs/dopairb-demo.mp4](docs/dopairb-demo.mp4). The scripts that recorded it are in [docs/recording](docs/recording).
|
|
9
|
+
|
|
6
10
|
```
|
|
7
11
|
dopairb(main):001> (1..100).sum COMBO 00 SCORE 33 [⣿⣿⣀⣀⣀⣀⣀⣀] x1.5
|
|
8
12
|
⠂⠌⠟⠃ <- sparks, afterglow and HUD on every keystroke
|
|
@@ -29,22 +33,29 @@ To try it without installing, run `ruby exe/dopairb` in the checkout.
|
|
|
29
33
|
## Usage
|
|
30
34
|
|
|
31
35
|
```console
|
|
32
|
-
$ dopairb # start it (IRB options are passed through)
|
|
36
|
+
$ dopairb # start it, at full blast with sound (IRB options are passed through)
|
|
33
37
|
$ dopairb --calm # quieter: low intensity, no flash, shorter effects
|
|
34
|
-
$ dopairb --max # everything at maximum; the biggest moments go full screen
|
|
35
38
|
$ dopairb --party # max + full-screen flash + sound effects
|
|
36
|
-
$ dopairb --
|
|
39
|
+
$ dopairb --intensity=normal --no-sound # the tamer pre-0.4.0 defaults
|
|
37
40
|
$ dopairb --no-flash # suppress flashing, available from the very first start
|
|
38
41
|
$ DOPAIRB="intensity=low,flash=off" dopairb
|
|
39
42
|
```
|
|
40
43
|
|
|
41
|
-
To
|
|
44
|
+
To turn your regular `irb` into dopairb, add this to `~/.irbrc`:
|
|
42
45
|
|
|
43
46
|
```ruby
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
begin
|
|
48
|
+
require "dopairb"
|
|
49
|
+
Dopairb.enable # also e.g. Dopairb.enable(sound: :off, intensity: :normal)
|
|
50
|
+
rescue LoadError
|
|
51
|
+
# a Ruby without dopairb keeps plain irb
|
|
52
|
+
end
|
|
46
53
|
```
|
|
47
54
|
|
|
55
|
+
The title (and the loading screen on a cold start) plays right before the first prompt, as with the `dopairb` command.
|
|
56
|
+
Sessions opened by `binding.irb` get the effects too; `Dopairb.enable(binding_irb: false)` keeps them plain.
|
|
57
|
+
Settings for a single run (`dopairb` options and `$DOPAIRB`) win over the ones given to `Dopairb.enable` in `.irbrc`.
|
|
58
|
+
|
|
48
59
|
Inside a session, the `dopa` command changes settings:
|
|
49
60
|
|
|
50
61
|
```
|
|
@@ -53,6 +64,8 @@ dopa off|low|normal|max
|
|
|
53
64
|
dopa calm / dopa party
|
|
54
65
|
dopa demo play every effect once (does not change your score)
|
|
55
66
|
dopa loading show the loading screen
|
|
67
|
+
dopa gallery the bonus art you have collected (`dopa gallery starry_night` shows one)
|
|
68
|
+
dopa gallery tour a full-screen slideshow of all 100; uncollected ones appear as silhouettes
|
|
56
69
|
dopa flash=off duration=0.5 ...
|
|
57
70
|
```
|
|
58
71
|
|
|
@@ -60,10 +73,10 @@ dopa flash=off duration=0.5 ...
|
|
|
60
73
|
|
|
61
74
|
| Setting | Values | Default | Meaning |
|
|
62
75
|
| --- | --- | --- | --- |
|
|
63
|
-
| `intensity` | off / low / normal / max |
|
|
76
|
+
| `intensity` | off / low / normal / max | max | Overall amount. `off` behaves exactly like plain IRB |
|
|
64
77
|
| `motion` | on / off | on | `off` disables animation (badges only) |
|
|
65
78
|
| `flash` | off / soft / full | soft | `soft` flashes only the effect area, `full` inverts the whole screen |
|
|
66
|
-
| `sound` | off / bell / sfx |
|
|
79
|
+
| `sound` | off / bell / sfx | sfx | `sfx` plays synthesized sound effects, `bell` rings the terminal bell on big moments |
|
|
67
80
|
| `hud` | on / off | on | COMBO / SCORE / CHARGE on the right of the prompt line |
|
|
68
81
|
| `duration` | 0.1–5 | 1.0 | Length multiplier for every effect |
|
|
69
82
|
| `color` | auto / truecolor / 256 / 16 / none | auto | `none` when `NO_COLOR` is set |
|
|
@@ -71,6 +84,7 @@ dopa flash=off duration=0.5 ...
|
|
|
71
84
|
| `charge` | 0–0.3 s | 0.12 | Wind-up before a fast result lands; 0 disables it |
|
|
72
85
|
| `intro` | on / off | on | Title animation at startup |
|
|
73
86
|
| `keys` | on / off | on | Per-keystroke effects |
|
|
87
|
+
| `binding_irb` | on / off | on | Effects in sessions opened by `binding.irb` too |
|
|
74
88
|
|
|
75
89
|
## Effects
|
|
76
90
|
|
|
@@ -80,6 +94,7 @@ When several fire at once, only the biggest plays. The smaller ones are folded i
|
|
|
80
94
|
| Moment | Effect |
|
|
81
95
|
| --- | --- |
|
|
82
96
|
| Typing | A spark trail right of the cursor, a brief glow on the typed character, embers falling below, a x1.0–x3.0 multiplier |
|
|
97
|
+
| Typing streak of 8, 16, 32 … 1024 keys | Powers of two, in big half-block letters under the input: `8-BIT RUSH!`, `16-BIT COMBO!`, `32-BIT BLAZE!!`, `64-BIT OVERDRIVE!!`, `128-BIT HYPER MODE!!`, `0x100 OVERFLOW!!!`, `2^9 GODSPEED!!!`, `1KiB TYPING LEGEND!!!`, with a bonus of 4 points per key |
|
|
83
98
|
| Resuming after a pause | Reignites with `CHARGE!` |
|
|
84
99
|
| Backspace / Delete | The deleted character shatters and falls. The next keystroke shows `RECOVERY` |
|
|
85
100
|
| Cursor movement | A light streak in the direction of travel |
|
|
@@ -104,12 +119,29 @@ When several fire at once, only the biggest plays. The smaller ones are folded i
|
|
|
104
119
|
| Other exceptions | A red flash and a glitching `FAILED` |
|
|
105
120
|
| Ctrl-C | The stored charge scatters, `INTERRUPTED` |
|
|
106
121
|
| First eval / COMBO 5, 10, 25… / eval count 10, 50, 100… | `FIRST HIT!` / `COMBO 10!` / `100 EVALS` |
|
|
122
|
+
| Lucky success (about 1 in 10, up to 1 in 5 with a full CHARGE) | `CRITICAL!!`: a slash, a red flash and x2 / x4 / x8 points |
|
|
123
|
+
| Very lucky success (1 in 67) | `JACKPOT!!`: three slot reels stop on 7-7-7, coins rain, x16 points |
|
|
124
|
+
| COMBO 10 and beyond | `FEVER TIME!`: every point counts double until the next error; the HUD glows in rainbow `FEVER!` |
|
|
125
|
+
| Beating your all-time best combo | `BEST COMBO!` |
|
|
126
|
+
| Level up, jackpot, or a lucky drop (see [Career](#career)) | `LEVEL UP!` / `JACKPOT PRIZE` / `ART DROP!`, then a famous painting is unveiled full screen as bonus art |
|
|
107
127
|
| Numeric record (more than doubled) | `NEW RECORD` |
|
|
108
128
|
| Success right after an error | `FIXED!` (after one error) / `COMEBACK!` (after a streak), both huge |
|
|
109
129
|
| Exit | A full-screen finale: the title drops, stats slam in one by one, the total score counts up under a drum roll, a rank is stamped (S/A/B/C with a cheerful title), then fireworks. The result is left behind as a plain table |
|
|
110
130
|
|
|
111
131
|
Error effects name the kind of error. They never make fun of the failure.
|
|
112
132
|
|
|
133
|
+
## Career
|
|
134
|
+
|
|
135
|
+
Between sessions dopairb keeps a small profile: lifetime XP (the sum of your scores), level, best score, best combo, a day streak and the bonus art you have collected.
|
|
136
|
+
Levels need powers of two: LV 2 at 512 XP, LV 3 at 1,024, LV 4 at 2,048, and so on.
|
|
137
|
+
Each level-up and each jackpot unveils a masterpiece full screen, and now and then (about 1 success in 50) one simply drops: `ART DROP!`. There are 100 public-domain works to collect, from the Lascaux horses and Hokusai's *Great Wave* through Vermeer, Turner and van Gogh to Klimt, Kandinsky and Mondrian, all painted procedurally in half blocks (no image files ship with the gem). New ones come first; `dopa gallery` lists yours. (Spoilers: all 100 are in [docs/gallery.png](docs/gallery.png).)
|
|
138
|
+
The startup title shows your level and day streak, and the finale shows `NEW BEST!` and an XP bar.
|
|
139
|
+
|
|
140
|
+
The profile is `~/.local/state/dopairb/profile.json` (`$XDG_STATE_HOME` is honored). Set `DOPAIRB_PROFILE=path` to use another file, or `DOPAIRB_PROFILE=off` to keep nothing; levels are then not shown.
|
|
141
|
+
Non-interactive runs never touch it.
|
|
142
|
+
|
|
143
|
+
`DOPAIRB_SEED=n` makes the luck (critical hits, jackpots, particles) repeatable, which helps when recording a demo.
|
|
144
|
+
|
|
113
145
|
## Sound
|
|
114
146
|
|
|
115
147
|
With `sound=sfx`, every effect has a sound: key clicks, `NICE!` chimes, a comet ping, a stamp thump, a count-up that ends in a boom, fanfares and explosions for the big moments, a buzz or crack for errors.
|
|
@@ -172,7 +204,7 @@ The rest is terminal-independent logic:
|
|
|
172
204
|
- Scores live for the session only and are not saved.
|
|
173
205
|
- Effects use Unicode. Any glyph that is not one column wide is swapped for ASCII or braille at runtime.
|
|
174
206
|
- Start it with the `dopairb` command, or call `Dopairb.enable` in `.irbrc`.
|
|
175
|
-
- IRB sessions entered via `binding.irb` get the same Context hook. The result screen appears only when the outermost session ends.
|
|
207
|
+
- IRB sessions entered via `binding.irb` get the same Context hook (unless `binding_irb=off`; detected with `IRB::Context#from_binding?`). The result screen appears only when the outermost session ends.
|
|
176
208
|
|
|
177
209
|
## Performance
|
|
178
210
|
|
data/lib/dopairb/cli.rb
CHANGED
|
@@ -15,13 +15,13 @@ module Dopairb
|
|
|
15
15
|
o.separator ""
|
|
16
16
|
o.separator "IRB with fireworks. Every other option is handed to IRB."
|
|
17
17
|
o.separator ""
|
|
18
|
-
o.on("--intensity=LEVEL", Config::INTENSITIES.map(&:to_s), "off / low / normal (default)
|
|
18
|
+
o.on("--intensity=LEVEL", Config::INTENSITIES.map(&:to_s), "off / low / normal / max (default)") { |v| settings << [:intensity, v] }
|
|
19
19
|
o.on("--calm", "low intensity, no flash, shorter effects") { settings.concat([[:intensity, :low], [:flash, :off], [:duration, 0.7]]) }
|
|
20
|
-
o.on("--max", "everything at maximum") { settings << [:intensity, :max] }
|
|
20
|
+
o.on("--max", "everything at maximum (the default)") { settings << [:intensity, :max] }
|
|
21
21
|
o.on("--party", "max + full-screen flash + sound effects") { settings.concat([[:intensity, :max], [:flash, :full], [:sound, :sfx]]) }
|
|
22
22
|
o.on("--[no-]flash[=MODE]", "flash: off / soft (default) / full") { |v| settings << [:flash, v.nil? ? :soft : (v == false ? :off : v)] }
|
|
23
23
|
o.on("--[no-]motion", "animations (default on)") { |v| settings << [:motion, v] }
|
|
24
|
-
o.on("--[no-]sound[=MODE]", "sound: off
|
|
24
|
+
o.on("--[no-]sound[=MODE]", "sound: off / bell / sfx (default)") { |v| settings << [:sound, v.nil? ? :sfx : (v == false ? :off : v)] }
|
|
25
25
|
o.on("--[no-]hud", "COMBO / SCORE next to the prompt (default on)") { |v| settings << [:hud, v] }
|
|
26
26
|
o.on("--[no-]keys", "per-keystroke effects (default on)") { |v| settings << [:keys, v] }
|
|
27
27
|
o.on("--[no-]intro", "title animation (default on)") { |v| settings << [:intro, v] }
|
|
@@ -61,9 +61,7 @@ module Dopairb
|
|
|
61
61
|
ours, irb_args = split_args(argv.dup)
|
|
62
62
|
settings = []
|
|
63
63
|
parser(settings).parse!(ours)
|
|
64
|
-
settings.each
|
|
65
|
-
k == :string ? Dopairb.config.apply_string(v) : Dopairb.config.set(k, v)
|
|
66
|
-
end
|
|
64
|
+
settings.each { |k, v| Dopairb.override(k, v) }
|
|
67
65
|
run_irb(irb_args)
|
|
68
66
|
rescue OptionParser::ParseError, ArgumentError => e
|
|
69
67
|
warn "dopairb: #{e.message}"
|
data/lib/dopairb/config.rb
CHANGED
|
@@ -21,15 +21,16 @@ module Dopairb
|
|
|
21
21
|
charge: "seconds (max 0.3) of wind-up before a fast result lands; 0 disables",
|
|
22
22
|
intro: "on / off -- title animation at startup",
|
|
23
23
|
keys: "on / off -- per-keystroke effects while typing",
|
|
24
|
+
binding_irb: "on / off -- effects in sessions opened by binding.irb too",
|
|
24
25
|
}.freeze
|
|
25
26
|
|
|
26
|
-
attr_reader :intensity, :motion, :flash, :sound, :hud, :duration, :color, :trail, :charge, :intro, :keys
|
|
27
|
+
attr_reader :intensity, :motion, :flash, :sound, :hud, :duration, :color, :trail, :charge, :intro, :keys, :binding_irb
|
|
27
28
|
|
|
28
29
|
def initialize
|
|
29
|
-
@intensity = :
|
|
30
|
+
@intensity = :max
|
|
30
31
|
@motion = true
|
|
31
32
|
@flash = :soft
|
|
32
|
-
@sound = :
|
|
33
|
+
@sound = :sfx
|
|
33
34
|
@hud = true
|
|
34
35
|
@duration = 1.0
|
|
35
36
|
@color = :auto
|
|
@@ -37,6 +38,7 @@ module Dopairb
|
|
|
37
38
|
@charge = 0.12
|
|
38
39
|
@intro = true
|
|
39
40
|
@keys = true
|
|
41
|
+
@binding_irb = true
|
|
40
42
|
end
|
|
41
43
|
|
|
42
44
|
def level
|
|
@@ -60,7 +62,7 @@ module Dopairb
|
|
|
60
62
|
when :sound
|
|
61
63
|
v = bool_or_nil(value)
|
|
62
64
|
@sound = v.nil? ? enum(value, SOUNDS, name) : (v ? :sfx : :off)
|
|
63
|
-
when :motion, :hud, :intro, :keys
|
|
65
|
+
when :motion, :hud, :intro, :keys, :binding_irb
|
|
64
66
|
v = bool_or_nil(value)
|
|
65
67
|
raise ArgumentError, "#{name} expects on/off, got #{value.inspect}" if v.nil?
|
|
66
68
|
instance_variable_set(:"@#{name}", v)
|
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
|