def _generate_world(self): """ Private method that handles the procedural generation of the world. It will generate several levels and the levels will be populated with Actors. :return: None """ # Generate a town level level_name = "Town" level_difficulty = 1 Utilities.message( "Creating level: " + level_name + '(difficulty:' + str(level_difficulty) + ')', "GENERATION") town = TownLevel(self, level_difficulty, level_name) self._levels.append(town) self._currentLevel = town # Add some dungeon levels underneath the town # Dungeon levels are connected sequentially prev_level = None for i in range(1, WORLD.DUNGEON_LEVELS + 1): prev_level = self.levels[i - 1] self._add_dungeon_level(i, [prev_level]) # Add some cave levels # Caves are connected to town and to some other random level for i in range(1, WORLD.CAVE_LEVELS + 1): random_level = random.choice(self.levels) self._add_cave_level(2, [town, random_level])
def _add_dungeon_level(self, difficulty, connected_levels): """ Private method to add a dungeon level to the world. :param difficulty: Difficulty for the new dungeon level :param connected_levels: Levels to which the new dungeon level will be connected :return : None """ level_name = 'Dungeon level ' + str(difficulty) Utilities.message( "Creating level: " + level_name + '(difficulty:' + str(difficulty) + ')', "GENERATION") dungeon_level = DungeonLevel(self, difficulty, level_name) self._levels.append(dungeon_level) for lvl in connected_levels: # Add portal in previous level to current level down_portal = Portal( '>', 'stairs down', 'You follow the stairs down, looking for more adventure.') down_portal.sprite_id = SPRITES.STAIRS_DOWN down_portal.moveToLevel(lvl, lvl.getRandomEmptyTile()) # Add portal in current level to previous level up_portal = Portal( '<', 'stairs up', 'You follow the stairs up, hoping to find the exit.') up_portal.sprite_id = SPRITES.STAIRS_UP up_portal.moveToLevel(dungeon_level, dungeon_level.getRandomEmptyTile()) # Connect the two portals down_portal.connectTo(up_portal)
def _add_cave_level(self, difficulty, connected_levels): """ Private method to add a cave level to the world. :param difficulty: Difficulty for the new cave level :param connected_levels: Levels to which the new cave level will be connected :return : None """ level_name = 'Cave of the Cannibal' Utilities.message( "Creating level: " + level_name + '(difficulty:' + str(difficulty) + ')', "GENERATION") cave_level = CaveLevel(self, difficulty, level_name) self._levels.append(cave_level) # For each connected level for lvl in connected_levels: # create a portal in the connected level that leads to the new cave pit_message = 'You jump into the pit. As you fall deeper and deeper, you realize you didn\'t ' \ 'think about how to get back out afterward...' down_portal = Portal('>', 'Pit', pit_message) down_portal.moveToLevel(lvl, lvl.getRandomEmptyTile()) # create a portal in the new cave that leads back up_portal = Portal( '<', 'Opening above', 'After great difficulties you manage to get out of the pit.') up_portal.moveToLevel(cave_level, cave_level.getRandomEmptyTile()) # connect the two portals down_portal.connectTo(up_portal)
def __getattr__(self, attribute): """ Generic getter that serves up the json attributes as if it were attributes of this object. :param attribute: attribute name :return: attribute value from json definition. """ try: return self.json[attribute] except KeyError as e: Utilities.message("ERROR: Requested attribute " + attribute + "is not present in json.", "NETWORK") raise e
def receive_from_server(self): message = self.receive() if message is not None: Utilities.message("Received: " + str(message), "NETWORK") for header, json in message.items(): if header == "Player": self._player = Proxy(json) elif header == "Level": self._current_level = Proxy(json) elif header == "Message": # Write directly to messageBuffer, using message() would bounce loop the message back to server. Utilities.messageBuffer.append(json["text"]) else: Utilities.message("WARNING: Missing implementation for header " + header, "NETWORK")
def updateFieldOfView(self, x, y): """ Update the map tiles with what is in field of view, marking those as explored. """ view_range = self.range_of_view for tx, ty in self.each_map_position: tile = self.tiles[tx][ty] dist = Utilities.distance_between_points(x, y, tx, ty) visible = dist <= view_range line_of_sight = Utilities.line_of_sight( self.solidTileMatrix, x, y, tx, ty) if visible and line_of_sight: tile.inView = True tile.explored = True else: tile.inView = False # set all actors as in view too for actor in tile.actors: actor.inView = visible and line_of_sight
def __init__(self): """ Constructor to create a new world :return : World object """ # Initialize class variables self._players = [] self._levels = [] self._world_time = 0 # Running total of game time (in milliseconds) self._tick_time = 0 # Time spent in the current tick (in milliseconds) self._tick_speed = GAME.SPEED # Speed of game ticks in milliseconds (how fast the game moves) # Initialize libraries self._monsterLibrary = MonsterLibrary() self._itemLibrary = ItemLibrary() # Clean up Utilities.reset_utility_queues() # Procedural generation of the world self._generate_world()
def new_player(self): """ Adds a new player to the world. :return : Player """ player = Player() self.players.append(player) first_level = self.levels[0] player.moveToLevel(first_level, first_level.getRandomEmptyTile()) # Starting gear potion = self.item_library.create_item("healingpotion") player.addItem(potion) potion = self.item_library.create_item("healingpotion") player.addItem(potion) # TODO: multiplayer - move FOV code into player (it will be different for every player) first_level.map.updateFieldOfView(player.tile.x, player.tile.y) # Quick start if GAME.QUICK_START: town = self.levels[0] # Group portals together i = 1 for portal in town.portals: if portal.destinationPortal.level not in town.subLevels: tile = town.map.tiles[1][i] i += 1 portal.moveToTile(tile) # Move player close to portals tile = town.map.tiles[len(self.players) + 1][1] player.moveToTile(tile) # Provide more starting gear scroll = self.item_library.create_item("firenova", "double") player.addItem(scroll) scroll = self.item_library.create_item("tremor") player.addItem(scroll) potion = self.item_library.create_item("healingvial", "exquisite") player.addItem(potion) cloak = self.item_library.create_item("cloak") player.addItem(cloak) scroll = self.item_library.create_item("fireball") player.addItem(scroll) scroll = self.item_library.create_item("confuse") player.addItem(scroll) scroll = self.item_library.create_item("lightning") player.addItem(scroll) # Add a chest with extra gear chest = Chest() tile = town.map.tiles[len(self.players) + 1][2] chest.moveToTile(tile) for i in range(1, 9): item = self.item_library.get_random_item(i) chest.inventory.add(item) # Send welcome message to the player Utilities.message( 'You are ' + player.name + ', a young and fearless adventurer. It is time to begin your ' + 'legendary and without doubt heroic expedition into the ' + 'unknown. Good luck!', "GAME") return player
def generate_map(self): """ generate a randomized dungeon map with a minimum of two rooms """ # Clear existing rooms self.clear_rooms() # Constants used to generate map room_max_size = DUNGEON.ROOM_MAX_SIZE room_min_size = DUNGEON.ROOM_MIN_SIZE max_rooms = DUNGEON.MAX_ROOMS if self.width < room_max_size or self.height < room_max_size: raise Utilities.GameError("Requested size is too small, can't generate dungeon.") # Create a new map with empty tiles self._tiles = [[Tile(self, x, y) for y in range(self. height)] for x in range(self. width)] # Block all tiles for y in range(self.height): for x in range(self.width): t = self.tiles[x][y] t.blocked = True t.blockSight = True t.color = DUNGEON.COLOR_WALL t.material = MaterialType.STONE # Cut out rooms (minimum 2) rooms_to_generate = random.randint(2, max_rooms) while len(self.rooms) < rooms_to_generate: # Random width and height w = random.randrange(room_min_size, room_max_size) h = random.randrange(room_min_size, room_max_size) # Random position without going out of the boundaries of the map x = random.randrange(0, self.width - w - 1) y = random.randrange(0, self.height - h - 1) # Create a new room new_room = Room(self, x, y, w, h) # Abort if room intersects with existing room intersects = False for other_room in self.rooms: if new_room.intersect(other_room): intersects = True break if intersects is True: break # Cut it out of the map, go through the tiles in the room and make them passable for x in range(new_room.x1 + 1, new_room.x2): for y in range(new_room.y1 + 1, new_room.y2): self.tiles[x][y].blocked = False self.tiles[x][y].blockSight = False self.tiles[x][y].color = DUNGEON.COLOR_FLOOR self.tiles[x][y].material = MaterialType.DIRT # Create corridor towards previous room (new_x, new_y) = new_room.center # All rooms, after the first room, connect to the previous room if len(self.rooms) > 0: # Center coordinates of previous room prev_room = self.rooms[len(self.rooms) - 1] (prev_x, prev_y) = prev_room.center # Create a corridor: First move horizontally, then vertically self._create_horizontal_tunnel(prev_x, new_x, new_y) self._create_vertical_tunnel(prev_y, new_y, prev_x) # Finally, append the new room to the list self.rooms.append(new_room) # Set entry and exit tiles (entryX, entryY) = self.rooms[0].center self._entryTile = self._tiles[entryX][entryY] (exitX, exitY) = self.rooms[len(self.rooms) - 1].center self._exitTile = self._tiles[exitX][exitY] # Assign texture ID based on texture hash # The assignments below are calculated using a helper spreadsheet for x in range(self.width): for y in range(self.height): t = self.tiles[x][y] h = t.texture_hash # Map hash to a tileset ID if not self.tiles[x][y].blockSight: t.texture_id = SPRITES.TILE_EMPTY if random.random() < 0.05: t.texture_id = SPRITES.TILE_SUBTILES if random.random() < 0.05: t.texture_id = SPRITES.TILE_LINED if random.random() < 0.05: t.texture_id = SPRITES.TILE_CRACKED elif h in [16, 511]: t.texture_id = SPRITES.PILLAR elif h in [24, 25, 88, 89]: t.texture_id = SPRITES.NS_WALL_W_CAP elif h in [56, 57, 60, 63, 120, 121, 124, 125, 127, 312, 313, 316, 317, 319, 377, 380, 381, 383, 504, 505, 508, 509]: t.texture_id = SPRITES.NS_WALL elif h in [48, 52, 304, 308]: t.texture_id = SPRITES.NS_WALL_E_CAP elif h in [18, 19, 22, 23]: t.texture_id = SPRITES.EW_WALL_N_CAP elif h in [146, 147, 150, 151, 210, 214, 215, 219, 223, 402, 403, 407, 438, 439, 466, 467, 470, 471, 475, 479, 502, 503]: t.texture_id = SPRITES.EW_WALL elif h in [144, 208, 400, 464]: t.texture_id = SPRITES.EW_WALL_S_CAP elif h in [26, 27, 30, 31, 90, 91, 94, 95, 283, 510]: t.texture_id = SPRITES.NW_CORNER elif h in [50, 51, 54, 55, 118, 306, 307, 310, 311, 507]: t.texture_id = SPRITES.NE_CORNER elif h in [152, 153, 216, 217, 220, 408, 409, 447, 472, 473]: t.texture_id = SPRITES.SW_CORNER elif h in [176, 180, 240, 244, 255, 432, 433, 436, 496, 500]: t.texture_id = SPRITES.SE_CORNER elif h in [186, 187, 190, 250, 254, 442, 443]: t.texture_id = SPRITES.CROSS elif h in [58, 59, 62, 122, 123, 126, 314, 315, 318, 378, 379, 382, 506]: t.texture_id = SPRITES.T_SOUTH elif h in [178, 179, 182, 183, 242, 243, 246, 247, 251, 434, 435, 498, 499]: t.texture_id = SPRITES.T_WEST elif h in [154, 155, 158, 159, 218, 222, 410, 411, 414, 415, 446, 474, 478]: t.texture_id = SPRITES.T_EAST elif h in [184, 185, 188, 189, 191, 248, 249, 252, 253, 440, 441, 444, 445]: t.texture_id = SPRITES.T_NORTH else: print("WARNING: Unknown hash " + str(h) + ", can't assign tileset ID.")
def generate_map(self): """ Place holder function, subclass must provide actual implementation. """ raise Utilities.GameError("Can't use Map class directly, use a subclass!")