# This file is part of A Robot's Conundrum. # # A Robot's Conundrum is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # # A Robot's Conundrum is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # A Robot's Conundrum. If not, see . # # ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' # # level1.py # -------------------- # date created : Tue Aug 7 2012 # copyright : (C) 2012 Sakse Dalum # maintained by : Sakse Dalum """ The first level. """ import os import pygame import random import re import itertools import level import player import tile import block import boulder import lever import trigger import worldobject import level_bonus import fadeout import logic.teleportermap import logic.rollingstone import logic.colourboxes class Level1(level.Level): def __init__(self, game, graphics_dir, paused=False): level.Level.__init__(self, game, graphics_dir, size=(64 * 40, 48 * 40), paused=paused) self.solution = range(1, 6) random.shuffle(self.solution) self.solution = self.solution[:3] self.task_completions = [0, 0, 0] self.set_darkness(0) for i in range(self.size[0] / 64): for j in range(self.size[1] / 48): self.tiles.append( tile.Tile(self, i*64, j*48, self.imgs['ground1'])) start_pos = 7 * 64, 34 * 48 self.player.set_pos(*start_pos) self.player.set_init_pos() self.objects.append(block.Block(self, start_pos[0] - 2 * 64, start_pos[1] + 48, self.imgs['spacecraft'], z=16)) self.objects.append(block.InvisBlock(self, start_pos[0] - 2 * 64, start_pos[1])) self.objects.append(block.InvisBlock(self, start_pos[0] - 64, start_pos[1])) self.objects.append(block.InvisBlock(self, start_pos[0] - 64, start_pos[1] + 48)) self.objects.append(block.InvisBlock(self, start_pos[0], start_pos[1] + 48)) self.add_tile(start_pos[0] + 4 * 64, start_pos[1] - 3 * 48, 'indoor%d' % random.randint(1, 6), blocking=False) self.add_tile(start_pos[0] + 2 * 64, start_pos[1] - 7 * 48, 'indoor%d' % random.randint(1, 6), blocking=False) for i in range(self.size[0] / 64): if not i % 3: self.objects.append(block.Block(self, i * 64, 48 * 3, self.imgs['wall_outside'], width=3)) self.objects.append(block.InvisBlock(self, i * 64, self.size[1])) for i in range(self.size[1] / 48): self.objects.append(block.InvisBlock(self, - 64, i * 48)) self.objects.append(block.InvisBlock(self, self.size[0], i * 48)) ### Task 1: Teleporters task5_size = 8, 5 task5_pos = (64 * 12, 48 * 19) tmap = logic.teleportermap.generate_teleporter_map3(*task5_size) for x in range(task5_size[0]): for y in range(task5_size[1]): # if not (x, y) in tmap: # continue self.add_tile(64 * (x - 6) + task5_pos[0], 48 * (y - 4) + task5_pos[1], 'indoor%d' % random.randint(1, 6), blocking=False) for x, y in tmap: self.objects.append( trigger.Trigger(self, task5_pos[0] - 64 * (x - 1), task5_pos[1] - 48 * y, [lambda x: self.player.set_pos( task5_pos[0] + 2 * 64, (task5_pos[1] - random.randint(0, task5_size[1] - 1) * 48))], self.imgs['hole'], [self.player], visible=False, no_stop=True)) for i in range(task5_size[0] + 1): self.add_tile(task5_pos[0] - 64 * i, task5_pos[1] + 48, 'moat_horizontal') self.add_tile(task5_pos[0] - 64 * i, task5_pos[1] - task5_size[1] * 48, 'moat_horizontal') self.add_tile(task5_pos[0] + 64, task5_pos[1] + 48, 'moat_end_horizontal_flip') # self.add_tile(task5_pos[0] + 64, # task5_pos[1] - task5_size[1] * 48, # 'moat_end_horizontal_flip') self.add_tile(task5_pos[0] - 64 * (task5_size[0] + 1), task5_pos[1] + 48, 'moat_corner_south') self.add_tile(task5_pos[0] - 64 * (task5_size[0] + 1), task5_pos[1] - task5_size[1] * 48, 'moat_corner_north') for i in range(task5_size[0]): self.add_tile(task5_pos[0] - 64 * (task5_size[0] + 1), task5_pos[1] - 48 * i, 'moat_vertical') self.objects.append( lever.Lever(self, task5_pos[0] - 64 * (task5_size[0]), task5_pos[1] - (task5_size[1] / 2) * 48, [lambda *x: self.complete_task(1)])) ### Task 2: Rolling stone task2_size = 15, 10 task2_pos = (64 * 20, 48 * 20) playfield, nsteps, directions = ( logic.rollingstone.generate_simple_unsolved_solvable_extra( task2_size[0], task2_size[1], 5, 50)) n = 0 c = len(playfield) / len(directions) ns = [k * c for k in range(len(directions))] arrow_blocks = [] for i, j in playfield: if n in ns: arrow_blocks.append( block.ArrowBlock(self, task2_pos[0] + 64 * i, task2_pos[1] + 48 * j, directions[n / c].next_pos((0, 0)))) else: self.objects.append( block.Block(self, task2_pos[0] + 64 * i, task2_pos[1] + 48 * j, self.imgs['block1'], movable=True) if (random.randint(0, 2) or (i, j) == (4, 0)) else block.Block(self, task2_pos[0] + 64 * i, task2_pos[1] + 48 * j, self.imgs['block3'], movable=False)) n += 1 # arrow_blocks = [] # n = 0 # for i in directions: # arrow_blocks.append( # block.ArrowBlock(self, # task2_pos[0] + 64 * (task2_size[0] - n), # task2_pos[1] + 48 * (task2_size[1] + 2), # i.next_pos((0, 0)))) # n += 1 self.objects.extend(arrow_blocks) self.objects.append(lever.Lever(self, task2_pos[0] + 64 * 3, task2_pos[1] - 48 * 2, [arrow_block.activate for arrow_block in arrow_blocks], toggling=True, anim='lever_updown')) b = boulder.Boulder(self, task2_pos[0], task2_pos[1] - 48, direction=(0, 1)) self.objects.append(b) self.objects.append(lever.Lever(self, task2_pos[0] + 64 * 2, task2_pos[1] - 48 * 2, [b.activate], anim='lever_updown')) # Moat sides for i in range(-1, task2_size[1]): self.add_tile(task2_pos[0] - 64, task2_pos[1] + 48 * i, 'moat_vertical') for i in range(task2_size[1] - 2): self.add_tile(task2_pos[0] + 64 * task2_size[0], task2_pos[1] + 48 * i, 'moat_vertical') for i in range(6, task2_size[0]): self.add_tile(task2_pos[0] + 64 * i, task2_pos[1] - 48, 'moat_horizontal') for i in range(task2_size[0] - 2): self.add_tile(task2_pos[0] + 64 * i, task2_pos[1] + 48 * task2_size[1], 'moat_horizontal') # Corners self.add_tile(task2_pos[0] + 64 * task2_size[0], task2_pos[1] - 48, 'moat_corner_north_flip') self.add_tile(task2_pos[0] - 64, task2_pos[1] + 48 * task2_size[1], 'moat_corner_south') # Start self.add_tile(task2_pos[0] + 64 * 2, task2_pos[1] - 48, 'moat_horizontal') self.add_tile(task2_pos[0] + 64 * 3, task2_pos[1] - 48, 'moat_end_horizontal_flip') self.add_tile(task2_pos[0] + 64 * 5, task2_pos[1] - 48, 'moat_end_horizontal') self.add_tile(task2_pos[0] - 64, task2_pos[1] - 48 * 2, 'moat_corner_north') self.add_tile(task2_pos[0], task2_pos[1] - 48 * 2, 'moat_horizontal') self.add_tile(task2_pos[0] + 64, task2_pos[1] - 48 * 2, 'moat_corner_north_flip') self.add_tile(task2_pos[0] + 64, task2_pos[1] - 48, 'moat_corner_south') # End self.add_tile(task2_pos[0] + 64 * task2_size[0], task2_pos[1] + 48 * (task2_size[1] - 2), 'moat_corner_south') self.add_tile(task2_pos[0] + 64 * (task2_size[0] + 1), task2_pos[1] + 48 * (task2_size[1] - 2), 'moat_corner_north_flip') self.add_tile(task2_pos[0] + 64 * (task2_size[0] + 1), task2_pos[1] + 48 * (task2_size[1] - 1), 'moat_vertical') self.add_tile(task2_pos[0] + 64 * (task2_size[0] + 1), task2_pos[1] + 48 * (task2_size[1]), 'moat_corner_south_flip') self.add_tile(task2_pos[0] + 64 * (task2_size[0]), task2_pos[1] + 48 * (task2_size[1]), 'moat_end_horizontal') self.add_tile(task2_pos[0] + 64 * (task2_size[0] - 2), task2_pos[1] + 48 * (task2_size[1]), 'moat_end_horizontal_flip') self.objects.append( trigger.Trigger(self, task2_pos[0] + 64 * (task2_size[0]), task2_pos[1] + 48 * (task2_size[1] - 1), [self.complete_task, b.fall], self.imgs['hole'], [b], signal=[0, 2], toggling=True)) ### Task 3: Inverted bits task4_pos = (64 * 26, 48 * 18) b = boulder.Boulder(self, task4_pos[0] - 64, task4_pos[1] - 48 * 3, direction=(1, 0)) self.objects.append(b) self.objects.append(lever.Lever(self, task4_pos[0] - 64 * 3, task4_pos[1] - 48 * 3, [b.activate])) risingblocks = [block.RisingBlock(self, task4_pos[0] + 64 * i, task4_pos[1] - 48 * 3, is_up = False) for i in range(8)] for i in range(8): r = range(8) r.remove(i) self.objects.append(risingblocks[i]) n = random.choice(r) self.objects.append(lever.Lever(self, task4_pos[0] + 64 * i, task4_pos[1] - 48 * 2, [risingblocks[i].activate, risingblocks[n].activate], anim='lever_updown', toggling=True, signal=[0, 1])) for k in range(random.randint(0, 1)): self.objects[-1].use(self) self.objects[-1].set_init_pos() risingblocks[i].set_init_pos() risingblocks[n].set_init_pos() n = random.choice(r) self.objects.append(lever.Lever(self, task4_pos[0] + 64 * i, task4_pos[1], [risingblocks[i].activate, risingblocks[n].activate], anim='lever_updown', toggling=True, signal=[2, 3])) for k in range(random.randint(0, 1)): self.objects[-1].use(self) self.objects[-1].set_init_pos() risingblocks[i].set_init_pos() risingblocks[n].set_init_pos() self.objects.append(trigger.Trigger(self, task4_pos[0] + 64 * 8, task4_pos[1] - 48 * 3, [self.complete_task, b.fall], self.imgs['hole'], [b], signal=[0, 3], toggling=True)) # Moat self.add_tile(task4_pos[0] - 64 * 2, task4_pos[1] - 48 * 4, 'moat_corner_north') self.add_tile(task4_pos[0] - 64 * 2, task4_pos[1] - 48 * 3, 'moat_vertical') self.add_tile(task4_pos[0] - 64 * 2, task4_pos[1] - 48 * 2, 'moat_corner_south') self.add_tile(task4_pos[0] - 64, task4_pos[1] - 48 * 2, 'moat_end_horizontal_flip') for i in range(10): self.add_tile(task4_pos[0] + 64 * (i - 1), task4_pos[1] - 48 * 4, 'moat_horizontal') self.add_tile(task4_pos[0] + 64 * 9, task4_pos[1] - 48 * 4, 'moat_corner_north_flip') self.add_tile(task4_pos[0] + 64 * 9, task4_pos[1] - 48 * 3, 'moat_vertical') self.add_tile(task4_pos[0] + 64 * 9, task4_pos[1] - 48 * 2, 'moat_corner_south_flip') self.add_tile(task4_pos[0] + 64 * 8, task4_pos[1] - 48 * 2, 'moat_end_horizontal') ### Task 4: Colour blocks task3_pos = (64 * 15, 48 * 20) # Abstract "boxes", actually colour fields boxes = logic.colourboxes.generate_colour_boxes(1, 3) boxes += boxes boxes += [logic.colourboxes.generate_random_box(1) for _ in range(3)] random.shuffle(boxes) pos_colour = {} for box, (x, y) in zip(boxes, itertools.product(range(3), range(3))): # self.tiles.append(tile.Tile(self, x * 64 + task3_pos[0], # y * 48 + task3_pos[1], # self.imgs['ground1'])) pos_colour[(x, y)] = box self.add_tile(task3_pos[0] + 64 * x, task3_pos[1] + 48 * (y + 1), 'indoor%d' % random.randint(1, 6), blocking=False) action_blocks = [block.ActionBlock(self, 64 * i + task3_pos[0], task3_pos[1], movable=True) for i in range(3)] self.objects.extend(action_blocks) wells = [block.ColorWell(self, task3_pos[0] + 64, task3_pos[1] + 48 * 5)] self.objects.extend(wells) def update_wells(block): cur_boxes = [] for block in action_blocks: box = pos_colour.get(((block.x - task3_pos[0]) / 64, (block.y - task3_pos[1] - 48) / 48)) if box: cur_boxes.append(box) if not cur_boxes: well_colours = [(0, 0, 0)] * len(wells) else: well_colours = logic.colourboxes.get_colours(cur_boxes) for i in range(len(wells)): wells[i].set_colour(*well_colours[i]) for b in action_blocks: b.action = update_wells self.objects.append( lever.Lever(self, task3_pos[0] + 64, task3_pos[1] + 48 * 4, [lambda *x: (self.complete_task(4) if (len([w for w in wells if (w.well_colour == (255, 255, 255)) ]) == len(wells)) else lambda: None)])) ### Task 5: Rising columns task1_pos = (64 * 15, 48 * 13) ws = [] for i in range(5): w1 = Wheel(self, task1_pos[0] + 64 * i, task1_pos[1] - 48 * 2) w2 = Wheel(self, task1_pos[0] + 64 * i, task1_pos[1], immitate=w1) self.objects.extend([w1, w2]) self.objects.append(lever.Lever(self, task1_pos[0] + 64 * i, task1_pos[1] + 48 * 1, [w2.activate], anim='lever_updown')) ws.append(w2) self.add_tile(task1_pos[0] + 64 * i, task1_pos[1] - 48 * 3, 'moat_horizontal') self.objects.append( lever.Lever(self, task1_pos[0] + 64 * 5, task1_pos[1] + 48 * 2, [lambda *xs: self.complete_task(5) if len(ws) == len([w for w in ws if w.on]) else lambda: None])) self.add_tile(task1_pos[0] - 64, task1_pos[1] - 48 * 3, 'moat_corner_north') self.add_tile(task1_pos[0] + 64 * 5, task1_pos[1] - 48 * 3, 'moat_corner_north_flip') for i in range(3): self.add_tile(task1_pos[0] - 64, task1_pos[1] + 48 * (i - 2), 'moat_vertical') self.add_tile(task1_pos[0] + 64 * 5, task1_pos[1] + 48 * (i - 2), 'moat_vertical') self.add_tile(task1_pos[0] - 64, task1_pos[1] + 48, 'moat_corner_south_flip') self.add_tile(task1_pos[0] + 64 * 5, task1_pos[1] + 48, 'moat_corner_south') self.add_tile(task1_pos[0] - 64 * 2, task1_pos[1] + 48, 'moat_horizontal') self.add_tile(task1_pos[0] + 64 * 6, task1_pos[1] + 48, 'moat_end_horizontal_flip') ### Final: The door door_x = 22 for i in range(12): self.add_tile(door_x * 64, 48 * (5 + i), 'indoor%d' % random.randint(1, 6), blocking=False) for i in range(7): self.add_tile((door_x - i - 1) * 64, 48 * (5 + 11), 'indoor%d' % random.randint(1, 6), blocking=False) door = block.Door(self, 64 * door_x, 48 * 4) self.objects.append(door) self.objects.append( lever.Lever(self, 64 * (door_x + 3), 48 * 4, [lambda x: door.activate(x) if self.task_completions == self.solution else lambda *v: None], anim='lever_updown', toggling=False)) self._next_level_trigger = trigger.Trigger( self, 64 * door_x, 48 * 4, [lambda setting: self.goto_next_level()], self.imgs['hole'], [self.player], visible=False) self.objects.append(self._next_level_trigger) if random.randint(0, 1): for i in range(3): self.objects.append( CompletionBlock(self, 64 * (door_x - 5 + i), 48 * 4, self.solution[i], i + 1)) else: for i in range(3): self.objects.append( CompletionBlock(self, 64 * (door_x - 3 - i), 48 * 4, self.solution[i], i + 1)) ### Link to bonus level bonus = level_bonus.Level(self.game, self.graphics_dir) def goto_bonus(): self._update = self.update self.update = lambda *args: None def g(): self.update = self._update bonus.enter(self) fadeout.Fadeout(self.game, g) self.objects.append( lever.Lever( self, 64 * 39, 48 * 39, [lambda setting: goto_bonus()], toggling=False, anim='lever_updown')) # DRAW THE BACKGROUND self.draw_background() def add_tile(self, x, y, img, blocking=True): self.tiles.append(tile.Tile(self, x, y, self.imgs[img])) if blocking: self.objects.append(block.InvisBlock(self, x, y)) def complete_task(self, task): if task == 0: return self.task_completions.append(task) if len(self.task_completions) > 3: self.task_completions = self.task_completions[-3:] def restart(self): for obj in self.objects: obj.reset_pos() def goto_next_level(self): self.objects.remove(self._next_level_trigger) self._old_player_update = self.player.update self.player.update = lambda e, t, dt: self._old_player_update([], t, dt) fadeout.Fadeout(self.game, lambda: self.game.goto_level(2), duration=1500) class CompletionBlock(block.Block): def __init__(self, level, x, y, task, number): self.__dict__.update(locals()) block.Block.__init__(self, level, x, y, blocking=False) def update(self, e, t, dt): self.img = self.level.imgs['symbol%02d-0018' % (self.task + 12)] for t in range(1, 4): if (self.level.task_completions[-t:] == self.level.solution[:t] and t >= self.number): self.img = self.level.imgs['symbol%02d-0019' % (self.task + 12)] break class Wheel(block.Block): def __init__(self, level, x, y, immitate=None): self.__dict__.update(locals()) worldobject.WorldObject.__init__(self, level, x, y) self.frame = 0 self.anim_speed = 15 self.nsettings = 5 self.setting = random.randint(0, self.nsettings - 1) if self.immitate: self.on = self.setting == self.immitate.setting self.init_setting = self.setting self.anim = 'elevating_column' self.anim_direction = 1 def set_init_pos(self): worldobject.WorldObject.set_init_pos(self) self.init_setting = self.setting def reset_pos(self): worldobject.WorldObject.reset_pos(self) self.setting = self.init_setting def set_direction(self, setting): self.anim_direction = -1 if setting else 1 def activate(self, setting): self.setting = (self.setting + 1 * self.anim_direction) % self.nsettings self.on = self.setting == self.immitate.setting def update(self, e, t, dt): # Update the animation l = len(self.level.imgs[self.anim]) if not (int(self.frame) == self.setting * (l - 1) / (self.nsettings - 1)): self.frame = ((self.frame + self.anim_speed * dt) % l if (self.setting > 0 and self.anim_direction > 0) or (self.setting == self.nsettings - 1 and self.anim_direction < 0) else (self.frame - self.anim_speed * dt) % l) worldobject.WorldObject.update(self, e, t, dt) def draw(self, window): self.img = self.level.imgs[self.anim][int(self.frame)] if self.visible: window.blit(self.img, (self.x - 32 - self.level.camera_x, self.y - self.img.get_size()[1] + 24 - self.level.camera_y))