Merge branch 'master' of ctpug.org.za:tabakrolletjie
[tabakrolletjie.git] / tabakrolletjie / enemies.py
1 # Boyd, the friendly, misunderstood turnip loving, light hating space mould
2
3 import random
4
5 import pymunk
6 import pymunk.pygame_util
7 import pygame.draw
8 import pygame.surface
9 import pygame.display
10
11 import pygame.locals as pgl
12
13 from .constants import SCREEN_SIZE, MOULD_CATEGORY, OBSTACLE_CATEGORY
14 from .loader import loader
15
16 MOULD_FILTER = pymunk.ShapeFilter(
17     mask=MOULD_CATEGORY | OBSTACLE_CATEGORY,
18     categories=MOULD_CATEGORY)
19
20
21 class Mould(pymunk.Body):
22     """A segment of Boyd"""
23
24     def __init__(self, gamestate, space, pos):
25         super(Mould, self).__init__(0, 0, pymunk.Body.STATIC)
26         self.position = pos
27         self._shape = pymunk.Circle(self, 16)
28         space.add(self, self._shape)
29         self._shape.filter = MOULD_FILTER
30         self._resistances = {}
31         self._age = 0
32         self._img = None
33
34     def pygame_pos(self, surface):
35         """Convert to pygame coordinates and offset position so
36            our position is the centre of the image."""
37         # The odd sign combination is because of the pymunk / pygame
38         # transform, but we do it this way to exploit Vec2d math magic
39         return pymunk.pygame_util.to_pygame(self.position + (-16, 16), surface)
40
41     def get_image(self):
42         if not self._img:
43             name = random.choice(
44                 ('mouldA.png', 'mouldB.png', 'mouldC.png'))
45             self._img = loader.load_image("32", name)
46         return self._img
47
48     def tick(self, gamestate, space, moulds):
49         """Grow and / or Die"""
50         self._age += 1
51
52         refresh = False
53
54         if (self._age % 15) == 0 and len(moulds) < 1000:
55             # Spawn a new child, if we can
56             spawn = True
57             choice = random.randint(0, 4)
58             if choice == 0:
59                 pos = self.position + (0, 24)
60             elif choice == 1:
61                 pos = self.position + (24, 0)
62             elif choice == 2:
63                 pos = self.position + (-24, 0)
64             else:
65                 pos = self.position + (0, -24)
66             # check for bounds
67             if pos[0] < 0 or pos[0] >= SCREEN_SIZE[0]:
68                 spawn = False
69             if pos[1] < 0 or pos[1] >= SCREEN_SIZE[1]:
70                 spawn = False
71             # Check for free space
72             # We allow some overlap, hence not checking full radius
73             query = space.point_query(pos, 8, MOULD_FILTER)
74             if query:
75                 # for x in query:
76                 #     if not isinstance(x.shape.body, Mould):
77                 #         print x.shape, x.shape.body
78                 spawn = False
79             if spawn:
80                 child = Mould(gamestate, space, pos)
81                 moulds.append(child)
82                 refresh = True
83
84         if self._age > 120:
85             # We die of old age
86             space.remove(self, self._shape)
87             moulds.remove(self)
88             refresh = True
89         return refresh
90
91     def damage(self, light_color, intensity):
92         """Take damage for light, adjusted for resistances."""
93
94
95 class Boyd(object):
96
97     def __init__(self, gamestate, space):
98         seed = Mould(gamestate, space, (350, 350))
99         self._moulds = [seed]
100         self._image = pygame.surface.Surface(SCREEN_SIZE)
101         self._image.convert_alpha(pygame.display.get_surface())
102         self._image.fill((0, 0, 0, 0))
103         self._image.blit(seed.get_image(),
104             seed.pygame_pos(self._image), None, pgl.BLEND_RGBA_ADD)
105
106     def tick(self, gamestate, space):
107         redraw = False
108         for mould in self._moulds[:]:
109             if mould.tick(gamestate, space, self._moulds):
110                 redraw = True
111         if redraw:
112             self._image.fill((0, 0, 0, 0))
113             for mould in self._moulds:
114                 self._image.blit(mould.get_image(),
115                     mould.pygame_pos(self._image), None, pgl.BLEND_RGBA_ADD)
116
117     def render(self, surface):
118         """Draw ourselves"""
119         surface.blit(self._image, (0, 0), None, pgl.BLEND_RGBA_ADD)