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.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +5 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +19 -0
  5. data/Rakefile +2 -0
  6. data/bin/pacmanx +7 -0
  7. data/bin/pacman~ +7 -0
  8. data/lib/pacman/animated_object.rb +10 -0
  9. data/lib/pacman/cage.rb +14 -0
  10. data/lib/pacman/controller.rb +127 -0
  11. data/lib/pacman/ghost.rb +41 -0
  12. data/lib/pacman/ghosts_controller.rb +253 -0
  13. data/lib/pacman/ghosts_renderer.rb +70 -0
  14. data/lib/pacman/level.rb +62 -0
  15. data/lib/pacman/level_builder.rb +129 -0
  16. data/lib/pacman/level_renderer.rb +100 -0
  17. data/lib/pacman/levels/level1.lvl +98 -0
  18. data/lib/pacman/media/bg2_2.jpg +0 -0
  19. data/lib/pacman/media/blinky_down.png +0 -0
  20. data/lib/pacman/media/blinky_left.png +0 -0
  21. data/lib/pacman/media/blinky_right.png +0 -0
  22. data/lib/pacman/media/blinky_up.png +0 -0
  23. data/lib/pacman/media/clyde_down.png +0 -0
  24. data/lib/pacman/media/clyde_left.png +0 -0
  25. data/lib/pacman/media/clyde_right.png +0 -0
  26. data/lib/pacman/media/clyde_up.png +0 -0
  27. data/lib/pacman/media/inky_down.png +0 -0
  28. data/lib/pacman/media/inky_left.png +0 -0
  29. data/lib/pacman/media/inky_right.png +0 -0
  30. data/lib/pacman/media/inky_up.png +0 -0
  31. data/lib/pacman/media/pacman_down.png +0 -0
  32. data/lib/pacman/media/pacman_left.png +0 -0
  33. data/lib/pacman/media/pacman_right.png +0 -0
  34. data/lib/pacman/media/pacman_up.png +0 -0
  35. data/lib/pacman/media/pellet.png +0 -0
  36. data/lib/pacman/media/pinky_down.png +0 -0
  37. data/lib/pacman/media/pinky_left.png +0 -0
  38. data/lib/pacman/media/pinky_right.png +0 -0
  39. data/lib/pacman/media/pinky_up.png +0 -0
  40. data/lib/pacman/media/power_pellet.png +0 -0
  41. data/lib/pacman/media/wall.png +0 -0
  42. data/lib/pacman/pacman.rb +4 -0
  43. data/lib/pacman/pellet.rb +16 -0
  44. data/lib/pacman/player.rb +12 -0
  45. data/lib/pacman/version.rb +4 -0
  46. data/lib/pacman/wall.rb +8 -0
  47. data/pacman_classes.png +0 -0
  48. data/pacmanx.gemspec +24 -0
  49. data/spec/level_builder_spec.rb +111 -0
  50. data/spec/level_spec.rb +69 -0
  51. data/spec/spec_helper.rb +7 -0
  52. metadata +141 -0
@@ -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
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in pacman.gemspec
4
+ gem "gosu", ">= 0.8.6"
5
+ gemspec
@@ -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.
@@ -0,0 +1,19 @@
1
+ # Pacman
2
+
3
+ Classic Pacman game remake
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'pacman'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install pacman
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pacman/controller'
4
+ # require 'controller'
5
+
6
+ game = Pacman::Game.new("lib/pacman/")
7
+ game.show
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pacman/pacman'
4
+ # require 'controller'
5
+
6
+ # game = Pacman::Game.new
7
+ # game.show
@@ -0,0 +1,10 @@
1
+ module Pacman
2
+ # Objext using animation
3
+ class AnimatedObject
4
+ def initialize
5
+ @animation_index = 0
6
+ end
7
+
8
+ attr_accessor :animation_index
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ module Pacman
2
+ # base point for ghosts
3
+ class Cage
4
+ def initialize(x, y)
5
+ @x, @y = x, y
6
+ end
7
+
8
+ def passable?
9
+ true
10
+ end
11
+
12
+ attr_accessor :x, :y
13
+ end
14
+ end
@@ -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
@@ -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