2024-07-10 20:08:18 +02:00
import pygame
import numpy as np
2024-07-12 22:04:31 +02:00
from scripts . utils import TILE_DICT , to_isometric_pixel
2024-07-10 20:08:18 +02:00
BASE_MAP_PATH = ' data/maps/ '
2024-07-12 22:04:31 +02:00
NEIGHBOR_OFFSETS = [ np . array ( ( i , j ) ) for i in ( - 1 , 0 , 1 ) for j in ( - 1 , 0 , 1 ) ]
2024-07-10 20:08:18 +02:00
class Tilemap :
2024-07-17 00:08:42 +02:00
def __init__ ( self , game , tile_size , map_path = None ) :
2024-07-10 20:08:18 +02:00
self . game = game
self . tile_size = np . array ( tile_size )
2024-07-17 18:01:42 +02:00
#self.offgrid_tiles = []
2024-07-17 00:08:42 +02:00
if map_path :
self . load_map ( map_path )
2024-07-17 12:24:04 +02:00
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
2024-07-17 18:01:42 +02:00
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 ' )
2024-07-17 12:24:04 +02:00
2024-07-10 20:08:18 +02:00
def load_map ( self , map_path ) :
2024-07-17 12:24:04 +02:00
#print(map_path)
2024-07-10 20:08:18 +02:00
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 ] ]
2024-07-12 22:04:31 +02:00
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 ] ) ) :
2024-07-10 20:08:18 +02:00
for x in range ( self . tilemap . shape [ 0 ] ) :
for y in range ( self . tilemap . shape [ 1 ] ) :
tile = self . tilemap [ x , y ]
2024-07-17 12:24:04 +02:00
surface . blit ( self . game . assets [ tile [ ' type ' ] ] [ tile [ ' variant ' ] ] , ( to_isometric_pixel ( np . array ( ( x , y ) ) ) * self . tile_size ) - offset )
2024-07-17 18:01:42 +02:00
#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
2024-07-12 22:04:31 +02:00
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