From b1cbbc6a48f68e43b72293ef0d6d24450ce40c7e Mon Sep 17 00:00:00 2001 From: Simon Cross Date: Fri, 9 Sep 2016 23:39:02 +0200 Subject: [PATCH] Move radius and angle limits into ray manager. --- tabakrolletjie/enemies.py | 5 --- tabakrolletjie/lights.py | 87 ++++++++++++++++++--------------------- tabakrolletjie/rays.py | 72 ++++++++++++++++++++++++++++++-- 3 files changed, 107 insertions(+), 57 deletions(-) diff --git a/tabakrolletjie/enemies.py b/tabakrolletjie/enemies.py index a4ed7b7..d40af93 100644 --- a/tabakrolletjie/enemies.py +++ b/tabakrolletjie/enemies.py @@ -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 diff --git a/tabakrolletjie/lights.py b/tabakrolletjie/lights.py index 5334d64..d20ab35 100644 --- a/tabakrolletjie/lights.py +++ b/tabakrolletjie/lights.py @@ -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() diff --git a/tabakrolletjie/rays.py b/tabakrolletjie/rays.py index 32ae1a7..9e5a1bf 100644 --- a/tabakrolletjie/rays.py +++ b/tabakrolletjie/rays.py @@ -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) -- 2.34.1