Running the game
================
+This game requires Python 3.
+
Run the game using:
$ python -m koperkapel
+
+or, if your default python is Python 2,
+
+$ python3 -m koperkapel
+
+or just
+
+$ ./run_game.py
from pgzero.actor import Actor
from pgzero.clock import each_tick
+from .enemy_roach import get_enemy_roach
+
class EnemyGenerator(Actor):
"""Generators are currently invisble, but we want the update hook."""
- def __init__(self, info):
+ def __init__(self, info, level):
self.gen_pos = info['pos']
+ self.level = level
self.enemy_type = info['type']
self.rate = info['rate']
self.max_enemies = info['max']
self._time_since_last_update = 0
self.paused = False
each_tick(self.update)
+ self._made_enemies = []
super().__init__(os.path.join('weapons', 'blank'))
def update(self, dt):
return
self._time_since_last_update += dt
if self._time_since_last_update > self.rate:
- print('A horrible monster (%s) appears' % self.enemy_type, self.gen_pos)
- self._time_since_last_update = 0
+ if len(self._made_enemies) < self.max_enemies:
+ self._make_enemy()
+
+ def _make_enemy(self):
+ if self.enemy_type == 'roach':
+ roach = get_enemy_roach()
+ self._made_enemies.append(roach)
+ self.level.add_enemy(roach, *self.gen_pos)
+
+ def killed(self, enemy):
+ if enemy in self._made_enemies:
+ self._made_enemies.remove(enemy)
def pause(self):
self.paused = True
--- /dev/null
+# Roach utilities
+
+from ..roaches import t32_roaches, WorldRoach
+
+
+def get_enemy_roach():
+ # red
+ roach = t32_roaches.assemble(WorldRoach(), color=(255, 0, 0, 255))
+ roach.anchor = (0, 0)
+ roach.game_pos = (0, 0)
+ return roach
""" Class holding the level info """
+import random
+
class Level(object):
def get_exit_level(self):
return self.exit["next level"]
+
+ def is_on_enemy(self, x, y):
+ for enemy in self.enemies:
+ if (x, y) == enemy.game_pos:
+ return True
+ return False
+
+ def get_enemy(self, x, y):
+ for enemy in self.enemies:
+ if (x, y) == enemy.game_pos:
+ return enemy
+ return None
+
+ def add_enemy(self, enemy, x, y):
+ """Add an enemy to an empty floor space near x, y"""
+ added = False
+ while not added:
+ if self.can_walk(x, y, 'floor'):
+ if not self.is_on_friend(x, y):
+ if not self.is_on_enemy(x, y):
+ added = True
+ enemy.game_pos = (x, y)
+ self.enemies.append(enemy)
+ added = True
+ x += random.randint(-1, 1)
+ y += random.randint(-1, 1)
+
for item in level_data['items']:
self._level.items.append(get_item(item))
for generator in level_data['enemy generators']:
- enemy = EnemyGenerator(generator)
+ enemy = EnemyGenerator(generator, self._level)
self._level.enemy_generators.append(enemy)
return self._level
from pygame.constants import BLEND_RGBA_MULT
from .actors.animsurf import AnimatedSurfActor
from .serums import roach_serum_color
+from .util import safepath
NAMES = [
"roupert",
self.frames = 4
def assemble_frame(self, i, color, roach_data, weapon=None):
- roach = images.load("roach%s/roach_%d" % (self.suffix, i + 1))
- eyes = images.load("roach%s/eyes_%d" % (self.suffix, i + 1))
+ roach = images.load(safepath("roach%s/roach_%d") % (self.suffix, i + 1))
+ eyes = images.load(safepath("roach%s/eyes_%d") % (self.suffix, i + 1))
if weapon is None:
frame = roach.copy()
frame.fill(color, None, BLEND_RGBA_MULT)
frame.blit(eyes, (0, 0))
return frame
- def assemble(self, roach_data, weapon=None):
- color = roach_serum_color(roach_data)
+ def assemble(self, roach_data, color=None, weapon=None):
+ if not color:
+ color = roach_serum_color(roach_data)
frames = []
frames = [
self.assemble_frame(i, color, roach_data, weapon)
def _can_move(self, x, y):
if self._mode == 'walk':
- return self._level.can_walk(x, y, self._level_layer)
+ if not self._level.is_on_enemy(x, y):
+ return self._level.can_walk(x, y, self._level_layer)
+ return False
elif self._mode == 'fly':
return self._level.can_fly(x, y, self._level_layer)
elif self._mode == 'crawl':
for item in self._items:
item.pos = self.calc_offset(
item.game_pos[0] * TILE_SIZE, item.game_pos[1] * TILE_SIZE)
+ self._check_enemies()
+ for enemy in self._enemies:
+ enemy.pos = self.calc_offset(
+ enemy.game_pos[0] * TILE_SIZE, enemy.game_pos[1] * TILE_SIZE)
more = self._check_held_keys(dt)
if more:
events.extend(more)
return events
+ def _check_enemies(self):
+ if len(self._level.enemies) != len(self._enemies):
+ # New nemy has spawned
+ for enemy in self._level.enemies:
+ if enemy not in self._enemies:
+ self._enemies.add(enemy)
+
def _check_held_keys(self, dt):
for key in self._held_keys:
self._last_key_down += dt
from ..serums import big_serums, roach_is_serumless, SERUMS
from ..vehicles.base import Vehicle
from .base import Scene, ChangeSceneEvent, defer_to_update
+from ..util import safepath
TOOLBAR_LEFT_X = WIDTH * 3 // 4
def _init_pads(self):
self._roach_pad = self._pad_layer.add(
- Actor("roach_management/roach_pad", anchor=("left", "bottom")))
+ Actor(safepath("roach_management/roach_pad"), anchor=("left", "bottom")))
self._roach_pad.pos = (TOOLBAR_LEFT_X, TOOLBAR_MID_Y)
self._inventory_pad = self._pad_layer.add(
- Actor("roach_management/inventory_pad", anchor=("left", "top")))
+ Actor(safepath("roach_management/inventory_pad"), anchor=("left", "top")))
self._inventory_pad.pos = (TOOLBAR_LEFT_X, TOOLBAR_MID_Y)
def _add_button(self, name, anchor, inset, pos, action):
def _init_buttons(self):
self._button_layer.clear()
self._add_button(
- "roach_management/left_button", ("left", "bottom"), (1, -1),
+ safepath("roach_management/left_button"), ("left", "bottom"), (1, -1),
self._roach_pad.bottomleft, self._roach_left)
self._add_button(
- "roach_management/right_button", ("right", "bottom"), (-1, -1),
+ safepath("roach_management/right_button"), ("right", "bottom"), (-1, -1),
self._roach_pad.bottomright, self._roach_right)
self._add_button(
- "roach_management/left_button", ("left", "bottom"), (1, -1),
+ safepath("roach_management/left_button"), ("left", "bottom"), (1, -1),
self._inventory_pad.bottomleft, self._inventory_left)
self._add_button(
- "roach_management/right_button", ("right", "bottom"), (-1, -1),
+ safepath("roach_management/right_button"), ("right", "bottom"), (-1, -1),
self._inventory_pad.bottomright, self._inventory_right)
self._add_button(
- "roach_management/eject_button", ("right", "top"), (-1, 1),
+ safepath("roach_management/eject_button"), ("right", "top"), (-1, 1),
(TOOLBAR_LEFT_X, TOOLBAR_TOP_Y), self._eject_roach)
def _roach_left(self):
"""Render a level and allow moving the scene"""
-import os
-
from pygame.constants import BLEND_RGBA_MULT
from pgzero.constants import keys
from pgzero.loaders import images
from .level import BaseLevelScene
from .base import MoveViewportEvent
from ..constants import TILE_SIZE
+from ..util import safepath
class ViewLevelScene(BaseLevelScene):
def enter(self, world):
super().enter(world)
# Mark starting position
- self._roach = images.load(os.path.join("roach/roach_1")).copy()
+ self._roach = images.load(safepath("roach/roach_1")).copy()
self._roach.fill((255, 0, 0, 255), None, BLEND_RGBA_MULT)
x = self._level.start_pos[0] * TILE_SIZE
y = self._level.start_pos[1] * TILE_SIZE
from pygame.constants import BLEND_RGBA_MULT
from pygame.transform import rotate
from .actors.surf import SurfActor
+from .util import safepath
SERUMS = ["smart", "fast", "strong"]
def assemble(self, name):
assert name in SERUMS
- puddle = images.load("serum%s/serum" % (self.suffix,))
+ puddle = images.load(safepath("serum%s/serum") % (self.suffix,))
puddle = rotate(puddle, 90 * random.randint(0, 3))
- serum_icon = images.load("serum%s/%s" % (
+ serum_icon = images.load(safepath("serum%s/%s") % (
self.suffix, SERUM_TILENAME_MAP[name],))
frame = puddle.copy()
frame.fill(SERUM_OVERLAY_COLORS[name], None, BLEND_RGBA_MULT)
--- /dev/null
+"""Utility functions"""
+
+import os
+
+def safepath(path):
+ return os.path.join(*path.split("/"))
from ..actors.orientatedsurf import SelectableSurfActor
from ..actors.animsurf import AnimatedSurfActor
from ..weapons import default_weapons
+from ..util import safepath
class Vehicle:
self.game_pos = (0, 0)
def roach_management_overlay(self):
- return images.load("vehicles/walking/background")
+ return images.load(safepath("vehicles/walking/background"))
def roach_management_frame(self):
if self.overlay_frame_no is None:
return None
- return images.load("vehicle_big/%s_%d" % (
+ return images.load(safepath("vehicle_big/%s_%d") % (
self.vehicle_type, self.overlay_frame_no))
def init_seats(self):
cls.register(Roomba)
def _avatar_frame(self, i, weapon, suffix="_tiles"):
- vehicle = images.load("vehicle%s/%s_%d" % (
+ vehicle = images.load(safepath("vehicle%s/%s_%d") % (
suffix, self.vehicle_type, i + 1))
frame = vehicle.copy()
frame.blit(weapon.surf, (0, 0))
self.vehicle_pos = (pos[0] * vrad, pos[1] * vrad)
def actor(self):
- seat = images.load("vehicles/walking/seat")
+ seat = images.load(safepath("vehicles/walking/seat"))
selected_seat = seat.copy()
selected_seat.fill(
self.vehicle.selected_seat_overlay_color, None, BLEND_RGBA_MULT)
from pgzero.loaders import images
from .actors.animsurf import AnimatedSurfActor
+from .util import safepath
class Weapon:
class WeaponFactory:
def assemble_frame(self, suffix, weapon, tape):
- surf = images.load("weapons/%s%s" % (weapon.image_name, suffix))
+ surf = images.load(safepath("weapons/%s%s")
+ % (weapon.image_name, suffix))
frame = surf.copy()
if tape:
- tape_surf = images.load("weapons/tape")
+ tape_surf = images.load(safepath("weapons/tape"))
frame.blit(tape_surf, (0, 0))
return frame
-#!/usr/bin/env python
+#!/usr/bin/env python3
""" Run Portentosa. """
import koperkapel.__main__
+