import random as _random import copy as _copy import io as _io from PIL import Image as _Image from PIL import ImageDraw as _ImageDraw from utils import logs as _logs from bot_libs import syms as _bot_syms from entities import resource as _resource class BrSimMap(): def __init__(self, players= None, items= None): self.players= players or [] self.items= items or [] self.world_width= 10 #seems a reasonable width for smartphones larger maps would go on a new line self.world_height= 10 self.game_map= [] self.field_sym= _bot_syms.MAP_UTF8_FIELD self.player_male_sym= _bot_syms.MAP_UTF8_PLAYER_MALE self.player_female_sym= _bot_syms.MAP_UTF8_PLAYER_FEMALE self.player_nonbinary_sym= _bot_syms.MAP_UTF8_PLAYER_NONBINARY self.dead_player_sym= _bot_syms.MAP_UTF8_DEATH_PLAYER self.item_sym= _bot_syms.MAP_UTF8_ITEM self.mountain_sym = _bot_syms.MAP_UTF8_MOUNTAIN self.init_map_matrix() self.init_players_coordinates() self.init_items_coordinates() self.populate_map() def init_map_matrix(self): # show a matrix representing the game's map # 🟩 is and empty cell # (tomorrow we can choose different colors for different locations # 🟠 this is a player (we could use different colors for different genders) # 📦 this is an item (weapon or another item) # 💀 this is icon when the player is dead # ⛰️ this is icon for the mountain (We can prevent players from passing through the mountains and thus use them for map boundaries.) self.game_map= [] width= [] #mon = [] #for i in range(self.world_width): #mon.append(self.mountain_sym) for i in range(self.world_width): #if i == 0 or i == self.world_width - 1: width.append(self.mountain_sym) #else: width.append(self.field_sym) width.append(None) #width.append(self.field_sym) for i in range(self.world_height): #if i == 0 or i == self.world_height - 1: self.game_map.append(mon) #else: self.game_map.append(_copy.deepcopy(width)) self.game_map.append(_copy.deepcopy(width)) _logs.log_debug(f'init_map_matrix: {self.game_map}') def populate_map(self): for player in self.players: p_coord_x, p_coord_y= player.get_coordinates() self.game_map[p_coord_y][p_coord_x]= player #if not player.is_alive(): self.game_map[p_coord_y][p_coord_x]= self.dead_player_sym #elif player.player_gender_is_male(): self.game_map[p_coord_y][p_coord_x]= self.player_male_sym #elif player.player_gender_is_female(): self.game_map[p_coord_y][p_coord_x]= self.player_female_sym #else: self.game_map[p_coord_y][p_coord_x]= self.player_nonbinary_sym for item in self.items: i_coord_x, i_coord_y= item.get_coordinates() self.game_map[p_coord_y][p_coord_x]= item #self.game_map[i_coord_y][i_coord_x]= self.item_sym def _set_coordinates(self, target): x= _random.randint(1, self.world_width -2) # from 1 to width-2 because 1 cell is occupied by the mountain y= _random.randint(1, self.world_height -2) resource= self.get_map_matrix()[y][x] while resource: #while self.get_map_matrix()[y][x] != self.field_sym: _logs.log_debug('_set_coordinates: collision, regenerate coordinates') x= _random.randint(1, self.world_width -2) y= _random.randint(1, self.world_height -2) target.set_coordinates(x, y) def init_players_coordinates(self): for player in self.players: self._set_coordinates(player) def init_items_coordinates(self): for item in self.items: self._set_coordinates(item) def add_player_to_map(self, player): self.players.append(player) self._set_coordinates(player) def add_item_to_map(self, item): self.items.append(item) self._set_coordinates(item) def get_map_matrix(self): return self.game_map def get_player_available_directions(self, Player): coord_x, coord_y= Player.get_player_coordinates() avail_directions= [] if self.get_map_matrix()[coord_x - 1][coord_y] not in [self.mountain_sym]: avail_directions.append((-1, 0, 'left')) if self.get_map_matrix()[coord_x + 1][coord_y] not in [self.mountain_sym]: avail_directions.append((1, 0, 'right')) if self.get_map_matrix()[coord_x][coord_y - 1] not in [self.mountain_sym]: avail_directions.append((0, -1, 'up')) if self.get_map_matrix()[coord_x][coord_y + 1] not in [self.mountain_sym]: avail_directions.append((0, 1, 'bottom')) return avail_directions def get_renderized_map(self): res= '' self.populate_map() game_map= self.get_map_matrix() for y in game_map: for x in y: if not x: el= self.field_sym #XXX how to manage mountains? elif x.is_player(): if x.player_gender_is_male(): el= self.player_male_sym elif x.player_gender_is_female(): el= self.player_female_sym else: el= self.player_nonbinary_sym elif x.is_item(): el= self.item_sym res+= el res+= '\n' return res def get_image_map(self): self.populate_map() scale_x= 20 scale_y= 20 final_x= self.world_width * scale_x final_y= self.world_height * scale_y image = _Image.new('RGB', (final_x, final_y)) draw = _ImageDraw.Draw(image) outline= '#000000' for y in range(self.world_height): for x in range(self.world_width): resource= self.game_map[y][x] if not resource: pixel_color= _bot_syms.MAP_IMAGE_FIELD # XXX how to manage mountains? maybe another class? #elif resource == self.mountain_sym: #pixel_color= _bot_syms.MAP_IMAGE_MOUNTAIN elif resource.is_item(): pixel_color= _bot_syms.MAP_IMAGE_ITEM elif resource.is_player(): if not resource.is_alive(): pixel_color= _bot_syms.MAP_IMAGE_DEATH_PLAYER elif resource.player_gender_is_male(): pixel_color= _bot_syms.MAP_IMAGE_PLAYER_MALE elif resource.player_gender_is_female(): pixel_color= _bot_syms.MAP_IMAGE_PLAYER_FEMALE elif resource.player_gender_is_not_binary(): pixel_color= _bot_syms.MAP_IMAGE_PLAYER_NONBINARY scaled_x_coord= x * scale_x scaled_y_coord= y * scale_y # if x == 1 distance from top-right is 20 (because everything is 20x bigger) # then we want to draw a rectanghe 20x20 (instead of 1x1) # this mean that if x == 1 (20px from top-right), x+1 == 2 (40px from top-right) # the same for y # this means that i keep the same factor proportions but 20x bigger scaled_x_width= (x + 1) * scale_x scaled_y_height= (y + 1) * scale_y draw.rectangle([scaled_x_coord, scaled_y_coord, scaled_x_width, scaled_y_height], fill= pixel_color, outline= outline ) # debug #image.save('/tmp/battle_royale_map.png') #image.show() bio = _io.BytesIO() image.save(bio, 'PNG') bio.seek(0) return bio