Move radius and angle limits into ray manager.
authorSimon Cross <hodgestar@gmail.com>
Fri, 9 Sep 2016 21:39:02 +0000 (23:39 +0200)
committerSimon Cross <hodgestar@gmail.com>
Fri, 9 Sep 2016 21:39:02 +0000 (23:39 +0200)
tabakrolletjie/enemies.py
tabakrolletjie/lights.py
tabakrolletjie/rays.py

index a4ed7b73432e67f7ef54027b6d1674791c8debd0..d40af93750b64be2592558d14e13c45242b2ba2d 100644 (file)
@@ -126,11 +126,6 @@ class Mould(pymunk.Body):
 
     def damage(self, light, space, moulds):
         """Take damage for light, adjusted for resistances."""
-        distance = light.position.get_distance(self.position)
-        if distance < (light.radius_limits[0] or 0.0):
-            return False
-        if distance > (light.radius_limits[1] or 50.0):
-            return False
         self._health -= 3
         if self._health <= 0 and self._age <= 120:
             # We die of damage
index 5334d641110a81443d680fff23ad3ee497e517c3..d20ab3570663ba1132345d1f093aea8b9b7985ce 100644 (file)
@@ -96,27 +96,34 @@ class BaseLight(object):
         "white": (255, 255, 255),
     }
 
+    # defaults
+    RAY_MANAGER = RayPolyManager
     FITTING_IMG = None
+    FITTING_RADIUS = 10.0
 
     # cached surfaces
     _surface_cache = {}
 
     def __init__(
-            self, colour, position, intensity=1.0,
-            radius_limits=(None, None), angle_limits=None):
+            self, colour, position, intensity=1.0, radius_limits=None,
+            angle_limits=None):
         self.colour = colour
-        self.position = pymunk.Vec2d(position)
         self.on = True
         self.intensity = intensity
-        self.radius_limits = radius_limits
-        self.angle_limits = angle_limits
         self.body = pymunk.Body(0, 0, pymunk.body.Body.STATIC)
-        self.fitting = pymunk.Circle(self.body, 10.0, self.position)
-        self.fitting.filter = FITTINGS_FILTER
         self.body.light = self
-        self.ray_manager = RayPolyManager(self.body, LIGHT_FILTER)
+        self.ray_manager = self.RAY_MANAGER(
+            self.body, position, ray_filter=LIGHT_FILTER,
+            radius_limits=radius_limits, angle_limits=angle_limits)
+        self.fitting = pymunk.Circle(
+            self.body, self.FITTING_RADIUS, self.ray_manager.position)
+        self.fitting.filter = FITTINGS_FILTER
         self._image = None
 
+    @property
+    def position(self):
+        return self.ray_manager.position
+
     @classmethod
     def load(cls, config):
         kw = config.copy()
@@ -129,31 +136,25 @@ class BaseLight(object):
     def add(self, space):
         if self.body.space is not None:
             space.remove(self.body, *self.body.shapes)
-        self.ray_manager.generate_rays(space, self.position)
-        self.ray_manager.set_angle_limits(self.angle_limits)
-        ray_shapes = self.ray_manager.polys()
-        space.add(self.body, self.fitting, *ray_shapes)
-
-    def shapes_for_ray_polys(self, ray_polys):
-        return ray_polys
+        space.add(self.body, self.fitting)
+        self.ray_manager.set_space(space)
+        self.ray_manager.update_shapes()
 
     def toggle(self):
         self.on = not self.on
 
-    def _cached_surfaces(self, surface):
-        radius_mask = self._surface_cache.get('radius_mask')
-        if radius_mask is None:
-            radius_mask = self._surface_cache['radius_mask'] = (
-                pygame.surface.Surface(
-                    surface.get_size(), pgl.SWSURFACE)).convert_alpha()
-
-        ray_mask = self._surface_cache.get('ray_mask')
-        if ray_mask is None:
-            ray_mask = self._surface_cache['ray_mask'] = (
-                pygame.surface.Surface(
-                    surface.get_size(), pgl.SWSURFACE)).convert_alpha()
+    def _cached_surface(self, name, surface):
+        surf = self._surface_cache.get(name)
+        if surf is None:
+            surf = self._surface_cache[name] = pygame.surface.Surface(
+                surface.get_size(), pgl.SWSURFACE
+            ).convert_alpha()
+        return surf
 
-        return radius_mask, ray_mask
+    def light_colour(self):
+        light_colour = self.COLOURS[self.colour]
+        intensity = int(255 * self.intensity)
+        return light_colour + (intensity,)
 
     def render_light(self, surface):
         if not self.on:
@@ -161,20 +162,17 @@ class BaseLight(object):
 
         dt = DetailedTimer("render_light")
         dt.start()
-        dt.show("%s, %s" % (self.ray_manager._start, self.ray_manager._end))
-
-        max_radius = self.radius_limits[1] or 50.0
-        min_radius = self.radius_limits[0] or 0
 
-        rw = max_radius * 2
-        rx, ry = pymunk.pygame_util.to_pygame(self.position, surface)
-        dest_rect = pygame.rect.Rect(rx, ry, rw, rw)
-        dest_rect.move_ip(- max_radius, - max_radius)
+        max_radius = self.ray_manager.max_radius
+        min_radius = self.ray_manager.min_radius
+        dest_rect = self.ray_manager.pygame_rect(surface)
 
         white, black = (255, 255, 255, 255), (0, 0, 0, 0)
+        light_colour = self.light_colour()
 
-        radius_mask, ray_mask = self._cached_surfaces(surface)
+        radius_mask = self._cached_surface('radius_mask', surface)
         radius_mask.set_clip(dest_rect)
+        ray_mask = self._cached_surface('ray_mask', surface)
         ray_mask.set_clip(dest_rect)
 
         ray_mask.fill(black)
@@ -183,12 +181,8 @@ class BaseLight(object):
             pygame.draw.polygon(ray_mask, white, pygame_poly, 1)
         dt.lap("ray mask rendered")
 
-        light_colour = self.COLOURS[self.colour]
-        intensity = int(255 * self.intensity)
-        light_colour = light_colour + (intensity,)
-
         radius_mask.fill(black)
-        centre = pymunk.pygame_util.to_pygame(self.position, surface)
+        centre = self.ray_manager.pygame_position(surface)
         pygame.draw.circle(
             radius_mask, light_colour, centre, int(max_radius), 0)
         pygame.draw.circle(
@@ -211,7 +205,7 @@ class BaseLight(object):
         return self._image
 
     def render_fitting(self, surface):
-        rx, ry = pymunk.pygame_util.to_pygame(self.position, surface)
+        rx, ry = self.ray_manager.pygame_position(surface)
         surface.blit(self.get_image(), (rx - 32, ry - 32), None, 0)
 
     def tick(self):
@@ -238,8 +232,5 @@ class SpotLight(BaseLight):
 
     def tick(self):
         if self.angular_velocity:
-            start, end = self.angle_limits
-            start = (start + self.angular_velocity) % 360.0
-            end = (end + self.angular_velocity) % 360.0
-            self.angle_limits = (start, end)
-            self.ray_manager.set_angle_limits(self.angle_limits)
+            self.ray_manager.rotate_degrees(self.angular_velocity)
+            self.ray_manager.update_shapes()
index 32ae1a749bbb86c539e8c46e95709eb0292bd0d0..9e5a1bf6d0050534ec79fda916e55c7c01d0a595 100644 (file)
@@ -2,6 +2,8 @@
 
 import math
 
+import pygame.rect
+
 import pymunk
 import pymunk.autogeometry
 import pymunk.pygame_util
@@ -75,19 +77,70 @@ def to_pymunk_radians(deg):
 
 
 class RayPolyManager(object):
-    def __init__(self, body, ray_filter):
+    def __init__(
+            self, body, position, ray_filter, radius_limits, angle_limits):
         self._body = body  # light's body
+        self._position = pymunk.Vec2d(position)  # light's position
         self._ray_filter = ray_filter  # light filter
         self._rays = []  # list of RayPolys
         self._start = None  # normal vector in direction of start angle limit
         self._end = None  # normal vector in direction of end angle limit
+        self._set_angle_limits(angle_limits)
+        self._max_radius = None  # maximum radius in pixels
+        self._min_radius = None  # minimum radius in pixels
+        self._set_radius_limits(radius_limits)
+        self._old_poly_cache = None  # last polys added to the space
         self._poly_cache = None  # list of pymunk.Polys for rays
+        self._space = None  # space the rays form part of
+
+    def set_space(self, space):
+        self._space = space
+        self._rays = calculate_ray_polys(
+            self._space, self._position, self._ray_filter)
+        self._poly_cache = None
+
+    def update_shapes(self):
+        if self._old_poly_cache:
+            self._space.remove(*self._old_poly_cache)
+        new_polys = self._old_poly_cache = self.polys()
+        self._space.add(*new_polys)
+
+    @property
+    def position(self):
+        return self._position
+
+    @property
+    def max_radius(self):
+        return self._max_radius
+
+    @max_radius.setter
+    def max_radius_setter(self, value):
+        self._max_radius = value or 0.0
 
-    def generate_rays(self, space, position):
-        self._rays = calculate_ray_polys(space, position, self._ray_filter)
+    @property
+    def min_radius(self):
+        return self._min_radius
+
+    @min_radius.setter
+    def min_radius_setter(self, value):
+        self._min_radius = value or 0.0
+
+    def _set_radius_limits(self, radius_limits):
+        if radius_limits is None or not radius_limits[0]:
+            self._min_radius = 0
+        else:
+            self._min_radius = radius_limits[0]
+        if radius_limits is None or not radius_limits[1]:
+            self._max_radius = 50.0
+        else:
+            self._max_radius = radius_limits[1]
+
+    def rotate_degrees(self, degrees):
+        self._start.rotate_degrees(degrees)
+        self._end.rotate_degrees(degrees)
         self._poly_cache = None
 
-    def set_angle_limits(self, angle_limits):
+    def _set_angle_limits(self, angle_limits):
         if angle_limits is None:
             self._start = None
             self._end = None
@@ -109,6 +162,17 @@ class RayPolyManager(object):
                     poly_cache.append(poly)
         return self._poly_cache
 
+    def pygame_position(self, surface):
+        return pymunk.pygame_util.to_pygame(self._position, surface)
+
+    def pygame_rect(self, surface):
+        half_width = self.max_radius
+        rect_width = half_width * 2
+        rect_x, rect_y = pymunk.pygame_util.to_pygame(self._position, surface)
+        dest_rect = pygame.rect.Rect(rect_x, rect_y, rect_width, rect_width)
+        dest_rect.move_ip(-half_width, -half_width)
+        return dest_rect
+
     def pygame_polys(self, surface):
         return [
             [pymunk.pygame_util.to_pygame(v, surface)