Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

initial pyscroll rendering #280

Merged
merged 5 commits into from Apr 10, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions requirements.txt
@@ -1,4 +1,5 @@
pytmx>=3.20.14
pyscroll>=2.16.13
pygame
six
neteria
174 changes: 68 additions & 106 deletions tuxemon/core/components/map.py
Expand Up @@ -23,17 +23,16 @@
# Contributor(s):
#
# William Edwards <shadowapex@gmail.com>
# Leif Theden <leif.theden@gmail.com>
#
#
# core.components.map Game map module.
#
#

import logging
import re
from collections import namedtuple

from core.components.pyganim import PygAnimation
from core import prepare, tools
from core.components.event import Action
from core.components.event import Condition
from core.components.event import EventObject
Expand All @@ -44,6 +43,8 @@
except ImportError:
from pytmx.util_pygame import load_pygame

import pyscroll

# Create a logger for optional handling of debug messages.
logger = logging.getLogger(__name__)
logger.debug("%s successfully imported" % __name__)
Expand All @@ -57,9 +58,12 @@ class Map(object):
**Tiled:** http://www.mapeditor.org/

"""

def __init__(self, filename):
self.filename = None
self.data = None
self.size = None
self.renderer = None

# Get the tile size from a tileset in our map. This is used to calculate the number of tiles
# in a collision region.
Expand All @@ -83,7 +87,6 @@ def __init__(self, filename):
# Initialize the map
self.load(filename)


def load(self, filename):
"""Load map data from a tmx map file and get all the map's events and collision areas.
Loading the map data is done using the pytmx library.
Expand Down Expand Up @@ -153,17 +156,22 @@ def load(self, filename):
'y': 0}

"""

# Load the tmx map data using the pytmx library.
self.filename = filename
#self.data = pytmx.TiledMap(filename)
self.data = load_pygame(filename, pixelalpha=True)

# Scale the loaded tiles if enabled
if prepare.CONFIG.scaling == "1":
self.scale_tiles(prepare.TILE_SIZE)

# make a scrolling rendererer
self.initialize_renderer()

# Get the map dimensions
self.size = (self.data.width, self.data.height)
self.size = self.data.width, self.data.height

# Get the tile size of the map
self.tile_size = (self.data.tilesets[0].tilewidth, self.data.tilesets[0].tileheight)
self.tile_size = self.data.tilesets[0].tilewidth, self.data.tilesets[0].tileheight

# Load all objects from the map file and sort them by their type.
for obj in self.data.objects:
Expand All @@ -182,6 +190,33 @@ def load(self, filename):
elif obj.type == 'interact':
self.interacts.append(self.loadevent(obj))

def initialize_renderer(self):
""" Tuxemon uses the pyscroll library to draw the game map

Here it is initialized. This must be done again if tile size changes.

:return:
"""
visual_data = pyscroll.data.TiledMapData(self.data)
self.renderer = pyscroll.BufferedRenderer(visual_data, prepare.SCREEN_SIZE, clamp_camera=False)
visual_data.convert_surfaces(self.renderer._buffer, True)

def scale_tiles(self, tile_size):
""" Scale the tiles of the map data.

If you call this, you will have to reinitialize the renderer

:param tile_size:
:return: None
"""
def scale_tile(image):
if image:
return tools.scale_tile(image, tile_size)
return None

self.data.images = [scale_tile(i) for i in self.data.images]
self.data.tilewidth, self.data.tileheight = tile_size

def loadevent(self, obj):
conds = []
acts = []
Expand Down Expand Up @@ -334,81 +369,6 @@ def loadfile(self, tile_size):
(0, 6)])

"""

# Create a list of all of the tiles in the map
tiles = []

# Loop through all tiles in our map file and get the pygame surface associated with it.
for x in range(0, int(self.data.width)):

# Create a list of tile for the y-axis
y_list = []

for y in range(0, int(self.data.height)):

layer_list = []

# Get the number of tile layers.
num_of_layers = 0

# PyTMX recently changed some of their attribute names.
# This ensures we get the number of layers regardless of
# the version of PyTMX.
try:
for layer in self.data.layers:
if hasattr(layer, 'data'):
num_of_layers += 1
except AttributeError:
for layer in self.data.tilelayers:
num_of_layers += 1

# Get all the map tiles for each layer
for layer in range(0, num_of_layers):

# PyTMX recently changed their method names. This
# ensures the map will load regardless of the PyTMX
# version.
try:
surface = self.data.getTileImage(x, y, layer)
except AttributeError:
surface = self.data.get_tile_image(x, y, layer)

# Check to see if this tile has an animation
tile_properties = self.data.get_tile_properties(x, y, layer)
if tile_properties and "frames" in tile_properties:
images_and_durations = []
for frame in tile_properties["frames"]:
# bitcraft/PyTMX 3.20.14+ support
if hasattr(frame, 'gid'):
anim_surface = self.data.get_tile_image_by_gid(frame.gid)
images_and_durations.append((anim_surface, float(frame.duration) / 1000))
# ShadowApex/PyTMX 3.20.13 fork support
elif "gid" in frame:
anim_surface = self.data.get_tile_image_by_gid(frame["gid"])
images_and_durations.append((anim_surface, float(frame["duration"]) / 1000))
# bitcraft/PyTMX 3.20.13 support
elif "gid" not in frame:
images_and_durations = [(surface, 1)]
break
if images_and_durations:
surface = PygAnimation(images_and_durations)
surface.play()

# Create a tile based on the image
if surface:
tile = {'tile_pos': (x, y),
'position': (x * tile_size[0], y * tile_size[1]),
'layer': layer + 1,
'name': str(x) + "," + str(y),
'surface': surface
}

layer_list.append(tile)

y_list.append(layer_list)

tiles.append(y_list)

# Get the dimensions of the map
mapsize = self.size

Expand All @@ -429,7 +389,7 @@ def loadfile(self, tile_size):
for collision_region in self.collisions:

# >>> collision_region.__dict__
#{'gid': 0,
# {'gid': 0,
# 'height': 16,
# 'name': None,
# 'parent': <TiledMap: "resources/maps/pallet_town-room.tmx">,
Expand Down Expand Up @@ -526,21 +486,21 @@ def loadfile(self, tile_size):

if line_type is 'vertical':
# get all tile coordinates on either side
x = point1[0] / self.tile_size[0] # same as point2[0] b/c vertical
x = point1[0] / self.tile_size[0] # same as point2[0] b/c vertical
line_start = point1[1]
line_end = point2[1]
num_tiles_in_line = abs(line_start - line_end) / self.tile_size[1] # [1] b/c vertical
num_tiles_in_line = abs(line_start - line_end) / self.tile_size[1] # [1] b/c vertical
curr_y = line_start / self.tile_size[1]
for i in range(int(num_tiles_in_line)):
if line_start > line_end: # slightly different
# behavior depending on
# direction
left_side_tile = (x-1,curr_y-1)
right_side_tile = (x,curr_y-1)
if line_start > line_end: # slightly different
# behavior depending on
# direction
left_side_tile = (x - 1, curr_y - 1)
right_side_tile = (x, curr_y - 1)
curr_y -= 1
else:
left_side_tile = (x-1,curr_y)
right_side_tile = (x,curr_y)
left_side_tile = (x - 1, curr_y)
right_side_tile = (x, curr_y)
curr_y += 1

# TODO - if we want to enable single-direction
Expand All @@ -553,20 +513,20 @@ def loadfile(self, tile_size):

elif line_type is 'horizontal':
# get all tile coordinates on either side
y = point1[1] / self.tile_size[1] # same as point2[1] b/c horizontal
y = point1[1] / self.tile_size[1] # same as point2[1] b/c horizontal
line_start = point1[0]
line_end = point2[0]
num_tiles_in_line = abs(line_start - line_end) / self.tile_size[0] # [0] b/c horizontal
num_tiles_in_line = abs(line_start - line_end) / self.tile_size[0] # [0] b/c horizontal
curr_x = line_start / self.tile_size[0]
for i in range(int(num_tiles_in_line)):
if line_start > line_end: # slightly different
# behavior depending on
# direction
top_side_tile = (curr_x-1,y-1)
bottom_side_tile = (curr_x-1,y)
if line_start > line_end: # slightly different
# behavior depending on
# direction
top_side_tile = (curr_x - 1, y - 1)
bottom_side_tile = (curr_x - 1, y)
curr_x -= 1
else:
top_side_tile = (curr_x, y-1)
top_side_tile = (curr_x, y - 1)
bottom_side_tile = (curr_x, y)
curr_x += 1

Expand All @@ -578,9 +538,10 @@ def loadfile(self, tile_size):
collision_lines_map.add((top_side_tile, "down"))
collision_lines_map.add((bottom_side_tile, "up"))

return tiles, collision_map, collision_lines_map, mapsize
return collision_map, collision_lines_map, mapsize

def round_to_divisible(self, x, base=16):
@staticmethod
def round_to_divisible(x, base=16):
"""Rounds a number to a divisible base. This is used to round collision areas that aren't
defined well. This function assists in making sure collisions work if the map creator
didn't set the collision areas to round numbers.
Expand All @@ -602,9 +563,10 @@ def round_to_divisible(self, x, base=16):
16

"""
return int(base * round(float(x)/base))
return int(base * round(float(x) / base))

def split_escaped(self, string_to_split, delim=","):
@staticmethod
def split_escaped(string_to_split, delim=","):
"""Splits a string by the specified deliminator excluding escaped
deliminators.

Expand Down Expand Up @@ -632,10 +594,10 @@ class Tile(object):
as the layer it's on, its position, surface, and other properties.

"""

def __init__(self, name, surface, tileset):
self.name = name
self.surface = surface
self.layer = None
self.type = None
self.tileset = tileset