1 """ May it be a light for you in dark places, when all other lights go out.
5 import pymunk.pygame_util
9 from .constants import (
10 SCREEN_SIZE, LIGHT_CATEGORY, FITTINGS_CATEGORY)
11 from .utils import debug_timer
13 LIGHT_FILTER = pymunk.ShapeFilter(
14 mask=pymunk.ShapeFilter.ALL_MASKS ^ (
15 LIGHT_CATEGORY | FITTINGS_CATEGORY),
16 categories=LIGHT_CATEGORY)
18 FITTINGS_FILTER = pymunk.ShapeFilter(
19 mask=pymunk.ShapeFilter.ALL_MASKS ^ (
20 LIGHT_CATEGORY | FITTINGS_CATEGORY),
21 categories=FITTINGS_CATEGORY)
25 """ An iterable that returns ordered rays from pos to the edge of the
26 screen, starting with the edge point (0, 0) and continuing clockwise
27 in pymunk coordinates.
30 left, right, bottom, top = 0, w, 0, h
32 for y in range(0, h, step):
33 yield pymunk.Vec2d(left, y)
34 for x in range(0, w, step):
35 yield pymunk.Vec2d(x, top)
36 for y in range(top, -1, -step):
37 yield pymunk.Vec2d(right, y)
38 for x in range(right, -1, -step):
39 yield pymunk.Vec2d(x, bottom)
42 @debug_timer("lights.calculate_ray_polys")
43 def calculate_ray_polys(space, body, position):
44 position = pymunk.Vec2d(position)
47 for ray in screen_rays(position):
48 info = space.segment_query_first(position, ray, 1, LIGHT_FILTER)
49 point = ray if info is None else info.point
50 vertices.append(point)
52 trial_poly = pymunk.Poly(None, vertices)
53 trial_poly.update(pymunk.Transform.identity())
54 query_prev = trial_poly.point_query(vertices[-2])
55 query_pos = trial_poly.point_query(position)
56 if query_prev.distance < -0.01 or query_pos.distance < -0.01:
57 new_poly = pymunk.Poly(body, vertices[:-1])
58 vertices = [position, vertices[-1]]
59 ray_polys.append(new_poly)
61 vertices = trial_poly.get_vertices() + [point]
63 ray_polys.append(pymunk.Poly(body, vertices))
67 class LightManager(object):
68 """ Manages a set of lights. """
70 def __init__(self, space, gamestate):
73 BaseLight.load(cfg) for cfg in gamestate.station["lights"]]
74 for light in self._lights:
75 light.add(self._space)
77 def toggle_nearest(self, *args, **kw):
78 light = self.nearest(*args, **kw)
82 def nearest(self, pos, surfpos=False, max_distance=1.0):
84 surface = pygame.display.get_surface()
85 pos = pymunk.pygame_util.from_pygame(pos, surface)
86 point_info = self._space.point_query_nearest(
87 pos, max_distance, pymunk.ShapeFilter(mask=FITTINGS_CATEGORY))
88 if point_info is not None:
89 return point_info.shape.body.light
92 def lit_by(self, pos, surfpos=False, max_distance=0.0):
94 surface = pygame.display.get_surface()
95 pos = pymunk.pygame_util.from_pygame(pos, surface)
96 point_info_list = self._space.point_query(
97 pos, max_distance, pymunk.ShapeFilter(mask=LIGHT_CATEGORY))
98 lights = [p.shape.body.light for p in point_info_list]
99 return [light for light in lights if light.on]
101 def render_light(self, surface):
102 for light in self._lights:
103 light.render_light(surface)
105 def render_fittings(self, surface):
106 for light in self._lights:
107 light.render_fitting(surface)
110 class BaseLight(object):
111 """ Common light functionality. """
115 "green": (0, 255, 0),
116 "blue": (0, 255, 255),
117 "yellow": (255, 255, 0),
118 "white": (255, 255, 255),
121 def __init__(self, colour, position):
123 self.position = pymunk.Vec2d(position)
125 self.body = pymunk.Body(0, 0, pymunk.body.Body.STATIC)
126 self.fitting = pymunk.Circle(self.body, 10.0, self.position)
127 self.body.light = self
130 def load(cls, config):
132 light_type = kw.pop("type")
134 c for c in cls.__subclasses__()
135 if c.__name__.lower() == light_type]
136 return light_class(**kw)
138 def add(self, space):
139 if self.body.space is not None:
140 space.remove(self.body, *self.body.shapes)
141 shapes = self.shapes_for_ray_polys(
142 calculate_ray_polys(space, self.body, self.position))
144 shape.filter = LIGHT_FILTER
145 self.fitting.filter = FITTINGS_FILTER
146 space.add(self.body, self.fitting, *shapes)
148 def shapes_for_ray_polys(self, ray_polys):
152 self.on = not self.on
154 def render_light(self, surface):
157 subsurface = surface.copy()
158 light_colour = self.COLOURS[self.colour]
159 for shape in self.body.shapes:
160 if shape is self.fitting:
163 pymunk.pygame_util.to_pygame(v, surface) for v in
164 shape.get_vertices()]
166 subsurface, light_colour, pygame_poly, 0)
168 subsurface, light_colour, True, pygame_poly, 1)
169 subsurface.set_alpha(50)
170 surface.blit(subsurface, (0, 0), None)
172 def render_fitting(self, surface):
174 surface, (255, 255, 0),
175 pymunk.pygame_util.to_pygame(self.fitting.offset, surface),
176 int(self.fitting.radius))
179 class SpotLight(BaseLight):
181 self, colour="white", position=None, direction=90.0, spread=45.0):
182 super(SpotLight, self).__init__(colour, position)
183 self.direction = direction
188 class Lamp(BaseLight):
189 def __init__(self, colour="white", position=None, radius=100.0):
190 super(Lamp, self).__init__(colour, position)