view nagslang/protagonist.py @ 180:026297a03963

Add DoorEvent and tweak ScreenChange to keep more state when the player goes through a door
author Neil Muller <drnlmuller@gmail.com>
date Tue, 03 Sep 2013 16:58:45 +0200
parents 1ee8756888e4
children dc0cc8228e2a
line wrap: on
line source

import pymunk
import pymunk.pygame_util

import math

from nagslang.constants import COLLISION_TYPE_PLAYER, ZORDER_MID
from nagslang.game_object import (
    GameObject, SingleShapePhysicser, AnimatedFacingImageRenderer, make_body)
from nagslang.mutators import FLIP_H
from nagslang.resources import resources


class Protagonist(GameObject):
    """Representation of our fearless protagonist.

    TODO: Factor out a bunch of this stuff when we need it for other objects.
    """

    HUMAN_FORM = 'human'
    HUMAN_FORM_BACK = 'human_back'
    WOLF_FORM = 'wolf'
    WOLF_FORM_BACK = 'wolf_back'

    def __init__(self, space, position):
        self._setup_physics(space, position)
        self._setup_renderers()
        self.inventory = {}
        self.form = self.HUMAN_FORM
        self.render_form = self.HUMAN_FORM

        super(Protagonist, self).__init__(
            self._physicsers[self.form], self._renderers[self.form])
        self.zorder = ZORDER_MID

        self.go_human()

    def _setup_physics(self, space, position):
        self._body = make_body(10, pymunk.inf, position, 0.8)

        self._shapes = {
            self.HUMAN_FORM: pymunk.Poly(
                self._body, [(-15, -30), (15, -30), (15, 30), (-15, 30)]),
            self.WOLF_FORM: pymunk.Circle(self._body, 30),
        }
        self._physicsers = {}
        for form, shape in self._shapes.iteritems():
            shape.elasticity = 1.0
            shape.friction = 10.0
            shape.collision_type = COLLISION_TYPE_PLAYER
            self._physicsers[form] = SingleShapePhysicser(space, shape)
        self.angle = 0

    def _get_image(self, name, *transforms):
        return resources.get_image('creatures', name, transforms=transforms)

    def _setup_renderers(self):
        self._renderers = {
            self.HUMAN_FORM: AnimatedFacingImageRenderer(
                (self._get_image('human_1.png'),
                 self._get_image('human_1.png'),
                 self._get_image('human_1.png'),
                 self._get_image('human_2.png'),
                 self._get_image('human_2.png'),
                 self._get_image('human_2.png')),
                (self._get_image('human_1.png', FLIP_H),
                 self._get_image('human_1.png', FLIP_H),
                 self._get_image('human_1.png', FLIP_H),
                 self._get_image('human_2.png', FLIP_H),
                 self._get_image('human_2.png', FLIP_H),
                 self._get_image('human_2.png', FLIP_H))),
            self.HUMAN_FORM_BACK: AnimatedFacingImageRenderer(
                (self._get_image('human_back_1.png'),
                 self._get_image('human_back_1.png'),
                 self._get_image('human_back_1.png'),
                 self._get_image('human_back_2.png'),
                 self._get_image('human_back_2.png'),
                 self._get_image('human_back_2.png')),
                (self._get_image('human_back_1.png', FLIP_H),
                 self._get_image('human_back_1.png', FLIP_H),
                 self._get_image('human_back_1.png', FLIP_H),
                 self._get_image('human_back_2.png', FLIP_H),
                 self._get_image('human_back_2.png', FLIP_H),
                 self._get_image('human_back_2.png', FLIP_H))),
            self.WOLF_FORM: AnimatedFacingImageRenderer(
                (self._get_image('werewolf_1.png'),
                 self._get_image('werewolf_1.png'),
                 self._get_image('werewolf_1.png'),
                 self._get_image('werewolf_2.png'),
                 self._get_image('werewolf_2.png'),
                 self._get_image('werewolf_2.png')),
                (self._get_image('werewolf_1.png', FLIP_H),
                 self._get_image('werewolf_1.png', FLIP_H),
                 self._get_image('werewolf_1.png', FLIP_H),
                 self._get_image('werewolf_2.png', FLIP_H),
                 self._get_image('werewolf_2.png', FLIP_H),
                 self._get_image('werewolf_2.png', FLIP_H))),
            self.WOLF_FORM_BACK: AnimatedFacingImageRenderer(
                (self._get_image('werewolf_back_1.png'),
                 self._get_image('werewolf_back_1.png'),
                 self._get_image('werewolf_back_1.png'),
                 self._get_image('werewolf_back_2.png'),
                 self._get_image('werewolf_back_2.png'),
                 self._get_image('werewolf_back_2.png')),
                (self._get_image('werewolf_back_1.png', FLIP_H),
                 self._get_image('werewolf_back_1.png', FLIP_H),
                 self._get_image('werewolf_back_1.png', FLIP_H),
                 self._get_image('werewolf_back_2.png', FLIP_H),
                 self._get_image('werewolf_back_2.png', FLIP_H),
                 self._get_image('werewolf_back_2.png', FLIP_H))),
        }
        for renderer in self._renderers.values():
            renderer.set_game_object(self)

    @classmethod
    def from_saved_state(cls, saved_state):
        """Create an instance from the provided serialised state.
        """
        obj = cls()
        # TODO: Update from saved state.
        return obj

    def get_render_angle(self):
        return self.angle

    def go_werewolf(self):
        self._physicsers[self.form].remove_from_space()
        self.form = self.WOLF_FORM
        self._physicsers[self.form].add_to_space()
        self._body.mass = 100
        self._body.velocity_limit = 1000
        self.impulse_factor = 4000
        self._body.damping = 0.9
        if self.render_form == self.HUMAN_FORM:
            self.render_form = self.WOLF_FORM
        elif self.render_form == self.HUMAN_FORM_BACK:
            self.render_form = self.WOLF_FORM_BACK
        else:
            self.render_form = self.WOLF_FORM
        self.renderer = self._renderers[self.render_form]

    def go_human(self):
        self._physicsers[self.form].remove_from_space()
        self.form = self.HUMAN_FORM
        self._physicsers[self.form].add_to_space()
        self._body.mass = 10
        self._body.velocity_limit = 1000
        self.impulse_factor = 500
        self._body.damping = 0.8
        if self.render_form == self.WOLF_FORM:
            self.render_form = self.HUMAN_FORM
        elif self.render_form == self.WOLF_FORM_BACK:
            self.render_form = self.HUMAN_FORM_BACK
        else:
            self.render_form = self.HUMAN_FORM
        self.renderer = self._renderers[self.render_form]

    def _switch_to_back(self):
        if self.render_form == self.HUMAN_FORM:
            self.render_form = self.HUMAN_FORM_BACK
        elif self.render_form == self.WOLF_FORM:
            self.render_form = self.WOLF_FORM_BACK
        self.renderer = self._renderers[self.render_form]

    def _switch_to_front(self):
        if self.render_form == self.HUMAN_FORM_BACK:
            self.render_form = self.HUMAN_FORM
        elif self.render_form == self.WOLF_FORM_BACK:
            self.render_form = self.WOLF_FORM
        self.renderer = self._renderers[self.render_form]

    def set_direction(self, dx, dy):
        if (dx, dy) == (0, 0):
            self.renderer.stop()
            return
        old_angle = self.angle
        self.angle = pymunk.Vec2d((dx, dy)).angle
        # If we've gone from quadrants 2 & 3 to 1 & 4 (or vice versa)
        # switch between front & back views
        if self.angle != math.pi:
            # == math.pi is going straight left, which can't
            # trigger a front/back swap and simplifies these checks
            if self.angle > 0 and old_angle != self.angle:
                self._switch_to_back()
            elif self.angle < 0 and old_angle != self.angle:
                self._switch_to_front()
        self._body.apply_impulse(
            (dx * self.impulse_factor, dy * self.impulse_factor))
        self.renderer.start()

    def set_position(self, position):
        self._body.position = position

    def copy_state(self, old_protagonist):
        self._physicsers[self.form].remove_from_space()
        self._body.position = old_protagonist._body.position
        self.form = old_protagonist.form
        self.angle = old_protagonist.angle
        self.render_form = old_protagonist.render_form
        self.inventory = old_protagonist.inventory
        self.renderer = self._renderers[self.render_form]
        self._physicsers[self.form].add_to_space()

    def toggle_form(self):
        if self.form == self.WOLF_FORM:
            self.go_human()
        else:
            self.go_werewolf()

    def act_on(self, target):
        """Perform an action on the target.
        """
        # TODO: Decide how best to do this.
        pass

    def attack(self):
        """Attempt to hurt something.
        """
        pass

    def in_wolf_form(self):
        return self.form == self.WOLF_FORM

    def in_human_form(self):
        return self.form == self.HUMAN_FORM

    def has_item(self, item):
        return item in self.inventory

    def environmental_movement(self, dx, dy):
        if (dx, dy) == (0, 0):
            return
        self._body.apply_impulse((dx, dy))