コード例 #1
0
ファイル: field.py プロジェクト: AFDudley/equanimity
class Field(Persistent):

    """Player owned field logic."""

    @classmethod
    def get(self, world, loc):
        w = get_world(world)
        if w is not None:
            return w.fields.get(tuple(loc))

    def __init__(self, world, coord, element, owner=None, grid=None):
        self.world = world
        self.world_coord = Hex._make(coord)
        if grid is None:
            grid = Grid()
        self.grid = grid
        self.element = element
        self.clock = FieldClock()
        self.stronghold = Stronghold(self)
        self.queue = FieldQueue()
        self.plantings = {}
        self.battle = None
        self._owner = None
        if owner is None:
            owner = WorldPlayer.get()
        self.owner = owner

    def api_view(self, requester=None):
        if requester is not None:
            visible = requester.get_visible_fields(self.world.uid)
            if self.world_coord not in visible:
                return {}
        # TODO -- add factory etc stuff
        return dict(owner=self.owner.uid,
                    element=self.element,
                    coordinate=self.world_coord,
                    state=self.state,
                    clock=self.clock.api_view(),
                    queue=self.queue.as_array(),
                    battle=getattr(self.battle, 'uid', None))

    @property
    def in_battle(self):
        return (self.battle is not None and not self.battle.state.game_over)

    @property
    def state(self):
        if self.in_battle:
            return FIELD_BATTLE
        else:
            return self.clock.state(self)

    @property
    def owner(self):
        return self._owner

    @owner.setter
    def owner(self, owner):
        self._owner = owner
        for s in self.stronghold.squads:
            s.owner = owner
        for u in self.stronghold.free:
            u.owner = owner

    def get_adjacent(self, direction):
        """ Returns the field adjacent to this one in a given direction.
        Returns None if at the border. """
        t = self.world.grid.get_adjacent(self.world_coord, direction=direction)
        if t:
            return self.world.fields.get(tuple(tuple(t)[0]))

    def process_battle_and_movement(self):
        """ Starts a battle if an attacker is available, otherwise moves
        a friendly squad into the stronghold if available """
        # First, check if there was a previous battle and if it is over
        if self.battle is not None and self.battle.state.game_over:
            # If the winner is not the owner, that means the stronghold was
            # still garrisoned, and we must start a new battle
            if self.battle.winner != self.owner:
                self.start_battle(self.battle.battlefield.atksquad)
        else:
            next_squad = self.queue.pop()
            if next_squad is not None:
                if next_squad.owner == self.owner:
                    self.stronghold.move_squad_in(next_squad)
                else:
                    self.start_battle(next_squad)

    def start_battle(self, attacking_squad):
        self.battle = Battle(self, attacking_squad)
        self.battle.persist()
        self.battle.start()

    def check_ungarrisoned(self):
        """ Reverts ownership to the WorldPlayer if unoccupied """
        wp = WorldPlayer.get()
        if self.owner != wp and not self.stronghold.garrisoned:
            self.owner = wp

    def get_taken_over(self, atkr):
        """ Transfers a winning attacking squad to the field. Returns False
        if the stronghold is still controlled. """
        if self.stronghold.garrisoned:
            return False
        self.owner = atkr.owner
        self.stronghold.move_squad_in(self.battlefield.atksquad)
        return True

    def place_scient(self, unit, location):
        if getattr(unit, 'type', '') != 'scient':
            raise ValueError('Unit {0} must be a scient'.format(unit))
        location = Hex._make(location)
        # Placement can only be on one side of the field
        if location[0] <= 0:
            raise ValueError('First coordinate of location must be positive')
        # Unit must be in a squad
        if not isinstance(unit.container, Squad):
            raise ValueError('Unit must be in a squad to be placed on a field')
        # Location must fit on grid
        if not self.grid.in_bounds(location):
            msg = 'Location {0} does not fit on the field'
            raise ValueError(msg.format(location))
        # Unit must not collide with other units placed in its squad
        for u in unit.container:
            if u != unit and u.chosen_location == location:
                msg = 'Location is already occupied by squad member {0}'
                raise ValueError(msg.format(u))
        unit.chosen_location = location

    def rand_place_scient(self, unit):
        """Randomly place a unit on the grid."""
        available = set(self.grid.placement_coords())
        if not available:
            raise ValueError("Grid is full")
        taken = set([u.chosen_location for u in unit.container
                     if not u.chosen_location.is_null()])
        available = available - taken
        return self.place_scient(unit, random.choice(available))

    def rand_place_squad(self, squad):
        """place the units in a squad randomly on the battlefield"""
        # Clear any previously chosen locations
        for u in squad:
            u.chosen_location = Hex.null
        available = set(self.grid.placement_coords())
        positions = random.sample(available, len(squad))
        for unit, pos in zip(squad, positions):
            self.place_scient(unit, pos)

    def get_tile_comps(self):
        """returns a list of stones 1/8th the value of the tile comps."""
        stone_list = []
        for tile in self.grid.iter_tiles():
            stone = Stone()
            for suit, value in tile.comp.iteritems():
                stone[suit] += value / 8  # this 8 will need to be tweaked.
            if stone.value() != 0:
                stone_list += [stone]
        return stone_list

    def set_silo_limit(self):
        """Sets the silo limit to 1 year's worth of stones."""
        # this uses get_tile_comps so the / 8 is only maintained in one place.
        limit = {'Earth': 0, 'Fire': 0, 'Ice': 0, 'Wind': 0}
        for stone in self.get_tile_comps():
            for element in limit.keys():
                limit[element] += stone[element]
        return self.stronghold.silo.set_limit(limit)

    def add_planting(self, loc, comp):
        self.planting[loc] = comp, sum(comp.values())

    def plant(self):
        """Plants from self.plantlings"""
        if self.stronghold.farm.produce(self.plantings):
            for loc, comp in self.plantings.iteritems():
                stone = self.stronghold.silo.get(comp)
                self.grid.imbue_tile(loc, stone)
                self.grid.get(loc)._p_changed = 1
                self.grid._p_changed = 1
                self.element = get_element(self.grid.comp)
                self._p_changed = 1
                self.set_silo_limit()

    def harvest(self):
        """returns set of stones generated at harvest"""
        # this needs to be more clever and relate to the units in
        # the stronghold somehow.
        # happens once a year.
        return self.stronghold.silo.imbue_list(self.get_tile_comps())

    def battle_end_callback(self, atksquad, defsquad, winner, awards,
                            prisoners):
        """ Triggered by Battle when it ends. Dead units/squads should
        have been disbanded before calling this. """
        # Add awards to the silo
        self.stronghold.silo.imbue_list(awards)
        if winner == atksquad:
            # Attempt to capture the field. It will fail if the stronghold
            # is still garrisoned.
            if atksquad:
                self.get_taken_over(atksquad)
        else:
            # Prisoners must be transferred from attacker to defender's
            # stronghold's free units.
            # If prisoners cannot fit they return to where they came from.
            # Highest valued units are captured first, in case they don't fit.
            prisoners = sorted(prisoners, key=attrgetter('value'),
                               reverse=True)
            for u in prisoners:
                try:
                    self.stronghold.add_free_unit(u)
                except ValueError:
                    # We've filled the stronghold
                    break
        # Allow the world to take over if the defender is vacant
        self.check_ungarrisoned()

    """ Special """

    def __eq__(self, other):
        if not isinstance(other, Field):
            return False
        return (other.world == self.world and
                other.world_coord == self.world_coord)

    def __ne__(self, other):
        return not self.__eq__(other)