py_yugi_clone/scripts/tiles.py
2024-07-17 18:01:42 +02:00

70 lines
3.7 KiB
Python

import pygame
import numpy as np
from scripts.utils import TILE_DICT, to_isometric_pixel
BASE_MAP_PATH = 'data/maps/'
NEIGHBOR_OFFSETS = [np.array((i,j)) for i in (-1,0,1) for j in (-1,0,1)]
class Tilemap:
def __init__(self, game, tile_size, map_path = None):
self.game = game
self.tile_size = np.array(tile_size)
#self.offgrid_tiles = []
if map_path:
self.load_map(map_path)
def extend_tilemap(self, pos):
#extend tilemap such that pos becomes an accessible tile
#by default adds the air block TILE_DICT[0]
if self.tilemap.shape[0] <= pos[0]:
self.tilemap = np.concatenate([self.tilemap, np.full(np.array((pos[0] - self.tilemap.shape[0] + 1, self.tilemap.shape[1])), {'type' : TILE_DICT[0][0], 'variant' : TILE_DICT[0][1], 'walkable': TILE_DICT[0][2]})], axis = 0)
if self.tilemap.shape[1] <= pos[1]:
self.tilemap = np.concatenate([self.tilemap, np.full(np.array((self.tilemap.shape[0], pos[1] - self.tilemap.shape[1] + 1)), {'type' : TILE_DICT[0][0], 'variant' : TILE_DICT[0][1], 'walkable': TILE_DICT[0][2]})], axis = 1)
def add_tile(self, pos, tile):
#by design if np.any(pos) < 0 it loops to the other side without errors
if np.any(self.tilemap.shape <= pos):
self.extend_tilemap(pos)
self.tilemap[*pos] = tile
def save(self, map_path):
inverse_tile_dict = {TILE_DICT[i]:i for i in range(len(TILE_DICT))}
tile_arr = np.empty_like(self.tilemap,dtype=np.int8)
for row in range(self.tilemap.shape[0]):
for col in range(self.tilemap.shape[1]):
tile = self.tilemap[row,col]
tile_arr[row,col] = inverse_tile_dict[(tile['type'], tile['variant'], tile['walkable'])]
tile_arr = np.transpose(tile_arr)
np.savetxt(BASE_MAP_PATH + map_path, tile_arr, delimiter=',', fmt='%.1d')
def load_map(self, map_path):
#print(map_path)
tile_arr = np.transpose(np.loadtxt(BASE_MAP_PATH + map_path, delimiter=',', dtype=np.int8))
self.tilemap = np.empty_like(tile_arr, dtype = object)
for row in range(tile_arr.shape[0]):
for col in range(tile_arr.shape[1]):
type_var = TILE_DICT[tile_arr[row,col]]
self.tilemap[row,col] = {'type' : type_var[0], 'variant' : type_var[1], 'pos': np.array([row,col]), 'walkable': type_var[2]}
def render(self, surface, offset = np.array([0,0])):
for x in range(self.tilemap.shape[0]):
for y in range(self.tilemap.shape[1]):
tile = self.tilemap[x,y]
surface.blit(self.game.assets[tile['type']][tile['variant']],(to_isometric_pixel(np.array((x,y)))*self.tile_size)-offset)
#for tile in self.offgrid_tiles:
# surface.blit(self.game.assets[tile['type']][tile['variant']],(to_isometric_pixel(tile['pos'])*self.tile_size)-offset) #this tile is written offgrid so no mult by tile_size
def tiles_around(self,pos):
tiles = []
tile_loc = np.array(pos // np.array((self.tile_size[0],self.tile_size[0])),dtype=int) #gotta use this and not just self.tile_size to normalize wrt the change of variables for the isometric view
for offset in NEIGHBOR_OFFSETS:
check_loc = tile_loc + offset
if np.all(check_loc >= 0) and np.all(check_loc < self.tilemap.shape):
tiles.append(self.tilemap[tuple(check_loc)])
return tiles
def physics_rects_around(self,pos):
rects = []
for tile in self.tiles_around(pos):
if not tile['walkable']:
rects.append(pygame.Rect(tile['pos'] * np.array((self.tile_size[0],self.tile_size[0])), np.array((self.tile_size[0],self.tile_size[0]))))
return rects