Untitled

mail@pastecode.io avatar
unknown
python
2 years ago
9.7 kB
1
Indexable
Never
import math
import arcade
from uuid import uuid4

import pyglet.gl
from pyglet.math import Vec2

from src.managers.event_manager import *


class ArcadePort(object):
    def __init__(self, width, height, engine):
        self.width, self.height = width, height

        # Strong References
        self.engine = engine
        self.event_manager = self.engine.event_manager
        self.event_manager.register_listener(self)

        # States
        self.debug = False

        # Main Camera
        self.camera = arcade.Camera(self.width, self.height)
        self.camera_proj = None
        self.camera_zoom = 1.0
        self.camera_zoom_range = Vec2(0.51, 3)
        self.center_point = Vec2(self.width / 2, self.height / 2)
        self.camera_pos = Vec2(-self.width / 2, -self.height / 2)

        # GUI Camera
        self.camera_gui = arcade.Camera(self.width, self.height)

        # Box Dragging Function
        self.drag_box_position = None
        self.drag_box_size = None

        # Camera Dragging Function
        self.initial_position = None

    def notify(self, event: Event):
        """
        Notify by the event manager (if the instance was registered)
        """

        # Fetch the current state of the engine.
        current_state = self.engine.state_machine.peek()

        if isinstance(event, InitializeEvent):
            self.initialize()

        if isinstance(event, TickEvent):
            self.viewport_draw()

        if current_state == self.engine.state_machine.STATE_BATTLE_MODE:
            if isinstance(event, MouseWheelEvent):
                if self.camera_zoom_range[0] <= self.camera_zoom <= self.camera_zoom_range[1]:
                    self.camera_zoom -= 0.05 * event.wheel_value

                if self.camera_zoom > self.camera_zoom_range[1]:
                    self.camera_zoom = self.camera_zoom_range[1]

                if self.camera_zoom < self.camera_zoom_range[0]:
                    self.camera_zoom = self.camera_zoom_range[0]

            if isinstance(event, InputEvent):
                if event.symbol == 103:
                    self.debug = not self.debug

                if event.symbol == 122:
                    if self.camera_zoom > self.camera_zoom_range[0]:
                        self.camera_zoom -= 0.05
                if event.symbol == 115:
                    if self.camera_zoom < self.camera_zoom_range[1]:
                        self.camera_zoom += 0.05

                if event.symbol == 65363:
                    self.camera_pos = Vec2(self.camera_pos.x + 10,
                                           self.camera_pos.y)
                if event.symbol == 65361:
                    self.camera_pos = Vec2(self.camera_pos.x - 10,
                                           self.camera_pos.y)
                if event.symbol == 65362:
                    self.camera_pos = Vec2(self.camera_pos.x,
                                           self.camera_pos.y + 10)
                if event.symbol == 65364:
                    self.camera_pos = Vec2(self.camera_pos.x,
                                           self.camera_pos.y - 10)

            if isinstance(event, MouseInputEvent):
                # Left Mouse Click
                if event.button == 1:
                    if event.drag:
                        if self.drag_box_position is None:
                            self.drag_box_position = Vec2(event.x, event.y)
                        self.drag_box_size = Vec2(self.drag_box_position.x - event.x, self.drag_box_position.y - event.y)

                # Middle Mouse Button
                if event.button == 2:
                    if event.drag:
                        if self.initial_position is None:
                            self.initial_position = Vec2(event.x, event.y)

                        # Calculate the delta between the initial position and the current mouse position.
                        movement_delta = Vec2(self.initial_position.x - event.x, self.initial_position.y - event.y)

                        # Update the camera position with the delta.
                        self.camera_pos = Vec2(self.camera_pos.x + movement_delta.x * self.camera_zoom,
                                               self.camera_pos.y + movement_delta.y * self.camera_zoom)

                        # Update the initial position
                        self.initial_position = Vec2(event.x, event.y)

                        # Update the box drag if the player is currently dragging it.
                        if self.drag_box_position is not None:
                            self.drag_box_size = Vec2(self.drag_box_position.x - event.x,
                                                      self.drag_box_position.y - event.y)

            # If the mouse input is released
            if isinstance(event, ReleaseMouseEvent):
                # Reset the drag box
                if event.button == 1:
                    self.drag_box_position = None
                    self.drag_box_size = None

                # Reset the camera dragging
                if event.button == 2:
                    self.initial_position = None

    def initialize(self):
        # Initialize the viewport texture. Mandatory for the zoom.
        size = (int(self.width * self.camera_zoom), int(self.height * self.camera_zoom))
        self.internal_texture = arcade.Texture.create_empty(str(uuid4()), size)
        self.internal_sprite = arcade.Sprite(center_x=self.width / 2, center_y=self.height / 2,
                                             texture=self.internal_texture)
        self.internal_sprite_list = arcade.SpriteList()
        self.internal_sprite_list.append(self.internal_sprite)

    def update_internal(self):
        # Calculate the offset between the current screen and the new one to center the zoom properly.
        camera_border = Vec2(self.center_point.x - ((self.width * self.camera_zoom) / 2),
                             self.center_point.y - ((self.height * self.camera_zoom) / 2))


        # Calculate the project based on the camera world position, the current zoom and the camera borders.
        self.camera_proj = self.camera_pos.x + math.floor(camera_border.x * 2), self.camera_pos.x + math.floor(
               (self.width * self.camera_zoom)), \
                           self.camera_pos.y + math.floor(camera_border.y * 2), self.camera_pos.y + math.floor(
               (self.height * self.camera_zoom))

        # Use the project with the internal_texture and render the arcade.SpriteList() objects.
        with self.internal_sprite_list.atlas.render_into(self.internal_texture, projection=self.camera_proj) as fbo:
            # Clear the texture
            fbo.clear(arcade.color.BLACK)

            # Draw arcade.SpriteList()
            for x in range(self.engine.battle_manager.map_size.x):
                for y in range(self.engine.battle_manager.map_size.y):
                    self.engine.battle_manager.map.tile_map[x, y].sprite.draw(filter=pyglet.gl.GL_NEAREST, pixelated=True)

            for unit in self.engine.battle_manager.battalions.units:
                unit.sprite.draw(filter=pyglet.gl.GL_NEAREST, pixelated=True)

            # Draw outline if the battalion is selected
            if self.engine.battle_manager.selected_battalion is not None:
                for unit in self.engine.battle_manager.selected_battalion.units:
                    arcade.draw_rectangle_outline(unit.sprite.center_x, unit.sprite.center_y, 16, 16, arcade.color.RED, 1)
                    if unit.new_position is not None and self.debug:
                        arcade.draw_line(unit.position.x, unit.position.y, unit.new_position.x, unit.new_position.y, arcade.color.GREEN)

            # --- DEBUG ---
            # Selection Square
            if self.debug:
                self.engine.battle_manager.selection_box_collider.draw()

                # Cost Field
                for x in range(self.engine.battle_manager.map_size.x):
                    for y in range(self.engine.battle_manager.map_size.y):
                        arcade.draw_rectangle_outline(x * 32, y * 32, 32, 32, arcade.color.GREEN, 2)
                        text = str(self.engine.battle_manager.map.tile_map[x, y].tile_cost)
                        arcade.draw_text(text, x * 32 - 16, y * 32, arcade.color.RED, 10)

    def viewport_draw(self):
        """
        Render the screen.
        """

        # This command has to happen before we start drawing.
        arcade.start_render()

        # Switch to the main camera
        self.camera.use()

        # If the battle manager loaded his files
        if self.engine.battle_manager.initialized:
            # Update the internal texture based on the zoom level.
            self.update_internal()
            # Draw the internal textures.
            self.internal_sprite_list.draw()

        # Switch to the GUI camera
        self.camera_gui.use()

        # Draw the GUI
        arcade.draw_rectangle_filled(self.width / 2, 20, self.width, 40, arcade.color.ALMOND)
        text = f"Zoom Level: {self.camera_zoom}"
        arcade.draw_text(text, 10, 10, arcade.color.BLACK_BEAN, 16)

        # Draw Selection Mouse Box
        if self.drag_box_size is not None:
            # Find the center and the w,h based on the position and size of the drag box.
            center = Vec2(self.drag_box_position.x - self.drag_box_size.x / 2,
                          self.drag_box_position.y - self.drag_box_size.y / 2)
            arcade.draw_rectangle_outline(center.x, center.y, self.drag_box_size.x, self.drag_box_size.y,
                                          arcade.color.BLUE, 2)