-
Notifications
You must be signed in to change notification settings - Fork 0
/
ghost.py
337 lines (309 loc) · 12.3 KB
/
ghost.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
"""
Author Paul Brace April 2024
PacMan game developed using PyGame
Ghost class and functions
"""
import pygame
from game_sprite import GameSprite
from constants import *
import random
pygame.init()
# Speed reached at level 6 and above
ghost_mex_speed = 3.33
# number of frames between change of random target
random_interval = 250
# Ghosts will look in the direction of movement
ghost_image = [
[ # blinky
pygame.image.load('images/BlinkyUp.png'), # For hold
pygame.image.load('images/BlinkyLeft.png'),
pygame.image.load('images/BlinkyRight.png'),
pygame.image.load('images/BlinkyUp.png'),
pygame.image.load('images/BlinkyDown.png')
],
[ # pinky
pygame.image.load('images/PinkyUp.png'),
pygame.image.load('images/PinkyLeft.png'),
pygame.image.load('images/PinkyRight.png'),
pygame.image.load('images/PinkyUp.png'),
pygame.image.load('images/PinkyDown.png')
],
[ # inky
pygame.image.load('images/InkyUp.png'),
pygame.image.load('images/InkyLeft.png'),
pygame.image.load('images/InkyRight.png'),
pygame.image.load('images/InkyUp.png'),
pygame.image.load('images/InkyDown.png')
],
[ # clyde
pygame.image.load('images/ClydeUp.png'),
pygame.image.load('images/ClydeLeft.png'),
pygame.image.load('images/ClydeRight.png'),
pygame.image.load('images/ClydeUp.png'),
pygame.image.load('images/ClydeDown.png')
]
]
frightened = pygame.image.load('images/frightened.png')
frightenedW = pygame.image.load('images/frightened2.png')
caught = pygame.image.load('images/caught.png')
caught_sound = pygame.mixer.Sound('sounds/eatghost.wav')
# Ghosts
BLINKY = 0
PINKY = 1
INKY = 2
CLYDE = 3
# Score increases the more ghosts caught in a fright cycle
ghost_score = [200, 400, 800, 1600]
# The number of dots eaten before the release of each ghost type
delay_to_release = [1, 10, 30, 90]
delay_to_release_after_caught = [1, 5, 15, 25]
# Ghost modes
CHASE = 0
SCATTER = 1
FRIGHTENED = 2
RANDOM = 3
CAUGHT = 4
TO_PEN = 5
# Used if ghost cannot move towards the target to cycle through to find a
# direction that it can move
cycle_order = [
# for target x > 0 and > target y > 0
[RIGHT, DOWN, LEFT, UP],
# for target x < 0 and > target y > 0
[LEFT, DOWN, RIGHT, UP],
# for target x > 0 and > target y < 0
[RIGHT, UP, LEFT, DOWN],
# for target x < 0 and > target y < 0
[LEFT, UP, RIGHT, DOWN],
# for target x > 0 and < target y > 0
[DOWN, RIGHT, UP, LEFT],
# for target x < 0 and < target y > 0
[DOWN, LEFT, UP, RIGHT],
# for target x > 0 and < target y < 0
[UP, RIGHT, DOWN, LEFT],
# for target x < 0 and < target y < 0
[UP, LEFT, DOWN, RIGHT]
]
class Ghost(GameSprite):
# Set when an energiser eaten
fright_timer = 0
# The positions ghosts move to when released from pen
ghost_exit_point = ()
def __init__(self, gtype, x, y):
self.gtype = gtype
# set grid position
x = x * 20 + 20
y = y * 20 + 40
if gtype == BLINKY:
# Center between bricks
x -= 10
# Set the ghost exit point
Ghost.ghost_exit_point = (x, y)
# use frightened just to initialise
super().__init__(frightened, x, y)
self.start_position = (x, y)
self.speed = ghost_mex_speed
self.speed_for_level = ghost_mex_speed
# Just set to a random position as will be set as soon as ghost released
self.target = (400, 600)
self.last_target = (400, 600)
self.mode = CHASE
self.current_direction = HOLD
self.delay = 0
self.set_default_mode(False)
self.random_timer = random_interval
self.set_delay()
def reverse_direction(self):
# reverses the direction of movement - called on mode changes
if self.current_direction == LEFT:
self.current_direction = RIGHT
elif self.current_direction == RIGHT:
self.current_direction = LEFT
elif self.current_direction == UP:
self.current_direction = DOWN
else:
self.current_direction = UP
def set_default_mode(self, reverse):
# Set default image and mode of movement
if self.mode != CAUGHT:
self.image = ghost_image[self.gtype][HOLD]
if self.gtype == CLYDE:
self.mode = RANDOM
else:
self.mode = CHASE
if reverse:
self.reverse_direction()
self.speed = self.speed_for_level
def set_speed_percent(self, perc):
# Adjust the speed of the ghost
if perc <= 100 and perc >= 0:
self.speed = ghost_mex_speed * perc / 100
self.speed_for_level = self.speed
def set_scatter_mode(self):
# Set scatter mode
if self.mode != CAUGHT:
self.mode = SCATTER
self.reverse_direction()
def set_frightened_mode(self):
# set frightened mode
self.mode = FRIGHTENED
self.image = frightened
self.reverse_direction()
# reduce speed in frightened mode
self.speed = self.speed * 0.66
def return_to_pen(self):
# Ghost caught so set to return to pen
caught_sound.play()
self.image = caught
self.mode = CAUGHT
self.speed = ghost_mex_speed * 2
def set_delay(self):
# Set the delay in dots eaten before ghost can leave the pen
self.delay = delay_to_release[self.gtype]
def reduce_delay(self):
# called when a dot is eaten
if self.delay > 0:
self.delay -= 1
def jump_to_start(self):
# return ghost to its start position
self.x = self.start_position[0]
self.y = self.start_position[1]
self.mode = CHASE
self.set_default_mode(False)
self.set_delay()
self.speed = self.speed_for_level
self.current_direction = HOLD
def set_direction_image(self, direction):
if self.mode != FRIGHTENED and self.mode != CAUGHT:
# Set image for direction
self.image = ghost_image[self.gtype][direction]
def set_dirction(self, pacman):
if self.delay <= 0:
# If currently hold then in the pen so position ghost outside the pen
# If self.x == self.start_position[0] and self.y == self.start_position[1]:
if self.current_direction == HOLD:
self.x = Ghost.ghost_exit_point[0]
self.y = Ghost.ghost_exit_point[1]
# Set target grid cell based on ghost and mode
if self.mode == CHASE:
if self.gtype == BLINKY or self.gtype == CLYDE:
# never put in chase mode but here as a catch-all
# set target to players current position
self.target = (pacman.x, pacman.y)
elif self.gtype == PINKY:
# set target ahead of player
if pacman.current_direction == LEFT or pacman.current_direction == HOLD:
self.target = (pacman.x - 80, pacman.y)
elif pacman.current_direction == RIGHT:
self.target = (pacman.x + 80, pacman.y)
elif pacman.current_direction == UP:
self.target = (pacman.x, pacman.y - 80)
elif pacman.current_direction == DOWN:
self.target = (pacman.x, pacman.y + 80)
elif self.gtype == INKY:
# set target behind player
if pacman.current_direction == LEFT or pacman.current_direction == HOLD:
self.target = (pacman.x + 80, pacman.y)
elif pacman.current_direction == RIGHT:
self.target = (pacman.x - 80, pacman.y)
elif pacman.current_direction == UP:
self.target = (pacman.x, pacman.y + 80)
elif pacman.current_direction == DOWN:
self.target = (pacman.x, pacman.y - 80)
elif self.mode == SCATTER:
# In scatter mode so set each ghosts target as a corner of the maze
if self.gtype == BLINKY:
self.target = (-200, -100)
elif self.gtype == PINKY:
self.target = (WIDTH + 200, -100)
elif self.gtype == INKY:
self.target = (-200, HEIGHT + 250)
elif self.gtype == CLYDE:
self.target = (WIDTH + 200, HEIGHT + 250)
elif self.mode == RANDOM or self.mode == FRIGHTENED:
# Move to a random target at each interval
self.random_timer += 1
if self.random_timer >= random_interval - 1:
self.last_target = (random.randint(0, WIDTH - 1), random.randint(0, HEIGHT - 1))
self.random_timer = 0
self.target = self.last_target
if self.mode == FRIGHTENED and Ghost.fright_timer < 120:
if Ghost.fright_timer % 15 == 0:
if self.image == frightened:
self.image = frightenedW
else:
self.image = frightened
elif self.mode == CAUGHT:
# Ghost has been caught so see if reached exit_point
if abs(Ghost.ghost_exit_point[0] - self.x) < 20 and abs(Ghost.ghost_exit_point[1] - self.y) < 20:
self.x = self.start_position[0]
self.y = self.start_position[1]
self.mode = CHASE
self.set_default_mode(False)
self.current_direction = HOLD
self.delay = delay_to_release_after_caught[self.gtype]
# OK to leave as exit_point as will be changed next refresh
self.target = Ghost.ghost_exit_point
# set direction to go to target and return
tx = self.target[0] - self.x
ty = self.target[1] - self.y
# Test to see if just escaped so default to either left or right
if self.current_direction == HOLD:
if tx > 0:
self.current_direction = RIGHT
else:
self.current_direction = LEFT
if abs(tx) > abs(ty):
# See if ghost can go in x direction of largest distance
# if not try and go y direction
if self.target[0] > self.x:
if self.current_direction != LEFT:
go_to = RIGHT
elif self.target[1] > self.y:
go_to = DOWN
else:
go_to = UP
elif self.current_direction != RIGHT:
go_to = LEFT
elif self.target[1] < self.y:
go_to = UP
else:
go_to = DOWN
else:
if self.target[1] > self.y:
if self.current_direction != UP:
go_to = DOWN
elif self.target[0] > self.x:
go_to = RIGHT
else:
go_to = LEFT
elif self.current_direction != DOWN:
go_to = UP
elif self.target[0] > self.x:
go_to = RIGHT
else:
go_to = LEFT
return go_to
else:
return HOLD
def get_order(self):
# set new direction sequence to try
tx = self.target[0] - self.x
ty = self.target[1] - self.y
if tx > 0 and ty > 0 and abs(tx) > abs(ty):
order = cycle_order[0]
elif tx > 0 and ty > 0 and abs(tx) < abs(ty):
order = cycle_order[4]
elif tx < 0 and ty > 0 and abs(tx) > abs(ty):
order = cycle_order[1]
elif tx < 0 and ty > 0 and abs(tx) < abs(ty):
order = cycle_order[5]
elif tx > 0 and ty < 0 and abs(tx) > abs(ty):
order = cycle_order[2]
elif tx > 0 and ty < 0 and abs(tx) < abs(ty):
order = cycle_order[6]
elif tx < 0 and ty < 0 and abs(tx) > abs(ty):
order = cycle_order[3]
else:
order = cycle_order[7]
return order