示例#1
0
    def attack(self, target):
        """
        Attack random member of enemy party not on cooldown.
        If all members of enemy party on cooldown, kills one at random.
        Puts attacker on cooldown
        :param target: Entity party being attacked
        :return: dict results of attack
        """
        results = []
        self.cooldown = self.offensive_cd

        # pick random member off cooldown
        member = target.party.random_member_no_cooldown()
        if member:
            results.append({
                'message':
                Message("{} {} stuns {} {} for {} turns".format(
                    self.profession, self.name, member.profession, member.name,
                    member.defensive_cd))
            })
            member.take_damage()
        else:
            # if all on cooldown, kill random member
            member = target.party.random_member()
            results.append({
                'message':
                Message(
                    "{} {} kills {} {}".format(self.profession, self.name,
                                               member.profession, member.name),
                    libtcod.orange)
            })
            # removes member, checks for death
            results.extend(target.party.remove_member(member))

        return results
示例#2
0
def cast_lightning(*args, **kwargs):
    caster = args[0]
    entities = kwargs.get('entities')
    fov_map = kwargs.get('fov_map')
    damage = kwargs.get('damage')
    maximum_range = kwargs.get('maximum_range')

    results = []

    target = None
    closest_distance = maximum_range + 1

    for entity in entities:
        if entity.fighter and entity != caster and tcod.map_is_in_fov(fov_map, entity.x, entity.y):
            distance = caster.distance_to(entity)

            if distance < closest_distance:
                target = entity
                closest_distance = distance

    if target:
        results.append({'consumed': True, 'target': target, 'message': Message(
            'A lightning bolt strikes the {0} with a thunderous boom! {1} takes {2} damage.'.format(
                target.name, target.name, damage), tcod.dark_yellow)})
        results.extend(target.fighter.take_damage(damage))
    else:
        results.append({'consumed': False, 'target': None, 'message': Message(
            'No enemy is close enough to strike.', tcod.red)})

    return results
示例#3
0
def cast_confuse(*args, **kwargs):
    entities = kwargs.get('entities')
    fov_map = kwargs.get('fov_map')
    target_x = kwargs.get('target_x')
    target_y = kwargs.get('target_y')

    results = []

    if not tcod.map_is_in_fov(fov_map, target_x, target_y):
        results.append({'consumed': False, 'message': Message('You cannot target something outside your field of view.',
                                                              tcod.yellow)})
        return results

    for entity in entities:
        if entity.x == target_x and entity.y == target_y and entity.ai:
            confused_ai = ConfusedMonster(entity.ai, 10)

            confused_ai.owner = entity
            entity.ai = confused_ai

            results.append({'consumed': True, 'message': Message('The eyes of the {0} glaze over, as it starts to '
                                                                 'stumble around'.format(entity.name),
                                                                 tcod.light_green)})

            break
    else:
        results.append({'consumed': False, 'message': Message('There is no targetable enemy at that location.',
                                                              tcod.yellow)})
    return results
示例#4
0
def cast_fireball(*args, **kwargs):
    entities = kwargs.get('entities')
    fov_map = kwargs.get('fov_map')
    damage = kwargs.get('damage')
    radius = kwargs.get('radius')
    target_x = kwargs.get('target_x')
    target_y = kwargs.get('target_y')

    results = []

    if not tcod.map_is_in_fov(fov_map, target_x, target_y):
        results.append({'consumed': False, 'message': Message('You cannot target something outside your field of view',
                                                              tcod.yellow)})
        return results

    results.append({'consumed': True, 'message': Message('The fireball explodes, burning everything within {0} tiles.'.
                                                         format(radius), tcod.orange)})

    for entity in entities:
        if entity.distance(target_x, target_y) <= radius and entity.fighter:
            results.append({'message': Message('The {0} gets burned for {1} hit points.'.format(entity.name, damage),
                            tcod.orange)})
            results.extend(entity.fighter.take_damage(damage))

    return results
示例#5
0
def heal(*args, **kwargs):
    entity = args[0]
    amount = kwargs.get('amount')

    results = []

    if entity.fighter.hp == entity.fighter.max_hp:
        results.append({'consumed': False, 'message': Message('You are already at full health.', tcod.yellow)})
    else:
        entity.fighter.heal(amount)
        results.append({'consumed': True, 'message': Message('Your wounds are starting to heal!', tcod.green)})

    return results
示例#6
0
    def attack(self, target):
        results = []

        damage = self.power - target.fighter.defense

        if damage > 0:
            target.fighter.take_damage(damage)
            results.append({'message': Message('{0} attacks {1} for {2} hit points.'.format(
                self.owner.name.capitalize(), target.name, str(damage)), tcod.white)})
            results.extend(target.fighter.take_damage(damage))
        else:
            results.append({'message': Message('{0} attacks {1} but does no damage.'.format(
                self.owner.name.capitalize(), target.name), tcod.white)})
        return results
示例#7
0
    def use(self, item_entity, **kwargs):
        results = []

        item_component = item_entity.item

        if item_component.use_function is None:
            equippable_component = item_entity.equippable

            # If equipment can be equipped, do so. Otherwise, print message
            if equippable_component:
                results.append({'equip': item_entity})
            else:
                results.append({'message': Message('The {0} cannot be used'.format(item_entity.name), tcod.yellow)})
        else:
            # Determines if targeting is true or not and if the coords were passed
            if item_component.targeting and not (kwargs.get('target_x') or kwargs.get('target_y')):
                results.append({'targeting': item_entity})
            else:
                kwargs = {**item_component.function_kwargs, **kwargs}
                item_use_results = item_component.use_function(self.owner, **kwargs)

                for item_use_result in item_use_results:
                    if item_use_result.get('consumed'):
                        self.remove_item(item_entity)

                results.extend(item_use_results)

        return results
示例#8
0
 def add_coins(self, amount):
     """
     Add coins to the party's treasure horde
     :param amount:
     :return: None
     """
     self.coins += amount
     return Message(text='Hero Party found {} coins'.format(amount))
示例#9
0
 def add_member(self, party_member):
     """
     Append a PartyMember to the list
     :param party_member: PartyMember object to add to the party list
     :return: results and message
     """
     if len(self.members) < 6:
         self.members.append(party_member)
         message = Message('{} {} added to party'.format(
             party_member.profession, party_member.name),
                           color=libtcod.lighter_blue)
         results = True
     else:
         message = Message('Not enough space for new members!'.format(
             party_member.profession, party_member.name),
                           color=libtcod.lighter_blue)
         results = False
     return message, results
示例#10
0
    def add_item(self, item):
        results = []

        if len(self.items) >= self.capacity:
            results.append({
                'item_added': None,
                'message': Message('You cannot carry any more, your inventory is full', tcod.yellow)
            })
        else:
            results.append({
                'item_added': item,
                'message': Message('You pick up the {0} and place it in your inventory'.format(item.name),
                                   tcod.blue)
            })

            self.items.append(item)

        return results
示例#11
0
    def drop_item(self, item):
        results = []

        item.x = self.owner.x
        item.y = self.owner.y

        self.remove_item(item)
        results.append({'item_dropped': item, 'message': Message('You dropped the {0}.'.format(item.name),
                                                                 tcod.yellow)})

        return results
示例#12
0
    def pay_members(self):
        """
        Remove coins from the party's treasure horde for each party member.
        If there are not enough coins to pay all party members, a random party member will be removed until there are
        :return: result list of messages
        """
        results = []
        while self.total_party_cost() > self.coins:
            person = self.random_member()
            results.append(
                Message(text="{} {} has left {} due to lack of funding".format(
                    person.profession, person.name, self.owner.name),
                        color=libtcod.orange))
            results.extend(self.remove_member(party_member=person))
        for member in self.members:
            results.append(
                Message(text="{} the {} gets paid {}".format(
                    member.name, member.profession, member.cost)))
            self.coins -= member.get_cost()

        return results
示例#13
0
def kill_monster(entity):
    death_message = Message(text='{} is dead!'.format(
        entity.name.capitalize()),
                            color=libtcod.orange)

    entity.char = '%'
    entity.color = libtcod.dark_red
    entity.blocks = False
    entity.render_order = RenderOrder.CORPSE
    entity.ai = None
    entity.name = 'Remains of {}'.format(entity.name)

    return death_message
示例#14
0
def kill_monster(monster):
    death_message = Message('{0} is dead!'.format(monster.name.capitalize()),
                            tcod.orange)

    monster.char = '%'
    monster.color = tcod.dark_red
    monster.blocks = False
    monster.fighter = None
    monster.ai = None
    monster.name = 'remains of ' + monster.name
    monster.render_order = RenderOrder.CORPSE

    return death_message
示例#15
0
    def drop_item(self, item):
        results = []

        if self.owner.equipment.main_hand == item or self.owner.equipment.off_hand == item:
            self.owner.equipment.toggle_equip(item)

        item.x = self.owner.x
        item.y = self.owner.y

        self.remove_item(item)
        results.append({'item_dropped': item, 'message': Message('You dropped the {0}.'.format(item.name),
                                                                 tcod.yellow)})

        return results
示例#16
0
    def take_turn(self, target, fov_map, game_map, entities):
        results = []

        if self.number_of_turns > 0:
            random_x = self.owner.x + randint(0, 2) - 1
            random_y = self.owner.y + randint(0, 2) - 1

            if random_x != self.owner.x and random_y != self.owner.y:
                self.owner.move_towards(random_x, random_y, game_map, entities)

            self.number_of_turns -= 1
        else:
            self.owner.ai = self.previous_ai
            results.append({'message': Message('The {0} is no longer confused!'.format(self.owner.name), tcod.red)})

        return results
示例#17
0
    def remove_member(self, party_member):
        """
        Remove a PartyMember from the list
        :param party_member: PartyMember object to remove from the party list
        :return: results of check for death
        """
        results = []

        self.members.remove(party_member)
        results.append({
            'message':
            Message('{} {} removed from {}'.format(party_member.profession,
                                                   party_member.name,
                                                   self.owner.name),
                    color=libtcod.lighter_blue)
        })
        if not self.members:
            results.append({'dead': self.owner})
        return results
示例#18
0
    def next_floor(self, player, message_log, constants):
        self.dungeon_level += 1
        entities = [player]

        # Creates a new map
        self.tiles = self.initialize_tiles()
        self.make_map(constants['max_rooms'], constants['room_min_size'],
                      constants['room_max_size'], constants['map_width'],
                      constants['map_height'], player, entities)

        # TODO note when we add stairs back up, we need to make sure that player doesn't gain health again going down
        # Gives player half of their max hp back
        player.fighter.heal(player.fighter.max_hp // 2)
        message_log.add_message(
            Message(
                'You take a moment to rest heading down the stairs, restoring some health',
                tcod.violet))

        return entities
示例#19
0
def main():
    screen_width = 80
    screen_height = 50

    bar_width = 20
    panel_height = 7
    panel_y = screen_height - panel_height

    message_x = bar_width + 2
    message_width = screen_width - bar_width - 2
    message_height = panel_height - 1

    map_width = 80
    map_height = 43

    # Conway's Game Of Life variables
    # 3 6 5 6 3 - good choice
    # 3 6 5 7 3 - another good choice
    # 3 5 5 6 4 - larger, more open
    # 2 4 6 8 6 - very full, but lots of obstacles (Forest?)
    # 3 7 5 6 4 - small, windy, lots of caverns
    survive_min = 3
    survive_max = 7
    resurrect_min = 6
    resurrect_max = 6
    iterations = 4

    # zone variables
    zone_seed_min_distance = 10
    min_cavern_size = 15
    max_monsters_per_room = 3

    fov_radius = 8

    colors = {
        'dark_wall': libtcod.darker_gray,
        'dark_ground': libtcod.dark_gray,
        'light_wall': libtcod.sepia,
        'light_ground': libtcod.light_sepia
    }

    party_component = Party()
    member_1 = PartyMember(name="Bill",
                           profession="Soldier",
                           offensive_cd=4,
                           defensive_cd=4,
                           attack_type={'line': 1},
                           cost=5)
    member_2 = PartyMember(name="John",
                           profession="Archer",
                           offensive_cd=5,
                           defensive_cd=5,
                           attack_type={'direct': 4},
                           cost=4)
    member_3 = PartyMember(name="Sam",
                           profession="Defender",
                           offensive_cd=5,
                           defensive_cd=3,
                           attack_type={'line': 1},
                           cost=5)
    member_4 = PartyMember(name="Ivan",
                           profession="Ice Mage",
                           offensive_cd=6,
                           defensive_cd=6,
                           attack_type={'cone': 3},
                           cost=8)
    party_component.add_member(member_1)
    party_component.add_member(member_2)
    party_component.add_member(member_3)
    party_component.add_member(member_4)
    player = Entity(x=0,
                    y=0,
                    char='@',
                    color=libtcod.white,
                    name='Hero Party',
                    blocks=True,
                    render_order=RenderOrder.ACTOR,
                    party=party_component)
    entities = [player]

    libtcod.console_set_custom_font(fontFile='images/arial10x10.png',
                                    flags=libtcod.FONT_TYPE_GREYSCALE
                                    | libtcod.FONT_LAYOUT_TCOD)

    libtcod.console_init_root(w=screen_width,
                              h=screen_height,
                              title='Hero Party',
                              fullscreen=False)

    con = libtcod.console_new(w=screen_width, h=screen_height)
    panel = libtcod.console_new(w=screen_width, h=panel_height)

    game_map = GameMap(width=map_width, height=map_height)
    game_map.make_map(survive_min=survive_min,
                      survive_max=survive_max,
                      resurrect_min=resurrect_min,
                      resurrect_max=resurrect_max,
                      iterations=iterations,
                      zone_seed_min_distance=zone_seed_min_distance,
                      min_cavern_size=min_cavern_size,
                      player=player,
                      entities=entities,
                      max_monsters_per_room=max_monsters_per_room)

    fov_recompute = True
    fov_map = initialize_fov(game_map=game_map)

    message_log = MessageLog(x=message_x,
                             width=message_width,
                             height=message_height)

    key = libtcod.Key()
    mouse = libtcod.Mouse()

    game_state = GameStates.PLAYER_TURN
    previous_member = None
    target_tiles = None

    while not libtcod.console_is_window_closed():
        libtcod.sys_check_for_event(mask=libtcod.EVENT_KEY_PRESS
                                    | libtcod.EVENT_MOUSE,
                                    k=key,
                                    m=mouse)

        if fov_recompute:
            recompute_fov(fov_map=fov_map,
                          x=player.x,
                          y=player.y,
                          radius=fov_radius)

        render_all(con=con,
                   panel=panel,
                   entities=entities,
                   player=player,
                   game_map=game_map,
                   fov_map=fov_map,
                   fov_recompute=fov_recompute,
                   message_log=message_log,
                   screen_width=screen_width,
                   screen_height=screen_height,
                   acting_member=previous_member,
                   target_tiles=target_tiles,
                   bar_width=bar_width,
                   panel_height=panel_height,
                   panel_y=panel_y,
                   mouse=mouse,
                   colors=colors)

        libtcod.console_flush()

        clear_all(con=con, entities=entities)

        # --------- PLAYER TURN: GET INPUTS -------------
        action = handle_keys(key=key, game_state=game_state)
        mouse_action = handle_mouse(mouse)

        move = action.get('move')
        exit_game = action.get('exit_game')
        fullscreen = action.get('fullscreen')
        auto = action.get('auto')
        selected_member = action.get('member')
        act_dir = action.get('act_dir')

        left_click = mouse_action.get('left_click')
        right_click = mouse_action.get('right_click')

        # --------- PLAYER TURN: PROCESS INPUTS -------------
        player_turn_results = []

        # AUTOMATIC -----------------------------------------
        if auto and GameStates.PLAYER_TURN:
            for entity in entities:
                if not entity.ai and not entity.blocks and entity.party.members \
                        and entity.x == player.x and entity.y == player.y:
                    player_turn_results.append({'add_member': entity})
                elif not entity.ai and not entity.blocks and entity.party.coins \
                        and entity.x == player.x and entity.y == player.y:
                    player_turn_results.append({'loot_coins': entity})
            else:  # Wait
                game_state = GameStates.ENEMY_TURN

        # MOVEMENT ------------------------------------------
        if move and game_state == GameStates.PLAYER_TURN:
            dx, dy = move
            destination_x = player.x + dx
            destination_y = player.y + dy

            if not game_map.is_blocked(x=destination_x, y=destination_y) \
                    and not get_blocking_entities_at_location(entities=entities, x=destination_x, y=destination_y):
                player.move(dx=dx, dy=dy)
                fov_recompute = True
                game_state = GameStates.ENEMY_TURN

        # SELECTED MEMBER -----------------------------------
        if selected_member and selected_member <= len(player.party.members) and \
                player.party.members[selected_member - 1].cooldown < 1:
            if not previous_member:
                previous_member = selected_member
                target_tiles = get_target_tiles(entity=player,
                                                member=previous_member - 1,
                                                game_map=game_map,
                                                fov_map=fov_map)
                player_turn_results.append({'message': Message("Direction?")})
                game_state = GameStates.TARGETING

            elif previous_member == selected_member:
                previous_member = None
                target_tiles = None
                game_state = GameStates.PLAYER_TURN

        if act_dir and previous_member and game_state == GameStates.TARGETING:
            attack_tiles = get_target_tiles(entity=player,
                                            member=previous_member - 1,
                                            game_map=game_map,
                                            fov_map=fov_map,
                                            attack_dir=[act_dir])
            targets = [
                entity for entity in entities
                if (entity.x, entity.y) in attack_tiles and entity.ai
            ]

            # TODO this code should really be somewhere else...
            if targets:
                if player.party.members[previous_member -
                                        1].attack_type.get('line'):
                    for target in targets:
                        attack_results = player.party.members[
                            previous_member - 1].attack(target=target)
                        player_turn_results.extend(attack_results)
                elif player.party.members[previous_member -
                                          1].attack_type.get('direct'):
                    closest = targets[0]
                    closest_dist = distance_to(player.x, closest.x, player.y,
                                               closest.y)
                    targets.remove(closest)
                    for target in targets:
                        distance = distance_to(player.x, player.y, target.x,
                                               target.y)
                        if distance < closest_dist:
                            closest = target
                            closest_dist = distance
                    attack_results = player.party.members[previous_member -
                                                          1].attack(
                                                              target=closest)
                    player_turn_results.extend(attack_results)
                elif player.party.members[previous_member -
                                          1].attack_type.get('cone'):
                    for target in targets:
                        attack_results = player.party.members[
                            previous_member - 1].attack(target=target)
                        player_turn_results.extend(attack_results)

            else:
                player_turn_results.append(
                    {'message': Message("Attack hits nothing")})
            previous_member = None
            target_tiles = None
            game_state = GameStates.ENEMY_TURN

        if exit_game:
            return True

        if fullscreen:
            libtcod.console_set_fullscreen(
                fullscreen=not libtcod.console_is_fullscreen())

        # --------- PLAYER TURN: PROCESS RESULTS -------------
        for result in player_turn_results:
            rescued_members = result.get('add_member')
            message = result.get('message')
            dead_entity = result.get('dead')
            loot_coins = result.get('loot_coins')

            if rescued_members:
                for member in rescued_members.party.members:
                    msg, res = player.party.add_member(member)
                    message_log.add_message(msg)
                    if res:
                        entities.remove(rescued_members)

            if loot_coins:
                msg = player.party.add_coins(loot_coins.party.coins)
                message_log.add_message(msg)
                entities.remove(loot_coins)

            if message:
                message_log.add_message(message=message)

            if dead_entity:
                if dead_entity == player:
                    msg, game_state = kill_player(dead_entity)
                else:
                    msg = kill_monster(dead_entity)

                message_log.add_message(message=msg)

        # --------- ENEMY TURN: GET INPUT -------------
        if game_state == GameStates.ENEMY_TURN:
            entities_in_distance_order = sorted(
                entities, key=lambda z: z.distance_to(player))

            for entity in entities_in_distance_order:
                if entity.ai:
                    enemy_turn_results = entity.ai.take_turn(target=player,
                                                             fov_map=fov_map,
                                                             game_map=game_map,
                                                             entities=entities)
                    # --------- ENEMY TURN: PROCESS RESULTS -------------
                    for result in enemy_turn_results:
                        message = result.get('message')
                        dead_entity = result.get('dead')

                        if message:
                            message_log.add_message(message=message)

                        if dead_entity:
                            if dead_entity == player:
                                message, game_state = kill_player(
                                    player=dead_entity)
                            else:
                                message = kill_monster(entity=dead_entity)

                            message_log.add_message(message=message)

                            if game_state == GameStates.PLAYER_DEAD:
                                break

                    if game_state == GameStates.PLAYER_DEAD:
                        break

            else:
                game_state = GameStates.PLAYER_TURN
                for entity in entities:
                    if entity.party:
                        entity.party.tick_all()
示例#20
0
def kill_player(player):
    player.char = '%'
    player.color = tcod.dark_red

    return Message('You died!', tcod.red), GameStates.PLAYER_DEAD
示例#21
0
def play_game(player, entities, game_map, message_log, game_state, con, panel,
              constants):
    # Field of view variables
    fov_recompute = True  # We only need to recompute when character moves
    fov_map = initialize_fov(game_map)

    # Variables for keyboard and mouse inputs
    key = tcod.Key()
    mouse = tcod.Mouse()

    # Player goes first
    game_state = GameStates.PLAYERS_TURN

    # Save previous game state (for inventory support)
    previous_game_state = game_state

    # Saves targeting item
    targeting_item = None

    # Main game loop
    while not tcod.console_is_window_closed():
        tcod.sys_check_for_event(tcod.EVENT_KEY_PRESS | tcod.EVENT_MOUSE, key,
                                 mouse)

        # Updates field of view if needed
        if fov_recompute:
            recompute_fov(fov_map, player.x, player.y, constants['fov_radius'],
                          constants['fov_light_walls'],
                          constants['fov_algorithm'])

        # Draws player and sets recompute to false until next player move
        render_all(con, panel, entities, player, game_map, fov_map,
                   fov_recompute, message_log, constants['screen_width'],
                   constants['screen_height'], constants['bar_width'],
                   constants['panel_height'], constants['panel_y'], mouse,
                   constants['colors'], game_state)
        fov_recompute = False
        tcod.console_flush()

        # Updates spot last at with a blank (avoids multiple @'s)
        clear_all(con, entities)

        # Keyboard and mouse inputs
        action = handle_keys(key, game_state)
        mouse_action = handle_mouse(mouse)

        # TODO add in help menu that lists commands, available both through menu and by hitting '?'
        # Action handlers
        move = action.get('move')
        wait = action.get('wait')
        pickup = action.get('pickup')
        show_inventory = action.get('show_inventory')
        drop_inventory = action.get('drop_inventory')
        inventory_index = action.get('inventory_index')
        level_up = action.get('level_up')
        show_character_screen = action.get('show_character_screen')
        # TODO add in take_stairs_up
        take_stairs_down = action.get('take_stairs_down')
        exit = action.get('exit')
        fullscreen = action.get('fullscreen')

        # Mouse action handlers
        left_click = mouse_action.get('left_click')
        right_click = mouse_action.get('right_click')

        # List to hold for result of battles
        player_turn_results = []

        # Player turn and handling of item pickups
        if move and game_state == GameStates.PLAYERS_TURN:
            dx, dy = move
            destination_x = player.x + dx
            destination_y = player.y + dy

            if not game_map.is_blocked(destination_x, destination_y):
                target = get_blocking_entities_at_location(
                    entities, destination_x, destination_y)

                if target:
                    attack_results = player.fighter.attack(target)
                    player_turn_results.extend(attack_results)
                else:
                    player.move(dx, dy)
                    fov_recompute = True

                game_state = GameStates.ENEMY_TURN

        # Player waits for a turn (and does nothing)
        elif wait and game_state == GameStates.PLAYERS_TURN:
            message_log.add_message(
                Message('You twiddle your thumbs for a turn.', tcod.turquoise))
            game_state = GameStates.ENEMY_TURN

        # Pickup items
        elif pickup and game_state == GameStates.PLAYERS_TURN:
            for entity in entities:
                if entity.item and entity.x == player.x and entity.y == player.y:
                    pickup_results = player.inventory.add_item(entity)
                    player_turn_results.extend(pickup_results)

                    break
            else:
                message_log.add_message(
                    Message('There is nothing here to pick up.', tcod.yellow))

        # Show inventory
        if show_inventory:
            previous_game_state = game_state
            game_state = GameStates.SHOW_INVENTORY

        # Drops item from inventory
        if drop_inventory:
            previous_game_state = game_state
            game_state = GameStates.DROP_INVENTORY

        # Use or drop item (only when in inventory game state and not dead)
        if inventory_index is not None and previous_game_state != GameStates.PLAYER_DEAD and inventory_index < len(
                player.inventory.items):
            item = player.inventory.items[inventory_index]

            if game_state == GameStates.SHOW_INVENTORY:
                player_turn_results.extend(
                    player.inventory.use(item,
                                         entities=entities,
                                         fov_map=fov_map))
            elif game_state == GameStates.DROP_INVENTORY:
                player_turn_results.extend(player.inventory.drop_item(item))

        # Leveling up
        if level_up:
            if level_up == 'hp':
                player.fighter.max_hp += 20
                player.fighter.hp += 20
            elif level_up == 'str':
                player.fighter.power += 1
            elif level_up == 'def':
                player.fighter.defense += 1

            game_state = previous_game_state

        # Character screen, switches to appropriate game state
        if show_character_screen:
            previous_game_state = game_state
            game_state = GameStates.CHARACTER_SCREEN

        # TODO will likely need to add a stairs_down and stairs_up to entities when making going up floors
        # Goes down a flight of stairs, going to a new map
        if take_stairs_down and game_state == GameStates.PLAYERS_TURN:
            for entity in entities:
                if entity.stairs and entity.x == player.x and entity.y == player.y:
                    entities = game_map.next_floor(player, message_log,
                                                   constants)
                    fov_map = initialize_fov(game_map)
                    fov_recompute = True
                    tcod.console_clear(con)

                    break
            else:
                message_log.add_message(
                    Message('There are no stairs here.', tcod.yellow))

        # Targeting mode is active - left mouse click sets target, right mouse click cancels
        if game_state == GameStates.TARGETING:
            if left_click:
                target_x, target_y = left_click

                item_use_results = player.inventory.use(targeting_item,
                                                        entities=entities,
                                                        fov_map=fov_map,
                                                        target_x=target_x,
                                                        target_y=target_y)
                player_turn_results.extend(item_use_results)
            elif right_click:
                player_turn_results.append({'targeting_cancelled': True})

        # Reverts back to previous game state while viewing inventory; otherwise, closes and saves game
        if exit:
            if game_state in (GameStates.SHOW_INVENTORY,
                              GameStates.DROP_INVENTORY,
                              GameStates.CHARACTER_SCREEN):
                game_state = previous_game_state
            elif game_state == GameStates.TARGETING:
                player_turn_results.append({'targeting_cancelled': True})
            else:
                save_game(player, entities, game_map, message_log, game_state)

                return True

        # Toggles fullscreen
        if fullscreen:
            tcod.console_set_fullscreen(not tcod.console_is_fullscreen())

        # Iterates results after turn
        for player_turn_result in player_turn_results:
            message = player_turn_result.get('message')
            dead_entity = player_turn_result.get('dead')
            item_added = player_turn_result.get('item_added')
            item_consumed = player_turn_result.get('consumed')
            item_dropped = player_turn_result.get('item_dropped')
            targeting = player_turn_result.get('targeting')
            targeting_cancelled = player_turn_result.get('targeting_cancelled')
            xp = player_turn_result.get('xp')

            # Displays supplied message
            if message:
                message_log.add_message(message)

            # Player or monster has died
            if dead_entity:
                if dead_entity == player:
                    message, game_state = kill_player(dead_entity)
                else:
                    message = kill_monster(dead_entity)

                message_log.add_message(message)

            # Item was added to inventory
            if item_added:
                entities.remove(item_added)

                game_state = GameStates.ENEMY_TURN

            # Item was used
            if item_consumed:
                game_state = GameStates.ENEMY_TURN

            # Item was dropped
            if item_dropped:
                entities.append(item_dropped)

                game_state = GameStates.ENEMY_TURN

            # Targeting is activated, switch to targeting mode
            if targeting:
                previous_game_state = GameStates.PLAYERS_TURN
                game_state = GameStates.TARGETING

                targeting_item = targeting

                message_log.add_message(targeting_item.item.targeting_message)

            # Targeting was cancelled, revert to previous game state
            if targeting_cancelled:
                game_state = previous_game_state

                message_log.add_message(Message('Targeting cancelled.'))

            # Experience results
            if xp:
                leveled_up = player.level.add_xp(xp)
                message_log.add_message(
                    Message('You gain {0} experience points.'.format(xp)))

                if leveled_up:
                    message_log.add_message(
                        Message(
                            'You have leveled up and reached level {0}!'.
                            format(player.level.current_level), tcod.green))
                    previous_game_state = game_state
                    game_state = GameStates.LEVEL_UP

        # Enemies turn
        if game_state == GameStates.ENEMY_TURN:
            for entity in entities:
                if entity.ai:
                    enemy_turn_results = entity.ai.take_turn(
                        player, fov_map, game_map, entities)

                    for enemy_turn_result in enemy_turn_results:
                        message = enemy_turn_result.get('message')
                        dead_entity = enemy_turn_result.get('dead')

                        if message:
                            message_log.add_message(message)

                        if dead_entity:
                            if dead_entity == player:
                                message, game_state = kill_player(dead_entity)
                            else:
                                message = kill_monster(dead_entity)

                            message_log.add_message(message)

                            if game_state == GameStates.PLAYER_DEAD:
                                break

                    if game_state == GameStates.PLAYER_DEAD:
                        break
            else:
                game_state = GameStates.PLAYERS_TURN
示例#22
0
def kill_player(player):
    player.char = '%'
    player.color = libtcod.dark_red

    return Message(text='Your party has been slain!',
                   color=libtcod.red), GameStates.PLAYER_DEAD
示例#23
0
    def place_entities(self, room, entities):
        # Bases number of monsters and item to which level of dungeon the player is on
        max_monsters_per_room = from_dungeon_level([[2, 1], [3, 4], [5, 6]],
                                                   self.dungeon_level)
        max_items_per_room = from_dungeon_level([[1, 1], [2, 4]],
                                                self.dungeon_level)

        number_of_monsters = randint(0, max_monsters_per_room)
        number_of_items = randint(0, max_items_per_room)

        # Monster chances to spawn - trolls will have more weight the further down the floors a player goes
        monster_chances = {
            'orc':
            80,
            'troll':
            from_dungeon_level([[15, 3], [30, 5], [60, 7]], self.dungeon_level)
        }

        # Item chances to drop - item weight changes based on the dungeon level with less healing potions spawning
        item_chances = {
            'healing_potion': 35,
            'sword': from_dungeon_level([[5, 4]], self.dungeon_level),
            'shield': from_dungeon_level([[15, 8]], self.dungeon_level),
            'lightning_scroll': from_dungeon_level([[25, 4]],
                                                   self.dungeon_level),
            'fireball_scroll': from_dungeon_level([[25, 6]],
                                                  self.dungeon_level),
            'confusion_scroll': from_dungeon_level([[10, 2]],
                                                   self.dungeon_level)
        }

        # Spawns monsters
        for i in range(number_of_monsters):
            x = randint(room.x1 + 1, room.x2 - 1)
            y = randint(room.y1 + 1, room.y2 - 1)

            # TODO - add more monsters as you go further down in levels
            # Checks if location to place monster is empty
            if not any([
                    entity
                    for entity in entities if entity.x == x and entity.y == y
            ]):
                # Used to determine which monster to spawn
                monster_choice = random_choice_from_dict(monster_chances)
                # Orc
                if monster_choice == 'orc':
                    fighter_component = Fighter(hp=20,
                                                defense=0,
                                                power=4,
                                                xp=35)
                    ai_component = BasicMonster()

                    monster = Entity(x,
                                     y,
                                     'o',
                                     tcod.desaturated_green,
                                     'Orc',
                                     blocks=True,
                                     render_order=RenderOrder.ACTOR,
                                     fighter=fighter_component,
                                     ai=ai_component)
                # Troll
                else:
                    fighter_component = Fighter(hp=30,
                                                defense=2,
                                                power=8,
                                                xp=100)
                    ai_component = BasicMonster()

                    monster = Entity(x,
                                     y,
                                     'T',
                                     tcod.darker_green,
                                     'Troll',
                                     blocks=True,
                                     render_order=RenderOrder.ACTOR,
                                     fighter=fighter_component,
                                     ai=ai_component)

                entities.append(monster)

        # Spawns items
        for i in range(number_of_items):
            x = randint(room.x1 + 1, room.x2 - 1)
            y = randint(room.y1 + 1, room.y2 - 1)

            if not any([
                    entity
                    for entity in entities if entity.x == x and entity.y == y
            ]):
                # Used to determine what item to spawn
                item_choice = random_choice_from_dict(item_chances)

                # Healing potion - heals 4 damage
                if item_choice == 'healing_potion':
                    item_component = Item(use_function=heal, amount=40)
                    item = Entity(x,
                                  y,
                                  '!',
                                  tcod.violet,
                                  'Healing Potion',
                                  render_order=RenderOrder.ITEM,
                                  item=item_component)

                # Sword
                elif item_choice == 'sword':
                    equippable_component = Equippable(EquipmentSlots.MAIN_HAND,
                                                      power_bonus=3)
                    item = Entity(x,
                                  y,
                                  '/',
                                  tcod.sky,
                                  'Sword',
                                  equippable=equippable_component)

                # Shield
                elif item_choice == 'shield':
                    equippable_component = Equippable(EquipmentSlots.OFF_HAND,
                                                      defense_bonus=1)
                    item = Entity(x,
                                  y,
                                  '[',
                                  tcod.darker_orange,
                                  'Shield',
                                  equippable=equippable_component)

                # Fireball scroll - deals 12 damage to all enemies in a radius of 3 tiles
                elif item_choice == 'fireball_scroll':
                    item_component = Item(
                        use_function=cast_fireball,
                        targeting=True,
                        targeting_message=Message(
                            'Left-click a target tile for the fireball, or right-click to cancel.',
                            tcod.light_cyan),
                        damage=25,
                        radius=3)
                    item = Entity(x,
                                  y,
                                  '#',
                                  tcod.red,
                                  'Fireball Scroll',
                                  render_order=RenderOrder.ITEM,
                                  item=item_component)

                # Confuse scroll - confuses enemy for 10 turns
                elif item_choice == 'confusion_scroll':
                    item_component = Item(
                        use_function=cast_confuse,
                        targeting=True,
                        targeting_message=Message(
                            'Left-click an enemy to confuse it, or right-click to cancel',
                            tcod.light_cyan))
                    item = Entity(x,
                                  y,
                                  '#',
                                  tcod.light_pink,
                                  'Confusion Scroll',
                                  render_order=RenderOrder.ITEM,
                                  item=item_component)

                # Lightning scroll - deals 20 damage to nearest enemy
                else:
                    item_component = Item(use_function=cast_lightning,
                                          damage=40,
                                          maximum_range=5)
                    item = Entity(x,
                                  y,
                                  '#',
                                  tcod.yellow,
                                  'Lightning Scroll',
                                  render_order=RenderOrder.ITEM,
                                  item=item_component)

                entities.append(item)