コード例 #1
0
def render_description_window(game):
    ent = entity_at_pos(game.walk_blocking_ents, *game.cursor.pos)
    if ent is not None:
        x, y = pos_on_screen(ent.x - 5, ent.y + 2, game.player)

        title = f'{ent.full_name}'
        body = ent.extended_descr(game, list_items_underneath = True)

        draw_window(title, body, game, window_x=x, window_y=y, show_cancel_option=False, title_color=ent.color)
コード例 #2
0
    def try_move(self,
                 dx: int,
                 dy: int,
                 game: Game,
                 ignore_entities: bool = False,
                 ignore_walls: bool = False,
                 absolute: bool = False):
        """
        Attempts to move the entity in the given direction or to the given coordinates.
        Returns True on successful move.
        Returns False if wall blocks movement or entity is unable to move due to effects.
        Returns Entity if entity blocks movement.

        :param dx:
        :type dx: int
        :param dy:
        :type dy: int
        :param game:
        :type game: Game
        :param ignore_entities: Whether to ignore blocking entities when checking for movement.
        :type ignore_entities: bool
        :param ignore_walls: Whether to ignore walls when checking for movement.
        :type ignore_walls: bool
        :param absolute: Whether dx&dy denote directions or a x/y coordinates on the grid
        :type absolute: bool
        :return:
        :rtype: bool
        """
        if not self.can_move:
            return False

        if not absolute:
            dest_x, dest_y = self.x + dx, self.y + dy
        else:
            dest_x, dest_y = dx, dy

        if game.map.is_wall(dest_x, dest_y) and not ignore_walls:
            return False

        blocked = entity_at_pos(game.walk_blocking_ents, dest_x, dest_y)
        if blocked and not ignore_entities:
            return blocked

        self.move(dx, dy, absolute=absolute)
        return True
コード例 #3
0
def initialize_fov(game):
    game_map = game.map
    fov_map = tcod.map.Map(game_map.width, game_map.height)

    for x in range(game_map.width):
        for y in range(game_map.height):
            fov_map.transparent[y, x] = not game_map.tiles[(x, y)].block_sight
            fov_map.walkable[y, x] = not game_map.tiles[(x, y)].blocked

            ent = entity_at_pos(game.sight_blocking_ents, x, y)
            if ent:
                fov_map.transparent[y, x] = not ent.blocks.get(
                    BlockLevel.SIGHT,
                    False)  #game_map.tiles[(x, y)].block_sight
                fov_map.walkable[y, x] = not ent.blocks.get(
                    BlockLevel.WALK, False)

    return fov_map
コード例 #4
0
    def execute(self, tx: int, ty: int, game: Game, **kwargs):
        user = self.owner
        user.color_bg = None  # Reset the entities bg-color, which the skill preparation had changed

        results = []
        results.append({
            'message':
            Message(f'The {user.name} slams down!',
                    category=MessageCategory.OBSERVATION,
                    type=MessageType.COMBAT)
        })
        hit = entity_at_pos(game.fighter_ents, tx, ty)
        if hit:
            if hit.fighter:
                results.extend(
                    user.f.attack_setup(hit,
                                        game,
                                        dmg_mod_multipl=2,
                                        verb='slams',
                                        ignore_moveset=True))
        return results
コード例 #5
0
    def ranged_attack(self, target_pos, game, draw_projectile=True):
        results = []
        target = None

        if draw_projectile:
            projectile_result = animate_projectile(
                *self.owner.pos,
                *target_pos,
                game,
                color=colors.beige,
                ignore_entities=False
            )  # TODO color and projectile can later be modified by weapon/ammo
            if not isinstance(projectile_result, bool):
                target = projectile_result
                # todo if target.pos != target_pos add some randomness?
        else:
            target = entity_at_pos(game.blocking_ents, *target_pos)

        if target is not None and target.fighter is not None:
            results.extend(self.attack_setup(target, game))
        else:
            exertion = self.get_attack_exertion(self.get_attack_power())
            self.exert(exertion, 'attack')
        return results
コード例 #6
0
def process_player_interaction(game, action):
    manual = action.get('manual')
    move = action.get('move')
    # dodge = action.get('dodge')
    interact = action.get('interact')
    direction = action.get('dir')
    wait = action.get('wait')
    pickup = action.get('pickup')
    prepare = action.get('prepare')
    equip = action.get('equip')
    quick_use_idx = action.get('quick_use')
    show_inventory = action.get('show_inventory')
    show_equipment = action.get('show_equipment')
    show_prepared = action.get('show_prepared')
    # drop_inventory = action.get('drop_inventory')
    # menu_selection = action.get('menu_selection')
    toggle_dash = action.get('toggle_dash')
    toggle_block = action.get('toggle_block')
    toggle_weapon = action.get('toggle_weapon')

    player = game.player
    game_map = game.map
    entities = game.entities
    fov_map = game.fov_map

    debug = action.get('debug')
    spawn = action.get('spawn')

    results = []

    if move or interact:
        dx, dy = direction
        destination_x, destination_y = player.x + dx, player.y + dy
        blocking = player.f.is_blocking
        dashing = player.f.is_dashing

        if not game_map.is_wall(destination_x, destination_y):
            target = entity_at_pos(game.walk_blocking_ents, destination_x, destination_y)

            if target is None and interact:  # Check for non-blocking interactable objects
                target = entity_at_pos(game.interactable_ents, destination_x, destination_y)

            if target:
                if player.can_attack is False:
                    results.append({'message': Message('You are unable to attack!',
                                                       type=MessageType.COMBAT_BAD)})
                # If a NPC is blocking the way #
                elif target.fighter:
                    if blocking:
                        results.append({'message': Message('PLACEHOLDER: cant attack while blocking.',
                                                                type=MessageType.SYSTEM)})
                    elif dashing:
                        results.append({'message': Message('PLACEHOLDER: cant tackle adjacent target.',
                                                           type=MessageType.SYSTEM)})
                    else:
                        if player.active_weapon_is_ranged:  # TODO should attacking in melee with a ranged weapon incur penalties or be disabled?
                            attack_results = player.f.attack_setup(target, game, dmg_mod_multipl=0.2,
                                                                         ignore_moveset=True)
                            results.append({'message': Message(
                                'PLACEHOLDER: attacking with ranged weapon in melee.', type=MessageType.SYSTEM)})
                        else:
                            attack_results = player.f.attack_setup(target, game)

                        results.extend(attack_results)
                # If a static object is blocking the way #
                elif target.architecture:

                    if interact and target.architecture.on_interaction:  # interacting with a architecture entity
                        interaction_results = target.architecture.on_interaction(player, target, game)
                        results.extend(interaction_results)

                    if move and target.architecture.on_collision:  # bumping into the object
                        collision_results = target.architecture.on_collision(player, target, game)
                        results.extend(collision_results)
                # elif move: # TODO Are these still required?
                #     print('PLACEHOLDER: Your way is blocked.')
                # elif interact:
                #     print('PLACEHOLDER: There is nothing to interact with')
            elif move:
                if player.can_move is False:
                    results.append({'message': Message('You are unable to move!',
                                                       type=MessageType.COMBAT_BAD)})
                elif dashing:
                    results.extend(player.f.dash(dx, dy, game))
                else:
                    player.move(dx, dy)

                results.append({'fov_recompute': True})

            if not target or not target.fighter:
                # Movement other than fighting resets the current moveset
                if player.f.active_weapon is not None:
                    player.f.active_weapon.moveset.cycle_moves(reset=True)

            game.state = GameState.NPCS_ACTIVE


    # Passing a turn or interacting #
    elif wait:
        if not player.in_combat(game): # outside combat, interact with item under player
            target = entity_at_pos(game.interactable_ents, *player.pos)
            if target is not None:
                interaction_results = target.architecture.on_interaction(player, target, game)
                results.extend(interaction_results)
        results.append({'waiting': True})


    # Picking up an item #
    elif pickup:
        items = [item for item in game.item_ents if item.same_pos_as(player)]
        if items:
            # Option menu is displayed if > 1 item is on the ground
            choice = items[0] if len(items) == 1 else \
                item_list_menu(player, items, game, title='Select Item', body='Pick up which item?')
            if choice is not None and choice is not False:
                pickup_results = player.inventory.add(choice)
                results.extend(pickup_results)
        else:
            results.append({'message': Message('There is nothing here.', category=MessageCategory.OBSERVATION)})

    # Combat related #
    if toggle_block:
        results.extend(player.f.toggle_blocking())

    if toggle_dash:
        results.extend(player.f.toggle_dashing())

    # No dodging while blocking possible; disable one or the other, depending on input
    if player.f.is_blocking and player.f.is_dashing:
        if toggle_block:
            player.f.toggle_dashing()
        elif toggle_dash:
            player.f.toggle_blocking()
        else:
            logging.debug('ERROR: Both blocking and dashing active.')


    if toggle_weapon:
        new_weapon = player.f.toggle_weapon()
        results.append({'weapon_switched': new_weapon})


    # Quick use handling #
    if quick_use_idx and quick_use_idx <= len(player.qu_inventory):
        quick_use_item = player.qu_inventory[quick_use_idx - 1]  # -1 as the idx is passed as a number key
        qu_results = player.qu_inventory.use(quick_use_item, game)
        results.extend(qu_results)

    # Inventory display #
    if show_inventory or prepare or equip:
        if show_inventory and len(player.inventory) == 0:
            Message('Your inventory is empty.', category=MessageCategory.OBSERVATION).add_to_log(game)
        elif prepare and len(player.inventory.useable_items) <= 0:
            Message('You have no items to prepare.', category=MessageCategory.OBSERVATION).add_to_log(game)
        elif equip and len(player.inventory.equippable_items) <= 0:
            Message('You have no items to equip.', category=MessageCategory.OBSERVATION).add_to_log(game)
        else:
            game.previous_state = game.state
            game.state = GameState.SHOW_INVENTORY

    if show_prepared:
        if len(player.qu_inventory) > 0:
            game.previous_state = game.state
            game.state = GameState.SHOW_QU_INVENTORY
        else:
            Message('You have no items prepared.', category=MessageCategory.OBSERVATION).add_to_log(game)

    if show_equipment:
        if len(player.paperdoll.equipped_items) > 0:
            game.previous_state = game.state
            game.state = GameState.SHOW_EQUIPMENT
        else:
            Message('You have no items equipped.', category=MessageCategory.OBSERVATION).add_to_log(game)

    # Other #
    if manual:
        display_manual()

    if spawn:
        results.extend(spawn_menu(game))

    if debug:
        debug_menu(game, clear=False)

    return results
コード例 #7
0
    def attack_setup(self,
                     target,
                     game,
                     dmg_mod_multipl: float = 1,
                     verb: str = 'hit',
                     ignore_moveset: bool = False):
        results = []
        extra_attacks = []
        #atk_exertion_divider = cfg.ATK_EXERT_MULTIPL
        daze_chance = 50  # chance to daze attacker on successful block # TODO dynamically calculated

        if self.active_weapon is not None:  # if a weapon is equipped, check the type
            melee_attack = True if self.active_weapon.type == ItemType.MELEE_WEAPON else False
            #ranged_attack = True if self.active_weapon.type == ItemType.RANGED_WEAPON else False
        else:  # unarmed attack
            melee_attack = True
            ranged_attack = False
            ignore_moveset = True

        # Apply moveset modifers #
        attack_power = self.get_attack_power(dmg_mod_multipl, ignore_moveset)
        attack_exertion = self.get_attack_exertion(attack_power,
                                                   ignore_moveset)
        if not ignore_moveset and self.active_weapon is not None:
            move_results = self.active_weapon.moveset.execute(
                self.owner, target)
            verb = move_results.get('attack_verb', verb)
            extra_attacks = move_results.get('extra_attacks', [])
            self.active_weapon.moveset.cycle_moves()
        # if ignore_moveset:
        #     attack_power = choice(self.base_dmg_potential) * dmg_mod_multipl
        #     attack_exertion = attack_power / atk_exertion_divider
        # else:
        #     if self.active_weapon is not None:
        #         move_results = self.active_weapon.moveset.execute(self.owner, target)
        #         verb = move_results.get('attack_verb', verb)
        #         extra_attacks = move_results.get('extra_attacks', [])
        #
        #     attack_power = self.dmg_roll * dmg_mod_multipl
        #     attack_exertion = attack_power / atk_exertion_divider * self.active_weapon.moveset.exert_multipl
        #     self.active_weapon.moveset.cycle_moves()

        logging.debug(
            f'{self.owner.name} prepares to attack {target.name} with base damage {self.base_dmg_potential},'
            f' (modded {self.modded_dmg_potential}) for a total power of {attack_power} and {attack_exertion}exert'
        )

        # Make sure attacker has enough stamina #
        if self.stamina < attack_exertion:
            message = M(f'You are too exhausted to attack!',
                        category=MessageCategory.COMBAT,
                        type=MessageType.ALERT)
            results.append({'message': message})
            logging.debug(
                f'Canceling {self}\'s attack: stamina of {self.stamina} too low.'
            )
            return results

        # Blocking #
        attack_blocked = False
        if target.f.is_blocking:
            attack_blocked = target.f.attempt_block(self, attack_power)

        if attack_blocked:
            # TODO should attacker also take sta damage?
            sta_dmg_multipl = self.active_weapon.moveset.get_modifier(
                Mod.BLOCK_STA_DMG_MULTIPL
            )  # Some weapons afflict a higher stamina damage
            sta_dmg = round((attack_power / 2) * sta_dmg_multipl)
            logging.debug(
                f'{target.name} block exert multiplied by {sta_dmg_multipl} due to {self.owner.name} attack mod'
            )
            results.append(target.f.exert(sta_dmg, 'block'))

            if melee_attack and randint(0, 100) > daze_chance:
                if target.is_player:
                    message = M(
                        f'{target.address_colored.title()} block the attack, dazing {self.owner.address_colored}!',
                        category=MessageCategory.COMBAT,
                        type=MessageType.COMBAT_GOOD)
                else:
                    message = M(
                        f'{self.owner.address_colored.title()} blocks your attack, dazing {target.owner.address_colored}!',
                        category=MessageCategory.COMBAT,
                        type=MessageType.COMBAT_BAD)
                self.set_effect(State.DAZED, True, 1)
                results.append({'message': message})
        else:
            attack_string = self.owner.verb_declination(verb)
            results.extend(
                self.attack_execute(target, attack_power, attack_string))

        for attack_pos in extra_attacks:
            extra_target = entity_at_pos(game.npc_ents, *attack_pos)
            if extra_target:
                results.extend(
                    self.attack_execute(extra_target, attack_power // 2,
                                        'also hit')
                )  # TODO currently flat half damage and can't be blocked; can be replaced with moveset-tailored multiplier

        self.exert(attack_exertion, 'attack')

        return results