Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 71 additions & 79 deletions apps/demo/demo-stitched.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
Very basic! No animations.

"""

from __future__ import annotations

from pathlib import Path
Expand All @@ -24,7 +25,11 @@
VIDEORESIZE,
K_r,
)
from pytmx.util_pygame import load_pygame
from pygame.math import Vector2
from pygame.rect import Rect
from pygame.sprite import Sprite
from pygame.surface import Surface
from pytmx.util_pygame import load_pygame # type: ignore

import pyscroll
from pyscroll.data import MapAggregator, TiledMapData
Expand All @@ -33,27 +38,36 @@
# define configuration variables here
CURRENT_DIR = Path(__file__).parent
RESOURCES_DIR = CURRENT_DIR
WINDOW_SIZE = (800, 600)
HERO_MOVE_SPEED = 200 # pixels per second
ZOOM_STEP = 0.25
INITIAL_ZOOM = 2.0


def init_screen(width: int, height: int) -> pygame.Surface:
screen = pygame.display.set_mode((width, height), pygame.RESIZABLE)
return screen
def init_screen(width: int, height: int) -> Surface:
return pygame.display.set_mode((width, height), pygame.RESIZABLE)


def load_image(filename: str) -> pygame.Surface:
return pygame.image.load(str(RESOURCES_DIR / filename))
def load_image(filename: str) -> Surface:
path = RESOURCES_DIR / filename
if not path.exists():
raise FileNotFoundError(f"Image not found: {path}")
return pygame.image.load(str(path)).convert_alpha()


class Hero(pygame.sprite.Sprite):
class Hero(Sprite):
def __init__(self) -> None:
super().__init__()
self.image = load_image("hero.png").convert_alpha()
self.velocity = [0, 0]
self._position = [0.0, 0.0]
self._old_position = self.position
self.rect = self.image.get_rect()
self.feet = pygame.Rect(0, 0, self.rect.width * 0.5, 8)
self.image = load_image("hero.png")
self.velocity = Vector2(0, 0)
self.position = Vector2(400.0, 400.0)
self.old_position = self.position.copy()
self.rect = self.image.get_rect(topleft=self.position)
self.feet = Rect(0, 0, self.rect.width * 0.5, 8)
self.update_feet()

def update_feet(self) -> None:
self.feet.midbottom = self.rect.midbottom

@property
def position(self) -> list[float]:
Expand All @@ -64,27 +78,25 @@ def position(self, value: list[float]) -> None:
self._position = list(value)

def update(self, dt: float) -> None:
self._old_position = self._position[:]
self._position[0] += self.velocity[0] * dt
self._position[1] += self.velocity[1] * dt
self.rect.topleft = self._position
self.feet.midbottom = self.rect.midbottom
self.old_position = self.position.copy()
self.position += self.velocity * dt
self.rect.topleft = self.position
self.update_feet()

def move_back(self, dt: float) -> None:
self._position = self._old_position
self.rect.topleft = self._position
self.feet.midbottom = self.rect.midbottom
self.position = self.old_position.copy()
self.rect.topleft = self.position
self.update_feet()


class QuestGame:
map_path = RESOURCES_DIR / "grasslands.tmx"

def __init__(self, screen: pygame.Surface) -> None:
def __init__(self, screen: Surface) -> None:
self.screen = screen
self.running = False

# Load and stitch maps
world_data = MapAggregator((16, 16))
for filename, offset in [
stitched_maps = [
("stitched0.tmx", (-20, -20)),
("stitched1.tmx", (0, -20)),
("stitched2.tmx", (20, -20)),
Expand All @@ -94,25 +106,26 @@ def __init__(self, screen: pygame.Surface) -> None:
("stitched6.tmx", (-20, 20)),
("stitched7.tmx", (0, 20)),
("stitched8.tmx", (20, 20)),
]:
tmx_data = load_pygame(RESOURCES_DIR / filename)
pyscroll_data = TiledMapData(tmx_data)
world_data.add_map(pyscroll_data, offset)
]

self.map_layer = pyscroll.BufferedRenderer(
for filename, offset in stitched_maps:
path = RESOURCES_DIR / filename
if not path.exists():
raise FileNotFoundError(f"TMX map not found: {path}")
tmx_data = load_pygame(str(path))
world_data.add_map(TiledMapData(tmx_data), offset)

self.map_layer = pyscroll.orthographic.BufferedRenderer(
data=world_data,
size=screen.get_size(),
clamp_camera=True,
)
self.map_layer.zoom = 2
self.map_layer.zoom = INITIAL_ZOOM
self.group = PyscrollGroup(map_layer=self.map_layer, default_layer=0)

# put the hero in the center of the map
# Hero setup
self.hero = Hero()
self.hero.layer = 0
self.hero.position = (400, 400)

# add our hero to the group
self.group.add(self.hero)

def draw(self) -> None:
Expand All @@ -122,84 +135,63 @@ def draw(self) -> None:
def handle_input(self) -> None:
"""
Handle pygame input events

"""
for event in pygame.event.get():
if event.type == QUIT:
if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
self.running = False
break

elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
self.running = False
break

elif event.key == K_r:
if event.key == K_r:
self.map_layer.reload()

elif event.key == K_EQUALS:
self.map_layer.zoom += 0.25

self.map_layer.zoom += ZOOM_STEP
elif event.key == K_MINUS:
value = self.map_layer.zoom - 0.25
if value > 0:
self.map_layer.zoom = value

# this will be handled if the window is resized
new_zoom = self.map_layer.zoom - ZOOM_STEP
if new_zoom > 0:
self.map_layer.zoom = new_zoom
elif event.type == VIDEORESIZE:
self.screen = init_screen(event.w, event.h)
self.map_layer.set_size((event.w, event.h))

# use `get_pressed` for an easy way to detect held keys
pressed = pygame.key.get_pressed()
self.hero.velocity.x = 0
self.hero.velocity.y = 0

if pressed[K_UP]:
self.hero.velocity[1] = -HERO_MOVE_SPEED
self.hero.velocity.y = -HERO_MOVE_SPEED
elif pressed[K_DOWN]:
self.hero.velocity[1] = HERO_MOVE_SPEED
else:
self.hero.velocity[1] = 0
self.hero.velocity.y = HERO_MOVE_SPEED

if pressed[K_LEFT]:
self.hero.velocity[0] = -HERO_MOVE_SPEED
self.hero.velocity.x = -HERO_MOVE_SPEED
elif pressed[K_RIGHT]:
self.hero.velocity[0] = HERO_MOVE_SPEED
else:
self.hero.velocity[0] = 0
self.hero.velocity.x = HERO_MOVE_SPEED

def update(self, dt: float) -> None:
"""
Tasks that occur over time should be handled here

"""
self.group.update(dt)

def run(self) -> None:
clock = pygame.time.Clock()
self.running = True

try:
while self.running:
dt = clock.tick() / 1000.0
self.handle_input()
self.update(dt)
self.draw()
pygame.display.flip()

except KeyboardInterrupt:
self.running = False
while self.running:
dt = clock.tick(60) / 1000.0
self.handle_input()
self.update(dt)
self.draw()
pygame.display.flip()


def main() -> None:
pygame.init()
pygame.font.init()
screen = init_screen(800, 600)
pygame.display.set_caption("Quest - An epic journey.")
screen = init_screen(*WINDOW_SIZE)
pygame.display.set_caption("Quest - An Epic Journey")

try:
game = QuestGame(screen)
game.run()
except KeyboardInterrupt:
pass
except Exception as e:
print(f"Error: {e}")
finally:
pygame.quit()

Expand Down
Loading