# 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 . # # ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' # # level3.py # -------------------- # date created : Fri Aug 10 2012 # copyright : (C) 2012 Niels G. W. Serup # maintained by : Niels G. W. Serup """ The third level. """ from __future__ import print_function import os import pygame import random import re import itertools import level import player import tile import block import lever from mirror import Mirror from laser import Laser import misc import worldobject import fadeout import logic.lasermirror as lm from logic.direction import * class Level3(level.Level): def __init__(self, game, graphics_dir, paused=False): level.Level.__init__(self, game, graphics_dir, size=(64 * 17, 48 * 21), paused=paused) self.dimensions = 17, 17 not_ok_pos = set(itertools.product(range(7, 10), range(1, 4))) for i, j in filter(lambda p: p not in not_ok_pos, self._positions()): self.tiles.append( tile.Tile(self, i * 64, (j + 4) * 48, self.imgs[ ('indoor%d' % random.randint(1, 6)) if random.randrange(6) != 0 else 'ground1'])) self.draw_background() self.playfield = lm.generate_simple_playfield(16) self.target_blocks = [] for (x, y), t in self.playfield.items(): x1, y1 = 64 * x, 48 * (y + 4) if isinstance(t, lm.Source): self.objects.append( block.LaserSource(self, x1, y1, t.direction)) continue def mir(b, x1, y1): def f(x, y): def g(setting): self.playfield[(x, y)] = lm.MirrorLeft \ if self.playfield[(x, y)] is lm.MirrorRight \ else lm.MirrorRight self.generate_lasers() return g return Mirror(self, x, y, b, links=[f(x, y)]) def targ(): b = block.LaserTarget(self, x, y) self.target_blocks.append(b) return b self.objects.append({ lm.MirrorLeft: lambda: mir(True, x1, y1), lm.MirrorRight: lambda: mir(False, x1, y1), lm.Lever: lambda: lever.Lever( self, x1, y1, [lambda setting: self.generate_lasers], toggling=True, anim='lever_leftright' if x in (0, 15) else 'lever_updown'), lm.Target: targ, lm.Blocker: lambda: block.Block(self, x1, y1, self.imgs['block1'], movable=False) }[t]()) mirrors = list(filter(lambda obj: isinstance(obj, Mirror), self.objects)) levers = list(filter(lambda obj: isinstance(obj, lever.Lever), self.objects)) random.shuffle(levers) for l in levers: m = min(mirrors, key=lambda m: misc.manhattan_dist( (m.x, m.y), (l.x, l.y))) mirrors.remove(m) l.links.insert(0, (lambda m: lambda setting: m.rotate())(m)) top = self.dimensions[0] for i in range(top): if i % 3 == 0: self.objects.append(block.Block( self, i * 64, 48 * 3, self.imgs['wall'], width=2 if i == top - 2 else 3, blit_area=(0, 0, 160, 192) if i == top - 2 else None)) self.objects.append(block.InvisBlock(self, i * 64, self.size[1])) for i in range(self.dimensions[1]): self.objects.append(block.InvisBlock(self, - 64, (i + 4) * 48)) self.objects.append(block.InvisBlock(self, self.size[0], (i + 4) * 48)) self.generate_lasers() self.bottom_objects.append(worldobject.WithBackground( self, self.imgs['elevator_top'], 64 * 7, 48 * 5)) self.elevator = worldobject.WithBackground( self, self.imgs['elevator'], 64 * 7, 48 * 6, 48 * 3, (0, 0, 256, 192 - 24 - 48 * 2)) self.bottom_objects.append(self.elevator) self.bottom_objects.append(worldobject.WithBackground( self, self.imgs['elevator_bottom'], 64 * 7, 48 * 7)) self.player.set_pos(64 * 8, 48 * 5) self.player.set_init_pos() self._old_player_update = self.player.update self.player.update = lambda e, t, dt: self._old_player_update([], t, dt) self._start_time = pygame.time.get_ticks() def on_completion(self): # End game here. fadeout.Fadeout(self.game, lambda: self.game.goto_level(4), duration=5000) def restart(self): for obj in self.objects: obj.reset_pos() def generate_lasers(self): lasers = lm.generate_lasers(self.playfield) self.lasers_orig = list(itertools.chain(*lasers)) self.lasers = [] for laser in lasers: laser = iter(laser) self.lasers.append(Laser(self, next(laser), first_laser=True)) self.lasers.extend(Laser(self, line) for line in laser) for b in self.target_blocks: b.new_playfield_update() if all(b.glows for b in self.target_blocks): self.on_completion() def update(self, e, t, dt): self.update2(e, t, dt) start_offset = (t - self._start_time) / 25 if start_offset >= 96: start_offset = 96 self.player.y = 48 * 7 - start_offset self.elevator.z_px = 48 * 3 - start_offset self.elevator.blit_area = (0, 0, 256, 192 - 24 - 48 * 2 + start_offset) if start_offset == 96: self.update = self.update2 self.player.update = self._old_player_update def update2(self, e, t, dt): level.Level.update(self, e, t, dt) for laser in self.lasers: laser.update(e, t, dt) def draw(self, window): self._blit_background(window) for obj in self._sorted_objs(self.bottom_objects): try: obj.draw(window) except IndexError: print("Skipping frames ...") objs = self._sorted_objs(self.objects + self.lasers) objs = self._after_sort(itertools.groupby( objs, lambda obj: obj.y + obj.z)) for obj in objs: obj.draw(window) self.darkness.draw(window) def _after_sort(self, objss): n_objs = [] for c, objs in objss: n_objs.extend(self._after_sort_line(list(objs))) return n_objs def _after_sort_line(self, objs): is_special = lambda obj: type(obj) in \ (Mirror, Laser, block.LaserSource, block.LaserTarget) specials, nonspecials = (filter(is_special, objs), filter(lambda obj: not is_special(obj), objs)) return nonspecials + self._sort_line_specials(specials) def _sort_line_specials(self, objs): mirrors = filter(lambda obj: isinstance(obj, Mirror), objs) lasers = filter(lambda obj: isinstance(obj, Laser), objs) sources = filter(lambda obj: isinstance(obj, block.LaserSource), objs) targets = filter(lambda obj: isinstance(obj, block.LaserTarget), objs) lasers_back = set(lasers) sep_ords = [] for obj in itertools.chain(mirrors, targets): before, after = [], [] for laser in _hit_lasers(obj, lasers): lasers_back.discard(laser) if _obj_is_behind_laser(obj, laser): after.append(laser) else: before.append(laser) sep_ords.append(before + [obj] + after) points = set((obj.x0, obj.y0) for obj in itertools.chain(mirrors, targets)) for laser in filter(lambda laser: (laser.x0, laser.y0) in points and (laser.x1, laser.y1) in points, lasers): # print(laser) xs, ys = filter(lambda sep_ord: laser in sep_ord, sep_ords) sep_ords.remove(xs) sep_ords.remove(ys) xs, ys, nobjs = iter(xs), iter(ys), [] while True: x = next(xs) if x is laser: break nobjs.append(x) while True: x = next(ys) if x is laser: break nobjs.append(x) nobjs.append(laser) nobjs.extend(xs) nobjs.extend(ys) sep_ords.append(nobjs) objs = list(itertools.chain(*sep_ords)) + list(lasers_back) \ + sources return objs def _hit_lasers(obj, lasers): p = (obj.x0, obj.y0) return filter(lambda laser: (laser.x0, laser.y0) == p or (laser.x1, laser.y1) == p, lasers) def _obj_is_behind_laser(obj, laser): return (_mirror_is_behind_laser if isinstance(obj, Mirror) else _target_is_behind_laser)(obj, laser) def _mirror_is_behind_laser(mirror, laser): return \ laser.y0 == laser.y1 \ and ( (mirror.left_up and ( (laser.x0 == mirror.x0 and laser.x1 > mirror.x0) or (laser.x1 == mirror.x0 and laser.x0 > mirror.x0) ) ) \ or \ (not mirror.left_up and ( (laser.x0 == mirror.x0 and laser.x1 < mirror.x0) or (laser.x1 == mirror.x0 and laser.x0 < mirror.x0) ) ) ) def _target_is_behind_laser(target, laser): return laser.x0 != laser.x1