def target_tile(self, max_range=None): # return the position of a tile left-clicked in player's FOV (optionally in # a range), or (None,None) if right-clicked. while True: tdl.flush() clicked = False for event in tdl.event.get(): if event.type == 'MOUSEMOTION': self.mouse_controller.mouse_coord = event.cell if event.type == 'MOUSEDOWN' and event.button == 'LEFT': clicked = True elif ((event.type == 'MOUSEDOWN' and event.button == 'RIGHT') or (event.type == 'KEYDOWN' and event.key == 'ESCAPE')): return (None, None) self.camera.render_all_objects() coord = Vector2( *self.mouse_controller.mouse_coord) + self.camera.camera_coord if (clicked and self.map.is_visible_tile(coord.X, coord.Y) and (max_range is None or Vector2.distance(coord, self.player.coord) <= max_range)): self.mouse_controller.set_mouse_coord(coord) return self.mouse_controller.mouse_coord
def load_tile_template(stringified_template): global my_map, map_before, objects rows = stringified_template height = len(rows) width = len(rows[0]) my_map = [[Tile(False, x, y) for y in range(height)] for x in range(width)] for y, row in enumerate(rows): for x, char in enumerate(row): if char is "#": print("painting tile", x, y) my_map[x][y] = Tile(True, x, y) print(my_map[x][y]) elif char is ".": my_map[x][y] = Tile(False, x, y) else: obj = GameObject(coord=Vector2(x, y), char=char, color=(255, 255, 255)) objects.append(obj) map_before = [[Tile(my_map[x][y].blocked, x, y) for y in range(height)] for x in range(width)] return width, height
def move_towards(self, target: Vector2): result = PathFinding.breadth_first_search(self.collision_handler.map, self.coord.as_tuple(), target.as_tuple()) print(result) coord = Vector2(*result[1]) self.move(coord - self.coord)
def move_camera(self, target_coord): # new camera coordinates (top-left corner of the screen relative to the map) x = target_coord.X - self.camera_width // 2 # coordinates so that the target is at the center of the screen y = target_coord.Y - self.camera_height // 2 # make sure the camera doesn't see outside the map x = 0 if x < 0 else y y = 0 if y < 0 else y if x >= self.map_width - self.camera_width - 1: x = self.map_width - self.camera_width - 1 if y >= self.map_height - self.camera_height - 1: y = self.map_height - self.camera_height - 1 if Vector2(x, y) != self.camera_coord: self.fov_recompute = True self.camera_coord = Vector2(x, y)
def take_turn(self): if self.num_turns > 0: # still confused... # move in a random direction, and decrease the number of turns confused self.owner.move(Vector2(randint(-1, 1), randint(-1, 1))) self.num_turns -= 1 else: # restore the previous AI (this one will be deleted because it's not referenced anymore) self.owner.ai = self.old_ai send_message('The ' + self.owner.name + ' is no longer confused!', Colors.red)
def _place_object(self, room, max_objects, tag, z_index, mandatory=False): num_objects = max(1, randint( 0, max_objects)) if mandatory else randint(0, max_objects) print("Trying to put {} from {} max {} on room {}".format( num_objects, max_objects, tag, room)) for i in range(num_objects): # choose random spot for this object if type(room) == Room: print("It is a proper room") internals = room.internals positions = [] for y in range(len(internals)): for x in range(len(internals[0])): if internals[y][x] != "#": positions.append(Vector2(x + room.x1, y + room.y1)) tries = 3 coord = choice(positions) while tries or mandatory: if not self.collision_handler.is_blocked(coord.X, coord.Y): print("monster placed", room, coord, tries) break else: coord = choice(positions) tries -= 1 print("could not add monster to map", room, coord, tries) else: print("It is just a rect!") coord = Vector2(randint(room.x1 + 1, room.x2 - 1), randint(room.y1 + 1, room.y2 - 1)) if not self.collision_handler.is_blocked(coord.X, coord.Y): print("Adding monster to map") prototype = self._get_random_object_template(tag) prototype.coord = coord prototype.z_index = z_index prototype.collision_handler = self.collision_handler self.object_pool.append(prototype) else: print("could not add monster to map", room, coord)
def is_blocked(self, x, y): if self.map.get_map()[x][y].blocked: logger.debug( "CollisionHandler [collided_with={} position={}]".format(self.map.get_map()[x][y], Vector2(x, y))) return True # now check for any blocking objects for obj in self.object_pool.get_objects_as_list(): if obj.blocks and obj.coord == Vector2(x, y): logger.debug("CollisionHandler [collided_with={} position={}]".format(obj.name, obj.coord)) return True return False
def set_tile_under_mouse(): global my_map global map_before start = (min(mouse_coord[0], mouse_starting_position[0]), min(mouse_coord[1], mouse_starting_position[1])) end = (max(mouse_coord[0], mouse_starting_position[0]) + 1, max(mouse_coord[1], mouse_starting_position[1]) + 1) if mouse_left_down: if editor_option == 'w': for x in range(len(my_map)): for y in range(len(my_map[0])): my_map[x][y] = Tile(map_before[x][y].blocked, x, y) for x in range(start[0], end[0]): for y in range(start[1], end[1]): try: my_map[x][y].block_sight = not my_map[x][y].block_sight my_map[x][y].blocked = my_map[x][y].block_sight except IndexError: pass else: for x in range(len(my_map)): for y in range(len(my_map[0])): map_before[x][y] = Tile(my_map[x][y].blocked, x, y) if editor_option == 'a': coord = Vector2(*mouse_coord) position = None for n, obj in enumerate(objects): if obj.coord == coord: position = n break if position is not None and position >= 0: del objects[position] else: try: get_cardinal(*coord) obj = GameObject(coord=coord, char='A', color=(255, 255, 255)) objects.append(obj) except Exception as e: print( "You must place the attachment in the borders of the tile template" )
def targeting(self, **kwargs): target_mode, target_tag, range, visible_only, radius = None, None, None, True, 0 for k, v in kwargs.items(): if k == "target_mode": target_mode = v if k == "target_tag": target_tag = v if k == "range": range = v if k == "visible_only": visible_only = v if k == "radius": radius = v if target_mode == "ranged": return [self.target_object(range, target_tag)] elif target_mode == "self": return [self.player] elif target_mode == "closest": return [self.closest_object(range, target_tag)] elif target_mode == "area": x, y = self.target_tile(range) coord = Vector2(x, y) if not x and not y: return [] else: logger.error("target_mode unknown {}".format(target_mode)) return [] ret = [] if target_tag: for obj in self.object_pool.find_by_tag(target_tag): if not visible_only or self.map.is_visible_tile( obj.coord.X, obj.coord.Y): if Vector2.distance(obj.coord, coord) <= radius: ret.append(obj) else: for obj in self.object_pool.get_objects_as_list(): if not visible_only or self.map.is_visible_tile( obj.coord.X, obj.coord.Y): if Vector2.distance(obj.coord, coord) <= radius: ret.append(obj) return ret
def target_object(self, max_range=None, target_tag=None): # returns a clicked monster inside FOV up to a range, or None if right-clicked while True: (x, y) = self.target_tile(max_range) if x is None: # player cancelled return None mouse_coord = Vector2(x, y) # return the first clicked monster, otherwise continue looping if target_tag: for obj in self.object_pool.find_by_tag(target_tag): if obj.coord == mouse_coord and obj != self.player: return obj else: for obj in self.object_pool.get_objects_as_list(): if obj.coord == mouse_coord and obj != self.player: return obj
def __init__(self, root, object_pool=None, map=None, width: int=0, height: int=0, origin: Vector2=None, target: Vector2=None, console: object=None, mouse_controller=None, map_width=None, map_height=None, camera_width=None, camera_height=None, ): self.object_pool = object_pool self.map = map self.root = root if console: self.console = console else: self.console = tdl.Console(width, height) self.origin = origin self.target = target self.height = height self.width = width self.fov_recompute = True self.fov_algorithm = 'SHADOW' self.fov_light_walls = True self.visible_tiles = None self.bars = [] self.game_msg = None self.message_height = None self.message_width = None self.message_origin_x = None self.message_origin_y = None self.map_width = map_width self.map_height = map_height self.camera_height = camera_height self.camera_width = camera_width self.camera_coord = Vector2(0,0) self.mouse_controller = mouse_controller self.extras = []
def get_names_under_mouse(self): # return a string with the names of all objects under the mouse (x, y) = self.camera.camera_coord + Vector2(*self.mouse_coord) # create a list with the names of all objects at the mouse's coordinates and in FOV objects = self.object_pool.get_objects_as_list() names = "" if self.map and self.object_pool: if objects and self.map: names = [ obj.name for obj in objects if obj.coord.X == x and obj.coord.Y == y and ( x, y) in self.map.get_visible_tiles() ] names = ', '.join(names) # join the names, separated by commas else: logger.warning("map or object pool not initialized!") return names.capitalize()
def new_game(): global game_context # Start to setup the object which will handle most of the generally accessed stuff # GameContext will keep setup of the object managers # ObjectPool keeps track of all the objects on the scene # Starting up the collision handler, which manages all the objects collision events # Adding the object pool and the map to the collision handler so they interact game_context = GameContext(next_level=next_level, game_state=ObjectManager.GameState( EGameState.LOADING), real_time=REALTIME, menu=menu) game_context.set_object_pool(ObjectPool.ObjectPool()) # A map constructor, that randomly create rooms with (not yet implemented) many different strategies # Legacy mode makes the map be drawn using chars instead of colored blocks game_context.set_map(make_map(max_rooms=1)) # Before we start the map objects constructor we load the level data that is being hold on a file # With all the information necessary to build the monsters from the template # Map objects constructor is a special factory that randomly populates the map with object templates # and does deal with weighted distributions, It makes everything in place, by reference # Creation of the player player = Character.load(yaml_file=PLAYER_DATA, coord=game_context.map.get_rooms()[0].center(), collision_handler=game_context.collision_handler, inventory=list(), game_state=game_context.game_state) game_context.set_player(player, inventory_width=INVENTORY_WIDTH) game_context.add_extra("dungeon_level", 1) MapObjectsConstructor(game_instance=game_context).load_object_templates( LEVEL_DATA).give_item_for_player("ICFBS01").populate_map() viewport = ObjectManager.ConsoleBuffer( root_view, object_pool=game_context.object_pool, map=game_context.map, width=SCREEN_WIDTH, height=SCREEN_HEIGHT - PANEL_HEIGHT, origin=Vector2.zero(), target=Vector2.zero(), camera_height=SCREEN_HEIGHT - PANEL_HEIGHT, camera_width=SCREEN_WIDTH, map_height=MAP_SIZE[0], map_width=MAP_SIZE[1], mouse_controller=game_context.mouse_controller) lower_gui_renderer = ObjectManager.ConsoleBuffer( root_view, origin=Vector2(0, SCREEN_HEIGHT - PANEL_HEIGHT), target=Vector2(0, 0), width=SCREEN_WIDTH, height=PANEL_HEIGHT, mouse_controller=game_context.mouse_controller) lower_gui_renderer.add_message_console(MSG_WIDTH, MSG_HEIGHT, MSG_X, MSG_Y) lower_gui_renderer.add_bar(1, 1, BAR_WIDTH, 'HP', 'hp', 'max_hp', player.fighter, Colors.light_red, Colors.darker_red) lower_gui_renderer.add_bar(1, 3, BAR_WIDTH, 'XP', 'xp', 'level_up_xp', game_context.player.fighter, Colors.light_blue, Colors.darker_blue) lower_gui_renderer.add_extra(1, 6, 'DUNGEON LEVEL', game_context.get_extra("dungeon_level"), Colors.yellow, None) # a warm welcoming message! Messenger.send_message( 'Welcome stranger! Prepare to perish in the Tombs of the Ancient Kings.', Colors.red) game_context.game_state.set_state(EGameState.PLAYING) game_context.set_camera(viewport) game_context.lower_gui_renderer = lower_gui_renderer Messenger.broadcast_message("game-controller", {EMessage.MONSTERS_LEVEL_UP: None}) return game_context
def center(self): center_x = (self.x1 + self.x2) // 2 center_y = (self.y1 + self.y2) // 2 return Vector2(center_x, center_y)
def handle_keys(self): self.player_action = EAction.DIDNT_TAKE_TURN self.fov_recompute = False user_input = None keypress = False for event in tdl.event.get(): if event.type == 'KEYDOWN': user_input = event keypress = True if event.type == 'MOUSEMOTION': self.mouse_controller.set_mouse_coord(event.cell) if not keypress: return logger.debug("User_Input [key={} alt={} ctrl={} shift={}]".format( user_input.key, user_input.alt, user_input.control, user_input.shift)) if user_input.key == 'ENTER' and user_input.alt: # Alt+Enter: toggle fullscreen tdl.set_fullscreen(not tdl.get_fullscreen()) elif user_input.key == 'ESCAPE': self.player_action = EAction.EXIT return if self.game_state.state == EGameState.PLAYING: self.fov_recompute = False # movement keys if user_input.key == "UP" or user_input.key == "KP8": self.fov_recompute = self.player.move_or_attack(Vector2(0, -1)) self.player_action = EAction.MOVE_UP elif user_input.key == "DOWN" or user_input.key == "KP2": self.fov_recompute = self.player.move_or_attack(Vector2(0, 1)) self.player_action = EAction.MOVE_DOWN elif user_input.key == "LEFT" or user_input.key == "KP4": self.fov_recompute = self.player.move_or_attack(Vector2(-1, 0)) self.player_action = EAction.MOVE_LEFT elif user_input.key == "RIGHT" or user_input.key == "KP6": self.fov_recompute = self.player.move_or_attack(Vector2(1, 0)) self.player_action = EAction.MOVE_RIGHT elif user_input.key == "HOME" or user_input.key == "KP7": self.fov_recompute = self.player.move_or_attack(Vector2( -1, -1)) self.player_action = EAction.MOVE_DIAGONAL_UL elif user_input.key == "PAGEUP" or user_input.key == "KP9": self.fov_recompute = self.player.move_or_attack(Vector2(1, -1)) self.player_action = EAction.MOVE_DIAGONAL_UR elif user_input.key == "END" or user_input.key == "KP1": self.fov_recompute = self.player.move_or_attack(Vector2(-1, 1)) self.player_action = EAction.MOVE_DIAGONAL_DL elif user_input.key == "PAGEDOWN" or user_input.key == "KP3": self.fov_recompute = self.player.move_or_attack(Vector2(1, 1)) self.player_action = EAction.MOVE_DIAGONAL_DR elif user_input.key == "KP5": self.player_action = EAction.WAITING # do nothing ie wait for the monster to come to you elif user_input.text == 'g': # pick up an item for obj in [ item for item in self.object_pool.get_objects_as_list() if (type(item) == Item or type(item) == Equipment) and item.coord == self.player.coord ]: obj.pick_up(self.player) self.object_pool.delete_by_id(obj._id) break elif user_input.text == '<' or user_input.text == '/': # pick up an item for obj in [ stair for stair in self.object_pool.find_by_tag("stairs") if stair.coord == self.player.coord ]: self.extras["dungeon_level"] += 1 self.next_level() self.fov_recompute = True break elif user_input.text == 'i': # show the inventory chosen_item = self.inventory_menu( 'Press the key next to an item to use it, or any other to cancel.\n' ) if chosen_item is not None: chosen_item.use() elif user_input.text == 'd': # show the inventory; if an item is selected, drop it chosen_item = self.inventory_menu( 'Press the key next to an item to' + 'drop it, or any other to cancel.\n') if chosen_item is not None: chosen_item.drop() elif user_input.text == 'c': # show character information self.message_box('Character Information\n\nLevel: ' + str(self.player.fighter.level) + '\nExperience: ' + str(self.player.fighter.xp) + '\nExperience to level up: ' + str(self.player.fighter.level_up_xp) + '\n\nMaximum HP: ' + str(self.player.fighter.max_hp) + '\nAttack: ' + str(self.player.fighter.power) + '\nDefense: ' + str(self.player.fighter.defense))
def load(): global game_context with shelve.open('savegame', 'r') as savefile: map = savefile['map'] objects = savefile['object_pool'] player_id = savefile['player-id'] game_msgs = savefile['game_msgs'] game_state = savefile['game_state'] ais = savefile['ais'] fighters = savefile['fighters'] extras = savefile['extras'] game_context = GameContext(next_level=next_level, game_state=ObjectManager.GameState(game_state), real_time=REALTIME, menu=menu) game_context.extras = extras game_context.set_object_pool(ObjectPool.ObjectPool()) game_context.set_map(map) for k, v in objects.items(): v.collision_handler = game_context.collision_handler if k in fighters.keys(): v.fighter = fighters[k] if v.fighter: v.fighter.owner = v if k in ais.keys(): v.ai = ais[k] if v.ai: v.ai.owner = v v.ai.visible_tiles_ref = map.visible_tiles if k == player_id: v.game_state = game_context.game_state game_context.set_player(v, inventory_width=INVENTORY_WIDTH) else: game_context.object_pool.append(v) inventory = game_context.player.get_inventory() for i in inventory: i.context = game_context viewport = ObjectManager.ConsoleBuffer( root_view, object_pool=game_context.object_pool, map=game_context.map, width=SCREEN_WIDTH, height=SCREEN_HEIGHT - PANEL_HEIGHT, origin=Vector2.zero(), target=Vector2.zero(), camera_height=SCREEN_HEIGHT - PANEL_HEIGHT, camera_width=SCREEN_WIDTH, map_height=MAP_SIZE[0], map_width=MAP_SIZE[1], mouse_controller=game_context.mouse_controller) lower_gui_renderer = ObjectManager.ConsoleBuffer( root_view, origin=Vector2(0, SCREEN_HEIGHT - PANEL_HEIGHT), target=Vector2(0, 0), width=SCREEN_WIDTH, height=PANEL_HEIGHT, mouse_controller=game_context.mouse_controller) lower_gui_renderer.add_message_console(MSG_WIDTH, MSG_HEIGHT, MSG_X, MSG_Y) lower_gui_renderer.game_msg = game_msgs lower_gui_renderer.add_bar(1, 1, BAR_WIDTH, 'HP', 'hp', 'max_hp', game_context.player.fighter, Colors.light_red, Colors.darker_red) lower_gui_renderer.add_bar(1, 3, BAR_WIDTH, 'XP', 'xp', 'level_up_xp', game_context.player.fighter, Colors.light_blue, Colors.darker_blue) game_context.game_state.set_state(EGameState.PLAYING) game_context.set_camera(viewport) game_context.lower_gui_renderer = lower_gui_renderer Messenger.broadcast_message("game-controller", {EMessage.MONSTERS_LEVEL_UP: None})
def next_level(): global game_context # advance to the next level Messenger.send_message( 'You take a moment to rest, and recover your strength.', Colors.light_violet) player = game_context.player player.fighter.heal_percent(0.5) # heal the player by 50% game_context.object_pool.clear_object_pool(keep_player=True) game_context.set_map( make_map(max_rooms=game_context.get_extra("dungeon_level", default=1))) MapObjectsConstructor(game_instance=game_context).load_object_templates( LEVEL_DATA).populate_map() player.coord = game_context.map.get_rooms()[0].center() Messenger.send_message( 'After a rare moment of peace, you descend deeper into the heart of the dungeon...', Colors.red) viewport = ObjectManager.ConsoleBuffer( root_view, object_pool=game_context.object_pool, map=game_context.map, width=SCREEN_WIDTH, height=SCREEN_HEIGHT - PANEL_HEIGHT, origin=Vector2.zero(), target=Vector2.zero(), camera_height=SCREEN_HEIGHT - PANEL_HEIGHT, camera_width=SCREEN_WIDTH, map_height=MAP_SIZE[0], map_width=MAP_SIZE[1], mouse_controller=game_context.mouse_controller) lower_gui_renderer = ObjectManager.ConsoleBuffer( root_view, origin=Vector2(0, SCREEN_HEIGHT - PANEL_HEIGHT), target=Vector2(0, 0), width=SCREEN_WIDTH, height=PANEL_HEIGHT, mouse_controller=game_context.mouse_controller) lower_gui_renderer.add_message_console(MSG_WIDTH, MSG_HEIGHT, MSG_X, MSG_Y) lower_gui_renderer.add_bar(1, 1, BAR_WIDTH, 'HP', 'hp', 'max_hp', player.fighter, Colors.light_red, Colors.darker_red) lower_gui_renderer.add_bar(1, 3, BAR_WIDTH, 'XP', 'xp', 'level_up_xp', game_context.player.fighter, Colors.light_blue, Colors.darker_blue) lower_gui_renderer.add_extra(1, 6, 'DUNGEON LEVEL', game_context.get_extra("dungeon_level"), Colors.yellow, None) game_context.set_camera(viewport) game_context.lower_gui_renderer = lower_gui_renderer Messenger.broadcast_message("game-controller", {EMessage.MONSTERS_LEVEL_UP: None})