From: David Sharpe Date: Sun, 28 Feb 2016 21:44:22 +0000 (+0200) Subject: Merge branch 'master' of ctpug.org.za:koperkapel X-Git-Url: https://git.ctpug.org.za/?a=commitdiff_plain;h=6a8c0b31ee60408c54c9ff0d9ba46b225613f48a;hp=4e4e28b06e05a8eee323a76a62ed910feb8b0983;p=koperkapel.git Merge branch 'master' of ctpug.org.za:koperkapel --- diff --git a/images/roach/eyes_1.png b/images/roach/eyes_1.png new file mode 100644 index 0000000..c77fa0c Binary files /dev/null and b/images/roach/eyes_1.png differ diff --git a/images/roach/eyes_2.png b/images/roach/eyes_2.png new file mode 100644 index 0000000..3545635 Binary files /dev/null and b/images/roach/eyes_2.png differ diff --git a/images/roach/eyes_3.png b/images/roach/eyes_3.png new file mode 100644 index 0000000..c77fa0c Binary files /dev/null and b/images/roach/eyes_3.png differ diff --git a/images/roach/eyes_4.png b/images/roach/eyes_4.png new file mode 100644 index 0000000..6273074 Binary files /dev/null and b/images/roach/eyes_4.png differ diff --git a/images/roach/roach_1.png b/images/roach/roach_1.png new file mode 100644 index 0000000..7227eba Binary files /dev/null and b/images/roach/roach_1.png differ diff --git a/images/roach/roach_2.png b/images/roach/roach_2.png new file mode 100644 index 0000000..4e4f5d6 Binary files /dev/null and b/images/roach/roach_2.png differ diff --git a/images/roach/roach_3.png b/images/roach/roach_3.png new file mode 100644 index 0000000..c967f99 Binary files /dev/null and b/images/roach/roach_3.png differ diff --git a/images/roach/roach_4.png b/images/roach/roach_4.png new file mode 100644 index 0000000..314e742 Binary files /dev/null and b/images/roach/roach_4.png differ diff --git a/koperkapel/actors/buttons.py b/koperkapel/actors/buttons.py new file mode 100644 index 0000000..d6e3954 --- /dev/null +++ b/koperkapel/actors/buttons.py @@ -0,0 +1,22 @@ +""" Buttons. """ + +from pgzero.ptext import getsurf +from .surf import SurfActor + + +class TextButton(SurfActor): + def __init__(self, text, action=None, **kwargs): + self.text(text, **kwargs) + super(TextButton, self).__init__(self._surf) + self.action = action + + def select(self): + self.text(self._text, owidth=2, ocolor="white", color="black") + + def deselect(self): + self.text(self._text) + + def text(self, text, **kw): + self._text = text + self._text_kwargs = kw + self.surf = getsurf(text, **kw) diff --git a/koperkapel/actors/navigator.py b/koperkapel/actors/navigator.py new file mode 100644 index 0000000..40835cb --- /dev/null +++ b/koperkapel/actors/navigator.py @@ -0,0 +1,46 @@ +""" A navigable graph of actors. """ + + +class ActorNavigator: + """ A navigable graph of actors. """ + + def __init__(self): + self._actors = {} + self._graph = {} + self._current_id = None + + @property + def current(self): + return self._actors.get(self._current_id) + + def connect_pair(self, a1, a2, edge): + self._actors[id(a1)] = a1 + self._actors[id(a2)] = a2 + if self._current_id is None: + self._current_id = id(a1) + a1edges = self._graph.setdefault(id(a1), {}) + a1edges[edge] = id(a2) + + def connect(self, actors, edges, reverse=None, wrap=True): + if not edges or not actors: + return + first, previous, current = None, None, None + for actor in actors: + previous, current = current, actor + if previous is None: + first = current + continue + for edge in edges: + self.connect_pair(previous, current, edge) + if wrap and first is not current: + for edge in edges: + self.connect_pair(current, first, edge) + if reverse is not None: + self.connect(list(reversed(actors)), reverse) + + def follow(self, edge): + future = self._graph.get(self._current_id, {}).get(edge) + if future: + old, self._current_id = self._current_id, future + return self._actors[old], self._actors[future] + return None, None diff --git a/koperkapel/actors/surf.py b/koperkapel/actors/surf.py new file mode 100644 index 0000000..23dccc6 --- /dev/null +++ b/koperkapel/actors/surf.py @@ -0,0 +1,30 @@ +""" Actor that takes a surface. """ + +from pgzero.actor import Actor, POS_TOPLEFT, ANCHOR_CENTER + + +class SurfActor(Actor): + def __init__(self, surf, pos=POS_TOPLEFT, anchor=ANCHOR_CENTER, **kwargs): + self._handle_unexpected_kwargs(kwargs) + + self.surf = surf + # Initialise it at (0,0). We'll move it to the right place later + super(Actor, self).__init__((0, 0), self._surf.get_size()) + + self._init_position(pos, anchor, **kwargs) + + def _update_pos(self): + p = self.pos + self.width, self.height = self._surf.get_size() + self._calc_anchor() + self.pos = p + + @property + def surf(self): + """ The original surface displaying the actor. """ + return self._orig_surf + + @surf.setter + def surf(self, surf): + self._orig_surf = self._surf = surf + self._update_pos() diff --git a/koperkapel/levels/level1.json b/koperkapel/levels/level1.json new file mode 100644 index 0000000..42e9782 --- /dev/null +++ b/koperkapel/levels/level1.json @@ -0,0 +1,855 @@ +{ + "tiles": [ + { + "behaviour": [], + "base": "cwall", + "pos": [ + 0, + 0 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 0, + 1 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 0, + 2 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 0, + 3 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 0, + 4 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 0, + 5 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 0, + 6 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 0, + 7 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 0, + 8 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 0, + 9 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 1, + 0 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 1, + 1 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 1, + 2 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 1, + 3 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 1, + 4 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 1, + 5 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 1, + 6 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 1, + 7 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 1, + 8 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 1, + 9 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 2, + 0 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 2, + 1 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 2, + 2 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 2, + 3 + ] + }, + { + "behaviour": [ + "walk", + "fly" + ], + "base": "floor", + "pos": [ + 2, + 4 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 2, + 5 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 2, + 6 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 2, + 7 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 2, + 8 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 2, + 9 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 3, + 0 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 3, + 1 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 3, + 2 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 3, + 3 + ] + }, + { + "behaviour": [ + "walk", + "fly" + ], + "base": "floor", + "pos": [ + 3, + 4 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 3, + 5 + ] + }, + { + "behaviour": [ + "walk", + "fly" + ], + "base": "floor", + "pos": [ + 3, + 6 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 3, + 7 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 3, + 8 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 3, + 9 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 4, + 0 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 4, + 1 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 4, + 2 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 4, + 3 + ] + }, + { + "behaviour": [ + "walk", + "fly" + ], + "base": "floor", + "pos": [ + 4, + 4 + ] + }, + { + "behaviour": [ + "walk", + "fly" + ], + "base": "floor", + "pos": [ + 4, + 5 + ] + }, + { + "behaviour": [ + "walk", + "fly" + ], + "base": "floor", + "pos": [ + 4, + 6 + ] + }, + { + "behaviour": [ + "walk", + "fly" + ], + "base": "floor", + "pos": [ + 4, + 7 + ] + }, + { + "behaviour": [ + "walk", + "fly" + ], + "base": "floor", + "pos": [ + 4, + 8 + ] + }, + { + "behaviour": [ + "walk", + "fly" + ], + "base": "floor", + "pos": [ + 4, + 9 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 5, + 0 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 5, + 1 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 5, + 2 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 5, + 3 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 5, + 4 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 5, + 5 + ] + }, + { + "behaviour": [ + "walk", + "fly" + ], + "base": "floor", + "pos": [ + 5, + 6 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 5, + 7 + ] + }, + { + "behaviour": [ + "walk", + "fly" + ], + "base": "floor", + "pos": [ + 5, + 8 + ] + }, + { + "behaviour": [ + "walk", + "fly" + ], + "base": "floor", + "pos": [ + 5, + 9 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 6, + 0 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 6, + 1 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 6, + 2 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 6, + 3 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 6, + 4 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 6, + 5 + ] + }, + { + "behaviour": [ + "walk", + "fly" + ], + "base": "floor", + "pos": [ + 6, + 6 + ] + }, + { + "behaviour": [ + "walk", + "fly" + ], + "base": "floor", + "pos": [ + 6, + 7 + ] + }, + { + "behaviour": [ + "walk", + "fly" + ], + "base": "floor", + "pos": [ + 6, + 8 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 6, + 9 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 7, + 0 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 7, + 1 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 7, + 2 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 7, + 3 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 7, + 4 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 7, + 5 + ] + }, + { + "behaviour": [ + "walk", + "fly" + ], + "base": "floor", + "pos": [ + 7, + 6 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 7, + 7 + ] + }, + { + "behaviour": [ + "walk", + "fly" + ], + "base": "floor", + "pos": [ + 7, + 8 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 7, + 9 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 8, + 0 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 8, + 1 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 8, + 2 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 8, + 3 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 8, + 4 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 8, + 5 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 8, + 6 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 8, + 7 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 8, + 8 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 8, + 9 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 9, + 0 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 9, + 1 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 9, + 2 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 9, + 3 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 9, + 4 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 9, + 5 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 9, + 6 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 9, + 7 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 9, + 8 + ] + }, + { + "behaviour": [], + "base": "cwall", + "pos": [ + 9, + 9 + ] + } + ] +} diff --git a/koperkapel/scenes/base.py b/koperkapel/scenes/base.py index 9bad13c..58f3874 100644 --- a/koperkapel/scenes/base.py +++ b/koperkapel/scenes/base.py @@ -30,6 +30,10 @@ class Engine: self._scene = scene self._scene.enter() + def quit_game(self): + from pgzero.game import exit + exit() + @apply_events def update(self, dt): return self._scene.update(dt) @@ -77,9 +81,37 @@ class ChangeSceneEvent(Event): ENGINE_METHOD = "change_scene" +class QuitEvent(Event): + """ Quit the game. """ + + ENGINE_METHOD = "quit_game" + + +class Actors: + """ A list of actors. """ + + def __init__(self): + self._actors = [] + + def add(self, actor): + self._actors.append(actor) + return actor + + def remove(self, actor): + self._actors.remove(actor) + return actor + + def draw(self, screen): + for actor in self._actors: + actor.draw() # TODO: allow an option screen to be passed in + + class Scene: """ Base class for scenes. """ + def __init__(self): + self.actors = Actors() + def enter(self): pass @@ -90,7 +122,8 @@ class Scene: pass def draw(self, screen): - pass + screen.clear() + self.actors.draw(screen) def on_mouse_down(self, pos, button): pass diff --git a/koperkapel/scenes/constants.py b/koperkapel/scenes/constants.py new file mode 100644 index 0000000..cc6f1cb --- /dev/null +++ b/koperkapel/scenes/constants.py @@ -0,0 +1,8 @@ +# Various constants + +import os + +TILE_SIZE = 64 + +# FIXME: Do betterer +LEVEL_PATH = os.path.join(os.path.dirname(__file__), '..', 'levels') diff --git a/koperkapel/scenes/level.py b/koperkapel/scenes/level.py new file mode 100644 index 0000000..6537e5c --- /dev/null +++ b/koperkapel/scenes/level.py @@ -0,0 +1,42 @@ +"""Render a level""" + +import json +import os + +from pgzero.constants import keys +from pgzero.loaders import images +from .base import Scene, ChangeSceneEvent +from .constants import TILE_SIZE, LEVEL_PATH + + +class LevelScene(Scene): + """ Level scene. """ + + def __init__(self, level_name): + self._level_name = level_name + f = open(os.path.join(LEVEL_PATH, level_name + '.json')) + level_data = json.load(f) + f.close() + self._tiles = level_data['tiles'] + self._load_tile_images() + + def draw(self, screen): + screen.clear() + #screen.draw.text("This is level {}".format(self._level_name), (200, 100)) + for tile in self._tiles: + pos = tile['pos'] + pos = [pos[0] * TILE_SIZE, pos[1] * TILE_SIZE] + if not 'image' in tile: + # Skip broken tiles for now + continue + screen.blit(tile['image'], pos) + + def _load_tile_images(self): + """Load all the tile images""" + for tile in self._tiles: + tile['image'] = getattr(images, tile['base']) + + def on_key_down(self, key, mod, unicode): + if key == keys.ESCAPE: + from .menu import MenuScene + return [ChangeSceneEvent(MenuScene())] diff --git a/koperkapel/scenes/menu.py b/koperkapel/scenes/menu.py index dac4402..39ab12c 100644 --- a/koperkapel/scenes/menu.py +++ b/koperkapel/scenes/menu.py @@ -1,17 +1,53 @@ """ Main menu scene. """ from pgzero.constants import keys -from .base import Scene, ChangeSceneEvent -from .credits import CreditsScene +from ..actors.buttons import TextButton +from ..actors.navigator import ActorNavigator +from .base import Scene, ChangeSceneEvent, QuitEvent + + class MenuScene(Scene): """ Main menu scene. """ - def draw(self, screen): - screen.clear() - screen.draw.text("Main menu", (300, 300)) + def __init__(self): + super().__init__() + self._title = self.actors.add(TextButton("Main Menu")) + self._title.pos = (300, 200) + self._nav = ActorNavigator() + self._menu = [ + TextButton("Play", action=self.change_to_level), + TextButton("Credits", action=self.change_to_credits), + TextButton("Quit", action=self.quit), + ] + for i, item in enumerate(self._menu): + item.pos = (300, 300 + (i * 100)) + self.actors.add(item) + self._nav.connect( + self._menu, [keys.DOWN, keys.RIGHT], [keys.UP, keys.LEFT], + wrap=True) + self._nav.current.select() + + def change_to_level(self): + from .level import LevelScene + return [ChangeSceneEvent(LevelScene('level1'))] + + def change_to_credits(self): + from .credits import CreditsScene + return [ChangeSceneEvent(CreditsScene())] + + def quit(self): + return [QuitEvent()] def on_key_down(self, key, mod, unicode): - if key == keys.C: - return [ChangeSceneEvent(CreditsScene())] + if key == keys.RETURN: + current = self._nav.current + if current is not None: + return current.action() + else: + prev, current = self._nav.follow(key) + if prev is not None: + prev.deselect() + if current is not None: + current.select() diff --git a/screenshots/koperkapel_2016_02_28_levelish.png b/screenshots/koperkapel_2016_02_28_levelish.png new file mode 100644 index 0000000..2b0ecb9 Binary files /dev/null and b/screenshots/koperkapel_2016_02_28_levelish.png differ diff --git a/sources/vector/big_roach.svg b/sources/vector/big_roach.svg new file mode 100644 index 0000000..320d1b1 --- /dev/null +++ b/sources/vector/big_roach.svg @@ -0,0 +1,304 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sources/vector/generate_bitmaps.sh b/sources/vector/generate_bitmaps.sh new file mode 100755 index 0000000..bb1a259 --- /dev/null +++ b/sources/vector/generate_bitmaps.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +for dir in roach +do + mkdir -p ../../images/$dir + for x in $dir/* + do + inkscape -z -e ../../images/${x%.*}.png -w 64 -h 64 $x + done +done diff --git a/sources/vector/roach/eyes_1.svg b/sources/vector/roach/eyes_1.svg new file mode 100644 index 0000000..fb6fe23 --- /dev/null +++ b/sources/vector/roach/eyes_1.svg @@ -0,0 +1,100 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/sources/vector/roach/eyes_2.svg b/sources/vector/roach/eyes_2.svg new file mode 100644 index 0000000..60f742e --- /dev/null +++ b/sources/vector/roach/eyes_2.svg @@ -0,0 +1,109 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + diff --git a/sources/vector/roach/eyes_3.svg b/sources/vector/roach/eyes_3.svg new file mode 100644 index 0000000..171f519 --- /dev/null +++ b/sources/vector/roach/eyes_3.svg @@ -0,0 +1,100 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/sources/vector/roach/eyes_4.svg b/sources/vector/roach/eyes_4.svg new file mode 100644 index 0000000..f0f4c75 --- /dev/null +++ b/sources/vector/roach/eyes_4.svg @@ -0,0 +1,100 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/sources/vector/roach/roach_1.svg b/sources/vector/roach/roach_1.svg new file mode 100644 index 0000000..86d42aa --- /dev/null +++ b/sources/vector/roach/roach_1.svg @@ -0,0 +1,257 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sources/vector/roach/roach_2.svg b/sources/vector/roach/roach_2.svg new file mode 100644 index 0000000..5a23ad3 --- /dev/null +++ b/sources/vector/roach/roach_2.svg @@ -0,0 +1,238 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sources/vector/roach/roach_3.svg b/sources/vector/roach/roach_3.svg new file mode 100644 index 0000000..d3a3768 --- /dev/null +++ b/sources/vector/roach/roach_3.svg @@ -0,0 +1,258 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sources/vector/roach/roach_4.svg b/sources/vector/roach/roach_4.svg new file mode 100644 index 0000000..33f1289 --- /dev/null +++ b/sources/vector/roach/roach_4.svg @@ -0,0 +1,243 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +