Mercurial > nagslang
view nagslang/render.py @ 647:aeb366d97774
Show splash image on startup
author | Stefano Rivera <stefano@rivera.za.net> |
---|---|
date | Sun, 08 Sep 2013 02:02:09 +0200 |
parents | 9ea26b835271 |
children |
line wrap: on
line source
import math import textwrap import pygame import pygame.locals as pgl import pymunk from nagslang.options import options from nagslang.utils import ( tile_surface, vec_from_angle, points_to_pygame, extend_line) from nagslang.widgets.text import LabelWidget, MultiLineWidget 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.shapes: self._render_shape(surface) def update(self, seconds): # Used by time animatations to advance the clock pass class NullRenderer(Renderer): def render(self, surface): pass class HatchRendererMixin(object): def draw_hatch_line(self, surface, a, b): ai, bi = extend_line(a, b, -2) a, b, ai, bi = points_to_pygame(surface, (a, b, ai, bi)) pygame.draw.line( surface, pygame.color.THECOLORS['black'], a, b, 7) pygame.draw.line( surface, pygame.color.THECOLORS['lightblue'], ai, bi, 5) def render_hatch(self, surface): shape = self.game_object.get_shape() a = shape.body.local_to_world(shape.a) b = shape.body.local_to_world(shape.b) if self.game_object.puzzler.get_state(): offset = vec_from_angle((b - a).angle, 10) ai = a + offset bi = b - offset self.draw_hatch_line(surface, a, ai) self.draw_hatch_line(surface, bi, b) else: mid = a + (b - a) / 2 self.draw_hatch_line(surface, a, mid) self.draw_hatch_line(surface, mid, b) class HatchRenderer(Renderer, HatchRendererMixin): def render(self, surface): self.render_hatch(surface) def image_pos(image, pos): return (pos[0] - image.get_width() / 2, pos[1] - image.get_height() / 2) class ImageRenderer(Renderer): def __init__(self, image): self._image = image def get_image(self): return self._image def rotate_image(self, 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 KeyedHatchRenderer(ImageRenderer, HatchRendererMixin): def render(self, surface): self.render_hatch(surface) if not self.game_object.puzzler.get_state(): self.render_image(surface, self.get_image()) 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 update(self, seconds): self.advance_tick() class MovementAnimatedRenderer(TimedAnimatedRenderer): def update(self, seconds): 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 update(self, seconds): return self.renderer.update(seconds) def select_renderer(self): raise NotImplementedError() class FacingSelectionRenderer(RendererSelectionRenderer): def select_renderer(self): return self.game_object.get_facing_direction() 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) class Overlay(object): def set_game_object(self, game_object): self.game_object = game_object def render(self, surface, display_offset, max_width): pass def is_visible(self): return self.game_object.puzzler.get_state() class TextOverlay(Overlay): def __init__(self, text, **kwargs): self.text = text self.widget = LabelWidget((20, 20), self.text, **kwargs) def render(self, surface, display_offset, max_width): x, y = 20, 20 if display_offset[0] < 0: x += abs(display_offset[0]) if display_offset[1] < 0: y += abs(display_offset[1]) if self.widget.rect.width > max_width - 40: # Need to relayout the widget factor = 2 while self.widget.rect.width > max_width - 40: wrapped = '\n'.join(textwrap.wrap(self.text, len(self.text) // factor)) factor *= 2 self.widget = MultiLineWidget((20, 20), wrapped) if self.widget.rect.width < 100: # safety valve break self.widget.rect.topleft = (x, y) self.widget.draw(surface) # TODO: undo the mad folding else: self.widget.rect.topleft = (x, y) self.widget.draw(surface) class ImageOverlay(Overlay): def __init__(self, image): self.image = image def render(self, surface, display_offset, max_width): x = (surface.get_width() - self.image.get_width()) / 2 y = (surface.get_height() - self.image.get_height()) / 2 surface.blit(self.image, (x, y)) class TiledRenderer(Renderer): """Tile the given image to fit the given outline Outline is assumed to be in pymunk coordinates""" def __init__(self, outline, tile_image, alpha=255): self._tile_image = tile_image self.outline = outline self._tiled = None self._offset = None self._alpha = alpha def _make_surface(self, surface, image): size = surface.get_size() mask = pygame.surface.Surface(size, pgl.SRCALPHA) pointlist = [pymunk.pygame_util.to_pygame(p, surface) for p in self.outline] rect = pygame.draw.polygon(mask, pygame.color.Color( 255, 255, 255, self._alpha), pointlist, 0) self._offset = (rect.x, rect.y) tiled = tile_surface((rect.w, rect.h), image) tiled.blit(mask, (0, 0), rect, special_flags=pgl.BLEND_RGBA_MULT) return tiled def render(self, surface): if not self._tiled: self._tiled = self._make_surface(surface, self._tile_image) surface.blit(self._tiled, self._offset) super(TiledRenderer, self).render(surface) class TimedTiledRenderer(TiledRenderer): """Animate tiles""" # Should make this a mixin with TimeAnimate, but not right now def __init__(self, outline, images, frame_ticks=1, alpha=255): self._images = images self._frame_ticks = frame_ticks self.outline = outline self._frames = [None] * len(images) self._offset = None self._alpha = alpha self._frame = 0 self._frame_tick = 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 update(self, seconds): self.advance_tick() def render(self, surface): if not self._frames[self._frame]: self._frames[self._frame] = self._make_surface( surface, self._images[self._frame]) self._tiled = self._frames[self._frame] super(TimedTiledRenderer, self).render(surface)