1 from naja.constants import ACTION_GLYPHS, BITS, CHESS_PIECES
2 from naja.sound import sound
3 from naja.utils import bit_glyphs, move_glyph, parse_bits
6 class LocationAction(object):
8 An action that may be performed on a location.
15 def __init__(self, required_bits, **data):
16 self.required_bits = required_bits
19 def sanity_check(self, location):
25 def get_msb_glyph(self):
28 def get_text(self, location=None):
29 substitutions = self.data.copy()
31 if 'shift' in self.data:
32 substitutions['shift'] = self.data['shift']
33 substitutions['shift_glyph'] = ('{SHIFT_%s}'
34 % self.data['direction'].upper())
35 elif 'direction' in self.data:
36 substitutions['rowcol'] = {
41 }[self.data['direction']]
42 substitutions['direction'] = '{%s}' % (substitutions['direction'],)
44 if 'chesspiece' in self.data:
45 substitutions['chesspiece_name'] = move_glyph(
46 self.data['chesspiece'])
48 if 'rot_direction' in self.data:
49 substitutions['rot_direction_name'] = '{%s}' % (
50 substitutions['rot_direction'],)
53 substitutions['location_bits'] = 'bits specified by this tile'
55 substitutions['location_bits'] = bit_glyphs(
56 location.bitwise_operand)
59 if self.data.get('message', None) is not None:
60 text = self.data['message']
62 return text % substitutions
64 def check_available(self, player):
65 return player.bits.check_bits(self.required_bits)
67 def perform_action(self, board, location):
68 raise NotImplementedError(
69 "%s does not implement perform_action()." % (type(self).__name__,))
71 def check_and_clear_MSB(self, player):
72 if player.bits.check_bit(BITS.MSB):
73 player.bits.clear_bit(BITS.MSB)
79 return {'required_bits': list(self.required_bits),
81 'action_class': self.__class__.__name__}
83 def take_damage(self, board):
84 sound.play_sound('awwww.ogg')
88 class DoNothing(LocationAction):
90 GLYPHS = (ACTION_GLYPHS.NOTHING,)
92 def perform_action(self, board, location):
96 class LoseHealthOrMSB(LocationAction):
97 TEXT = "Lose {HEALTH} or {MSB}."
98 MSB_GLYPH = ACTION_GLYPHS.DAMAGE
100 def perform_action(self, board, location):
101 if not self.check_and_clear_MSB(board.player):
102 self.take_damage(board)
105 class SetBits(LocationAction):
106 TEXT = "Set %(location_bits)s."
107 GLYPHS = (ACTION_GLYPHS.SET_BITS,)
109 def perform_action(self, board, location):
110 board.player.bits.set_bits(location.bitwise_operand)
113 class ClearBits(LocationAction):
114 TEXT = "Clear %(location_bits)s."
115 GLYPHS = (ACTION_GLYPHS.CLEAR_BITS,)
117 def perform_action(self, board, location):
118 board.player.bits.clear_bits(location.bitwise_operand)
121 class ClearBitsAndHealth(LocationAction):
122 TEXT = "Clear %(location_bits)s and lose {HEALTH}."
123 GLYPHS = (ACTION_GLYPHS.CLEAR_BITS, ACTION_GLYPHS.DAMAGE)
125 def perform_action(self, board, location):
126 board.player.bits.clear_bits(location.bitwise_operand)
127 self.take_damage(board)
130 class ToggleBits(LocationAction):
131 TEXT = "Toggle %(location_bits)s."
132 GLYPHS = (ACTION_GLYPHS.TOGGLE_BITS,)
134 def perform_action(self, board, location):
135 board.player.bits.toggle_bits(location.bitwise_operand)
138 class ToggleBitsAndHarm(LocationAction):
139 TEXT = "Toggle %(location_bits)s and lose {HEALTH}."
140 GLYPHS = (ACTION_GLYPHS.TOGGLE_BITS, ACTION_GLYPHS.DAMAGE)
142 def perform_action(self, board, location):
143 board.player.bits.toggle_bits(location.bitwise_operand)
144 self.take_damage(board)
147 class GenericBits(LocationAction):
148 GLYPHS = (ACTION_GLYPHS.SET_BITS, ACTION_GLYPHS.CLEAR_BITS)
150 def __init__(self, *args, **kw):
151 super(GenericBits, self).__init__(*args, **kw)
152 self.set_bits = parse_bits(self.data.get('set', []))
153 self.clear_bits = parse_bits(self.data.get('clear', []))
154 self.toggle_bits = parse_bits(self.data.get('toggle', []))
155 self.once = self.data.get('once', False)
156 self.acquire_win = self.data.get('acquire_win', False)
157 self.lose_health = self.data.get('lose_health', False)
159 def sanity_check(self, location):
161 missing_bits.update(self.set_bits - set(location.bitwise_operand))
162 missing_bits.update(self.clear_bits - set(location.bitwise_operand))
163 missing_bits.update(self.toggle_bits - set(location.bitwise_operand))
166 "Location %s missing bits %r"
167 % (location.card_name, sorted(list(missing_bits))))
169 def perform_action(self, board, location):
170 bits = board.player.bits
171 bits.set_bits(self.set_bits)
172 bits.toggle_bits(self.toggle_bits)
173 bits.clear_bits(self.clear_bits)
175 sound.play_sound('yipee.ogg')
176 board.acquire_win_token()
178 sound.play_sound('awwww.ogg')
181 location.actions.remove(self)
183 def get_glyphs(self):
186 glyphs.append(ACTION_GLYPHS.WINTOKEN)
188 glyphs.append(ACTION_GLYPHS.DAMAGE)
190 glyphs.append(ACTION_GLYPHS.SET_BITS)
192 glyphs.append(ACTION_GLYPHS.CLEAR_BITS)
194 glyphs.append(ACTION_GLYPHS.TOGGLE_BITS)
197 def get_text(self, location=None):
198 if 'message' in self.data:
199 return super(GenericBits, self).get_text()
202 parts.append("Gain a {WINTOKEN}.")
204 parts.append("Lose {HEALTH}.")
205 for template, bits in [
206 ('Set %s.', self.set_bits), ('Clear %s.', self.clear_bits),
207 ('Toggle %s.', self.toggle_bits)]:
209 parts.append(template % (bit_glyphs(bits)))
211 parts.append('Usable once only.')
212 return " ".join(parts)
215 class ShiftBits(LocationAction):
216 TEXT = "Barrel-shift player bits %(shift_glyph)s %(shift)s."
217 GLYPHS = (ACTION_GLYPHS.SHIFT_LEFT,)
219 def perform_action(self, board, location):
220 shift = self.data['shift']
221 if self.data['direction'] == 'left':
222 board.player.bits.shift_bits_left(shift)
224 board.player.bits.shift_bits_right(shift)
227 class LoseHealthOrMSBAndSetBits(LocationAction):
228 TEXT = "Lose {HEALTH} or {MSB}, then set %(location_bits)s."
229 GLYPHS = (ACTION_GLYPHS.SET_BITS,)
230 MSB_GLYPH = ACTION_GLYPHS.DAMAGE
232 def perform_action(self, board, location):
233 if not self.check_and_clear_MSB(board.player):
234 sound.play_sound('awwww.ogg')
236 board.player.bits.set_bits(location.bitwise_operand)
239 class AcquireWinToken(LocationAction):
240 TEXT = "Gain {WINTOKEN}, then clear {RED,GREEN,BLUE}."
241 GLYPHS = (ACTION_GLYPHS.WINTOKEN,)
243 def perform_action(self, board, location):
244 sound.play_sound('yipee.ogg')
245 board.acquire_win_token()
246 board.player.bits.clear_bits(set([
247 BITS.RED, BITS.GREEN, BITS.BLUE,
250 class AcquireWinTokenAndLoseHealth(AcquireWinToken):
251 TEXT = "Gain {WINTOKEN}, lose {HEALTH}, then clear {RED,GREEN,BLUE}."
252 GLYPHS = (ACTION_GLYPHS.WINTOKEN, ACTION_GLYPHS.DAMAGE)
254 def perform_action(self, board, location):
255 self.take_damage(board)
256 super(AcquireWinTokenAndLoseHealth, self).perform_action(board,
259 class GainHealth(LocationAction):
260 TEXT = "Gain {HEALTH}."
261 GLYPHS = (ACTION_GLYPHS.HEAL,)
263 def perform_action(self, board, location):
264 sound.play_sound('aha.ogg')
268 class GainHealthAndClearBitsOrMSB(LocationAction):
269 TEXT = "Gain {HEALTH}, then clear %(location_bits)s or {MSB}."
270 GLYPHS = (ACTION_GLYPHS.HEAL,)
271 MSB_GLYPH = ACTION_GLYPHS.CLEAR_BITS
273 def perform_action(self, board, location):
274 sound.play_sound('aha.ogg')
276 if not self.check_and_clear_MSB(board.player):
277 board.player.bits.clear_bits(location.bitwise_operand)
280 class ShiftLocations(LocationAction):
281 TEXT = "Shift current %(rowcol)s %(direction)s."
282 GLYPHS = (ACTION_GLYPHS.CHANGE_BOARD,)
284 def perform_action(self, board, location):
285 sound.play_sound('grind.ogg')
286 board.shift_locations(self.data['direction'])
289 class RotateLocations(LocationAction):
290 TEXT = "Rotate adjacent tiles %(rot_direction_name)s."
291 GLYPHS = (ACTION_GLYPHS.CHANGE_BOARD,)
293 def perform_action(self, board, location):
294 sound.play_sound('grind.ogg')
295 board.rotate_locations(self.data['rot_direction'])
298 class AllowChessMove(LocationAction):
299 TEXT = "Move like a %(chesspiece_name)s for one turn."
300 GLYPHS = (ACTION_GLYPHS.MOVEMENT,)
302 def perform_action(self, board, location):
303 if self.data['chesspiece'] in CHESS_PIECES:
304 chesspiece = CHESS_PIECES[self.data['chesspiece']]
305 board.allow_chess_move(chesspiece)
308 class AllowChessMoveIfMSB(LocationAction):
310 "Clear {MSB} and move like a %(chesspiece_name)s for one turn if it "
312 MSB_GLYPH = ACTION_GLYPHS.MOVEMENT
314 def perform_action(self, board, location):
315 if self.data['chesspiece'] in CHESS_PIECES:
316 if self.check_and_clear_MSB(board.player):
317 chesspiece = CHESS_PIECES[self.data['chesspiece']]
318 board.allow_chess_move(chesspiece)
321 class GainMSB(LocationAction):
323 GLYPHS = (ACTION_GLYPHS.MSB,)
325 def perform_action(self, board, location):
326 board.player.bits.set_bit(BITS.MSB)