# HG changeset patch # User Neil Muller # Date 1378211978 -7200 # Node ID ce8d4fc3baf4f42f56e4a62663393ed7be70377a # Parent bb297f3f99f479e0d4e76082f46ac1b64080b73e A patrolling alien diff -r bb297f3f99f4 -r ce8d4fc3baf4 nagslang/constants.py --- a/nagslang/constants.py Tue Sep 03 11:21:43 2013 +0200 +++ b/nagslang/constants.py Tue Sep 03 14:39:38 2013 +0200 @@ -13,6 +13,7 @@ COLLISION_TYPE_WALL = 2 COLLISION_TYPE_SWITCH = 3 COLLISION_TYPE_BOX = 4 +COLLISION_TYPE_ENEMY = 5 SWITCH_PUSHERS = [COLLISION_TYPE_PLAYER, COLLISION_TYPE_BOX] diff -r bb297f3f99f4 -r ce8d4fc3baf4 nagslang/enemies.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nagslang/enemies.py Tue Sep 03 14:39:38 2013 +0200 @@ -0,0 +1,116 @@ +import pymunk +import pymunk.pygame_util + +from nagslang.constants import COLLISION_TYPE_ENEMY, ZORDER_MID +from nagslang.game_object import ( + GameObject, SingleShapePhysicser, AnimatedFacingImageRenderer, make_body) +from nagslang.mutators import FLIP_H +from nagslang.resources import resources + + +class Enemy(GameObject): + """A base class for mobile enemies""" + + def __init__(self, space, position): + self._setup_physics(space, position) + self._setup_renderer() + + super(Enemy, self).__init__( + self._physicser, self.renderer) + self.zorder = ZORDER_MID + + def _get_image(self, name, *transforms): + return resources.get_image('creatures', name, transforms=transforms) + + def _setup_physics(self, space, position): + raise NotImplementedError + + def _setup_renderer(self): + raise NotImplementedError + + def attack(self): + raise NotImplementedError + + +class PatrollingAlien(Enemy): + + def __init__(self, space, position, end_position): + # An enemy that patrols between the two points + super(PatrollingAlien, self).__init__(space, position) + self._start_pos = position + self._end_pos = end_position + self._direction = 'away' + + def _setup_physics(self, space, position): + self._body = make_body(5, pymunk.inf, position, 0.8) + + self._shape = pymunk.Circle(self._body, 30) + + self._shape.elasticity = 1.0 + self._shape.friction = 10.0 + self._shape.collision_type = COLLISION_TYPE_ENEMY + self._physicser = SingleShapePhysicser(space, self._shape) + self.impulse_factor = 50 + self.angle = 0 + + def _setup_renderer(self): + self.renderer = AnimatedFacingImageRenderer( + (self._get_image('alien_A_1.png'), + self._get_image('alien_A_1.png'), + self._get_image('alien_A_1.png'), + self._get_image('alien_A_1.png'), + self._get_image('alien_A_1.png'), + self._get_image('alien_A_1.png'), + self._get_image('alien_A_2.png'), + self._get_image('alien_A_2.png'), + self._get_image('alien_A_2.png')), + (self._get_image('alien_A_1.png', FLIP_H), + self._get_image('alien_A_1.png', FLIP_H), + self._get_image('alien_A_1.png', FLIP_H), + self._get_image('alien_A_1.png', FLIP_H), + self._get_image('alien_A_1.png', FLIP_H), + self._get_image('alien_A_1.png', FLIP_H), + self._get_image('alien_A_2.png', FLIP_H), + self._get_image('alien_A_2.png', FLIP_H), + self._get_image('alien_A_2.png', FLIP_H))) + # We're always animated + self.renderer.start() + + def get_render_angle(self): + return self.angle + + def _switch_direction(self): + if self._direction == 'away': + self._direction = 'towards' + else: + self._direction = 'away' + + def set_direction(self, dx, dy): + self.angle = pymunk.Vec2d((dx, dy)).angle + self._body.apply_impulse( + (dx * self.impulse_factor, dy * self.impulse_factor)) + + def animate(self): + # Calculate the step every frame + if self._direction == 'away': + target = self._end_pos + else: + target = self._start_pos + x_step = 0 + y_step = 0 + if (target[0] < self._body.position[0]): + x_step = max(-1, target[0] - self._body.position[0]) + elif (target[0] > self._body.position[0]): + x_step = min(1, target[0] - self._body.position[0]) + if abs(x_step) < 0.5: + x_step = 0 + if (target[1] < self._body.position[1]): + y_step = max(-1, target[1] - self._body.position[1]) + elif (target[1] > self._body.position[1]): + y_step = min(1, target[1] - self._body.position[1]) + if abs(y_step) < 0.5: + y_step = 0 + if abs(x_step) < 1 and abs(y_step) < 1: + self._switch_direction() + self.set_direction(x_step, y_step) + super(PatrollingAlien, self).animate() diff -r bb297f3f99f4 -r ce8d4fc3baf4 nagslang/level.py --- a/nagslang/level.py Tue Sep 03 11:21:43 2013 +0200 +++ b/nagslang/level.py Tue Sep 03 14:39:38 2013 +0200 @@ -2,6 +2,7 @@ import pygame.locals as pgl from nagslang import game_object as go +from nagslang import enemies from nagslang.resources import resources from nagslang.yamlish import load, dump @@ -30,6 +31,7 @@ self._glue = go.PuzzleGlue() self._drawables = [] self._game_objects = [] + self._enemies = [] def _get_data(self): # For overriding in tests. @@ -43,6 +45,7 @@ 'base_tile': self.basetile, 'polygons': self.polygons, 'game_objects': self._game_objects, + 'enemies': self._enemies, }, f) def load(self, space): @@ -56,6 +59,9 @@ self._game_objects = data.get('game_objects', []) for game_object_dict in self._game_objects: self._create_game_object(space, **game_object_dict) + self._enemies = data.get('enemies', []) + for enemy_dict in self._enemies: + self._create_enemy(space, **enemy_dict) def _create_game_object(self, space, classname, args, name=None): # We should probably build a registry of game objects or something. @@ -73,6 +79,18 @@ if name is not None: self._glue.add_component(name, gobj) + def _create_enemy(self, space, classname, args, name=None): + cls = getattr(enemies, classname) + if issubclass(cls, go.GameObject): + gobj = cls(space, *args) + self._drawables.append(gobj) + else: + raise TypeError( + "Expected a subclass of GameObject, got %s" % ( + classname)) + if name is not None: + self._glue.add_component(name, gobj) + def all_closed(self): """Check if all the polygons are closed""" closed = True