Example #1
0
    def __init__(self, parent, model, surface_manager, action_factory, rng,
                 rules_engine, configuration):
        """
        Default constructor
        """
        super().__init__(parent)

        self.model = model
        self.scene = None
        self.surface_manager = surface_manager
        self.action_factory = action_factory
        self.rng = rng
        self.rules_engine = rules_engine
        self.configuration = configuration

        self.current_level = None
        self.view = None
        self.animation_adapters = []
        self.animation_timers = []

        for adapter in range(10):
            self.animation_adapters.append(TimerAdapter())
            self.animation_timers.append(QTimer(self))
            self.animation_timers[adapter].timeout.connect(self.animation_adapters[adapter].trigger_animations)
            self.animation_timers[adapter].start(450 + adapter * 10)

        self.animations = []
        self.move_controller = MoveController(action_factory = action_factory,
                                              rng = rng)

        self.__set_layout()
        self.keymap, self.move_key_map = self._construct_keymaps(
                                                        configuration.controls)

        self.animation_factory = AnimationFactory()
Example #2
0
    def __init__(self, model, surface_manager, action_factory, rng,
                 rules_engine, configuration, screen):
        """
        Default constructor
        """
        super(MapScreen, self).__init__()

        self.model = model
        self.surface_manager = surface_manager
        self.action_factory = action_factory
        self.rng = rng
        self.rules_engine = rules_engine
        self.configuration = configuration
        self.screen = screen
        self.keymap, self.move_key_map = self._construct_keymaps(
            configuration.controls)
        self.move_controller = MoveController(action_factory, rng)
        self.messages = []
Example #3
0
    def __init__(self, parent, model, surface_manager, action_factory, rng,
                 rules_engine, configuration):
        """
        Default constructor
        """
        super().__init__(parent)

        self.model = model
        self.scene = None
        self.surface_manager = surface_manager
        self.action_factory = action_factory
        self.rng = rng
        self.rules_engine = rules_engine
        self.configuration = configuration

        self.current_level = None
        self.view = None
        self.animation_adapters = []
        self.animation_timers = []

        for adapter in range(10):
            self.animation_adapters.append(TimerAdapter())
            self.animation_timers.append(QTimer(self))
            self.animation_timers[adapter].timeout.connect(
                self.animation_adapters[adapter].trigger_animations)
            self.animation_timers[adapter].start(450 + adapter * 10)

        self.animations = []
        self.move_controller = MoveController(action_factory=action_factory,
                                              rng=rng)

        self.__set_layout()
        self.keymap, self.move_key_map = self._construct_keymaps(
            configuration.controls)

        self.animation_factory = AnimationFactory()
Example #4
0
    def __init__(self, model, surface_manager, action_factory, rng,
                 rules_engine, configuration, screen):
        """
        Default constructor
        """
        super(MapScreen, self).__init__()

        self.model = model
        self.surface_manager = surface_manager
        self.action_factory = action_factory
        self.rng = rng
        self.rules_engine = rules_engine
        self.configuration = configuration
        self.screen = screen
        self.keymap, self.move_key_map = self._construct_keymaps(
                                                        configuration.controls)
        self.move_controller = MoveController(action_factory,
                                              rng)
        self.messages = []
Example #5
0
class MapScreen():
    """
    Class for map screen

    .. versionadded:: 0.9
    """
    @log_debug
    def __init__(self, model, surface_manager, action_factory, rng,
                 rules_engine, configuration, screen):
        """
        Default constructor
        """
        super(MapScreen, self).__init__()

        self.model = model
        self.surface_manager = surface_manager
        self.action_factory = action_factory
        self.rng = rng
        self.rules_engine = rules_engine
        self.configuration = configuration
        self.screen = screen
        self.keymap, self.move_key_map = self._construct_keymaps(
                                                        configuration.controls)
        self.move_controller = MoveController(action_factory,
                                              rng)
        self.messages = []

    @log_debug
    def _construct_keymaps(self, config):
        """
        Construct keymaps for handling input
        """
        keymap = {}
        move_keymap = {}
        for key in config.move_left:
            keymap[key] = self._move
            move_keymap[key] = 7
        for key in config.move_up:
            keymap[key] = self._move
            move_keymap[key] = 1
        for key in config.move_right:
            keymap[key] = self._move
            move_keymap[key] = 3
        for key in config.move_down:
            keymap[key] = self._move
            move_keymap[key] = 5
        for key in config.action_a:
            keymap[key] = self._action_a
        for key in config.back:
            keymap[key] = self._back

        return keymap, move_keymap


    @log_info
    def show(self):
        """
        Show the map
        """
        self.refresh_screen()
        player = self.model.player

        while self.model.end_condition == 0 and player.level is not None:
            next_creature = self.model.get_next_creature(self.rules_engine)

            if next_creature == player:
                self._handle_player_input()
            elif next_creature is not None:
                next_creature.act()
            else:
                self.model.end_condition = DIED_IN_DUNGEON

            self.refresh_screen()

        dialog = EndScreen(model = self.model,
                           dying_rules = self.rules_engine.dying_rules,
                           screen = self.screen,
                           controller = EndScreenController())
        dialog.show()

    @log_debug
    def _handle_player_input(self):
        """
        Handle input from player
        """
        key = chr(self.screen.getch())

        if key in self.keymap:
            self.keymap[key](key)
        elif key == 'i':
            inv = InventoryScreen(character = self.model.player,
                                  config = self.configuration,
                                  screen = self.screen,
                                  action_factory = self.action_factory,
                                  parent = self.screen)
            inv.show()
        elif key == 'c':
            dialog = CharacterScreen(character = self.model.player,
                                     screen = self.screen)
            dialog.show()
        elif key == 'a':
            dir_key = chr(self.screen.getch())
            if dir_key in self.move_key_map:
                direction = self.move_key_map[dir_key]
                pyherc.vtable['\ufdd0:attack'](self.model.player,
                                               direction)
        elif key == 'Q':
            self.model.end_condition = 1

    @log_debug
    def _move(self, key):
        """
        Process movement key

        :param key: key triggering the processing
        :type key: string
        """
        direction = self.move_key_map[key]
        self.move_controller.move_or_attack(self.model.player, direction)

    @log_debug
    def _back(self, key):
        """
        Process back key

        :param key: key triggering the processing
        :type key: int

        .. versionadded:: 0.10
        """
        pyherc.vtable['\ufdd0:wait'](self.model.player, Duration.fast)

    @log_debug
    def _action_a(self, key):
        """
        Process action a key

        :param key: key triggering the processing
        :type key: int
        """
        player = self.model.player
        level = player.level
        items = level.get_items_at(player.location)

        if items is not None and len(items) > 0:
            pick_up(player,
                    items[0])

        elif pyherc.vtable['\ufdd0:is-move-legal'](player, 9, 'walk', self.action_factory):
            pyherc.vtable['\ufdd0:move'](player, 9, self.action_factory)

    @log_debug
    def refresh_screen(self):
        """
        Draw whole screen
        """
        player = self.model.player
        level = player.level

        if level is None:
            return

        for column_number, column in enumerate(level.floor):
            for row_number, tile in enumerate(column):
                self.screen.addch(row_number + 1,
                                  column_number,
                                  ord(self.surface_manager.get_icon(tile)),
                                  self.surface_manager.get_attribute_by_name('dim'))

        for column_number, column in enumerate(level.walls):
            for row_number, tile in enumerate(column):
                glyph_number = self.surface_manager.get_icon(tile)
                if glyph_number is not None:
                    self.screen.addch(row_number + 1,
                                      column_number,
                                      ord(glyph_number),
                                      self.surface_manager.get_attribute_by_name('dim'))

        for portal in level.portals:
            self.screen.addch(portal.location[1] + 1,
                              portal.location[0],
                              ord(self.surface_manager.get_icon(portal.icon)),
                              self.surface_manager.get_attribute(portal.icon))

        for item in level.items:
            self.screen.addch(item.location[1] + 1,
                              item.location[0],
                              ord(self.surface_manager.get_icon(item.icon)),
                              self.surface_manager.get_attribute(item.icon))

        for monster in level.creatures:
            self.screen.addch(monster.location[1] + 1,
                              monster.location[0],
                              ord(self.surface_manager.get_icon(monster.icon)),
                              self.surface_manager.get_attribute(monster.icon))

        self.screen.addch(player.location[1] + 1,
                          player.location[0],
                          ord(self.surface_manager.get_icon(player.icon)),
                          self.surface_manager.get_attribute(player.icon))

        stats = 'HP:{0}({1}) MG:{2}({3})'.format(player.hit_points,
                                                 player.max_hp,
                                                 0,
                                                 0)
        stats = stats.ljust(80)
        self.screen.addstr(22, 0, stats)

        self.screen.refresh()

    @log_debug
    def receive_event(self, event):
        """
        Receive event
        """
        if event.event_type == 'move':
            self.refresh_screen()

            if event.mover == self.model.player:
                self.messages = []
                self.screen.addstr(0, 0, ' '.ljust(80))

        if e_event_type(event) in ['attack hit',
                                   'attack miss',
                                   'attack nothing',
                                   'poison triggered',
                                   'poison ended',
                                   'poisoned',
                                   'heal started',
                                   'heal ended',
                                   'heal triggered',
                                   'death',
                                   'pick up',
                                   'drop',
                                   'damage triggered',
                                   'equip',
                                   'unequip',
                                   'notice',
                                   'lose focus',
                                   'error']:
            message = event.get_description(self.model.player)
            self.messages.append(message)
            if len(self.messages) > 2:
                self.messages = self.messages[-2:]
            displayed_messages = ', '.join(self.messages)
            displayed_messages = displayed_messages.ljust(80)
            self.screen.addstr(0, 0, displayed_messages)
Example #6
0
class PlayMapWidget(QWidget):
    """
    Widget for displaying playing world

    .. versionadded:: 0.5
    """
    def __init__(self, parent, model, surface_manager, action_factory, rng,
                 rules_engine, configuration):
        """
        Default constructor
        """
        super().__init__(parent)

        self.model = model
        self.scene = None
        self.surface_manager = surface_manager
        self.action_factory = action_factory
        self.rng = rng
        self.rules_engine = rules_engine
        self.configuration = configuration

        self.current_level = None
        self.view = None
        self.animation_adapters = []
        self.animation_timers = []

        for adapter in range(10):
            self.animation_adapters.append(TimerAdapter())
            self.animation_timers.append(QTimer(self))
            self.animation_timers[adapter].timeout.connect(self.animation_adapters[adapter].trigger_animations)
            self.animation_timers[adapter].start(450 + adapter * 10)

        self.animations = []
        self.move_controller = MoveController(action_factory = action_factory,
                                              rng = rng)

        self.__set_layout()
        self.keymap, self.move_key_map = self._construct_keymaps(
                                                        configuration.controls)

        self.animation_factory = AnimationFactory()

    MenuRequested = pyqtSignal(name='MenuRequested')
    EndScreenRequested = pyqtSignal(name='EndScreenRequested')
    NextSpellRequested = pyqtSignal(name='NextSpellRequested')
    PreviousSpellRequested = pyqtSignal(name='PreviousSpellRequested')


    def _construct_keymaps(self, config):
        """
        Construct keymaps for handling input
        """
        keymap = {}
        move_keymap = {}
        for key in config.move_left:
            keymap[key] = self._move
            move_keymap[key] = 7
        for key in config.move_up:
            keymap[key] = self._move
            move_keymap[key] = 1
        for key in config.move_right:
            keymap[key] = self._move
            move_keymap[key] = 3
        for key in config.move_down:
            keymap[key] = self._move
            move_keymap[key] = 5
        for key in config.start:
            keymap[key] = self._menu
        for key in config.action_a:
            keymap[key] = self._action_a
        for key in config.back:
            keymap[key] = self._back
        for key in config.left_shoulder:
            keymap[key] = self._shoulder_left
        for key in config.right_shoulder:
            keymap[key] = self._shoulder_right
        for key in config.mode_1:
            keymap[key] = self._zoom_out
        for key in config.mode_2:
            keymap[key] = self._zoom_in

        return keymap, move_keymap

    def __set_layout(self):
        """
        Set layout of this widget
        """
        self.scene = QGraphicsScene()

        layout = QHBoxLayout()

        self.view = QGraphicsView(self.scene)
        self.view.setFocusPolicy(Qt.StrongFocus)
        self.view.installEventFilter(self)
        self.view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        layout.addWidget(self.view)

        self.setLayout(layout)

    def construct_scene(self):
        """
        Construct scene to display
        """
        self.__construct_scene(self.model, self.scene)

        self.model.player.register_for_updates(self)
        self.model.register_event_listener(self)
        self.__center_view_on_character(self.model.player)

    def __center_view_on_character(self, entity):
        """
        Center view on given entity
        """
        location = entity.location
        width = 32
        height = 32

        self.view.setSceneRect((location[0] * 32) - width // 2,
                              (location[1] * 32) - height // 2,
                              width,
                              height)

    def __construct_scene(self, model, scene):
        """
        Constructs scene to display
        """
        for anim in [x for x in self.animations]:
            anim.stop()
            anim.clear()

        for adapter in self.animation_adapters:
            adapter.glyphs.clear()

        self.animations = []

        scene.clear()

        self.current_level = model.player.level

        for location, tile in get_tiles(self.current_level):
            if tile['\ufdd0:floor']:
                new_glyph = MapGlyph(self.surface_manager.get_icon(tile['\ufdd0:floor']),
                                     None,
                                     self.animation_adapters[0])
                new_glyph.setZValue(zorder_floor)
                new_glyph.setPos(location[0] * 32, location[1] * 32)
                scene.addItem(new_glyph)
            if tile['\ufdd0:wall']:
                new_glyph = MapGlyph(self.surface_manager.get_icon(tile['\ufdd0:wall']),
                                     None)
                new_glyph.setZValue(zorder_wall)
                new_glyph.setPos(location[0] * 32, location[1] * 32)
                scene.addItem(new_glyph)

            for tile_id in tile['\ufdd0:ornamentation']:
                new_glyph = MapGlyph(self.surface_manager.get_icon(tile_id),
                                     None,
                                     self.rng.choice(self.animation_adapters))
                new_glyph.setZValue(zorder_ornament)
                new_glyph.setPos(location[0] * 32, location[1] * 32)
                scene.addItem(new_glyph)
            for item in tile['\ufdd0:items']:
                self.add_glyph(item, scene, zorder_item)
            for trap in tile['\ufdd0:traps']:
                self.add_glyph(trap, scene, zorder_trap)

        for creature in get_characters(self.current_level):
            self.add_glyph(creature,
                           scene,
                           zorder_character)

    def add_glyph(self, entity, scene, z_order):
        """
        Add graphical representation of an entity

        :param entity: entity to display
        :param scene: scene where glyph will be added
        :type scene: QGraphicsScene
        :param z_order: z-order of entity being displayed
        :type z_order: int
        """
        new_glyph = MapGlyph(self.surface_manager.get_icon(entity.icon),
                             entity, self.rng.choice(self.animation_adapters))
        new_glyph.setZValue(z_order)
        new_glyph.setPos(entity.location[0] * 32,
                         entity.location[1] * 32)
        scene.addItem(new_glyph)

    def remove_glyph(self, entity):
        """
        Remove graphical representation of an entity
        """
        glyphs = [x for x in self.view.items()
                  if (hasattr(x, 'entity'))
                  and (x.entity == entity)]

        for glyph in glyphs:
            self.view.scene().removeItem(glyph)

    def receive_event(self, event):
        """
        Receive event from model
        """

        anim = self.animation_factory.create_animation(event)
        anim.trigger(self)

    def remove_finished_animation(self):
        """
        Remove finished animation
        """
        finished_animations  = [x for x in self.animations
                                if x.state() == QAbstractAnimation.Stopped]
        counters = [x.animationAt(0).targetObject().object_to_animate
                    for x in finished_animations] #TODO: works only if single thing is animated

        for item in finished_animations:
            item.clear()
            self.animations.remove(item)

    def receive_update(self, event):
        """
        Receive update from entity
        """
        if e_event_type(event) == 'move':
            if self.model.player.level != self.current_level:
                self.__construct_scene(self.model, self.scene)
                self.__center_view_on_character(self.model.player)

    def eventFilter(self, qobject, event): #pylint: disable-msg=C0103
        """
        Filter events

        .. Note:: This is done in order to process cursor keys
        """
        result = False

        if event.type() == QEvent.KeyPress:
            self.keyPressEvent(event)
            result = True
        elif event.type() == QEvent.MouseButtonPress:
            self.mouseEvent(event)
            result = True
        else:
            result = super().eventFilter(qobject, event)

        return result

    def mouseEvent(self, event):
        """
        Handle mouse events
        """
        if self.model.player is None:
            return

        player = self.model.player
        next_creature = self.model.get_next_creature(self.rules_engine)

        if next_creature == player:
            point = self.view.mapToScene(event.pos())
            x = int(point.x() / 32)
            y = int(point.y() / 32)

            player = self.model.player
            level = player.level
            location = player.location

            # if player was clicked, take stairs or open menu
            if (x, y) == location:
                if get_portal(level, location):
                    if pyherc.vtable['\ufdd0:is-move-legal'](player, 9):
                        pyherc.vtable['\ufdd0:move'](player, 9)
                else:
                    self.MenuRequested.emit()

            elif is_move(event, player, (x, y)): # TODO: maybe moving should be possible with mouse?
                move(event, player, (x, y))
            else:
                direction = find_direction(location, (x, y))
                distance = distance_between(location, (x, y))

                if distance == 1:
                    #melee attack?
                    if pyherc.vtable['\ufdd0:is-attack-legal'](player, direction):
                        pyherc.vtable['\ufdd0:attack'](player, direction)
                else:
                    if (location[0] == x) or (location[1] == y):
                        if pyherc.vtable['\ufdd0:is-attack-legal'](player, direction):
                            pyherc.vtable['\ufdd0:attack'](player, direction)

        self.process_npc()

        
    def keyPressEvent(self, event): #pylint: disable-msg=C0103
        """
        Handle key events
        """
        if self.model.player is None:
            return

        key_code = event.key()

        player = self.model.player
        next_creature = self.model.get_next_creature(self.rules_engine)

        if next_creature == player:

            if key_code in self.keymap:
                self.keymap[key_code](key_code, event.modifiers())

        self.process_npc()

    def process_npc(self):
        """
        Process npc characters
        """
        player = self.model.player
        next_creature = self.model.get_next_creature(self.rules_engine)

        if next_creature is None:
            self.model.end_condition = DIED_IN_DUNGEON

        while (next_creature != player
                and next_creature is not None
                and self.model.end_condition == 0):
            next_creature.act()
            next_creature = self.model.get_next_creature(self.rules_engine)

            if next_creature is None:
                self.model.end_condition = DIED_IN_DUNGEON

        if self.model.end_condition != 0:
            self.EndScreenRequested.emit()

            
    def _move(self, key, modifiers):
        """
        Process movement key

        :param key: key triggering the processing
        :type key: int
        """
        player = self.model.player
        direction = self.move_key_map[key]

        if modifiers & Qt.ControlModifier:
            if direction != 9:
                pyherc.vtable['\ufdd0:attack'](player,
                                               direction)
        elif modifiers & Qt.AltModifier:
            if direction != 9:
                cast(player,
                     direction,
                     'fireball')

        else:
            self.move_controller.move_or_attack(player, direction)

    def _menu(self, key, modifiers):
        """
        Process menu key

        :param key: key triggering the processing
        :type key: int
        """
        self.MenuRequested.emit()

    def _back(self, key, modifiers):
        """
        Process back key

        :param key: key triggering the processing
        :type key: int
        """
        pyherc.vtable['\ufdd0:wait'](self.model.player, Duration.fast)

    def _zoom_in(self, key, modifiers):
        """
        Zoom map in
        """
        self.view.scale(1.1, 1.1)

    def _zoom_out(self, key, modifiers):
        """
        Zoom map out
        """
        self.view.scale(0.9, 0.9)

    def _shoulder_right(self, key, modifiers):
        """
        Process right shoulder button

        :param key: key triggering the processing
        :type key: int

        .. versionadded:: 0.10
        """
        self.NextSpellRequested.emit()

    def _shoulder_left(self, key, modifiers):
        """
        Process left shoulder button

        :param key: key triggering the processing
        :type key: int

        .. versionadded:: 0.10
        """
        self.PreviousSpellRequested.emit()

    def _action_a(self, key, modifiers):
        """
        Process action a key

        :param key: key triggering the processing
        :type key: int
        """
        player = self.model.player
        level = player.level
        items = list(get_items(level, player.location))

        if items is not None and len(items) > 0:
            pick_up(player,
                    items[0])

        elif pyherc.vtable['\ufdd0:is-move-legal'](player, 9):
            pyherc.vtable['\ufdd0:move'](player, 9)

        elif is_dig_legal(player):
            dig(player)
Example #7
0
class MapScreen():
    """
    Class for map screen

    .. versionadded:: 0.9
    """
    @log_debug
    def __init__(self, model, surface_manager, action_factory, rng,
                 rules_engine, configuration, screen):
        """
        Default constructor
        """
        super(MapScreen, self).__init__()

        self.model = model
        self.surface_manager = surface_manager
        self.action_factory = action_factory
        self.rng = rng
        self.rules_engine = rules_engine
        self.configuration = configuration
        self.screen = screen
        self.keymap, self.move_key_map = self._construct_keymaps(
            configuration.controls)
        self.move_controller = MoveController(action_factory, rng)
        self.messages = []

    @log_debug
    def _construct_keymaps(self, config):
        """
        Construct keymaps for handling input
        """
        keymap = {}
        move_keymap = {}
        for key in config.move_left:
            keymap[key] = self._move
            move_keymap[key] = 7
        for key in config.move_up:
            keymap[key] = self._move
            move_keymap[key] = 1
        for key in config.move_right:
            keymap[key] = self._move
            move_keymap[key] = 3
        for key in config.move_down:
            keymap[key] = self._move
            move_keymap[key] = 5
        for key in config.action_a:
            keymap[key] = self._action_a
        for key in config.back:
            keymap[key] = self._back

        return keymap, move_keymap

    @log_info
    def show(self):
        """
        Show the map
        """
        self.refresh_screen()
        player = self.model.player

        while self.model.end_condition == 0 and player.level is not None:
            next_creature = self.model.get_next_creature(self.rules_engine)

            if next_creature == player:
                self._handle_player_input()
            elif next_creature is not None:
                next_creature.act()
            else:
                self.model.end_condition = DIED_IN_DUNGEON

            self.refresh_screen()

        dialog = EndScreen(model=self.model,
                           dying_rules=self.rules_engine.dying_rules,
                           screen=self.screen,
                           controller=EndScreenController())
        dialog.show()

    @log_debug
    def _handle_player_input(self):
        """
        Handle input from player
        """
        key = chr(self.screen.getch())

        if key in self.keymap:
            self.keymap[key](key)
        elif key == 'i':
            inv = InventoryScreen(character=self.model.player,
                                  config=self.configuration,
                                  screen=self.screen,
                                  action_factory=self.action_factory,
                                  parent=self.screen)
            inv.show()
        elif key == 'c':
            dialog = CharacterScreen(character=self.model.player,
                                     screen=self.screen)
            dialog.show()
        elif key == 'a':
            dir_key = chr(self.screen.getch())
            if dir_key in self.move_key_map:
                direction = self.move_key_map[dir_key]
                pyherc.vtable['\ufdd0:attack'](self.model.player, direction)
        elif key == 'Q':
            self.model.end_condition = 1

    @log_debug
    def _move(self, key):
        """
        Process movement key

        :param key: key triggering the processing
        :type key: string
        """
        direction = self.move_key_map[key]
        self.move_controller.move_or_attack(self.model.player, direction)

    @log_debug
    def _back(self, key):
        """
        Process back key

        :param key: key triggering the processing
        :type key: int

        .. versionadded:: 0.10
        """
        pyherc.vtable['\ufdd0:wait'](self.model.player, Duration.fast)

    @log_debug
    def _action_a(self, key):
        """
        Process action a key

        :param key: key triggering the processing
        :type key: int
        """
        player = self.model.player
        level = player.level
        items = level.get_items_at(player.location)

        if items is not None and len(items) > 0:
            pick_up(player, items[0])

        elif pyherc.vtable['\ufdd0:is-move-legal'](player, 9, 'walk',
                                                   self.action_factory):
            pyherc.vtable['\ufdd0:move'](player, 9, self.action_factory)

    @log_debug
    def refresh_screen(self):
        """
        Draw whole screen
        """
        player = self.model.player
        level = player.level

        if level is None:
            return

        for column_number, column in enumerate(level.floor):
            for row_number, tile in enumerate(column):
                self.screen.addch(
                    row_number + 1, column_number,
                    ord(self.surface_manager.get_icon(tile)),
                    self.surface_manager.get_attribute_by_name('dim'))

        for column_number, column in enumerate(level.walls):
            for row_number, tile in enumerate(column):
                glyph_number = self.surface_manager.get_icon(tile)
                if glyph_number is not None:
                    self.screen.addch(
                        row_number + 1, column_number, ord(glyph_number),
                        self.surface_manager.get_attribute_by_name('dim'))

        for portal in level.portals:
            self.screen.addch(portal.location[1] + 1, portal.location[0],
                              ord(self.surface_manager.get_icon(portal.icon)),
                              self.surface_manager.get_attribute(portal.icon))

        for item in level.items:
            self.screen.addch(item.location[1] + 1, item.location[0],
                              ord(self.surface_manager.get_icon(item.icon)),
                              self.surface_manager.get_attribute(item.icon))

        for monster in level.creatures:
            self.screen.addch(monster.location[1] + 1, monster.location[0],
                              ord(self.surface_manager.get_icon(monster.icon)),
                              self.surface_manager.get_attribute(monster.icon))

        self.screen.addch(player.location[1] + 1, player.location[0],
                          ord(self.surface_manager.get_icon(player.icon)),
                          self.surface_manager.get_attribute(player.icon))

        stats = 'HP:{0}({1}) MG:{2}({3})'.format(player.hit_points,
                                                 player.max_hp, 0, 0)
        stats = stats.ljust(80)
        self.screen.addstr(22, 0, stats)

        self.screen.refresh()

    @log_debug
    def receive_event(self, event):
        """
        Receive event
        """
        if event.event_type == 'move':
            self.refresh_screen()

            if event.mover == self.model.player:
                self.messages = []
                self.screen.addstr(0, 0, ' '.ljust(80))

        if e_event_type(event) in [
                'attack hit', 'attack miss', 'attack nothing',
                'poison triggered', 'poison ended', 'poisoned', 'heal started',
                'heal ended', 'heal triggered', 'death', 'pick up', 'drop',
                'damage triggered', 'equip', 'unequip', 'notice', 'lose focus',
                'error'
        ]:
            message = event.get_description(self.model.player)
            self.messages.append(message)
            if len(self.messages) > 2:
                self.messages = self.messages[-2:]
            displayed_messages = ', '.join(self.messages)
            displayed_messages = displayed_messages.ljust(80)
            self.screen.addstr(0, 0, displayed_messages)
Example #8
0
class PlayMapWidget(QWidget):
    """
    Widget for displaying playing world

    .. versionadded:: 0.5
    """
    def __init__(self, parent, model, surface_manager, action_factory, rng,
                 rules_engine, configuration):
        """
        Default constructor
        """
        super().__init__(parent)

        self.model = model
        self.scene = None
        self.surface_manager = surface_manager
        self.action_factory = action_factory
        self.rng = rng
        self.rules_engine = rules_engine
        self.configuration = configuration

        self.current_level = None
        self.view = None
        self.animation_adapters = []
        self.animation_timers = []

        for adapter in range(10):
            self.animation_adapters.append(TimerAdapter())
            self.animation_timers.append(QTimer(self))
            self.animation_timers[adapter].timeout.connect(
                self.animation_adapters[adapter].trigger_animations)
            self.animation_timers[adapter].start(450 + adapter * 10)

        self.animations = []
        self.move_controller = MoveController(action_factory=action_factory,
                                              rng=rng)

        self.__set_layout()
        self.keymap, self.move_key_map = self._construct_keymaps(
            configuration.controls)

        self.animation_factory = AnimationFactory()

    MenuRequested = pyqtSignal(name='MenuRequested')
    EndScreenRequested = pyqtSignal(name='EndScreenRequested')
    NextSpellRequested = pyqtSignal(name='NextSpellRequested')
    PreviousSpellRequested = pyqtSignal(name='PreviousSpellRequested')

    def _construct_keymaps(self, config):
        """
        Construct keymaps for handling input
        """
        keymap = {}
        move_keymap = {}
        for key in config.move_left:
            keymap[key] = self._move
            move_keymap[key] = 7
        for key in config.move_up:
            keymap[key] = self._move
            move_keymap[key] = 1
        for key in config.move_right:
            keymap[key] = self._move
            move_keymap[key] = 3
        for key in config.move_down:
            keymap[key] = self._move
            move_keymap[key] = 5
        for key in config.start:
            keymap[key] = self._menu
        for key in config.action_a:
            keymap[key] = self._action_a
        for key in config.back:
            keymap[key] = self._back
        for key in config.left_shoulder:
            keymap[key] = self._shoulder_left
        for key in config.right_shoulder:
            keymap[key] = self._shoulder_right
        for key in config.mode_1:
            keymap[key] = self._zoom_out
        for key in config.mode_2:
            keymap[key] = self._zoom_in

        return keymap, move_keymap

    def __set_layout(self):
        """
        Set layout of this widget
        """
        self.scene = QGraphicsScene()

        layout = QHBoxLayout()

        self.view = QGraphicsView(self.scene)
        self.view.setFocusPolicy(Qt.StrongFocus)
        self.view.installEventFilter(self)
        self.view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        layout.addWidget(self.view)

        self.setLayout(layout)

    def construct_scene(self):
        """
        Construct scene to display
        """
        self.__construct_scene(self.model, self.scene)

        self.model.player.register_for_updates(self)
        self.model.register_event_listener(self)
        self.__center_view_on_character(self.model.player)

    def __center_view_on_character(self, entity):
        """
        Center view on given entity
        """
        location = entity.location
        width = 32
        height = 32

        self.view.setSceneRect((location[0] * 32) - width // 2,
                               (location[1] * 32) - height // 2, width, height)

    def __construct_scene(self, model, scene):
        """
        Constructs scene to display
        """
        for anim in [x for x in self.animations]:
            anim.stop()
            anim.clear()

        for adapter in self.animation_adapters:
            adapter.glyphs.clear()

        self.animations = []

        scene.clear()

        self.current_level = model.player.level

        for location, tile in get_tiles(self.current_level):
            if tile['\ufdd0:floor']:
                new_glyph = MapGlyph(
                    self.surface_manager.get_icon(tile['\ufdd0:floor']), None,
                    self.animation_adapters[0])
                new_glyph.setZValue(zorder_floor)
                new_glyph.setPos(location[0] * 32, location[1] * 32)
                scene.addItem(new_glyph)
            if tile['\ufdd0:wall']:
                new_glyph = MapGlyph(
                    self.surface_manager.get_icon(tile['\ufdd0:wall']), None)
                new_glyph.setZValue(zorder_wall)
                new_glyph.setPos(location[0] * 32, location[1] * 32)
                scene.addItem(new_glyph)

            for tile_id in tile['\ufdd0:ornamentation']:
                new_glyph = MapGlyph(self.surface_manager.get_icon(tile_id),
                                     None,
                                     self.rng.choice(self.animation_adapters))
                new_glyph.setZValue(zorder_ornament)
                new_glyph.setPos(location[0] * 32, location[1] * 32)
                scene.addItem(new_glyph)
            for item in tile['\ufdd0:items']:
                self.add_glyph(item, scene, zorder_item)
            for trap in tile['\ufdd0:traps']:
                self.add_glyph(trap, scene, zorder_trap)

        for creature in get_characters(self.current_level):
            self.add_glyph(creature, scene, zorder_character)

    def add_glyph(self, entity, scene, z_order):
        """
        Add graphical representation of an entity

        :param entity: entity to display
        :param scene: scene where glyph will be added
        :type scene: QGraphicsScene
        :param z_order: z-order of entity being displayed
        :type z_order: int
        """
        new_glyph = MapGlyph(self.surface_manager.get_icon(entity.icon),
                             entity, self.rng.choice(self.animation_adapters))
        new_glyph.setZValue(z_order)
        new_glyph.setPos(entity.location[0] * 32, entity.location[1] * 32)
        scene.addItem(new_glyph)

    def remove_glyph(self, entity):
        """
        Remove graphical representation of an entity
        """
        glyphs = [
            x for x in self.view.items()
            if (hasattr(x, 'entity')) and (x.entity == entity)
        ]

        for glyph in glyphs:
            self.view.scene().removeItem(glyph)

    def receive_event(self, event):
        """
        Receive event from model
        """

        anim = self.animation_factory.create_animation(event)
        anim.trigger(self)

    def remove_finished_animation(self):
        """
        Remove finished animation
        """
        finished_animations = [
            x for x in self.animations
            if x.state() == QAbstractAnimation.Stopped
        ]
        counters = [
            x.animationAt(0).targetObject().object_to_animate
            for x in finished_animations
        ]  #TODO: works only if single thing is animated

        for item in finished_animations:
            item.clear()
            self.animations.remove(item)

    def receive_update(self, event):
        """
        Receive update from entity
        """
        if e_event_type(event) == 'move':
            if self.model.player.level != self.current_level:
                self.__construct_scene(self.model, self.scene)
                self.__center_view_on_character(self.model.player)

    def eventFilter(self, qobject, event):  #pylint: disable-msg=C0103
        """
        Filter events

        .. Note:: This is done in order to process cursor keys
        """
        result = False

        if event.type() == QEvent.KeyPress:
            self.keyPressEvent(event)
            result = True
        elif event.type() == QEvent.MouseButtonPress:
            self.mouseEvent(event)
            result = True
        else:
            result = super().eventFilter(qobject, event)

        return result

    def mouseEvent(self, event):
        """
        Handle mouse events
        """
        if self.model.player is None:
            return

        player = self.model.player
        next_creature = self.model.get_next_creature(self.rules_engine)

        if next_creature == player:
            point = self.view.mapToScene(event.pos())
            x = int(point.x() / 32)
            y = int(point.y() / 32)

            player = self.model.player
            level = player.level
            location = player.location

            # if player was clicked, take stairs or open menu
            if (x, y) == location:
                if get_portal(level, location):
                    if pyherc.vtable['\ufdd0:is-move-legal'](player, 9):
                        pyherc.vtable['\ufdd0:move'](player, 9)
                else:
                    self.MenuRequested.emit()

            elif is_move(
                    event, player,
                (x, y)):  # TODO: maybe moving should be possible with mouse?
                move(event, player, (x, y))
            else:
                direction = find_direction(location, (x, y))
                distance = distance_between(location, (x, y))

                if distance == 1:
                    #melee attack?
                    if pyherc.vtable['\ufdd0:is-attack-legal'](player,
                                                               direction):
                        pyherc.vtable['\ufdd0:attack'](player, direction)
                else:
                    if (location[0] == x) or (location[1] == y):
                        if pyherc.vtable['\ufdd0:is-attack-legal'](player,
                                                                   direction):
                            pyherc.vtable['\ufdd0:attack'](player, direction)

        self.process_npc()

    def keyPressEvent(self, event):  #pylint: disable-msg=C0103
        """
        Handle key events
        """
        if self.model.player is None:
            return

        key_code = event.key()

        player = self.model.player
        next_creature = self.model.get_next_creature(self.rules_engine)

        if next_creature == player:

            if key_code in self.keymap:
                self.keymap[key_code](key_code, event.modifiers())

        self.process_npc()

    def process_npc(self):
        """
        Process npc characters
        """
        player = self.model.player
        next_creature = self.model.get_next_creature(self.rules_engine)

        if next_creature is None:
            self.model.end_condition = DIED_IN_DUNGEON

        while (next_creature != player and next_creature is not None
               and self.model.end_condition == 0):
            next_creature.act()
            next_creature = self.model.get_next_creature(self.rules_engine)

            if next_creature is None:
                self.model.end_condition = DIED_IN_DUNGEON

        if self.model.end_condition != 0:
            self.EndScreenRequested.emit()

    def _move(self, key, modifiers):
        """
        Process movement key

        :param key: key triggering the processing
        :type key: int
        """
        player = self.model.player
        direction = self.move_key_map[key]

        if modifiers & Qt.ControlModifier:
            if direction != 9:
                pyherc.vtable['\ufdd0:attack'](player, direction)
        elif modifiers & Qt.AltModifier:
            if direction != 9:
                cast(player, direction, 'fireball')

        else:
            self.move_controller.move_or_attack(player, direction)

    def _menu(self, key, modifiers):
        """
        Process menu key

        :param key: key triggering the processing
        :type key: int
        """
        self.MenuRequested.emit()

    def _back(self, key, modifiers):
        """
        Process back key

        :param key: key triggering the processing
        :type key: int
        """
        pyherc.vtable['\ufdd0:wait'](self.model.player, Duration.fast)

    def _zoom_in(self, key, modifiers):
        """
        Zoom map in
        """
        self.view.scale(1.1, 1.1)

    def _zoom_out(self, key, modifiers):
        """
        Zoom map out
        """
        self.view.scale(0.9, 0.9)

    def _shoulder_right(self, key, modifiers):
        """
        Process right shoulder button

        :param key: key triggering the processing
        :type key: int

        .. versionadded:: 0.10
        """
        self.NextSpellRequested.emit()

    def _shoulder_left(self, key, modifiers):
        """
        Process left shoulder button

        :param key: key triggering the processing
        :type key: int

        .. versionadded:: 0.10
        """
        self.PreviousSpellRequested.emit()

    def _action_a(self, key, modifiers):
        """
        Process action a key

        :param key: key triggering the processing
        :type key: int
        """
        player = self.model.player
        level = player.level
        items = list(get_items(level, player.location))

        if items is not None and len(items) > 0:
            pick_up(player, items[0])

        elif pyherc.vtable['\ufdd0:is-move-legal'](player, 9):
            pyherc.vtable['\ufdd0:move'](player, 9)

        elif is_dig_legal(player):
            dig(player)