source: nagslang/enemies.py @ 277:56e42c00da25

Last change on this file since 277:56e42c00da25 was 277:56e42c00da25, checked in by Neil Muller <drnlmuller@…>, 7 years ago

Protagonist and enemies should see the world

File size: 4.3 KB
Line 
1import math
2
3import pymunk
4import pymunk.pygame_util
5
6from nagslang import render
7from nagslang.constants import COLLISION_TYPE_ENEMY, ZORDER_MID
8from nagslang.game_object import GameObject, SingleShapePhysicser, make_body
9from nagslang.mutators import FLIP_H
10from nagslang.resources import resources
11
12
13def get_editable_enemies():
14    classes = []
15    for cls_name, cls in globals().iteritems():
16        if isinstance(cls, type) and issubclass(cls, Enemy):
17            if hasattr(cls, 'requires'):
18                classes.append((cls_name, cls))
19    return classes
20
21
22class Enemy(GameObject):
23    """A base class for mobile enemies"""
24
25    def __init__(self, space, world, position):
26        self._setup_physics(space, position)
27        self._setup_renderer()
28
29        super(Enemy, self).__init__(
30            self._physicser, self.renderer)
31        self.zorder = ZORDER_MID
32        self.world = world
33
34    def _get_image(self, name, *transforms):
35        return resources.get_image('creatures', name, transforms=transforms)
36
37    def _setup_physics(self, space, position):
38        raise NotImplementedError
39
40    def _setup_renderer(self):
41        raise NotImplementedError
42
43    def attack(self):
44        raise NotImplementedError
45
46    @classmethod
47    def requires(cls):
48        return [("name", "string"), ("position", "coordinates")]
49
50
51class PatrollingAlien(Enemy):
52    is_moving = True  # Always walking.
53
54    def __init__(self, space, world, position, end_position):
55        # An enemy that patrols between the two points
56        super(PatrollingAlien, self).__init__(space, world, position)
57        self._start_pos = position
58        self._end_pos = end_position
59        self._direction = 'away'
60
61    def _setup_physics(self, space, position):
62        self._body = make_body(10, pymunk.inf, position, 0.8)
63
64        self._shape = pymunk.Circle(self._body, 30)
65
66        self._shape.elasticity = 1.0
67        self._shape.friction = 0.05
68        self._shape.collision_type = COLLISION_TYPE_ENEMY
69        self._physicser = SingleShapePhysicser(space, self._shape)
70        self.impulse_factor = 50
71        self.angle = 0
72
73    def _setup_renderer(self):
74        self.renderer = render.FacingSelectionRenderer({
75            'left': render.TimedAnimatedRenderer(
76                [self._get_image('alien_A_1.png'),
77                 self._get_image('alien_A_2.png')], 3),
78            'right': render.TimedAnimatedRenderer(
79                [self._get_image('alien_A_1.png', FLIP_H),
80                 self._get_image('alien_A_2.png', FLIP_H)], 3),
81        })
82
83    def get_render_angle(self):
84        # No image rotation when rendering, please.
85        return 0
86
87    def get_facing_direction(self):
88        # Enemies can face left or right.
89        if - math.pi / 2 < self.angle <= math.pi / 2:
90            return 'right'
91        else:
92            return 'left'
93
94    def _switch_direction(self):
95        if self._direction == 'away':
96            self._direction = 'towards'
97        else:
98            self._direction = 'away'
99
100    def set_direction(self, dx, dy):
101        self.angle = pymunk.Vec2d((dx, dy)).angle
102        self._body.apply_impulse(
103            (dx * self.impulse_factor, dy * self.impulse_factor))
104
105    def animate(self):
106        # Calculate the step every frame
107        if self._direction == 'away':
108            target = self._end_pos
109        else:
110            target = self._start_pos
111        x_step = 0
112        y_step = 0
113        if (target[0] < self._body.position[0]):
114            x_step = max(-1, target[0] - self._body.position[0])
115        elif (target[0] > self._body.position[0]):
116            x_step = min(1, target[0] - self._body.position[0])
117        if abs(x_step) < 0.5:
118            x_step = 0
119        if (target[1] < self._body.position[1]):
120            y_step = max(-1, target[1] - self._body.position[1])
121        elif (target[1] > self._body.position[1]):
122            y_step = min(1, target[1] - self._body.position[1])
123        if abs(y_step) < 0.5:
124            y_step = 0
125        if abs(x_step) < 1 and abs(y_step) < 1:
126            self._switch_direction()
127        self.set_direction(x_step, y_step)
128        super(PatrollingAlien, self).animate()
129
130    def collide_with_protagonist(self, protagonist):
131        protagonist.lose_health(15)
132
133    @classmethod
134    def requires(cls):
135        return [("name", "string"), ("position", "coordinates"),
136                ("end_position", "coordinates")]
Note: See TracBrowser for help on using the repository browser.