Merge branch 'master' of git://ctpug.org.za/naja
authorDavid Sharpe <decoydavid@gmail.com>
Sat, 17 May 2014 19:56:12 +0000 (21:56 +0200)
committerDavid Sharpe <decoydavid@gmail.com>
Sat, 17 May 2014 19:56:12 +0000 (21:56 +0200)
14 files changed:
data/images/board/msb_lock_decoration.png [new file with mode: 0644]
data/location_decks/introduction.yaml
data/location_decks/puzzles/chess.yaml
naja/actions.py
naja/gameboard.py
naja/options.py
naja/scenes/howto.py
naja/scenes/introduction.py [new file with mode: 0644]
naja/scenes/menu.py
naja/scenes/puzzlelist.py
naja/widgets/info_area.py
naja/widgets/tile.py
packaging/darwin-py2app.sh
sources/images/msb_lock_decoration.xcf [new file with mode: 0644]

diff --git a/data/images/board/msb_lock_decoration.png b/data/images/board/msb_lock_decoration.png
new file mode 100644 (file)
index 0000000..2b00be8
Binary files /dev/null and b/data/images/board/msb_lock_decoration.png differ
index 4f7979b16872f4dfdafa6b107b07b194bd74b6b2..c6a47870cb20b237dda5d0773264c129dc8ba3ef 100644 (file)
@@ -184,6 +184,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 the timer expires) the tile will be replaced by another one."
       set: [NORTH]
       clear: [SOUTH, EAST, WEST]
 
index d98e400fdfe1524a6c203b53827ef384a33e0c08..dca1a9f8931c86ea25a18e1292f10d7235fd346f 100644 (file)
@@ -1,6 +1,18 @@
 description: "Kasparov to F3"
 puzzle: true
 
+# This field is ignored, but it's a useful place to put some action definitions
+# we can reference later.
+_action_defitions:
+  - &PYRRHIC-VICTORY
+    action_class: GenericBits
+    required_bits: [RED, GREEN, BLUE]
+    data:
+      clear: [NORTH, SOUTH, EAST, WEST, RED, GREEN, BLUE, MSB]
+      acquire_win: true
+      lose_health: true
+      once: true
+
 # This field is ignored, but it's a useful place to put some card definitions
 # we can reference later.
 _card_definitions:
@@ -11,13 +23,7 @@ _card_definitions:
         required_bits: []
         data:
           chesspiece: BISHOP
-      - action_class: GenericBits
-        required_bits: [RED, GREEN, BLUE]
-        data:
-          clear: [NORTH, SOUTH, EAST, WEST, RED, GREEN, BLUE, MSB]
-          acquire_win: true
-          lose_health: true
-          once: true
+      - *PYRRHIC-VICTORY
   - &CHESS-WIN-CARD-CASTLE
     card_name: 'chess-win-rook'
     actions:
@@ -25,10 +31,7 @@ _card_definitions:
         required_bits: []
         data:
           chesspiece: CASTLE
-      - action_class: AcquireWinToken
-        required_bits: [RED, GREEN, BLUE]
-        data:
-          once: true
+      - *PYRRHIC-VICTORY
   - &CHESS-WIN-CARD-KNIGHT
     card_name: 'chess-win-knight'
     actions:
@@ -36,19 +39,13 @@ _card_definitions:
         required_bits: []
         data:
           chesspiece: KNIGHT
-      - action_class: AcquireWinToken
-        required_bits: [RED, GREEN, BLUE]
-        data:
-          once: true
+      - *PYRRHIC-VICTORY
   - &CHESS-WIN-CARD-SEPPUKU
     card_name: 'chess-win-seppuku'
     actions:
       - action_class: LoseHealthOrMSB
         required_bits: []
-      - action_class: AcquireWinToken
-        required_bits: [RED, GREEN, BLUE]
-        data:
-          once: true
+      - *PYRRHIC-VICTORY
   - &TEST-BORING
     card_name: 'test-boring'
     actions:
@@ -72,6 +69,7 @@ _card_definitions:
         data:
           toggle: [RED]
           clear: [NORTH, EAST, WEST]
+          set: [SOUTH]
   - &CHESS-SOUTH
     card_name: 'chess-south'
     bits: [SOUTH, EAST, WEST, RED, GREEN]
@@ -81,6 +79,7 @@ _card_definitions:
         data:
           toggle: [RED, GREEN]
           clear: [SOUTH, EAST, WEST]
+          set: [NORTH]
   - &CHESS-EAST
     card_name: 'chess-east'
     bits: [NORTH, SOUTH, EAST, RED, BLUE]
@@ -90,7 +89,7 @@ _card_definitions:
         data:
           toggle: [RED, BLUE]
           clear: [NORTH, SOUTH, EAST]
-
+          set: [WEST]
   - &CHESS-WEST
     card_name: 'chess-west'
     bits: [NORTH, SOUTH, WEST, BLUE, MSB]
@@ -100,6 +99,7 @@ _card_definitions:
         data:
           toggle: [BLUE, MSB]
           clear: [NORTH, SOUTH, WEST]
+          set: [EAST]
 
 cards:
   - *CHESS-WIN-CARD-BISHOP
index 7ae189e0b1c1db50f4ea1c576fbb1b9222fd4f18..f5b03a0e4399e3715c2d0cee3ffd5feeec5a15d4 100644 (file)
@@ -191,7 +191,7 @@ class GenericBits(LocationAction):
             parts.append("Lose {HEALTH}.")
         for template, bits in [
                 ('Set %s.', self.set_bits), ('Clear %s.', self.clear_bits),
-                ('Toggle %s', self.toggle_bits)]:
+                ('Toggle %s.', self.toggle_bits)]:
             if bits:
                 parts.append(template % (bit_glyphs(bits)))
         if self.once:
index 7d9dfafe1c44b30b160da7d1ba276858ebfa5504..52d1323bd176a5cd3ca7205086b7fe44455f450f 100644 (file)
@@ -56,6 +56,8 @@ class GameBoard(object):
         if wins_required is None:
             wins_required = defaults['wins_required']
 
+        assert wins_required + max_health == 8
+
         # Overriden by command line
         if options.initial_bits:
             initial_bits = options.initial_bits
index 73129490cf7903e4796543968f13248495c97353..498a979d8ecae6299cbf4d5d280b89bd6bfb2a46 100644 (file)
@@ -30,7 +30,7 @@ def load_deck(parser, deck):
     '''
     from naja.gamestate import GameState
     try:
-        state = GameState.new(deck=deck, max_health=4, wins_required=4)
+        state = GameState.new(deck=deck)
     except:
         if options.debug:
             raise
index cf6f3549fcf6d66eefd6375bde6b4992eb8259e7..4d341786d780968b739c96aa6589e39c266f1863 100644 (file)
@@ -62,7 +62,7 @@ class HowtoScene(Scene):
             box_width=740, view_port=(780, 540)))
 
     def handle_scene_event(self, ev):
-        from naja.scenes.menu import MenuScene
+        from naja.scenes.introduction import IntroductionScene
         if ev.type == pgl.KEYDOWN and ev.key in KEYS.QUIT:
-            SceneChangeEvent.post(MenuScene)
+            SceneChangeEvent.post(IntroductionScene)
             return
diff --git a/naja/scenes/introduction.py b/naja/scenes/introduction.py
new file mode 100644 (file)
index 0000000..95defaa
--- /dev/null
@@ -0,0 +1,68 @@
+"""
+Load and save scenes.
+"""
+
+import pygame.locals as pgl
+
+from naja.constants import KEYS
+from naja.events import SceneChangeEvent, LoadGameEvent
+from naja.gamestate import GameState
+from naja.scenes.scene import Scene
+from naja.scenes.howto import HowtoScene
+from naja.widgets.image_box import ImageBox
+from naja.widgets.selector import SelectorWidget
+from naja.widgets.text import TextWidget
+
+
+class IntroductionScene(Scene):
+    def __init__(self, state):
+        super(IntroductionScene, self).__init__(state)
+
+        background = ImageBox(
+            (0, 0), "screens/splash.png")
+        self.add(background)
+
+        selector = SelectorWidget()
+        self.add(selector)
+
+        y_offset, y_diff = 270, 40
+        x_offset = 400
+
+        y_offset += y_diff
+        title = TextWidget(
+            (x_offset, y_offset), 'Getting started', colour='white',
+            centre=True)
+        self.add(title)
+
+        y_offset += y_diff
+        howto = TextWidget(
+            (x_offset, y_offset), 'How to play', colour='white', centre=True)
+        howto.add_callback('click', self.howto_scene)
+        selector.add(howto)
+
+        y_offset += y_diff
+        intro = TextWidget(
+            (x_offset, y_offset), 'Introductory Level', colour='white',
+            centre=True)
+        intro.add_callback('click', self.introduction_level)
+        selector.add(intro)
+
+        y_offset += 2*y_diff
+        back = TextWidget(
+            (x_offset, y_offset), 'Press ESC to return to the main menu.',
+            colour='white', centre=True)
+        self.add(back)
+
+    def howto_scene(self, event):
+        SceneChangeEvent.post(HowtoScene)
+
+    def introduction_level(self, event):
+        from naja.scenes.game import GameScene
+        LoadGameEvent.post(state=GameState.new(deck='introduction'))
+        SceneChangeEvent.post(GameScene)
+
+    def handle_scene_event(self, ev):
+        if ev.type == pgl.KEYDOWN and ev.key in KEYS.QUIT:
+            from naja.scenes.menu import MenuScene
+            SceneChangeEvent.post(MenuScene)
+            return
index 84911c3eb1987b13e00e8758d258b102d63ad49f..2431e07cf2cc9f4be35c1e9419f59f17376c4405 100644 (file)
@@ -10,7 +10,7 @@ from naja.options import options
 from naja.scenes.scene import Scene
 from naja.scenes.credits import CreditsScene
 from naja.scenes.game import GameScene
-from naja.scenes.howto import HowtoScene
+from naja.scenes.introduction import IntroductionScene
 from naja.scenes.load_save import LoadGameScene, SaveGameScene
 from naja.scenes.new_game import NewGameScene
 from naja.scenes.puzzlelist import PuzzleListScene
@@ -33,6 +33,13 @@ class MenuScene(Scene):
         y_offset, y_diff = 270, 36
         x_offset = 400
 
+        y_offset += y_diff
+        intro = TextWidget(
+            (x_offset, y_offset), 'Introduction', colour=PALETTE.WHITE,
+            centre=True)
+        intro.add_callback('click', self.scene_callback(IntroductionScene))
+        selector.add(intro)
+
         y_offset += y_diff
         resume = TextWidget(
             (x_offset, y_offset), 'Resume Game', colour=PALETTE.WHITE,
@@ -41,6 +48,11 @@ class MenuScene(Scene):
         resume.set_selectable_callback(lambda: state is not None)
         selector.add(resume)
 
+        if state is None:
+            selector.position = 2
+        else:
+            selector.position = 1
+
         y_offset += y_diff
         new = TextWidget(
             (x_offset, y_offset), 'New Random Game', colour=PALETTE.WHITE,
@@ -70,13 +82,6 @@ class MenuScene(Scene):
         save.set_selectable_callback(lambda: state is not None)
         selector.add(save)
 
-        y_offset += y_diff
-        howto = TextWidget(
-            (x_offset, y_offset), 'How To Play', colour=PALETTE.WHITE,
-            centre=True)
-        howto.add_callback('click', self.scene_callback(HowtoScene))
-        selector.add(howto)
-
         y_offset += y_diff
         credits = TextWidget(
             (x_offset, y_offset), 'Credits', colour=PALETTE.WHITE,
index f9c51c1fa13b1a0dbddf5bc9126247ae1a7e1adb..4baab4bb66712696f302ff92b5e427e90cfe23a6 100644 (file)
@@ -42,17 +42,14 @@ class PuzzleListScene(Scene):
                (x_offset, y_offset), deck['description'], fontsize=32,
                colour='white', centre=True)
             callback = functools.partial(self.start_puzzle_game,
-                                         puzzle=puzzle, deck=deck)
+                                         puzzle=puzzle)
             puzzle_but.add_callback('click', callback)
             selector.add(puzzle_but)
 
-    def start_puzzle_game(self, event, puzzle=None, deck=None):
+    def start_puzzle_game(self, event, puzzle=None):
         from naja.scenes.game import GameScene
-        max_health = deck.get('max_health', 4)
-        wins_required = deck.get('wins_required', 4)
         level = 'puzzles/%s' % puzzle
-        state = GameState.new(deck=level, max_health=max_health,
-                              wins_required=wins_required)
+        state = GameState.new(deck=level)
         LoadGameEvent.post(state=state)
         SceneChangeEvent.post(GameScene)
 
index 34bbec746df01e9756ae092e31d1a9811922b96c..f0f8eefee7c86b1ac291c9f418d915cba17d2118 100644 (file)
@@ -7,7 +7,7 @@ import pygame.locals as pgl
 from naja.constants import (
     INFO_SIZE, ACT, KEYS, EXAMINE, PALETTE,
     ACTION_TEXT_OFFSET, INFO_LEFT_PADDING,
-    INFO_RIGHT_PADDING, BIT_SIZE)
+    INFO_RIGHT_PADDING, BIT_SIZE, BITS)
 from naja.events import finish_event
 from naja.resources import resources
 from naja.resources.mutators import EIGHT_BIT, blender
@@ -138,8 +138,10 @@ class InfoAreaWidget(Widget):
                               [(x_offset, y_offset), (right, y_offset),
                                (right, bottom), (x_offset, bottom)], 4)
 
-        if action.required_bits in BIT_MAP:
-            img_name = BIT_MAP[action.required_bits].replace(
+        required_keys = action.required_bits & frozenset([
+            BITS.RED, BITS.GREEN, BITS.BLUE])
+        if required_keys in BIT_MAP:
+            img_name = BIT_MAP[required_keys].replace(
                 '.png', '_small.png')
             img = resources.get_image(img_name,
                                       transforms=(EIGHT_BIT,))
@@ -148,6 +150,14 @@ class InfoAreaWidget(Widget):
         else:
             glyphs_x_offset = INFO_LEFT_PADDING
 
+        if BITS.MSB in action.required_bits:
+            msb = resources.get_image('board/msb_lock_decoration.png',
+                                      transforms=(EIGHT_BIT,))
+            msb_rect = msb.get_rect()
+            self.surface.blit(
+                msb, (glyphs_x_offset - msb_rect.width - 4, glyphs_y_offset)
+            )
+
         for glyph in action.get_glyphs():
             img = resources.get_image(
                 glyph, transforms=(EIGHT_BIT, blender(PALETTE.GREY)))
index 9e8fe8508056dae9bc61a4e93ccd547c772514d9..06a0f78a3baaceca3047c8e40af29f38b07b46ef 100644 (file)
@@ -94,10 +94,12 @@ class TileWidget(Widget):
         self.surface.blit(img, (TILE_SIZE[0] - 20, 0))
 
     def _prepare_lock(self, action, y_offset):
-        if action.required_bits not in BIT_MAP:
+        required_keys = action.required_bits & frozenset([
+            BITS.RED, BITS.GREEN, BITS.BLUE])
+        if required_keys not in BIT_MAP:
             return 4
 
-        img_name = BIT_MAP[action.required_bits]
+        img_name = BIT_MAP[required_keys]
 
         if self.board_pos != self.state.player.position:
             x_offset = 0
@@ -111,8 +113,18 @@ class TileWidget(Widget):
                 y_offset += LOCK_HEIGHT - SMALL_LOCK_HEIGHT - 2
 
         img = resources.get_image(img_name, transforms=(EIGHT_BIT,))
+        img_rect = img.get_rect()
         self.surface.blit(img, (x_offset, y_offset))
-        return x_offset + img.get_width() + 2
+
+        if BITS.MSB in action.required_bits:
+            msb = resources.get_image('board/msb_lock_decoration.png',
+                                      transforms=(EIGHT_BIT,))
+            msb_rect = msb.get_rect()
+            self.surface.blit(
+                msb, (x_offset + img_rect.width - msb_rect.width, y_offset)
+            )
+
+        return x_offset + img_rect.width + 2
 
     def _prepare_action(self, action, y_offset):
         x_offset = self._prepare_lock(action, y_offset)
index 8af88291989ee2216a757281456ee9daa07b2b32..ee0013929bd25f62db4fb1b3f68f41ed597d07aa 100755 (executable)
@@ -19,9 +19,7 @@ python setup.py sdist > /dev/null
 mkdir build
 (cd build; tar xzf ../dist/${BUILD_NAME}.tar.gz)
 
-echo "[${BUILD_FOLDER}]"
 cd ${BUILD_FOLDER}
-echo "$PWD"
 
 echo ""
 echo "=== Running python setup.py ==="
@@ -48,10 +46,17 @@ echo ""
 
 cd ${BASEDIR}
 
-pwd
 rm dist/${DMG_NAME} > /dev/null
 
-echo -e "For some reason the game starts without a foreground window. Click on the icon in the dock (or minimize and restore from the menu) to get it back.\n\nIf this doesn't work, please let me (<firxen@gmail.com>) know, especially if you have any ideas about how to fix it.\n\nYou should also be able to use the unix tarball available at the same place you got this dmg.\n\nThanks." > ${BUILD_FOLDER}/dist/IMPORTANT\ NOTE.txt
+cat > ${BUILD_FOLDER}/dist/IMPORTANT\ NOTE.txt <<EOF
+For some reason the game starts without a foreground window. Click on the icon in the dock (or minimize and restore from the menu) to get it back.
+
+If this doesn't work, please let me (<firxen@gmail.com>) know, especially if you have any ideas about how to fix it.
+
+You should also be able to use the unix tarball available at the same place you got this dmg.
+
+Thanks.
+EOF
 
 hdiutil create -volname ${GAME_NAME} -srcfolder ${BUILD_FOLDER}/dist/*.app/ -srcfolder ${BUILD_FOLDER}/dist/IMPORTANT\ NOTE.txt dist/${DMG_NAME}
 
diff --git a/sources/images/msb_lock_decoration.xcf b/sources/images/msb_lock_decoration.xcf
new file mode 100644 (file)
index 0000000..7ada9cd
Binary files /dev/null and b/sources/images/msb_lock_decoration.xcf differ