from __future__ import annotations
from typing import Optional, TYPE_CHECKING
import pygame
from nqp.core import systems
from nqp.world_elements.camera import Camera
if TYPE_CHECKING:
from nqp.core.game import Game
from nqp.world.model import WorldModel
__all__ = ["WorldView"]
[docs]class WorldView:
"""
Render Terrain and game entities
Attributes:
camera: Used to show portions of the game world
* Do not modify the state of the game, entities, terrain, etc.
* Only draw the state of the game, nothing else
The view should query the model or controller and make changes
based on that info. The view should not collect input from the
user.
"""
[docs] def __init__(self, game: Game, model: WorldModel):
self._game = game
self._model = model
self._has_centered_camera: bool = False
# interface
self.camera = Camera(game.window.base_resolution)
self.debug_pathfinding: bool = False
self.clamp_primary_terrain: bool = True
[docs] def update(self, delta_time: float):
self._update_camera(delta_time)
[docs] def draw(self, surface: pygame.Surface):
if self.clamp_primary_terrain:
self.camera.clamp(self._model.boundaries)
else:
next_boundaries = self._model.next_boundaries
if next_boundaries.contains(self.camera.get_rect()):
self.camera.clamp(next_boundaries)
else:
self.camera.clamp(self._model.total_boundaries)
_surface = None
if self.camera.zoom != 0.0:
_surface = surface
area = self.camera.get_rect()
surface = pygame.Surface(area.size)
offset = self.camera.render_offset()
self._model.terrain.draw(surface, offset)
if not self.clamp_primary_terrain:
next_offset = pygame.Vector2(offset.x + self._model.terrain.boundaries.width, offset.y + 0)
self._model.next_terrain.draw(surface, next_offset)
self._draw_units(surface, offset)
self._model.projectiles.draw(surface, offset)
self._model.particles.draw(surface, offset)
if self.debug_pathfinding:
self._draw_path_debug(surface)
if _surface is not None:
pygame.transform.scale(surface, _surface.get_size(), _surface)
[docs] def reset_camera(self):
self._has_centered_camera = False
self._update_camera(0)
def _update_camera(self, delta_time: float):
"""
Update the camera's position to follow the player's Units
"""
target_pos = self.get_team_center("player")
if target_pos:
self.camera.set_target_position(target_pos)
self.camera.update(delta_time)
# prevent camera from panning from 0,0
if not self._has_centered_camera:
self.camera.reset_movement()
self._has_centered_camera = True
def _draw_units(self, surface: pygame.Surface, offset: pygame.Vector2):
units = self._model.get_all_units()
systems.draw_entities(surface, shift=offset)
# # organize entities for layered rendering
# entity_list = []
# for unit in units:
# for entity in unit.entities + unit.dead_entities:
# entity_list.append((entity.pos[1] + entity.img.get_height() // 2, len(entity_list), entity))
#
# entity_list.sort()
#
# for entity in entity_list:
# entity[2].draw(surface, shift=offset)
for unit in units:
if unit.team == "player":
unit.draw_banner(surface, offset)
if unit.is_selected:
unit.draw_border(surface, offset)
def _draw_path_debug(self, surface: pygame.Surface):
"""
Draw lines to indicate the pathfinding
"""
# FIXME - update to align to ecs
for entity in self._model.get_all_entities():
if entity._parent_unit.default_behaviour != "swarm":
if entity.behaviour.current_path and len(entity.behaviour.current_path):
points = [
(p[0] + self.camera.render_offset()[0], p[1] + self.camera.render_offset()[1])
for p in ([entity.pos] + entity.behaviour.current_path)
]
pygame.draw.lines(surface, (255, 0, 0), False, points)
[docs] def get_team_center(self, team) -> Optional[pygame.Vector2]:
"""
Get centre coordinates for the team
"""
count = 0
pos_totals = pygame.Vector2()
for unit in self._model.get_all_units():
if unit.team == team:
pos_totals += unit.pos
count += 1
if count:
return pygame.Vector2(pos_totals / count)
else:
return None
[docs] def view_to_point(self, point: pygame.Vector2):
"""
Return map pixel coords under the point
* Point must be relative to the topleft corner of the view
"""
offset = self.camera.render_offset()
return pygame.Vector2(point) - offset