Files
Python/blockwars-game/blockwars-game.py
T

386 lines
13 KiB
Python
Raw Normal View History

2025-07-13 12:45:46 -07:00
#!/usr/bin/python3
# Description: blockwars - A simple game written in Python.
2025-07-21 11:31:07 -07:00
# Usage: python3 blockwars-game.py
2025-07-13 12:45:46 -07:00
# Author: Justin Oros
# Source: https://github.com/JustinOros
import pygame
import random
import time
import os
# Initialize pygame
pygame.init()
# Set up full-screen mode
SCREEN_WIDTH = pygame.display.Info().current_w
SCREEN_HEIGHT = pygame.display.Info().current_h
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.FULLSCREEN)
pygame.display.set_caption("Block Wars")
2025-07-13 13:26:47 -07:00
# Hide mouse cursor
pygame.mouse.set_visible(False)
2025-07-13 12:45:46 -07:00
# Define colors
2025-07-21 11:26:56 -07:00
NEON_BLUE = (0, 200, 255)
2025-07-21 10:55:30 -07:00
BLUE = (0, 200, 255) # Player glow blue
2025-07-13 12:45:46 -07:00
RED = (255, 0, 0)
DARK_GRAY = (50, 50, 50)
# Function to load sound safely
def load_sound(filename):
if os.path.exists(filename):
return pygame.mixer.Sound(filename)
else:
print(f"Warning: {filename} not found. Sound will be disabled.")
return None
# Load sounds
2025-07-21 10:55:30 -07:00
GO_SOUND = load_sound("audio_go.wav")
FIRE_SOUND = load_sound("audio_playerfire.wav")
DIE_SOUND = load_sound("audio_playerdeath.wav")
LEVEL_UP_SOUND = load_sound("audio_levelup.wav")
ENEMY_DEATH_SOUND = load_sound("audio_enemydeath.wav")
2025-07-13 12:45:46 -07:00
EXPLOSION_SOUND = load_sound("explosion_sound.wav")
# Set font for score
font = pygame.font.SysFont(None, 30)
# Initialize the joystick (controller)
pygame.joystick.init()
controller = None
if pygame.joystick.get_count() > 0:
controller = pygame.joystick.Joystick(0)
controller.init()
2025-07-21 10:55:30 -07:00
# Draw glowing rectangle ONLY (no solid fill)
def draw_glow_rect(surf, rect, glow_color, glow_radius):
glow_surface = pygame.Surface((rect.width + glow_radius*2, rect.height + glow_radius*2), pygame.SRCALPHA)
max_glow = int(glow_radius * 0.6)
for i in range(max_glow, 0, -1):
alpha = int(255 * (i / max_glow) ** 2)
glow_rect = pygame.Rect(
glow_radius - i,
glow_radius - i,
rect.width + i * 2,
rect.height + i * 2
)
pygame.draw.rect(
glow_surface,
glow_color + (alpha,),
glow_rect,
border_radius=3,
width=2
)
surf.blit(glow_surface, (rect.x - glow_radius, rect.y - glow_radius))
2025-07-13 12:45:46 -07:00
# Player class
class Player:
def __init__(self):
self.x = SCREEN_WIDTH // 2
self.y = SCREEN_HEIGHT // 2
self.size = 20
2025-07-21 11:17:00 -07:00
self.speed = 3 # Start speed will be set in game()
2025-07-13 12:45:46 -07:00
self.direction = None
def move(self, keys, joystick_axes):
if joystick_axes[1] < -0.5:
self.y -= self.speed
self.direction = 'up'
elif joystick_axes[1] > 0.5:
self.y += self.speed
self.direction = 'down'
if joystick_axes[0] < -0.5:
self.x -= self.speed
self.direction = 'left'
elif joystick_axes[0] > 0.5:
self.x += self.speed
self.direction = 'right'
if keys[pygame.K_w]:
self.y -= self.speed
self.direction = 'up'
if keys[pygame.K_s]:
self.y += self.speed
self.direction = 'down'
if keys[pygame.K_a]:
self.x -= self.speed
self.direction = 'left'
if keys[pygame.K_d]:
self.x += self.speed
self.direction = 'right'
if self.x < 0:
self.x = SCREEN_WIDTH - self.size
elif self.x > SCREEN_WIDTH - self.size:
self.x = 0
if self.y < 0:
self.y = SCREEN_HEIGHT - self.size
elif self.y > SCREEN_HEIGHT - self.size:
self.y = 0
def draw(self):
2025-07-21 10:55:30 -07:00
rect = pygame.Rect(self.x, self.y, self.size, self.size)
draw_glow_rect(screen, rect, BLUE, glow_radius=10)
2025-07-13 12:45:46 -07:00
# Projectile class
class Projectile:
def __init__(self, x, y, direction):
self.x = x
self.y = y
self.size = 5
2025-07-21 11:17:00 -07:00
self.color = BLUE
2025-07-13 12:45:46 -07:00
self.speed = 15
self.direction = direction
def move(self):
if self.direction == 'up':
self.y -= self.speed
elif self.direction == 'down':
self.y += self.speed
elif self.direction == 'left':
self.x -= self.speed
elif self.direction == 'right':
self.x += self.speed
def draw(self):
pygame.draw.rect(screen, self.color, (self.x, self.y, self.size, self.size))
2025-07-21 11:17:00 -07:00
# Explosion class
2025-07-13 12:45:46 -07:00
class Explosion:
2025-07-21 10:55:30 -07:00
def __init__(self, x, y, color=RED):
2025-07-13 12:45:46 -07:00
self.x = x
self.y = y
self.size = 50
self.lifetime = 10
2025-07-21 10:55:30 -07:00
self.color = color
2025-07-13 12:45:46 -07:00
def draw(self):
2025-07-21 10:55:30 -07:00
pygame.draw.circle(screen, self.color, (self.x, self.y), self.size)
2025-07-13 12:45:46 -07:00
self.lifetime -= 1
def is_alive(self):
return self.lifetime > 0
# Enemy class
class Enemy:
def __init__(self, speed):
edge = random.choice(["top", "bottom", "left", "right"])
if edge == "top":
self.x = random.randint(0, SCREEN_WIDTH - 20)
self.y = 0
elif edge == "bottom":
self.x = random.randint(0, SCREEN_WIDTH - 20)
self.y = SCREEN_HEIGHT - 20
elif edge == "left":
self.x = 0
self.y = random.randint(0, SCREEN_HEIGHT - 20)
else:
self.x = SCREEN_WIDTH - 20
self.y = random.randint(0, SCREEN_HEIGHT - 20)
self.size = 20
self.speed = speed
def move(self, player_x, player_y):
if self.x < player_x:
self.x += self.speed
elif self.x > player_x:
self.x -= self.speed
if self.y < player_y:
self.y += self.speed
elif self.y > player_y:
self.y -= self.speed
def draw(self):
2025-07-21 10:55:30 -07:00
rect = pygame.Rect(self.x, self.y, self.size, self.size)
draw_glow_rect(screen, rect, RED, glow_radius=10)
2025-07-13 12:45:46 -07:00
2025-07-21 11:17:00 -07:00
# Pause screen
2025-07-13 13:26:47 -07:00
def draw_pause_screen(paused_by_controller):
2025-07-13 12:45:46 -07:00
overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT))
overlay.set_alpha(180)
overlay.fill((0, 0, 0))
screen.blit(overlay, (0, 0))
pause_font = pygame.font.SysFont(None, 100)
instruction_font = pygame.font.SysFont(None, 40)
2025-07-21 11:26:56 -07:00
pause_text = pause_font.render("PAUSED", True, NEON_BLUE)
2025-07-13 13:26:47 -07:00
if paused_by_controller:
instruction = "Press START to resume or X to Exit."
else:
instruction = "Press ESC to resume or Q to Quit."
2025-07-21 11:26:56 -07:00
instruction_text = instruction_font.render(instruction, True, NEON_BLUE)
2025-07-13 12:45:46 -07:00
screen.blit(pause_text, (
SCREEN_WIDTH // 2 - pause_text.get_width() // 2,
SCREEN_HEIGHT // 2 - pause_text.get_height() // 2 - 30
))
screen.blit(instruction_text, (
SCREEN_WIDTH // 2 - instruction_text.get_width() // 2,
SCREEN_HEIGHT // 2 + 30
))
# Main game loop
def game():
clock = pygame.time.Clock()
player = Player()
projectiles = []
explosions = []
level = 1
score = 0
2025-07-21 11:17:00 -07:00
enemy_speed = 2
enemies = [Enemy(enemy_speed)]
player.speed = enemy_speed + 1 # Player slightly faster than enemies
2025-07-13 12:45:46 -07:00
running = True
paused = False
2025-07-13 13:26:47 -07:00
paused_by_controller = False
2025-07-13 12:45:46 -07:00
player_dead = False
2025-07-13 13:26:47 -07:00
controller_active = False
def rumble(duration=300, strength=1.0):
if controller_active and controller and hasattr(controller, "rumble"):
try:
controller.rumble(strength, strength, duration)
except Exception as e:
print(f"Rumble error: {e}")
2025-07-13 12:45:46 -07:00
if GO_SOUND:
GO_SOUND.play()
while running:
screen.fill(DARK_GRAY)
keys = pygame.key.get_pressed()
joystick_axes = [controller.get_axis(0), controller.get_axis(1)] if controller else [0, 0]
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
paused = not paused
2025-07-13 13:26:47 -07:00
paused_by_controller = False
2025-07-13 12:45:46 -07:00
elif paused and event.key == pygame.K_q:
running = False
elif not paused and event.key == pygame.K_SPACE:
if player.direction:
projectiles.append(Projectile(player.x + player.size // 2, player.y + player.size // 2, player.direction))
if FIRE_SOUND:
FIRE_SOUND.play()
if event.type == pygame.JOYBUTTONDOWN:
2025-07-21 10:55:30 -07:00
controller_active = True
if event.button == 7 or event.button == 6:
2025-07-13 12:45:46 -07:00
paused = not paused
2025-07-13 13:26:47 -07:00
paused_by_controller = True
2025-07-21 10:55:30 -07:00
elif paused and event.button == 2:
2025-07-13 12:45:46 -07:00
running = False
2025-07-21 10:55:30 -07:00
elif not paused and event.button == 0:
2025-07-13 12:45:46 -07:00
if player.direction:
projectiles.append(Projectile(player.x + player.size // 2, player.y + player.size // 2, player.direction))
if FIRE_SOUND:
FIRE_SOUND.play()
if event.type == pygame.MOUSEBUTTONDOWN:
if not paused and event.button == 1:
if player.direction:
projectiles.append(Projectile(player.x + player.size // 2, player.y + player.size // 2, player.direction))
if FIRE_SOUND:
FIRE_SOUND.play()
if paused:
2025-07-13 13:26:47 -07:00
draw_pause_screen(paused_by_controller)
2025-07-13 12:45:46 -07:00
pygame.display.flip()
clock.tick(60)
continue
2025-07-21 10:55:30 -07:00
if player_dead:
for explosion in explosions[:]:
explosion.draw()
if not explosion.is_alive():
explosions.remove(explosion)
pygame.display.flip()
clock.tick(60)
if not explosions:
show_final_score(score)
running = False
continue
2025-07-13 12:45:46 -07:00
player.move(keys, joystick_axes)
for projectile in projectiles[:]:
projectile.move()
projectile.draw()
if projectile.x < 0 or projectile.x > SCREEN_WIDTH or projectile.y < 0 or projectile.y > SCREEN_HEIGHT:
projectiles.remove(projectile)
for enemy in enemies[:]:
enemy.move(player.x, player.y)
enemy.draw()
2025-07-21 11:17:00 -07:00
if pygame.Rect(player.x, player.y, player.size, player.size).colliderect(pygame.Rect(enemy.x, enemy.y, enemy.size, enemy.size)):
2025-07-13 12:45:46 -07:00
if not player_dead:
if DIE_SOUND:
DIE_SOUND.play()
2025-07-13 13:26:47 -07:00
rumble(duration=1000, strength=1.0)
2025-07-13 12:45:46 -07:00
player_dead = True
2025-07-21 10:55:30 -07:00
explosions.append(Explosion(player.x + player.size // 2, player.y + player.size // 2, color=BLUE))
2025-07-13 12:45:46 -07:00
for projectile in projectiles[:]:
2025-07-21 11:17:00 -07:00
if pygame.Rect(enemy.x, enemy.y, enemy.size, enemy.size).colliderect(pygame.Rect(projectile.x, projectile.y, projectile.size, projectile.size)):
2025-07-13 12:45:46 -07:00
explosions.append(Explosion(enemy.x + enemy.size // 2, enemy.y + enemy.size // 2))
if ENEMY_DEATH_SOUND:
ENEMY_DEATH_SOUND.play()
2025-07-13 13:26:47 -07:00
rumble(duration=150, strength=0.5)
2025-07-13 12:45:46 -07:00
enemies.remove(enemy)
projectiles.remove(projectile)
score += 100
break
for explosion in explosions[:]:
explosion.draw()
if not explosion.is_alive():
explosions.remove(explosion)
if not enemies:
level += 1
if LEVEL_UP_SOUND:
LEVEL_UP_SOUND.play()
2025-07-21 11:17:00 -07:00
# Increase enemy speed every 2 levels, cap at 8
if level % 2 == 0 and enemy_speed < 8:
enemy_speed += 1
player.speed = enemy_speed + 1 # Player stays slightly faster
2025-07-13 12:45:46 -07:00
enemies = [Enemy(enemy_speed) for _ in range(level)]
player.draw()
2025-07-21 11:26:56 -07:00
score_text = font.render(f"Score: {score}", True, NEON_BLUE)
level_text = font.render(f"Level: {level}", True, NEON_BLUE)
2025-07-13 12:45:46 -07:00
screen.blit(score_text, (10, 10))
screen.blit(level_text, (SCREEN_WIDTH - level_text.get_width() - 10, 10))
pygame.display.flip()
clock.tick(60)
2025-07-21 11:17:00 -07:00
# Show final score on death
2025-07-13 12:45:46 -07:00
def show_final_score(score):
font_large = pygame.font.SysFont(None, 80)
2025-07-21 11:31:07 -07:00
if score == 0:
text = "GAME OVER"
else:
text = f"Final Score: {score}"
final_score_text = font_large.render(text, True, RED)
2025-07-13 12:45:46 -07:00
screen.fill(DARK_GRAY)
2025-07-13 13:26:47 -07:00
screen.blit(final_score_text, (SCREEN_WIDTH // 2 - final_score_text.get_width() // 2,
SCREEN_HEIGHT // 2 - final_score_text.get_height() // 2))
2025-07-13 12:45:46 -07:00
pygame.display.flip()
2025-07-21 11:17:00 -07:00
time.sleep(2)
2025-07-13 12:45:46 -07:00
if __name__ == "__main__":
game()
pygame.quit()