road_to_rubykaigi 0.1.0 → 0.2.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/.standard.yml +11 -0
- data/CHANGELOG.md +6 -0
- data/README.md +15 -1
- data/Rakefile +1 -0
- data/lib/road_to_rubykaigi/audio/audio_engine.rb +70 -0
- data/lib/road_to_rubykaigi/audio/oscillator.rb +159 -0
- data/lib/road_to_rubykaigi/audio/sequencer.rb +263 -0
- data/lib/road_to_rubykaigi/audio/wav/attack_01.wav +0 -0
- data/lib/road_to_rubykaigi/audio/wav/attack_02.wav +0 -0
- data/lib/road_to_rubykaigi/audio/wav/attack_03.wav +0 -0
- data/lib/road_to_rubykaigi/audio/wav/attack_04.wav +0 -0
- data/lib/road_to_rubykaigi/audio/wav/attack_05.wav +0 -0
- data/lib/road_to_rubykaigi/audio/wav/bonus.wav +0 -0
- data/lib/road_to_rubykaigi/audio/wav/crouch.wav +0 -0
- data/lib/road_to_rubykaigi/audio/wav/defeat.wav +0 -0
- data/lib/road_to_rubykaigi/audio/wav/game_over.wav +0 -0
- data/lib/road_to_rubykaigi/audio/wav/jump.wav +0 -0
- data/lib/road_to_rubykaigi/audio/wav/laptop.wav +0 -0
- data/lib/road_to_rubykaigi/audio/wav/stun.wav +0 -0
- data/lib/road_to_rubykaigi/audio/wav/walk_01.wav +0 -0
- data/lib/road_to_rubykaigi/audio/wav/walk_02.wav +0 -0
- data/lib/road_to_rubykaigi/audio/wav_source.rb +55 -0
- data/lib/road_to_rubykaigi/event_dispatcher.rb +122 -0
- data/lib/road_to_rubykaigi/fireworks.rb +4 -4
- data/lib/road_to_rubykaigi/game.rb +49 -60
- data/lib/road_to_rubykaigi/graphics/demo-map.txt +30 -0
- data/lib/road_to_rubykaigi/graphics/demo-mask.txt +30 -0
- data/lib/road_to_rubykaigi/graphics/map.rb +6 -1
- data/lib/road_to_rubykaigi/graphics/mask.rb +7 -1
- data/lib/road_to_rubykaigi/graphics/player.rb +56 -65
- data/lib/road_to_rubykaigi/graphics/player.txt +26 -0
- data/lib/road_to_rubykaigi/manager/audio_manager.rb +81 -0
- data/lib/road_to_rubykaigi/manager/collision_manager.rb +23 -108
- data/lib/road_to_rubykaigi/manager/drawing_manager.rb +7 -6
- data/lib/road_to_rubykaigi/manager/game_manager.rb +50 -13
- data/lib/road_to_rubykaigi/manager/physics_engine.rb +21 -0
- data/lib/road_to_rubykaigi/manager/update_manager.rb +15 -12
- data/lib/road_to_rubykaigi/map.rb +1 -15
- data/lib/road_to_rubykaigi/score_board.rb +18 -1
- data/lib/road_to_rubykaigi/sprite/attack.rb +13 -6
- data/lib/road_to_rubykaigi/sprite/bonus.rb +16 -0
- data/lib/road_to_rubykaigi/sprite/deadline.rb +3 -3
- data/lib/road_to_rubykaigi/sprite/enemy.rb +12 -8
- data/lib/road_to_rubykaigi/sprite/player.rb +110 -29
- data/lib/road_to_rubykaigi/version.rb +1 -1
- data/lib/road_to_rubykaigi.rb +20 -4
- metadata +55 -3
@@ -0,0 +1,122 @@
|
|
1
|
+
module RoadToRubykaigi
|
2
|
+
module EventDispatcher
|
3
|
+
class << self
|
4
|
+
def subscribe(event, &block)
|
5
|
+
subscribers[event] << block
|
6
|
+
end
|
7
|
+
|
8
|
+
def publish(event, *args)
|
9
|
+
subscribers[event].each { |block| block.call(*args) }
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def subscribers
|
15
|
+
@subscribers ||= Hash.new { |hash, key| hash[key] = [] }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class EventHander
|
21
|
+
def self.subscribe(attacks:, bonuses:, effects:, enemies:, player:, game_manager:)
|
22
|
+
new(attacks: attacks, bonuses: bonuses, effects: effects, enemies: enemies, player: player, game_manager: game_manager).subscribe
|
23
|
+
end
|
24
|
+
|
25
|
+
def subscribe
|
26
|
+
EventDispatcher.subscribe(:input) { |action| handle_input(action) }
|
27
|
+
EventDispatcher.subscribe(:collision) { |collision| handle_collision(collision) }
|
28
|
+
EventDispatcher.subscribe(:ending) do
|
29
|
+
Manager::AudioManager.instance.fanfare
|
30
|
+
@game_manager.ending
|
31
|
+
end
|
32
|
+
EventDispatcher.subscribe(:finish) do
|
33
|
+
until Manager::AudioManager.instance.fanfare_finished?
|
34
|
+
sleep 0.5
|
35
|
+
end
|
36
|
+
@game_manager.finish
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def initialize(attacks:, bonuses:, effects:, enemies:, player:, game_manager:)
|
43
|
+
@attacks = attacks
|
44
|
+
@bonuses = bonuses
|
45
|
+
@effects = effects
|
46
|
+
@enemies = enemies
|
47
|
+
@player = player
|
48
|
+
@game_manager = game_manager
|
49
|
+
end
|
50
|
+
|
51
|
+
def handle_input(action)
|
52
|
+
return if @player.stunned?
|
53
|
+
|
54
|
+
case action
|
55
|
+
when :jump; @player.jump
|
56
|
+
when :crouch
|
57
|
+
@player.crouch
|
58
|
+
Manager::AudioManager.instance.crouch
|
59
|
+
when :right; @player.right
|
60
|
+
when :left; @player.left
|
61
|
+
when :attack
|
62
|
+
if @player.can_attack?(@attacks)
|
63
|
+
@player.attack(@attacks)
|
64
|
+
Manager::AudioManager.instance.attack
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def handle_collision(collision)
|
70
|
+
__send__(collision[:type], *collision[:pair])
|
71
|
+
end
|
72
|
+
|
73
|
+
def attack_bonus(attack, bonus)
|
74
|
+
@attacks.delete(attack)
|
75
|
+
@bonuses.delete(bonus)
|
76
|
+
@effects.heart(@player.x + @player.width - 1, @player.y)
|
77
|
+
@game_manager.increment_score
|
78
|
+
Manager::AudioManager.instance.bonus
|
79
|
+
end
|
80
|
+
|
81
|
+
def attack_enemy(attack, enemy)
|
82
|
+
@attacks.delete(attack)
|
83
|
+
@effects.note(@player.x + @player.width - 1, @player.y)
|
84
|
+
@enemies.delete(enemy)
|
85
|
+
@game_manager.increment_score
|
86
|
+
Manager::AudioManager.instance.defeat
|
87
|
+
end
|
88
|
+
|
89
|
+
def player_bonus(_, bonus)
|
90
|
+
@bonuses.delete(bonus)
|
91
|
+
@effects.heart(@player.x + @player.width - 1, @player.y)
|
92
|
+
@game_manager.increment_score
|
93
|
+
if bonus.type == :laptop
|
94
|
+
@player.can_attack!
|
95
|
+
Manager::AudioManager.instance.laptop
|
96
|
+
else
|
97
|
+
Manager::AudioManager.instance.bonus
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def player_deadline(*args)
|
102
|
+
@player.stun
|
103
|
+
@game_manager.game_over
|
104
|
+
Manager::AudioManager.instance.game_over
|
105
|
+
end
|
106
|
+
|
107
|
+
def player_enemy(_, enemy)
|
108
|
+
if @player.stompable?
|
109
|
+
@effects.note(@player.x + @player.width - 1, @player.y)
|
110
|
+
@enemies.delete(enemy)
|
111
|
+
@player.jump
|
112
|
+
@game_manager.increment_score
|
113
|
+
Manager::AudioManager.instance.defeat
|
114
|
+
else
|
115
|
+
@effects.lightning(@player.x + @player.width - 1, @player.y)
|
116
|
+
@enemies.delete(enemy)
|
117
|
+
@player.stun
|
118
|
+
Manager::AudioManager.instance.stun
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module RoadToRubykaigi
|
2
2
|
class Fireworks
|
3
3
|
START_X = 632
|
4
|
+
DEMO_START_X = 524
|
4
5
|
DURATION_SECOND = 0.1
|
5
6
|
|
6
7
|
def shoot
|
@@ -10,7 +11,7 @@ module RoadToRubykaigi
|
|
10
11
|
def update
|
11
12
|
return unless shooting?
|
12
13
|
if finished?
|
13
|
-
return
|
14
|
+
return EventDispatcher.publish(:finish)
|
14
15
|
end
|
15
16
|
|
16
17
|
if Time.now - @last_frame_time >= DURATION_SECOND
|
@@ -37,10 +38,9 @@ module RoadToRubykaigi
|
|
37
38
|
|
38
39
|
private
|
39
40
|
|
40
|
-
def initialize
|
41
|
-
@x = START_X
|
41
|
+
def initialize
|
42
|
+
@x = RoadToRubykaigi.demo? ? DEMO_START_X : START_X
|
42
43
|
@y = 3
|
43
|
-
@game_manager = game_manager
|
44
44
|
@start_time = Time.now
|
45
45
|
@frame_index = 0
|
46
46
|
@last_frame_time = Time.now
|
@@ -1,34 +1,34 @@
|
|
1
1
|
module RoadToRubykaigi
|
2
2
|
class Game
|
3
3
|
def run
|
4
|
-
@start_time = Time.now
|
5
4
|
ANSI.clear
|
5
|
+
last_time = Time.now
|
6
|
+
accumulator = 0.0
|
6
7
|
$stdin.raw do
|
7
8
|
loop do
|
8
9
|
RoadToRubykaigi.debug.clear
|
9
10
|
process_input($stdin.read_nonblock(4, exception: false))
|
10
11
|
|
11
|
-
if @game_manager.
|
12
|
-
|
13
|
-
print(["CLEAR!", @score_board.render.strip, "Time: #{result_time} seconds"].map.with_index do |message, i|
|
14
|
-
ANSI::RESULT_DATA[i] + message
|
15
|
-
end.join)
|
12
|
+
if @game_manager.result?
|
13
|
+
print(@game_manager.render_result)
|
16
14
|
exit
|
17
15
|
else
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
@
|
16
|
+
current_time = Time.now
|
17
|
+
accumulator += current_time - last_time
|
18
|
+
last_time = current_time
|
19
|
+
while accumulator >= Manager::GameManager::UPDATE_RATE
|
20
|
+
@game_manager.update
|
21
|
+
@physics_engine.simulate
|
22
|
+
@update_manager.update(offset_x: @game_manager.offset_x)
|
23
|
+
@collision_manager.process
|
24
|
+
accumulator -= Manager::GameManager::UPDATE_RATE
|
26
25
|
end
|
27
|
-
|
26
|
+
|
27
|
+
@drawing_manager.draw(offset_x: @game_manager.offset_x) unless @game_manager.game_over?
|
28
28
|
end
|
29
29
|
|
30
30
|
puts RoadToRubykaigi.debug
|
31
|
-
sleep
|
31
|
+
sleep Manager::GameManager::FRAME_RATE
|
32
32
|
end
|
33
33
|
end
|
34
34
|
end
|
@@ -36,62 +36,51 @@ module RoadToRubykaigi
|
|
36
36
|
private
|
37
37
|
|
38
38
|
def initialize
|
39
|
-
|
40
|
-
|
41
|
-
@player = Sprite::Player.new
|
39
|
+
map = Map.new
|
40
|
+
player = Sprite::Player.new
|
42
41
|
bonuses = Sprite::Bonuses.new
|
43
42
|
enemies = Sprite::Enemies.new
|
44
|
-
|
43
|
+
attacks = Sprite::Attacks.new
|
45
44
|
effects = Sprite::Effects.new
|
46
|
-
deadline = Sprite::Deadline.new
|
45
|
+
deadline = Sprite::Deadline.new
|
47
46
|
|
48
|
-
@
|
49
|
-
player:
|
50
|
-
|
51
|
-
|
52
|
-
enemies: enemies,
|
53
|
-
|
54
|
-
|
47
|
+
@game_manager = Manager::GameManager.new(
|
48
|
+
map: map, deadline: deadline, enemies: enemies, player: player,
|
49
|
+
)
|
50
|
+
@physics_engine = Manager::PhysicsEngine.new(
|
51
|
+
attacks: attacks, deadline: deadline, enemies: enemies, player: player,
|
52
|
+
)
|
53
|
+
@update_manager = Manager::UpdateManager.new(
|
54
|
+
map: map, attacks: attacks, effects: effects, enemies: enemies, player: player, fireworks: @game_manager.fireworks,
|
55
|
+
)
|
56
|
+
@collision_manager = Manager::CollisionManager.new(
|
57
|
+
attacks: attacks, bonuses: bonuses, deadline: deadline, enemies: enemies, player: player,
|
58
|
+
)
|
59
|
+
@drawing_manager = Manager::DrawingManager.new(
|
60
|
+
map: map, player: player, deadline: deadline, bonuses: bonuses, enemies: enemies, attacks: attacks, effects: effects, game_manager: @game_manager,
|
55
61
|
)
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
@scroll_offset_x = 0
|
62
|
+
EventHander.subscribe(
|
63
|
+
attacks: attacks, bonuses: bonuses, effects: effects, enemies: enemies, player: player, game_manager: @game_manager,
|
64
|
+
)
|
65
|
+
Manager::AudioManager.instance
|
61
66
|
end
|
62
67
|
|
63
68
|
def process_input(input)
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
69
|
+
actions = {
|
70
|
+
"\e[A" => :jump,
|
71
|
+
"\e[B" => :crouch,
|
72
|
+
"\e[C" => :right,
|
73
|
+
"\e[D" => :left,
|
74
|
+
" " => :attack,
|
75
|
+
}
|
70
76
|
stop = %W[q \x03]
|
71
77
|
|
72
|
-
case
|
73
|
-
when
|
74
|
-
|
75
|
-
when
|
76
|
-
@player.right
|
77
|
-
when left
|
78
|
-
@player.left
|
79
|
-
when attack
|
80
|
-
# @attacks.add(
|
81
|
-
# @player.x + @player.width,
|
82
|
-
# @player.y + 1,
|
83
|
-
# )
|
84
|
-
when *stop
|
78
|
+
case
|
79
|
+
when actions[input]
|
80
|
+
EventDispatcher.publish(:input, actions[input])
|
81
|
+
when stop.include?(input)
|
85
82
|
exit
|
86
83
|
end
|
87
84
|
end
|
88
|
-
|
89
|
-
def game_over
|
90
|
-
result_time = (Time.now - @start_time).round(2)
|
91
|
-
print([ANSI::RED + "Game Over", ANSI::DEFAULT_TEXT_COLOR + @score_board.render.strip, "Time: #{result_time} seconds"].map.with_index do |message, i|
|
92
|
-
ANSI::RESULT_DATA[i] + " #{message} "
|
93
|
-
end.join)
|
94
|
-
exit
|
95
|
-
end
|
96
85
|
end
|
97
86
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
┌──────────────────────────────────────────────────────────────────────────────────────────┬──────────────────────────────────────────────────┬────────────────────────────────────────────────┬──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
2
|
+
│ │ │ │ ★ ° . * ° . °☆ . * ● ¸ │
|
3
|
+
│ │ │ │ . ★ ° :. ★ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ │
|
4
|
+
│ ┌──────────────────────────────────────────────┐ .─┴─. .─┴─. .─┴─. ⣰⣾⣾⣿⣿⣿⣿⣆ . * . ⠀⠀⠀⠀⠀⠀⠀⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠳⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ │
|
5
|
+
│ │ │ ( ) ( ) ( ) ⣰⣾⣾⣾⣾⡀⣾⣾⣾⣾⣾⣷⣄ ° . ● . ★ ° . *⠀⠀⠀⠀⠀⠀⣀⡴⢧⣀⠀⠀⣀⣠⠤⠤⠤⠤⣄⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ │
|
6
|
+
│ │ Walk with ← → key │ '───' '───' '───' ⣴⣿⣿⣷⣾⣿⣿⣆ ⢀⣰⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣷⣄ . * ● ¸ . ★ ⠀⠀⠀⠀⠀⠀⠀⠘⠏⢀⡴⠊⠁⠀⠀⠀⠀⠀⠀⠈⠙⠦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ¸ │
|
7
|
+
│ │ Jump with ↑ key │ ⢾⣿⡿⢿⣿⣿⣿⠟ ⣰⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣿⣷⣄ • ○ ° ★ . * ⠀⠀⠀⠀⠀⠀⠀⠀⣰⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢶⣶⣒⣶⠦⣤⣀⠀⠀ ★ ° . °☆ . * ●╔═══════╗ ║ ║ │
|
8
|
+
│ │ Stomp bugs for bonus │ ⢀⣰⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾ ⣾⣾⣾⣾⣾⣾⣾⣾⣾⣷⣄ ° . ● . ★ ° ⠀⠀⠀⠀⠀⠀⢀⣰⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣟⠲⡌⠙⢦⠈⢧⠀ :. ★ * • ○ ° ║ ║ ║ ║ │
|
9
|
+
│ │ Bumping into bugs stuns you │ ┌────────────────────────────────┐ ┌────────────────────────────────┐ ┌────────────────────────────────┐ ⣰⣾⣾⣾⣾⣾⣾ ⣾⣾⣾⣾⣾⣾⣾⣾⣾ ⣾⣾⣾⣾⣾ ⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣶⣄⡀ °☆ . * ● ¸ .⠀⠀⠀⣠⢴⡾⢟⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⡴⢃⡠⠋⣠⠋⠀°☆ . . ║ ║ ║ ║ ║ ║ ║ ╔═══════║ │
|
10
|
+
│ │ │ │ │ │ │ │ ▷▷ │ ⣴⣿⣿⣷⣾⣿⣿⣆ ⣴⣿⣿⣷⣾⣿⣿⣆ ⢀⣰⣾⣾⣾⣾⣾⣾ ⠟ ⡞ ⣾ ⣾⣾ ⣾⣾⣾⣾⣾ ⣾⣾⣾⣾⣾ ⣾⣾⣾⣾⣷⣄ ⠀⠀⠞⣱⠋⢰⠁⢿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣠⠤⢖⣋⡥⢖⣫⠔⠋⠀⠀⠀ * ★ ° . ° . ║☆ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ │
|
11
|
+
│ │ Escape the deadline to attend RubyKaigi! │ │ ╔═════╗ ═══╦═══ ╔══════╗ │ │ ╔═════╗ ═══╦═══ ║║ ║║ │ │ ▷ ▷▷ │ ⢾⣿⡿⢿⣿⣿⣿⠟ ⢾⣿⡿⢿⣿⣿⣿⠟ ⢀⣰⣾⣾⣾⣾⣾⣾⣾⠟ ⡞ ⣾ ⣾⣾⣾ ⣾ ⠈⠻⣾⣷⣦⣄ ⠀⠀⡀⠹⢤⣈⣙⠚⠶⠤⠤⠤⠴⠶⣒⣒⣚⣩⠭⢵⣒⣻⠭⢖⠏⠁⢀⣀⠀⠀⠀⠀ . ★ ° :●. ╠═════╦═╝ ║ ║ ╠══════╗ ║ ║ ╠═════╦═╝ ╔═══════║ ║ ║ │
|
12
|
+
│ │ │ │ ║ ║ ║ ║ ║ │ │ ║ ║ ║ ║║ ║║ │ │ ▷▷▷▷▷▷▷▷▷▷▷▷▷▷▷▷▷▷▷▷ ▷▷ │ ┌──────────┐ ╔═══════╗ ⢀⣰⣾⣾⠟ ⠟ ⣾⡞ ⡌⢿⣷⣄ ⠠⠀⠈⠓⠒⠦⠭⠭⠭⣭⠭⠭⠭⠭⠿⠓⠒⠛⠉⠉⠀⠀⣠⠏⠀⠀⠘⠞⠀⠀⠀⠀ . . . ║ ╚═╗ ║ ║ ║ ╚╗ ╚═══════╣ ║ ╚═╗ ║ ║ ║ ╚═══════╣ ║ │
|
13
|
+
│ └──────────────────────────────────────────────┘ │ ║ ║ ║ ║ │ │ ║ ║ ║ ║║ ║║ │ │ ▷ ▷▷ │ │ □ □ □ □ │ ║ ║ ⣀⣾⣾⣾⠟ ⡞ ⠈⢿⣷⣄ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠓⢤⣀⠀⠀⠀⠀⠀⠀⣀⡤⠞⠁⠀⣰⣆⠀⠀⠀⠀⠀⠀ . ★ ° . * ° ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ │
|
14
|
+
│ │ ╚═════╗ ║ ╠══════╣ │ │ ╠═══╦═╝ ║ ║║ ║║ │ │ ▷▷▷▷▷▷▷▷▷▷▷▷▷▷▷▷▷▷▷▷ ▷▷ │ │ │ ║ ║ ║ ⢀⣰⣾⣾⣾⣾⠟ ⠈⠻⣦⡀ ★ ° . * ° . °☆ .⠀⠀⠀⠈⠉⠙⠒⠒⠛⠉⠁⠀⠀⠀⠉⢳⡞⠉⠀⠀⠀⠀⠁ . ● ¸ . ║ ║ ╚═══════║ ╚═══════╝ ════════╝ ║ ║ ╚═══════║ ║ ════════╝ ║ │
|
15
|
+
│ │ ║ ║ ║ ║ │ │ ║ ╚═╗ ║ │ │ ▷ ▷▷ │ └──────────┘ ║ ║ ║ ⢀⣰⣾⣾⣾⣾⠟ ⠈⠻⣷⣦⣄ * ● ¸ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ○ ° . . │
|
16
|
+
│ │ ╚═════╝ ║ ║ ║ │ │ ║ ║ ║ ║║ ║║ │ │ ▷▷ │ ╠═════╦═╝ ╔═══════╗ ╔═══════║ ╔══════╣ ⣀⣀⣀⣀⣀⠤⣾⣾⣾⠟ ⠈⠻⣷⣦⣄ . ★ ° :. ★ * • . . │
|
17
|
+
│ └────────────────────────────────┘ └────────────────────────────────┘ └────────────────────────────────┘ ║ ╚═╗ ║ ║ ║ ║ ╔╝ ║ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ ☆ ¸ . ╭────────────────────────────────────╮ │
|
18
|
+
│ ║ ║ ║ ║ ║ ║ ║ ║ ┌────────┐ ╭─┼─╮ ╭─┼─╮ ° . . │ │ │
|
19
|
+
│ ┌──────────┐ ║ ║ ╚═══════╝ ╚═══════║ ╚═══════╝ │ │ ╭─╯ │ ╰──╮ ╭──╯ │ ╰─╮ ¸ ├▄─▄─▄─▄─▄─▄─▄─▄─▄─▄─▄─▄─▄─▄─▄─▄─▄─▄─┤ │
|
20
|
+
│ │ □ □ □ □ │ │ │ ╭─╯ ╰─╮ ╰─╮ ╭─────╮ ╭─────╮ ╭─╯ ╭─╯ ╰─╮ . ○ ╭────────────┼─▀─▀─▀─▀─▀─▀─▀─▀─▀─▀─▀─▀─▀─▀─▀─▀─▀─▀┼────────────╮ │
|
21
|
+
│ ████████ ████████ ████████ ████████ │ │ │ │ ╭─╯ ╰─╮ ╰─╮ │╭───╮│ ╭────╮ ╭────╮ ╭────╮ ╭────╮ ╭────╮ ╭────╮ ╭────╮ │╭───╮│ ╭─╯ ╭─╯ ╰─╮ ⠀⠀⠀⠀⠀⠀⠀⠀ ° . │ │ ▗▄▄▄▄▄▄▖ │ │ │
|
22
|
+
│ ████████ ████████ ▓░▓ ████████ ████████ ░ └──────────┘ │ │ ╭─╯ ╰──╮ ╰──╮ ││ ││ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ││ ││ ╭──╯ ╭──╯ ╰─╮ │ ▯ ▯ ▯ │ ▐██████▌ │ ▯ ▯ ▯ │ │
|
23
|
+
│ ▀▀▀▜▛▀▀▀ ▀▀▀▜▛▀▀▀ █ ▓░▓ █ ▀▀▀▜▛▀▀▀ ▀▀▀▜▛▀▀▀ █▘ ░ ┌──────────┐ │ │ ╭─┴╮ ╰────╯ │╰───╯│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │╰───╯│ ╰────╯ ╭┴─╮ │ ▯ ▯ ▯ │ │ │ │ ▯ ▯ ▯ │ │
|
24
|
+
│ ══╦═════▝▘══════════╦══ ══╦═════▝▘════════════════════▝▘═════╦══ ══╦═════▝▘══════════╦══ │ □ □ □ □ │ ══╦═════════════════╦══ │ ◯ │ ╭─╯ ╰─╮ │ │ ╰────╯ ╰────╯ ╰────╯ ╰────╯ ╰────╯ ╰────╯ ╰────╯ │ │ ╭─╯ ╰─╮ │ │ └──────┘ │ │ │
|
25
|
+
│ ║ ║ ║ ║ ║ ║ ┌──────────┐ │ │ ║ ║ │ │ ╭─╯ ╰─╮ │ │ │ │ ╭─╯ ╰─╮ │ ▯ ▯ ▯ ├─▄─▄─▄┐ ┌▄─▄─▄─▄─▄─▄─▄─▄─▄─▖ ▗─▄─▄─▄┤ ▯ ▯ ▯ │ │
|
26
|
+
│ ║ ║ ║ ║ ║ ║ │ □ □ □ □ │ └──────────┘ ║ ║ │ │ │ ╰─╮ │ │ │ │ ╭─╯ │ │ ▯ ▯ ▯ ├▀─▀─▀─▘ ▝─▀─▀─▀─▀─▀─▀─▀─▀─▀┘ └▀─▀─▀─┤ ▯ ▯ ▯ │ │
|
27
|
+
│ ║ ║ ║ ║ ║ ║ │ │ ║ ║ │ │ │ ╰─╮ ╰─────╯ ╰─────╯ ╭─╯ │ │ ▯ ▯ ▯ │ ▄▄▄▄▄▖ ╔═══╗ ▗▄▄▄▄▄▄▖ ╔═══╗ ▗▄▄▄▄▄ │ ▯ ▯ ▯ │ │
|
28
|
+
│ ╨ ╨ ╨ ╨ ╨ ╨ └──────────┘ ╨ ╨ │ │ ╰──────────────┴┬───┬───┬───┬────────────────────────────────────────────────────────────────────────────────────────────────────────────┬───┬───┬───┬┴──────────────╯ │ ▯ ▯ ▯ │ █████▌ ║▐█▌║ ▐██████▌ ║▐█▌║ ▐█████ │ ▯ ▯ ▯ │ │
|
29
|
+
│ └────────┘ ╰◯◯◯╯ ╰◯◯◯╯ ╰◯◯◯╯ ╰◯◯◯╯ ╰────────────╯ ╰────────────╯ │
|
30
|
+
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
@@ -0,0 +1,30 @@
|
|
1
|
+
###########################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################
|
2
|
+
# # # # ★ ° . * ° . °☆ . * ● ¸ #
|
3
|
+
# # # # . ★ ° :. ★ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ #
|
4
|
+
# ################################################ .---. .---. .---. ⣰⣾⣾⣿⣿⣿⣿⣆ . * . ⠀⠀⠀⠀⠀⠀⠀⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠳⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ #
|
5
|
+
# # # ( ) ( ) ( ) ⣰⣾⣾⣾⣾⡀⣾⣾⣾⣾⣾⣷⣄ ° . ● . ★ ° . *⠀⠀⠀⠀⠀⠀⣀⡴⢧⣀⠀⠀⣀⣠⠤⠤⠤⠤⣄⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ #
|
6
|
+
# # Walk with ← → key # '---' '---' '---' ⣴⣿⣿⣷⣾⣿⣿⣆ ⢀⣰⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣷⣄ . * ● ¸ . ★ ⠀⠀⠀⠀⠀⠀⠀⠘⠏⢀⡴⠊⠁⠀⠀⠀⠀⠀⠀⠈⠙⠦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ¸ #
|
7
|
+
# # Jump with ↑ key # ⢾⣿⡿⢿⣿⣿⣿⠟ ⣰⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣿⣷⣄ • ○ ° ★ . * ⠀⠀⠀⠀⠀⠀⠀⠀⣰⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢶⣶⣒⣶⠦⣤⣀⠀⠀ ★ ° . °☆ . * ●######### # # #
|
8
|
+
# # Stomp ugs for onus # ⢀⣰⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾ ⣾⣾⣾⣾⣾⣾⣾⣾⣾⣷⣄ ° . ● . ★ ° ⠀⠀⠀⠀⠀⠀⢀⣰⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣟⠲⡌⠙⢦⠈⢧⠀ :. ★ * • ○ ° # # # # #
|
9
|
+
# # umping into ugs stuns you # ################################## ################################## ################################## ⣰⣾⣾⣾⣾⣾⣾ ⣾⣾⣾⣾⣾⣾⣾⣾⣾ ⣾⣾⣾⣾⣾ ⣾⣾⣾⣾⣾⣾⣾⣾⣾⣾⣶⣄⡀ °☆ . * ● ¸ .⠀⠀⠀⣠⢴⡾⢟⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⡴⢃⡠⠋⣠⠋⠀°☆ . . # # # # # # # ######### #
|
10
|
+
# # # - - - - - ▷▷ - ⣴⣿⣿⣷⣾⣿⣿⣆ ⣴⣿⣿⣷⣾⣿⣿⣆ ⢀⣰⣾⣾⣾⣾⣾⣾ ⠟ ⡞ ⣾ ⣾⣾ ⣾⣾⣾⣾⣾ ⣾⣾⣾⣾⣾ ⣾⣾⣾⣾⣷⣄ ⠀⠀⠞⣱⠋⢰⠁⢿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣠⠤⢖⣋⡥⢖⣫⠔⠋⠀⠀⠀ * ★ ° . ° . #☆ # # # # # # # # # # #
|
11
|
+
# # Escape the deadline to attend Ru yKaigi! # - ------- ------- -------- - - ------- ------- -- -- - - ▷ ▷▷ - ⢾⣿⡿⢿⣿⣿⣿⠟ ⢾⣿⡿⢿⣿⣿⣿⠟ ⢀⣰⣾⣾⣾⣾⣾⣾⣾⠟ ⡞ ⣾ ⣾⣾⣾ ⣾ ⠈⠻⣾⣷⣦⣄ ⠀⠀⡀⠹⢤⣈⣙⠚⠶⠤⠤⠤⠴⠶⣒⣒⣚⣩⠭⢵⣒⣻⠭⢖⠏⠁⢀⣀⠀⠀⠀⠀ . ★ ° :●. ######### # # ######## # # ######### ######### # # #
|
12
|
+
# # # - - - - - - - - - - - -- -- - - ▷▷▷▷▷▷▷▷▷▷▷▷▷▷▷▷▷▷▷▷ ▷▷ - ############ ######### ⢀⣰⣾⣾⠟ ⠟ ⣾⡞ ⡌⢿⣷⣄ ⠠⠀⠈⠓⠒⠦⠭⠭⠭⣭⠭⠭⠭⠭⠿⠓⠒⠛⠉⠉⠀⠀⣠⠏⠀⠀⠘⠞⠀⠀⠀⠀ . . . # ### # # # ## ######### # ### # # # ######### # #
|
13
|
+
# ################################################ - - - - - - - - - - -- -- - - ▷ ▷▷ - # # # # ⣀⣾⣾⣾⠟ ⡞ ⠈⢿⣷⣄ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠓⢤⣀⠀⠀⠀⠀⠀⠀⣀⡤⠞⠁⠀⣰⣆⠀⠀⠀⠀⠀⠀ . ★ ° . * ° # # # # # # # # # # # # # # #
|
14
|
+
# - ------- - -------- - - ------- - -- -- - - ▷▷▷▷▷▷▷▷▷▷▷▷▷▷▷▷▷▷▷▷ ▷▷ - # # # # # ⢀⣰⣾⣾⣾⣾⠟ ⠈⠻⣦⡀ ★ ° . * ° . °☆ .⠀⠀⠀⠈⠉⠙⠒⠒⠛⠉⠁⠀⠀⠀⠉⢳⡞⠉⠀⠀⠀⠀⠁ . ● ¸ . # # ######### ######### ######### # # ######### # ######### # #
|
15
|
+
# - - - - - - - - --- - - - ▷ ▷▷ - ############ # # # ⢀⣰⣾⣾⣾⣾⠟ ⠈⠻⣷⣦⣄ * ● ¸ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ○ ° . . #
|
16
|
+
# - ------- - - - - - - - - -- -- - - ▷▷ - ######### ######### ######### ######## ⣀⣀⣀⣀⣀⠤⣾⣾⣾⠟ ⠈⠻⣷⣦⣄ . ★ ° :. ★ * • . . #
|
17
|
+
# ################################## ################################## ################################## # ### # # # # ## # ###################################################################################################################################### ☆ ¸ . ###################################### #
|
18
|
+
# # # # # # # # # ########## ########################################################################################################################################## ° . . # # #
|
19
|
+
# ############ # # ######### ######### ######### ########## ### # #### #### # ### ¸ ###################################### #
|
20
|
+
# # # ########## ######### ### ####### ####### ### ### ### . ○ ################################################################ #
|
21
|
+
# ######## ######## ######## ######## # # ########## ############# ### ####### ###### ###### ###### ###### ###### ###### ###### ####### ### ### ### ⠀⠀⠀⠀⠀⠀⠀⠀ ° . - - -------- # # #
|
22
|
+
# ######## ######## ### ######## ######## # ############ ########## ################## #### ## ## # # # # # # # # # # # # # # ## ## #### #### ### - ▯ ▯ ▯ - -------- # ▯ ▯ ▯ # #
|
23
|
+
# ######## ######## # ### # ######## ######## ## # ############ ########## ######################### ####### # # # # # # # # # # # # # # ####### ###### #### - ▯ ▯ ▯ - - - # ▯ ▯ ▯ # #
|
24
|
+
# ####################### ######################################## ####################### # # ####################### ########## ######## # # ###### ###### ###### ###### ###### ###### ###### # # ### ### - - -------- # # #
|
25
|
+
# # # # # # # ############ # # # # ########## ### ### # # # # ### ### - ▯ ▯ ▯ -------- -----------------### ######## ▯ ▯ ▯ # #
|
26
|
+
# # # # # # # # # ############ # # ########## # ### # # # # ### # - ▯ ▯ ▯ -------- -----------------### ######## ▯ ▯ ▯ # #
|
27
|
+
# # # # # # # # # # # ########## # ### ####### ####### ### # - ▯ ▯ ▯ - ------ ----- -------- --### ###### # ▯ ▯ ▯ # #
|
28
|
+
# # # # # # # ############ # # ########## ###################################################################################################################################################################### - ▯ ▯ ▯ - ------ ----- -------- --### ###### # ▯ ▯ ▯ # #
|
29
|
+
# ########## ##### ##### ##### ##### ############## ############## #
|
30
|
+
###########################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################
|
@@ -2,9 +2,14 @@ module RoadToRubykaigi
|
|
2
2
|
module Graphics
|
3
3
|
module Map
|
4
4
|
FILE_PATH = "map.txt"
|
5
|
+
DEMO_FILE_PATH = "demo-map.txt"
|
5
6
|
|
6
7
|
def self.data
|
7
|
-
File.read("#{__dir__}/#{
|
8
|
+
File.read("#{__dir__}/#{file_path}").split("\n")
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.file_path
|
12
|
+
@file_path ||= RoadToRubykaigi.demo? ? DEMO_FILE_PATH : FILE_PATH
|
8
13
|
end
|
9
14
|
end
|
10
15
|
end
|
@@ -2,9 +2,15 @@ module RoadToRubykaigi
|
|
2
2
|
module Graphics
|
3
3
|
module Mask
|
4
4
|
FILE_PATH = "mask.txt"
|
5
|
+
DEMO_FILE_PATH = "demo-mask.txt"
|
5
6
|
MASK_CHARAS = "[╭─╮╰│╯┬◯╔═║╠╚╦╝╗╣┤┴┼├╽▐▗▖▌◻▥▞▟█▙◺◸┌┐┘└╨▝▘▄▜▛▀░▓]"
|
7
|
+
|
6
8
|
def self.data
|
7
|
-
File.read("#{__dir__}/#{
|
9
|
+
File.read("#{__dir__}/#{file_path}").split("\n")
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.file_path
|
13
|
+
@file_path ||= RoadToRubykaigi.demo? ? DEMO_FILE_PATH : FILE_PATH
|
8
14
|
end
|
9
15
|
end
|
10
16
|
end
|
@@ -3,77 +3,68 @@ module RoadToRubykaigi
|
|
3
3
|
module Player
|
4
4
|
RIGHT = RoadToRubykaigi::Sprite::Player::RIGHT
|
5
5
|
LEFT = RoadToRubykaigi::Sprite::Player::LEFT
|
6
|
-
|
7
|
-
CHARACTERS = {
|
8
|
-
normal: {
|
9
|
-
RIGHT => [
|
10
|
-
<<~SPRITE,
|
11
|
-
╭──────╮
|
12
|
-
│。・◡・│
|
13
|
-
╰ᜊ───ᜊ─╯
|
14
|
-
SPRITE
|
15
|
-
<<~SPRITE,
|
16
|
-
╭──────╮
|
17
|
-
│。・◡・│
|
18
|
-
╰─∪───∪╯
|
19
|
-
SPRITE
|
20
|
-
],
|
21
|
-
LEFT => [
|
22
|
-
<<~SPRITE,
|
23
|
-
╭──────╮
|
24
|
-
│・◡・。│
|
25
|
-
╰─ᜊ───ᜊ╯
|
26
|
-
SPRITE
|
27
|
-
<<~SPRITE,
|
28
|
-
╭──────╮
|
29
|
-
│・◡・。│
|
30
|
-
╰∪───∪─╯
|
31
|
-
SPRITE
|
32
|
-
],
|
33
|
-
},
|
34
|
-
stunned: {
|
35
|
-
RIGHT => [
|
36
|
-
<<~SPRITE,
|
37
|
-
╭──────╮
|
38
|
-
│ ´×⌓× │
|
39
|
-
╰─ᜊ───ᜊ╯
|
40
|
-
SPRITE
|
41
|
-
<<~SPRITE,
|
42
|
-
╭──────╮
|
43
|
-
│ ´×⌓× │
|
44
|
-
╰─∪───∪╯
|
45
|
-
SPRITE
|
46
|
-
],
|
47
|
-
LEFT => [
|
48
|
-
<<~SPRITE,
|
49
|
-
╭──────╮
|
50
|
-
│ ×⌓×` │
|
51
|
-
╰─ᜊ───ᜊ╯
|
52
|
-
SPRITE
|
53
|
-
<<~SPRITE,
|
54
|
-
╭──────╮
|
55
|
-
│ ×⌓×` │
|
56
|
-
╰─∪───∪╯
|
57
|
-
SPRITE
|
58
|
-
],
|
59
|
-
},
|
60
|
-
}
|
6
|
+
FILE_PATH = "player.txt"
|
61
7
|
|
62
8
|
class << self
|
63
|
-
def character(status
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
fullwidth?(character) ? [character, ANSI::NULL] : character
|
68
|
-
end.flatten
|
69
|
-
end
|
70
|
-
end
|
9
|
+
def character(posture:, status:, direction:, attack_mode:)
|
10
|
+
load_data
|
11
|
+
characters = attack_mode ? @attack_characters : @default_characters
|
12
|
+
characters[posture][status][direction]
|
71
13
|
end
|
72
14
|
|
73
15
|
private
|
74
16
|
|
75
|
-
def
|
76
|
-
|
17
|
+
def load_data
|
18
|
+
return if @default_characters
|
19
|
+
index = { "RIGHT" => Sprite::Player::RIGHT, "LEFT" => Sprite::Player::LEFT }
|
20
|
+
hash = Hash.new { |h, k| h[k] = [] }
|
21
|
+
@default_characters = {
|
22
|
+
standup: { normal: hash.dup, stunned: hash.dup },
|
23
|
+
crouching: { normal: hash.dup, stunned: hash.dup },
|
24
|
+
}
|
25
|
+
@attack_characters = {
|
26
|
+
standup: { normal: hash.dup, stunned: hash.dup },
|
27
|
+
crouching: { normal: hash.dup, stunned: hash.dup },
|
28
|
+
}
|
29
|
+
set = {}
|
30
|
+
File.read("#{__dir__}/#{FILE_PATH}").scan(/# (\w+)\n(.*)\n/) do |type, line|
|
31
|
+
set[type.to_sym] = line.chars.map do |char|
|
32
|
+
fullwidth?(char) ? [char, ANSI::NULL] : char
|
33
|
+
end.flatten
|
34
|
+
end
|
35
|
+
|
36
|
+
%i[standup crouching].each do |posture|
|
37
|
+
%i[normal stunned].each do |status|
|
38
|
+
index.each do |(direction, direction_value)|
|
39
|
+
(1..2).each do |i|
|
40
|
+
@default_characters[posture][status][direction_value] << (
|
41
|
+
[
|
42
|
+
set[:head],
|
43
|
+
set[:"face_#{posture}_#{status}_#{direction}"],
|
44
|
+
posture == :crouching ? nil : set[:"foot_#{status}_#{i}"],
|
45
|
+
].compact
|
46
|
+
)
|
47
|
+
@attack_characters[posture][status][direction_value] << (
|
48
|
+
direction == "RIGHT" ?
|
49
|
+
[
|
50
|
+
set[:head] + " ".chars,
|
51
|
+
set[:"face_#{posture}_#{status}_#{direction}"] + "_◢◤".chars,
|
52
|
+
posture == :crouching ? nil : set[:"foot_#{status}_#{i}"] + " ".chars,
|
53
|
+
].compact :
|
54
|
+
[
|
55
|
+
" ".chars + set[:head],
|
56
|
+
"◥◣_".chars + set[:"face_#{posture}_#{status}_#{direction}"],
|
57
|
+
posture == :crouching ? nil : " ".chars + set[:"foot_#{status}_#{i}"],
|
58
|
+
].compact
|
59
|
+
)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def fullwidth?(char)
|
67
|
+
%w[・].include? char
|
77
68
|
end
|
78
69
|
end
|
79
70
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# head
|
2
|
+
╭──────╮
|
3
|
+
# foot_normal_1
|
4
|
+
╰─∪───∪╯
|
5
|
+
# foot_normal_2
|
6
|
+
╰ᜊ───ᜊ─╯
|
7
|
+
# foot_stunned_1
|
8
|
+
╰─ᜊ───ᜊ╯
|
9
|
+
# foot_stunned_2
|
10
|
+
╰ᜊ───ᜊ─╯
|
11
|
+
# face_standup_normal_RIGHT
|
12
|
+
│。・◡・│
|
13
|
+
# face_standup_normal_LEFT
|
14
|
+
│・◡・。│
|
15
|
+
# face_standup_stunned_RIGHT
|
16
|
+
│ ´×⌓× │
|
17
|
+
# face_standup_stunned_LEFT
|
18
|
+
│ ×⌓×` │
|
19
|
+
# face_crouching_normal_RIGHT
|
20
|
+
╰ᜊ・◡・╯
|
21
|
+
# face_crouching_normal_LEFT
|
22
|
+
╰・◡・ᜊ╯
|
23
|
+
# face_crouching_stunned_RIGHT
|
24
|
+
╰ ´×⌓× ╯
|
25
|
+
# face_crouching_stunned_LEFT
|
26
|
+
╰ ×⌓×` ╯
|