From 101e919517ec5e8e39668b0d9996792ffe8cfac0 Mon Sep 17 00:00:00 2001 From: FoamyGuy Date: Sun, 17 May 2020 09:45:32 -0500 Subject: [PATCH] adding Circuit Python tilemap game --- .../basic_movement.py | 90 +++ .../basic_movement_transparent_sprite.py | 91 +++ .../basic_rendering.py | 68 ++ Tilemap_Game_With_CircuitPython/code.py | 603 ++++++++++++++++++ Tilemap_Game_With_CircuitPython/readme.md | 5 + .../requirements.txt | 2 + .../tilegame_assets/__init__.py | 0 .../tilegame_assets/castle_sprite_sheet.bmp | Bin 0 -> 3262 bytes .../castle_sprite_sheet_edited.bmp | Bin 0 -> 3266 bytes .../tilegame_assets/fun_facts.py | 13 + .../game_message_background.bmp | Bin 0 -> 20694 bytes .../tilegame_assets/map0.csv | 8 + .../tilegame_assets/map1.csv | 17 + .../tilegame_assets/sprite_sheet.bmp | Bin 0 -> 4070 bytes .../tilegame_assets/states.py | 16 + .../tilegame_assets/text_helper.py | 23 + .../tilegame_assets/tiles.py | 132 ++++ 17 files changed, 1068 insertions(+) create mode 100644 Tilemap_Game_With_CircuitPython/basic_movement.py create mode 100644 Tilemap_Game_With_CircuitPython/basic_movement_transparent_sprite.py create mode 100644 Tilemap_Game_With_CircuitPython/basic_rendering.py create mode 100644 Tilemap_Game_With_CircuitPython/code.py create mode 100644 Tilemap_Game_With_CircuitPython/readme.md create mode 100644 Tilemap_Game_With_CircuitPython/requirements.txt create mode 100644 Tilemap_Game_With_CircuitPython/tilegame_assets/__init__.py create mode 100644 Tilemap_Game_With_CircuitPython/tilegame_assets/castle_sprite_sheet.bmp create mode 100644 Tilemap_Game_With_CircuitPython/tilegame_assets/castle_sprite_sheet_edited.bmp create mode 100644 Tilemap_Game_With_CircuitPython/tilegame_assets/fun_facts.py create mode 100644 Tilemap_Game_With_CircuitPython/tilegame_assets/game_message_background.bmp create mode 100644 Tilemap_Game_With_CircuitPython/tilegame_assets/map0.csv create mode 100644 Tilemap_Game_With_CircuitPython/tilegame_assets/map1.csv create mode 100644 Tilemap_Game_With_CircuitPython/tilegame_assets/sprite_sheet.bmp create mode 100644 Tilemap_Game_With_CircuitPython/tilegame_assets/states.py create mode 100644 Tilemap_Game_With_CircuitPython/tilegame_assets/text_helper.py create mode 100644 Tilemap_Game_With_CircuitPython/tilegame_assets/tiles.py diff --git a/Tilemap_Game_With_CircuitPython/basic_movement.py b/Tilemap_Game_With_CircuitPython/basic_movement.py new file mode 100644 index 000000000..8ca844273 --- /dev/null +++ b/Tilemap_Game_With_CircuitPython/basic_movement.py @@ -0,0 +1,90 @@ +import board +import displayio +import adafruit_imageload +#from tilegame_assets.controls_helper import controls +import ugame + +display = board.DISPLAY +player_loc = {"x": 4, "y": 3} + +# Load the sprite sheet (bitmap) +sprite_sheet, palette = adafruit_imageload.load("tilegame_assets/castle_sprite_sheet.bmp", + bitmap=displayio.Bitmap, + palette=displayio.Palette) + +# Create the sprite TileGrid +sprite = displayio.TileGrid(sprite_sheet, pixel_shader=palette, + width=1, + height=1, + tile_width=16, + tile_height=16, + default_tile=0) + +# Create the castle TileGrid +castle = displayio.TileGrid(sprite_sheet, pixel_shader=palette, + width=10, + height=8, + tile_width=16, + tile_height=16) + +# Create a Group to hold the sprite and add it +sprite_group = displayio.Group() +sprite_group.append(sprite) + +# Create a Group to hold the castle and add it +castle_group = displayio.Group(scale=1) +castle_group.append(castle) + +# Create a Group to hold the sprite and castle +group = displayio.Group() + +# Add the sprite and castle to the group +group.append(castle_group) +group.append(sprite_group) + +# Castle tile assignments +# corners +castle[0, 0] = 3 # upper left +castle[9, 0] = 5 # upper right +castle[0, 7] = 9 # lower left +castle[9, 7] = 11 # lower right +# top / bottom walls +for x in range(1, 9): + castle[x, 0] = 4 # top + castle[x, 7] = 10 # bottom +# left/ right walls +for y in range(1, 7): + castle[0, y] = 6 # left + castle[9, y] = 8 # right +# floor +for x in range(1, 9): + for y in range(1, 7): + castle[x, y] = 7 # floor + +# put the sprite somewhere in the castle +sprite.x = 16 * player_loc["x"] +sprite.y = 16 * player_loc["y"] + +# Add the Group to the Display +display.show(group) + +prev_btn_vals = ugame.buttons.get_pressed() + +while True: + cur_btn_vals = ugame.buttons.get_pressed() + if not prev_btn_vals & ugame.K_UP and cur_btn_vals & ugame.K_UP: + player_loc["y"] = max(1, player_loc["y"]-1) + if not prev_btn_vals & ugame.K_DOWN and cur_btn_vals & ugame.K_DOWN: + player_loc["y"] = min(6, player_loc["y"]+1) + + if not prev_btn_vals & ugame.K_RIGHT and cur_btn_vals & ugame.K_RIGHT: + player_loc["x"] = min(8, player_loc["x"]+1) + if not prev_btn_vals & ugame.K_LEFT and cur_btn_vals & ugame.K_LEFT: + player_loc["x"] = max(1, player_loc["x"]-1) + + # update the the player sprite position + sprite.x = 16 * player_loc["x"] + sprite.y = 16 * player_loc["y"] + + # update the previous values + prev_btn_vals = cur_btn_vals diff --git a/Tilemap_Game_With_CircuitPython/basic_movement_transparent_sprite.py b/Tilemap_Game_With_CircuitPython/basic_movement_transparent_sprite.py new file mode 100644 index 000000000..afedd46dd --- /dev/null +++ b/Tilemap_Game_With_CircuitPython/basic_movement_transparent_sprite.py @@ -0,0 +1,91 @@ +import board +import displayio +import adafruit_imageload +from tilegame_assets.controls_helper import controls + +display = board.DISPLAY +player_loc = {"x":4, "y":3} + +# Load the sprite sheet (bitmap) +sprite_sheet, palette = adafruit_imageload.load("tilegame_assets/castle_sprite_sheet_edited.bmp", + bitmap=displayio.Bitmap, + palette=displayio.Palette) +# make the color at 0 index transparent. +palette.make_transparent(0) + +# Create the sprite TileGrid +sprite = displayio.TileGrid(sprite_sheet, pixel_shader=palette, + width=1, + height=1, + tile_width=16, + tile_height=16, + default_tile=0) + +# Create the castle TileGrid +castle = displayio.TileGrid(sprite_sheet, pixel_shader=palette, + width=10, + height=8, + tile_width=16, + tile_height=16) + +# Create a Group to hold the sprite and add it +sprite_group = displayio.Group() +sprite_group.append(sprite) + +# Create a Group to hold the castle and add it +castle_group = displayio.Group(scale=1) +castle_group.append(castle) + +# Create a Group to hold the sprite and castle +group = displayio.Group() + +# Add the sprite and castle to the group +group.append(castle_group) +group.append(sprite_group) + +# Castle tile assignments +# corners +castle[0, 0] = 3 # upper left +castle[9, 0] = 5 # upper right +castle[0, 7] = 9 # lower left +castle[9, 7] = 11 # lower right +# top / bottom walls +for x in range(1, 9): + castle[x, 0] = 4 # top + castle[x, 7] = 10 # bottom +# left/ right walls +for y in range(1, 7): + castle[0, y] = 6 # left + castle[9, y] = 8 # right +# floor +for x in range(1, 9): + for y in range(1, 7): + castle[x, y] = 7 # floor + +# put the sprite somewhere in the castle +sprite.x = 16 * player_loc["x"] +sprite.y = 16 * player_loc["y"] + +# Add the Group to the Display +display.show(group) + +prev_btn_vals = controls.button + +while True: + cur_btn_vals = controls.button + if not prev_btn_vals.up and cur_btn_vals.up: + player_loc["y"] = max(1, player_loc["y"]-1) + if not prev_btn_vals.down and cur_btn_vals.down: + player_loc["y"] = min(6, player_loc["y"]+1) + + if not prev_btn_vals.right and cur_btn_vals.right: + player_loc["x"] = min(8, player_loc["x"]+1) + if not prev_btn_vals.left and cur_btn_vals.left: + player_loc["x"] = max(1, player_loc["x"]-1) + + # update the the player sprite position + sprite.x = 16 * player_loc["x"] + sprite.y = 16 * player_loc["y"] + + # update the previous values + prev_btn_vals = cur_btn_vals diff --git a/Tilemap_Game_With_CircuitPython/basic_rendering.py b/Tilemap_Game_With_CircuitPython/basic_rendering.py new file mode 100644 index 000000000..4a31e1bcf --- /dev/null +++ b/Tilemap_Game_With_CircuitPython/basic_rendering.py @@ -0,0 +1,68 @@ +import board +import displayio +import adafruit_imageload + +display = board.DISPLAY + +# Load the sprite sheet (bitmap) +sprite_sheet, palette = adafruit_imageload.load("tilegame_assets/castle_sprite_sheet.bmp", + bitmap=displayio.Bitmap, + palette=displayio.Palette) + +# Create the sprite TileGrid +sprite = displayio.TileGrid(sprite_sheet, pixel_shader=palette, + width=1, + height=1, + tile_width=16, + tile_height=16, + default_tile=0) + +# Create the castle TileGrid +castle = displayio.TileGrid(sprite_sheet, pixel_shader=palette, + width=10, + height=8, + tile_width=16, + tile_height=16) + +# Create a Group to hold the sprite and add it +sprite_group = displayio.Group() +sprite_group.append(sprite) + +# Create a Group to hold the castle and add it +castle_group = displayio.Group(scale=1) +castle_group.append(castle) + +# Create a Group to hold the sprite and castle +group = displayio.Group() + +# Add the sprite and castle to the group +group.append(castle_group) +group.append(sprite_group) + +# Castle tile assignments +# corners +castle[0, 0] = 3 # upper left +castle[9, 0] = 5 # upper right +castle[0, 7] = 9 # lower left +castle[9, 7] = 11 # lower right +# top / bottom walls +for x in range(1, 9): + castle[x, 0] = 4 # top + castle[x, 7] = 10 # bottom +# left/ right walls +for y in range(1, 7): + castle[0, y] = 6 # left + castle[9, y] = 8 # right +# floor +for x in range(1, 9): + for y in range(1, 7): + castle[x, y] = 7 # floor + +# put the sprite somewhere in the castle +sprite.x = 16 * 4 +sprite.y = 16 * 3 + +# Add the Group to the Display +display.show(group) +while True: + pass diff --git a/Tilemap_Game_With_CircuitPython/code.py b/Tilemap_Game_With_CircuitPython/code.py new file mode 100644 index 000000000..067893102 --- /dev/null +++ b/Tilemap_Game_With_CircuitPython/code.py @@ -0,0 +1,603 @@ +import time +import random +import gc +import board +import displayio +import adafruit_imageload +from displayio import Palette + +import terminalio +from adafruit_display_text import label +from tilegame_assets.controls_helper import controls +from tilegame_assets.tiles import TILES +from tilegame_assets.tiles import take_item +from tilegame_assets.states import * +from tilegame_assets.fun_facts import FACTS +from tilegame_assets.text_helper import wrap_nicely + +# Direction constants for comparison +UP = 0 +DOWN = 1 +RIGHT = 2 +LEFT = 3 + +# how long to wait between rendering frames +FPS_DELAY = 1 / 60 + +# how many tiles can fit on thes screen. Tiles are 16x16 pixels +SCREEN_HEIGHT_TILES = 8 +SCREEN_WIDTH_TILES = 10 + +# list of maps in order they should be played +MAPS = ["map0.csv", "map1.csv"] + +GAME_STATE = { + # hold the map state as it came out of the csv. Only holds non-entities. + "ORIGINAL_MAP": {}, + # hold the current map state as it changes. Only holds non-entities. + "CURRENT_MAP": {}, + # Dictionary with touple keys that map to lists of entity objects. + # Each one has the index of the sprite in the ENTITY_SPRITES list + # and the tile type string + "ENTITY_SPRITES_DICT": {}, + # hold the location of the player in tile coordinates + "PLAYER_LOC": (0, 0), + # list of items the player has in inventory + "INVENTORY": [], + # how many hearts there are in this map level + "TOTAL_HEARTS": 0, + # sprite object to draw for the player + "PLAYER_SPRITE": None, + # size of the map + "MAP_WIDTH": 0, + "MAP_HEIGHT": 0, + # which map level within MAPS we are currently playing + "MAP_INDEX": 0, + # current state of the state machine + "STATE": STATE_PLAYING, +} + +# dictionary with tuple keys that map to tile type values +# e.x. {(0,0): "left_wall", (1,1): "floor"} +CAMERA_VIEW = {} + +# how far offset the camera is from the GAME_STATE['CURRENT_MAP'] +# used to determine where things are at in the camera view vs. the MAP +CAMERA_OFFSET_X = 0 +CAMERA_OFFSET_Y = 0 + +# list of sprite objects, one for each entity +ENTITY_SPRITES = [] + +# list of entities that need to be on the screen currently based on the camera view +NEED_TO_DRAW_ENTITIES = [] + + +# return from GAME_STATE['CURRENT_MAP'] the tile name of the tile of the given coords +def get_tile(coords): + return GAME_STATE["CURRENT_MAP"][coords[0], coords[1]] + +# return from TILES dict the tile object with stats and behavior for the tile at the given coords. +def get_tile_obj(coords): + return TILES[GAME_STATE["CURRENT_MAP"][coords[0], coords[1]]] + +# check the can_walk property of the tile at the given coordinates +def is_tile_moveable(tile_coords): + return TILES[GAME_STATE["CURRENT_MAP"][tile_coords[0], tile_coords[1]]]["can_walk"] + +print("after funcs {}".format(gc.mem_free())) + +# display object variable +display = board.DISPLAY + +# Load the sprite sheet (bitmap) +sprite_sheet, palette = adafruit_imageload.load( + "tilegame_assets/sprite_sheet.bmp", + bitmap=displayio.Bitmap, + palette=displayio.Palette, +) + +# make green be transparent so entities can be drawn on top of map tiles +palette.make_transparent(0) + +# Create the castle TileGrid +castle = displayio.TileGrid( + sprite_sheet, + pixel_shader=palette, + width=10, + height=8, + tile_width=16, + tile_height=16, +) + +# Create a Group to hold the sprites and add it +sprite_group = displayio.Group(max_size=32) + +# Create a Group to hold the castle and add it +castle_group = displayio.Group() +castle_group.append(castle) + +# Create a Group to hold the sprite and castle +group = displayio.Group() + +# Add the sprite and castle to the group +group.append(castle_group) +group.append(sprite_group) + + +def load_map(file_name): + global ENTITY_SPRITES, CAMERA_VIEW + + # empty the sprite_group + for cur_s in ENTITY_SPRITES: + sprite_group.remove(cur_s) + # remove player sprite + try: + sprite_group.remove(GAME_STATE["PLAYER_SPRITE"]) + except: + pass + + # reset map and other game state objects + GAME_STATE["ORIGINAL_MAP"] = {} + GAME_STATE["CURRENT_MAP"] = {} + ENTITY_SPRITES = [] + GAME_STATE["ENTITY_SPRITES_DICT"] = {} + CAMERA_VIEW = {} + GAME_STATE["INVENTORY"] = [] + GAME_STATE["TOTAL_HEARTS"] = 0 + + # Open and read raw string from the map csv file + f = open("tilegame_assets/{}".format(file_name), "r") + map_csv_str = f.read() + f.close() + + # split the raw string into lines + map_csv_lines = map_csv_str.replace("\r", "").split("\n") + + # set the WIDTH and HEIGHT variables. + # this assumes the map is rectangular. + GAME_STATE["MAP_HEIGHT"] = len(map_csv_lines) + GAME_STATE["MAP_WIDTH"] = len(map_csv_lines[0].split(",")) + + # loop over each line storing index in y variable + for y, line in enumerate(map_csv_lines): + # ignore empty line + if line != "": + # loop over each tile type separated by commas, storing index in x variable + for x, tile_name in enumerate(line.split(",")): + print("%s '%s'" % (len(tile_name), str(tile_name))) + + # if the tile exists in our main dictionary + if tile_name in TILES.keys(): + + # if the tile is an entity + if ( + "entity" in TILES[tile_name].keys() + and TILES[tile_name]["entity"] + ): + # set the map tiles to floor + GAME_STATE["ORIGINAL_MAP"][x, y] = "floor" + GAME_STATE["CURRENT_MAP"][x, y] = "floor" + + if tile_name == "heart": + GAME_STATE["TOTAL_HEARTS"] += 1 + + # if it's the player + if tile_name == "player": + # Create the sprite TileGrid + GAME_STATE["PLAYER_SPRITE"] = displayio.TileGrid( + sprite_sheet, + pixel_shader=palette, + width=1, + height=1, + tile_width=16, + tile_height=16, + default_tile=TILES[tile_name]["sprite_index"], + ) + + # set the position of sprite on screen + GAME_STATE["PLAYER_SPRITE"].x = x * 16 + GAME_STATE["PLAYER_SPRITE"].y = y * 16 + + # set position in x,y tile coords for reference later + GAME_STATE["PLAYER_LOC"] = (x, y) + + # add sprite to the group + sprite_group.append(GAME_STATE["PLAYER_SPRITE"]) + else: # not the player + # Create the sprite TileGrid + entity_srite = displayio.TileGrid( + sprite_sheet, + pixel_shader=palette, + width=1, + height=1, + tile_width=16, + tile_height=16, + default_tile=TILES[tile_name]["sprite_index"], + ) + # set the position of sprite on screen + # default to off the edge + entity_srite.x = -16 + entity_srite.y = -16 + + # add the sprite object to ENTITY_SPRITES list + ENTITY_SPRITES.append(entity_srite) + # print("setting GAME_STATE['ENTITY_SPRITES_DICT'][%s,%s]" % (x,y)) + + # create an entity obj + entity_obj = { + "entity_sprite_index": len(ENTITY_SPRITES) - 1, + "map_tile_name": tile_name, + } + + # if there are no entities at this location yet + if (x, y) not in GAME_STATE["ENTITY_SPRITES_DICT"]: + # create a list and add it to the dictionary at the x,y location + GAME_STATE["ENTITY_SPRITES_DICT"][x, y] = [entity_obj] + else: + # append the entity to the existing list in the dictionary + GAME_STATE["ENTITY_SPRITES_DICT"][x, y].append( + entity_obj + ) + + else: # tile is not entity + # set the tile_name into MAP dictionaries + GAME_STATE["ORIGINAL_MAP"][x, y] = tile_name + GAME_STATE["CURRENT_MAP"][x, y] = tile_name + + else: # tile type wasn't found in dict + print("tile: %s not found in TILES dict" % tile_name) + # add all entity sprites to the group + print("appending {} sprites".format(len(ENTITY_SPRITES))) + for entity in ENTITY_SPRITES: + sprite_group.append(entity) + + +print("loading map") +load_map(MAPS[GAME_STATE["MAP_INDEX"]]) + +# Add the Group to the Display +display.show(group) + +# variables to store previous value of button state +prev_up = False +prev_down = False +prev_left = False +prev_right = False + + +# helper function returns true if player is allowed to move given direction +# based on can_walk property of the tiles next to the player +def can_player_move(direction): + if direction == UP: + tile_above_coords = ( + GAME_STATE["PLAYER_LOC"][0], + GAME_STATE["PLAYER_LOC"][1] - 1, + ) + return TILES[ + GAME_STATE["CURRENT_MAP"][tile_above_coords[0], tile_above_coords[1]] + ]["can_walk"] + + if direction == DOWN: + tile_below_coords = ( + GAME_STATE["PLAYER_LOC"][0], + GAME_STATE["PLAYER_LOC"][1] + 1, + ) + return TILES[ + GAME_STATE["CURRENT_MAP"][tile_below_coords[0], tile_below_coords[1]] + ]["can_walk"] + + if direction == LEFT: + tile_left_of_coords = ( + GAME_STATE["PLAYER_LOC"][0] - 1, + GAME_STATE["PLAYER_LOC"][1], + ) + return TILES[ + GAME_STATE["CURRENT_MAP"][tile_left_of_coords[0], tile_left_of_coords[1]] + ]["can_walk"] + + if direction == RIGHT: + tile_right_of_coords = ( + GAME_STATE["PLAYER_LOC"][0] + 1, + GAME_STATE["PLAYER_LOC"][1], + ) + return TILES[ + GAME_STATE["CURRENT_MAP"][tile_right_of_coords[0], tile_right_of_coords[1]] + ]["can_walk"] + + +# set the appropriate tiles into the CAMERA_VIEW dictionary +# based on given starting coords and size +def set_camera_view(startX, startY, width, height): + global CAMERA_OFFSET_X + global CAMERA_OFFSET_Y + # set the offset variables for use in other parts of the code + CAMERA_OFFSET_X = startX + CAMERA_OFFSET_Y = startY + + # loop over the rows and indexes in the desired size section + for y_index, y in enumerate(range(startY, startY + height)): + # loop over columns and indexes in the desired size section + for x_index, x in enumerate(range(startX, startX + width)): + # print("setting camera_view[%s,%s]" % (x_index,y_index)) + try: + # set the tile at the current coordinate of the MAP into the CAMERA_VIEW + CAMERA_VIEW[x_index, y_index] = GAME_STATE["CURRENT_MAP"][x, y] + except KeyError: + # if coordinate is out of bounds set it to floor by default + CAMERA_VIEW[x_index, y_index] = "floor" + + +# draw the current CAMERA_VIEW dictionary and the GAME_STATE['ENTITY_SPRITES_DICT'] +def draw_camera_view(): + # list that will hold all entities that have been drawn based on their MAP location + # any entities not in this list should get moved off the screen + drew_entities = [] + # print(CAMERA_VIEW) + + # loop over y tile coordinates + for y in range(0, SCREEN_HEIGHT_TILES): + # loop over x tile coordinates + for x in range(0, SCREEN_WIDTH_TILES): + # tile name at this location + tile_name = CAMERA_VIEW[x, y] + + # if tile exists in the main dictionary + if tile_name in TILES.keys(): + # if there are entity(s) at this location + if (x + CAMERA_OFFSET_X, y + CAMERA_OFFSET_Y) in GAME_STATE[ + "ENTITY_SPRITES_DICT" + ]: + # default background for entities is floor + castle[x, y] = TILES["floor"]["sprite_index"] + + # if it's not the player + if tile_name != "player": + # loop over all entities at this location + for entity_obj_at_tile in GAME_STATE["ENTITY_SPRITES_DICT"][ + x + CAMERA_OFFSET_X, y + CAMERA_OFFSET_Y + ]: + # set appropriate x,y screen coordinates + # based on tile coordinates + ENTITY_SPRITES[ + int(entity_obj_at_tile["entity_sprite_index"]) + ].x = (x * 16) + ENTITY_SPRITES[ + int(entity_obj_at_tile["entity_sprite_index"]) + ].y = (y * 16) + + # add the index of the entity sprite to the draw_entities + # list so we know not to hide it later. + drew_entities.append( + entity_obj_at_tile["entity_sprite_index"] + ) + + else: # no entities at this location + # set the sprite index of this tile into the CASTLE dictionary + castle[x, y] = TILES[tile_name]["sprite_index"] + + else: # tile type not found in main dictionary + # default to floor tile + castle[x, y] = TILES["floor"]["sprite_index"] + + # if the player is at this x,y tile coordinate accounting for camera offset + if GAME_STATE["PLAYER_LOC"] == ((x + CAMERA_OFFSET_X, y + CAMERA_OFFSET_Y)): + # set player sprite screen coordinates + GAME_STATE["PLAYER_SPRITE"].x = x * 16 + GAME_STATE["PLAYER_SPRITE"].y = y * 16 + + # loop over all entity sprites + for index in range(0, len(ENTITY_SPRITES)): + # if the sprite wasn't drawn then it's outside the camera view + if index not in drew_entities: + # hide the sprite by moving it off screen + ENTITY_SPRITES[index].x = int(-16) + ENTITY_SPRITES[index].y = int(-16) + + +# variable to store timestamp of last drawn frame +last_update_time = 0 + +# variables to store movement offset values +x_offset = 0 +y_offset = 0 + + +def show_splash(new_text, color, vertical_offset=18): + text_area.text = "" + text_area.text = new_text + text_area.y = round(text_area.text.count("\n") * vertical_offset / 2) + text_area.color = color + group.append(splash) + + +# game message background bmp file +with open( + "tilegame_assets/game_message_background.bmp", "rb" +) as game_message_background: + # Make the splash context + splash = displayio.Group(max_size=4) + + odb = displayio.OnDiskBitmap(game_message_background) + + bg_grid = displayio.TileGrid(odb, pixel_shader=displayio.ColorConverter()) + + splash.append(bg_grid) + + # Text for the message + text_group = displayio.Group(max_size=8, scale=1, x=14, y=18) + text_area = label.Label(terminalio.FONT, text=" " * 180, color=0xD39AE5) + text_group.append(text_area) + splash.append(text_group) + + # main loop + while True: + # set the current button values into variables + cur_btn_vals = controls.button + cur_up = cur_btn_vals.up + cur_down = cur_btn_vals.down + cur_right = cur_btn_vals.right + cur_left = cur_btn_vals.left + cur_a = cur_btn_vals.a + + if GAME_STATE["STATE"] == STATE_WAITING: + print(cur_a) + if cur_a: + GAME_STATE["STATE"] = STATE_PLAYING + group.remove(splash) + + if GAME_STATE["STATE"] == STATE_PLAYING: + # check for up button press / release + if not cur_up and prev_up: + if can_player_move(UP): + x_offset = 0 + y_offset = -1 + + # check for down button press / release + if not cur_down and prev_down: + if can_player_move(DOWN): + x_offset = 0 + y_offset = 1 + + # check for right button press / release + if not cur_right and prev_right: + if can_player_move(RIGHT): + x_offset = 1 + y_offset = 0 + + # check for left button press / release + if not cur_left and prev_left: + if can_player_move(LEFT): + x_offset = -1 + y_offset = 0 + + # if any offset is not zero then we need to process player movement + if x_offset != 0 or y_offset != 0: + # variable to store if player is allowed to move + can_move = False + + # coordinates the player is moving to + moving_to_coords = ( + GAME_STATE["PLAYER_LOC"][0] + x_offset, + GAME_STATE["PLAYER_LOC"][1] + y_offset, + ) + + # tile name of the spot player is moving to + moving_to_tile_name = GAME_STATE["CURRENT_MAP"][ + moving_to_coords[0], moving_to_coords[1] + ] + + # if there are entity(s) at spot the player is moving to + if moving_to_coords in GAME_STATE["ENTITY_SPRITES_DICT"]: + print("found entity(s) where we are moving to") + + # loop over all entities at the location player is moving to + for entity_obj in GAME_STATE["ENTITY_SPRITES_DICT"][ + moving_to_coords + ]: + print("checking entity %s" % entity_obj["map_tile_name"]) + # if the entity has a before_move behavior function + if "before_move" in TILES[entity_obj["map_tile_name"]].keys(): + print( + "calling before_move %s, %s, %s" + % ( + moving_to_coords, + GAME_STATE["PLAYER_LOC"], + entity_obj, + ) + ) + # call the before_move behavior function act upon it's result + if TILES[entity_obj["map_tile_name"]]["before_move"]( + moving_to_coords, + GAME_STATE["PLAYER_LOC"], + entity_obj, + GAME_STATE, + ): + # all the movement if it returned true + can_move = True + else: + # break and don't allow movement if it returned false + break + else: # entity does not have a before_move function + # allow movement + can_move = True + if can_move: + # set the player loc variable to the new coords + GAME_STATE["PLAYER_LOC"] = moving_to_coords + + else: # no entities at the location player is moving to + # set player loc variable to new coords + GAME_STATE["PLAYER_LOC"] = moving_to_coords + + # reset movement offset variables + y_offset = 0 + x_offset = 0 + + # set previous button values for next iteration + prev_up = cur_up + prev_down = cur_down + prev_right = cur_right + prev_left = cur_left + + # current time + now = time.monotonic() + + # if it has been long enough based on FPS delay + if now > last_update_time + FPS_DELAY: + # Set camera to 10x8 centered on the player + # Clamped to (0, MAP_WIDTH) and (0, MAP_HEIGHT) + set_camera_view( + max( + min( + GAME_STATE["PLAYER_LOC"][0] - 4, + GAME_STATE["MAP_WIDTH"] - SCREEN_WIDTH_TILES, + ), + 0, + ), + max( + min( + GAME_STATE["PLAYER_LOC"][1] - 3, + GAME_STATE["MAP_HEIGHT"] - SCREEN_HEIGHT_TILES, + ), + 0, + ), + 10, + 8, + ) + # draw the camera + draw_camera_view() + # if player beat this map + if GAME_STATE["STATE"] == STATE_MAPWIN: + GAME_STATE["MAP_INDEX"] += 1 + # if player has beaten all maps + if GAME_STATE["MAP_INDEX"] >= len(MAPS): + GAME_STATE["MAP_INDEX"] = 0 + GAME_STATE["STATE"] = STATE_WAITING + show_splash( + "You Win \n =D \nCongratulations. \nStart Over?", 0x29C1CF + ) + load_map(MAPS[GAME_STATE["MAP_INDEX"]]) + else: + # prompt to start next + GAME_STATE["STATE"] = STATE_WAITING + show_splash( + "You beat this level\n =D \nCongratulations. \nStart Next?", + 0x29C1CF, + ) + load_map(MAPS[GAME_STATE["MAP_INDEX"]]) + # game over from sparky + elif GAME_STATE["STATE"] == STATE_LOST_SPARKY: + GAME_STATE["MAP_INDEX"] = 0 + GAME_STATE["STATE"] = STATE_WAITING + game_over_text = "Be careful not to \ntouch Sparky unless \nyou've collected \nenough Mho's.\nStarting Over" + show_splash(game_over_text, 0x25AFBB) + load_map(MAPS[GAME_STATE["MAP_INDEX"]]) + # talking to minerva + elif GAME_STATE["STATE"] == STATE_MINERVA: + GAME_STATE["STATE"] = STATE_WAITING + random_fact = random.choice(FACTS) + minerva_txt = wrap_nicely("Minerva: {}".format(random_fact), 23) + show_splash(minerva_txt, 0xD39AE5, 10) + + # store the last update time + last_update_time = now diff --git a/Tilemap_Game_With_CircuitPython/readme.md b/Tilemap_Game_With_CircuitPython/readme.md new file mode 100644 index 000000000..f7361e3de --- /dev/null +++ b/Tilemap_Game_With_CircuitPython/readme.md @@ -0,0 +1,5 @@ +# Creating Your First Tilemap Game With CircuitPython + +Respository for an Example Tilemap Game built with Circuit Python for PyGamer, PyBdage, or Pew Pew M4 Devices. + +Tutorial for this proroject is at [https://learn.adafruit.com/creating-your-first-tilemap-game-with-circuit-python/](https://learn.adafruit.com/creating-your-first-tilemap-game-with-circuit-python/) \ No newline at end of file diff --git a/Tilemap_Game_With_CircuitPython/requirements.txt b/Tilemap_Game_With_CircuitPython/requirements.txt new file mode 100644 index 000000000..d6212a759 --- /dev/null +++ b/Tilemap_Game_With_CircuitPython/requirements.txt @@ -0,0 +1,2 @@ +adafruit_display_text +adafruit_imageload diff --git a/Tilemap_Game_With_CircuitPython/tilegame_assets/__init__.py b/Tilemap_Game_With_CircuitPython/tilegame_assets/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/Tilemap_Game_With_CircuitPython/tilegame_assets/castle_sprite_sheet.bmp b/Tilemap_Game_With_CircuitPython/tilegame_assets/castle_sprite_sheet.bmp new file mode 100644 index 0000000000000000000000000000000000000000..84d9c819151bee9995ff9355fa517509c616dd00 GIT binary patch literal 3262 zcmb7GJC4*q5N#Xp*fX;;HsS zmyVHo2E5ztFgu1{kGk;S;j{1?_Uk7v!{xn?XkWsMSMS32Z+FA{Pd~%u=P_J<9m9`5 zf5Z6iLm0;~RL9DCy{^#Kjr2BByWvu`meEcP)l3umj z%l+)QX+zOowcAZq;fK+Z4VhGA0b76SXEqe((F>MZ@cTQKGmftePd-W?N_iW5j$INT z<8LICk@S*~^AWwpAxZs7R1*~oHigRZX(z_NO30t$`3l~c3u8S7&jG-)KCGvc5eC9V z$S5C;9pmd>N|sRim|_^>Tuc3L!r#td?!3Srs|}No&SP^Qi@yazQZTKAw2!r!;)$Kb zlM8F)(yS%E;-waOe5SAUO!r6m06G(2@r+&YS1j>GaF1;|&sX_ef#)6+ycrxTzoMSS{l~pAS;A!egM5nkc6aDo zIynCcpLo~A!`$T6M!S&fo|g<9q2NFs*4(ripSD~l&%5!@l$hxBRV(i+zDM{3y0#&d z&rmBglFEo4Fa^p^nfk_l2&fMh(prk2=3;U>MYPmGLdN*YUARWZo`cQ`;t1*~qsjLu zPa<9pO!6nCw|r$rxeDhz5z9N}OLpbANQdAqqB`NRk$QdRSTUIsHbb*wl8v1KF9b>?2!W_&cyXw$mleQ=TWFM( epH?=gYhDKJIgQ{$?{{;L4-pgLC7nzi8vX~xZf~pr literal 0 HcmV?d00001 diff --git a/Tilemap_Game_With_CircuitPython/tilegame_assets/castle_sprite_sheet_edited.bmp b/Tilemap_Game_With_CircuitPython/tilegame_assets/castle_sprite_sheet_edited.bmp new file mode 100644 index 0000000000000000000000000000000000000000..63d54b80694942db31b6c6c6ae4f745287e3585e GIT binary patch literal 3266 zcmb7GyROqf5ZyR9u@m3KhA$vJ5;X`3ZUYSs5)I-Hprb&dri2C|3VI5V=;)EIlrP{@ z_yBXxJl4C86K*Eio!Q+pvu9>Eb}pY@H9_PG{cE(lXb;h5p+XPA`mMFLn;5cPK7RIw zOXo;22KPF#+wEZV;pc-Y+`IoI{DS@J(TlLZ^8xL1c>eNj`1bX7c=z!~*njH7{!1Ue z|NayDzwble_n|mfnx-kxno@cjsaS9~!XgdCiQb@1b z?bTs++_a=0jUD6bUP_iw`j}!E;#^DpufyNYVCuZU9;+pjkj`UsAB(>QLQ*iTgtU*f8RLna z#FGnagU-43lG(OYUdJgwT`2ac*U-68c@mDPIS#XbSI?Y%4oWOGr3Z4v(mGNkU z$UozwwVB|b)sxQ`m$=87@kj7@H+cqWzT9V$4{Jlo_@Ag}cK>m2Oja-%|0tg7!PjSA34}7wFoC zP(DMg&`2sFdc+hcJ7ww{`yrq{SQyrZ_`_UGPN#^LI!MSEU%3m{$k=nxSwWmZeaL9^ zK1!2_mjjdhO6e_MnNhC7IZed!9`Yr-{9U9&a2HWM;IWavukEc}%IiCQg#!@l0|Td! z^fuI~bV(1HNc+D52p$6vi6#~gG|mi#x}Mc;@VRI=byZch{bd{f)k?>J0aw*^wOlSk zoi1he+(v>16wMp!P%1! z$TL0_;-~QFfSk2BA>{m1HeJja6^f-hiti#GC1rHo!Ai)Suo;>XvR-C+Ay6W*1)>&u taVB&wZg$3icQ-1%Y->Lw`q*GTCs2D%BY4;P-8|q!#7KBaM^nd!{{Ye=WAFd~ literal 0 HcmV?d00001 diff --git a/Tilemap_Game_With_CircuitPython/tilegame_assets/fun_facts.py b/Tilemap_Game_With_CircuitPython/tilegame_assets/fun_facts.py new file mode 100644 index 000000000..7b5f41f50 --- /dev/null +++ b/Tilemap_Game_With_CircuitPython/tilegame_assets/fun_facts.py @@ -0,0 +1,13 @@ +# Fun facts to be read to the player by Minerva +FACTS = [ + "The digitalio module lets you read and write HIGH and LOW values with IO pins.", + "JSON stands for Javascript Object Notation. It's a language for storing and transfering data.", + "PMW stands for pulse width modulation. The pulsio module lets you use PWM pins.", + "REPL stands for Read Evaluate Print Loop. You access it by sending ctrl-C via serial.", + "Released in 1979 Namco's arcade game Galaxian was the first tile-based video game.", + "Python dictionaries can have keys and values of any type! Even functions.", + "Circuitpython was initially released July 19, 2017.", + "The displayio module was introducted in Circuipython 4.0. It simplified drawing images and text on screens.", + "The adafruit_dotstar and neopixel libraries make it easy control dazzling addressable RGB LEDs.", + "You can sense human touch on an IO pin with the touchio module." +] diff --git a/Tilemap_Game_With_CircuitPython/tilegame_assets/game_message_background.bmp b/Tilemap_Game_With_CircuitPython/tilegame_assets/game_message_background.bmp new file mode 100644 index 0000000000000000000000000000000000000000..0856b00f8a98aa456a96439af69687e6a8cd6c9a GIT binary patch literal 20694 zcmeI)JxT*n6b0aynMRBlF{n$iu#1H@0TJxP71&t_R&KyO*iGUdlB^*KWIdkuCO@7a z!s)JX9^9FCGG4fRlNq}VhO4_&)(XdG)Fj0RyAH1;gbCGXt`2vQTulSWOU^^*t z!1)d#m#aL>q2_BJUEJLE_U2Ql`Kpt`)?L2@^SH6*Yrl5Y`V{Z|yt%Q3vE1!geq}B? zcs}BggZ*SOYOo)1$iaRx88z6CIOJeInT#6jM;vmnpG-y#_9G5C*iR;-2Ky0*9PB5P zQG@-6Lk{+n$*948#32X!$z;@EKjM&s{bVv~upe>A!G1CsHQ0|h?e~^gZ+p@4)&ADsKI{3AqV@(WYl0k;*f*=WHM^7A92XR zeli&~*pE2mU_Y6R8tg|La27;1IQ!FY5)KL literal 0 HcmV?d00001 diff --git a/Tilemap_Game_With_CircuitPython/tilegame_assets/map0.csv b/Tilemap_Game_With_CircuitPython/tilegame_assets/map0.csv new file mode 100644 index 000000000..5228b4980 --- /dev/null +++ b/Tilemap_Game_With_CircuitPython/tilegame_assets/map0.csv @@ -0,0 +1,8 @@ +top_left_wall,top_wall,top_wall,top_right_wall,,,top_left_wall,top_wall,top_wall,top_wall,top_right_wall,,, +left_wall,player,floor,floor,top_wall,top_wall,floor,floor,floor,right_wall,right_wall,top_wall,top_wall,top_right_wall +left_wall,floor,floor,mho,floor,floor,floor,floor,top_wall,right_wall,minerva,floor,sparky,right_wall +left_wall,minerva,floor,floor,heart,floor,floor,left_wall,floor,sparky,floor,floor,floor,right_wall +left_wall,top_wall,top_wall,sparky,top_wall,top_wall,top_wall,left_wall,floor,right_wall,floor,floor,floor,right_wall +left_wall,floor,floor,mho,floor,floor,floor,floor,floor,right_wall,heart,floor,robot,right_wall +left_wall,floor,floor,floor,floor,floor,heart,floor,floor,right_wall,right_wall,bottom_wall,bottom_wall,bottom_right_wall +bottom_left_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_right_wall,,, diff --git a/Tilemap_Game_With_CircuitPython/tilegame_assets/map1.csv b/Tilemap_Game_With_CircuitPython/tilegame_assets/map1.csv new file mode 100644 index 000000000..c896316ec --- /dev/null +++ b/Tilemap_Game_With_CircuitPython/tilegame_assets/map1.csv @@ -0,0 +1,17 @@ +top_left_wall,top_wall,top_wall,top_right_wall,floor,floor,top_left_wall,top_wall,top_wall,top_wall,top_right_wall,,, +left_wall,mho,floor,floor,top_wall,top_wall,minerva,floor,right_wall,floor,heart,top_wall,top_wall,top_right_wall +left_wall,floor,player,floor,floor,left_wall,floor,floor,sparky,floor,floor,floor,floor,right_wall +left_wall,floor,floor,floor,floor,sparky,floor,floor,right_wall,mho,floor,floor,floor,right_wall +left_wall,floor,floor,floor,floor,left_wall,floor,floor,right_wall,top_wall,top_wall,sparky,top_wall,right_wall +left_wall,floor,mho,floor,floor,left_wall,floor,floor,right_wall,floor,floor,floor,floor,right_wall +left_wall,floor,floor,floor,floor,left_wall,heart,floor,right_wall,floor,floor,floor,mho,right_wall +bottom_left_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,heart,floor,floor,right_wall +,top_left_wall,top_wall,top_wall,top_wall,top_wall,top_right_wall,floor,floor,floor,left_wall,floor,floor,right_wall +,left_wall,robot,floor,floor,floor,sparky,floor,floor,floor,left_wall,heart,floor,right_wall +,left_wall,floor,floor,floor,floor,right_wall,floor,floor,floor,sparky,floor,floor,right_wall +,left_wall,floor,floor,minerva,floor,right_wall,floor,heart,floor,left_wall,floor,floor,right_wall +,left_wall,floor,floor,floor,floor,right_wall,top_wall,top_wall,top_wall,left_wall,floor,floor,right_wall +,left_wall,floor,floor,floor,floor,right_wall,floor,floor,floor,left_wall,floor,mho,right_wall +,left_wall,floor,floor,floor,floor,sparky,floor,mho,floor,sparky,floor,minerva,right_wall +,left_wall,floor,floor,floor,floor,right_wall,floor,floor,floor,left_wall,floor,floor,right_wall +,bottom_left_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_wall,bottom_right_wall diff --git a/Tilemap_Game_With_CircuitPython/tilegame_assets/sprite_sheet.bmp b/Tilemap_Game_With_CircuitPython/tilegame_assets/sprite_sheet.bmp new file mode 100644 index 0000000000000000000000000000000000000000..c23093206af4bfbff0bc869dca15cb4f1532c2d8 GIT binary patch literal 4070 zcmb7HF>fnH5MH0XXWv~8+r;r5OGWx8Q9tww(f|b#LZU(Z1L!D_s1X%3D59W$1xR$X zPbfuN8vaB6z$+-t_sz`i-P)JrW$fLV-Th|vo7weUa`oxm+KJqu{|4=Sw9n9HZiOC# z_47vCyBM-vef0UaTzZNW$KZobTwY$nIJlQDR_;$}8~6N!kKJFG`})H#-K%Fmxm&bX z&#s}b-PI@8?u)O!b1#2;&wc;%AMWbMgS-6&?bn05xw&z_|NXDK{pZddUjN{p|95bQ z!@(6#mF;$0plz4Z+eqytmx?`Clj4f5p5kR`3csfm(yK1^OC3$eUsDR{RY!Y$njJS? zQnXhc?PXEmi_w}5nG|FJTfgdOHWcR33zk~&r#t2|5nmXde3U+v@;3GyM;Z7SeHBqr(Q>Y?-*opBU67r|SdKCEXXBMgL#kWoGw zJH^+%lq{k2F~u;%MJ@Gz6aIDvQ|ATtSY0v+>0)f|WAXPuND8KvkoKuI$9Q5V@#Ml9 zMOkhozT%}8Qp>p9#X?@_r_!mlkxB6lf}2YL*LvX z@;~B}?wWX*n>^ZRXY$zZWdu%8a3BwB(X^R3ZMh!(-i`lAiHS}hweq~;bA-RZsBH)p zXQ&mLNR5czV+xd=GL0L@LqL77Fsu#nhq;uTP7y72kdP_9au=?Nam+zy1@Q#xLq=|oLoxeEE$Xse;8ULY+SlqJ z@FR!0`PFsXw)GiW3$y0zCfH{bdcHCTVEh8my7uLn;1OLNMmx+`D{}xYgtF=@s71~A z#)c|xK)5*oKAVL_wV*1`6z_p$GrYpp0r-9vd@pLD@q-5;G{Z-@I8=TS0N=qO__AXB z5PHP!Ls0wxr$b-Cqu-Tq@Kx250l+FkXAFnKu7~WrhXaj=@Ii9kLCVsXB@Ky~g?A}_ zP`vY{NBDAqkHD1>SlLJ&td}`Yu#afLV?N?#hq?6@BT~^J2+Hqcf!_}X z0CaD6bn4!LO74+9@j>vx@ZD_I&APX!x`dDH$p_>ap9=9WIylZkpz1CxK5~wSobxA- zE=YVR;e{zwD3$I6pYdlRfH#b;J7guy27)y_OQhork_{3b1r5H@%eEPxQ8q|$-l5RP RyP1W|24e?{K1|(b{SOsUqYMB5 literal 0 HcmV?d00001 diff --git a/Tilemap_Game_With_CircuitPython/tilegame_assets/states.py b/Tilemap_Game_With_CircuitPython/tilegame_assets/states.py new file mode 100644 index 000000000..3de05b70a --- /dev/null +++ b/Tilemap_Game_With_CircuitPython/tilegame_assets/states.py @@ -0,0 +1,16 @@ +# State machine constants + +# playing the game: draw the map, listen for D-pad buttons to move player +STATE_PLAYING = 0 + +# player beat this map level +STATE_MAPWIN = 1 + +# waiting for player to press A button to continue +STATE_WAITING = 2 + +# player lost by touching sparky +STATE_LOST_SPARKY = 3 + +# minerva shows a fun fact +STATE_MINERVA = 4 diff --git a/Tilemap_Game_With_CircuitPython/tilegame_assets/text_helper.py b/Tilemap_Game_With_CircuitPython/tilegame_assets/text_helper.py new file mode 100644 index 000000000..5096edf62 --- /dev/null +++ b/Tilemap_Game_With_CircuitPython/tilegame_assets/text_helper.py @@ -0,0 +1,23 @@ +def wrap_nicely(string, max_chars): + """ From: https://www.richa1.com/RichardAlbritton/circuitpython-word-wrap-for-label-text/ + A helper that will return the string with word-break wrapping. + :param str string: The text to be wrapped. + :param int max_chars: The maximum number of characters on a line before wrapping. + """ + string = string.replace('\n', '').replace('\r', '') # strip confusing newlines + words = string.split(' ') + the_lines = [] + the_line = "" + for w in words: + if len(the_line+' '+w) <= max_chars: + the_line += ' '+w + else: + the_lines.append(the_line) + the_line = w + if the_line: + the_lines.append(the_line) + the_lines[0] = the_lines[0][1:] + the_newline = "" + for w in the_lines: + the_newline += '\n'+w + return the_newline diff --git a/Tilemap_Game_With_CircuitPython/tilegame_assets/tiles.py b/Tilemap_Game_With_CircuitPython/tilegame_assets/tiles.py new file mode 100644 index 000000000..bbd747fa7 --- /dev/null +++ b/Tilemap_Game_With_CircuitPython/tilegame_assets/tiles.py @@ -0,0 +1,132 @@ +from tilegame_assets.states import * + + +# Minerva before_move. Set game state to STATE_MINERVA +def minerva_walk(to_coords, from_coords, entity_obj, GAME_STATE): + GAME_STATE['STATE'] = STATE_MINERVA + return False + + +# Sparky before_move. If user does not have a Mho in inventory they lose. +# If user does have Mho subtract one from inventory and consume Sparky. +def sparky_walk(to_coords, from_coords, entity_obj, GAME_STATE): + if GAME_STATE['INVENTORY'].count("mho") > 0: + GAME_STATE['INVENTORY'].remove("mho") + GAME_STATE['ENTITY_SPRITES_DICT'][to_coords].remove(entity_obj) + if len(GAME_STATE['ENTITY_SPRITES_DICT'][to_coords]) == 0: + del GAME_STATE['ENTITY_SPRITES_DICT'][to_coords] + if (-1, -1) in GAME_STATE['ENTITY_SPRITES_DICT']: + GAME_STATE['ENTITY_SPRITES_DICT'][-1, -1].append(entity_obj) + else: + GAME_STATE['ENTITY_SPRITES_DICT'][-1, -1] = [entity_obj] + return True + else: + GAME_STATE['STATE'] = STATE_LOST_SPARKY + return True + + +# Robot before_move. If user has all Hearts they win the map. +def robot_walk(to_coords, from_coords, entity_obj, GAME_STATE): + if GAME_STATE['INVENTORY'].count("heart") == GAME_STATE['TOTAL_HEARTS']: + GAME_STATE["STATE"] = STATE_MAPWIN + return True + + return False + + +# Remove the item from this location and add it to player inventory. +def take_item(to_coords, from_coords, entity_obj, GAME_STATE): + print(entity_obj) + GAME_STATE['INVENTORY'].append(entity_obj["map_tile_name"]) + GAME_STATE['ENTITY_SPRITES_DICT'][to_coords].remove(entity_obj) + if len(GAME_STATE['ENTITY_SPRITES_DICT'][to_coords]) == 0: + del GAME_STATE['ENTITY_SPRITES_DICT'][to_coords] + + if (-1, -1) in GAME_STATE['ENTITY_SPRITES_DICT']: + GAME_STATE['ENTITY_SPRITES_DICT'][-1, -1].append(entity_obj) + else: + GAME_STATE['ENTITY_SPRITES_DICT'][-1, -1] = [entity_obj] + + return True + + +# main dictionary that maps tile type strings to objects. +# each one stores the sprite_sheet index and any necessary +# behavioral stats like can_walk or before_move +TILES = { + # empty strings default to floor and no walk. + "": { + "sprite_index": 10, + "can_walk": False + }, + "floor": { + "sprite_index": 10, + "can_walk": True + }, + "top_wall": { + "sprite_index": 7, + "can_walk": False + }, + "top_right_wall": { + "sprite_index": 8, + "can_walk": False + }, + "top_left_wall": { + "sprite_index": 6, + "can_walk": False + }, + "bottom_right_wall": { + "sprite_index": 14, + "can_walk": False + }, + "bottom_left_wall": { + "sprite_index": 12, + "can_walk": False + }, + "right_wall": { + "sprite_index": 11, + "can_walk": False + }, + "left_wall": { + "sprite_index": 9, + "can_walk": False + }, + "bottom_wall": { + "sprite_index": 13, + "can_walk": False + }, + "robot": { + "sprite_index": 1, + "can_walk": True, + "entity": True, + "before_move": robot_walk + }, + "heart": { + "sprite_index": 5, + "can_walk": True, + "entity": True, + "before_move": take_item + }, + "mho": { + "sprite_index": 2, + "can_walk": True, + "entity": True, + "before_move": take_item + }, + "sparky": { + "sprite_index": 4, + "can_walk": True, + "entity": True, + "before_move": sparky_walk + }, + "minerva": { + "sprite_index": 3, + "can_walk": True, + "entity": True, + "before_move": minerva_walk + }, + "player": { + "sprite_index": 0, + "entity": True, + } +}