Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: 7d00949a10
570 lines (484 sloc) 14.863 kb
#!/usr/bin/python
# coding=utf-8
# SquareShooter - an abstract Asteroids-like game in PyGame
# 2012-07-15 Felix Pleșoianu <felixp7@yahoo.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
import math
import random
import pygame
BLACK = (0, 0, 0)
GREEN = (0, 204, 0)
RED = (255, 0, 0)
SILVER = (204, 204, 204)
WHITE = (255, 255, 255)
powerup_types = ["shield", "bullet", "freeze"]
def scale_and_round(x, y):
x = int(round(x * 480))
y = int(round(y * 480))
return x, y
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __iadd__(self, vector):
self.x += vector.x
self.y += vector.y
return self
def __isub__(self, vector):
self.x -= vector.x
self.y -= vector.y
return self
def copy(self, vector):
self.x = vector.x
self.y = vector.y
class Bubble2D:
def __init__(self, radius):
self.position = Vector2D(0, 0)
self.radius = radius;
self.speed = Vector2D(0, 0)
def update(self, delta_t):
self.position.x += self.speed.x * delta_t
self.position.y += self.speed.y * delta_t
def wrap_around(self):
pos = self.position
if pos.x < 0: pos.x = 1
if pos.y < 0: pos.y = 1
if pos.x > 1: pos.x = 0
if pos.y > 1: pos.y = 0
def is_out(self):
pos = self.position;
return pos.x < 0 or pos.y < 0 or pos.x > 1 or pos.y > 1
def collides_with(self, bubble):
a = abs(self.position.x - bubble.position.x)
b = abs(self.position.y - bubble.position.y)
distance = math.sqrt(a * a + b * b)
return distance < (self.radius + bubble.radius)
def random_position():
return (random.random() - 0.5) * 3 + 0.5;
def random_speed(magnitude):
return (random.random() * magnitude * 2 - magnitude)
def make_bubble(kind):
if kind == "big":
size = 0.1
speed = 0.1
elif kind == "medium":
size = 0.075
speed = 0.15
elif kind == "small":
size = 0.05
speed = 0.25
new_bubble = Bubble2D(size)
new_bubble.position = Vector2D(
random_position(),
random_position())
new_bubble.speed = Vector2D(
random_speed(speed),
random_speed(speed))
new_bubble.kind = kind
return new_bubble
class GameWorld:
bubbles = []
explosions = []
powerups = []
bullet = None
ship = None
accel_x = 0
accel_y = 0
move_timer = 0
death_timer = 0
finish_timer = 0
ship_shield_timer = 0
bullet_shield_timer = 0
freeze_timer = 0
level = 0
score = 0
max_level = 0
high_score = 0
lives = 0
powerup_types = ["shield", "bullet", "freeze"]
def init_level(self, level):
self.level = level
if (level > self.max_level): self.max_level = level
if self.ship == None:
self.ship = Bubble2D(1.0 / 25)
self.ship.position = Vector2D(0.5, 0.5)
self.ship.speed = Vector2D(0, 0)
self.bullet = None;
self.move_timer = 0
self.death_timer = 0
self.finish_timer = 0
self.ship_shield_timer = 6;
self.bullet_shield_timer = 0;
self.freeze_timer = 0;
del self.bubbles[:]
del self.explosions[:]
del self.powerups[:]
for i in range(level):
self.bubbles.append(make_bubble("big"))
def update(self, delta_t):
self.handle_collisions(delta_t)
if len(self.explosions) > 0:
if self.explosions[0].radius > 0.5:
self.explosions.pop(0)
for i in self.explosions:
i.radius += delta_t
if len(self.powerups) > 0:
if self.powerups[0].age > 9:
self.powerups.pop(0)
for i in self.powerups:
i.age += delta_t
if self.ship_shield_timer > 0:
self.ship_shield_timer -= delta_t
if self.bullet_shield_timer > 0:
self.bullet_shield_timer -= delta_t
if self.freeze_timer > 0:
self.freeze_timer -= delta_t
if len(self.bubbles) == 0:
if self.finish_timer > 0:
self.finish_timer -= delta_t;
else:
self.level += 1
self.lives += 1
self.init_level(self.level)
return
elif self.freeze_timer <= 0:
for i in self.bubbles:
i.update(delta_t)
i.wrap_around()
if self.bullet != None:
self.bullet.update(delta_t)
if self.bullet.is_out():
self.bullet = None
if self.ship == None:
if self.death_timer > 0:
self.death_timer -= delta_t
elif self.lives > 0:
self.ship = Bubble2D(1.0 / 25)
self.ship.position = Vector2D(0.5, 0.5)
self.ship_shield_timer = 6;
else:
self.level = 0 # Game over
return
self.ship.speed.x += self.accel_x
self.ship.speed.y += self.accel_y
self.ship.speed.x *= 0.99
self.ship.speed.y *= 0.99
self.ship.update(delta_t)
self.ship.wrap_around()
def handle_collisions(self, delta_t):
for b in self.bubbles:
if self.bullet != None and b.collides_with(self.bullet):
self.bubbles.remove(b)
if self.bullet_shield_timer <= 0:
self.bullet = None
else:
# Push it along or it will just
# destroy the newly formed bubbles.
self.bullet.update(delta_t * 5)
self.spawn_bubbles(b)
self.spawn_explosion(b)
self.mark_score(b)
if len(self.bubbles) == 0:
self.finish_timer = 3
break
elif self.ship != None:
if not b.collides_with(self.ship):
continue
if self.ship_shield_timer > 0:
continue
self.spawn_explosion(self.ship)
self.ship = None
self.lives -= 1
self.death_timer = 3;
break
if self.ship == None: return
for p in self.powerups[:]:
if p.collides_with(self.ship):
self.apply_powerup(p)
self.powerups.remove(p)
def spawn_bubbles(self, parent):
if parent.kind == "small":
if random.random() < 0.25:
self.spawn_powerup(parent)
else:
if parent.kind == "big":
new_type = "medium"
elif parent.kind == "medium":
new_type = "small"
b = make_bubble(new_type)
b.position.copy(parent.position)
self.bubbles.append(b)
b = make_bubble(new_type)
b.position.copy(parent.position)
self.bubbles.append(b)
def spawn_explosion(self, bubble):
explosion = Bubble2D(0)
explosion.position.copy(bubble.position)
self.explosions.append(explosion)
def spawn_powerup(self, bubble):
powerup = Bubble2D(0.03)
powerup.position.copy(bubble.position)
powerup.kind = random.choice(powerup_types)
powerup.age = 0
self.powerups.append(powerup)
def mark_score(self, bubble):
if bubble.kind == "small":
self.score += 5
elif bubble.kind == "medium":
self.score += 2
elif bubble.kind == "big":
self.score += 1
if self.score > self.high_score:
self.high_score = self.score
def apply_powerup(self, powerup):
if powerup.kind == "shield":
self.ship_shield_timer += 6
elif powerup.kind == "bullet":
self.bullet_shield_timer += 6
elif powerup.kind == "freeze":
self.freeze_timer += 6
else:
raise "Bad powerup type"
self.score += self.level * 10
if self.score > self.high_score:
self.high_score = self.score
def shoot_at(self, x, y):
if self.bullet != None or self.ship == None:
return
x -= self.ship.position.x;
y -= self.ship.position.y;
b = Bubble2D(0.01)
b.position.copy(self.ship.position);
b.speed.x = x * 3
b.speed.y = y * 3
# Help out the poor sods who click on their
# own ship and get stuck with a non-moving
# bullet. (2009-11-14)
absx = abs(x)
absy = abs(y)
if absx < 0.1 and absy < 0.1:
b.speed.x *= 30
b.speed.y *= 30
self.bullet = b
def thrust_at(self, x, y):
if self.ship == None:
return
x -= self.ship.position.x;
y -= self.ship.position.y;
self.accel_x += x * 0.03;
self.accel_y += y * 0.03;
def stop_thrust(self):
self.accel_x = 0
self.accel_y = 0
class GameScreen:
def __init__(self, model, screen):
self.model = model
self.screen = screen
self.width, self.height = screen.get_size()
self.bglayer = pygame.Surface(screen.get_size())
font_name = pygame.font.get_default_font()
self.hud_font = pygame.font.SysFont(
font_name, self.height / 10)
self.msg_font = pygame.font.SysFont(
font_name, self.height / 20)
self.bubble_colors = ["#ffffcc", "#ffccff", "#ccffff",
"#ffdddd", "#ddffdd", "#ddddff"]
for i in range(6):
self.bubble_colors[i] = pygame.Color(
self.bubble_colors[i])
self.game_paused = False
self.render_backround()
def render(self):
m = self.model
self.screen.blit(self.bglayer, (0, 0))
if m.level == 0:
self.render_title_screen()
# Hide the [P]ause text.
self.screen.fill(GREEN, (500, 424, 140, 24))
else:
self.render_game_world()
# Hide the [P]lay text.
self.screen.fill(GREEN, (500, 400, 140, 24))
if self.game_paused: self.render_pause_text()
text = self.hud_font.render(str(m.level), False, BLACK)
self.screen.blit(text, (500, 48))
text = self.hud_font.render(str(m.lives), False, BLACK)
self.screen.blit(text, (500, 48 * 3))
text = self.hud_font.render(str(m.score), False, BLACK)
self.screen.blit(text, (500, 48 * 5))
#fps_text = self.msg_font.render(str(self.fps), False, GREEN)
#self.screen.blit(fps_text, (0, 0))
pygame.display.flip()
def render_backround(self):
self.bglayer.fill(BLACK)
self.bglayer.fill(GREEN, (480, 0, 160, 480))
msg = ["Level", "Lives", "Score"]
for i in range(3):
text = self.hud_font.render(msg[i], False, BLACK)
self.bglayer.blit(text, (500, i * 96))
msg = ["[Q]uit", "[P]ause", "[P]lay"]
for i in range(3):
text = self.msg_font.render(msg[i], False, WHITE)
self.bglayer.blit(text, (500, 448 - i * 24))
def render_title_screen(self):
text = self.hud_font.render("SQUARE", False, GREEN)
self.screen.blit(text, text.get_rect(midbottom = (240, 240)))
text = self.hud_font.render("SHOOTER", False, GREEN)
self.screen.blit(text, text.get_rect(midtop = (240, 240)))
text = self.msg_font.render("FCP", False, GREEN)
self.screen.blit(text, text.get_rect(midbottom = (240, 120)))
text = self.msg_font.render("presents", False, GREEN)
self.screen.blit(text, text.get_rect(midtop = (240, 120)))
high_score = "High score: " + str(self.model.high_score)
text = self.msg_font.render(high_score, False, GREEN)
self.screen.blit(text, text.get_rect(midbottom = (240, 360)))
max_level = "Max level: " + str(self.model.max_level)
text = self.msg_font.render(max_level, False, GREEN)
self.screen.blit(text, text.get_rect(midtop = (240, 360)))
def render_game_world(self):
m = self.model
self.screen.set_clip((0, 0, 480, 480))
if m.ship != None: self.render_ship()
if m.bullet != None: self.render_bullet()
for bubble in m.bubbles:
if not hasattr(bubble, "color"):
bubble.color = random.choice(
self.bubble_colors)
pos = bubble.position
pygame.draw.circle(
self.screen,
bubble.color,
scale_and_round(pos.x, pos.y),
int(round(bubble.radius * 480)),
1)
for explosion in m.explosions:
pos = explosion.position
pygame.draw.circle(
self.screen,
RED,
scale_and_round(pos.x, pos.y),
int(round(explosion.radius * 480)),
1)
for i in m.powerups:
self.render_powerup(i)
self.screen.set_clip(None)
def render_ship(self):
ship = self.model.ship
pos = ship.position
bbox = pygame.draw.circle(
self.screen,
SILVER,
scale_and_round(pos.x, pos.y),
int(round(ship.radius * 480)))
pygame.draw.circle(
self.screen,
BLACK,
scale_and_round(pos.x, pos.y),
int(round(ship.radius * 0.5 * 480)),
1)
if self.model.ship_shield_timer > 0:
pygame.draw.rect(self.screen, SILVER, bbox, 1)
def render_bullet(self):
bullet = self.model.bullet
pos = bullet.position
bbox = pygame.draw.circle(
self.screen,
RED,
scale_and_round(pos.x, pos.y),
int(round(bullet.radius * 480)))
if self.model.bullet_shield_timer > 0:
pygame.draw.rect(self.screen, RED, bbox, 1)
def render_powerup(self, powerup):
pos = powerup.position
radius = powerup.radius * 480
if powerup.kind == "shield":
bbox = pygame.draw.circle(
self.screen,
WHITE,
scale_and_round(pos.x, pos.y),
int(round(radius)),
1)
pygame.draw.rect(self.screen, WHITE, bbox, 1)
elif powerup.kind == "bullet":
pygame.draw.circle(
self.screen,
WHITE,
scale_and_round(pos.x, pos.y),
int(round(radius * 0.3)),
1)
bbox = pygame.Rect(0, 0, radius * 2, radius * 2)
bbox.center = (pos.x * 480, pos.y * 480)
pygame.draw.rect(self.screen, WHITE, bbox, 1)
elif powerup.kind == "freeze":
bbox = pygame.Rect(0, 0, radius * 2, radius * 2)
bbox.center = (pos.x * 480, pos.y * 480)
pygame.draw.rect(self.screen, WHITE, bbox, 1)
bbox.inflate_ip(-radius, -radius)
pygame.draw.rect(self.screen, WHITE, bbox, 1)
bbox.inflate_ip(-radius * 0.5, -radius * 0.5)
pygame.draw.rect(self.screen, WHITE, bbox, 1)
else:
raise "Bad power-up kind: " + powerup.kind
def render_pause_text(self):
pause_text = self.msg_font.render("Game paused", False, GREEN)
self.screen.blit(
pause_text,
pause_text.get_rect(midbottom = (240, 480)))
pygame.init()
screen = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()
model = GameWorld()
renderer = GameScreen(model, screen)
pygame.display.set_caption("Square Shooter Desktop Edition")
pygame.event.set_blocked(pygame.MOUSEMOTION)
running = True
while running:
delta_t = clock.tick(60)
#renderer.fps = int(round(clock.get_fps()))
ev = pygame.event.poll()
if ev.type == pygame.QUIT:
running = False
elif ev.type == pygame.KEYUP:
if ev.key == pygame.K_ESCAPE:
running = False
elif ev.key == pygame.K_q:
if model.level > 0:
model.level = 0
else:
running = False
elif ev.key == pygame.K_p:
if model.level == 0:
model.score = 0
model.lives = 1
model.init_level(1)
else:
renderer.game_paused = not renderer.game_paused
elif ev.type == pygame.MOUSEBUTTONDOWN:
if model.level > 0 and not renderer.game_paused:
x, y = ev.pos
model.shoot_at(x / 480.0, y / 480.0)
model.thrust_at(x / 480.0, y / 480.0)
elif ev.type == pygame.MOUSEBUTTONUP:
if model.level > 0:
model.stop_thrust()
# Simulations need the time in seconds, dammit!
if model.level > 0 and not renderer.game_paused:
model.update(delta_t * 0.001)
renderer.render()
Jump to Line
Something went wrong with that request. Please try again.