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)
158 self.gain_health = self.data.get('gain_health', False)
160 def sanity_check(self, location):
162 missing_bits.update(self.set_bits - set(location.bitwise_operand))
163 missing_bits.update(self.clear_bits - set(location.bitwise_operand))
164 missing_bits.update(self.toggle_bits - set(location.bitwise_operand))
167 "Location %s missing bits %r"
168 % (location.card_name, sorted(list(missing_bits))))
170 def perform_action(self, board, location):
171 bits = board.player.bits
172 bits.set_bits(self.set_bits)
173 bits.toggle_bits(self.toggle_bits)
174 bits.clear_bits(self.clear_bits)
176 sound.play_sound('yipee.ogg')
177 board.acquire_win_token()
179 sound.play_sound('awwww.ogg')
182 sound.play_sound('aha.ogg')
185 location.actions.remove(self)
187 def get_glyphs(self):
190 glyphs.append(ACTION_GLYPHS.WINTOKEN)
192 glyphs.append(ACTION_GLYPHS.DAMAGE)
194 glyphs.append(ACTION_GLYPHS.HEAL)
196 glyphs.append(ACTION_GLYPHS.SET_BITS)
198 glyphs.append(ACTION_GLYPHS.CLEAR_BITS)
200 glyphs.append(ACTION_GLYPHS.TOGGLE_BITS)
203 def get_text(self, location=None):
204 if 'message' in self.data:
205 return super(GenericBits, self).get_text()
208 parts.append("Gain {WINTOKEN}.")
210 parts.append("Lose {HEALTH}.")
212 parts.append("Gain {HEALTH}.")
213 for template, bits in [
214 ('Set %s.', self.set_bits), ('Clear %s.', self.clear_bits),
215 ('Toggle %s.', self.toggle_bits)]:
217 parts.append(template % (bit_glyphs(bits)))
219 parts.append('Usable once only.')
220 return " ".join(parts)
223 class ShiftBits(LocationAction):
224 TEXT = "Barrel-shift player bits %(shift_glyph)s %(shift)s."
225 GLYPHS = (ACTION_GLYPHS.SHIFT_LEFT,)
227 def perform_action(self, board, location):
228 shift = self.data['shift']
229 if self.data['direction'] == 'left':
230 board.player.bits.shift_bits_left(shift)
232 board.player.bits.shift_bits_right(shift)
235 class LoseHealthOrMSBAndSetBits(LocationAction):
236 TEXT = "Lose {HEALTH} or {MSB}, then set %(location_bits)s."
237 GLYPHS = (ACTION_GLYPHS.SET_BITS,)
238 MSB_GLYPH = ACTION_GLYPHS.DAMAGE
240 def perform_action(self, board, location):
241 if not self.check_and_clear_MSB(board.player):
242 sound.play_sound('awwww.ogg')
244 board.player.bits.set_bits(location.bitwise_operand)
247 class AcquireWinToken(LocationAction):
248 TEXT = "Gain {WINTOKEN}, then clear {RED,GREEN,BLUE}."
249 GLYPHS = (ACTION_GLYPHS.WINTOKEN,)
251 def perform_action(self, board, location):
252 sound.play_sound('yipee.ogg')
253 board.acquire_win_token()
254 board.player.bits.clear_bits(set([
255 BITS.RED, BITS.GREEN, BITS.BLUE,
258 class AcquireWinTokenAndLoseHealth(AcquireWinToken):
259 TEXT = "Gain {WINTOKEN}, lose {HEALTH}, then clear {RED,GREEN,BLUE}."
260 GLYPHS = (ACTION_GLYPHS.WINTOKEN, ACTION_GLYPHS.DAMAGE)
262 def perform_action(self, board, location):
263 self.take_damage(board)
264 super(AcquireWinTokenAndLoseHealth, self).perform_action(board,
267 class GainHealth(LocationAction):
268 TEXT = "Gain {HEALTH}."
269 GLYPHS = (ACTION_GLYPHS.HEAL,)
271 def perform_action(self, board, location):
272 sound.play_sound('aha.ogg')
276 class GainHealthAndClearBitsOrMSB(LocationAction):
277 TEXT = "Gain {HEALTH}, then clear %(location_bits)s or {MSB}."
278 GLYPHS = (ACTION_GLYPHS.HEAL,)
279 MSB_GLYPH = ACTION_GLYPHS.CLEAR_BITS
281 def perform_action(self, board, location):
282 sound.play_sound('aha.ogg')
284 if not self.check_and_clear_MSB(board.player):
285 board.player.bits.clear_bits(location.bitwise_operand)
288 class ShiftLocations(LocationAction):
289 TEXT = "Shift current %(rowcol)s %(direction)s."
290 GLYPHS = (ACTION_GLYPHS.CHANGE_BOARD,)
292 def perform_action(self, board, location):
293 sound.play_sound('grind.ogg')
294 board.shift_locations(self.data['direction'])
297 class RotateLocations(LocationAction):
298 TEXT = "Rotate adjacent tiles %(rot_direction_name)s."
299 GLYPHS = (ACTION_GLYPHS.CHANGE_BOARD,)
301 def perform_action(self, board, location):
302 sound.play_sound('grind.ogg')
303 board.rotate_locations(self.data['rot_direction'])
306 class AllowChessMove(LocationAction):
307 TEXT = "Move like a %(chesspiece_name)s for one turn."
308 GLYPHS = (ACTION_GLYPHS.MOVEMENT,)
310 def perform_action(self, board, location):
311 if self.data['chesspiece'] in CHESS_PIECES:
312 chesspiece = CHESS_PIECES[self.data['chesspiece']]
313 board.allow_chess_move(chesspiece)
316 class AllowChessMoveIfMSB(LocationAction):
318 "Clear {MSB} and move like a %(chesspiece_name)s for one turn if it "
320 MSB_GLYPH = ACTION_GLYPHS.MOVEMENT
322 def perform_action(self, board, location):
323 if self.data['chesspiece'] in CHESS_PIECES:
324 if self.check_and_clear_MSB(board.player):
325 chesspiece = CHESS_PIECES[self.data['chesspiece']]
326 board.allow_chess_move(chesspiece)
329 class GainMSB(LocationAction):
331 GLYPHS = (ACTION_GLYPHS.MSB,)
333 def perform_action(self, board, location):
334 board.player.bits.set_bit(BITS.MSB)