diff --git a/assets/stair_down.png b/assets/stair_down.png new file mode 100644 index 0000000..8ac654b Binary files /dev/null and b/assets/stair_down.png differ diff --git a/assets/stair_up.png b/assets/stair_up.png new file mode 100644 index 0000000..4d2813d Binary files /dev/null and b/assets/stair_up.png differ diff --git a/src/game.py b/src/game.py index d84d804..8e1caa4 100644 --- a/src/game.py +++ b/src/game.py @@ -27,7 +27,7 @@ for filename in os.listdir(os.path.join(os.path.dirname(os.path.realpath(sys.arg # Map map = Map(METER) # map.load("map.json") -map.generate() +map.generate(0) # Player player = Player() @@ -35,8 +35,7 @@ player.sprite.texture = spritesheet.SpriteSheet(assets.get("character.png"), 32, player.sprite.set_rect((8, 32, 16, 16)) # TODO: Use asset loader for spritesheets player.sprite.texture.set_animation(0, 0, 0) -player.set_pos(1.1, 1.1) -player.set_pos(map.generator.rooms[0].x + 2, map.generator.rooms[0].y + 2) +player.set_pos(map.generators[0].rooms[0].x + 2, map.generators[0].rooms[0].y + 2) CENTER = [winsize[0] / 2, winsize[1] / 2] BGCOLOR = pygame.Color("#3e1202") @@ -67,43 +66,39 @@ while 1: player_rendered = False - for z in range(len(map.map)): - for y in range(len(map.map[z])): - for x in range(len(map.map[z][y])): - tile = map.get_tile(x, y, z) - if tile: - # NOTE: Rotations are clockwise due to Y+ down rendering - # Will this break if there is no texture for the given rotation? Yes. - # Do I care? No. - texture = assets.get(tile.textures[tile.get_rotation()]) + for z in range(player.z - 1, player.z + 1): + if z < len(map.map): + for y in range(len(map.map[z])): + for x in range(len(map.map[z][y])): + tile = map.get_tile(x, y, z) + if tile: + # NOTE: Rotations are clockwise due to Y+ down rendering + # Will this break if there is no texture for the given rotation? Yes. + # Do I care? No. + texture = assets.get(tile.textures[tile.get_rotation()]) - tilesize = texture.get_rect().size - scaledsize = [tilesize[0] * SCALE, tilesize[1] * SCALE] + tilesize = texture.get_rect().size + scaledsize = [tilesize[0] * SCALE, tilesize[1] * SCALE] - tilex = (x * METER) - ((tilesize[0] / 2) - (METER / 2)) - tiley = (y * METER) - (tilesize[1] - METER) + tilex = (x * METER) - ((tilesize[0] / 2) - (METER / 2)) + tiley = (y * METER) - (tilesize[1] - METER) - pos = [ - camera[0] + round((tilex - (player.pos.x * METER)) * SCALE), - camera[1] + round((tiley - (player.pos.y * METER)) * SCALE) - ] + pos = [ + camera[0] + round((tilex - (player.pos.x * METER)) * SCALE), + camera[1] + round((tiley - (player.pos.y * METER)) * SCALE) + ] - # Only render tile if on-screen - if pos[0] + scaledsize[0] >= 0 and pos[0] <= winsize[0] and \ - pos[1] + scaledsize[1] >= 0 and pos[1] <= winsize[1]: - tile.on_step(dtime, map, player) - screen.blit(pygame.transform.scale(texture, [round(scaledsize[0]) + 1, round(scaledsize[1]) + 1]), pos) + # Only render tile if on-screen + if pos[0] + scaledsize[0] >= 0 and pos[0] <= winsize[0] and \ + pos[1] + scaledsize[1] >= 0 and pos[1] <= winsize[1]: + tile.on_step(dtime, map, player) + screen.blit(pygame.transform.scale(texture, [round(scaledsize[0]) + 1, round(scaledsize[1]) + 1]), pos) - # DEBUG - # text = arial.render(str(int(x)) + ", " + str(int(y)), False, (255, 255, 255)) - # screen.blit(text, [x * 64 - (player.pos.x * round(SCALE * METER)) + camera[0], y * 64 - (player.pos.y * round(SCALE * METER)) + camera[1]]) - - if not player_rendered and z == 1 and y == math.ceil(player.pos.y + 1 + player.sprite.get_rect()[3] / METER): - # Draw player - screen.blit(pygame.transform.scale(player.sprite.texture.frame, [round(SCALE * player.sprite.texture.width), round(SCALE * player.sprite.texture.height)]), camera) - player_rendered = True + if not player_rendered and z == player.z and y == math.ceil(player.pos.y + 1 + player.sprite.get_rect()[3] / METER): + # Draw player + screen.blit(pygame.transform.scale(player.sprite.texture.frame, [round(SCALE * player.sprite.texture.width), round(SCALE * player.sprite.texture.height)]), camera) + player_rendered = True player.hud.render(screen, SCALE) pygame.display.update() - # pygame.display.flip() diff --git a/src/level.py b/src/level.py new file mode 100644 index 0000000..583ac4c --- /dev/null +++ b/src/level.py @@ -0,0 +1,36 @@ +from .rand import rand +import math + +def populate(map, generator, z): + # Place stairways + if z != 0: + mroom = generator.rooms[int(math.ceil(len(generator.rooms) / 2))] # Middle room + enterx = mroom.x + rand(2, mroom.width - 2) + entery = mroom.y + rand(2, mroom.height - 3) + map.set_tile(enterx, entery, z + 1, "map:stair_up") + generator.enterance = (enterx, entery) + + lroom = generator.rooms[0]#len(generator.rooms) - 1] # Last room + exitx = lroom.x + rand(2, lroom.width - 2) + exity = lroom.y + rand(2, lroom.height - 3) + map.set_tile(exitx, exity, z + 1, "map:stair_down") + generator.exit = (exitx, exity) + + # Place loot + loot = [ + ["loot:coins", 10], + ["loot:pile", 50], + ] + + for room in generator.rooms: + if rand(0, 2) != 0: # 1 in 3 chance of no loot + for y in range(1, room.height - 2): + for x in range(1, room.width - 1): + if map.get_tile(x, y, z) == None: + placed = False + for l in loot: + if not placed and rand(1, l[1]) == 1: # 1 in n chance of placing + map.set_tile(room.x + x, room.y + y, z + 1, l[0]) + placed = True + + diff --git a/src/loot.py b/src/loot.py deleted file mode 100644 index dc1480b..0000000 --- a/src/loot.py +++ /dev/null @@ -1,18 +0,0 @@ -from .rand import rand - -def place_loot(map, z): - loot = [ - ["loot:coins", 10], - ["loot:pile", 50], - ] - - for room in map.generator.rooms: - if rand(0, 2) != 0: # 1 in 3 chance of no loot - for y in range(1, room.height - 2): - for x in range(1, room.width - 1): - placed = False - for l in loot: - if not placed and rand(1, l[1]) == 1: # 1 in n chance of placing - map.set_tile(room.x + x, room.y + y, 1, l[0]) - placed = True - diff --git a/src/map.py b/src/map.py index 1ddda6a..144119f 100644 --- a/src/map.py +++ b/src/map.py @@ -1,11 +1,12 @@ import pygame import json, math -from . import assets, dungeon, loot, tiles +from . import assets, dungeon, level, tiles from .tiles import Tile from .vector import Vector class Map: map = [] + generators = {} def __init__(self, meter): self.METER = meter # Pixels per 1 meter @@ -14,12 +15,14 @@ class Map: # with open(filename) as file: # self.map = json.load(file) - def generate(self): - self.generator = dungeon.Generator(80) - self.generator.generate(self, 0) - loot.place_loot(self, 0) + def generate(self, z): + generator = dungeon.Generator(80) + generator.generate(self, z) + self.generators[z] = generator - def collides(self, pos, rect): + level.populate(self, generator, z) + + def collides(self, pos, z, rect): # Player position handling really needs to be reworked ... METER = self.METER cx = pos.x + (rect[0] / METER) @@ -30,11 +33,12 @@ class Map: ch = cy + (rect[3] / METER) for y in range(py - 1, py + 1 + math.ceil(rect[3] / METER)): for x in range(px - 1, px + 2): - if y >= 0 and y < len(self.map[1]) and x >= 0 and x < len(self.map[1][y]): - tile = self.map[1][y][x] + if y >= 0 and y < len(self.map[z]) and x >= 0 and x < len(self.map[z][y]): + tile = self.map[z][y][x] if tile and tile.is_solid(): if cw >= x and cx <= (x + 1) and ch >= y and cy <= (y + 1): return True + return False def set_tile(self, x, y, z, name): diff --git a/src/player.py b/src/player.py index 878a429..78a1118 100644 --- a/src/player.py +++ b/src/player.py @@ -7,6 +7,8 @@ from .sprite import Sprite class Player: pos = Vector(0, 0) + z = 1 + last_level_change = 0 hp = 100 coins = 0 @@ -59,13 +61,13 @@ class Player: oldx = self.pos.x self.set_pos(self.pos.x + self.vel.x * self.speed * dtime, self.pos.y) - if map.collides(self.pos, self.sprite.rect): + if map.collides(self.pos, self.z, self.sprite.rect): self.set_pos(oldx, self.pos.y) oldy = self.pos.y self.set_pos(self.pos.x, self.pos.y + self.vel.y * self.speed * dtime) - if map.collides(self.pos, self.sprite.rect): + if map.collides(self.pos, self.z, self.sprite.rect): self.set_pos(self.pos.x, oldy) if controller.is_down("up") or controller.is_down("down") or \ diff --git a/src/register.py b/src/register.py index 27145c4..31575c5 100644 --- a/src/register.py +++ b/src/register.py @@ -1,7 +1,10 @@ +import math import random -from . import tiles +import time +from . import controller, tiles from .vector import Vector +# Structure tiles tiles.register_tile("map:floor", { "textures": ["floor.png"], }) @@ -33,7 +36,45 @@ tiles.register_tile("map:wall_corner_outer", { ], }) -def pick_up(self, dtime, map, player): +# Stairs +def prev_level(self, _, map, player): + # Lazy distance check + if math.hypot(player.pos.x - self.pos[0], player.pos.y + 1 - self.pos[1]) <= 2 \ + and player.pos.y + 0.5 > self.pos[1] and controller.is_down("shift") \ + and time.time() - player.last_level_change > 0.5: # Prevent multiple shifts + player.z = max(1, player.z - 2) + generator = map.generators[player.z - 1] + + player.set_pos(generator.exit[0], generator.exit[1] - 1) + player.dir = 2 + player.last_level_change = time.time() + +tiles.register_tile("map:stair_up", { + "textures": ["stair_up.png"], + "solid": True, + "on_step": prev_level, +}) + +def next_level(self, _, map, player): + if math.hypot(player.pos.x - self.pos[0], player.pos.y + 1 - self.pos[1]) <= 1 \ + and controller.is_down("shift") and time.time() - player.last_level_change > 0.5: + player.z += 2 + if not player.z - 1 in map.generators: + map.generate(player.z - 1) + generator = map.generators[player.z - 1] + + player.set_pos(generator.enterance[0], generator.enterance[1] + 0.1) + player.dir = 0 + player.last_level_change = time.time() + +tiles.register_tile("map:stair_down", { + "textures": ["stair_down.png"], + "solid": False, + "on_step": next_level, +}) + +# Loot +def pick_up(self, _, map, player): METER = map.METER rect = player.sprite.get_rect() cx = player.pos.x + (rect[0] / METER)