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 nearest(self, pos, surfpos=False, max_distance=1.0):
79 surface = pygame.display.get_surface()
80 pos = pymunk.pygame_util.from_pygame(pos, surface)
81 point_info = self._space.point_query_nearest(
82 pos, 1.0, pymunk.ShapeFilter(mask=FITTINGS_CATEGORY))
83 if point_info is not None:
84 return point_info.shape.body.light
87 def render_light(self, surface):
88 for light in self._lights:
89 light.render_light(surface)
91 def render_fittings(self, surface):
92 for light in self._lights:
93 light.render_fitting(surface)
96 class BaseLight(object):
97 """ Common light functionality. """
101 "green": (0, 255, 0),
102 "blue": (0, 255, 255),
103 "yellow": (255, 255, 0),
104 "white": (255, 255, 255),
107 def __init__(self, colour, position):
109 self.position = pymunk.Vec2d(position)
111 self.body = pymunk.Body(0, 0, pymunk.body.Body.STATIC)
112 self.fitting = pymunk.Circle(self.body, 10.0, self.position)
113 self.body.light = self
116 def load(cls, config):
118 light_type = kw.pop("type")
120 c for c in cls.__subclasses__()
121 if c.__name__.lower() == light_type]
122 return light_class(**kw)
124 def add(self, space):
125 if self.body.space is not None:
126 space.remove(self.body, *self.body.shapes)
127 shapes = self.shapes_for_ray_polys(
128 calculate_ray_polys(space, self.body, self.position))
130 shape.filter = LIGHT_FILTER
131 self.fitting.filter = FITTINGS_FILTER
132 space.add(self.body, self.fitting, *shapes)
134 def shapes_for_ray_polys(self, ray_polys):
138 self.on = not self.on
140 def render_light(self, surface):
143 subsurface = surface.copy()
144 light_colour = self.COLOURS[self.colour]
145 for shape in self.body.shapes:
146 if shape is self.fitting:
149 pymunk.pygame_util.to_pygame(v, surface) for v in
150 shape.get_vertices()]
152 subsurface, light_colour, pygame_poly, 0)
154 subsurface, light_colour, True, pygame_poly, 1)
155 subsurface.set_alpha(50)
156 surface.blit(subsurface, (0, 0), None)
158 def render_fitting(self, surface):
160 surface, (255, 255, 0),
161 pymunk.pygame_util.to_pygame(self.fitting.offset, surface),
162 int(self.fitting.radius))
165 class SpotLight(BaseLight):
167 self, colour="white", position=None, direction=90.0, spread=45.0):
168 super(SpotLight, self).__init__(colour, position)
169 self.direction = direction
174 class Lamp(BaseLight):
175 def __init__(self, colour="white", position=None, radius=100.0):
176 super(Lamp, self).__init__(colour, position)