X-Git-Url: https://git.ctpug.org.za/?a=blobdiff_plain;f=naja%2Fwidgets%2Finfo_area.py;h=b4c6e2b2a66208b522d638928eddc300f9118399;hb=3273de8b8e17a1d13e46ba920241eae1602d54bb;hp=0bb02f41aca8551d453680fdf68e363066ee8ad6;hpb=963216cd4493996f5859347fe50249fb5865d6e5;p=naja.git diff --git a/naja/widgets/info_area.py b/naja/widgets/info_area.py index 0bb02f4..b4c6e2b 100644 --- a/naja/widgets/info_area.py +++ b/naja/widgets/info_area.py @@ -4,26 +4,34 @@ Widget for the game board information area. import pygame import pygame.locals as pgl -from naja.constants import INFO_SIZE, EIGHT_BIT_SCALE, MOVE, ACT, KEYS -from naja.events import PlayerMoved, finish_event +from naja.constants import ( + INFO_SIZE, ACT, KEYS, EXAMINE, PALETTE, + ACTION_TEXT_OFFSET, INFO_LEFT_PADDING, + INFO_RIGHT_PADDING, BIT_SIZE, BITS) +from naja.events import finish_event from naja.resources import resources -from naja.resources.mutators import EIGHT_BIT +from naja.resources.mutators import EIGHT_BIT, blender +from naja.sound import sound +from naja.utils import bit_glyphs, Flashlight from naja.widgets.base import Widget from naja.widgets.tile import BIT_MAP -from naja.widgets.text import TextBoxWidget, TextWidget +from naja.widgets.text import TextBoxWidget +from naja import constants HINTS = { - MOVE: "Move using the arrow keys.\nPress SPACE to stay in place", - ACT: "Choose an action using the Up/Down keys.\n" - "Press Return to execute the action.", - } + 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." TITLES = { - MOVE: "Move the Robot", - ACT: "Choose an Action", - } + ACT: "Choose an Action", + EXAMINE: "Select a Tile", +} class InfoAreaWidget(Widget): @@ -33,73 +41,199 @@ class InfoAreaWidget(Widget): def __init__(self, pos, state): super(InfoAreaWidget, self).__init__(pos, INFO_SIZE) self.state = state + self.chosen = None + self.card_position = state.player.position self.set_position(state.player.position) + self.flash_light = Flashlight(constants.FPS // 2) def prepare(self): + if self.state.gameboard.player_mode == ACT: + self.set_position(self.state.player.position) self.surface = pygame.surface.Surface(INFO_SIZE) self.surface.fill((0, 0, 0)) - # Extract actions and such from the card - title = TextWidget((0, 0), TITLES[self.state.gameboard.player_mode], - colour=(255, 255, 255)) - title.render(self.surface) - y_offset = title.surface.get_rect().height + 8 + + box_width = INFO_SIZE[0] - INFO_LEFT_PADDING - INFO_RIGHT_PADDING + y_offset = 0 + pos = lambda: (INFO_LEFT_PADDING, y_offset) + + # Bits + y_offset += 12 + bits_text = ''.join('1' if bit in self.card.bitwise_operand else '0' + for bit in reversed(range(8))) + if self.card.bitwise_operand: + bits_text = '%s %s' % ( + bits_text, bit_glyphs(self.card.bitwise_operand)) + card_bits = TextBoxWidget( + (0, 0), bits_text, padding=4, centre=True, + colour=PALETTE.WHITE, border=2, + bg_colour=PALETTE.BLACK, border_colour=PALETTE.BLUE, + box_width=box_width) + card_bits.prepare() + self.surface.blit(card_bits.surface, pos()) + y_offset += card_bits.surface.get_rect().height + 12 + + # Actions for choice, action in enumerate(self.card.actions): - if action.required_bits: - img_name = BIT_MAP[action.required_bits].replace('.png', '_small.png') - img = resources.get_image(img_name, - transforms=(EIGHT_BIT,)) - self.surface.blit(img, (0, y_offset)) - y_offset += 8 - text = TextBoxWidget((12, y_offset), action.TEXT, - box_width=(INFO_SIZE[0] - 12) // EIGHT_BIT_SCALE, - fontsize=28) - text.render(self.surface) - if choice == self.chosen: - colour = (255, 255, 0, 128) - bottom = y_offset + text.surface.get_rect().height - right = text.surface.get_rect().width + 12 - pygame.draw.lines(self.surface, colour, True, - [(12, y_offset), (right, y_offset), - (right, bottom), (12, bottom)], 4) - y_offset += text.surface.get_rect().height + 16 - # We cheat horribly for layout reasons - hint = TextBoxWidget((0, 0), HINTS[self.state.gameboard.player_mode], - box_width = INFO_SIZE[0] // EIGHT_BIT_SCALE) + y_offset = self.prepare_action(choice, action, y_offset, box_width) + + # Hint text + hint_text = HINTS[self.state.gameboard.player_mode] + if self.state.gameboard.player_mode == EXAMINE: + if self.card_position in self.state.player.legal_moves(): + hint_text += HINT_LEGAL_MOVE + + hint = TextBoxWidget( + (0, 0), hint_text, padding=4, + colour=PALETTE.WHITE, border=2, + bg_colour=PALETTE.BLACK, border_colour=PALETTE.BLUE, + box_width=box_width) hint.prepare() - y_offset = INFO_SIZE[1] - hint.surface.get_rect().height - self.surface.blit(hint.surface, (0, y_offset)) + y_offset = (INFO_SIZE[1] - hint.surface.get_rect().height + - BIT_SIZE[1] - 2) + self.surface.blit(hint.surface, pos()) + + # Game mode (puzzle, easy, standard, hard, etc ...) + game_mode = TextBoxWidget( + (0, 0), self.mode_hint(), padding=4, centre=True, + colour=PALETTE.WHITE, border=2, + bg_colour=PALETTE.BLACK, border_colour=PALETTE.BLUE, + box_width=box_width) + game_mode.prepare() + + y_offset = (INFO_SIZE[1] - BIT_SIZE[1] + 12) + self.surface.blit(game_mode.surface, pos()) + + def mode_hint(self): + gameboard = self.state.gameboard + if gameboard.puzzle: + return "PUZZLE" + return { + 0: "DEATH", 1: "NINTENDO HARD", + 2: "VERY HARD", 3: "HARD", + 4: "STANDARD", 5: "EASY", + }.get(gameboard.max_health, "UNKNOWN") + + def prepare_action(self, choice, action, y_offset, box_width): + x_offset = INFO_LEFT_PADDING + glyphs_x_offset = 2 + glyphs_y_offset = y_offset + y_offset += ACTION_TEXT_OFFSET + action_viable = action.check_available(self.state.player) + text_colour = PALETTE.BLACK if action_viable else PALETTE.GREY + + text = TextBoxWidget( + (x_offset, y_offset), action.get_text(self.card), + box_width=box_width, + fontsize=28, colour=text_colour, + border=2, border_colour=PALETTE.GREY) + text.render(self.surface) + + border_colour = None + if choice == self.chosen: + if self.flash_light.on: + border_colour = (PALETTE.GREEN if action_viable else + PALETTE.ORANGE) + else: + border_colour = (PALETTE.DARK_GREEN if action_viable else + PALETTE.DARK_RED) + + if border_colour: + bottom = y_offset + text.surface.get_rect().height + right = text.surface.get_rect().width + x_offset + pygame.draw.lines(self.surface, border_colour, True, + [(x_offset, y_offset), (right, y_offset), + (right, bottom), (x_offset, bottom)], 4) + + 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,)) + self.surface.blit(img, (glyphs_x_offset, glyphs_y_offset)) + glyphs_x_offset += img.get_width() + 4 + 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))) + self.surface.blit(img, (glyphs_x_offset, glyphs_y_offset - 4)) + glyphs_x_offset += img.get_width() + if action.get_msb_glyph() is not None: + img = resources.get_image( + action.get_msb_glyph(), + transforms=(EIGHT_BIT, blender(PALETTE.LIGHT_VIOLET))) + self.surface.blit(img, (glyphs_x_offset, glyphs_y_offset - 4)) + + return y_offset + text.surface.get_rect().height + 16 def set_position(self, position): - self.card = self.state.board_locations[position] - self.chosen = 0 + self.card_position = position + self.card = self.state.board_locations[self.card_position] + if self.state.gameboard.player_mode == ACT: + if self.chosen is None: + self.chosen = 0 + else: + self.chosen = None def draw(self, surface): + if self.flash_light.tick(): + self.prepare() surface.blit(self.surface, self.pos) - def handle_event(self, ev): - if PlayerMoved.matches(ev): - self.set_position(self.state.player.position) - return False + def next_action(self, viable_only=False, step=1): + num_actions = len(self.card.actions) + if num_actions == 0: + return + player = self.state.player + chosen = self.chosen + for i in range(num_actions - 1): + # loop through each action at most once. + chosen = (chosen + step) % num_actions + action = self.card.actions[chosen] + if not viable_only or action.check_available(player): + sound.play_sound('zoop.ogg', volume=0.05) + self.chosen = chosen + + def prev_action(self, viable_only=False): + return self.next_action(viable_only=viable_only, step=-1) - if self.state.gameboard.player_mode == MOVE: + def try_perform_action(self): + player = self.state.player + action = self.card.actions[self.chosen] + if not action.check_available(player): + sound.play_sound('error.ogg') + else: + sound.play_sound('chirp.ogg', volume=0.5) + action.perform_action(self.state.gameboard, self.card) + self.state.gameboard.card_used(player.position) + self.state.gameboard.change_mode(EXAMINE) + self.set_position(player.position) + + def handle_event(self, ev): + if self.state.gameboard.player_mode == EXAMINE: return super(InfoAreaWidget, self).handle_event(ev) if ev.type == pgl.KEYDOWN: if ev.key in KEYS.SELECT: - player = self.state.gameboard.player - action = self.card.actions[self.chosen] - if not action.check_available(player): - print "BEEP!" - else: - action.perform_action(self.state.gameboard, self.card) - self.state.gameboard.replace_card(player.position) - self.state.gameboard.change_mode() + self.try_perform_action() return finish_event() if ev.key in KEYS.UP: - if self.chosen > 0: - self.chosen -= 1 + self.next_action() return finish_event() if ev.key in KEYS.DOWN: - if self.chosen + 1 < len(self.card.actions): - self.chosen += 1 + self.prev_action() + return finish_event() + if ev.key in KEYS.SWITCH: + self.next_action(viable_only=True) return finish_event() return super(InfoAreaWidget, self).handle_event(ev)