1 """ May it be a light for you in dark places, when all other lights go out.
5 import pymunk.pygame_util
8 from .constants import (
9 SCREEN_SIZE, LIGHT_CATEGORY, FITTINGS_CATEGORY)
10 from .utils import debug_timer
12 LIGHT_FILTER = pymunk.ShapeFilter(
13 mask=pymunk.ShapeFilter.ALL_MASKS ^ (
14 LIGHT_CATEGORY | FITTINGS_CATEGORY),
15 categories=LIGHT_CATEGORY)
17 FITTINGS_FILTER = pymunk.ShapeFilter(
18 mask=pymunk.ShapeFilter.ALL_MASKS ^ (
19 LIGHT_CATEGORY | FITTINGS_CATEGORY),
20 categories=FITTINGS_CATEGORY)
24 """ An iterable that returns ordered rays from pos to the edge of the
25 screen, starting with the edge point (0, 0) and continuing clockwise
26 in pymunk coordinates.
29 left, right, bottom, top = 0, w, 0, h
31 for y in range(0, h, step):
32 yield pymunk.Vec2d(left, y)
33 for x in range(0, w, step):
34 yield pymunk.Vec2d(x, top)
35 for y in range(top, -1, -step):
36 yield pymunk.Vec2d(right, y)
37 for x in range(right, -1, -step):
38 yield pymunk.Vec2d(x, bottom)
41 @debug_timer("lights.calculate_ray_polys")
42 def calculate_ray_polys(space, body, position):
43 position = pymunk.Vec2d(position)
46 for ray in screen_rays(position):
47 info = space.segment_query_first(position, ray, 1, LIGHT_FILTER)
48 point = ray if info is None else info.point
49 vertices.append(point)
51 trial_poly = pymunk.Poly(None, vertices)
52 trial_poly.update(pymunk.Transform.identity())
53 query_prev = trial_poly.point_query(vertices[-2])
54 query_pos = trial_poly.point_query(position)
55 if query_prev.distance < -0.01 or query_pos.distance < -0.01:
56 new_poly = pymunk.Poly(body, vertices[:-1])
57 vertices = [position, vertices[-1]]
58 ray_polys.append(new_poly)
60 vertices = trial_poly.get_vertices() + [point]
62 ray_polys.append(pymunk.Poly(body, vertices))
66 class LightManager(object):
67 """ Manages a set of lights. """
69 def __init__(self, space, gamestate):
72 BaseLight.load(cfg) for cfg in gamestate.station["lights"]]
73 for light in self._lights:
74 light.add(self._space)
76 def render_light(self, surface):
77 for light in self._lights:
78 light.render_light(surface)
80 def render_fittings(self, surface):
81 for light in self._lights:
82 light.render_fitting(surface)
85 class BaseLight(object):
86 """ Common light functionality. """
91 "blue": (0, 255, 255),
92 "yellow": (255, 255, 0),
93 "white": (255, 255, 255),
96 def __init__(self, colour, position):
98 self.position = pymunk.Vec2d(position)
100 self.body = pymunk.Body(0, 0, pymunk.body.Body.STATIC)
101 self.fitting = pymunk.Circle(self.body, 10.0, self.position)
102 self.body.light = self
105 def load(cls, config):
107 light_type = kw.pop("type")
109 c for c in cls.__subclasses__()
110 if c.__name__.lower() == light_type]
111 return light_class(**kw)
113 def add(self, space):
114 if self.body.space is not None:
115 space.remove(self.body, *self.body.shapes)
116 shapes = self.shapes_for_ray_polys(
117 calculate_ray_polys(space, self.body, self.position))
119 shape.filter = LIGHT_FILTER
120 self.fitting.filter = FITTINGS_FILTER
121 space.add(self.body, self.fitting, *shapes)
123 def shapes_for_ray_polys(self, ray_polys):
127 self.on = not self.on
129 def render_light(self, surface):
132 subsurface = surface.copy()
133 light_colour = self.COLOURS[self.colour]
134 for shape in self.body.shapes:
135 if shape is self.fitting:
138 pymunk.pygame_util.to_pygame(v, surface) for v in
139 shape.get_vertices()]
141 subsurface, light_colour, pygame_poly, 0)
143 subsurface, light_colour, True, pygame_poly, 1)
144 subsurface.set_alpha(50)
145 surface.blit(subsurface, (0, 0), None)
147 def render_fitting(self, surface):
149 surface, (255, 255, 0),
150 pymunk.pygame_util.to_pygame(self.fitting.offset, surface),
151 int(self.fitting.radius))
154 class SpotLight(BaseLight):
156 self, colour="white", position=None, direction=90.0, spread=45.0):
157 super(SpotLight, self).__init__(colour, position)
158 self.direction = direction
163 class Lamp(BaseLight):
164 def __init__(self, colour="white", position=None, radius=100.0):
165 super(Lamp, self).__init__(colour, position)