diff --git a/.gitignore b/.gitignore index 894a44c..14d6198 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +.vscode/ +.json + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/CHANGELOG.md b/CHANGELOG.md index 36f19dd..e44a0a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,11 @@ ## New Features -1. Added new level. -2. New level will be selected after a player kills another player. +None... ## Changes -1. Increased bullet speed (again). +1. Increased explosion radius of Missiles. +2. Missiles will no longer naturally despawn. +3. Powerups will now have a chance of spawning at fixed intervals in the game, intead of spawning at random times. This will prevent overcrowding of the powerups. +4. Fixed Bug where powerups would overlap with walls. diff --git a/README.md b/README.md index 29a1277..f731d71 100644 --- a/README.md +++ b/README.md @@ -25,15 +25,15 @@ To install this game follow these steps: Right-click the file `start_win.bat` And click Run as Administator. **MacOS and Linux**: -Open the game folder in Terminal and enter `sh start_unix.sh`. +Open the game folder in Terminal and enter `./start_unix.sh`. ## How to play **Player 1:** -A and D for turning, W for accelerating and S for shooting. +A and D for turning, W for accelerating and S for shooting. Press E for using a PowerUp (when available). **Player 2:** -Left and right arrow keys for turning, up arrow key for accelerating and down arrow key for shooting. +Left and right arrow keys for turning, up arrow key for accelerating and down arrow key for shooting. Press Right Shift for using a PowerUp (when available). ## Issues diff --git a/scripts/bullet.py b/scripts/bullet.py index ffb5e04..46c41ac 100644 --- a/scripts/bullet.py +++ b/scripts/bullet.py @@ -16,7 +16,7 @@ def __init__(self, x, y, hdg, color): self.draw_x = self.x - self.radius self.draw_y = self.y - self.radius self.heading = hdg - self.speed = 25 + self.speed = 18 self.x_vel = 0 self.y_vel = 0 self.color = color diff --git a/scripts/main.py b/scripts/main.py index 410b283..24e1f07 100644 --- a/scripts/main.py +++ b/scripts/main.py @@ -13,6 +13,7 @@ from rocket import Rocket from bullet import Bullet from wall import Wall +from powerups import PowerUp, ScatterShot, Missile class AstroRockets: @@ -24,13 +25,14 @@ def __init__(self, size, title, background): self.clock = pygame.time.Clock() self.FPS = 60 self.running = True + self.game_over = False self.win = pygame.display.set_mode(size) pygame.display.set_caption(title) - # ###################### # - # ### Ignore this 🔼 ### # - # ###################### # + # ##################### # + # #### Ignore this #### # + # ##################### # def start(self): self.p1 = Rocket(self.width / 2 - 250, self.height / 2, (255, 0, 0)) @@ -45,19 +47,48 @@ def start(self): self.current_level = randint(0 ,len(self.levels)-1) + self.powerup_ids = [ + "none", # id = 0 + "scatter_shot", # id = 1 + "missile" # id = 2 + ] + + self.powerups = [] + self.powerup_spawn_timer = 0 + def logic(self): + # Randomly spawn powerups + if self.powerup_spawn_timer % 600 == 0: + if randint(0, 100) <= 50: # 50% chance of a powerups spawning + powerup_id = self.powerup_ids[randint(1, len(self.powerup_ids)-1)] + x = randint(5, self.width - 5) + y = randint(5, self.height - 5) + + if powerup_id == "scatter_shot": + powerup = ScatterShot(x, y) + self.powerups.append(powerup) + + if powerup_id == "missile": + powerup = Missile(x, y) + self.powerups.append(powerup) + keys = pygame.key.get_pressed() # speed = 5 - acceleration = 0.3 + acceleration = 0.25 turn_speed = 3 - friction = 0.974 + friction = 0.978 recoil = -5 + despawn_time = 300 + + scattershot_count = 16 + scattershot_speed = 12 + + missile_speed = 10 + missile_fade = 150 # Player 1 controls if keys[pygame.K_w]: self.p1.accelerate(acceleration) - - self.p1.speed *= friction if keys[pygame.K_s]: if not self.p1.bullet_is_shot: @@ -73,11 +104,31 @@ def logic(self): if keys[pygame.K_d]: self.p1.drift_heading += turn_speed + # PowerUp Shooting for Player 1 + if keys[pygame.K_e]: + + if self.powerup_ids[self.p1.current_powerup] == "none": + print("No Powerup Collected!") + + elif self.powerup_ids[self.p1.current_powerup] == "scatter_shot": + for i in range(scattershot_count): + bullet_hdg = ((360 / scattershot_count) * i) + self.p1.drift_heading + b = Bullet(self.p1.x, self.p1.y, bullet_hdg, ScatterShot(0, 0).color) + b.speed = scattershot_speed + self.p1.bullets.append(b) + + elif self.powerup_ids[self.p1.current_powerup] == "missile": + m = Bullet(self.p1.x, self.p1.y, self.p1.drift_heading, Missile(0, 0).color) + m.speed = missile_speed + self.p1.bullets.append(m) + self.p1.accelerate(recoil) + + self.p1.assign_powerup(0) + + # Player 2 controls if keys[pygame.K_UP]: self.p2.accelerate(acceleration) - - self.p2.speed *= friction if keys[pygame.K_DOWN]: if not self.p2.bullet_is_shot: @@ -93,11 +144,34 @@ def logic(self): if keys[pygame.K_RIGHT]: self.p2.drift_heading += turn_speed + # PowerUp Shooting for Player 2 + if keys[pygame.K_RSHIFT] or keys[pygame.K_QUESTION]: + + if self.powerup_ids[self.p2.current_powerup] == "none": + print("No Powerup Collected!") + + elif self.powerup_ids[self.p2.current_powerup] == "scatter_shot": + for i in range(scattershot_count): + bullet_hdg = ((360 / scattershot_count) * i) + self.p2.drift_heading + b = Bullet(self.p2.x, self.p2.y, bullet_hdg, ScatterShot(0, 0).color) + b.speed = scattershot_speed + self.p2.bullets.append(b) + + elif self.powerup_ids[self.p2.current_powerup] == "missile": + m = Bullet(self.p2.x, self.p2.y, self.p2.drift_heading, Missile(0, 0).color) + m.speed = missile_speed + self.p2.bullets.append(m) + self.p2.accelerate(recoil) + + self.p2.assign_powerup(0) + self.p1.update() + self.p1.speed *= friction + self.p2.update() + self.p2.speed *= friction - despawn_time = 200 - + # Update Player 1's Bullets for bullet in self.p1.bullets: bullet.update() @@ -113,14 +187,19 @@ def logic(self): bullet.update_collider() + # Collision between player 2 and player 1's bullets if self.collision_circle(bullet.x, bullet.y, bullet.radius, self.p2.x, self.p2.y, self.p2.collider_size): - print("RED WINS!!") - sleep(3) - self.running = False + if bullet.color != Missile(0, 0).color: + print("RED WINS!!") + sleep(3) + self.running = False + # Automatic despawn if bullet.despawn_timer > despawn_time: - self.p1.bullets.remove(bullet) + if bullet.color != Missile(0, 0).color: + self.p1.bullets.remove(bullet) + # Update Player 2's Bullets for bullet in self.p2.bullets: bullet.update() @@ -136,13 +215,17 @@ def logic(self): bullet.update_collider() + # Collisions between player 1 and player 2's bullets if self.collision_circle(bullet.x, bullet.y, bullet.radius, self.p1.x, self.p1.y, self.p1.collider_size): - print("BLUE WINS!!") - sleep(3) - self.running = False + if bullet.color != Missile(0, 0).color: + print("BLUE WINS!!") + sleep(3) + self.running = False + # Automatic despawn if bullet.despawn_timer > despawn_time: - self.p2.bullets.remove(bullet) + if bullet.color != Missile(0, 0).color: + self.p2.bullets.remove(bullet) # Player 1 boundary collisions @@ -170,7 +253,7 @@ def logic(self): self.p1.update_collider() self.p2.update_collider() - # Handle collisions between rockets and walls + # Handle collisions between rockets and walls, and powerups and walls for wall in self.levels[self.current_level]: # Player 1 if wall.collider.colliderect(self.p1.wall_collider): @@ -184,22 +267,87 @@ def logic(self): self.p2.y = self.p2.old_y self.p2.speed = 0 + for powerup in self.powerups: + if wall.collider.colliderect(powerup.wall_collider): + x = randint(5, self.width - 5) + y = randint(5, self.height - 5) + powerup.x = x + powerup.y = y + # Handle collisions between bullets and walls for wall in self.levels[self.current_level]: # Player 1 for bullet in self.p1.bullets: if bullet.wall_collider.colliderect(wall.collider): + if bullet.color == Missile(0, 0).color: + self.p1.missile_exploded = True + self.p1.missile_explosion_x = bullet.x + self.p1.missile_explosion_y = bullet.y + if self.dist(bullet.x, bullet.y, self.p1.x, self.p1.y) <= self.p1.collider_size + self.p1.missile_explosion_radius: + print("BLUE WINS") + self.game_over = True + + if self.dist(bullet.x, bullet.y, self.p2.x, self.p2.y) <= self.p2.collider_size + self.p2.missile_explosion_radius: + print("RED WINS") + self.game_over = True + self.p1.bullets.remove(bullet) # Player 2 for bullet in self.p2.bullets: if bullet.wall_collider.colliderect(wall.collider): + if bullet.color == Missile(0, 0).color: + self.p2.missile_exploded = True + self.p2.missile_explosion_x = bullet.x + self.p2.missile_explosion_y = bullet.y + if self.dist(bullet.x, bullet.y, self.p1.x, self.p1.y) <= self.p1.collider_size + self.p1.missile_explosion_radius: + print("BLUE WINS") + self.game_over = True + + if self.dist(bullet.x, bullet.y, self.p2.x, self.p2.y) <= self.p2.collider_size + self.p2.missile_explosion_radius: + print("RED WINS") + self.game_over = True + self.p2.bullets.remove(bullet) + # Handle collisions between rockets and powerups + for powerup in self.powerups: + # Player 1 + if self.collision_circle(self.p1.x, self.p1.y, self.p1.collider_size, powerup.x, powerup.y, powerup.radius): + self.p1.assign_powerup(powerup.id) + self.powerups.remove(powerup) + + # Player 2 + if self.collision_circle(self.p2.x, self.p2.y, self.p2.collider_size, powerup.x, powerup.y, powerup.radius): + self.p2.assign_powerup(powerup.id) + self.powerups.remove(powerup) + self.p1.update_old_coords() self.p2.update_old_coords() + # Missile Explosion Fade Player 1 + if self.p1.missile_exploded: + self.p1.missile_timer += 1 + + if self.p1.missile_timer > missile_fade: + self.p1.missile_exploded = False + if self.game_over: + self.running = False + + # Missile Explosion Fade Player 2 + if self.p2.missile_exploded: + self.p2.missile_timer += 1 + + if self.p2.missile_timer > missile_fade: + self.p2.missile_exploded = False + if self.game_over: + self.running = False + + self.powerup_spawn_timer += 1 + def render(self): + self.win.fill(self.background) + self.p1.render(self.win) self.p2.render(self.win) @@ -212,8 +360,45 @@ def render(self): for wall in self.levels[self.current_level]: wall.render(self.win) + for powerup in self.powerups: + powerup.render(self.win) + + if self.p1.missile_exploded: + pygame.draw.ellipse(self.win, (255, 255, 255), (self.p1.missile_explosion_x - self.p1.missile_explosion_radius, + self.p1.missile_explosion_y - self.p1.missile_explosion_radius, + self.p1.missile_explosion_radius * 2, + self.p1.missile_explosion_radius * 2 + )) + + if self.p2.missile_exploded: + pygame.draw.ellipse(self.win, (255, 255, 255), (self.p2.missile_explosion_x - self.p2.missile_explosion_radius, + self.p2.missile_explosion_y - self.p2.missile_explosion_radius, + self.p2.missile_explosion_radius * 2, + self.p2.missile_explosion_radius * 2 + )) + + pygame.display.update() + # Gameplay functions + # def get_spawn_location(self): + # """Finds a suitable random spawn location for a powerup, staying within the + # boundaries and avoiding all walls. + + # Returns: + # tuple -- Contains the x and y coordinates for the suitable location + # """ + + # x = randint(5, self.width - 5) + # y = randint(5, self.height - 5) + # size = ScatterShot(0, 0).radius + # rect = pygame.Rect(x - size, y - size, size * 2, size * 2) + # for wall in self.levels[self.current_level]: + # if wall.collider.colliderect(rect): + # self.get_spawn_location() + + # return (x, y) + def dist(self, x1, y1, x2, y2): """Finds the distance between 2 points on a 2D plane using the Pythagorean Theorem. a^2 + b^2 = c^2 @@ -297,9 +482,7 @@ def main(): quit() game.logic() - game.win.fill(game.background) game.render() - pygame.display.update() if __name__ == "__main__": diff --git a/scripts/powerups.py b/scripts/powerups.py new file mode 100644 index 0000000..9846def --- /dev/null +++ b/scripts/powerups.py @@ -0,0 +1,50 @@ +""" +Produced by Rehatbir Singh(MysteryCoder456) +Contact at rehatbir.singh@gmail.com for any questions +Full Rights Reserved under MIT License +""" + +import pygame + + +# Base Class for PowerUps +class PowerUp: + def __init__(self, x, y): + self.id = 0 + + self.x = x + self.y = y + self.radius = 12 + self.color = (255, 255, 255) + self.draw_x = self.x - self.radius + self.draw_y = self.y - self.radius + self.wall_collider = pygame.Rect(self.draw_x, self.draw_y, self.radius * 2, self.radius * 2) + + def render(self, window): + pygame.draw.ellipse(window, self.color, (self.draw_x, self.draw_y, self.radius * 2, self.radius * 2)) + + +# Subclasses of PowerUp base class (Arranged by ID number) + +class ScatterShot(PowerUp): + def __init__(self, x, y): + super().__init__(x, y) + + self.id = 1 + + self.color = (30, 230, 209) + + +class Missile(PowerUp): + def __init__(self, x, y): + super().__init__(x, y) + + self.id = 2 + + self.color = (149, 0, 2) + self.inner_color = (251, 251, 251) + self.inner_radius = self.radius / 2 + + def render(self, window): + pygame.draw.ellipse(window, self.color, (self.draw_x, self.draw_y, self.radius * 2, self.radius * 2)) + pygame.draw.ellipse(window, self.inner_color, (self.x - self.inner_radius, self.y - self.inner_radius, self.inner_radius * 2, self.inner_radius * 2)) diff --git a/scripts/rocket.py b/scripts/rocket.py index 15ecd4c..ecae27a 100644 --- a/scripts/rocket.py +++ b/scripts/rocket.py @@ -27,9 +27,20 @@ def __init__(self, x, y, color): self.color = color self.collider_size = 20 self.wall_collider = pygame.Rect(self.x - self.collider_size, self.y - self.collider_size, self.collider_size * 2, self.collider_size * 2) + self.bullets = [] self.bullet_is_shot = False self.shoot_timer = 0 + + self.current_powerup = 0 + self.missile_exploded = False + self.missile_explosion_radius = 250 + self.missile_timer = 0 + self.missile_explosion_x = 0 + self.missile_explosion_y = 0 + + def assign_powerup(self, powerup=0): + self.current_powerup = powerup def update_collider(self): self.wall_collider = pygame.Rect(self.x - self.collider_size, self.y - self.collider_size, self.collider_size * 2, self.collider_size * 2) diff --git a/setup_unix.sh b/setup_unix.sh old mode 100644 new mode 100755 index 60ec325..4157b39 --- a/setup_unix.sh +++ b/setup_unix.sh @@ -1,3 +1,4 @@ echo "installing dependencies..." pip install pygame -sh start_unix.sh +chmod +x ./start_unix.sh +./start_unix.sh diff --git a/setup_win.bat b/setup_win.bat index 60ec325..564428b 100644 --- a/setup_win.bat +++ b/setup_win.bat @@ -1,3 +1,3 @@ echo "installing dependencies..." pip install pygame -sh start_unix.sh +sh ./start_unix.sh diff --git a/start_unix.sh b/start_unix.sh old mode 100644 new mode 100755 index b45d45e..9ba92e2 --- a/start_unix.sh +++ b/start_unix.sh @@ -1,2 +1,2 @@ echo "running game..." -python3 scripts/main.py \ No newline at end of file +python3 ./scripts/main.py \ No newline at end of file diff --git a/start_win.bat b/start_win.bat index b45d45e..9ba92e2 100644 --- a/start_win.bat +++ b/start_win.bat @@ -1,2 +1,2 @@ echo "running game..." -python3 scripts/main.py \ No newline at end of file +python3 ./scripts/main.py \ No newline at end of file