ba21bbeb94857284f829ebbb8ccc1c731d84ee9e
[koperkapel.git] / koperkapel / vehicles / base.py
1 """ Base class for vehicles.  """
2
3 import math
4 import random
5 from itertools import chain, islice, repeat
6 from pygame.constants import BLEND_RGBA_MULT
7 from pgzero.loaders import images
8 from ..actors.orientatedsurf import SelectableSurfActor
9 from ..actors.animsurf import AnimatedSurfActor
10 from ..weapons import default_weapons
11
12
13 class Vehicle:
14     """ Vehicle base class. """
15
16     vehicle_type = None
17     overlay_frame_no = None
18     approximate_radius = 200
19     weapons_taped_on = True
20     selected_seat_overlay_color = (255, 0, 0, 255)
21
22     def __init__(self):
23         self.seats = self.init_seats()
24         self.game_pos = (0, 0)
25
26     def roach_management_overlay(self):
27         return images.load("vehicles/walking/background")
28
29     def roach_management_frame(self):
30         if self.overlay_frame_no is None:
31             return None
32         return images.load("vehicle_big/%s_%d" % (
33             self.vehicle_type, self.overlay_frame_no))
34
35     def init_seats(self):
36         raise NotImplementedError("Vehicles should specify a list of seats")
37
38     def seating(self, world):
39         roach_seating = world.vehicles[self.vehicle_type].seating
40         roach_seating_numbers = enumerate(zip(roach_seating, self.seats))
41         return {
42             roach: seat_pos
43             for seat_pos, (roach, _) in roach_seating_numbers if roach
44         }
45
46     def seat_roach(self, world, roach, seat_pos):
47         vehicle = world.vehicles[self.vehicle_type]
48         seats = len(self.seats)
49         seating = list(vehicle.seating)
50         seating = list(islice(
51             chain(seating, repeat(None, seats)), 0, seats))
52         seating[seat_pos] = roach
53         # line below records new seating on the world proxy
54         vehicle.seating = seating
55
56     def roach_at(self, world, seat_pos):
57         roach_seating = world.vehicles[self.vehicle_type].seating
58         if seat_pos >= len(roach_seating):
59             return None
60         return roach_seating[seat_pos]
61
62     def changed(self):
63         return False  # TODO: remove this
64
65     _vehicle_types = {}
66
67     @classmethod
68     def current(cls, world):
69         return cls.by_type(world.vehicles.current)
70
71     @classmethod
72     def by_type(cls, vehicle_type):
73         return cls._vehicle_types.get(vehicle_type)()
74
75     @classmethod
76     def register(cls, vehicle_cls):
77         cls._vehicle_types[vehicle_cls.__name__.lower()] = vehicle_cls
78
79     @classmethod
80     def random(cls):
81         return random.choice(list(cls._vehicle_types.keys()))
82
83     @classmethod
84     def register_all(cls):
85         from .walking import Walking
86         from .quadcopter import Quadcopter
87         from .robot import Robot
88         from .roomba import Roomba
89         cls.register(Walking)
90         cls.register(Quadcopter)
91         cls.register(Robot)
92         cls.register(Roomba)
93
94     def _avatar_frame(self, i, weapon, suffix="_tiles"):
95         vehicle = images.load("vehicle%s/%s_%d" % (
96             suffix, self.vehicle_type, i + 1))
97         frame = vehicle.copy()
98         frame.blit(weapon.surf, (0, 0))
99         return frame
100
101     def get_avatar(self, world):
102         weapon = default_weapons.assemble(
103             world.weapons.current, tape=self.weapons_taped_on)
104         frames = [self._avatar_frame(i, weapon) for i in range(4)]
105         return AnimatedSurfActor(frames, anchor=(0, 0))
106
107
108 class Seat:
109     """ A space in a vehicle for a roach.
110
111     * pos -- (x, y) position of the seat relative to the centre of the vehicle.
112       x and y may be numbers from approximately -1.0 to 1.0. They will be
113       multiplied by the approximate_radius of the vehicle.
114     * roach -- name of the roach occupying the seat, if any.
115     * allowed -- f(roach) for checking whether a roach may occupy the
116       seat.
117     """
118
119     def __init__(self, vehicle, pos, roach=None, allowed=None):
120         self.vehicle = vehicle
121         self.pos = pos
122         self.roach = roach
123         self.allowed = allowed or (lambda roach: True)
124         vrad = vehicle.approximate_radius
125         self.vehicle_pos = (pos[0] * vrad, pos[1] * vrad)
126
127     def actor(self):
128         seat = images.load("vehicles/walking/seat")
129         selected_seat = seat.copy()
130         selected_seat.fill(
131             self.vehicle.selected_seat_overlay_color, None, BLEND_RGBA_MULT)
132         return SelectableSurfActor(seat, selected_seat)
133
134
135 def circle_of_seats(n_seats, **kw):
136     d_theta = 2 * math.pi / n_seats
137     return [
138         Seat(pos=(math.sin(i * d_theta), math.cos(i * d_theta)), **kw)
139         for i in range(n_seats)]
140
141
142 Vehicle.register_all()