pacmanx 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|