def setUp(self): self.map_size = 10 self.active_map = DungeonMap(self.map_size) self.player = Player(self.active_map) self.loaded_map = DungeonMap(0, False) self.loaded_player = Player(self.loaded_map) self.file_path = 'test_save.dat'
def start_game(): """ Start game dialog :return: None """ global active_map global active_player global thread_enemy active_map = DungeonMap(0, False) active_player = Player(active_map) response = utils.get_input( validator_response, "Do you wish to load last saved game? [Y\\N]: ") if response.lower() == 'y': if not utils.load_game(SAVE_PATH, active_map, active_player): print("Failed to load saved game! Starting new game") init_game() else: logger.info("Starting new game.") init_game() thread_enemy = ThreadEnemyWrapper(active_map, active_player) thread_enemy.start()
def make_map(player, height, width): #fill map with "blocked" tiles dmap = DungeonMap(width, height) num_rooms = 0 for r in range(MAX_ROOMS): #random width and height w = libtcod.random_get_int(0, ROOM_MIN_SIZE, ROOM_MAX_SIZE) h = libtcod.random_get_int(0, ROOM_MIN_SIZE, ROOM_MAX_SIZE) #random position without going out of the boundaries of the map x = libtcod.random_get_int(0, 0, width - w - 1) y = libtcod.random_get_int(0, 0, height - h - 1) #"Rect" class makes rectangles easier to work with new_room = Rect(x, y, w, h) #run through the other rooms and see if they intersect with this one failed = False for other_room in dmap.rooms: if new_room.intersect(other_room): failed = True break if not failed: #this means there are no intersections, so this room is valid #"paint" it to the map's tiles create_room(dmap, new_room) #center coordinates of new room, will be useful later (new_x, new_y) = new_room.center() if num_rooms == 0: #this is the first room, where the player starts at player.x = new_x player.y = new_y else: #all rooms after the first: #connect it to the previous room with a tunnel #center coordinates of previous room (prev_x, prev_y) = dmap.rooms[num_rooms - 1].center() #draw a coin (random number that is either 0 or 1) if libtcod.random_get_int(0, 0, 1) == 1: #first move horizontally, then vertically create_h_tunnel(dmap, prev_x, new_x, prev_y) create_v_tunnel(dmap, prev_y, new_y, new_x) else: #first move vertically, then horizontally create_v_tunnel(dmap, prev_y, new_y, prev_x) create_h_tunnel(dmap, prev_x, new_x, new_y) #finally, append the new room to the list dmap.rooms.append(new_room) num_rooms += 1 return dmap
def __init__(self): """ Constructor of Player class """ super().__init__() self.bag = 0 self.name = "" self.proficiency = "" self.discovered_map = DungeonMap()
class Application: def __init__(self, window): self.window = window self.initialize_gl() self.dungeon_map = DungeonMap() def update(self, dt): pass def initialize_gl(self): self.program = GlProgram(shaders.vertex_sprite, shaders.fragment_sprite) self.program.uniform2f(b'offset', 0, 0) self.buffer = gl.GLuint(0) gl.glGenBuffers(1, ctypes.pointer(self.buffer)) gl.glActiveTexture(gl.GL_TEXTURE0) self.sprite_texture = gltexture.make_texture('sprites.png') gl.glBindTexture(gl.GL_TEXTURE_2D, self.sprite_texture) self.program.uniform1i(b'tex', 0) gl.glEnable(gl.GL_BLEND) gl.glEnable(gl.GL_DEPTH_TEST) gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA) def on_resize(self, width, height): logging.debug('window resized to %sx%s', width, height) gl.glViewport(0, 0, width, height) TILE_SIZE = 64 self.program.uniform2f(b'scale', 2*TILE_SIZE/width, 2*TILE_SIZE/height) self.vp_width = width self.vp_height = height def on_draw(self): gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) STRIDE = 8 self.program.use() self.program.vertex_attrib_pointer(self.buffer, b'position', 4, stride=STRIDE * ctypes.sizeof(gl.GLfloat)) self.program.vertex_attrib_pointer(self.buffer, b'tex_coord', 4, stride=STRIDE * ctypes.sizeof(gl.GLfloat), offset=4 * ctypes.sizeof(gl.GLfloat)) nb_vertices = 6*40*40 data = self.dungeon_map.vertex_data() data = (gl.GLfloat * (STRIDE * nb_vertices))(*data) gl.glBufferData(gl.GL_ARRAY_BUFFER, ctypes.sizeof(data), data, gl.GL_DYNAMIC_DRAW) gl.glDrawArrays(gl.GL_TRIANGLES, 0, nb_vertices) def on_key_press(self, symbol, modifiers): logging.debug('Key Press {} {}'.format(symbol, modifiers)) if symbol == key.I: logging.info('FPS: {}'.format(pyglet.clock.get_fps()))
def init_game(): """ Initializes game map and player position :return: None """ global active_map global active_player while True: map_size = utils.get_input(validator_map_size, "Input map size [5 - 100]: ") try: active_map = DungeonMap(int(map_size)) except MapInitError as exc: print(f"Failed to create map! Error: f{str(exc)}") else: break active_player = Player(active_map)
def __init__(self, window): self.window = window self.initialize_gl() self.dungeon_map = DungeonMap()
class DungeonGame: MIN_MAP_WIDTH = 5 MAX_MAP_WIDTH = 50 MIN_MAP_HEIGHT = 1 MAX_MAP_HEIGHT= 50 default_health = 3 treasure_to_win = 3 trap_rarity = 0.2 treasure_rarity = 0.05 enemy_turn_time = 3.0 actions = {"go north":'w', "go east":'d', "go south":'s', "go west":'a', "check health":"h", "check bag":"b", "view map":'m', "view map legend":"lg", "save":'sv', "load":'ld', "exit":"e"} actions_cheat = {"go north":'w', "go east":'d', "go south":'s', "go west":'a', "check health":"h", "check bag":"b", "view map":'m', "view map legend":"lg", "save":'sv', "load":'ld', "exit":"e", "cheat view map":"cheat"} action_to_position = { "w":Position(0, 1), "d":Position(1,0), "s":Position(0, -1), "a":Position(-1, 0) } position_directions = [Position(0, 1), Position(1,0), Position(0, -1), Position(-1, 0)] dmap = DungeonMap() player = Player() enemy = choice(enemies_list) is_room_being_processed = False is_enemy_encounter_being_processed = False @debug_decorator def __init__(self): pass @debug_decorator def is_game_ended(self): """ Checks if one of conditions for ending game is met: currently they are: 1) Player collects treasure_to_win number of treasures. 2) Player's health reaches zero. """ is_game_over = (DungeonGame.player.bag >= DungeonGame.treasure_to_win or DungeonGame.player.health <= 0) return is_game_over @debug_decorator def init_new_game(self): """ Initializes new games """ logger.info(text.new_game_start) input() logger.info(text.administration) input() logger.info(text.enter_proficiency_prompt) user_input = input().lower() if user_input in DungeonGame.player.proficiencies: DungeonGame.player.proficiency = user_input else: DungeonGame.player.proficiency = "none" logger.info(text.enter_dungeon_width_prompt) width = input_number_from_boundaries(DungeonGame.MIN_MAP_WIDTH,\ DungeonGame.MAX_MAP_WIDTH) logger.info(text.enter_dungeon_height_prompt) height = input_number_from_boundaries(DungeonGame.MIN_MAP_HEIGHT,\ DungeonGame.MAX_MAP_HEIGHT) size = width * height number_of_treasures = max (size * DungeonGame.treasure_rarity,\ DungeonGame.treasure_to_win) number_of_traps = min (size - number_of_treasures - 1, size * DungeonGame.trap_rarity) traps = Trap.generate_traps(number_of_traps) starting_position = Position.generate_random_position(width, height) DungeonGame.dmap.initialize(height, width, number_of_treasures, traps, starting_position) DungeonGame.player.health = DungeonGame.default_health DungeonGame.player.bag = 0 DungeonGame.player.position = starting_position DungeonGame.player.discovered_map.initialize_discovered_map(height, width,\ starting_position) self.respawn_enemy() @debug_decorator def process_player_commands(self): """ Process player commands. """ logger.info(text.action_prompt) user_input = input().lower() while (not user_input in DungeonGame.actions_cheat.keys() and not user_input in DungeonGame.actions_cheat.values()): logger.info(text.action_wrong) print_dictionary(DungeonGame.actions) user_input = input().lower() if user_input in DungeonGame.actions_cheat.keys(): user_input = DungeonGame.actions_cheat[user_input] if user_input in DungeonGame.action_to_position.keys(): move_position = DungeonGame.player.position + DungeonGame.action_to_position[user_input] if not DungeonGame.dmap.is_position_in_map(move_position): logger.info(choice(text.no_passage)) else: DungeonGame.player.position = move_position self.process_room() elif user_input == "m": map_to_print = deepcopy(DungeonGame.player.discovered_map) map_to_print.assign_cell(DungeonGame.player.position, "p") map_to_print.print_map() elif user_input == "lg": print_dictionary(DungeonMap.discovery_dict) elif user_input == "h": logger.info(text.health_check) logger.info(text.health_description[DungeonGame.player.health]) elif user_input == "b": logger.info(text.treasure_check.format(DungeonGame.player.bag)) elif user_input == "ld": if isfile("./{}.pickle".format(DungeonGame.player.name)): self.load_game() logger.info(choice(text.load_ingame)) else: logger.info(choice(text.on_no_save_file)) elif user_input == "sv": logger.info(choice(text.on_save_1)) self.save_game() logger.info(choice(text.on_save_2)) elif user_input == "e": logger.info(choice(text.lets_end_this)) while DungeonGame.player.is_alive(): DungeonGame.player.take_damage() elif user_input == "cheat": map_to_print = deepcopy(DungeonGame.dmap) map_to_print.assign_cell(DungeonGame.player.position, "p") map_to_print.print_map() @debug_decorator def process_room(self): """ Resolves possible trap encounters and prints feedback. """ #to prevent room and enemy encounter texts from mixing while DungeonGame.is_enemy_encounter_being_processed: pass if not self.player.is_alive(): return logger.info(text.tell_position.format(DungeonGame.player.position.x,\ DungeonGame.player.position.y)) current_cell = DungeonGame.dmap.cell(DungeonGame.player.position) if not current_cell: logger.debug("processing non-existant room!") return logger.debug("entered cell {}".format(current_cell)) current_cell = DungeonMap.cells_dict[current_cell] if current_cell.legend in trap_legends: self.process_hostile_encounter(current_cell) if not self.player.is_alive(): return else: logger.info(choice(current_cell.encounter_description)) if current_cell.legend == treasure_cell.legend: DungeonGame.player.bag += 1 if DungeonGame.dmap.cell(DungeonGame.player.position) != entrance_cell.legend: DungeonGame.dmap.assign_cell(DungeonGame.player.position, empty_cell.legend) is_trap_nearby = DungeonGame.dmap.check_for_adjacent_types(DungeonGame.player.position,\ trap_legends) is_treasure_nearby = DungeonGame.dmap.check_for_adjacent_types(DungeonGame.player.position,\ treasure_cell.legend) if is_trap_nearby and not is_treasure_nearby: logger.info(choice(text.adjacent_trap)) DungeonGame.player.discovered_map.assign_cell(DungeonGame.player.position,\ DungeonMap.discovery_dict["trap near"]) elif not is_trap_nearby and is_treasure_nearby: logger.info(choice(text.adjacent_treasure)) DungeonGame.player.discovered_map.assign_cell(DungeonGame.player.position,\ DungeonMap.discovery_dict["treasure near"]) elif is_trap_nearby and is_treasure_nearby: logger.info(choice(text.adjacent_trap)) logger.info(choice(text.also)) logger.info(choice(text.adjacent_treasure)) DungeonGame.player.discovered_map.assign_cell(DungeonGame.player.position,\ DungeonMap.discovery_dict["treasure and trap near"]) elif DungeonGame.dmap.cell(DungeonGame.player.position) == entrance_cell.legend: DungeonGame.player.discovered_map.assign_cell(DungeonGame.player.position,\ DungeonMap.discovery_dict["entrance"]) else: DungeonGame.player.discovered_map.assign_cell(DungeonGame.player.position,\ DungeonMap.discovery_dict["empty"]) @debug_decorator def save_game(self): """ Saves current game. """ hash = hashlib.md5(str(DungeonGame.player.name).encode()); current_data = (str(hash), DungeonGame.player, DungeonGame.enemy, DungeonGame.dmap) save_file_name = "".join([DungeonGame.player.name, ".pickle"]) logger.debug("saving to {}".format(save_file_name)) with open(save_file_name, 'wb') as save_file: dump(current_data, save_file) @debug_decorator def load_game(self): """ Loading game data from file. returns True on succsessful load, False on failed """ save_file_name = "".join([DungeonGame.player.name, ".pickle"]) logger.debug("loading from {}".format(save_file_name)) with open(save_file_name, 'rb') as save_file: try: game_data = load(save_file) except UnpicklingError as error: try: raise NotValidSaveFileError("UnpicklingError:{}".\ format(str(error)), save_file_name) except NotValidSaveFileError as custom_error: logger.debug(str(custom_error)) return False hash = hashlib.md5(str(DungeonGame.player.name).encode()); if str(hash) == game_data[0]: _, DungeonGame.player, DungeonGame.enemy, DungeonGame.dmap = game_data return True else: try: raise NotValidSaveFileError("Save file hash not verified.", save_file_name) except NotValidSaveFileError as custom_error: logger.debug(str(custom_error)) return False @debug_decorator def respawn_enemy(self): """ Respawns enemy at random not occupied by Player cell. """ DungeonMap.enemy = choice(enemies_list) enemy_position = Position.generate_random_position(DungeonGame.dmap.width,\ DungeonGame.dmap.height) while enemy_position == DungeonGame.player.position: enemy_position = Position.generate_random_position(DungeonGame.dmap.width,\ DungeonGame.dmap.height) DungeonMap.enemy.position = enemy_position logger.debug("enemy spawned at {},{}".format(DungeonGame.enemy.position.x,\ DungeonGame.enemy.position.y)) @debug_decorator def process_enemy_turn(self): """ Moves enemy to random ajucent cell """ new_position = DungeonGame.enemy.position + choice(DungeonGame.position_directions) while not self.dmap.is_position_in_map(new_position): new_position = DungeonGame.enemy.position + choice(DungeonGame.position_directions) DungeonGame.enemy.position = new_position logger.debug("enemy moves to {},{}".format(DungeonGame.enemy.position.x,\ DungeonGame.enemy.position.y)) @debug_decorator def process_enemy(self): """ Process enemy turns, which are made in enemy_turn_time intervals. """ time_of_last_turn = time.time() while not self.is_game_ended(): if (DungeonGame.enemy.position == DungeonGame.player.position and not DungeonGame.is_room_being_processed): logger.debug("enemy finds player") DungeonGame.is_enemy_encounter_being_processed = True self.process_hostile_encounter(DungeonGame.enemy) DungeonGame.is_enemy_encounter_being_processed = False self.respawn_enemy() current_time = time.time() if current_time - time_of_last_turn > DungeonGame.enemy_turn_time: self.process_enemy_turn() time_of_last_turn = current_time @debug_decorator @thread_lock_decorator def process_hostile_encounter(self, hostile_entity): """ Resolves Player's encounter with Trap or Enemy. """ logger.info(choice(hostile_entity.encounter_description)) if DungeonGame.player.proficiency in hostile_entity.fight_description.keys(): logger.info(choice(hostile_entity.fight_description[DungeonGame.player.proficiency])) else: logger.info(choice(hostile_entity.fight_description["other"])) is_damaged = DungeonGame.player.proficiency_immunity[DungeonGame.player.proficiency]\ != hostile_entity.enemy_type if is_damaged: DungeonGame.player.take_damage() if DungeonGame.player.is_alive(): if DungeonGame.player.proficiency in hostile_entity.survive_description.keys(): logger.info(choice(hostile_entity.survive_description[DungeonGame.player.proficiency])) else: logger.info(choice(hostile_entity.survive_description["other"])) else: if DungeonGame.player.proficiency in hostile_entity.defeat_description.keys(): logger.info(choice(hostile_entity.defeat_description[DungeonGame.player.proficiency])) else: logger.info(choice(hostile_entity.defeat_description["other"])) @debug_decorator def game_loop(self): """ Game loop. """ while True: logger.info(text.enter_name_prompt) name = input() name = list(filter(lambda c: c.isalpha(), name)) name = "".join(name) if not name: name = "Anonymous" DungeonGame.player.name = name is_load_game = False if isfile("./{}.pickle".format(DungeonGame.player.name)): logger.info(text.load_game_on_start_prompt) is_load_game = process_yes_no_input() if is_load_game: self.load_game() logger.info(choice(text.load_on_start)) else: self.init_new_game() threading.Thread(target=self.process_enemy).start() self.process_room() while not self.is_game_ended(): self.process_player_commands() if DungeonGame.player.is_alive(): logger.info(choice(text.won)) logger.debug("DungeonGame.player won") else: logger.info(choice(text.lost)) logger.debug("DungeonGame.player lost") input() logger.info(text.end_map_description) map_to_print = deepcopy(DungeonGame.dmap) map_to_print.assign_cell(DungeonGame.player.position, "p") map_to_print.print_map() print_dictionary(DungeonMap.cells_dict_explained) input() logger.info(text.play_again_prompt) is_play_again = process_yes_no_input() if not is_play_again: logger.debug("user exits game") break logger.debug("user decides to play again")
def generate_city_map(width, height, district): # generates a grid-based city map dmap = DungeonMap(width, height) def draw_street(start, end, size=2): # draws a street from start to end, inclusive # there are size street tiles both left and right of center start_x, start_y = start end_x, end_y = end do_barrier = random.random() < BARRIER_PROBABILITY if start_x == end_x: barrier_position = random.randint(start_y, end_y) if do_barrier else -100 for y in range(start_y, end_y + 1): for x in range(start_x - size, start_x + size + 1): if not dmap.within_map(x, y): continue if random.random() < (3 - abs(y - barrier_position) ) * BARRIER_DENSITY / 3.0: dmap[x][y] = create_tile(TileType.rock) else: dmap[x][y] = create_tile(TileType.street) elif start_y == end_y: # (I'm sure there's a nice way to deduplicate this code, but s/x/y is easier) barrier_position = random.randint(start_x, end_x) if do_barrier else -100 for x in range(start_x, end_x + 1): for y in range(start_y - size, start_y + size + 1): if not dmap.within_map(x, y): continue if random.random() < (3 - abs(x - barrier_position) ) * BARRIER_DENSITY / 3.0: dmap[x][y] = create_tile(TileType.rock) else: dmap[x][y] = create_tile(TileType.street) blocks = [] dmap.spawn = None def streetify_rect(rect): if rect.w < 2 * MIN_BLOCK_SIZE and rect.h < 2 * MIN_BLOCK_SIZE: # find the adjacent streets x1, y1 = rect.center() x2, y2 = x1, y1 while dmap.within_map( x1, y1) and dmap[x1][y1].tile_type not in STREET_TILES: x1 -= 1 x1 += 1 while dmap.within_map( x1, y1) and dmap[x1][y1].tile_type not in STREET_TILES: y1 -= 1 y1 += 1 while dmap.within_map( x2, y2) and dmap[x2][y2].tile_type not in STREET_TILES: x2 += 1 x2 -= 1 while dmap.within_map( x2, y2) and dmap[x2][y2].tile_type not in STREET_TILES: y2 += 1 y2 -= 1 blocks.append(Rect(x1, y1, x2 - x1 + 1, y2 - y1 + 1)) return vertical = random.randint(0, 1) == 1 if rect.w < 2 * MIN_BLOCK_SIZE: vertical = False elif rect.h < 2 * MIN_BLOCK_SIZE: vertical = True if vertical: street_x = random.randint(rect.x1 + MIN_BLOCK_SIZE, rect.x2 - MIN_BLOCK_SIZE) if not dmap.spawn: dmap.spawn = (street_x, (rect.y1 + rect.y2) / 2) draw_street((street_x, rect.y1), (street_x, rect.y2)) streetify_rect(Rect(rect.x1, rect.y1, street_x - rect.x1, rect.h)) streetify_rect(Rect(street_x, rect.y1, rect.x2 - street_x, rect.h)) else: street_y = random.randint(rect.y1 + MIN_BLOCK_SIZE, rect.y2 - MIN_BLOCK_SIZE) if not dmap.spawn: dmap.spawn = ((rect.x1 + rect.x2) / 2, street_y) draw_street((rect.x1, street_y), (rect.x2, street_y)) streetify_rect(Rect(rect.x1, rect.y1, rect.w, street_y - rect.y1)) streetify_rect(Rect(rect.x1, street_y, rect.w, rect.y2 - street_y)) for x in range(0, width): for y in range(0, height): if x < 3 or y < 3 or width - x <= 3 or height - y <= 3: dmap[x][y] = create_tile(TileType.street) streetify_rect(Rect(3, 3, width - 6, height - 6)) # clear any rocks around the spawn spawn_x, spawn_y = dmap.spawn for dx in range(-2, 3): for dy in range(-2, 3): if dmap[spawn_x + dx][spawn_y + dy].tile_type == TileType.rock: dmap[spawn_x + dx][spawn_y + dy] = create_tile(TileType.street) dmap.rooms = [] def create_room(rect): for y in range(rect.y1, rect.y2): for x in range(rect.x1, rect.x2): if x == rect.x1 or x == rect.x2 - 1 or y == rect.y1 or y == rect.y2 - 1: dmap[x][y] = create_tile(TileType.wall) else: dmap[x][y] = create_tile(TileType.floor) dmap.rooms.append( Room(Rect(rect.x1 + 1, rect.y1 + 1, rect.w - 2, rect.h - 2), None)) def fill_block(block): # used to make sure rooms don't intersect each other room_hitboxes = [] block_rooms = [] top_divs = divide_line(block.w - 1, MIN_BIZ_SIZE, MAX_BIZ_SIZE) bottom_divs = divide_line(block.w - 1, MIN_BIZ_SIZE, MAX_BIZ_SIZE) left_divs = divide_line(block.h - 1, MIN_BIZ_SIZE, MAX_BIZ_SIZE) right_divs = divide_line(block.h - 1, MIN_BIZ_SIZE, MAX_BIZ_SIZE) # place corner rooms first room = Rect(block.x1, block.y1, top_divs[0] + 1, left_divs[0] + 1) room_hitboxes.append(Rect(room.x1, room.y1, room.w - 1, room.h - 1)) create_room(room) room = Rect(block.x1 + top_divs[-1], block.y1, block.w - top_divs[-1], right_divs[0] + 1) room_hitboxes.append(Rect(room.x1, room.y1, room.w - 1, room.h - 1)) create_room(room) room = Rect(block.x1, block.y1 + left_divs[-1], bottom_divs[0] + 1, block.h - left_divs[-1]) room_hitboxes.append(Rect(room.x1, room.y1, room.w - 1, room.h - 1)) create_room(room) room = Rect(block.x1 + bottom_divs[-1], block.y1 + right_divs[-1], block.w - bottom_divs[-1], block.h - right_divs[-1]) room_hitboxes.append(Rect(room.x1, room.y1, room.w - 1, room.h - 1)) create_room(room) for left, right in zip(top_divs, top_divs[1:]): collide = True while collide: height = random.randint(MIN_BIZ_SIZE, MAX_BIZ_SIZE) hitbox = Rect(block.x1 + left, block.y1, right - left, height) collide = False for other_hitbox in room_hitboxes: if hitbox.intersect(other_hitbox): collide = True break room_hitboxes.append(hitbox) room = Rect(hitbox.x1, hitbox.y1, hitbox.w + 1, hitbox.h + 1) create_room(room) for left, right in zip(bottom_divs, bottom_divs[1:]): collide = True while collide: height = random.randint(MIN_BIZ_SIZE, MAX_BIZ_SIZE) hitbox = Rect(block.x1 + left, block.y2 - height - 1, right - left, height) collide = False for other_hitbox in room_hitboxes: if hitbox.intersect(other_hitbox): collide = True break room_hitboxes.append(hitbox) height = random.randint(MIN_BIZ_SIZE, MAX_BIZ_SIZE) room = Rect(hitbox.x1, hitbox.y1, hitbox.w + 1, hitbox.h + 1) create_room(room) for top, bottom in zip(left_divs, left_divs[1:]): collide = True while collide: width = random.randint(MIN_BIZ_SIZE, MAX_BIZ_SIZE) hitbox = Rect(block.x1, block.y1 + top, width, bottom - top) collide = False for other_hitbox in room_hitboxes: if hitbox.intersect(other_hitbox): collide = True break room_hitboxes.append(hitbox) room = Rect(hitbox.x1, hitbox.y1, hitbox.w + 1, hitbox.h + 1) create_room(room) for top, bottom in zip(right_divs, right_divs[1:]): collide = True while collide: width = random.randint(MIN_BIZ_SIZE, MAX_BIZ_SIZE) hitbox = Rect(block.x2 - width - 1, block.y1 + top, width, bottom - top) collide = False for other_hitbox in room_hitboxes: if hitbox.intersect(other_hitbox): collide = True break room_hitboxes.append(hitbox) height = random.randint(MIN_BIZ_SIZE, MAX_BIZ_SIZE) room = Rect(hitbox.x1, hitbox.y1, hitbox.w + 1, hitbox.h + 1) create_room(room) for block in blocks: fill_block(block) # create doors for all the rooms for room in dmap.rooms: rect = room.rect good_spots = [] cx, cy = rect.center() for y in range(rect.y1 + 1, rect.y2): if dmap[rect.x1 - 2][y].tile_type == TileType.street: good_spots.append((rect.x1 - 1, y)) if dmap[rect.x2 + 1][y].tile_type == TileType.street: good_spots.append((rect.x2, y)) for x in range(rect.x1 + 1, rect.x2): if dmap[x][rect.y1 - 2].tile_type == TileType.street: good_spots.append((x, rect.y1 - 1)) if dmap[x][rect.y2 + 1].tile_type == TileType.street: good_spots.append((x, rect.y2)) if len(good_spots) > 0: door_x, door_y = random.choice(good_spots) room.door = (door_x, door_y) dmap[door_x][door_y] = create_tile(TileType.wall) dmap.rooms = filter(lambda x: x.door, dmap.rooms) random.shuffle(dmap.rooms) # assign businesses to rooms rooms_to_generate = int(len(dmap.rooms) * BIZ_IN_ROOM_PROPORTION) for room in dmap.rooms[:rooms_to_generate]: biz = district.add_business(room) if room.door: door_x, door_y = room.door dmap[door_x][door_y] = create_tile(TileType.door) if biz.owner: # spawn the owner inside the business biz.owner.x, biz.owner.y = biz.room.rect.center() dmap.objects.append(biz.owner) return dmap
def setUp(self): self.game_map = DungeonMap(10) self.player = Player(self.game_map)
class TestPlayer(unittest.TestCase): def setUp(self): self.game_map = DungeonMap(10) self.player = Player(self.game_map) def test_init_position(self): self.assertTrue(self.game_map.is_index_valid(self.player.position)) self.assertTrue(self.game_map.game_map[self.player.position[0]][ self.player.position[1]] == DungeonMap.SYMBOL_TILE) def test_move(self): move_directions = ["U", "L", "R", "D"] move_coords = {"U": [-1, 0], "L": [0, -1], "R": [0, +1], "D": [+1, 0]} direction = random.choice(move_directions) player_pos = self.player.position if self.player.move(direction, self.game_map): self.assertTrue(self.player.position[0] == player_pos[0] + move_coords[direction][0]) self.assertTrue(self.player.position[1] == player_pos[1] + move_coords[direction][1]) def test_mark_last_pos(self): self.player.mark_last_pos(self.game_map) self.assertTrue(self.game_map.game_map[self.player.position[0]][ self.player.position[1]] == DungeonMap.SYMBOL_LASTPOS) def test_is_dead(self): self.assertFalse(self.player.is_dead()) self.player.decrease_hitpoints(5) self.assertTrue(self.player.is_dead()) def test_is_bag_full(self): self.assertFalse(self.player.is_bag_full()) self.player.bag += 1 self.assertFalse(self.player.is_bag_full()) self.player.bag += 2 self.assertTrue(self.player.is_bag_full()) def test_get_hitpoints(self): self.assertTrue(self.player.get_hitpoints() == self.player.hitpoints) def test_decrease_hitpoints(self): player_hitpoints = self.player.get_hitpoints() damage = 1 self.player.decrease_hitpoints(damage) self.assertTrue(self.player.get_hitpoints() == (player_hitpoints - damage)) def tearDown(self): del self.player
class DungeonGame: MIN_MAP_WIDTH = 5 MAX_MAP_WIDTH = 50 MIN_MAP_HEIGHT = 1 MAX_MAP_HEIGHT = 50 default_health = 3 treasure_to_win = 3 trap_rarity = 0.2 treasure_rarity = 0.05 actions = { "go north": 'w', "go east": 'd', "go south": 's', "go west": 'a', "check health": "h", "check bag": "b", "view map": 'm', "view map legend": "lg", "save": 'sv', "load": 'ld', "exit": "e" } actions_cheat = { "go north": 'w', "go east": 'd', "go south": 's', "go west": 'a', "check health": "h", "check bag": "b", "view map": 'm', "view map legend": "lg", "save": 'sv', "load": 'ld', "exit": "e", "cheat view map": "cheat" } action_to_position = { "w": Position(0, 1), "d": Position(1, 0), "s": Position(0, -1), "a": Position(-1, 0) } dmap = DungeonMap() player = Player() @debug_decorator def __init__(self): pass @debug_decorator def init_new_game(self): """ Initializes new games """ logger.info(text.new_game_start) input() logger.info(text.administration) input() logger.info(text.enter_proficiency_prompt) user_input = input().lower() if user_input in DungeonGame.player.proficiencies: DungeonGame.player.proficiency = user_input else: DungeonGame.player.proficiency = "none" logger.info(text.enter_dungeon_width_prompt) width = input_number_from_boundaries(DungeonGame.MIN_MAP_WIDTH,\ DungeonGame.MAX_MAP_WIDTH) logger.info(text.enter_dungeon_height_prompt) height = input_number_from_boundaries(DungeonGame.MIN_MAP_HEIGHT,\ DungeonGame.MAX_MAP_HEIGHT) size = width * height number_of_treasures = max (size * DungeonGame.treasure_rarity,\ DungeonGame.treasure_to_win) number_of_traps = min(size - number_of_treasures - 1, size * DungeonGame.trap_rarity) traps = Trap.generate_traps(number_of_traps) starting_position = Position.generate_random_position(width, height) DungeonGame.dmap.initialize(height, width, number_of_treasures, traps, starting_position) DungeonGame.player.health = DungeonGame.default_health DungeonGame.player.bag = 0 DungeonGame.player.position = starting_position DungeonGame.player.discovered_map.initialize_discovered_map(height, width,\ starting_position) @debug_decorator def process_player_commands(self): """ Process player commands. """ logger.info(text.action_prompt) user_input = input().lower() while (not user_input in DungeonGame.actions_cheat.keys() and not user_input in DungeonGame.actions_cheat.values()): logger.info(text.action_wrong) print_dictionary(DungeonGame.actions) user_input = input().lower() if user_input in DungeonGame.actions_cheat.keys(): user_input = DungeonGame.actions_cheat[user_input] if user_input in DungeonGame.action_to_position.keys(): move_position = DungeonGame.player.position + DungeonGame.action_to_position[ user_input] if not DungeonGame.dmap.is_position_in_map(move_position): logger.info(choice(text.no_passage)) else: DungeonGame.player.position = move_position self.process_room() elif user_input == "m": map_to_print = deepcopy(DungeonGame.player.discovered_map) map_to_print.assign_cell(DungeonGame.player.position, "p") map_to_print.print_map() elif user_input == "lg": print_dictionary(DungeonMap.discovery_dict) elif user_input == "h": logger.info(text.health_check) logger.info(text.health_description[DungeonGame.player.health]) elif user_input == "b": logger.info(text.treasure_check.format(DungeonGame.player.bag)) elif user_input == "ld": if isfile("./{}.pickle".format(DungeonGame.player.name)): self.load_game() logger.info(choice(text.load_ingame)) else: logger.info(choice(text.on_no_save_file)) elif user_input == "sv": logger.info(choice(text.on_save_1)) self.save_game() logger.info(choice(text.on_save_2)) elif user_input == "e": logger.info(choice(text.lets_end_this)) while DungeonGame.player.is_alive(): DungeonGame.player.take_damage() elif user_input == "cheat": map_to_print = deepcopy(DungeonGame.dmap) map_to_print.assign_cell(DungeonGame.player.position, "p") map_to_print.print_map() @debug_decorator def process_room(self): """ Resolves possible trap encounters and prints feedback. """ logger.info(text.tell_position.format(DungeonGame.player.position.x,\ DungeonGame.player.position.y)) current_cell = DungeonGame.dmap.cell(DungeonGame.player.position) if not current_cell: logger.debug("processing non-existant room!") return logger.debug("entered cell {}".format(current_cell)) current_cell = DungeonMap.cells_dict[current_cell] logger.info(choice(current_cell.description)) input() if current_cell.legend == treasure_cell.legend: DungeonGame.player.bag += 1 elif current_cell.legend in trap_legends: if DungeonGame.player.proficiency in current_cell.fight_description.keys( ): logger.info( choice(current_cell.fight_description[ DungeonGame.player.proficiency])) else: logger.info(choice(current_cell.fight_description["other"])) input() is_damaged = DungeonGame.player.proficiency_immunity[ DungeonGame.player.proficiency] != current_cell.trap_type if is_damaged: DungeonGame.player.take_damage() if DungeonGame.player.is_alive(): if DungeonGame.player.proficiency in current_cell.survive_description.keys( ): logger.info( choice(current_cell.survive_description[ DungeonGame.player.proficiency])) else: logger.info( choice(current_cell.survive_description["other"])) input() else: if DungeonGame.player.proficiency in current_cell.defeat_description.keys( ): logger.info( choice(current_cell.defeat_description[ DungeonGame.player.proficiency])) else: logger.info( choice(current_cell.defeat_description["other"])) input() return if DungeonGame.dmap.cell( DungeonGame.player.position) != entrance_cell.legend: DungeonGame.dmap.assign_cell(DungeonGame.player.position, empty_cell.legend) is_trap_nearby = DungeonGame.dmap.check_for_adjacent_types(DungeonGame.player.position,\ trap_legends) is_treasure_nearby = DungeonGame.dmap.check_for_adjacent_types(DungeonGame.player.position,\ treasure_cell.legend) if is_trap_nearby and not is_treasure_nearby: logger.info(choice(text.adjacent_trap)) DungeonGame.player.discovered_map.assign_cell(DungeonGame.player.position,\ DungeonMap.discovery_dict["trap near"]) input() elif not is_trap_nearby and is_treasure_nearby: logger.info(choice(text.adjacent_treasure)) DungeonGame.player.discovered_map.assign_cell(DungeonGame.player.position,\ DungeonMap.discovery_dict["treasure near"]) input() elif is_trap_nearby and is_treasure_nearby: logger.info(choice(text.adjacent_trap)) logger.info(choice(text.also)) logger.info(choice(text.adjacent_treasure)) DungeonGame.player.discovered_map.assign_cell(DungeonGame.player.position,\ DungeonMap.discovery_dict["treasure and trap near"]) input() else: DungeonGame.player.discovered_map.assign_cell(DungeonGame.player.position,\ DungeonMap.discovery_dict["empty"]) @debug_decorator def save_game(self): """ Saves current game. """ current_data = (DungeonGame.player, DungeonGame.dmap) save_file_name = "".join([DungeonGame.player.name, ".pickle"]) logger.debug("saving to {}".format(save_file_name)) with open(save_file_name, 'wb') as save_file: dump(current_data, save_file) @debug_decorator def load_game(self): """ Loading game data from file. """ save_file_name = "".join([DungeonGame.player.name, ".pickle"]) logger.debug("loading from {}".format(save_file_name)) with open(save_file_name, 'rb') as save_file: game_data = load(save_file) DungeonGame.player, DungeonGame.dmap = game_data @debug_decorator def game_loop(self): """ Game loop. """ while True: logger.info(text.enter_name_prompt) name = input() name = list(filter(lambda c: c.isalpha(), name)) name = "".join(name) if not name: name = "Anonymous" DungeonGame.player.name = name is_load_game = False if isfile("./{}.pickle".format(DungeonGame.player.name)): logger.info(text.load_game_on_start_prompt) is_load_game = process_yes_no_input() if is_load_game: self.load_game() logger.info(choice(text.load_on_start)) else: self.init_new_game() self.process_room() while DungeonGame.player.is_alive( ) and DungeonGame.player.bag < DungeonGame.treasure_to_win: self.process_player_commands() if DungeonGame.player.is_alive(): logger.info(choice(text.won)) logger.debug("DungeonGame.player won") else: logger.info(choice(text.lost)) logger.debug("DungeonGame.player lost") input() logger.info(text.end_map_description) map_to_print = deepcopy(DungeonGame.dmap) map_to_print.assign_cell(DungeonGame.player.position, "p") map_to_print.print_map() print_dictionary(DungeonMap.cells_dict_explained) input() logger.info(text.play_again_prompt) is_play_again = process_yes_no_input() if not is_play_again: logger.debug("user exits game") break logger.debug("user decides to play again")