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'
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'
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'
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]
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]
--- /dev/null
+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
class LoseHealthOrMSB(LocationAction):
- TEXT = "Lose {HEALTH} or {MSB}."
+ TEXT = "Lose {MSB} or {HEALTH}."
MSB_GLYPH = ACTION_GLYPHS.DAMAGE
def perform_action(self, board, location):
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
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}."
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
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):
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):
PUZZLES = (
'chess',
'marathon',
+ 'belts',
)
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
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
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
def __init__(self, state):
super(CreditsScene, self).__init__(state)
+ self.flashlight = Flashlight(int(1.5 * FPS))
background = ImageBox(
(0, 0), "screens/splash.png")
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',
'',
'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/',
'',
]),
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:
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",
def _check_markup(self, word):
suffix = ''
- if word[-1] in '.,':
+ if word[-1] in '.,:':
suffix = word[-1]
word = word[:-1]