changeset 617:2070ce83637b

Merge
author David Sharpe <decoydavid@gmail.com>
date Sun, 08 Sep 2013 00:48:35 +0200
parents 11a3ac7365cd (current diff) 9ea26b835271 (diff)
children cc94512d14f2
files data/levels/cargo_bay data/levels/crew
diffstat 21 files changed, 312 insertions(+), 31 deletions(-) [+]
line wrap: on
line diff
--- a/data/levels/alientunnel1	Sun Sep 08 00:33:15 2013 +0200
+++ b/data/levels/alientunnel1	Sun Sep 08 00:48:35 2013 +0200
@@ -99,7 +99,7 @@
   classname: collectable.KeyCard
   name: keycard_yellow
 lines: []
-music: POL-cyber-factory-short.ogg
+music: POL-future-war-short.ogg
 polygons:
   1:
   - [500, 1200]
--- a/data/levels/alientunnel2	Sun Sep 08 00:33:15 2013 +0200
+++ b/data/levels/alientunnel2	Sun Sep 08 00:48:35 2013 +0200
@@ -81,7 +81,7 @@
 lines:
 - - [1100, 800]
   - [1100, 400]
-music: POL-cyber-factory-short.ogg
+music: POL-future-war-short.ogg
 polygons:
   1:
   - [500, 1200]
--- a/data/levels/alientunnel3	Sun Sep 08 00:33:15 2013 +0200
+++ b/data/levels/alientunnel3	Sun Sep 08 00:48:35 2013 +0200
@@ -70,7 +70,7 @@
   classname: AcidFloor
   name: acid_3_2
 lines: []
-music: POL-cyber-factory-short.ogg
+music: POL-future-war-short.ogg
 polygons:
   1:
   - [500, 1200]
--- a/data/levels/cargo_bay	Sun Sep 08 00:33:15 2013 +0200
+++ b/data/levels/cargo_bay	Sun Sep 08 00:48:35 2013 +0200
@@ -122,7 +122,7 @@
   - 'This place always reminds me of something... Oh, I must buy milk.'
   classname: Note
 lines: []
-music: POL-cyber-factory-short.ogg
+music: POL-tekdrome-short.ogg
 polygons:
   1:
   - [614, 1200]
--- a/data/levels/crew	Sun Sep 08 00:33:15 2013 +0200
+++ b/data/levels/crew	Sun Sep 08 00:48:35 2013 +0200
@@ -125,6 +125,21 @@
   - 300
   classname: ChargingAlien
   name: room_two_alien
+- args:
+  - [2816, 1344]
+  - 300
+  classname: ChargingAlien
+  name: lift_alien_1
+- args:
+  - [2648, 1336]
+  - 300
+  classname: ChargingAlien
+  name: lift_alien_2
+- args:
+  - [2728, 1528]
+  - 300
+  classname: ChargingAlien
+  name: lift_alien_3
 game_objects:
 - args:
   - [730, 936]
@@ -212,6 +227,10 @@
   classname: Door
   name: room_one_exit
 - args:
+  - [440, 572]
+  classname: collectable.Gun
+  name: gun
+- args:
   - [982, 671]
   - null
   - [1041, 685]
@@ -360,8 +379,20 @@
   - cargo_bay
   - [1760, 1265]
   - -45
-  classname: Door
+  - hangar_door_puzzle
+  classname: PuzzleDoor
   name: to_hangar
+- args: [lift_switch_1, lift_switch_2]
+  classname: puzzle.StateLogicalAndPuzzler
+  name: hangar_door_puzzle
+- args:
+  - [2900, 1576]
+  classname: FloorSwitch
+  name: lift_switch_1
+- args:
+  - [2556, 1220]
+  classname: FloorSwitch
+  name: lift_switch_2
 - args:
   - [2596, 2248]
   - - [2573, 1869]
@@ -446,7 +477,7 @@
   classname: SokoBox
   name: lift_box_3
 lines: []
-music: POL-cyber-factory-short.ogg
+music: POL-against-the-system-short.ogg
 polygons:
   1:
   - [766, 2334]
--- a/data/levels/finale	Sun Sep 08 00:33:15 2013 +0200
+++ b/data/levels/finale	Sun Sep 08 00:48:35 2013 +0200
@@ -41,8 +41,99 @@
     - [2400, 1800]
   classname: AcidFloor
   name: acid_4
-lines: []
+- args:
+  - [1900, 1700]
+  - sensor_1
+  classname: FloorLight
+  name: light_1
+- args:
+  - [1700, 1900]
+  - sensor_2
+  classname: FloorLight
+  name: light_2
+- args:
+  - [700, 2100]
+  - end
+  - [100, 100]
+  - 135
+  - keycard_cyan
+  classname: KeyedDoor
+  name: exit
+- args:
+  - [950, 1850]
+  - finale
+  - [900, 1900]
+  - 135
+  - keycard_magenta
+  classname: KeyedDoor
+  name: pre_exit
+- args:
+  - [1200, 1600]
+  - finale
+  - [1150, 1650]
+  - 135
+  - keycard_yellow
+  classname: KeyedDoor
+  name: pre_pre_exit
+- args:
+  - [1900, 1400]
+  classname: FloorSwitch
+  name: sensor_2
+- args:
+  - [1400, 900]
+  classname: FloorSwitch
+  name: sensor_1
+- args: [sensor_1, sensor_2]
+  classname: puzzle.StateLogicalAndPuzzler
+  name: both_sensors
+- args:
+  - [1800, 1900]
+  - [1900, 1800]
+  - both_sensors
+  classname: Hatch
+  name: finale_hatch
+- args:
+  - [2000, 2000]
+  classname: ToggleSwitch
+  name: finale_toggle
+- args:
+  - [900, 1000]
+  - [1000, 900]
+  - finale_toggle
+  classname: Hatch
+  name: finale_magenta_hatch
+- args:
+  - [800, 800]
+  - keycard_magenta
+  classname: collectable.KeyCard
+  name: keycard_magenta
+lines:
+- - [1100, 1500]
+  - [500, 1900]
+- - [1300, 1700]
+  - [900, 2300]
+- - [1300, 1700]
+  - [1100, 1500]
+- - [1100, 2000]
+  - [800, 1700]
+- - [1900, 2300]
+  - [1700, 2000]
+- - [2000, 1700]
+  - [2300, 1900]
+- - [1800, 1900]
+  - [1700, 2000]
+- - [2000, 1700]
+  - [1900, 1800]
+- - [800, 1100]
+  - [500, 900]
+- - [1100, 800]
+  - [900, 500]
+- - [900, 1000]
+  - [800, 1100]
+- - [1100, 800]
+  - [1000, 900]
 music: moonlight-sonata.ogg
+music_volume: 1.0
 polygons:
   1:
   - [1000, 2400]
--- a/data/levels/sheep	Sun Sep 08 00:33:15 2013 +0200
+++ b/data/levels/sheep	Sun Sep 08 00:48:35 2013 +0200
@@ -80,7 +80,7 @@
   - [400, 600]
 - - [650, 500]
   - [650, 600]
-music: POL-cyber-factory-short.ogg
+music: POL-tunnels-short.ogg
 polygons:
   1:
   - [50, 50]
Binary file data/music/POL-against-the-system-short.ogg has changed
Binary file data/music/POL-future-war-short.ogg has changed
Binary file data/music/POL-tekdrome-short.ogg has changed
Binary file data/music/POL-tunnels-short.ogg has changed
--- a/data/music/SOURCES.txt	Sun Sep 08 00:33:15 2013 +0200
+++ b/data/music/SOURCES.txt	Sun Sep 08 00:48:35 2013 +0200
@@ -20,6 +20,82 @@
     Ogg generated using 'oggenc POL-cyber-factory-short.wav'
 
 
+POL-against-the-system-short.ogg
+--------------------------------
+
+URL:
+    http://www.backgroundmusicloops.net/free-music-loops/POL-against-the-system-short.zip
+Source:
+    http://www.playonloop.com/2011-music-loops/against-the-system/
+License:
+    http://creativecommons.org/licenses/by/3.0/
+Credit:
+    This royalty-free music loop is filed under the Heavy Rock > Rock
+    category, tagged as Action, Aggressive, Evolving, Powerful and is
+    ready for instant download! You can use this background music loop
+    in any kind of production such as Video, Game, Flash, Website,
+    Slideshow, Presentation, Mobile App, Radio, TV, and much more!
+Notes:
+    Ogg generated using 'oggenc -q -1 POL-against-the-system-short.wav'
+
+
+POL-tekdrome-short.ogg
+----------------------
+
+URL:
+    http://www.backgroundmusicloops.net/free-music-loops/POL-tekdrome-short.zip
+Source:
+    http://www.playonloop.com/2012-music-loops/tekdrome/
+License:
+    http://creativecommons.org/licenses/by/3.0/
+Credit:
+    This royalty-free music loop is filed under the Dance > Electronic
+    category, tagged as Action, Driving, Hi-tech, Powerful and is
+    ready for instant download! You can use this background music loop
+    in any kind of production such as Video, Game, Flash, Website,
+    Slideshow, Presentation, Mobile App, Radio, TV, and much more!
+Notes:
+    Ogg generated using 'oggenc -q -1 POL-tekdrome-short.wav'
+
+
+POL-future-war-short.ogg
+------------------------
+
+URL:
+    http://www.backgroundmusicloops.net/free-music-loops/POL-future-war-short.zip
+Source:
+    http://www.playonloop.com/2010-music-loops/future-war/
+License:
+    http://creativecommons.org/licenses/by/3.0/
+Credit:
+    This royalty-free music loop is filed under the Electronic >
+    Psychedelic category, tagged as Action, Dark and is ready for
+    instant download! You can use this background music loop in any
+    kind of production such as Video, Game, Flash, Website, Slideshow,
+    Presentation, Mobile App, Radio, TV, and much more!
+Notes:
+    Ogg generated using 'oggenc -q -1 POL-future-war-short.wav'
+
+
+POL-tunnels-short.ogg
+---------------------
+
+URL:
+    http://www.backgroundmusicloops.net/free-music-loops/POL-tunnels-short.zip
+Source:
+    http://www.playonloop.com/2010-music-loops/tunnels/
+License:
+    http://creativecommons.org/licenses/by/3.0/
+Credit:
+    This royalty-free music loop is filed under the 8 Bit > Videogame
+    category, tagged as Action, Driving and is ready for instant
+    download! You can use this background music loop in any kind of
+    production such as Video, Game, Flash, Website, Slideshow,
+    Presentation, Mobile App, Radio, TV, and much more!
+Notes:
+    Ogg generated using 'oggenc POL-tunnels-short.wav'
+
+
 moonlight-sonata.ogg
 --------------------
 
--- a/nagslang/collectable.py	Sun Sep 08 00:33:15 2013 +0200
+++ b/nagslang/collectable.py	Sun Sep 08 00:48:35 2013 +0200
@@ -57,6 +57,10 @@
             render.ImageRenderer(resources.get_image('objects', 'gun.png')),
         )
 
+    @classmethod
+    def requires(cls):
+        return [("name", "string"), ("position", "coordinates")]
+
 
 class KeyCard(CollectibleGameObject):
     def __init__(self, space, position, name):
--- a/nagslang/constants.py	Sun Sep 08 00:33:15 2013 +0200
+++ b/nagslang/constants.py	Sun Sep 08 00:48:35 2013 +0200
@@ -19,6 +19,8 @@
 BITSIZE = -16  # unsigned 16 bit
 CHANNELS = 2   # 1 == mono, 2 == stereo
 BUFFER = 1024  # audio buffer size in no. of samples
+DEFAULT_SOUND_VOLUME = 1.0  # sound volume
+DEFAULT_MUSIC_VOLUME = 0.3  # music volume
 
 COLLISION_TYPE_OTHER = 0
 COLLISION_TYPE_PLAYER = 1
@@ -60,7 +62,7 @@
 PROTAGONIST_HEALTH_MAX_LEVEL = 100
 PROTAGONIST_HEALTH_MIN_LEVEL = 0
 
-BULLET_DAMAGE = 10
+BULLET_DAMAGE = 25
 CLAW_DAMAGE = 5
 ACID_DAMAGE = 7
 
--- a/nagslang/enemies.py	Sun Sep 08 00:33:15 2013 +0200
+++ b/nagslang/enemies.py	Sun Sep 08 00:48:35 2013 +0200
@@ -10,6 +10,7 @@
     ACID_SPEED, ACID_DAMAGE, ZORDER_MID)
 from nagslang.game_object import (GameObject, SingleShapePhysicser, Result,
                                   Bullet, make_body)
+from nagslang.collectable import KeyCard
 from nagslang.mutators import FLIP_H
 from nagslang.resources import resources
 from nagslang.utils import vec_with_length
@@ -334,6 +335,9 @@
     def update(self, dt):
         result = super(Queen, self).update(dt)
         self.spawn(result)
+        if self.health <= 0:
+            result.add += (KeyCard(self.get_space(),
+            self.physicser.position, "keycard_cyan"),)
         return result
 
     @classmethod
@@ -353,7 +357,7 @@
 
     def make_physics(self, space, position):
         body = make_body(10, pymunk.inf, position, 0.8)
-        shape = pymunk.Circle(body, 30)
+        shape = pymunk.Circle(body, 20)
         shape.elasticity = 1.0
         shape.friction = 0.05
         shape.collision_type = COLLISION_TYPE_SHEEP
--- a/nagslang/game_object.py	Sun Sep 08 00:33:15 2013 +0200
+++ b/nagslang/game_object.py	Sun Sep 08 00:48:35 2013 +0200
@@ -6,7 +6,7 @@
 from nagslang import environment
 from nagslang import puzzle
 from nagslang import render
-from nagslang.mutators import FLIP_H, ImageOverlay, rotator
+from nagslang.mutators import FLIP_H, ImageOverlay, rotator, scaler
 from nagslang.constants import (
     COLLISION_TYPE_DOOR, COLLISION_TYPE_FURNITURE, COLLISION_TYPE_PROJECTILE,
     COLLISION_TYPE_SWITCH, COLLISION_TYPE_SHEEP, COLLISION_TYPE_SHEEP_PEN,
@@ -508,8 +508,15 @@
     zorder = ZORDER_FLOOR
 
     def __init__(self, space, end1, end2, key_state=None):
-        body = make_body(None, None, (0, 0))
-        self.shape = pymunk.Segment(body, tuple(end1), tuple(end2), 7)
+        a = pymunk.Vec2d(end1)
+        b = pymunk.Vec2d(end2)
+        offset = b - a
+        offset.length /= 2
+        mid = (a + offset).int_tuple
+        body = make_body(None, None, mid)
+        self.shape = pymunk.Segment(
+            body, body.world_to_local(tuple(end1)),
+            body.world_to_local(tuple(end2)), 7)
         self.shape.collision_type = COLLISION_TYPE_DOOR
         if key_state is None:
             puzzler = puzzle.YesPuzzler()
@@ -534,7 +541,53 @@
         return [("name", "string"), ("end1", "coordinates"),
                 ("end2", "coordinates"), ("key_state", "puzzler")]
 
-    # The level knows that bulkheads are magical
+    # The level knows that hatches are magical
+    @classmethod
+    def movable(cls):
+        return True
+
+
+class KeyedHatch(GameObject):
+    zorder = ZORDER_FLOOR
+
+    def __init__(self, space, end1, end2, key_item):
+        a = pymunk.Vec2d(end1)
+        b = pymunk.Vec2d(end2)
+        offset = b - a
+        offset.length /= 2
+        mid = (a + offset).int_tuple
+        body = make_body(None, None, mid)
+        self.shape = pymunk.Segment(
+            body, body.world_to_local(tuple(end1)),
+            body.world_to_local(tuple(end2)), 7)
+        self.shape.collision_type = COLLISION_TYPE_DOOR
+        self._key_item = key_item
+        super(KeyedHatch, self).__init__(
+            SingleShapePhysicser(space, self.shape),
+            render.KeyedHatchRenderer(
+                resources.get_image(
+                    'objects', '%s.png' % (key_item,),
+                    transforms=(scaler((32, 32)),))),
+            puzzle.ParentAttrPuzzler('is_open'),
+        )
+        self.add_timer('door_open', 0.1)
+
+    @property
+    def is_open(self):
+        return self.check_timer('door_open')
+
+    def collide_with_protagonist(self, protagonist):
+        if protagonist.has_item(self._key_item):
+            self.start_timer('door_open')
+            return False
+        return True
+
+    @classmethod
+    def requires(cls):
+        return [("name", "string"), ("end1", "coordinates"),
+                ("end2", "coordinates"), ("key_item", "item name")]
+
+    # The level knows that hatches are magical
     @classmethod
     def movable(cls):
         return True
--- a/nagslang/level.py	Sun Sep 08 00:33:15 2013 +0200
+++ b/nagslang/level.py	Sun Sep 08 00:48:35 2013 +0200
@@ -12,6 +12,7 @@
     tile_surface, points_to_pygame, extend_line, points_to_lines)
 from nagslang.resources import resources
 from nagslang.yamlish import load, dump
+from nagslang.constants import DEFAULT_MUSIC_VOLUME
 
 POLY_COLORS = {
     1: pygame.color.THECOLORS['red'],
@@ -42,6 +43,7 @@
         self.world.level_state.setdefault(name, {})
         self.basetile = 'tiles/floor.png'
         self.music = None
+        self.music_volume = None
         self._tile_image = None
         self._surface = None
         self._base_surface = None
@@ -65,6 +67,7 @@
             'polygons': self.polygons,
             'lines': self.lines,
             'music': self.music,
+            'music_volume': self.music_volume,
             'game_objects': self._game_objects,
             'enemies': self._enemies,
         }, f)
@@ -94,6 +97,7 @@
         self.x, self.y = data['size']
         self.basetile = data['base_tile']
         self.music = data['music']
+        self.music_volume = data.get('music_volume', DEFAULT_MUSIC_VOLUME)
         for i, points in data['polygons'].iteritems():
             self.polygons[i] = []
             for point in points:
--- a/nagslang/mutators.py	Sun Sep 08 00:33:15 2013 +0200
+++ b/nagslang/mutators.py	Sun Sep 08 00:48:35 2013 +0200
@@ -18,7 +18,7 @@
     def __eq__(self, other):
         if not isinstance(other, Mutator):
             return NotImplemented
-        return (self._func is other._func) and (self._args == other._args)
+        return (self._func == other._func) and (self._args == other._args)
 
     def __repr__(self):
         return '<%s %r>' % (self.__class__.__name__, self._args)
--- a/nagslang/render.py	Sun Sep 08 00:33:15 2013 +0200
+++ b/nagslang/render.py	Sun Sep 08 00:48:35 2013 +0200
@@ -47,7 +47,7 @@
         pass
 
 
-class HatchRenderer(Renderer):
+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))
@@ -56,18 +56,25 @@
         pygame.draw.line(
             surface, pygame.color.THECOLORS['lightblue'], ai, bi, 5)
 
-    def render(self, surface):
+    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((shape.b - shape.a).angle, 10)
-            ai = shape.a + offset
-            bi = shape.b - offset
-            self.draw_hatch_line(surface, shape.a, ai)
-            self.draw_hatch_line(surface, bi, shape.b)
+            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 = shape.a + (shape.b - shape.a) / 2
-            self.draw_hatch_line(surface, shape.a, mid)
-            self.draw_hatch_line(surface, mid, shape.b)
+            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):
@@ -96,6 +103,13 @@
         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
--- a/nagslang/screens/area.py	Sun Sep 08 00:33:15 2013 +0200
+++ b/nagslang/screens/area.py	Sun Sep 08 00:48:35 2013 +0200
@@ -94,7 +94,7 @@
         self.add_protagonist()
         self.add_game_objects()
         self.save_progress()
-        sound.play_music(self._level.music)
+        sound.play_music(self._level.music, self._level.music_volume)
         self._background = None
         self._surface = None
 
--- a/nagslang/sound.py	Sun Sep 08 00:33:15 2013 +0200
+++ b/nagslang/sound.py	Sun Sep 08 00:48:35 2013 +0200
@@ -4,17 +4,19 @@
 
 from nagslang.options import options
 from nagslang.resources import resources
-from nagslang.constants import FREQ, BITSIZE, CHANNELS, BUFFER
+from nagslang.constants import (
+    FREQ, BITSIZE, CHANNELS, BUFFER, DEFAULT_SOUND_VOLUME,
+    DEFAULT_MUSIC_VOLUME)
 
 
 class DummySound(object):
     def init(self):
         pass
 
-    def play_sound(self, name, volume=None):
+    def play_sound(self, name, volume=DEFAULT_SOUND_VOLUME):
         pass
 
-    def play_music(self, name, volume=None):
+    def play_music(self, name, volume=DEFAULT_MUSIC_VOLUME):
         pass
 
     def pause_music(self):
@@ -42,13 +44,13 @@
             sound = self._sounds[track_name] = mixer.Sound(track_name)
         return sound
 
-    def play_sound(self, name, volume=1.0):
+    def play_sound(self, name, volume=DEFAULT_SOUND_VOLUME):
         sound = self.load_sound(name)
         if sound is not None:
             sound.set_volume(volume)
             sound.play()
 
-    def play_music(self, name, volume=1.0):
+    def play_music(self, name, volume=DEFAULT_MUSIC_VOLUME):
         if not options.music:
             return
         track_name = resources.get_resource_path("music", name)