-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathmain.py
510 lines (390 loc) · 14.5 KB
/
main.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
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
# Import Modules
import pygame
import time
import random
import sys
# Import Classes
from player import Player
from platform_manager import Platform_Manager
from enemy_manager import Enemy_Manager
from boost_items import BoostItem, BoostItemManager
from menu import Menu
from background import Background
# Code Inspired by:
# PYGAME SETUP
# _______________________________________________________________________________________________________________________
# Initialize Pygame and Mixer
pygame.init()
pygame.mixer.init()
# Color Dictionary
colors = {
"black": (0, 0, 0),
"red": (255, 0, 0),
"green": (0, 255, 0),
"blue": (0, 0, 255),
"yellow": (255, 255, 0),
"cyan": (0, 255, 255),
"magenta": (255, 0, 255),
"silver": (192, 192, 192),
"gray": (128, 128, 128),
"maroon": (128, 0, 0),
"olive": (128, 128, 0),
"purple": (128, 0, 128),
"white": (255, 255, 255),
}
screen_width = 1000
screen_height = 700
fps = 60
# Pygame Tools
timer = pygame.time.Clock()
# Screen Variables
window = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("Jump Adventure")
# Background Objects
background_1 = Background(
y=0, path="Resources/Sprites/Sprite-background_1.png", screen=window, moving=False
)
background_2 = Background(
y=-700,
path="Resources/Sprites/Sprite-background_2.png",
screen=window,
moving=False,
)
background_3 = Background(
y=700, path="Resources/Sprites/Sprite-background_3.png", screen=window, moving=False
)
background_list = [background_1, background_2, background_3]
# Font Variables
font = pygame.font.SysFont(None, 55)
font_medium = pygame.font.SysFont(None, 30)
font_small = pygame.font.SysFont(None, 20)
game_font = pygame.font.SysFont("Consolas", 30)
# The following code inspried by a guide found on The Python Code
# 'How to Add Sound Effects to your Python Game' by Michael Maranan
# Available at: https://thepythoncode.com/article/add-sound-effects-to-python-game-with-pygame
# Load Sound Effects
jump_sound = pygame.mixer.Sound(
"Resources/Sounds/Jump-SoundEffect.wav"
) # Royalty Free Music: https://mixkit.co/
shoot_sound = pygame.mixer.Sound(
"Resources/Sounds/Shoot-SoundEffect.wav"
) # Royalty Free Music: https://mixkit.co/
power_sound = pygame.mixer.Sound(
"Resources/Sounds/PowerUp-SoundEffect.wav"
) # Royalty Free Music: https://mixkit.co/
game_over_sound = pygame.mixer.Sound(
"Resources/Sounds/GameOver-SoundEffect.wav"
) # Royalty Free Music: https://mixkit.co/
hit_sound = pygame.mixer.Sound("Resources/Sounds/EnemyImpact-SoundEffect.wav")
break_sound = pygame.mixer.Sound("Resources/Sounds/WoodHit-SoundEffect.wav")
# Load Game Play Music
game_play_music = pygame.mixer.Sound(
"Resources/Sounds/GamePlay-SoundEffect.mp3"
) # Royalty Free Music: https://www.bensound.com/
# pygame.mixer.music.load(game_play_music)
# FUNCTIONS
# _______________________________________________________________________________________________________________________
# Event Handler (For User Inputs)
def event_handler(menu_active, game_over) -> None:
global running
global player
global start_time
# Iterate Through Pygame Events
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if menu_active:
start_time = time.time()
menu.handle_input(event)
# Reseting Game / Back to Menu / Quit at Game Over Screen
elif not menu_active and game_over:
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_r:
init_new_game(False)
if event.key == pygame.K_m:
init_new_game(True)
if event.key == pygame.K_q:
pygame.quit()
sys.exit()
else:
# Key Presses
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
player.jump()
jump_sound.play()
if event.key == pygame.K_LEFT:
player.x_delta -= player.speed
if event.key == pygame.K_RIGHT:
player.x_delta += player.speed
if event.key == pygame.K_UP:
player.shoot()
shoot_sound.play()
if event.key == pygame.K_q:
running = False
# Key Releases
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
player.x_delta += player.speed
if event.key == pygame.K_RIGHT:
player.x_delta -= player.speed
# The following code inspried by a guide found on Pygame tutorial
# 'Work with text' by Raphael Holzer Revision b84b643a
# Available at: https://pygame.readthedocs.io/en/latest/4_text/text.html
# Create and Render Text on the Screen
def render_text(text, font, color, surface, x, y) -> None:
textobj = font.render(text, True, color)
textrect = textobj.get_rect()
textrect.center = (x, y)
surface.blit(textobj, textrect)
# Check and Save High Score Function
def check_and_save_high_score(score, menu) -> None:
if score > 0:
menu.save_high_score(score)
# Render Player Image
def render_player_image(screen: pygame.surface) -> None:
global player
player_image = pygame.image.load("Resources/Sprites/monke1.png")
player_image = pygame.transform.scale(player_image, (player.width, player.height))
screen.blit(player_image, (player.x, player.y))
# Render Platform Images
def render_platform_images(screen: pygame.surface) -> None:
global platform_manager
temp_image = None
image_path_dict = {
"base": "Resources/Sprites/Sprite-normal_log.png",
"horizontal": "Resources/Sprites/Sprite-moving_log.png",
"falling": "Resources/Sprites/Sprite-falling_log.png",
"disappearing": "Resources/Sprites/Sprite-breaking_log.png",
}
for plat in platform_manager.platform_list:
temp_image = pygame.image.load(image_path_dict[plat.type])
temp_image = pygame.transform.scale(temp_image, (plat.width, plat.height))
screen.blit(temp_image, (plat.x, plat.y))
# Render Enemy Images
def render_enemy_images(screen: pygame.surface) -> None:
global enemy_manager
temp_image = None
image_path_dict = {
"base": "Resources/Sprites/Sprite-base_enemy.png",
"bounce": "Resources/Sprites/Sprite-bouncing_enemy.png",
"chase": "Resources/Sprites/Sprite-following_enemy.png",
}
for enemy in enemy_manager.enemy_list:
temp_image = pygame.image.load(image_path_dict[enemy.type])
temp_image = pygame.transform.scale(temp_image, (enemy.width, enemy.height))
screen.blit(temp_image, (enemy.x, enemy.y))
# Render Projectile Images
def render_projectile_images(screen: pygame.surface) -> None:
global player
for proj in player.projectile_manager.projectile_list:
proj_image = pygame.image.load("Resources/Sprites/Sprite-banana.png")
proj_image = pygame.transform.scale(proj_image, (proj.width, proj.height))
screen.blit(proj_image, (proj.x, proj.y))
def render_boost_images(screen: pygame.surface) -> None:
global boost_item_manager
temp_image = None
image_path_dict = {
"parachute": "Resources/Sprites/Sprite-parachute_powerup.png",
"double_jump": "Resources/Sprites/Sprite-double_jump_powerup.png",
"shield": "Resources/Sprites/Sprite-shield_powerup.png",
}
for boost in boost_item_manager.items:
# Load Images and Scale
temp_image = pygame.image.load(image_path_dict[boost.boost_type])
temp_image = pygame.transform.scale(temp_image, (boost.width, boost.height))
screen.blit(temp_image, (boost.x, boost.y))
# Render Moving Backround
def render_background(game_over: bool) -> None:
for background in background_list:
background.render()
if game_over == False:
background.move()
# Render Power Up Buff Effect Icon
def render_icon_image(
screen: pygame.surface, path: str, x_pos: int, y_pos: int
) -> None:
icon_image = pygame.image.load(path)
icon_image = pygame.transform.scale(icon_image, (40, 40))
screen.blit(icon_image, (x_pos, y_pos))
def render_all_icons(screen: pygame.surface) -> None:
# if player effect is active, display image
if player.double_jump.active:
render_icon_image(
window,
"Resources/Sprites/Sprite-double_jump_powerup.png",
10,
(screen.get_height() - 50),
)
if player.shield.active:
render_icon_image(
window,
"Resources/Sprites/Sprite-shield_powerup.png",
60,
(screen.get_height() - 50),
)
if player.parachute.active:
render_icon_image(
window,
"Resources/Sprites/Sprite-parachute_powerup.png",
110,
(screen.get_height() - 50),
)
# Rendering all Game Images
def render_game_images(window: pygame.surface) -> None:
# Render Player
render_player_image(window)
# Render Platforoms
render_platform_images(window)
# Render Enemies
render_enemy_images(window)
# Render Boost Images
render_boost_images(window)
# Render Projectiles
render_projectile_images(window)
# Render Power Up Icons When Active
render_all_icons(window)
# GAME SETUP
# _______________________________________________________________________________________________________________________
# Create Menu Object
menu = Menu(window, font, colors)
# Create Player Object
player = Player(
power_sound=power_sound,
hit_sound=hit_sound,
break_sound=break_sound,
)
# Create Platform Manager
platform_manager = Platform_Manager()
# Create Enemy Manager
enemy_manager = Enemy_Manager(player_x=player.x, player_y=player.y)
# Create Boost Item Manager
boost_item_manager = BoostItemManager(screen_width, screen_height, player)
# Game Loop
running = True
game_over = False
start_time = time.time()
final_time = 0
level = 1
level_time = time.time()
# Function to Reset / Start the Game
def init_new_game(menu_status: bool) -> None:
# Get Global Variables
global menu, game_over, start_time, final_time, level, player, platform_manager, enemy_manager, boost_item_manager, level_time
# Return to Menu or not
if menu_status:
menu = Menu(window, font, colors)
# Reset Globals
game_over = False
start_time = time.time()
final_time = 0
level = 1
level_time = time.time()
player = Player(
power_sound=power_sound,
hit_sound=hit_sound,
break_sound=break_sound,
)
platform_manager = Platform_Manager()
enemy_manager = Enemy_Manager(player_x=player.x, player_y=player.y)
boost_item_manager = BoostItemManager(screen_width, screen_height, player)
# Function to Increase the Level
def increase_level() -> None:
global level, enemy_manager, platform_manager, start_time, level_time
# Every 20 Seconds, Increase Level by One
if time.time() - level_time > 10.0:
level_time = time.time()
level += 1
# Increment Platform and Enemy Manager
platform_manager.increment_difficulty(level)
enemy_manager.increment_difficulty(level)
while running:
# Handle Menu Actions
if menu.active:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
menu.handle_input(event)
menu.draw()
# Start The Game
if not menu.active:
init_new_game(False)
pygame.mixer.stop()
game_play_music.play(-1)
else:
# Pygame Variables
timer.tick(fps)
window.fill((255, 255, 255))
render_background(game_over)
if not game_over:
# Render Actors
player.get_rect()
# Update Player position for Enemey Location
enemy_manager.player_x = player.x + (player.width // 2)
enemy_manager.player_y = player.y + (player.height // 2)
# Collision Detection
player.handle_player_platforms(platform_manager, window)
player.enemy_collision(enemy_manager)
# Update Actors and Check for Game Over (TODO: Summarize in Function)
player.update(window, enemy_manager)
# Spawn items at random intervals
if random.random() < 0.01: # Adjust frequency as needed
boost_item_manager.spawn_item()
# Manage Platforms
platform_manager.manage_platforms(window, colors)
# Manage Enemies
enemy_manager.manage_enemies(window)
# Update Boost Items
boost_item_manager.update_items()
# Increase Level (Dynamic Scaling)
increase_level()
# Capture the time at game over
if not player.alive:
final_time = time.time() - start_time
game_over = True
game_over_sound.play()
# Render Images
render_game_images(window)
# Display the Timer/Score
elapsed_time = final_time if game_over else time.time() - start_time
score_text = f"Score: {int(elapsed_time)}"
level_text = f"Level: {level}"
# Positioned near upper right corner
render_text(
score_text, game_font, colors["black"], window, screen_width - 920, 20
)
render_text(
level_text, game_font, colors["black"], window, screen_width - 922, 50
)
if game_over:
render_text(
"Game Over",
font,
colors["red"],
window,
screen_width / 2,
screen_height / 2,
)
check_and_save_high_score(int(elapsed_time), menu)
render_text(
f"Final Score: {int(elapsed_time)}, Level: {int(level)}",
font_medium,
colors["red"],
window,
screen_width / 2,
(screen_height / 2) + 30,
)
render_text(
"Press R to Reset, Press for Main Menu, Press Q to Quit",
font_small,
colors["black"],
window,
screen_width / 2,
(screen_height / 2) + 50,
)
# Event Handler
event_handler(menu.active, game_over)
# Update Display
pygame.display.flip()
pygame.quit()