class AI: """ Base class for all AIs. AI is a component and so must have owner. """ ais = enums.enum('base', 'player_control', 'basic_monster') def __init__(self, owner): self.owner = owner def get_type(self): """ Returns the type of ai Used by Actor's act() to supply proper arguments children should redefine this """ return AI.ais.base def work(self): """ Returns True if this Actor's turn is made. children should redefine this """ raise NotImplementedError("Subclass must implement abstract method")
class Entity: """ This is a generic game entity that can be assigned to map. It can't act on its own. Actors inherit from it (and items, projectiles will) """ types = enums.enum('Entity', 'Actor') def __init__(self, char, name, color, blocks): self.char = char self.color = color self.name = name # map-specific stuff # these are none, added when zone sets them self.x = None self.y = None self.zone = None # whether it blocks passage or not self.blocks = blocks def get_type(self): """ Returns own type of entity in Entity.types.* Each child redefines this. """ #raise NotImplementedError("Subclass must implement abstract method") return Entity.types.Entity def set_zone(self, zone, x, y): """ Sets entity's zone and positions the object at given coords. """ self.zone = zone self.x = x self.y = y
try: from pervasive import PervasiveDisplay except ImportError: # this will allow derived classes that do not depend on # PervasiveDisplay def PervasiveDisplay(): return None from PIL import Image, ImageFont, ImageDraw from pil2epd import convert from fontlist import FontList from enums import enum directions = enum(x=0, y=1) char_width = 9.0 char_height = 18.0 line_width = 4.0 def chars_to_pixels(chars, direction=directions.x): if direction == directions.x: return chars * char_width elif direction == directions.y: return chars * char_height else: raise Exception("Direction must be x or y.")
from enums import enum keystates = enum(down=1, up=0, hold=2) buckies = [ "KEY_LEFTSHIFT", "KEY_RIGHTSHIFT", "KEY_LEFTCTRL", "KEY_RIGHTCTRL", "KEY_CAPSLOCK", "KEY_LEFTALT", "KEY_RIGHTALT" ] alphabet = "abcdefghijklmnopqrstuvwxyz" numeric_keys = "0123456789)!@#$%^&*(" char_keys = { "KEY_GRAVE": "`", "S-KEY_GRAVE": "~", "KEY_SPACE": " ", "KEY_MINUS": "-", "S-KEY_MINUS": "_", "KEY_EQUAL": "=", "S-KEY_EQUAL": "+", "KEY_SEMICOLON": ";", "S-KEY_SEMICOLON": ":", "KEY_APOSTROPHE": "'", "S-KEY_APOSTROPHE": '"', "KEY_COMMA": ",", "S-KEY_COMMA": "<", "KEY_DOT": ".", "S-KEY_DOT": ">", "KEY_SLASH": "/", "S-KEY_SLASH": "?", "KEY_LEFTBRACE": "[", "S-KEY_LEFTBRACE": "{",
def __str__(self): return '%s, errors: %s' % (self.response_code, ', '.join(str(e) for e in self.errors)) @classmethod def server_error(cls, errors=()): return ServiceException(ResponseCode.SERVER_ERROR.name, errors) @classmethod def request_error(cls, errors=()): return ServiceException(ResponseCode.REQUEST_ERROR.name, errors) class ServiceRequest(serializable.Serializable): pass ResponseCode = enums.enum('SUCCESS', 'SERVER_ERROR', 'REQUEST_ERROR') class ServiceResponse(serializable.Serializable): PUBLIC_FIELDS = serializable.fields('response_code', serializable.objlistf('errors', ServiceError)) def __init__(self, response_code=None, errors=()): self.response_code = response_code self.errors = errors class MethodDescriptor(object): def __init__(self, method_name, request_class, response_class): self.method_name = method_name self.request_class = request_class self.response_class = response_class
class KeyHandler: """Handles Key through libtcode""" controls = enums.enum('none', 'other', 'exit', 'fullscreen') cheats = enums.enum('none', 'other', 'teleport', 'reveal_map') game = enums.enum('none', 'other', 'move_N', 'move_NE', 'move_E', 'move_SE', 'move_S', 'move_SW', 'move_W', 'move_NW', 'move_5') def __init__(self): pass def process_control_keys(self, key): """ Checks if passed key corresponds to control action. if so, return control action const. """ if key.vk == libtcod.KEY_NONE: logger.log(logger.types.input, "key none") return KeyHandler.controls.none elif key.vk == libtcod.KEY_ESCAPE: logger.log(logger.types.input, "key escape") return KeyHandler.controls.exit elif key.vk == libtcod.KEY_ENTER and key.lalt: logger.log(logger.types.input, "key alt+enter") return KeyHandler.controls.fullscreen else: # some other key return KeyHandler.controls.other def process_cheat_keys(self, key): """ Checks if passed key corresponds to cheat action. if so, returns cheat action const. """ if key.vk == libtcod.KEY_NONE: return KeyHandler.cheats.none elif key.vk == libtcod.KEY_1 and key.lctrl: return KeyHandler.cheats.teleport elif key.vk == libtcod.KEY_2 and key.lctrl: return KeyHandler.cheats.reveal_map else: return KeyHandler.cheats.other def process_game_key(self, key): """ Checks if passed key corresponds to if passed key corresponds to any game action (that takes a turn). If so, returns cheat action const. """ if libtcod.console_is_key_pressed( libtcod.KEY_UP) or libtcod.console_is_key_pressed( libtcod.KEY_KP8): return KeyHandler.game.move_N elif libtcod.console_is_key_pressed( libtcod.KEY_DOWN) or libtcod.console_is_key_pressed( libtcod.KEY_KP2): return KeyHandler.game.move_S elif libtcod.console_is_key_pressed( libtcod.KEY_LEFT) or libtcod.console_is_key_pressed( libtcod.KEY_KP4): return KeyHandler.game.move_W elif libtcod.console_is_key_pressed( libtcod.KEY_RIGHT) or libtcod.console_is_key_pressed( libtcod.KEY_KP6): return KeyHandler.game.move_E elif libtcod.console_is_key_pressed(libtcod.KEY_KP7): return KeyHandler.game.move_NW elif libtcod.console_is_key_pressed(libtcod.KEY_KP9): return KeyHandler.game.move_NE elif libtcod.console_is_key_pressed(libtcod.KEY_KP1): return KeyHandler.game.move_SW elif libtcod.console_is_key_pressed(libtcod.KEY_KP3): return KeyHandler.game.move_SE elif libtcod.console_is_key_pressed( libtcod.KEY_KP5) or libtcod.console_is_key_pressed( libtcod.KEY_5): return KeyHandler.game.move_5 elif key.vk == libtcod.KEY_NONE: return KeyHandler.game.none else: return KeyHandler.game.other def wait_for_key(self): """wait for a key and return it""" return libtcod.console_wait_for_keypress(True)
SHOW_MOVEMENT = False SHOW_LEVEL_GEN = False SHOW_RENDERING = False SHOW_COMBAT = True SHOW_AI = False # error log switch VERBOSITY_ERROR = True # game log switch VERBOSITY_GAME = True # suppressor based on message class types = enums.enum('cheats', 'movement', 'level_gen', 'rendering', 'input', 'ai', 'combat') # rendering - rendering-related stuff (consoles, e.t.c.) # input - various keypresses (actions) # cheats - cheat codes # level_gen - level generation # ai - ai related stuff # movement - movement and collisions # combat - getting and receiving damage def log(subtype, message): """Output debug log message of given type""" if VERBOSITY_DEBUG: if subtype == types.cheats and not SHOW_CHEATS:
import math from datetime import date, datetime from threading import Thread from paperui.key_events import ExclusiveKeyReader from paperui.core import * from enums import enum from paperui.keyboard import KeyTranslator from paperui.text_wrapper import TextWrapper align = enum(left=-1, center=0, right=1) def visible_text(text, writable_length, alignment=align.left): if len(text) < writable_length: if alignment == align.left: return text.ljust(writable_length) elif alignment == align.right: return text.rjust(writable_length) elif alignment == align.center: return text.center(writable_length) else: return text[0:writable_length] class WidgetSanityError(Exception): pass class Connectable(object): def __init__(self):
class Actor(Entity): """ An entity that can act, take turns and have stats dict. act() is called by engine each turn ai component must be passed as a constant: entities.AI.ais.basic_monster * ai - ai to use * design - design to use (hardcoded for now, later can be loaded) * stats['hp'] - stats are a dict """ designs = enums.enum('orc', 'troll', 'player') def __init__(self, char, name, color, blocks, ai, design): # call Entity's constructor class to setup the child Entity.__init__(self, char, name, color, blocks) # setup ai (it is a component, so will need an owner) if ai == AI.ais.player_control: # this ai just asks player for input self.ai = PlayerControl(owner=self) logger.log(logger.types.ai, "player_control ai assigned to " + self.name) elif ai == AI.ais.basic_monster: self.ai = BasicMonster(owner=self) logger.log(logger.types.ai, "basic_monster ai assigned to " + self.name) else: logger.error("Can't pick an ai for an actor") # setup stats based on design, hardcoded (later take from data files) if design == Actor.designs.player: self.stats = dict(max_hp=30, hp=30, defense=2, power=5) elif design == Actor.designs.orc: self.stats = dict(max_hp=10, hp=10, defense=2, power=5) elif design == Actor.designs.troll: self.stats = dict(max_hp=16, hp=16, defense=1, power=4) else: logger.error("Can't pick design for an actor") def get_type(self): """ Returns own type of entity in Entity.types.*. For now is used by main to call actor's act(). """ return Entity.types.Actor def act(self, act_data): """ This method is called each turn for all actors in the map. For now it only gets ai to work based on its type. act_data - dict containing all the stuff """ # for now we only need to call an ai properly ai_type = self.ai.get_type() if ai_type == AI.ais.player_control: # no inbound params for player result = self.ai.work(player_action=act_data['player_action']) elif ai_type == AI.ais.basic_monster: # we need to feed player and fov_map to it result = self.ai.work(player=act_data['player'], fov_map=act_data['fov_map']) else: logger.error('Unknown ai type') # skip its turn just in case result = True # true if turn was made, false if not return result def move(self, dx, dy): """move by the given amount""" # check if blocked by wall or entity if not self.zone.is_blocked(self.x + dx, self.y + dy): # not blocked, move there self.x += dx self.y += dy logger.log(logger.types.movement, self.name + " moved to " + str( (self.x, self.y))) else: # tile is blocked, let's try to bump into object! entity = self.zone.entity_at(self.x + dx, self.y + dy) if not entity is None: #it is an entity self.bump(entity) else: #it is a wall logger.log(logger.types.movement, 'The ' + self.name + ' bumps into a wall. Ugh.') pass def step_towards(self, target_x, target_y): """ Moves one tile towards target coords """ #vector from this object to the target, and distance dx = target_x - self.x dy = target_y - self.y distance = math.sqrt(dx**2 + dy**2) #normalize it to length 1 (preserving direction), then round it and #convert to integer so the movement is restricted to the map grid dx = int(round(dx / distance)) dy = int(round(dy / distance)) self.move(dx, dy) def distance_to(self, other): """ Returns distance to another object """ dx = other.x - self.x dy = other.y - self.y return math.sqrt(dx**2 + dy**2) def bump(self, ent): """bumps into an entity (this gets called for entities only, not for walls)""" # this should check for factions ) # FIXME: attacking should be handled inside player! here only spontaneous collisions! # works only for actors. logger.log(logger.types.movement, "Bump called for " + self.name + " and " + ent.name) if (ent.get_type() == Entity.types.Actor): # if player, attack any monster if (self.ai.get_type() == AI.ais.player_control) and (ent.ai.get_type() == AI.ais.basic_monster): self.attack(ent) def take_damage(self, damage): """apply damage if possible""" if damage > 0: self.stats['hp'] -= damage logger.log( logger.types.combat, self.name + ' now has ' + str(self.stats['hp']) + ' hp of ' + str(self.stats['max_hp'])) def attack(self, target): """ attack another actor convert to send_damage eventually """ #a simple formula for attack damage damage = self.stats['power'] - target.stats['defense'] if damage > 0: #make the target take some damage logger.game(self.name.capitalize() + ' attacks ' + target.name + ' for ' + str(damage) + ' hit points.') target.take_damage(damage) else: logger.game(self.name.capitalize() + ' attacks ' + target.name + ' but it has no effect!')
SHOW_CHEATS = True SHOW_MOVEMENT = False SHOW_LEVEL_GEN = False SHOW_RENDERING = False SHOW_COMBAT = True SHOW_AI = False # error log switch VERBOSITY_ERROR = True # game log switch VERBOSITY_GAME = True # suppressor based on message class types = enums.enum('cheats', 'movement', 'level_gen', 'rendering', 'input', 'ai', 'combat') # rendering - rendering-related stuff (consoles, e.t.c.) # input - various keypresses (actions) # cheats - cheat codes # level_gen - level generation # ai - ai related stuff # movement - movement and collisions # combat - getting and receiving damage def log(subtype, message): """Output debug log message of given type""" if VERBOSITY_DEBUG: if subtype == types.cheats and not SHOW_CHEATS: return
from PIL import Image, ImageFont, ImageDraw from enums import enum from paperui import ui from paperui.core import * from threading import Thread import math orientations = enum(landscape=0, portrait=1) class Line(object): def __init__(self, text, size): self.text = text self.size = size class Page(list): def __init__(self, size): list.__init__(self) self.size = size if self.size[0] < self.size[1]: self.orientation = orientations.portrait else: self.orientation = orientations.landscape def to_image(self, font, margin): image = Image.new("1", self.size, 1) drawer = ImageDraw.Draw(image) x, y = margin, 0