view nagslang/render.py @ 219:f9e92d540bfa

Less hacky rotation suppression.
author Jeremy Thurgood <firxen@gmail.com>
date Wed, 04 Sep 2013 16:21:21 +0200
parents 9e2ef2f15035
children 0c0d5919f70a
line wrap: on
line source

import math

import pygame
import pymunk

from nagslang.options import options


class Renderer(object):
    def set_game_object(self, game_object):
        self.game_object = game_object

    def _render_shape(self, surface):
        shape = self.game_object.get_shape()
        # Less general that pymunk.pygame_util.draw, but also a lot less noisy.
        color = getattr(shape, 'color', pygame.color.THECOLORS['lightblue'])
        # We only explicitly draw Circle and Poly shapes. Everything else we
        # forward to pymunk.
        if isinstance(shape, pymunk.Circle):
            centre = pymunk.pygame_util.to_pygame(shape.body.position, surface)
            radius = int(shape.radius)
            pygame.draw.circle(surface, color, centre, radius, 2)
        elif isinstance(shape, pymunk.Poly):
            # polygon bounding box
            points = [pymunk.pygame_util.to_pygame(p, surface)
                      for p in shape.get_vertices()]
            pygame.draw.lines(surface, color, True, points, 2)
        else:
            pymunk.pygame_util.draw(surface, shape)

    def render(self, surface):
        if options.debug:
            self._render_shape(surface)

    def animate(self):
        # Used by time animatations to advance the clock
        pass


def image_pos(image, pos):
    return (pos[0] - image.get_width() / 2,
            pos[1] - image.get_height() / 2)


class ImageRenderer(Renderer):
    rotate = True  # Set to `False` to suppress image rotation.

    def __init__(self, image):
        self._image = image

    def get_image(self):
        return self._image

    def rotate_image(self, image):
        if not self._rotate:
            return image
        angle = self.game_object.get_render_angle() * 180 / math.pi
        return pygame.transform.rotate(image, angle)

    def render_image(self, surface, image):
        image = self.rotate_image(image)
        pos = self.game_object.get_render_position(surface)
        surface.blit(image, image_pos(image, pos))

    def render(self, surface):
        self.render_image(surface, self.get_image())
        super(ImageRenderer, self).render(surface)


class ImageStateRenderer(ImageRenderer):
    def __init__(self, state_images):
        self._state_images = state_images

    def get_image(self):
        return self._state_images[self.game_object.puzzler.get_state()]


class TimedAnimatedRenderer(ImageRenderer):
    def __init__(self, images, frame_ticks=1):
        self._images = images
        self._frame_ticks = frame_ticks
        self._frame_tick = 0
        self._frame = 0

    def advance_tick(self):
        self._frame_tick += 1
        if self._frame_tick > self._frame_ticks:
            self._frame_tick = 0
            self._frame += 1
        if self._frame >= len(self._images):
            self._frame = 0

    def reset(self):
        self._frame_tick = 0
        self._frame = 0

    def get_image(self):
        return self._images[self._frame]

    def animate(self):
        self.advance_tick()


class MovementAnimatedRenderer(TimedAnimatedRenderer):
    def animate(self):
        if self.game_object.is_moving:
            self.advance_tick()
        else:
            self.reset()


class RendererSelectionRenderer(Renderer):
    def __init__(self, renderers):
        self._renderers = renderers

    def set_game_object(self, game_object):
        self.game_object = game_object
        for renderer in self._renderers.values():
            renderer.set_game_object(game_object)

    @property
    def renderer(self):
        return self._renderers[self.select_renderer()]

    def render(self, surface):
        return self.renderer.render(surface)

    def animate(self):
        return self.renderer.animate()

    def select_renderer(self):
        raise NotImplementedError()


class FacingSelectionRenderer(RendererSelectionRenderer):
    def __init__(self, renderers):
        for renderer in renderers.values():
            renderer.rotate = False
        super(FacingSelectionRenderer, self).__init__(renderers)
        self._face = 'left'

    def _update_facing(self, angle):
        if abs(angle) < math.pi / 2:
            self._face = 'right'
        elif abs(angle) > math.pi / 2:
            self._face = 'left'

    def select_renderer(self):
        angle = self.game_object.get_render_angle()
        self._update_facing(angle)
        return self._face


class ShapeRenderer(Renderer):
    def render(self, surface):
        self._render_shape(surface)
        super(ShapeRenderer, self).render(surface)


class ShapeStateRenderer(ShapeRenderer):
    """Renders the shape in a different colour depending on the state.

    Requires the game object it's attached to to have a puzzler.
    """
    def render(self, surface):
        if self.game_object.puzzler.get_state():
            color = pygame.color.THECOLORS['green']
        else:
            color = pygame.color.THECOLORS['red']

        self.game_object.get_shape().color = color
        super(ShapeStateRenderer, self).render(surface)