Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
def __init__(self, *, open=False):
self.open = open
# TODO maybe this merits a check rule? maybe EVERYTHING does.
# TODO only if closed
@Open.perform(Openable)
def do_open(event, openable):
openable.open = True
class ILockable(IComponent):
locked = static_attribute("""Whether I'm currently locked.""")
class Lockable(Component, interface=ILockable):
def __init__(self, *, locked=False):
self.locked = locked
# TODO maybe this merits a check rule? maybe EVERYTHING does.
# TODO only if closed
@Unlock.perform(Lockable)
def do_unlock(event, lockable):
# TODO check that the key is a key, player holds it, etc. (inform has
# touchability rules for all this...)
lockable.locked = False
# Destroy the key. TODO: need to be able to tell an entity that i'm taking
# it away from whatever owns it, whatever that may mean! inform's "now"
# does this
IContainer(event.actor).inventory.remove(event.agent)
def __contains__(self, component):
"""Returns True iff this entity supports the given component or
interface.
"""
if issubclass(component, Component):
try:
my_component = self.type.components[component.interface]
except KeyError:
return False
else:
return issubclass(my_component, component)
else:
return component in self.type.components
# probably, instead of special-casing here (yikes)
# TODO i am not actually sure if using an exception here is a good idea
from flax.entity import Player
if combatant.entity.isa(Player):
raise GameOver("you died :(", success=False)
event.world.current_map.remove(combatant.entity)
# TODO and drop inventory, and/or a corpse
@Die.announce(Combatant)
def announce_die(event, combatant):
log.info("{} has died".format(combatant.entity.type.name))
class Breakable(Component, interface=ICombatant):
def __typeinit__(self, *, health):
self.maximum_health = health
self.current_health = health
# TODO breakables don't /have/ strength. is this separate?
# TODO should interfaces/components be able to say they can only exist
# for entities that also support some other interface?
self.strength = 0
def __init__(self, health_fraction):
self.current_health = health_fraction * self.maximum_health
# -----------------------------------------------------------------------------
# AI
class IActor(IComponent):
@PickUp.announce(Portable)
def announce_pick_up(event, portable):
log.info("ooh picking up {}".format(portable.entity.type.name))
# -----------------------------------------------------------------------------
# Equipment
class IBodied(IComponent):
wearing = derived_attribute("")
# TODO this direly wants, like, a list of limbs and how many
class Bodied(Component, interface=IBodied):
wearing = RelationSubject(Wearing)
class IEquipment(IComponent):
# worn_by?
modifiers = static_attribute("Stat modifiers granted by this equipment.")
class Equipment(Component, interface=IEquipment):
# TODO i think this should live on Bodied as a simple dict of body part to
# equipment
# TODO problem is that if the player loses the item /for any reason
# whatsoever/, the item needs to vanish from the dict. ALSO, the existence
# of the item in the dict can block some other actions.
worn_by = RelationObject(Wearing)
# -----------------------------------------------------------------------------
# AI
class IActor(IComponent):
"""Implements an entity's active thought process. An entity with an
`IActor` component can decide to perform actions on its own, and has a
sense of speed and time.
"""
def act(world):
"""Return an action to be performed (i.e., an `Event` to be fired), or
`None` to do nothing.
it.
"""
class GenericAI(Component, interface=IActor):
def act(self, world):
from flax.geometry import Direction
from flax.event import Walk
from flax.event import MeleeAttack
import random
pos = world.current_map.find(self.entity).position
player_pos = world.current_map.find(world.player).position
for direction in Direction:
if pos + direction == player_pos:
world.queue_event(MeleeAttack(self.entity, direction))
return
# TODO try to walk towards player
world.queue_event(Walk(self.entity, random.choice(list(Direction))))
def init_entity(cls, entity, *args, **kwargs):
"""Initialize an entity. Calls the class's ``__init__`` method."""
if cls.__init__ is Component.__init__:
# Micro-optimization: if there's no __init__, don't even try to
# call it
return
self = cls.adapt(entity)
self.__init__(*args, **kwargs)
# -----------------------------------------------------------------------------
# Physics
class IPhysics(IComponent):
def blocks(actor):
"""Return True iff this object won't allow `actor` to move on top of
it.
"""
# TODO i'm starting to think it would be nice to eliminate the dummy base class
# i have for like every goddamn component? but how?
# TODO seems like i should /require/ that every entity type has a IPhysics,
# maybe others...
class Physics(Component, interface=IPhysics):
pass
class Solid(Physics):
def blocks(self, actor):
# TODO i have /zero/ idea how passwall works here -- perhaps objects
# should be made of distinct "materials"...
return True
class Empty(Physics):
def blocks(self, actor):
return False
class DoorPhysics(Physics):
event.cancel()
@Walk.perform(Physics)
def do_walk(event, _):
event.world.current_map.move(event.actor, event.target.position)
# -----------------------------------------------------------------------------
# Map portal
class IPortal(IComponent):
destination = static_attribute("Name of the destination map.")
class Portal(Component, interface=IPortal):
def __init__(self, *, destination):
self.destination = destination
class PortalDownstairs(Portal):
pass
@Descend.perform(PortalDownstairs)
def do_descend_stairs(event, portal):
event.world.change_map(portal.destination)
class PortalUpstairs(Portal):
pass
raise AttributeError
###############################################################################
# Particular interfaces and components follow.
# -----------------------------------------------------------------------------
# Rendering
class IRender(IComponent):
# TODO consolidate these into a single some-kind-of-object
sprite = static_attribute("")
color = static_attribute("")
class Render(Component, interface=IRender):
def __typeinit__(self, sprite, color):
self.sprite = sprite
self.color = color
class OpenRender(Component, interface=IRender):
def __typeinit__(self, *, open, closed, locked):
self.open = open
self.closed = closed
self.locked = locked
@property
def sprite(self):
# TODO what if it doesn't exist
if ILockable(self.entity).locked:
return self.locked[0]
else:
return self.closed[0]
@property
def color(self):
# TODO what if it doesn't exist
if ILockable(self.entity).locked:
return self.locked[1]
# TODO what if it doesn't exist
elif IOpenable(self.entity).open:
return self.open[1]
else:
return self.closed[1]
class HealthRender(Component, interface=IRender):
def __typeinit__(self, *choices):
self.choices = []
total_weight = sum(choice[0] for choice in choices)
for weight, sprite, color in choices:
self.choices.append((weight / total_weight, sprite, color))
def current_rendering(self):
health = (
ICombatant(self.entity).current_health /
ICombatant(self.entity).maximum_health)
for weight, sprite, color in self.choices:
if health <= weight:
return sprite, color
health -= weight