import os.path import yaml import textwrap import math import libtcodpy as libtcod import glob libtcod.console_set_keyboard_repeat(500, 50) for fil in glob.glob('./data/namegen/*.cfg'): libtcod.namegen_parse(fil) help = ''' 'i': Inventory 'd': Drop 'g': Get item (Pick up) '?': Help Alt+Escape: Exit Arrow Keys for movement / selecting Name of item under the mouse shown above the health bar ''' from game import GameBase import levels import objects import utilities if __name__ == 'main': class Null: pass class SettingsObject(object): def __init__(self, setting_name, default=Null): self.setting_name = setting_name self.default = default def __get__(self, instance, owner): result = instance.settings.get(self.setting_name, self.default) if result is Null: raise KeyError('%s is not specified in the configuration' % self.setting_name) return result class Game(GameBase): #actual size of the window def load_settings(self): self.settings = yaml.safe_load( file(os.path.join('./data/main.yml')) ) if self.settings == None: self.settings = {} SCREEN_WIDTH = SettingsObject('screen_width', 80) SCREEN_HEIGHT = SettingsObject('screen_height', 50) PANEL_HEIGHT = 15 INVENTORY_WIDTH = 50 @property def MAP_WIDTH(self): return self.SCREEN_WIDTH @property def MAP_HEIGHT(self): return self.SCREEN_HEIGHT - (self.PANEL_HEIGHT + 2) BAR_WIDTH = 25 MSG_X = BAR_WIDTH + 2 MSG_HEIGHT = PANEL_HEIGHT @property def PANEL_Y(self): return self.SCREEN_HEIGHT - self.PANEL_HEIGHT @property def MSG_WIDTH(self): return self.SCREEN_WIDTH - self.MSG_X @property def MSG_HEIGHT(self): return self.PANEL_HEIGHT - 1 ROOM_MIN_SIZE = SettingsObject('room_min_wall_length', 4) ROOM_MAX_SIZE = SettingsObject('room_max_wall_length', 7) MAX_ROOMS = SettingsObject('max_number_rooms', 10) MAX_ROOM_MONSTERS = SettingsObject('max_number_room_monsters', 6) MAX_ROOM_ITEMS = SettingsObject('max_number_room_items', 3) CONFUSE_NUM_TURNS = 17 LIMIT_FPS = 20 #frames-per-second maximum def __init__(self): self.load_settings() GameBase.__init__(self, 'caer flinding', self.SCREEN_WIDTH, self.SCREEN_HEIGHT) self.select_cb = None self.panel = libtcod.console_new(self.SCREEN_WIDTH, self.PANEL_HEIGHT) self.levels = [] self.current_level = 0 self.levels = [levels.Level(self.MAP_WIDTH, self.MAP_HEIGHT, self.con, self.item_types, self.monster_types)] x,y = None,None self.player = objects.Player(self.level.map, self.con, x,y, '@', libtcod.white, fighter=objects.Fighter(hp=40, defense = 2, power = 11, death_function=self.player_death) ) def player_death(self, player): self.message('You died!', libtcod.red) self.game_state = 'dead' player.char = '%' player.color = libtcod.dark_red def setup_map(self): self.player.enter_level(self.level) self.level.setup(self.MAX_ROOMS, self.ROOM_MIN_SIZE, self.ROOM_MAX_SIZE, self.MAX_ROOM_MONSTERS, self.MAX_ROOM_ITEMS ) self.set_fov() def set_fov(self): self.player.enter_level(self.level) self.level.init_fov() self.level.recompute_fov(True) item_types = {} @classmethod def register_item_type(cls, chance): def _inner(typ): cls.item_types[typ] = chance return typ return _inner monster_types = {} @classmethod def register_monster_type(cls, typ, chance): cls.monster_types[typ] = chance return typ @property def level(self): return self.levels[self.current_level] @property def map(self): return self.level.map def change_level(self, down=True): change = 1 if down else -1 self.current_level += change if ( (self.current_level < 0 and not down) or (self.current_level >= len(self.levels)) ): new_level = levels.Level( self.MAP_WIDTH, self.MAP_HEIGHT, self.con, self.item_types, self.monster_types ) if down: self.levels.append(new_level) else: self.levels.insert(0,new_level) self.current_level = self.levels.index(new_level) self.setup_map() else: self.set_fov() def main(self): self.message('Welcome %s! Prepare to perish in the Tombs of the Ancient Kings.' % self.player.name, libtcod.red ) for x in GameBase.main(self): if x == 1: if ( self.game_state in ('playing','selecting') and self.player_action != 'didnt-take-turn' ): for object in self.level.objects: if object.ai: object.clear() object.ai.take_turn() for object in self.level.objects: object.clear() if self.game_state == 'selecting': self.cursor.clear() elif x == 2: if self.player_action == 'move': self.player.tick() def do_playing(self): if self.player_action != 'didnt-take-turn': for object in self.level.iter_objects(): if object.ai: object.clear() object.ai.take_turn() self.player.clear() if self.player_action == 'move': self.player.tick() def do_selecting(self): self.cursor.clear() def do_dead(self): pass mvkeyhandler = utilities.MovementKeyListener() @mvkeyhandler.up def mvkeyhandler(self): self.player.move_or_attack(0,-1) return 'move' @mvkeyhandler.down def mvkeyhandler(self): self.player.move_or_attack(0, 1) return 'move' @mvkeyhandler.left def mvkeyhandler(self): self.player.move_or_attack(-1, 0) return 'move' @mvkeyhandler.right def mvkeyhandler(self): self.player.move_or_attack(1, 0) return 'move' @mvkeyhandler.handle('i') def mvkeyhandler(self): item = self.inventory_menu('Choose item to use\n') if item is not None: item.bind_game(self) self.player.use(item) @mvkeyhandler.handle('d') def mvkeyhandler(self): chosen_item = self.inventory_menu('Choose item to drop\n') if chosen_item is not None: self.player.drop(chosen_item.owner) @mvkeyhandler.handle('n') def mvkeyhandler(self): chosen_item = self.inventory_menu('Choose item to unmod\n') if chosen_item is not None: data = chosen_item.mods.keys() index = self.menu('Choose \nmod to \nundo\n', data, self.INVENTORY_WIDTH) if index is not None: self.player.unmodify(chosen_item.name, data[index]) @mvkeyhandler.handle('m') def mvkeyhandler(self): chosen_item = self.inventory_menu('Choose item to mod\n') if chosen_item is not None: chosen_mod = self.mod_menu('Choose mod to apply\n') if chosen_mod is not None: self.player.modify(chosen_item.name, chosen_mod.name) @mvkeyhandler.handle('g') def mvkeyhandler(self): for obj in self.level.iter_objects(): if obj.x == self.player.x and obj.y == self.player.y and obj.item: self.player.pick_up(obj) @mvkeyhandler.handle('<') def mvkeyhandler(self): self.change_level(down=False) @mvkeyhandler.handle('>') def mvkeyhandler(self): self.change_level(down=True) @mvkeyhandler.handle('?') def mvkeyhandler(self): self.menu(help, [], 50) selectkeyhandler = utilities.MovementKeyListener() @selectkeyhandler.up def selectkeyhandler(self): self.cursor.y -= 1 @selectkeyhandler.down def selectkeyhandler(self): self.cursor.y += 1 @selectkeyhandler.left def selectkeyhandler(self): self.cursor.x -= 1 @selectkeyhandler.right def selectkeyhandler(self): self.cursor.x += 1 @selectkeyhandler.handle(libtcod.KEY_ENTER) def selectkeyhandler(self): self.select_cb(self.cursor.x, self.cursor.y) self.cursor.clear() self.game_state = 'playing' def render_all(self): for obj in self.level.iter_objects(): if obj != self.player: obj.draw(self.player) self.player.draw() if self.game_state == 'selecting': self.cursor.draw() if self.player.fov_recompute: self.level.recompute_fov() libtcod.console_blit(self.con, 0,0, self.SCREEN_WIDTH,self.SCREEN_HEIGHT, 0,0, 0) libtcod.console_set_default_background(self.panel, libtcod.black) libtcod.console_clear(self.panel) utilities.render_bar(self.panel, 1,1, self.BAR_WIDTH, 'HP', self.player.fighter.hp, self.player.fighter.max_hp, libtcod.red, libtcod.darker_red ) libtcod.console_print_ex(self.panel, self.BAR_WIDTH/2,3, libtcod.BKGND_NONE, libtcod.CENTER, '%s p %s d' %(self.player.fighter.power, self.player.fighter.defense) ) fps = libtcod.sys_get_fps() libtcod.console_print_ex(self.panel, self.BAR_WIDTH/2,4, libtcod.BKGND_NONE, libtcod.CENTER, '%s FPS' %(libtcod.sys_get_fps()) ) libtcod.console_set_default_foreground(self.panel, libtcod.light_gray) libtcod.console_print(self.panel, 1, 0, self.get_names_under_mouse()) y = 1 for line, color in self.game_msgs: libtcod.console_set_default_foreground(self.panel, color) libtcod.console_print_ex(self.panel, self.MSG_X, y, libtcod.BKGND_NONE, libtcod.LEFT, line) y += 1 libtcod.console_blit(self.panel, 0,0, self.SCREEN_WIDTH,self.PANEL_HEIGHT, 0,0, self.PANEL_Y) def main_menu(self): message = ( 'Welcome to YinJAR: is not Just Another Roguelike (WIP)', '', 'Choose an option:', '', ) options = ['Resume', 'Play', 'Exit'] result = self.menu('\n'.join(message), options, len(message[0])) if result is not None: return options[result] game_instance = Game() from monsters import MonsterLoader if __name__ == '__main__': from main import game_instance from items import * from utilities import * from maps import * from objects import * from monsters import * from functools import partial render_bar = partial(render_bar, game_instance.panel) ml = MonsterLoader(os.path.join('.','data','monsters')) ml.load_monsters() il = ItemLoader(os.path.join('.','data','items')) il.load_items() game_instance.load_settings() action = game_instance.main_menu() libtcod.sys_set_fps(Game.LIMIT_FPS) while action is None or action.lower() != 'exit': if action is not None: if action.lower() == 'play': game_instance.setup_map() if action.lower() in {'play', 'resume'}: game_instance.main() libtcod.console_clear(0) action = game_instance.main_menu()