Ignore:
Files:
12 added
1 deleted
9 edited

Legend:

Unmodified
Added
Removed
  • data/levels/level1

    r191 r209  
    2828  name: light2
    2929- args: [light_switch, door_switch]
    30   classname: StateLogicalAndPuzzler
     30  classname: puzzle.StateLogicalAndPuzzler
    3131  name: both_switches
    3232- args:
     
    5151  - Run around, press some buttons, have fun!
    5252  classname: Note
     53lines:
     54- - [750, 680]
     55  - [950, 680]
     56- - [750, 480]
     57  - [950, 480]
     58- - [750, 480]
     59  - [750, 680]
     60- - [950, 480]
     61  - [950, 680]
    5362polygons:
    5463  1:
  • data/levels/level2

    r178 r209  
    77  - [600, 700]
    88  classname: Door
     9lines: []
    910polygons:
    1011  1:
  • nagslang/enemies.py

    r168 r208  
    22import pymunk.pygame_util
    33
     4from nagslang import render
    45from nagslang.constants import COLLISION_TYPE_ENEMY, ZORDER_MID
    5 from nagslang.game_object import (
    6     GameObject, SingleShapePhysicser, AnimatedFacingImageRenderer, make_body)
     6from nagslang.game_object import GameObject, SingleShapePhysicser, make_body
    77from nagslang.mutators import FLIP_H
    88from nagslang.resources import resources
     
    4343
    4444    def _setup_physics(self, space, position):
    45         self._body = make_body(5, pymunk.inf, position, 0.8)
     45        self._body = make_body(10, pymunk.inf, position, 0.8)
    4646
    4747        self._shape = pymunk.Circle(self._body, 30)
    4848
    4949        self._shape.elasticity = 1.0
    50         self._shape.friction = 10.0
     50        self._shape.friction = 0.05
    5151        self._shape.collision_type = COLLISION_TYPE_ENEMY
    5252        self._physicser = SingleShapePhysicser(space, self._shape)
     
    5555
    5656    def _setup_renderer(self):
    57         self.renderer = AnimatedFacingImageRenderer(
     57        self.renderer = render.AnimatedFacingImageRenderer(
    5858            (self._get_image('alien_A_1.png'),
    5959             self._get_image('alien_A_1.png'),
  • nagslang/game_object.py

    r196 r211  
    1 import math
    2 
    3 import pygame
    41import pymunk
    52import pymunk.pygame_util
    63
     4from nagslang import puzzle
     5from nagslang import render
    76from nagslang.constants import (
    87    SWITCH_PUSHERS, COLLISION_TYPE_SWITCH, COLLISION_TYPE_BOX, ZORDER_LOW,
    9     ZORDER_FLOOR, COLLISION_TYPE_DOOR, COLLISION_TYPE_PLAYER)
    10 from nagslang.options import options
     8    ZORDER_FLOOR, COLLISION_TYPE_DOOR)
    119from nagslang.resources import resources
    1210from nagslang.events import DoorEvent
    1311from nagslang.widgets.text import LabelWidget
    14 
    15 
    16 class PuzzleGlue(object):
    17     """Glue that holds bits of a puzzle together.
    18     """
    19     def __init__(self):
    20         self._components = {}
    21 
    22     def add_component(self, name, puzzler):
    23         if not isinstance(puzzler, Puzzler):
    24             puzzler = puzzler.puzzler
    25         self._components[name] = puzzler
    26         puzzler.set_glue(self)
    27 
    28     def get_state_of(self, name):
    29         return self._components[name].get_state()
    30 
    31 
    32 class Puzzler(object):
    33     """Behaviour specific to a puzzle component.
    34     """
    35     def set_glue(self, glue):
    36         self.glue = glue
    37 
    38     def set_game_object(self, game_object):
    39         self.game_object = game_object
    40 
    41     def get_state(self):
    42         raise NotImplementedError()
    43 
    44 
    45 class YesPuzzler(Puzzler):
    46     """Yes sir, I'm always on.
    47     """
    48     def get_state(self):
    49         return True
    50 
    51 
    52 class NoPuzzler(Puzzler):
    53     """No sir, I'm always off.
    54     """
    55     def get_state(self):
    56         return False
    57 
    58 
    59 class CollidePuzzler(Puzzler):
    60     def __init__(self, *collision_types):
    61         if not collision_types:
    62             collision_types = (COLLISION_TYPE_PLAYER,)
    63         self._collision_types = collision_types
    64 
    65     def get_state(self):
    66         space = self.game_object.get_space()
    67         for shape in space.shape_query(self.game_object.get_shape()):
    68             if shape.collision_type in self._collision_types:
    69                 return True
    70         return False
    71 
    72 
    73 class StateProxyPuzzler(Puzzler):
    74     def __init__(self, state_source):
    75         self._state_source = state_source
    76 
    77     def get_state(self):
    78         return self.glue.get_state_of(self._state_source)
    79 
    80 
    81 class StateLogicalAndPuzzler(Puzzler):
    82     def __init__(self, *state_sources):
    83         self._state_sources = state_sources
    84 
    85     def get_state(self):
    86         for state_source in self._state_sources:
    87             if not self.glue.get_state_of(state_source):
    88                 return False
    89         return True
    9012
    9113
     
    14769    def apply_impulse(self, j, r=(0, 0)):
    14870        return self._shape.body.apply_impulse(j, r)
    149 
    150 
    151 class Renderer(object):
    152     def set_game_object(self, game_object):
    153         self.game_object = game_object
    154 
    155     def _render_shape(self, surface):
    156         shape = self.game_object.get_shape()
    157         # Less general that pymunk.pygame_util.draw, but also a lot less noisy.
    158         color = getattr(shape, 'color', pygame.color.THECOLORS['lightblue'])
    159         # We only explicitly draw Circle and Poly shapes. Everything else we
    160         # forward to pymunk.
    161         if isinstance(shape, pymunk.Circle):
    162             centre = pymunk.pygame_util.to_pygame(shape.body.position, surface)
    163             radius = int(shape.radius)
    164             pygame.draw.circle(surface, color, centre, radius, 2)
    165         elif isinstance(shape, pymunk.Poly):
    166             # polygon bounding box
    167             points = [pymunk.pygame_util.to_pygame(p, surface)
    168                       for p in shape.get_vertices()]
    169             pygame.draw.lines(surface, color, True, points, 2)
    170         else:
    171             pymunk.pygame_util.draw(surface, shape)
    172 
    173     def render(self, surface):
    174         if options.debug:
    175             self._render_shape(surface)
    176 
    177     def animate(self):
    178         # Used by time animatations to advance the clock
    179         pass
    180 
    181 
    182 def image_pos(image, pos):
    183     return (pos[0] - image.get_width() / 2,
    184             pos[1] - image.get_height() / 2)
    185 
    186 
    187 class ImageRenderer(Renderer):
    188     def __init__(self, image):
    189         self._image = image
    190 
    191     def get_image(self):
    192         return self._image
    193 
    194     def rotate_image(self, image):
    195         angle = self.game_object.get_render_angle() * 180 / math.pi
    196         return pygame.transform.rotate(image, angle)
    197 
    198     def render_image(self, surface, image):
    199         image = self.rotate_image(image)
    200         pos = self.game_object.get_render_position(surface)
    201         surface.blit(image, image_pos(image, pos))
    202 
    203     def render(self, surface):
    204         self.render_image(surface, self.get_image())
    205         super(ImageRenderer, self).render(surface)
    206 
    207 
    208 class ImageStateRenderer(ImageRenderer):
    209     def __init__(self, state_images):
    210         self._state_images = state_images
    211 
    212     def get_image(self):
    213         return self._state_images[self.game_object.puzzler.get_state()]
    214 
    215 
    216 class FacingImageRenderer(ImageRenderer):
    217     def __init__(self, left_image, right_image):
    218         self._images = {
    219             'left': left_image,
    220             'right': right_image,
    221         }
    222         self._face = 'left'
    223 
    224     def _update_facing(self, angle):
    225         if abs(angle) < math.pi / 2:
    226             self._face = 'right'
    227         elif abs(angle) > math.pi / 2:
    228             self._face = 'left'
    229 
    230     def rotate_image(self, image):
    231         # Facing images don't get rotated.
    232         return image
    233 
    234     def get_facing_image(self):
    235         return self._images[self._face]
    236 
    237     def get_image(self):
    238         angle = self.game_object.get_render_angle()
    239         self._update_facing(angle)
    240         return self.get_facing_image()
    241 
    242 
    243 class AnimatedFacingImageRenderer(FacingImageRenderer):
    244     def __init__(self, left_images, right_images):
    245         self._images = {
    246             'left': left_images,
    247             'right': right_images,
    248         }
    249         self._frame = 0
    250         self._moving = False
    251         self._face = 'left'
    252 
    253     def get_facing_image(self):
    254         if self._frame >= len(self._images[self._face]):
    255             self._frame = 0
    256         return self._images[self._face][self._frame]
    257 
    258     def animate(self):
    259         if self._moving:
    260             self._frame += 1
    261         else:
    262             self._frame = 0
    263 
    264     def start(self):
    265         self._moving = True
    266 
    267     def stop(self):
    268         self._moving = False
    269 
    270 
    271 class TimedAnimatedRenderer(ImageRenderer):
    272 
    273     def __init__(self, images):
    274         self._images = images
    275         self._frame = 0
    276         self._image = None
    277 
    278     def get_image(self):
    279         if self._frame > len(self._imaages):
    280             self._frame = 0
    281         return self._images[self._frame]
    282 
    283     def animate(self):
    284         self._frame += 1
    285 
    286 
    287 class ShapeRenderer(Renderer):
    288     def render(self, surface):
    289         self._render_shape(surface)
    290         super(ShapeRenderer, self).render(surface)
    291 
    292 
    293 class ShapeStateRenderer(ShapeRenderer):
    294     """Renders the shape in a different colour depending on the state.
    295 
    296     Requires the game object it's attached to to have a puzzler.
    297     """
    298     def render(self, surface):
    299         if self.game_object.puzzler.get_state():
    300             color = pygame.color.THECOLORS['green']
    301         else:
    302             color = pygame.color.THECOLORS['red']
    303 
    304         self.game_object.get_shape().color = color
    305         super(ShapeStateRenderer, self).render(surface)
    30671
    30772
     
    32691        self.game_object = game_object
    32792
    328     def render(self, surface):
     93    def render(self, surface, display_offset):
    32994        pass
    33095
     
    338103        self.widget = LabelWidget((20, 20), self.text)
    339104
    340     def render(self, surface):
     105    def render(self, surface, display_offset):
     106        x, y = 20, 20
     107        if display_offset[0] < 0:
     108            x += abs(display_offset[0])
     109        if display_offset[1] < 0:
     110            y += abs(display_offset[1])
     111        self.widget.rect.topleft = (x, y)
    341112        self.widget.draw(surface)
    342113
     
    400171        super(FloorSwitch, self).__init__(
    401172            SingleShapePhysicser(space, self.shape),
    402             ImageStateRenderer({
     173            render.ImageStateRenderer({
    403174                True: resources.get_image('objects', 'sensor_on.png'),
    404175                False: resources.get_image('objects', 'sensor_off.png'),
    405176            }),
    406             CollidePuzzler(*SWITCH_PUSHERS),
     177            puzzle.CollidePuzzler(*SWITCH_PUSHERS),
    407178        )
    408179
     
    417188        super(Note, self).__init__(
    418189            SingleShapePhysicser(space, self.shape),
    419             ImageRenderer(resources.get_image('objects', 'note.png')),
    420             CollidePuzzler(),
     190            render.ImageRenderer(resources.get_image('objects', 'note.png')),
     191            puzzle.CollidePuzzler(),
    421192            TextOverlay(message),
    422193        )
     
    433204        super(FloorLight, self).__init__(
    434205            SingleShapePhysicser(space, self.shape),
    435             ImageStateRenderer({
     206            render.ImageStateRenderer({
    436207                True: resources.get_image('objects', 'light_on.png'),
    437208                False: resources.get_image('objects', 'light_off.png'),
    438209            }),
    439             StateProxyPuzzler(state_source),
     210            puzzle.StateProxyPuzzler(state_source),
    440211        )
    441212
     
    446217        self.shape = pymunk.Poly(
    447218            body, [(-20, -20), (20, -20), (20, 20), (-20, 20)])
     219        self.shape.friction = 0.5
    448220        self.shape.collision_type = COLLISION_TYPE_BOX
    449221        super(Box, self).__init__(
    450222            SingleShapePhysicser(space, self.shape),
    451             ImageRenderer(resources.get_image('objects', 'crate.png')),
     223            render.ImageRenderer(resources.get_image('objects', 'crate.png')),
    452224        )
    453225
     
    465237        self.dest_pos = tuple(dest_pos)
    466238        if key_state is None:
    467             puzzler = YesPuzzler()
     239            puzzler = puzzle.YesPuzzler()
    468240        else:
    469             puzzler = StateProxyPuzzler(key_state)
     241            puzzler = puzzle.StateProxyPuzzler(key_state)
    470242        super(Door, self).__init__(
    471243            SingleShapePhysicser(space, self.shape),
    472             ImageRenderer(resources.get_image('objects', 'door.png')),
     244            render.ImageRenderer(resources.get_image('objects', 'door.png')),
    473245            puzzler,
    474246        )
  • nagslang/level.py

    r191 r209  
    44from nagslang import game_object as go
    55from nagslang import enemies
     6from nagslang import puzzle
    67from nagslang.resources import resources
    78from nagslang.yamlish import load, dump
     
    1718
    1819
     20LINE_COLOR = pygame.color.THECOLORS['orange']
     21
     22
    1923class Level(object):
    2024
     
    2529        self.y = 600
    2630        self.polygons = {}
     31        self.lines = []
    2732        self.basetile = 'tiles/floor.png'
    2833        self._tile_image = None
    2934        self._surface = None
    3035        self._exterior = False
    31         self._glue = go.PuzzleGlue()
     36        self._glue = puzzle.PuzzleGlue()
    3237        self.drawables = []
    3338        self.overlay_drawables = []
     
    4651            'base_tile': self.basetile,
    4752            'polygons': self.polygons,
     53            'lines': self.lines,
    4854            'game_objects': self._game_objects,
    4955            'enemies': self._enemies,
     
    5864            for point in points:
    5965                self.polygons[i].append(tuple(point))
     66        self.lines = data.get('lines', [])
    6067        self._game_objects = data.get('game_objects', [])
    6168        for game_object_dict in self._game_objects:
     
    6673
    6774    def _create_game_object(self, space, classname, args, name=None):
    68         # We should probably build a registry of game objects or something.
    69         # At least this is better than just calling `eval`, right?
    70         cls = getattr(go, classname)
    71         if issubclass(cls, go.Puzzler):
     75        modules = {
     76            'game_object': go,
     77            'puzzle': puzzle,
     78        }
     79        if '.' in classname:
     80            module, classname = classname.split('.')
     81        else:
     82            module = 'game_object'
     83        cls = getattr(modules[module], classname)
     84
     85        if issubclass(cls, puzzle.Puzzler):
    7286            gobj = cls(*args)
    7387        elif issubclass(cls, go.GameObject):
     
    132146
    133147    def get_walls(self):
    134         return self.polygons.values()
     148        walls = self.polygons.values()
     149        walls.extend(self.lines)
     150        return walls
    135151
    136152    def _draw_walls(self):
     
    140156                pointlist = [self.point_to_pygame(p) for p in polygon]
    141157                pygame.draw.lines(self._surface, color, False, pointlist, 2)
     158        for line in self.lines:
     159            pointlist = [self.point_to_pygame(p) for p in line]
     160            pygame.draw.lines(self._surface, LINE_COLOR, False, pointlist, 2)
    142161
    143162    def get_background(self):
  • nagslang/protagonist.py

    r179 r208  
    44import math
    55
     6from nagslang import render
    67from nagslang.constants import COLLISION_TYPE_PLAYER, ZORDER_MID
    7 from nagslang.game_object import (
    8     GameObject, SingleShapePhysicser, AnimatedFacingImageRenderer, make_body)
     8from nagslang.game_object import GameObject, SingleShapePhysicser, make_body
    99from nagslang.mutators import FLIP_H
    1010from nagslang.resources import resources
     
    4343            self.WOLF_FORM: pymunk.Circle(self._body, 30),
    4444        }
     45        self._shapes[self.HUMAN_FORM].friction = 1.0
     46        self._shapes[self.WOLF_FORM].friction = 0.05
    4547        self._physicsers = {}
    4648        for form, shape in self._shapes.iteritems():
    4749            shape.elasticity = 1.0
    48             shape.friction = 10.0
    4950            shape.collision_type = COLLISION_TYPE_PLAYER
    5051            self._physicsers[form] = SingleShapePhysicser(space, shape)
     
    5657    def _setup_renderers(self):
    5758        self._renderers = {
    58             self.HUMAN_FORM: AnimatedFacingImageRenderer(
     59            self.HUMAN_FORM: render.AnimatedFacingImageRenderer(
    5960                (self._get_image('human_1.png'),
    6061                 self._get_image('human_1.png'),
     
    6970                 self._get_image('human_2.png', FLIP_H),
    7071                 self._get_image('human_2.png', FLIP_H))),
    71             self.HUMAN_FORM_BACK: AnimatedFacingImageRenderer(
     72            self.HUMAN_FORM_BACK: render.AnimatedFacingImageRenderer(
    7273                (self._get_image('human_back_1.png'),
    7374                 self._get_image('human_back_1.png'),
     
    8283                 self._get_image('human_back_2.png', FLIP_H),
    8384                 self._get_image('human_back_2.png', FLIP_H))),
    84             self.WOLF_FORM: AnimatedFacingImageRenderer(
     85            self.WOLF_FORM: render.AnimatedFacingImageRenderer(
    8586                (self._get_image('werewolf_1.png'),
    8687                 self._get_image('werewolf_1.png'),
     
    9596                 self._get_image('werewolf_2.png', FLIP_H),
    9697                 self._get_image('werewolf_2.png', FLIP_H))),
    97             self.WOLF_FORM_BACK: AnimatedFacingImageRenderer(
     98            self.WOLF_FORM_BACK: render.AnimatedFacingImageRenderer(
    9899                (self._get_image('werewolf_back_1.png'),
    99100                 self._get_image('werewolf_back_1.png'),
     
    127128        self.form = self.WOLF_FORM
    128129        self._physicsers[self.form].add_to_space()
     130        self.physicser = self._physicsers[self.form]
    129131        self._body.mass = 100
    130132        self._body.velocity_limit = 1000
     
    143145        self.form = self.HUMAN_FORM
    144146        self._physicsers[self.form].add_to_space()
     147        self.physicser = self._physicsers[self.form]
    145148        self._body.mass = 10
    146149        self._body.velocity_limit = 1000
     
    200203        self.renderer = self._renderers[self.render_form]
    201204        self._physicsers[self.form].add_to_space()
     205        self.physicser = self._physicsers[self.form]
    202206
    203207    def toggle_form(self):
  • nagslang/screens/area.py

    r192 r211  
    174174        for overlay in self._level.overlay_drawables:
    175175            if overlay.is_visible():
    176                 overlay.render(surface)
     176                overlay.render(surface, render_rect.topleft)
    177177
    178178    def tick_protagonist(self):
  • nagslang/tests/test_level.py

    r186 r209  
    44
    55from nagslang import game_object as go
     6from nagslang import puzzle
    67from nagslang.level import Level
    78from nagslang.yamlish import load
     
    3233            self.assertEqual((5, 10), level.get_size())
    3334            self.assertEqual([], level.get_walls())
    34             self.assertEqual([], level.get_drawables())
     35            self.assertEqual([], level.drawables)
    3536
    3637        level = self.make_level('foo', {
     
    4950            self.assertEqual((5, 10), level.get_size())
    5051            self.assertEqual([[(1, 1), (2, 1), (1, 2)]], level.get_walls())
    51             self.assertEqual([], level.get_drawables())
     52            self.assertEqual([], level.drawables)
    5253
    5354        level = self.make_level('foo', {
     
    6768            self.assertEqual((5, 10), level.get_size())
    6869            self.assertEqual([], level.get_walls())
    69             [box, switch] = level.get_drawables()
     70            [box, switch] = level.drawables
    7071            self.assertTrue(isinstance(box, go.Box))
    7172            self.assertEqual(box.shape.body.position, (3, 3))
     
    7778                             sorted(puzzle_bits.keys()))
    7879            self.assertTrue(
    79                 isinstance(puzzle_bits['foo_proxy'], go.StateProxyPuzzler))
     80                isinstance(puzzle_bits['foo_proxy'], puzzle.StateProxyPuzzler))
    8081            self.assertEqual('foo', puzzle_bits['foo_proxy']._state_source)
    81             self.assertTrue(isinstance(puzzle_bits['foo'], go.CollidePuzzler))
     82            self.assertTrue(isinstance(puzzle_bits['foo'],
     83                                       puzzle.CollidePuzzler))
    8284
    8385        level = self.make_level('foo', {
     
    9799                {
    98100                    'name': 'foo_proxy',
    99                     'classname': 'StateProxyPuzzler',
     101                    'classname': 'puzzle.StateProxyPuzzler',
    100102                    'args': ['foo'],
    101103                },
  • tools/area_editor.py

    r195 r206  
    2424from albow.root import RootWidget
    2525from albow.widget import Widget
    26 from albow.controls import Button
     26from albow.controls import Button, Label, CheckBox
    2727from albow.dialogs import alert
    2828
     29from nagslang.options import parse_args
    2930from nagslang.constants import SCREEN
    30 from nagslang.level import Level, POLY_COLORS
     31from nagslang.level import Level, POLY_COLORS, LINE_COLOR
     32from nagslang.enemies import Enemy
    3133
    3234
     
    6163            self.polygons[poly_index].append(point)
    6264        else:
    63             add_pos = self.fix_angle(poly_index, pos)
     65            add_pos = self.fix_poly_angle(poly_index, pos)
    6466            self.polygons[poly_index].append(add_pos)
    6567
    66     def fix_angle(self, index, pos):
    67         # Last point
    68         point1 = self.point_to_pygame(self.polygons[index][-1])
    69         pos = self.round_point(pos)
     68    def _fix_angle(self, point1, pos):
    7069        # We want the line (point1 to pos) to be an angle of
    7170        # 0, 45, 90, 135, 180, 225, 270, 305
     
    8887        return self.point_to_pymunk(new_pos)
    8988
     89    def fix_line_angle(self, start_pos, pos):
     90        start_pos = self.round_point(start_pos)
     91        pos = self.round_point(pos)
     92        return self._fix_angle(start_pos, pos)
     93
     94    def fix_poly_angle(self, index, pos):
     95        # Last point
     96        point1 = self.point_to_pygame(self.polygons[index][-1])
     97        pos = self.round_point(pos)
     98        return self._fix_angle(point1, pos)
     99
    90100    def delete_point(self, index):
    91101        if index in self.polygons and len(self.polygons[index]) > 0:
     
    101111            return False
    102112        first = self.polygons[index][0]
    103         if self.fix_angle(index, self.point_to_pygame(first)) == first:
     113        if self.fix_poly_angle(index, self.point_to_pygame(first)) == first:
    104114            self.add_point(index, self.point_to_pygame(first))
    105115            return True
     
    115125        poss = None
    116126        for cand in candidates:
    117             if self.fix_angle(index, self.point_to_pygame(cand)) == cand:
     127            if self.fix_poly_angle(index, self.point_to_pygame(cand)) == cand:
    118128                dist = (first[0] - cand[0]) ** 2 + (first[1] - cand[1]) ** 2
    119129                if dist < min_dist:
     
    125135        return False
    126136
    127     def draw(self, surface, topleft, mouse_pos, mouse_poly, filled):
     137    def add_line(self, start_pos, end_pos):
     138        endpoint = self.fix_line_angle(start_pos, end_pos)
     139        startpoint = self.point_to_pymunk(self.round_point(start_pos))
     140        self.lines.append([startpoint, endpoint])
     141
     142    def draw(self, mouse_pos, mouse_poly, filled, draw_cand_line, start_pos):
    128143        self._draw_background(True)
    129144        # Draw polygons as needed for the editor
     
    136151                pygame.draw.lines(self._surface, color, False, pointlist, 2)
    137152            if index == mouse_poly and mouse_pos:
    138                 endpoint = self.fix_angle(index, mouse_pos)
     153                endpoint = self.fix_poly_angle(index, mouse_pos)
    139154                pygame.draw.line(self._surface, color,
    140155                                 self.point_to_pygame(polygon[-1]),
    141156                                 self.point_to_pygame(endpoint))
    142         surface_area = pygame.rect.Rect(topleft, SCREEN)
    143         surface.blit(self._surface, (0, 0), surface_area)
     157        for line in self.lines:
     158            pointlist = [self.point_to_pygame(p) for p in line]
     159            pygame.draw.lines(self._surface, LINE_COLOR, False, pointlist, 2)
     160        if draw_cand_line and start_pos and mouse_pos:
     161            endpoint = self.fix_line_angle(start_pos, mouse_pos)
     162            pointlist = [self.round_point(start_pos),
     163                         self.point_to_pygame(endpoint)]
     164            pygame.draw.lines(self._surface, LINE_COLOR, False, pointlist, 1)
     165        return self._surface.copy()
    144166
    145167
     
    155177        self.cur_poly = None
    156178        self._mouse_drag = False
     179        self._draw_objects = False
     180        self._draw_enemies = False
     181        self._draw_lines = False
     182        self._start_pos = None
    157183
    158184    def _level_coordinates(self, pos):
     
    174200        self.pos = tuple(new_pos)
    175201
     202    def set_objects(self, value):
     203        if self._draw_objects != value:
     204            self._draw_objects = value
     205            self.invalidate()
     206
     207    def set_enemies(self, value):
     208        if self._draw_enemies != value:
     209            self._draw_enemies = value
     210            self.invalidate()
     211
    176212    def draw(self, surface):
    177213        if (self.cur_poly is not None and self.cur_poly in self.level.polygons
     
    179215            # We have an active polygon
    180216            mouse_pos = self._level_coordinates(self.mouse_pos)
     217        elif self._draw_lines:
     218            # Interior wall mode
     219            mouse_pos = self._level_coordinates(self.mouse_pos)
    181220        else:
    182221            mouse_pos = None
    183         level.draw(surface, self.pos, mouse_pos, self.cur_poly,
    184                    self.filled_mode)
     222        level_surface = level.draw(mouse_pos, self.cur_poly, self.filled_mode,
     223                                   self._draw_lines, self._start_pos)
     224        if self._draw_objects:
     225            for thing in self.level.drawables:
     226                if not isinstance(thing, Enemy):
     227                    thing.render(level_surface)
     228        if self._draw_enemies:
     229            for thing in self.level.drawables:
     230                if isinstance(thing, Enemy):
     231                    thing.render(level_surface)
     232        surface_area = pygame.rect.Rect(self.pos, SCREEN)
     233        surface.blit(level_surface, (0, 0), surface_area)
    185234
    186235    def change_poly(self, new_poly):
    187236        self.cur_poly = new_poly
     237        self._draw_lines = False
    188238        if self.cur_poly is not None:
    189239            self.filled_mode = False
     240
     241    def line_mode(self):
     242        self.cur_poly = None
     243        self._draw_lines = True
     244        self.filled_mode = False
     245        self._start_pos = None
    190246
    191247    def key_down(self, ev):
     
    214270            self.cur_poly = None
    215271            self.filled_mode = True
     272            self._draw_lines = False
    216273        else:
    217274            alert('Not all polygons closed, so not filling')
     
    220277        old_pos = self.mouse_pos
    221278        self.mouse_pos = ev.pos
    222         if self.cur_poly and old_pos != self.mouse_pos:
     279        if old_pos != self.mouse_pos and (self.cur_poly or self._draw_lines):
    223280            self.invalidate()
    224281
     
    234291    def mouse_down(self, ev):
    235292        if ev.button == 1:
    236             print "Click: %r" % (
    237                 self.level.point_to_pymunk(self._level_coordinates(ev.pos)),)
     293            if self._draw_lines:
     294                if self._start_pos is None:
     295                    self._start_pos = ev.pos
     296                else:
     297                    self.level.add_line(self._start_pos, ev.pos)
     298                    self._start_pos = None
     299            else:
     300                print "Click: %r" % (
     301                    self.level.point_to_pymunk(
     302                        self._level_coordinates(ev.pos)),)
    238303        if ev.button == 4:  # Scroll up
    239304            self._move_view((0, -10))
     
    305370        button_rect = pygame.rect.Rect(0, 0, MENU_WIDTH, MENU_BUTTON_HEIGHT)
    306371
     372        check_rect = pygame.rect.Rect(0, 0, MENU_BUTTON_HEIGHT // 2,
     373                                      MENU_BUTTON_HEIGHT // 2)
     374
    307375        end_poly_but = PolyButton(None, self.level_widget)
    308376        end_poly_but.rect = button_rect.copy()
    309377        end_poly_but.rect.move_ip(MENU_LEFT, y)
    310378        self.add(end_poly_but)
     379        y += MENU_BUTTON_HEIGHT + MENU_PAD
     380
     381        draw_line = Button("Draw interior wall", self.level_widget.line_mode)
     382        draw_line.rect = button_rect.copy()
     383        draw_line.rect.move_ip(MENU_LEFT, y)
     384        self.add(draw_line)
    311385        y += MENU_BUTTON_HEIGHT + MENU_PAD
    312386
     
    330404        y += MENU_BUTTON_HEIGHT + MENU_PAD
    331405
     406        white = pygame.color.Color("white")
     407        self.show_objs = CheckBox(fg_color=white)
     408        self.show_objs.rect = check_rect.copy()
     409        self.show_objs.rect.move_ip(MENU_LEFT, y)
     410        label = Label("Show Objects", fg_color=white)
     411        label.rect.move_ip(MENU_LEFT + MENU_BUTTON_HEIGHT // 2 + MENU_PAD, y)
     412        self.add(self.show_objs)
     413        self.add(label)
     414        y += label.rect.height + MENU_PAD
     415
     416        self.show_enemies = CheckBox(fg_color=white)
     417        self.show_enemies.rect = check_rect.copy()
     418        self.show_enemies.rect.move_ip(MENU_LEFT, y)
     419        label = Label("Show enemy start pos", fg_color=white)
     420        label.rect.move_ip(MENU_LEFT + MENU_BUTTON_HEIGHT // 2 + MENU_PAD, y)
     421        self.add(self.show_enemies)
     422        self.add(label)
     423        y += label.rect.height + MENU_PAD
     424
    332425        quit_but = Button('Quit', action=self.quit)
    333426        quit_but.rect = button_rect.copy()
     
    356449        self.level_widget.mouse_move(ev)
    357450
     451    def draw(self, surface):
     452        # Update checkbox state
     453        self.level_widget.set_objects(self.show_objs.value)
     454        self.level_widget.set_enemies(self.show_enemies.value)
     455        super(EditorApp, self).draw(surface)
     456
    358457
    359458if __name__ == "__main__":
     
    361460        print 'Please supply a levelname or levelname and level size'
    362461        sys.exit()
     462    # Need to ensure we have defaults for rendering
     463    parse_args([])
    363464    pygame.display.init()
    364465    pygame.font.init()
Note: See TracChangeset for help on using the changeset viewer.