Merge branch 'master' of git://ctpug.org.za/naja
authorDavid Sharpe <decoydavid@gmail.com>
Sat, 17 May 2014 23:15:24 +0000 (01:15 +0200)
committerDavid Sharpe <decoydavid@gmail.com>
Sat, 17 May 2014 23:15:24 +0000 (01:15 +0200)
data/location_decks/introduction.yaml
data/location_decks/puzzles/belts.yaml [new file with mode: 0644]
naja/actions.py
naja/constants.py
naja/gameboard.py
naja/player.py
naja/scenes/credits.py
naja/widgets/info_area.py
naja/widgets/text.py

index 6f289233f82a4fae9f112d5d2b284e00e4d40c61..eba72a50aca2758fcafc109515e01c2bbcd22093 100644 (file)
@@ -19,7 +19,7 @@ _action_definitions:
     action_class: 'DoNothing'
     required_bits: []
     data:
-      message: "Welcome to the tutorial!\nYou can select any tile, but only move in directions with bits set. These are checquered. Now, only the current tile and 1 tile {EAST}.\nSelect {EAST} tile and press {RETURN}."
+      message: "Welcome to the tutorial! You can select any tile, but only move {NORTH,SOUTH,EAST} or {WEST} if those bits are set — allowed tiles are chequered.\nRight now you can stay on this tile or move {EAST}.\nSelect {EAST} tile and press {RETURN}."
 
   - &STEP1-CARD
     card_name: 'step1'
@@ -67,13 +67,20 @@ _action_definitions:
     action_class: 'SetBits'
     required_bits: []
     data:
-      message: "There are key bits that can be set, such as {RED}. They may be required by actions.\nThis action also sets {EAST,SOUTH}."
+      message: "This action sets {EAST,SOUTH}."
+
+  - &STEP4-ACTION-GREEN
+    action_class: 'GainHealth'
+    required_bits: [GREEN]
+    data:
+      message: "Some actions require key bits to be set.\nIf you haven't got {GREEN} yet, this action is greyed out to show that you can't use it."
 
   - &STEP4-CARD
     card_name: 'step4'
     bits: [RED, SOUTH, EAST]
     actions:
       - *STEP4-ACTION
+      - *STEP4-ACTION-GREEN
 
   - &STEP5-ACTION-BLUE
     action_class: 'GenericBits'
@@ -125,7 +132,7 @@ _action_definitions:
     action_class: 'SetBits'
     required_bits: []
     data:
-      message: "Almost there. You have free rein in this column.\nFinish the game by collecting enough {WINTOKEN}. 1 in this level.\nGood luck!"
+      message: "Almost there. You have free rein in this column.\nFinish the game by collecting enough {WINTOKEN}: 1 in this level.\nGood luck!"
 
   - &STEP7-CARD
     card_name: 'step7'
@@ -178,7 +185,7 @@ _action_definitions:
     action_class: 'GenericBits'
     required_bits: []
     data:
-      message: "We may have lied to you about restricting to this column.\nSet {WEST}. Clear {NORTH,SOUTH,EAST}."
+      message: "We may have lied about restricting you to this column.\nSet {WEST}. Clear {NORTH,SOUTH,EAST}."
       set: [WEST]
       clear: [NORTH, SOUTH, EAST]
 
@@ -192,7 +199,7 @@ _action_definitions:
     action_class: 'GenericBits'
     required_bits: []
     data:
-      message: "In this board, tiles didn't change. In the random games, after taking an action (or a timer expires) the tile will be replaced.\nSet {NORTH}. Clear {SOUTH,EAST,WEST}."
+      message: "On this board, tiles don't change. In random games, after you take an action the tile will be replaced. Some tiles are also replaced when a timer expires.\nSet {NORTH}. Clear {SOUTH,EAST,WEST}."
       set: [NORTH]
       clear: [SOUTH, EAST, WEST]
 
diff --git a/data/location_decks/puzzles/belts.yaml b/data/location_decks/puzzles/belts.yaml
new file mode 100644 (file)
index 0000000..4f402fd
--- /dev/null
@@ -0,0 +1,123 @@
+description: "Moving the Mountain"
+puzzle: true
+
+# This field is ignored, but it's a useful place to put some action definitions
+# we can reference later.
+_action_definitions:
+  - &DO-NOTHING
+    action_class: DoNothing
+    required_bits: []
+  - &WIN-ONCE
+    action_class: GenericBits
+    required_bits: [RED, GREEN, BLUE]
+    data:
+      clear: [RED, GREEN, BLUE]
+      acquire_win: true
+      lose_health: true
+      once: true
+  - &KNIGHT
+    action_class: AllowChessMove
+    required_bits: []
+    data:
+      chesspiece: KNIGHT
+
+# This field is ignored, but it's a useful place to put some card definitions
+# we can reference later.
+_card_definitions:
+  - &WIN-CARD
+    card_name: 'win-card'
+    bits: [RED, GREEN, BLUE]
+    actions:
+      - *KNIGHT
+      - *WIN-ONCE
+  - &BORING
+    card_name: 'boring'
+    actions:
+      - *KNIGHT
+  - &RED-CARD
+    card_name: 'red-card'
+    bits: [RED]
+    actions:
+      - *KNIGHT
+      - action_class: GenericBits
+        required_bits: []
+        data:
+          toggle: [RED]
+  - &BLUE-CARD
+    card_name: 'blue-card'
+    bits: [BLUE]
+    actions:
+      - *KNIGHT
+      - action_class: GenericBits
+        required_bits: []
+        data:
+          toggle: [BLUE]
+  - &GREEN-CARD
+    card_name: 'green-card'
+    bits: [GREEN]
+    actions:
+      - *KNIGHT
+      - action_class: GenericBits
+        required_bits: []
+        data:
+          toggle: [GREEN]
+  - &HOSPITAL
+    card_name: 'hospital'
+    bits: []
+    actions:
+      - *KNIGHT
+      - action_class: GenericBits
+        required_bits: []
+        data:
+          gain_health: true
+  - &UNIVERSAL
+    card_name: 'universal-transporter'
+    bits: []
+    actions:
+      - *KNIGHT
+      - action_class: ShiftLocations
+        required_bits: []
+        data:
+          direction: NORTH
+          skip_current: false
+          move_player: true
+      - action_class: ShiftLocations
+        required_bits: []
+        data:
+          direction: WEST
+          skip_current: false
+          move_player: true
+
+cards:
+  - *WIN-CARD
+  - *BORING
+  - *BORING
+  - *BORING
+  - *WIN-CARD
+
+  - *BORING
+  - *BORING
+  - *GREEN-CARD
+  - *BORING
+  - *BORING
+
+  - *BORING
+  - *RED-CARD
+  - *UNIVERSAL
+  - *BLUE-CARD
+  - *BORING
+
+  - *BORING
+  - *BORING
+  - *HOSPITAL
+  - *BORING
+  - *BORING
+
+  - *WIN-CARD
+  - *BORING
+  - *BORING
+  - *BORING
+  - *WIN-CARD
+
+defaults:
+  initial_bits: 0x0
index 4d4a0f0b3ac9960b39f335ddb0e54253e18037d3..08d63693bf1c4915b1886177acba5b286ec934e2 100644 (file)
@@ -94,7 +94,7 @@ class DoNothing(LocationAction):
 
 
 class LoseHealthOrMSB(LocationAction):
-    TEXT = "Lose {HEALTH} or {MSB}."
+    TEXT = "Lose {MSB} or {HEALTH}."
     MSB_GLYPH = ACTION_GLYPHS.DAMAGE
 
     def perform_action(self, board, location):
@@ -233,7 +233,7 @@ class ShiftBits(LocationAction):
 
 
 class LoseHealthOrMSBAndSetBits(LocationAction):
-    TEXT = "Lose {HEALTH} or {MSB}, then set %(location_bits)s."
+    TEXT = "Lose {MSB} or {HEALTH}, then set %(location_bits)s."
     GLYPHS = (ACTION_GLYPHS.SET_BITS,)
     MSB_GLYPH = ACTION_GLYPHS.DAMAGE
 
@@ -255,14 +255,16 @@ class AcquireWinToken(LocationAction):
             BITS.RED, BITS.GREEN, BITS.BLUE,
         ]))
 
+
 class AcquireWinTokenAndLoseHealth(AcquireWinToken):
     TEXT = "Gain {WINTOKEN}, lose {HEALTH}, then clear {RED,GREEN,BLUE}."
     GLYPHS = (ACTION_GLYPHS.WINTOKEN, ACTION_GLYPHS.DAMAGE)
 
     def perform_action(self, board, location):
         self.take_damage(board)
-        super(AcquireWinTokenAndLoseHealth, self).perform_action(board,
-                                                                location)
+        super(AcquireWinTokenAndLoseHealth, self).perform_action(
+            board, location)
+
 
 class GainHealth(LocationAction):
     TEXT = "Gain {HEALTH}."
@@ -274,7 +276,7 @@ class GainHealth(LocationAction):
 
 
 class GainHealthAndClearBitsOrMSB(LocationAction):
-    TEXT = "Gain {HEALTH}, then clear %(location_bits)s or {MSB}."
+    TEXT = "Gain {HEALTH}, then clear {MSB} or %(location_bits)s."
     GLYPHS = (ACTION_GLYPHS.HEAL,)
     MSB_GLYPH = ACTION_GLYPHS.CLEAR_BITS
 
@@ -291,7 +293,15 @@ class ShiftLocations(LocationAction):
 
     def perform_action(self, board, location):
         sound.play_sound('grind.ogg')
-        board.shift_locations(self.data['direction'])
+        board.shift_locations(
+            self.data['direction'],
+            self.data.get('skip_current', True))
+        if self.data.get('move_player', False):
+            pos = {
+                'NORTH': (0, -1), 'SOUTH': (0, 1),
+                'EAST': (1, 0), 'WEST': (-1, 0),
+            }.get(self.data['direction'], (0, 0))
+            board.player.force_position(pos, delta=True)
 
 
 class RotateLocations(LocationAction):
@@ -315,8 +325,8 @@ class AllowChessMove(LocationAction):
 
 class AllowChessMoveIfMSB(LocationAction):
     TEXT = (
-        "Clear {MSB} and move like a %(chesspiece_name)s for one turn if it "
-        "was set.")
+        "If {MSB} is set, unset {MSB} and move like a "
+        "%(chesspiece_name)s for one turn. Otherwise do nothing.")
     MSB_GLYPH = ACTION_GLYPHS.MOVEMENT
 
     def perform_action(self, board, location):
index 486eacbf86033171e4d91d355890c783f1eb5515..7dbd9696d67fce27a0ed1d15756e4f2ff6e72dad 100644 (file)
@@ -160,4 +160,5 @@ TUNES = (
 PUZZLES = (
     'chess',
     'marathon',
+    'belts',
 )
index 66c651936099520c0d163786881b7422974fac94..894f2b31f052b1464a6cdb1876187a5aefc8d55c 100644 (file)
@@ -206,30 +206,34 @@ class GameBoard(object):
                     del choices[key]
         return choice(choices.values())
 
-    def shift_location_row(self, change, is_vertical):
+    def shift_location_row(self, change, is_vertical, skip_player=True):
         px, py = self.player.position
         shifted_locations = {}
         mkpos = lambda i: (px, i) if is_vertical else (i, py)
 
         for i in range(5):
-            if (px, py) == mkpos(i):
+            if skip_player and (px, py) == mkpos(i):
                 continue
             new_i = (i + change) % 5
-            if (px, py) == mkpos(new_i):
+            if skip_player and (px, py) == mkpos(new_i):
                 new_i = (new_i + change) % 5
             shifted_locations[mkpos(new_i)] = self.board_locations[mkpos(i)]
 
         self.board_locations.update(shifted_locations)
 
-    def shift_locations(self, direction):
+    def shift_locations(self, direction, skip_player=True):
         if BITS[direction] == BITS.NORTH:
-            self.shift_location_row(-1, is_vertical=True)
+            self.shift_location_row(-1, is_vertical=True,
+                                    skip_player=skip_player)
         elif BITS[direction] == BITS.SOUTH:
-            self.shift_location_row(1, is_vertical=True)
+            self.shift_location_row(1, is_vertical=True,
+                                    skip_player=skip_player)
         elif BITS[direction] == BITS.EAST:
-            self.shift_location_row(1, is_vertical=False)
+            self.shift_location_row(1, is_vertical=False,
+                                    skip_player=skip_player)
         elif BITS[direction] == BITS.WEST:
-            self.shift_location_row(-1, is_vertical=False)
+            self.shift_location_row(-1, is_vertical=False,
+                                    skip_player=skip_player)
 
     def rotate_locations(self, direction):
         px, py = self.player.position
index 58cb8245e6c5f0f3b466f25bf2d007dba2e544ef..d47e82682cce298a57424eaae1918d3f100e9615 100644 (file)
@@ -151,6 +151,13 @@ class Player(object):
             return True
         return False
 
+    def force_position(self, pos, delta=True):
+        if delta:
+            pos = (self.position[0] + pos[0],
+                   self.position[1] + pos[1])
+        if (0 <= pos[0] < 5 and 0 <= pos[1] < 5):
+            self.position = pos
+
     def set_gameboard(self, gameboard):
         self.gameboard = gameboard
 
index a522c986c1207ba6a97e7e84bac9bb56475eaabe..8a6cfc238510eddf4cd8573e1697becc2be3ac44 100644 (file)
@@ -3,10 +3,12 @@
 Credits scene.
 """
 
+import pygame
 import pygame.locals as pgl
 
-from naja.constants import KEYS
+from naja.constants import KEYS, FPS
 from naja.scenes.scene import Scene
+from naja.utils import Flashlight
 from naja.widgets.text import TextWidget, TextBoxWidget
 from naja.widgets.image_box import ImageBox
 from naja.events import SceneChangeEvent
@@ -19,6 +21,7 @@ class CreditsScene(Scene):
 
     def __init__(self, state):
         super(CreditsScene, self).__init__(state)
+        self.flashlight = Flashlight(int(1.5 * FPS))
 
         background = ImageBox(
             (0, 0), "screens/splash.png")
@@ -26,7 +29,7 @@ class CreditsScene(Scene):
         self.add(TextWidget(
             (400, 300), 'CREDITS', colour='white', centre=True))
 
-        self.add(TextBoxWidget(
+        self.credits = TextBoxWidget(
             (400, 340), '\n'.join([
                 'A game about robots and bits and other things',
                 '',
@@ -40,6 +43,10 @@ class CreditsScene(Scene):
                 'Desilu Crossman',
                 'for snacks and tutorial inspiration',
                 '',
+                'Bearnard Hibbins, Graham Inggs,',
+                'and William Stewart',
+                'for playtesting and finding bugs',
+                '',
                 'Music by Rolemusic:',
                 'http://rolemusic.sawsquarenoise.com/',
                 '',
@@ -48,7 +55,19 @@ class CreditsScene(Scene):
             ]),
             colour='white', padding=1,
             bg_colour=(0, 0, 0, 0), centre=True,
-            box_width=780, view_port=(780, 250)))
+            box_width=780, view_port=(780, 250))
+        self.add(self.credits)
+
+    def render_scene(self, surface):
+        if self.flashlight is not None:
+            if self.flashlight.tick():
+                fake_event = pygame.event.Event(pgl.KEYDOWN, key=pgl.K_DOWN)
+                self.credits.handle_event(fake_event)
+
+    def handle_event(self, ev):
+        if ev.type == pgl.KEYDOWN:
+            self.flashlight = None
+        return super(CreditsScene, self).handle_event(ev)
 
     def handle_scene_event(self, ev):
         if ev.type == pgl.KEYDOWN and ev.key in KEYS.QUIT:
index fd470387e6b994a2d15eace94df7df8c22892274..21122cae31797262fbf83eeb5a9add317d09209c 100644 (file)
@@ -21,12 +21,12 @@ from naja import constants
 
 
 HINTS = {
-    ACT: "Choose an action using {NORTH,SOUTH} keys.\n"
-         "Press {RETURN} to execute it.",
+    ACT: "Choose an action using {NORTH,SOUTH}."
+         " Press {RETURN} to execute it.",
     EXAMINE: "Browse the tiles with {NORTH,SOUTH,EAST,WEST} keys.",
 }
 
-HINT_LEGAL_MOVE = "\nPress {RETURN} to move to this tile."
+HINT_LEGAL_MOVE = "\nPress {RETURN} to move."
 
 TITLES = {
     ACT: "Choose an Action",
index af7f0ac55c11b898b103363aec27dcde9c855e12..f105e3bc1cb972dd6dde63534b1fd2e833d05928 100644 (file)
@@ -171,7 +171,7 @@ class TextBoxWidget(TextWidget):
 
     def _check_markup(self, word):
         suffix = ''
-        if word[-1] in '.,':
+        if word[-1] in '.,:':
             suffix = word[-1]
             word = word[:-1]