Skip to content

Commit

Permalink
Issue #21: Heavily cleaned up World
Browse files Browse the repository at this point in the history
  • Loading branch information
Nearoo committed Jul 16, 2015
1 parent 138ba49 commit f787b40
Showing 1 changed file with 139 additions and 82 deletions.
221 changes: 139 additions & 82 deletions World.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,11 @@


class World(EngineController):
"""
Stores all level-related data like tile-position, world-collision etc.
Before something can be done, a Tiled-levelfile must be loaded, using the "load_tmx_method.
"""

def __init__(self, engine_wrapper):
# Update the engine_wrapper:
self.engine_wrapper = engine_wrapper
"""
Stores and manages all world-related data, most important of all, the tiles.
"""
self.engine_wrapper = engine_wrapper # Save the engine-wrapper

self.grid_size = (1, 1) # Size of grid in amount of tiles
self.tile_size = (1, 1) # Size of indiv. tiles
Expand All @@ -29,66 +26,126 @@ def __init__(self, engine_wrapper):
self.tiles = [Tile("deco", [img]) for img in self.tile_images]
self.tile_by_types = {tile.material_group: [] for tile in self.tiles}

def set_tile_size(self, tile_size):
self.tile_size = tile_size
def _get_rects_with_material_group(self, layer, material_group):
"""
Returns all tiles of a layer that have a certain material group or multiple material group
:param layer: The layer on which the rects should be
:param material_group: material group or list of material-groups
:return: List of tiles with that/these material-group/s
"""
# Make list out of material_group
material_groups = [material_group] if type(material_group) is not list else material_group
# Catch possible errors:
assert layer in self.tile_grid_layers, "Layer %i doesn't exist." % layer

def set_gid_size(self, grid_size):
self.grid_size = grid_size
# Create return-list
return_list = []
for material_group in material_groups:
return_list.extend([self.tile_grid_layers[layer][i].rect for i in self.tile_by_types[layer][material_group]])

return return_list

def _get_tile_id_by_pos(self, pos):
"""
returns a tile-id based on its position
:param pos: The position of the wanted tile in pixels
:return: Tile_id
"""
return pos[0]/self.tile_size[0]+(pos[1]/self.tile_size[1])*self.grid_size[0]

def _get_tile_pos_by_id(self, tile_id):
"""
Returns the position of a tile in pixels by its id
:param tile_id: The id of the wanted tile-position
:return: The position of the tile
"""
x = (tile_id % self.grid_size[0])*self.tile_size[0]
y = (tile_id-(tile_id%self.grid_size[0]))/self.grid_size[0]*self.tile_size[1]

return x, y

def _get_layer_id(self, name):
if type(name) is int:
return name

else:
return self.layer_names.index(name)

def update(self):
pass

def get_tile_by_material_group(self, material_group):
"""
Returns the base-tile of a material-group
:param material_group: The material group desired
:return: Tile-instance
"""

for tile in self.tiles:
if tile.material_group == type:
return tile
else:
assert material_group in self.tiles, "Material-group unknown."

def get_colliding_rect(self, material_group, rect):
def get_colliding_rect(self, layer, material_group, rect):
"""
Returns a the rect of a tile with the block-groupd block_group that collides with the rect redt.
TODO: Improve this. Maybe make self.tile_grid_layers a dict that is sorted by block type, maybe add a rect to every tile,
add an additional abstraction layer for tiles, a class that contains images and one that contains xy coordinates?
Returns a tile-instance that collides with a given rect and has a certain material_group.
Returns None if no collision happens.
:param layer: The layer on which the tiles should be checked.
:param material_group: The desired material_group.
:param rect: The colliding rect.
:return: A tile instance that collides with the given rect.
"""
# First get the rects of the demanded material group
material_group_rects = self.get_material_group_rects(material_group)
# ...and let python do the work. Python is in C, so it's much faster than doing it in python...
colliding_index = rect.collidelist(material_group_rects)
return None if colliding_index == -1 else material_group_rects[colliding_index]

def get_colliding_rects(self, material_group, rect):
"""TODO: See self.get_colliding_rect()"""
# Catch possible errors:
assert layer in self.tile_by_types, "Layer does not exist."
if material_group not in self.tile_by_types[layer]: return None

# First, get a list of all rects that have the desired material-group:
rects_with_material_group = self._get_rects_with_material_group(layer, material_group)
# ...and let pygame do the work. Pygame is in C, so it's much faster than doing it in python...
colliding_index = rect.collidelist(rects_with_material_group)
# Return None if no collision happens, else the colliding rect:
return None if colliding_index == -1 else rects_with_material_group[colliding_index]

def get_colliding_rects(self, layer, material_group, rect):
"""
Returns a list of tile-instances that collide with a given rect and have a certain material-group.
Returns an empty list if no collision happens.
:param layer: The layer on which the tiles should be checked.
:param material_group: The desired material_group.
:param rect: The colliding rect.
:return: List of tile-instances that collide with the given rect
"""
# Catch possible errors:
assert layer in self.tile_by_types, "Layer does not exist."
if material_group not in self.tile_by_types[layer]: return []

# First get the rects of the demanded material group
material_group_rects = self.get_material_group_rects(material_group)
rects_with_material_group = self._get_rects_with_material_group(layer, material_group)
# ...and let python do the work. Python is in C, so it's much faster than doing it in python...
colliding_index = rect.collidelistall(material_group_rects)
return map(lambda index: material_group_rects[index], colliding_index)

def get_material_group_rects(self, material_group):
"""Returns all rects of all tiles that correspond to a certain material-group material_group.
TODO: See get_colliding_rect()"""

material_group = [material_group] if type(material_group) != list else material_group
tmp_list = []
for layer in self.tile_grid_layers.values():
for tile in layer:
if tile.material_group in material_group:
# Create a rect out of the tile and add it to the list:
tmp_list.append(tile.rect)
return tmp_list
colliding_index = rect.collidelistall(rects_with_material_group)
return map(lambda index: rects_with_material_group[index], colliding_index)

def get_tile_relative_to(self, layer, rect, offset):
"""Returns the material of the tile at position of rect offset with offset"""
layer = self.get_layer_id(layer)
# Calculate the postion of the wanted rect:
"""
Returns the tile with an offset to a given tile.
:param layer: The layer in which the tile should be
:param rect: The rect of the tile
:param offset: The offset which the tile should have relative to the given rect
:return: Tile with the offset
"""
layer = self._get_layer_id(layer)
# Calculate the position of the wanted rect:
pos_of_wanted_rect = map(lambda x, y, z: x+y*z, rect.topleft, self.tile_size, offset)
# Temporary: Calculate the maximum position a tile can have:
# Calculate the maximum position a tile can have:
max_pos = map(lambda x, y: x*y, self.tile_size, self.grid_size)
# If index of wanted tile isn't there, return the first tile (should actually be a tile with no type
# If wanted tile is outside the map (=doesn't exist), return a deco-tile:
if pos_of_wanted_rect[0] > max_pos[0] or pos_of_wanted_rect[0] < 0 or \
pos_of_wanted_rect[1] > max_pos[1] or pos_of_wanted_rect[1] < 0:
return self.get_tile_by_material_group("deco")
else:
return self.tile_grid_layers[layer][self.get_tile_id_by_pos(pos_of_wanted_rect)]

def update(self):
pass
return self.tile_grid_layers[layer][self._get_tile_id_by_pos(pos_of_wanted_rect)]

def get_tile_size(self):
"""Returns size of tiles in pixels"""
Expand All @@ -98,7 +155,7 @@ def get_grid_size(self):
"""Returns gridsize in number of tiles"""
return self.grid_size

def get_Tile(self, layer, pos_or_id):
def get_tile(self, layer, pos_or_id):
"""
Returns the tile with either position or id "pos_or_id", depending on the type of pos_or_id.
:param layer: The layer on which the tile is located
Expand All @@ -110,59 +167,59 @@ def get_Tile(self, layer, pos_or_id):
# If pos_or_id is a position:
if type(pos_or_id) is tuple:
# Convert tile_id from position to id:
tile_id = self.get_tile_id_by_pos(pos_or_id)
tile_id = self._get_tile_id_by_pos(pos_or_id)
else:
# Else just take pos_or_id
tile_id = pos_or_id
# Return the wanted tile
return self.tile_grid_layers[layer][tile_id]

def get_tile_id_by_pos(self, pos):
"""
returns a tile-id based on its position
:param pos: The position of the wanted tile in pixels
:return: Tile_id
"""
return pos[0]/self.tile_size[0]+(pos[1]/self.tile_size[1])*self.grid_size[0]

def get_tile_pos_by_id(self, tile_id):
def get_layer_amount(self):
"""
Returns the position of a tile in pixels by its id
:param tile_id: The id of the wanted tile-position
:return: The position of the tile
Returns the total amount of layers
"""
x = (tile_id % self.grid_size[0])*self.tile_size[0]
y = (tile_id-(tile_id%self.grid_size[0]))/self.grid_size[0]*self.tile_size[1]

return x, y

def get_layer_amount(self):
"""Returns the total amount of layers"""
return len(self.tile_grid_layers)

def get_layer_id(self, name):
if type(name) is int:
return name

else:
return self.layer_names.index(name)

def set_tile_property(self, tile_id, property_name, property_value):
self.tiles[tile_id].set_property(property_name, property_value)

def get_full_grid(self):
return self.tile_grid_layers

def create_tile(self, layer, position, size, tile_id):
# Create new tile - copy.copy only makes a shallow copy, so animation-instance is the same as original
new_tile = copy.copy(self.tiles[tile_id])
# Update the rect of the new tile:
new_tile.rect = pygame.Rect(position, size)
# Create the layer if he doesn't already exist:
if layer not in self.tile_grid_layers:
self.tile_grid_layers[layer] = []
# Append the new tile to this layer
self.tile_grid_layers[layer].append(new_tile)
tile_id = len(self.tile_grid_layers[layer])
if new_tile.material_group not in self.tile_by_types:
self.tile_by_types[new_tile.material_group] = [tile_id]
else:
self.tile_by_types[new_tile.material_group].append(tile_id)

def get_grid(self):
return self.tile_grid_layers
# Update self.tile_by_types:
# Get the id of the current tile (with which the tile can be accessed in self.tile_grid_layers:
tile_id = len(self.tile_grid_layers[layer])-1
# If layer doesn't exist, create it:
if layer not in self.tile_by_types:
self.tile_by_types[layer] = {}
# If material_group doesn't exist, create it:
if new_tile.material_group not in self.tile_by_types[layer]:
self.tile_by_types[layer][new_tile.material_group] = []
# Append tile:
self.tile_by_types[layer][new_tile.material_group].append(tile_id)

def set_tile_size(self, tile_size):
"""
Set the size of every tile in the grid.
:param tile_size: The size the grid-tiles should have
:return: None
"""
self.tile_size = tile_size

def set_gid_size(self, grid_size):
"""
:param grid_size: Size of grid in tiles
:return: None
"""
self.grid_size = grid_size

0 comments on commit f787b40

Please sign in to comment.