def load_from_map_file(self): layout_file = open(self.map_file) r = csv.reader(layout_file, delimiter=',') first_line = r.next() self.width = int(first_line[0]) self.height = int(first_line[1]) self.scenario_number = int(first_line[2]) self.terrain_layer = Matrix(self.width, self.height) self.scenario_layer = [Matrix(self.width, self.height) for i in\ range(self.scenario_number)] y = 0 for line in r: if len(line) == self.width: for x, value in enumerate(line): self.terrain_layer[x, y] = self.terrain_tileset.\ tiles[int(value)] y += 1 if y >= self.height: break for i in xrange(self.scenario_number): y = 0 for line in r: if len(line) == self.width: for x, value in enumerate(line): tile = self.scenario_tileset[i].tiles[int(value)] self.scenario_layer[i][x, y] = tile y += 1 if y >= self.height: break layout_file.close()
def __init__(self, width, height, width_in_cells, height_in_cells, visible=False, focusable=False, theme=None): Div.__init__(self, width, height, focusable, theme) self.visible = visible self.width_in_cells = width_in_cells self.height_in_cells = height_in_cells self.cell_width = self.width / self.width_in_cells self.cell_height = self.height / self.height_in_cells self.cells = Matrix(width_in_cells, height_in_cells) for y in xrange(height_in_cells): for x in xrange(width_in_cells): div = Div(self.cell_width, self.cell_height, focusable=False, theme=self.theme) pos = (x * self.cell_width, y * self.cell_height) self.cells[x, y] = div self.add_widget(div, pos)
class Grid(Div): """ A Grid is a Div that contains cells in a matrix-like disposition. All cells in a Grid will have the same size. Each cell is a Div itself, whose parent is the Grid. Any widget can be added to these cells. *width* and *height* specify the Grid's total size. *width_in_cells* and *height_in_cells* are the number of cells in a line and the number of cells in a column of the Grid, respectively. The size of each cell will be calculated from *width*, *height*, *width_in_cells* and *height_in_cells*. *visible* is unused for now, but will cause the Grid to be displayed in the future. *focusable* and *theme* behave like in any other Widget. """ def __init__(self, width, height, width_in_cells, height_in_cells, visible=False, focusable=False, theme=None): Div.__init__(self, width, height, focusable, theme) self.visible = visible self.width_in_cells = width_in_cells self.height_in_cells = height_in_cells self.cell_width = self.width / self.width_in_cells self.cell_height = self.height / self.height_in_cells self.cells = Matrix(width_in_cells, height_in_cells) for y in xrange(height_in_cells): for x in xrange(width_in_cells): div = Div(self.cell_width, self.cell_height, focusable=False, theme=self.theme) pos = (x * self.cell_width, y * self.cell_height) self.cells[x, y] = div self.add_widget(div, pos) def __getitem__(self, pos): """ Return the Div representing a cell of the Grid. *pos* should be an (x, y) tuple with the indexes of the intended cell. """ return self.cells[pos] def add_lines(self, number_of_lines=1): """ Add one or more cell lines to the Grid. The Grid's dimensions will be expanded, while the size of each cell will remain unchanged. *number_of_lines* specifies how many lines will be added. This defaults to 1. """ old_height = self.height_in_cells self.height_in_cells += number_of_lines self.cells.resize(self.width_in_cells, self.height_in_cells) for y in xrange(old_height, self.height_in_cells): for x in xrange(self.width_in_cells): div = Div(self.cell_width, self.cell_height, focusable=False, theme=self.theme) pos = (x * self.cell_width, y * self.cell_height) self.cells[x, y] = div self.add_widget(div, pos) def add_columns(self, number_of_columns=1): """ Add one or more cell columns to the Grid. The Grid's dimensions will be expanded, while the size of each cell will remain unchanged. *number_of_columns* specifies how many columns will be added. This defaults to 1. """ old_width = self.width_in_cells self.width_in_cells += number_of_columns self.cells.resize(self.width_in_cells, self.height_in_cells) for y in xrange(self.height_in_cells): for x in xrange(old_width, self.width_in_cells): div = Div(self.cell_width, self.cell_height, focusable=False, theme=self.theme) pos = (x * self.cell_width, y * self.cell_height) self.cells[x, y] = div self.add_widget(div, pos) def remove_lines(self, number_of_lines=1): """ Remove one or more cell lines from the Grid. The Grid's dimensions will be shrunk, while the size of each cell will remain unchanged. *number_of_lines* specifies how many lines will be removed. This defaults to 1. """ if self.height_in_cells <= number_of_lines: raise IndexError('Cannot remove %d lines from a Grid with %d ' 'lines' % (number_of_lines, self.height_in_cells)) old_height = self.height_in_cells self.height_in_cells -= number_of_lines for y in xrange(self.height_in_cells, old_height): for x in xrange(self.width_in_cells): self.remove_widget(self.cells[x, y]) self.cells.resize(self.width_in_cells, self.height_in_cells) def remove_columns(self, number_of_columns=1): """ Remove one or more cell columns from the Grid. The Grid's dimensions will be shrunk, while the size of each cell will remain unchanged. *number_of_columns* specifies how many columns will be removed. This defaults to 1. """ raise IndexError('Cannot remove %d columns from a Grid with %d '\ 'columns' % (number_of_columns, self.width_in_cells)) old_width = self.width_in_cells self.width_in_cells -= number_of_columns for y in xrange(self.height_in_cells): for x in xrange(self.width_in_cells, old_width): self.remove_widget(self.cells[x, y]) self.cells.resize(self.width_in_cells, self.height_in_cells)
def __init__(self, map_file, terrain_tileset_files, scenario_tileset_files_list): """ *Constructor:* Initialize the MapModel with a layout defined by *map_file* (a .map file). The terrain tileset is specified by *terrain_tileset_files*, which is a tuple (tileset image filename, tileset boundaries filename). Tileset image filename should be a bitmap file (.png typically) and tileset boundaries filename) should be a .bnd file. The scenario tilesets are specified by *scenario_tileset_files_list*, a list of tuples like the one passed as *terrain_tileset_files*. Each will correspond to a scenario layer. """ Model.__init__(self) self.world = None self.id = None self.music = None # Set up party self.party = None self.party_avatar = None # Load file data self.map_file = map_file self.terrain_tileset_files = terrain_tileset_files self.scenario_tileset_files_list = scenario_tileset_files_list self.terrain_tileset = Tileset(self.terrain_tileset_files[0], self.terrain_tileset_files[1]) self.scenario_tileset = [Tileset(i, j) for i, j in\ self.scenario_tileset_files_list] self.load_from_map_file() # Set up local state self.local_state = None # Set up objects self.objects = [] self.below_objects = [] self.obstacle_objects = [] self.above_objects = [] self.updatable_objects = [] self.object_layer = Matrix(self.width, self.height) for x in range(self.width): for y in range(self.height): self.object_layer[x, y] = ObjectCell() # Set up areas self.areas = [] self.area_layer = Matrix(self.width, self.height) for x in range(self.width): for y in range(self.height): self.area_layer[x, y] = [] # Set up context system self.pause_delay = 0 self.contexts = []
class MapModel(Model): """ The MapModel is the class that models a map's data and behaviour. It is the Model component of the MVC pattern. MapModel is a class made to be inherited, so that specific behavior (objects, areas, parallel processes) may be added. """ def __init__(self, map_file, terrain_tileset_files, scenario_tileset_files_list): """ *Constructor:* Initialize the MapModel with a layout defined by *map_file* (a .map file). The terrain tileset is specified by *terrain_tileset_files*, which is a tuple (tileset image filename, tileset boundaries filename). Tileset image filename should be a bitmap file (.png typically) and tileset boundaries filename) should be a .bnd file. The scenario tilesets are specified by *scenario_tileset_files_list*, a list of tuples like the one passed as *terrain_tileset_files*. Each will correspond to a scenario layer. """ Model.__init__(self) self.world = None self.id = None self.music = None # Set up party self.party = None self.party_avatar = None # Load file data self.map_file = map_file self.terrain_tileset_files = terrain_tileset_files self.scenario_tileset_files_list = scenario_tileset_files_list self.terrain_tileset = Tileset(self.terrain_tileset_files[0], self.terrain_tileset_files[1]) self.scenario_tileset = [Tileset(i, j) for i, j in\ self.scenario_tileset_files_list] self.load_from_map_file() # Set up local state self.local_state = None # Set up objects self.objects = [] self.below_objects = [] self.obstacle_objects = [] self.above_objects = [] self.updatable_objects = [] self.object_layer = Matrix(self.width, self.height) for x in range(self.width): for y in range(self.height): self.object_layer[x, y] = ObjectCell() # Set up areas self.areas = [] self.area_layer = Matrix(self.width, self.height) for x in range(self.width): for y in range(self.height): self.area_layer[x, y] = [] # Set up context system self.pause_delay = 0 self.contexts = [] def load_from_map_file(self): layout_file = open(self.map_file) r = csv.reader(layout_file, delimiter=',') first_line = r.next() self.width = int(first_line[0]) self.height = int(first_line[1]) self.scenario_number = int(first_line[2]) self.terrain_layer = Matrix(self.width, self.height) self.scenario_layer = [Matrix(self.width, self.height) for i in\ range(self.scenario_number)] y = 0 for line in r: if len(line) == self.width: for x, value in enumerate(line): self.terrain_layer[x, y] = self.terrain_tileset.\ tiles[int(value)] y += 1 if y >= self.height: break for i in xrange(self.scenario_number): y = 0 for line in r: if len(line) == self.width: for x, value in enumerate(line): tile = self.scenario_tileset[i].tiles[int(value)] self.scenario_layer[i][x, y] = tile y += 1 if y >= self.height: break layout_file.close() # Virtual, should be implemented. def initialize(self, local_state, global_state): """ *Virtual* Put the map in an initial, virgin state if the *local_state* specified is None. Puts the map in a state loaded from the *local_state*, and the *global_state* otherwise. *local_state* is the serializable object returned by MapModel.save_state() when this map was saved. *global_state* is a dict mapping all feature strings to their local states. """ pass # Virtual, should be implemented. def save_state(self): """ *Virtual* Save the map's state to a local state and return it. """ return None def add_party(self, party, position, facing=DOWN, speed=NORMAL_SPEED): """ Add a *party* (Party instance) to the Map at the given *position*. Optionally, the starting *facing* and *speed* may be specified. The defaults are *facing* down and normal *speed*. """ assert self.party is None, 'Map already has a party' self.party = party self.party_avatar = PartyAvatar(party, facing, speed) self.add_object(self.party_avatar, position) def remove_party(self): """ Remove the party from the Map, returning a 2-tuple with it as first element and its position as second element. Return (None, None) if there is no party in the map. """ if self.party is None: return None, None result = self.party, self.party_avatar.position self.remove_object(self.party_avatar) self.party = None self.party_avatar = None return result def add_object(self, obj, position): """ Add an object to the map at the specified position. Returns whether the operation was successful (it can fail when the position is occupied by an obstacle and the object to be added is also an obstacle). """ self.object_layer[position].add_object(obj) self.objects.append(obj) if obj.is_below(): self.below_objects.append(obj) elif obj.is_obstacle(): self.obstacle_objects.append(obj) elif obj.is_above(): self.above_objects.append(obj) else: raise Exception('Object is neither below, obstacle or above') if hasattr(obj, 'update'): self.updatable_objects.append(obj) obj.position = position obj.areas = self.area_layer[position] obj.map = self return True def remove_object(self, obj): """ Remove an object from the map and returns the Position where it was. Return None if the object was not in the map. """ self.objects.remove(obj) if obj.is_below(): self.below_objects.remove(obj) elif obj.is_obstacle(): self.obstacle_objects.remove(obj) elif obj.is_above(): self.above_objects.remove(obj) else: raise Exception('Object is neither below, obstacle or above') if hasattr(obj, 'update'): self.updatable_objects.remove(obj) self.object_layer[obj.position].remove_object(obj) result = obj.position obj.position, obj.map = None, None return result def add_area(self, area, positions): """ Add a MapArea to the map at the specified positions. *Positions* should be an iterable that returns the Positions over which the MapArea extends. """ self.areas.append(area) for pos in positions: self.area_layer[pos].append(area) area.area = positions def remove_area(self, area, positions): """ Remove a MapArea from the map at the specified positions. *Positions* should be an iterable that returns the Positions from which the area should be removed. """ self.areas.remove(area) for pos in area.area: self.area_layer[pos].remove(area) area.area = list(set(area.area) - set(positions)) def try_to_move_object(self, obj, direction, slide=False, back=False): """ Try to move an object to the specified direction (UP, DOWN, LEFT or RIGHT). Return whether the object could be moved. If *slide* is True, the movement will use only the static frame of the object. If *back* is True, the movement will be backwards. """ if obj.movement_phase > 0: return False if back: obj.facing = inverse(direction) else: obj.facing = direction old_pos = obj.position desired = obj.position.step(direction) if not self.terrain_layer.valid(desired): return False if not obj.is_obstacle() or self.can_move(old_pos, desired, direction): # Move old_object = self.object_layer[old_pos] new_object = self.object_layer[desired] self.move_object(obj, old_object, new_object, desired, slide, back) if obj is self.party_avatar: for area in self.area_layer[old_pos]: if area not in self.area_layer[desired]: area.party_left(self.party_avatar, old_pos) return True else: # Do not move, something is on the way new_object = self.object_layer[desired] if obj is self.party_avatar and new_object.obstacle is not None: new_object.obstacle.collide_with_party(self.party_avatar, direction) return False def can_move(self, old_pos, desired, direction): if not self.terrain_layer.valid(desired): return False old_terrain = self.terrain_layer[old_pos] new_terrain = self.terrain_layer[desired] old_scenario = [self.scenario_layer[i][old_pos] for i in\ range(self.scenario_number)] new_scenario = [self.scenario_layer[i][desired] for i in\ range(self.scenario_number)] new_object = self.object_layer[desired] return not self.is_obstructed(old_terrain, old_scenario, new_terrain, new_scenario, new_object, direction) def is_obstructed(self, old_terrain, old_scenario_list, new_terrain, new_scenario_list, new_object, direction): if new_object.obstacle is not None: return True if self.direction_obstructed(old_terrain, old_scenario_list, \ direction): return True inv = inverse(direction) if self.direction_obstructed(new_terrain, new_scenario_list, inv): return True return False def direction_obstructed(self, terrain, scenario_list, direction): for scenario in reversed(scenario_list): if scenario.cannot_be_entered(direction)\ or scenario.is_obstacle(): return True elif scenario.is_below(): return False if terrain.is_obstacle() or terrain.cannot_be_entered(direction): return True else: return False def move_object(self, obj, old_object, new_object, new_pos, slide, back): obj.movement_phase = obj.speed - 1 obj.sliding = slide obj.going_back = back old_object.remove_object(obj) new_object.add_object(obj) obj.prev_position = obj.position obj.position = new_pos obj.prev_areas = obj.areas obj.areas = self.area_layer[new_pos] def teleport_object(self, obj, new_pos): old_pos = obj.position old_object = self.object_layer[old_pos] new_object = self.object_layer[new_pos] old_object.remove_object(obj) new_object.add_object(obj) obj.prev_position = old_pos obj.position = new_pos obj.prev_areas = obj.areas obj.areas = self.area_layer[new_pos] def party_action(self): old_pos = self.party_avatar.position desired = old_pos.step(self.party_avatar.facing) # Activate object that the party is looking at if self.terrain_layer.valid(desired): obj_in_front = self.object_layer[desired].obstacle if obj_in_front is not None: obj_in_front.activate(self.party_avatar, self.party_avatar.facing) across_pos = desired.step(self.party_avatar.facing) if (self.terrain_layer.valid(across_pos) and ((obj_in_front is not None and obj_in_front.is_counter()) or any([layer[desired].is_counter() for layer in\ self.scenario_layer]))): # Counter attribute obj_across = self.object_layer[across_pos].obstacle if obj_across is not None: obj_across.activate(self.party_avatar, self.party_avatar.facing) # Activate objects that the party is standing on or under old_object = self.object_layer[old_pos] for obj in old_object.below: obj.activate(self.party_avatar, self.party_avatar.facing) for obj in old_object.above: obj.activate(self.party_avatar, self.party_avatar.facing) def schedule_message(self, message): """ Add a Dialog to the message queue, displaying it as soon as the messages that were previously there are done. """ self.controller.message_queue.push(message) def pause(self, length): """ Stop movement and acting in the map for *length* frames. """ self.pause_delay = length def __repr__(self): return '(Map width=%s height=%s file=%s)' % (str( self.width), str(self.height), self.map_file) def __str__(self): result = '' result += '+' + '-' * self.width + '+\n' for y in range(self.height): result += '|' for x in range(self.width): if self.party_avatar is not None and\ self.party_avatar.position == Position(x, y): result += 'P' else: result += ' ' result += '|\n' result += '+' + '-' * self.width + '+\n' return result def sync_movement(self, objects): """ Stop movement and acting in the map, except for the movement already scheduled or in progress in the objects specified. *objects* should be a list of those MapObjects. """ self.controller.sync_movement(objects) def save_world(self, filename): """ Save the game to the given file. """ self.world.state.save_local(self.id, self.save_state()) party_local_state = (self.id, self.party_avatar.position, self.party_avatar.facing) self.world.state.save_local(PARTY_POSITION_LOCAL_STATE, party_local_state) self.world.save(filename) def add_context(self, context): """ Add a context to be run over this map and the message queue context. """ self.contexts.append(context) def set_music(self, music_file): """ Set the background for the map. """ self.music = music_file def gameover(self): """ End the game. """ self.custom_gameover() self.controller.gameover() self.world.custom_gameover() def custom_gameover(self): """ *Virtual.* Overload to perform any reaction necessary to a MapModel.gameover() call. """ pass def create_controller(self): return MapController(self, self.controller_parent) def set_states(self, local_state, global_state): self.local_state = local_state self.global_state = global_state
print m print def test_get(x, y): try: value = m[x, y] print 'Getting', str(x) + ',', str(y), 'as', value except: print 'Error getting x=' + str(x), 'y=' + str(y) print m print if __name__ == '__main__': m = Matrix(3, 4) test_set(1, 1, 'a') test_set(1, 2, 'b') test_set(0, 3, 'c') test_set(3, 2, 'd') test_get(0, 1) test_get(1, 1) test_get(2, 2) test_get(0, 3) test_get(3, 2) m.resize(100, 100) test_get(99, 99)
class MapModel(Model): """ The MapModel is the class that models a map's data and behaviour. It is the Model component of the MVC pattern. MapModel is a class made to be inherited, so that specific behavior (objects, areas, parallel processes) may be added. """ def __init__(self, map_file, terrain_tileset_files, scenario_tileset_files_list): """ *Constructor:* Initialize the MapModel with a layout defined by *map_file* (a .map file). The terrain tileset is specified by *terrain_tileset_files*, which is a tuple (tileset image filename, tileset boundaries filename). Tileset image filename should be a bitmap file (.png typically) and tileset boundaries filename) should be a .bnd file. The scenario tilesets are specified by *scenario_tileset_files_list*, a list of tuples like the one passed as *terrain_tileset_files*. Each will correspond to a scenario layer. """ Model.__init__(self) self.world = None self.id = None self.music = None # Set up party self.party = None self.party_avatar = None # Load file data self.map_file = map_file self.terrain_tileset_files = terrain_tileset_files self.scenario_tileset_files_list = scenario_tileset_files_list self.terrain_tileset = Tileset(self.terrain_tileset_files[0], self.terrain_tileset_files[1]) self.scenario_tileset = [Tileset(i, j) for i, j in\ self.scenario_tileset_files_list] self.load_from_map_file() # Set up local state self.local_state = None # Set up objects self.objects = [] self.below_objects = [] self.obstacle_objects = [] self.above_objects = [] self.updatable_objects = [] self.object_layer = Matrix(self.width, self.height) for x in range(self.width): for y in range(self.height): self.object_layer[x, y] = ObjectCell() # Set up areas self.areas = [] self.area_layer = Matrix(self.width, self.height) for x in range(self.width): for y in range(self.height): self.area_layer[x, y] = [] # Set up context system self.pause_delay = 0 self.contexts = [] def load_from_map_file(self): layout_file = open(self.map_file) r = csv.reader(layout_file, delimiter=',') first_line = r.next() self.width = int(first_line[0]) self.height = int(first_line[1]) self.scenario_number = int(first_line[2]) self.terrain_layer = Matrix(self.width, self.height) self.scenario_layer = [Matrix(self.width, self.height) for i in\ range(self.scenario_number)] y = 0 for line in r: if len(line) == self.width: for x, value in enumerate(line): self.terrain_layer[x, y] = self.terrain_tileset.\ tiles[int(value)] y += 1 if y >= self.height: break for i in xrange(self.scenario_number): y = 0 for line in r: if len(line) == self.width: for x, value in enumerate(line): tile = self.scenario_tileset[i].tiles[int(value)] self.scenario_layer[i][x, y] = tile y += 1 if y >= self.height: break layout_file.close() # Virtual, should be implemented. def initialize(self, local_state, global_state): """ *Virtual* Put the map in an initial, virgin state if the *local_state* specified is None. Puts the map in a state loaded from the *local_state*, and the *global_state* otherwise. *local_state* is the serializable object returned by MapModel.save_state() when this map was saved. *global_state* is a dict mapping all feature strings to their local states. """ pass # Virtual, should be implemented. def save_state(self): """ *Virtual* Save the map's state to a local state and return it. """ return None def add_party(self, party, position, facing=DOWN, speed=NORMAL_SPEED): """ Add a *party* (Party instance) to the Map at the given *position*. Optionally, the starting *facing* and *speed* may be specified. The defaults are *facing* down and normal *speed*. """ assert self.party is None, 'Map already has a party' self.party = party self.party_avatar = PartyAvatar(party, facing, speed) self.add_object(self.party_avatar, position) def remove_party(self): """ Remove the party from the Map, returning a 2-tuple with it as first element and its position as second element. Return (None, None) if there is no party in the map. """ if self.party is None: return None, None result = self.party, self.party_avatar.position self.remove_object(self.party_avatar) self.party = None self.party_avatar = None return result def add_object(self, obj, position): """ Add an object to the map at the specified position. Returns whether the operation was successful (it can fail when the position is occupied by an obstacle and the object to be added is also an obstacle). """ self.object_layer[position].add_object(obj) self.objects.append(obj) if obj.is_below(): self.below_objects.append(obj) elif obj.is_obstacle(): self.obstacle_objects.append(obj) elif obj.is_above(): self.above_objects.append(obj) else: raise Exception('Object is neither below, obstacle or above') if hasattr(obj, 'update'): self.updatable_objects.append(obj) obj.position = position obj.areas = self.area_layer[position] obj.map = self return True def remove_object(self, obj): """ Remove an object from the map and returns the Position where it was. Return None if the object was not in the map. """ self.objects.remove(obj) if obj.is_below(): self.below_objects.remove(obj) elif obj.is_obstacle(): self.obstacle_objects.remove(obj) elif obj.is_above(): self.above_objects.remove(obj) else: raise Exception('Object is neither below, obstacle or above') if hasattr(obj, 'update'): self.updatable_objects.remove(obj) self.object_layer[obj.position].remove_object(obj) result = obj.position obj.position, obj.map = None, None return result def add_area(self, area, positions): """ Add a MapArea to the map at the specified positions. *Positions* should be an iterable that returns the Positions over which the MapArea extends. """ self.areas.append(area) for pos in positions: self.area_layer[pos].append(area) area.area = positions def remove_area(self, area, positions): """ Remove a MapArea from the map at the specified positions. *Positions* should be an iterable that returns the Positions from which the area should be removed. """ self.areas.remove(area) for pos in area.area: self.area_layer[pos].remove(area) area.area = list(set(area.area) - set(positions)) def try_to_move_object(self, obj, direction, slide=False, back=False): """ Try to move an object to the specified direction (UP, DOWN, LEFT or RIGHT). Return whether the object could be moved. If *slide* is True, the movement will use only the static frame of the object. If *back* is True, the movement will be backwards. """ if obj.movement_phase > 0: return False if back: obj.facing = inverse(direction) else: obj.facing = direction old_pos = obj.position desired = obj.position.step(direction) if not self.terrain_layer.valid(desired): return False if not obj.is_obstacle() or self.can_move(old_pos, desired, direction): # Move old_object = self.object_layer[old_pos] new_object = self.object_layer[desired] self.move_object(obj, old_object, new_object, desired, slide, back) if obj is self.party_avatar: for area in self.area_layer[old_pos]: if area not in self.area_layer[desired]: area.party_left(self.party_avatar, old_pos) return True else: # Do not move, something is on the way new_object = self.object_layer[desired] if obj is self.party_avatar and new_object.obstacle is not None: new_object.obstacle.collide_with_party(self.party_avatar, direction) return False def can_move(self, old_pos, desired, direction): if not self.terrain_layer.valid(desired): return False old_terrain = self.terrain_layer[old_pos] new_terrain = self.terrain_layer[desired] old_scenario = [self.scenario_layer[i][old_pos] for i in\ range(self.scenario_number)] new_scenario = [self.scenario_layer[i][desired] for i in\ range(self.scenario_number)] new_object = self.object_layer[desired] return not self.is_obstructed(old_terrain, old_scenario, new_terrain, new_scenario, new_object, direction) def is_obstructed(self, old_terrain, old_scenario_list, new_terrain, new_scenario_list, new_object, direction): if new_object.obstacle is not None: return True if self.direction_obstructed(old_terrain, old_scenario_list, \ direction): return True inv = inverse(direction) if self.direction_obstructed(new_terrain, new_scenario_list, inv): return True return False def direction_obstructed(self, terrain, scenario_list, direction): for scenario in reversed(scenario_list): if scenario.cannot_be_entered(direction)\ or scenario.is_obstacle(): return True elif scenario.is_below(): return False if terrain.is_obstacle() or terrain.cannot_be_entered(direction): return True else: return False def move_object(self, obj, old_object, new_object, new_pos, slide, back): obj.movement_phase = obj.speed - 1 obj.sliding = slide obj.going_back = back old_object.remove_object(obj) new_object.add_object(obj) obj.prev_position = obj.position obj.position = new_pos obj.prev_areas = obj.areas obj.areas = self.area_layer[new_pos] def teleport_object(self, obj, new_pos): old_pos = obj.position old_object = self.object_layer[old_pos] new_object = self.object_layer[new_pos] old_object.remove_object(obj) new_object.add_object(obj) obj.prev_position = old_pos obj.position = new_pos obj.prev_areas = obj.areas obj.areas = self.area_layer[new_pos] def party_action(self): old_pos = self.party_avatar.position desired = old_pos.step(self.party_avatar.facing) # Activate object that the party is looking at if self.terrain_layer.valid(desired): obj_in_front = self.object_layer[desired].obstacle if obj_in_front is not None: obj_in_front.activate(self.party_avatar, self.party_avatar.facing) across_pos = desired.step(self.party_avatar.facing) if (self.terrain_layer.valid(across_pos) and ((obj_in_front is not None and obj_in_front.is_counter()) or any([layer[desired].is_counter() for layer in\ self.scenario_layer]))): # Counter attribute obj_across = self.object_layer[across_pos].obstacle if obj_across is not None: obj_across.activate(self.party_avatar, self.party_avatar.facing) # Activate objects that the party is standing on or under old_object = self.object_layer[old_pos] for obj in old_object.below: obj.activate(self.party_avatar, self.party_avatar.facing) for obj in old_object.above: obj.activate(self.party_avatar, self.party_avatar.facing) def schedule_message(self, message): """ Add a Dialog to the message queue, displaying it as soon as the messages that were previously there are done. """ self.controller.message_queue.push(message) def pause(self, length): """ Stop movement and acting in the map for *length* frames. """ self.pause_delay = length def __repr__(self): return '(Map width=%s height=%s file=%s)' % (str(self.width), str(self.height), self.map_file) def __str__(self): result = '' result += '+' + '-' * self.width + '+\n' for y in range(self.height): result += '|' for x in range(self.width): if self.party_avatar is not None and\ self.party_avatar.position == Position(x, y): result += 'P' else: result += ' ' result += '|\n' result += '+' + '-' * self.width + '+\n' return result def sync_movement(self, objects): """ Stop movement and acting in the map, except for the movement already scheduled or in progress in the objects specified. *objects* should be a list of those MapObjects. """ self.controller.sync_movement(objects) def save_world(self, filename): """ Save the game to the given file. """ self.world.state.save_local(self.id, self.save_state()) party_local_state = (self.id, self.party_avatar.position, self.party_avatar.facing) self.world.state.save_local(PARTY_POSITION_LOCAL_STATE, party_local_state) self.world.save(filename) def add_context(self, context): """ Add a context to be run over this map and the message queue context. """ self.contexts.append(context) def set_music(self, music_file): """ Set the background for the map. """ self.music = music_file def gameover(self): """ End the game. """ self.custom_gameover() self.controller.gameover() self.world.custom_gameover() def custom_gameover(self): """ *Virtual.* Overload to perform any reaction necessary to a MapModel.gameover() call. """ pass def create_controller(self): return MapController(self, self.controller_parent) def set_states(self, local_state, global_state): self.local_state = local_state self.global_state = global_state