space_invaders 0.0.2
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 +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +38 -0
- data/Rakefile +1 -0
- data/assets/fonts/unifont.ttf +0 -0
- data/assets/images/Bullet.png +0 -0
- data/assets/images/FullBlock.png +0 -0
- data/assets/images/InvaderA1.png +0 -0
- data/assets/images/InvaderA2.png +0 -0
- data/assets/images/InvaderB1.png +0 -0
- data/assets/images/InvaderB2.png +0 -0
- data/assets/images/InvaderC1.png +0 -0
- data/assets/images/InvaderC2.png +0 -0
- data/assets/images/OkBlock.png +0 -0
- data/assets/images/RedInvader.png +0 -0
- data/assets/images/Ship.png +0 -0
- data/assets/images/ShipCrushedLeft.png +0 -0
- data/assets/images/ShipCrushedRight.png +0 -0
- data/assets/images/WeakBlock.png +0 -0
- data/assets/sounds/InvaderBullet.wav +0 -0
- data/assets/sounds/InvaderHit.wav +0 -0
- data/assets/sounds/ShipBullet.wav +0 -0
- data/assets/sounds/ShipHit.wav +0 -0
- data/bin/space-invaders +9 -0
- data/go.rb +5 -0
- data/lib/space_invaders.rb +4 -0
- data/lib/space_invaders/app.rb +99 -0
- data/lib/space_invaders/base.rb +14 -0
- data/lib/space_invaders/behaviors/centerable.rb +13 -0
- data/lib/space_invaders/behaviors/collideable.rb +18 -0
- data/lib/space_invaders/behaviors/fireable.rb +35 -0
- data/lib/space_invaders/behaviors/mesurable.rb +17 -0
- data/lib/space_invaders/blocks/block.rb +62 -0
- data/lib/space_invaders/blocks/u_block.rb +37 -0
- data/lib/space_invaders/blocks/u_block_container.rb +46 -0
- data/lib/space_invaders/bullets/bullet.rb +46 -0
- data/lib/space_invaders/bullets/bullet_collection.rb +23 -0
- data/lib/space_invaders/invaders/invader.rb +38 -0
- data/lib/space_invaders/invaders/invader_abc.rb +27 -0
- data/lib/space_invaders/invaders/invader_row.rb +52 -0
- data/lib/space_invaders/invaders/invaders_container.rb +146 -0
- data/lib/space_invaders/invaders/red_invader.rb +66 -0
- data/lib/space_invaders/screens/game_over_screen.rb +42 -0
- data/lib/space_invaders/screens/next_level_screen.rb +39 -0
- data/lib/space_invaders/screens/welcome_screen.rb +46 -0
- data/lib/space_invaders/ship/drowned_ship_animator.rb +46 -0
- data/lib/space_invaders/ship/ship.rb +99 -0
- data/lib/space_invaders/statics/button_controller.rb +33 -0
- data/lib/space_invaders/statics/game_status.rb +52 -0
- data/lib/space_invaders/statics/images.rb +39 -0
- data/lib/space_invaders/statics/sounds.rb +37 -0
- data/lib/space_invaders/trackers/lives_tracker.rb +27 -0
- data/lib/space_invaders/trackers/score_tracker.rb +27 -0
- data/lib/space_invaders/utils.rb +19 -0
- data/space_invaders.gemspec +23 -0
- metadata +143 -0
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'space_invaders/base'
|
2
|
+
require 'space_invaders/blocks/u_block'
|
3
|
+
|
4
|
+
module SpaceInvaders
|
5
|
+
class UBlockContainer < Base
|
6
|
+
|
7
|
+
attr_reader :u_blocks
|
8
|
+
|
9
|
+
def initialize app
|
10
|
+
super
|
11
|
+
initialize_u_blocks
|
12
|
+
end
|
13
|
+
|
14
|
+
def update
|
15
|
+
@u_blocks.each { |item| item.update }
|
16
|
+
end
|
17
|
+
|
18
|
+
def draw
|
19
|
+
@u_blocks.each {|item| item.draw }
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize_u_blocks
|
23
|
+
@u_blocks = [
|
24
|
+
UBlock.new(app, 150, 400),
|
25
|
+
UBlock.new(app, 350, 400),
|
26
|
+
UBlock.new(app, 550, 400)
|
27
|
+
]
|
28
|
+
end
|
29
|
+
|
30
|
+
def each(&block)
|
31
|
+
@u_blocks.each(&block)
|
32
|
+
end
|
33
|
+
|
34
|
+
def delete(item)
|
35
|
+
@u_blocks.delete(item)
|
36
|
+
end
|
37
|
+
|
38
|
+
def <<(item)
|
39
|
+
@u_blocks << item
|
40
|
+
end
|
41
|
+
|
42
|
+
alias_method :push, :<<
|
43
|
+
alias_method :add, :<<
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module SpaceInvaders
|
2
|
+
class Bullet < Base
|
3
|
+
|
4
|
+
attr_reader :x_position, :y_position, :going_down, :bullet_offset, :bullet_collection, :image, :fireing_vehicle
|
5
|
+
|
6
|
+
alias_method :going_down?, :going_down
|
7
|
+
|
8
|
+
def initialize fireing_vehicle, going_down, bullet_collection, bullet_offset=10
|
9
|
+
super(fireing_vehicle.app)
|
10
|
+
@fireing_vehicle = fireing_vehicle
|
11
|
+
@image = app.images.bullet
|
12
|
+
@bullet_offset = bullet_offset
|
13
|
+
@going_down = going_down
|
14
|
+
@bullet_collection = bullet_collection
|
15
|
+
@bullet_collection << self
|
16
|
+
set_position
|
17
|
+
end
|
18
|
+
|
19
|
+
def update
|
20
|
+
@y_position += going_down? ? bullet_offset : -bullet_offset
|
21
|
+
end
|
22
|
+
|
23
|
+
def draw
|
24
|
+
image.draw x_position, y_position, 1
|
25
|
+
end
|
26
|
+
|
27
|
+
def delete
|
28
|
+
bullet_collection.delete(self)
|
29
|
+
end
|
30
|
+
|
31
|
+
def out_of_screen
|
32
|
+
if going_down
|
33
|
+
y_position > app.height
|
34
|
+
else
|
35
|
+
y_position < 0
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def set_position
|
40
|
+
@x_position = fireing_vehicle.x_middle - image.width/2
|
41
|
+
@y_position = fireing_vehicle.y_position
|
42
|
+
@y_position += fireing_vehicle.height if going_down
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module SpaceInvaders
|
4
|
+
class BulletCollection
|
5
|
+
extend Forwardable
|
6
|
+
attr_reader :bullets
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@bullets = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def update
|
13
|
+
@bullets.delete_if {|bullet| bullet.out_of_screen}
|
14
|
+
@bullets.each { |bullet| bullet.update }
|
15
|
+
end
|
16
|
+
|
17
|
+
def draw
|
18
|
+
@bullets.each {|bullet| bullet.draw }
|
19
|
+
end
|
20
|
+
|
21
|
+
def_delegators :@bullets, :each, :delete, :<<, :clear
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'space_invaders/behaviors/mesurable'
|
2
|
+
require 'space_invaders/behaviors/collideable'
|
3
|
+
module SpaceInvaders
|
4
|
+
class Invader < Base
|
5
|
+
include Mesurable
|
6
|
+
include Collideable
|
7
|
+
|
8
|
+
attr_reader :original_x_position
|
9
|
+
|
10
|
+
def initialize app, x_position, y_position
|
11
|
+
super app
|
12
|
+
|
13
|
+
@was_first_image = true
|
14
|
+
@x_position = x_position
|
15
|
+
@original_x_position = x_position
|
16
|
+
@y_position = y_position
|
17
|
+
@image = @first_image
|
18
|
+
end
|
19
|
+
|
20
|
+
def update(direction, y_offset=0)
|
21
|
+
x_offset = direction == :right ? 10 : -10
|
22
|
+
@x_position += x_offset
|
23
|
+
@y_position += y_offset
|
24
|
+
|
25
|
+
@was_first_image = !@was_first_image
|
26
|
+
@image = @was_first_image ? @first_image : @second_image
|
27
|
+
end
|
28
|
+
|
29
|
+
def draw
|
30
|
+
@image.draw x_position, y_position, 1
|
31
|
+
end
|
32
|
+
|
33
|
+
def points
|
34
|
+
raise NotImplementedError.new("You must implement the inherited points method")
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'space_invaders/invaders/invader'
|
2
|
+
require 'space_invaders/utils'
|
3
|
+
|
4
|
+
module SpaceInvaders
|
5
|
+
[:invader_a, :invader_b, :invader_c].each do |invader_name|
|
6
|
+
clazz_name = Utils.camelcase(invader_name)
|
7
|
+
clazz = Class.new(Invader) do
|
8
|
+
def initialize app, x_position=0, y_position=0
|
9
|
+
@first_image = Gosu::Image.new app, app.images.send("#{Utils.snake_klazz_name(self.class)}1")
|
10
|
+
@second_image = Gosu::Image.new app, app.images.send("#{Utils.snake_klazz_name(self.class)}2")
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
def points
|
15
|
+
case self
|
16
|
+
when InvaderA
|
17
|
+
40
|
18
|
+
when InvaderB
|
19
|
+
20
|
20
|
+
when InvaderC
|
21
|
+
10
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
Object.const_set(clazz_name, clazz)
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'space_invaders/base'
|
3
|
+
|
4
|
+
module SpaceInvaders
|
5
|
+
class InvaderRow < Base
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
attr_accessor :direction
|
9
|
+
attr_reader :invader_clazz
|
10
|
+
|
11
|
+
X_POSITIONS = [40, 110, 180, 250, 320, 390, 460, 530]
|
12
|
+
|
13
|
+
def initialize app, y_position, invader_clazz
|
14
|
+
super(app)
|
15
|
+
@y_position = y_position
|
16
|
+
@direction = :right
|
17
|
+
@invader_clazz = invader_clazz
|
18
|
+
@invaders = []
|
19
|
+
X_POSITIONS.each do |x_position|
|
20
|
+
@invaders << invader_clazz.new(app, x_position, y_position)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def_delegators :@invaders, :each, :count, :select, :find, :empty?
|
25
|
+
|
26
|
+
def update direction, y_offset
|
27
|
+
@invaders.each {|invader| invader.update direction, y_offset }
|
28
|
+
end
|
29
|
+
|
30
|
+
def draw
|
31
|
+
@invaders.each {|invader| invader.draw }
|
32
|
+
end
|
33
|
+
|
34
|
+
def farmost_right_position
|
35
|
+
@invaders.max_by {|invader| invader.x_position }.x_position
|
36
|
+
end
|
37
|
+
|
38
|
+
def farmost_left_position
|
39
|
+
@invaders.min_by {|invader| invader.x_position }.x_position
|
40
|
+
end
|
41
|
+
|
42
|
+
def check_collision(bullets)
|
43
|
+
@invaders.delete_if do |invader|
|
44
|
+
if invader.collides_with? bullets
|
45
|
+
app.score_tracker.increase_by(invader.points)
|
46
|
+
app.sounds.play_invader_hit!
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'space_invaders/invaders/invader_row'
|
2
|
+
require 'space_invaders/invaders/invader_abc'
|
3
|
+
require 'space_invaders/behaviors/fireable'
|
4
|
+
|
5
|
+
module SpaceInvaders
|
6
|
+
class InvadersContainer < Base
|
7
|
+
include Fireable
|
8
|
+
attr_reader :invader_rows
|
9
|
+
|
10
|
+
def initialize app
|
11
|
+
super
|
12
|
+
create_invader_rows
|
13
|
+
@change_time = Time.now
|
14
|
+
@can_fire = Time.now
|
15
|
+
@direction = :right
|
16
|
+
@y_offset = 0
|
17
|
+
end
|
18
|
+
|
19
|
+
def update
|
20
|
+
check_collision
|
21
|
+
|
22
|
+
if no_invaders?
|
23
|
+
app.next_level_screen.timer_start!
|
24
|
+
return game_status.next_level!
|
25
|
+
end
|
26
|
+
|
27
|
+
if can_change?
|
28
|
+
change_direction
|
29
|
+
@change_time = Time.now
|
30
|
+
end
|
31
|
+
|
32
|
+
if can_fire?
|
33
|
+
fire!
|
34
|
+
@can_fire = Time.now
|
35
|
+
end
|
36
|
+
|
37
|
+
bullets.update
|
38
|
+
end
|
39
|
+
|
40
|
+
def draw
|
41
|
+
invader_rows.each {|invader_row| invader_row.draw }
|
42
|
+
bullets.draw
|
43
|
+
end
|
44
|
+
|
45
|
+
def count
|
46
|
+
invader_rows.map {|invader_row| invader_row.count}.inject(:+) || 0
|
47
|
+
end
|
48
|
+
|
49
|
+
def any_invaders?
|
50
|
+
not count.zero?
|
51
|
+
end
|
52
|
+
|
53
|
+
def no_invaders?
|
54
|
+
count.zero?
|
55
|
+
end
|
56
|
+
|
57
|
+
def reinitialize!
|
58
|
+
create_invader_rows
|
59
|
+
bullets.clear
|
60
|
+
rival_bullets.clear
|
61
|
+
@change_time = Time.now
|
62
|
+
@can_fire = Time.now
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def check_collision
|
68
|
+
invader_rows.each { |invader_row| invader_row.check_collision(rival_bullets) }
|
69
|
+
invader_rows.delete_if {|invader_row| invader_row.empty?}
|
70
|
+
hit_rock_bottom? if any_invaders?
|
71
|
+
end
|
72
|
+
|
73
|
+
def hit_rock_bottom?
|
74
|
+
if any_invaders? and fireable_invaders.first.y_position == app.height
|
75
|
+
app.game_status.finished!
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def change_direction
|
80
|
+
if farmost_right_position >= app.width - 80
|
81
|
+
@direction = :left
|
82
|
+
@y_offset = 10
|
83
|
+
elsif farmost_left_position <= 20
|
84
|
+
@direction = :right
|
85
|
+
@y_offset = 10
|
86
|
+
else
|
87
|
+
@y_offset = 0
|
88
|
+
end
|
89
|
+
update_direction(@direction, @y_offset)
|
90
|
+
end
|
91
|
+
|
92
|
+
def update_direction direction, y_offset
|
93
|
+
invader_rows.each {|invader_row| invader_row.update direction, y_offset }
|
94
|
+
end
|
95
|
+
|
96
|
+
def shooter
|
97
|
+
fireable_invaders.sample
|
98
|
+
end
|
99
|
+
|
100
|
+
def sound
|
101
|
+
app.sounds.invader_bullet_sound
|
102
|
+
end
|
103
|
+
|
104
|
+
def can_fire?
|
105
|
+
Time.now > @can_fire + 2
|
106
|
+
end
|
107
|
+
|
108
|
+
def can_change?
|
109
|
+
Time.now > @change_time + 0.25
|
110
|
+
end
|
111
|
+
|
112
|
+
def fireable_invaders
|
113
|
+
InvaderRow::X_POSITIONS.map do |x_position|
|
114
|
+
invader_rows.reverse.map do |invader_row|
|
115
|
+
invader_row.find {|invader| invader.original_x_position == x_position }
|
116
|
+
end.compact.first
|
117
|
+
end.compact
|
118
|
+
end
|
119
|
+
|
120
|
+
def create_invader_rows
|
121
|
+
@invader_rows = [
|
122
|
+
InvaderRow.new(app, 100, InvaderA),
|
123
|
+
InvaderRow.new(app, 150, InvaderB),
|
124
|
+
InvaderRow.new(app, 200, InvaderB),
|
125
|
+
InvaderRow.new(app, 250, InvaderC),
|
126
|
+
InvaderRow.new(app, 300, InvaderC),
|
127
|
+
]
|
128
|
+
end
|
129
|
+
|
130
|
+
def rival_bullets
|
131
|
+
app.ship.bullets
|
132
|
+
end
|
133
|
+
|
134
|
+
def farmost_right_position
|
135
|
+
invader_rows.max_by do |invader_row|
|
136
|
+
invader_row.farmost_right_position
|
137
|
+
end.farmost_right_position
|
138
|
+
end
|
139
|
+
|
140
|
+
def farmost_left_position
|
141
|
+
invader_rows.min_by do |invader_row|
|
142
|
+
invader_row.farmost_left_position
|
143
|
+
end.farmost_left_position
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'space_invaders/invaders/invader'
|
2
|
+
|
3
|
+
module SpaceInvaders
|
4
|
+
class RedInvader < Invader
|
5
|
+
attr_accessor :dead
|
6
|
+
|
7
|
+
alias_method :dead?, :dead
|
8
|
+
|
9
|
+
def initialize app, x_position=0, y_position=50
|
10
|
+
@first_image = app.images.red_invader
|
11
|
+
@second_image = app.images.red_invader
|
12
|
+
@can_move = Time.now
|
13
|
+
@direction = :right
|
14
|
+
@dead = false
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
def points
|
19
|
+
100
|
20
|
+
end
|
21
|
+
|
22
|
+
def alive?
|
23
|
+
not dead?
|
24
|
+
end
|
25
|
+
|
26
|
+
def update
|
27
|
+
return if dead?
|
28
|
+
|
29
|
+
if collides_with? rival_bullets
|
30
|
+
handle_death
|
31
|
+
elsif can_move?
|
32
|
+
set_direction
|
33
|
+
@x_position += @direction == :right ? 10 : -10
|
34
|
+
@can_move = Time.now
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def draw
|
39
|
+
return if dead?
|
40
|
+
@image.draw @x_position, @y_position, 1
|
41
|
+
end
|
42
|
+
|
43
|
+
def handle_death
|
44
|
+
@dead = true
|
45
|
+
app.score_tracker.increase_by(points)
|
46
|
+
app.sounds.play_invader_hit!
|
47
|
+
end
|
48
|
+
|
49
|
+
def set_direction
|
50
|
+
if @x_position >= app.width - 80
|
51
|
+
@direction = :left
|
52
|
+
elsif @x_position <= 20
|
53
|
+
@direction = :right
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def rival_bullets
|
58
|
+
app.ship.bullets
|
59
|
+
end
|
60
|
+
|
61
|
+
def can_move?
|
62
|
+
Time.now - @can_move > 0.1
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|