pacmanx 1.0.1
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/Gemfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +19 -0
- data/Rakefile +2 -0
- data/bin/pacmanx +7 -0
- data/bin/pacman~ +7 -0
- data/lib/pacman/animated_object.rb +10 -0
- data/lib/pacman/cage.rb +14 -0
- data/lib/pacman/controller.rb +127 -0
- data/lib/pacman/ghost.rb +41 -0
- data/lib/pacman/ghosts_controller.rb +253 -0
- data/lib/pacman/ghosts_renderer.rb +70 -0
- data/lib/pacman/level.rb +62 -0
- data/lib/pacman/level_builder.rb +129 -0
- data/lib/pacman/level_renderer.rb +100 -0
- data/lib/pacman/levels/level1.lvl +98 -0
- data/lib/pacman/media/bg2_2.jpg +0 -0
- data/lib/pacman/media/blinky_down.png +0 -0
- data/lib/pacman/media/blinky_left.png +0 -0
- data/lib/pacman/media/blinky_right.png +0 -0
- data/lib/pacman/media/blinky_up.png +0 -0
- data/lib/pacman/media/clyde_down.png +0 -0
- data/lib/pacman/media/clyde_left.png +0 -0
- data/lib/pacman/media/clyde_right.png +0 -0
- data/lib/pacman/media/clyde_up.png +0 -0
- data/lib/pacman/media/inky_down.png +0 -0
- data/lib/pacman/media/inky_left.png +0 -0
- data/lib/pacman/media/inky_right.png +0 -0
- data/lib/pacman/media/inky_up.png +0 -0
- data/lib/pacman/media/pacman_down.png +0 -0
- data/lib/pacman/media/pacman_left.png +0 -0
- data/lib/pacman/media/pacman_right.png +0 -0
- data/lib/pacman/media/pacman_up.png +0 -0
- data/lib/pacman/media/pellet.png +0 -0
- data/lib/pacman/media/pinky_down.png +0 -0
- data/lib/pacman/media/pinky_left.png +0 -0
- data/lib/pacman/media/pinky_right.png +0 -0
- data/lib/pacman/media/pinky_up.png +0 -0
- data/lib/pacman/media/power_pellet.png +0 -0
- data/lib/pacman/media/wall.png +0 -0
- data/lib/pacman/pacman.rb +4 -0
- data/lib/pacman/pellet.rb +16 -0
- data/lib/pacman/player.rb +12 -0
- data/lib/pacman/version.rb +4 -0
- data/lib/pacman/wall.rb +8 -0
- data/pacman_classes.png +0 -0
- data/pacmanx.gemspec +24 -0
- data/spec/level_builder_spec.rb +111 -0
- data/spec/level_spec.rb +69 -0
- data/spec/spec_helper.rb +7 -0
- metadata +141 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a38cf4a118a4ee9e1c27b8f19b603896aa5a6546
|
4
|
+
data.tar.gz: a7aa399345721a67320a4d9fa47f61f63c61e728
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 58b90c93229efe35f6963625e7a14f774577d0a7d762c2dceabe55a7276a71af476dcc03aed0cf40d17da74ea04b3ffa62161d97e7258a56784f74cd8f5bd5fd
|
7
|
+
data.tar.gz: bcf9890d7d891dda69133ae5bf7a1bff2e04bf4f233af179a21fc7cfef751bd06b71505156b8137a6a117aa76e423e48ecfe6caf915a9f982b16aea86790e827
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 fajmanjir
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
data/Rakefile
ADDED
data/bin/pacmanx
ADDED
data/bin/pacman~
ADDED
data/lib/pacman/cage.rb
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'gosu'
|
3
|
+
require_relative 'level_builder'
|
4
|
+
require_relative 'level_renderer'
|
5
|
+
require_relative 'ghosts_controller'
|
6
|
+
require_relative 'ghosts_renderer'
|
7
|
+
|
8
|
+
module Pacman
|
9
|
+
# controller
|
10
|
+
class Game < Gosu::Window
|
11
|
+
def initialize(rel_path = '')
|
12
|
+
super(1280, 800, false)
|
13
|
+
@last_milliseconds = 0
|
14
|
+
|
15
|
+
self.caption = 'PacMan'
|
16
|
+
@level = 1
|
17
|
+
|
18
|
+
@renderer = LevelRenderer.new(self, rel_path)
|
19
|
+
|
20
|
+
# affects pacman and creatures speed
|
21
|
+
@timer_default = 20
|
22
|
+
@rel_path = rel_path
|
23
|
+
|
24
|
+
load_level
|
25
|
+
|
26
|
+
@ghosts_controller = GhostsController.new(@level)
|
27
|
+
|
28
|
+
@state = :running
|
29
|
+
end
|
30
|
+
|
31
|
+
def load_level
|
32
|
+
file = File.open(@rel_path + "levels/level#{@level}.lvl", 'r')
|
33
|
+
dsl = file.read
|
34
|
+
file.close
|
35
|
+
|
36
|
+
@level = LevelBuilder.build(dsl)
|
37
|
+
|
38
|
+
@timer = @timer_default
|
39
|
+
end
|
40
|
+
|
41
|
+
# this is a callback for key up events or equivalent (there are
|
42
|
+
# constants for gamepad buttons and mouse clicks)
|
43
|
+
def button_up(key)
|
44
|
+
close if key == Gosu::KbEscape
|
45
|
+
end
|
46
|
+
|
47
|
+
def button_down(key)
|
48
|
+
case @state
|
49
|
+
when :running
|
50
|
+
@level.player.direction = :left if (key == Gosu::KbLeft)
|
51
|
+
@level.player.direction = :right if (key == Gosu::KbRight)
|
52
|
+
@level.player.direction = :up if (key == Gosu::KbUp)
|
53
|
+
@level.player.direction = :down if (key == Gosu::KbDown)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def draw
|
58
|
+
@renderer.render(@level, @state)
|
59
|
+
end
|
60
|
+
|
61
|
+
def update
|
62
|
+
update_delta
|
63
|
+
# with a delta we need to express the speed of our entities in
|
64
|
+
# terms of pixels/second
|
65
|
+
|
66
|
+
case @state
|
67
|
+
when :running
|
68
|
+
@timer -= 1
|
69
|
+
|
70
|
+
if (@timer % 10 == 0)
|
71
|
+
# update animation
|
72
|
+
@level.player.animation_index = (@level.player.animation_index + 1)
|
73
|
+
end
|
74
|
+
|
75
|
+
if (@timer == 0)
|
76
|
+
update_player
|
77
|
+
@ghosts_controller.update
|
78
|
+
@state = :win if @ghosts_controller.colission
|
79
|
+
@timer = @timer_default
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def update_player
|
85
|
+
player = @level.player
|
86
|
+
case @level.player.direction
|
87
|
+
when :left
|
88
|
+
next_obj = @level.get(player.x - 1, player.y)
|
89
|
+
player.x -= 1 if player.x > 0 && (next_obj.nil? || next_obj.passable?)
|
90
|
+
when :right
|
91
|
+
next_obj = @level.get(player.x + 1, player.y)
|
92
|
+
player.x += 1 if player.x < @level.width - 1 && (next_obj.nil? ||
|
93
|
+
next_obj.passable?)
|
94
|
+
when :up
|
95
|
+
next_obj = @level.get(player.x, player.y - 1)
|
96
|
+
player.y -= 1 if player.y > 0 && (next_obj.nil? || next_obj.passable?)
|
97
|
+
when :down
|
98
|
+
if player.y + 1 < @level.height - 1
|
99
|
+
next_obj = @level.get(player.x, player.y + 1)
|
100
|
+
player.y += 1 if next_obj.nil? || next_obj.passable?
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
return unless next_obj != 0 && next_obj.class.ancestors.include?(Item)
|
105
|
+
|
106
|
+
@level.pellets_left -= 1 if next_obj.instance_of?(Pellet)
|
107
|
+
@state = :win if @level.pellets_left == 0
|
108
|
+
|
109
|
+
eat_power_pellet if next_obj.instance_of?(PowerPellet)
|
110
|
+
|
111
|
+
@level[player.y][player.x] = nil
|
112
|
+
end
|
113
|
+
|
114
|
+
def eat_power_pellet
|
115
|
+
@level.ghosts_frozen = 10 # frozen for x steps
|
116
|
+
end
|
117
|
+
|
118
|
+
def update_delta
|
119
|
+
# Gosu::millisecodns returns the time since the game_window was created
|
120
|
+
# Divide by 1000 since we want to work in seconds
|
121
|
+
current_time = Gosu.milliseconds / 1000.0
|
122
|
+
# clamping here is important to avoid strange behaviors
|
123
|
+
@delta = [current_time - @last_milliseconds, 0.25].min
|
124
|
+
@last_milliseconds = current_time
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
data/lib/pacman/ghost.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
module Pacman
|
2
|
+
# model of ghost
|
3
|
+
class Ghost < AnimatedObject
|
4
|
+
def initialize(x, y, direction)
|
5
|
+
super()
|
6
|
+
@x, @y = x, y
|
7
|
+
@direction = direction
|
8
|
+
@stopped = false # used for slow ghost
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_accessor :x, :y, :direction, :stopped
|
12
|
+
end
|
13
|
+
|
14
|
+
# red ghost
|
15
|
+
class BlinkyGhost < Ghost
|
16
|
+
def initialize(x, y, direction)
|
17
|
+
super(x, y, direction)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# pink ghost
|
22
|
+
class PinkyGhost < Ghost
|
23
|
+
def initialize(x, y, direction)
|
24
|
+
super(x, y, direction)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# cyan ghost
|
29
|
+
class InkyGhost < Ghost
|
30
|
+
def initialize(x, y, direction)
|
31
|
+
super(x, y, direction)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# orange ghost
|
36
|
+
class ClydeGhost < Ghost
|
37
|
+
def initialize(x, y, direction)
|
38
|
+
super(x, y, direction)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,253 @@
|
|
1
|
+
module Pacman
|
2
|
+
# handles behaviour of ghosts
|
3
|
+
class GhostsController
|
4
|
+
def initialize(level)
|
5
|
+
@level = level
|
6
|
+
@colission = false
|
7
|
+
end
|
8
|
+
|
9
|
+
def update
|
10
|
+
@level.ghosts_frozen -= 1 unless (@level.ghosts_frozen == 0)
|
11
|
+
return unless (@level.ghosts_frozen == 0)
|
12
|
+
|
13
|
+
update_blinky ||
|
14
|
+
update_pinky ||
|
15
|
+
update_inky ||
|
16
|
+
update_clyde
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_accessor :colission
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def update_blinky
|
24
|
+
ghost = @level.ghosts[:blinky]
|
25
|
+
dirs = update_ghost(ghost)
|
26
|
+
|
27
|
+
behave_smart(ghost, dirs)
|
28
|
+
end
|
29
|
+
|
30
|
+
def update_pinky
|
31
|
+
ghost = @level.ghosts[:pinky]
|
32
|
+
dirs = update_ghost(ghost)
|
33
|
+
|
34
|
+
behave_random(ghost, dirs)
|
35
|
+
end
|
36
|
+
|
37
|
+
def update_inky
|
38
|
+
ghost = @level.ghosts[:inky]
|
39
|
+
dirs = update_ghost(ghost)
|
40
|
+
|
41
|
+
behave_random(ghost, dirs)
|
42
|
+
end
|
43
|
+
|
44
|
+
def update_clyde
|
45
|
+
ghost = @level.ghosts[:clyde]
|
46
|
+
dirs = update_ghost(ghost)
|
47
|
+
|
48
|
+
behave_slow_random(ghost, dirs)
|
49
|
+
end
|
50
|
+
|
51
|
+
def behave_smart(ghost, dirs)
|
52
|
+
return if dirs.nil?
|
53
|
+
|
54
|
+
if continue_forward?(ghost, dirs)
|
55
|
+
move(ghost, ghost.direction)
|
56
|
+
return
|
57
|
+
end
|
58
|
+
|
59
|
+
return if try_simply_continue(ghost, dirs)
|
60
|
+
|
61
|
+
dir = nil
|
62
|
+
if (ghost.x - @level.player.x).abs > (ghost.y - @level.player.y).abs
|
63
|
+
# prefer horizontal direction
|
64
|
+
if ghost.x - @level.player.x > 1
|
65
|
+
dir = :left if can_move_left?(ghost)
|
66
|
+
else
|
67
|
+
dir = :right if can_move_right?(ghost)
|
68
|
+
end
|
69
|
+
|
70
|
+
if dir.nil?
|
71
|
+
if ghost.y - @level.player.y > 1
|
72
|
+
dir = :up if can_move_up?(ghost)
|
73
|
+
else
|
74
|
+
dir = :down if can_move_down?(ghost)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
else
|
78
|
+
# prefer vertical direction
|
79
|
+
if ghost.y - @level.player.y > 1
|
80
|
+
dir = :up if can_move_up?(ghost)
|
81
|
+
else
|
82
|
+
dir = :down if can_move_down?(ghost)
|
83
|
+
end
|
84
|
+
|
85
|
+
if dir.nil?
|
86
|
+
if ghost.y - @level.player.y > 1
|
87
|
+
dir = :left if can_move_left?(ghost)
|
88
|
+
else
|
89
|
+
dir = :right if can_move_right?(ghost)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
dir = random_direction(dirs) if dir.nil?
|
94
|
+
move(ghost, dir)
|
95
|
+
end
|
96
|
+
|
97
|
+
def behave_slow_random(ghost, dirs)
|
98
|
+
if ghost.stopped
|
99
|
+
ghost.stopped = false
|
100
|
+
return
|
101
|
+
end
|
102
|
+
|
103
|
+
ghost.stopped = true
|
104
|
+
|
105
|
+
behave_random(ghost, dirs)
|
106
|
+
end
|
107
|
+
|
108
|
+
def behave_random(ghost, dirs)
|
109
|
+
return if dirs.nil?
|
110
|
+
|
111
|
+
if continue_forward?(ghost, dirs)
|
112
|
+
move(ghost, ghost.direction)
|
113
|
+
else
|
114
|
+
if (dirs.size == 2)
|
115
|
+
if (dirs.first == inverse_direction(ghost.direction))
|
116
|
+
move(ghost, dirs.last)
|
117
|
+
else
|
118
|
+
move(ghost, dirs.first)
|
119
|
+
end
|
120
|
+
return
|
121
|
+
end
|
122
|
+
|
123
|
+
dir = random_direction(dirs)
|
124
|
+
|
125
|
+
move(ghost, dir)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# return possible directions if decision needed, nil otherwise
|
130
|
+
def update_ghost(ghost)
|
131
|
+
dirs = possible_directions(ghost)
|
132
|
+
case
|
133
|
+
when dirs.size > 1
|
134
|
+
return dirs
|
135
|
+
when dirs.size == 1
|
136
|
+
move(ghost, dirs.first)
|
137
|
+
end
|
138
|
+
nil
|
139
|
+
end
|
140
|
+
|
141
|
+
def possible_directions(ghost)
|
142
|
+
res = []
|
143
|
+
res << :left if can_move_left?(ghost)
|
144
|
+
res << :right if can_move_right?(ghost)
|
145
|
+
res << :up if can_move_up?(ghost)
|
146
|
+
res << :down if can_move_down?(ghost)
|
147
|
+
|
148
|
+
res
|
149
|
+
end
|
150
|
+
|
151
|
+
def random_direction(dirs)
|
152
|
+
dir = nil
|
153
|
+
loop do
|
154
|
+
d = rand(4)
|
155
|
+
case d
|
156
|
+
when 0
|
157
|
+
dir = :left
|
158
|
+
when 1
|
159
|
+
dir = :right
|
160
|
+
when 2
|
161
|
+
dir = :up
|
162
|
+
when 3
|
163
|
+
dir = :down
|
164
|
+
end
|
165
|
+
break if dirs.include?(dir)
|
166
|
+
end
|
167
|
+
dir
|
168
|
+
end
|
169
|
+
|
170
|
+
def can_move_left?(ghost)
|
171
|
+
return false unless ghost.x > 0
|
172
|
+
obj = @level.get(ghost.x - 1, ghost.y)
|
173
|
+
obj.nil? || obj.passable?
|
174
|
+
end
|
175
|
+
|
176
|
+
def can_move_right?(ghost)
|
177
|
+
return false unless ghost.y < @level.width - 1
|
178
|
+
obj = @level.get(ghost.x + 1, ghost.y)
|
179
|
+
obj.nil? || obj.passable?
|
180
|
+
end
|
181
|
+
|
182
|
+
def can_move_up?(ghost)
|
183
|
+
return false unless ghost.y > 0
|
184
|
+
obj = @level.get(ghost.x, ghost.y - 1)
|
185
|
+
obj.nil? || obj.passable?
|
186
|
+
end
|
187
|
+
|
188
|
+
def can_move_down?(ghost)
|
189
|
+
return false unless ghost.y < @level.height
|
190
|
+
obj = @level.get(ghost.x, ghost.y + 1)
|
191
|
+
obj.nil? || obj.passable?
|
192
|
+
end
|
193
|
+
|
194
|
+
def continue_forward?(ghost, dirs)
|
195
|
+
return false if dirs.size > 2
|
196
|
+
return true if dirs.size == 1 && ghost.direction == dirs.first
|
197
|
+
(dirs.include?(:left) && dirs.include?(:right) &&
|
198
|
+
(ghost.direction == :left || ghost.direction == :right)) ||
|
199
|
+
(dirs.include?(:up) && dirs.include?(:down) &&
|
200
|
+
(ghost.direction == :up || ghost.direction == :down))
|
201
|
+
end
|
202
|
+
|
203
|
+
def inverse_direction(direction)
|
204
|
+
case direction
|
205
|
+
when :left
|
206
|
+
:right
|
207
|
+
when :right
|
208
|
+
:left
|
209
|
+
when :up
|
210
|
+
:down
|
211
|
+
when :down
|
212
|
+
:up
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def move(ghost, direction)
|
217
|
+
if colission_with_player?(ghost)
|
218
|
+
@colission = true
|
219
|
+
return
|
220
|
+
end
|
221
|
+
|
222
|
+
ghost.direction = direction
|
223
|
+
case direction
|
224
|
+
when :left
|
225
|
+
ghost.x -= 1
|
226
|
+
when :right
|
227
|
+
ghost.x += 1
|
228
|
+
when :up
|
229
|
+
ghost.y -= 1
|
230
|
+
when :down
|
231
|
+
ghost.y += 1
|
232
|
+
end
|
233
|
+
@colission = true if colission_with_player?(ghost)
|
234
|
+
end
|
235
|
+
|
236
|
+
def colission_with_player?(ghost)
|
237
|
+
ghost.x == @level.player.x && ghost.y == @level.player.y
|
238
|
+
end
|
239
|
+
|
240
|
+
def try_simply_continue(ghost, dirs)
|
241
|
+
if (dirs.size == 2)
|
242
|
+
# neotoci se uprostred cesty
|
243
|
+
if (dirs.first == inverse_direction(ghost.direction))
|
244
|
+
move(ghost, dirs.last)
|
245
|
+
else
|
246
|
+
move(ghost, dirs.first)
|
247
|
+
end
|
248
|
+
return true
|
249
|
+
end
|
250
|
+
false
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|