From 8360707a0408460e8ea4395257a656b85e6a6538 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Tuomas=20R=C3=A4s=C3=A4nen?= Date: Mon, 22 Oct 2007 23:33:43 +0300 Subject: [PATCH] Started to build Mapview-class. It acts as an observer for the Map. --- otium.py | 32 ++++++++++- world.py | 185 ++++++++++++++++++++++++++++++++++++++------------------------- 2 files changed, 142 insertions(+), 75 deletions(-) diff --git a/otium.py b/otium.py index 3dae23a..419b035 100644 --- a/otium.py +++ b/otium.py @@ -6,6 +6,8 @@ from world import Tile from world import Symbol from world import Object from world import Map +from world import RectangleArea +from world import MapEvent # Define the colors BLACK = 0 @@ -89,6 +91,30 @@ def generate_world(): y = random.randint(0, worldmap.height() - 1) create_tile(param.symbol, Position(x, y), "Symbol description.", param.size) +class Mapview(RectangleArea): + + def __init__(self, width, height, stdscr): + RectangleArea.__init__(self, width, height) + self.__width_offset__ = 0 + self.__height_offset__ = 0 + self.__stdscr__ = stdscr + + def map_event(self, event): + self.__stdscr__.addstr(20, 20, "Unknown command.") + self.__stdscr__.refresh() + if event.type() == MapEvent.AFTER_INSERT: + self.draw_object(event.object()) + + def draw_object(self, object): + char = object.symbol().char() + color = object.symbol().color() + x = object.position().x() - self.__width_offset__ + y = object.position().y() - self.__height_offset__ + pos = Position(x,y) + if self.has_position(pos): + self.__stdscr__.addstr(y,x, char, curses.color_pair(color)) + + # FIXME/tuos: This method should be a part of screen/vieport def draw_map(stdscr, player): """Draws the map and the player to the screen.""" @@ -129,7 +155,6 @@ def draw_map(stdscr, player): rown += 1 stdscr.addstr(PLAY_AREA_ROWS + player_row_correction, PLAY_AREA_COLS + player_col_correction, "@", curses.color_pair(WHITE)) - stdscr.refresh() # FIXME/tuos: This method should be a part of screen/vieport def init_curses(): @@ -146,11 +171,15 @@ def main(stdscr): """Asks for input and executes commands until game ends.""" init_curses() random.seed() +# mv = Mapview(PLAY_AREA_COLS, PLAY_AREA_ROWS, stdscr) generate_world() +# worldmap.add_observer(mv) +# worldmap.remove_observer(mv) end = False while not end: stdscr.clear() draw_map(stdscr, player) +# mv.draw_object(player) c = stdscr.getch() if c == ord('q'): end = True @@ -159,7 +188,6 @@ def main(stdscr): commands[c]() except: stdscr.addstr(20, 20, "Unknown command.") - pass player.move(directions[c]) if __name__ == "__main__": diff --git a/world.py b/world.py index 24633ff..7f68913 100644 --- a/world.py +++ b/world.py @@ -2,9 +2,9 @@ import math class Position: """ - Simple class for position information. Implements 'comparable interface' by - implementing __cmp__ -method. Comparing is based purely on distances - between two Position-instances. + Simple class for position information. Implements 'comparable + interface' by implementing __cmp__ -method. Comparing is based purely + on distances between two Position-instances. """ def __init__(self, x, y): self.__x__ = x @@ -76,11 +76,11 @@ class Position: class Symbol: """ Symbolic representation for the use of different objects. Every symbol - contains char+color -combination the be represented in the maps of the game. - Every symbol can be also referenced by an unique_char. At the moment, it's - up to the developer to take care of uniqueness of these unique_chars. It's - also up to the developer to take care of the relationship between - unique_char and char+color -combination. + contains char+color -combination the be represented in the maps of the + game. Every symbol can be also referenced by an unique_char. At the + moment, it's up to the developer to take care of uniqueness of these + unique_chars. It's also up to the developer to take care of the + relationship between unique_char and char+color -combination. """ def __init__(self, char, color, unique_char): @@ -113,9 +113,10 @@ class Symbol: class Object: """ - Abstract baseclass for every object in the game. At minimum, every object - has a position information, a symbolic (color + char) representation and a - description which reveals it's deepest essence to the players. + Abstract baseclass for every object in the game. At minimum, every + object has a position information, a symbolic (color + char) + representation and a description which reveals it's deepest essence to + the players. """ def __init__(self, position, symbol, description): self.__pos__ = position @@ -124,12 +125,13 @@ class Object: def move(self, direction): """ - Parameter direction is a kind of direction vector, still being instance - of Position-class. It represents the new relative position of this object. + Parameter direction is a kind of direction vector, still being + instance of Position-class. It represents the new relative + position of this object. Example: - >>> o = Object(4,5,None) - >>> o.move(Position(0,1)) # This means that object is moving to north. + >>> o = Object(Position(4,5),None, None) + >>> o.move(Position(0,1)) # North >>> Position(4,6) == o.position() # Assume 'speed' == 1 True """ @@ -153,43 +155,70 @@ class Tile(Object): Object.__init__(self, position, symbol, description) -class MapException(Exception): +class WorldException(Exception): """ Baseclass for every exception that Map-class may throw. """ pass -class Map: - """ - Class to represent rectangle-shaped areas populated by different kinds - of tiles. Tiles are accessed using coordinate-like attributes. - """ - def __init__(self, width, height, empty_symbol): - self.__empty_symbol__ = empty_symbol +class RectangleArea: + def __init__(self, width, height): self.__height__ = height self.__width__ = width + + def surfacearea(self): + """ + >>> Map(4,5,None).surfacearea() + 20 + """ + return self.width() * self.height() + + def has_position(self, pos): + """ + >>> m = Map(5,5, None) + >>> m.has_position(Position(3,3)) + True + >>> m.has_position(Position(6,1)) + False + >>> m.has_position(Position(5,5)) + False + """ + return pos.x() < self.width() and pos.y() < self.height() + + def height(self): + return self.__height__ + + def width(self): + return self.__width__ + + def __position__(self, i): + y = i / self.width() + x = i % self.width() + return Position(x,y) + + def __index__(self, pos): + return pos.y() * self.width() + pos.x() + +class StoringRectangleArea(RectangleArea): + def __init__(self, width, height, empty_symbol): + RectangleArea.__init__(self, width, height) + self.__empty_symbol__ = empty_symbol self.__items__ = [] self.__init_items__() def __init_items__(self): for i in range(self.surfacearea()): pos = self.__position__(i) - t = Tile(pos, self.empty_symbol(), "Space. Cold. HHRRR.") + t = Tile(pos, self.empty_symbol(), "Space. Cold.") self.__items__.append(t) - def surfacearea(self): - """ - >>> Map(4,5,None).surfacearea() - 20 - """ - return self.width() * self.height() def is_complete(self): """ - Returns a boolean value that indicates whether every position in the - area is populated by a item. If there is even one empty_symbol, this - method returns false. + Returns a boolean value that indicates whether every position + in the area is populated by a item. If there is even one + empty_symbol, this method returns false. Examples: @@ -210,35 +239,9 @@ class Map: return True return False - def has_position(self, position): - """ - >>> m = Map(5,5, None) - >>> m.has_position(Position(3,3)) - True - >>> m.has_position(Position(6,1)) - False - >>> m.has_position(Position(5,5)) - False - """ - return position.x() < self.width() and position.y() < self.height() - def empty_symbol(self): return self.__empty_symbol__ - def height(self): - return self.__height__ - - def width(self): - return self.__width__ - - def __position__(self, i): - y = i / self.width() - x = i % self.width() - return Position(x,y) - - def __index__(self, pos): - return pos.y() * self.width() + pos.x() - def get(self, pos): i = self.__index__(pos) return self.__items__[i] @@ -251,33 +254,69 @@ class Map: # Inserts tiles from the given list. # """ # if len(tiles) != self.surfacearea(): -# raise MapException("Data does not fit into the map.") -# raise MapException("Not yet implemented.") +# raise WorldException("Data does not fit into the map.") +# raise WorldException("Not yet implemented.") def insert(self, tile, replace=False): """ Public interface for inserting tiles into the map. Overrides Map's __insert-method and adds handling for replacing - inserts. Throws MapException if there is already a tile in - position (x,y) and replacing is not allowed. By default, replacing - is forbidden. + inserts. Throws WorldException if there is already a tile in + position (x,y) and replacing is not allowed. By default, + replacing is forbidden. """ pos = tile.position() if self.reserved(pos) and not replace: - raise MapException("Position %s is already reserved." % pos) + raise WorldException("Position %s is reserved." % pos) i = self.__index__(pos) self.__items__[i] = tile - def draw(self): + +class MapEvent: + BEFORE_INSERT = 0 + AFTER_INSERT = 1 + + def __init__(self, type, object): + self.__type__ = type + self.__object__ = object + + def type(self): + return self.__type__ + + def object(self): + return self.__object__ + + +class Map(StoringRectangleArea): + """ + Class to represent rectangle-shaped areas populated by different kinds + of tiles. Tiles are accessed using coordinate-like attributes. + """ + def __init__(self, width, height, empty_symbol): + StoringRectangleArea.__init__(self, width, height, empty_symbol) + self.__observers__ = set() + + def add_observer(self, observer): + self.__observers__.add(observer) + + def remove_observer(self, observer): + self.__observers__.discard(observer) + + def notify_observers(self, event): + for observer in self.__observers__: + observer.map_event(event) + + def insert(self, tile, replace=False): """ - This method is ONLY FOR TESTING PURPOSES. + Public interface for inserting tiles into the map. + Overrides Map's __insert-method and adds handling for replacing + inserts. Throws WorldException if there is already a tile in + position (x,y) and replacing is not allowed. By default, + replacing is forbidden. """ - last_line = 0 - for tile in self: - if tile.position().y() > last_line: - last_line += 1 - print "" - print tile, + self.notify_observers(MapEvent(MapEvent.BEFORE_INSERT, tile)) + StoringRectangleArea.insert(self, tile, replace) + self.notify_observers(MapEvent(MapEvent.AFTER_INSERT, tile)) def _test(): import doctest -- 2.11.4.GIT