Improved lasers, implemented the weight indicator, created a level 2 with the symbol blocks, deleted the much too difficult level 3 and the no-content level 2, made proper links between the levels, created an end screen.

This commit is contained in:
Niels G. W. Serup 2012-11-15 18:36:50 +01:00
parent 9b7d4279ce
commit a3aeb127af
48 changed files with 567 additions and 434 deletions

View File

@ -88,7 +88,7 @@ class Block(worldobject.WorldObject):
obj.holding = self obj.holding = self
self.holder = obj self.holder = obj
if hasattr(self, 'img_str'): if hasattr(self, 'img_str'):
self.img = self.level.imgs[self.img_str + '_lifted'] self.img = self.level.imgs[self.img_str + '-lifted']
# self.orig_alpha = self.orig_alphas['lifted'] # self.orig_alpha = self.orig_alphas['lifted']
def draw(self, window): def draw(self, window):

View File

@ -138,3 +138,6 @@ class Game(object):
self.menu.draw(self.window) self.menu.draw(self.window)
pygame.display.flip() pygame.display.flip()
def quit(self):
self.running = False

View File

@ -65,39 +65,53 @@ class Laser(worldobject.WorldObject):
self.x1d += x1de self.x1d += x1de
self.y1d += y1de self.y1d += y1de
self.start_dark = 0 # self.start_dark = 0
self.surf = pygame.Surface(self.level.game.window.get_size(), # self.surf = pygame.Surface(self.level.game.window.get_size(),
pygame.SRCALPHA) # pygame.SRCALPHA)
self.load()
def update(self, e, t, dt): def update(self, e, t, dt):
self.start_dark = (t % 200) / 100 # self.start_dark = (t % 200) / 100
worldobject.WorldObject.update(self, e, t, dt) worldobject.WorldObject.update(self, e, t, dt)
def load(self):
self.img_horizontal = self.level.imgs['laser_beam_horizontal']
self.img_vertical = self.level.imgs['laser_beam_vertical']
def draw(self, window): def draw(self, window):
self.surf.fill((0, 0, 0, 0)) # self.surf.fill((0, 0, 0, 0))
colors = [(155, 0, 0), (255, 0, 0)] # colors = [(155, 0, 0), (255, 0, 0)]
c = self.start_dark # c = self.start_dark
if self.x0d != self.x1d: if self.x0d != self.x1d:
length = self.x1d - self.x0d length = self.x1d - self.x0d
for i in range(0, length, 8): for i in range(0, length, 8):
x0d = self.x0d + i x0d = self.x0d + i + 2
pygame.draw.line(self.surf, colors[c], window.blit(self.img_horizontal,
(x0d - self.level.camera_x, (x0d - self.level.camera_x,
self.y0d - self.level.camera_y), self.y0d - self.level.camera_y),
(x0d + min(8, length - i) - self.level.camera_x, self.blit_area)
self.y1d - self.level.camera_y), 2) # pygame.draw.line(self.surf, colors[c],
c ^= 1 # (x0d - self.level.camera_x,
# self.y0d - self.level.camera_y),
# (x0d + min(8, length - i) - self.level.camera_x,
# self.y1d - self.level.camera_y), 2)
# c ^= 1
else: else:
length = self.y1d - self.y0d length = self.y1d - self.y0d
for i in range(0, length, 8): for i in range(0, length, 8):
y0d = self.y0d + i y0d = self.y0d + i + 2
pygame.draw.line(self.surf, colors[c], window.blit(self.img_vertical,
(self.x0d - self.level.camera_x, (self.x0d - self.level.camera_x,
y0d - self.level.camera_y), y0d - self.level.camera_y),
(self.x0d - self.level.camera_x, self.blit_area)
y0d + min(8, length - i) - self.level.camera_y), 2) # pygame.draw.line(self.surf, colors[c],
c ^= 1 # (self.x0d - self.level.camera_x,
# y0d - self.level.camera_y),
# (self.x0d - self.level.camera_x,
# y0d + min(8, length - i) - self.level.camera_y), 2)
# c ^= 1
window.blit(self.surf, (0, 0)) # window.blit(self.surf, (0, 0))

View File

@ -39,6 +39,8 @@ import boulder
import lever import lever
import trigger import trigger
import worldobject import worldobject
import level_bonus
import fadeout
import logic.teleportermap import logic.teleportermap
import logic.rollingstone import logic.rollingstone
@ -516,12 +518,17 @@ class Level1(level.Level):
if self.task_completions == self.solution if self.task_completions == self.solution
else lambda *v: None], else lambda *v: None],
anim='lever_updown', toggling=False)) anim='lever_updown', toggling=False))
self.objects.append(
trigger.Trigger(self, 64 * door_x, 48 * 4,
[self.game.goto_level],
self.imgs['indoor1'],
[self.player]))
door.activate(1)
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): if random.randint(0, 1):
for i in range(3): for i in range(3):
self.objects.append( self.objects.append(
@ -533,6 +540,26 @@ class Level1(level.Level):
CompletionBlock(self, 64 * (door_x - 3 - i), 48 * 4, CompletionBlock(self, 64 * (door_x - 3 - i), 48 * 4,
self.solution[i], i + 1)) 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 # DRAW THE BACKGROUND
self.draw_background() self.draw_background()
@ -553,6 +580,13 @@ class Level1(level.Level):
for obj in self.objects: for obj in self.objects:
obj.reset_pos() obj.reset_pos()
def goto_next_level(self):
print(333)
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): class CompletionBlock(block.Block):
def __init__(self, level, x, y, task, number): def __init__(self, level, x, y, task, number):
self.__dict__.update(locals()) self.__dict__.update(locals())

View File

@ -1,53 +1,168 @@
#!/usr/bin/env python.
# -*- coding: utf-8 -*-
# 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 <http://www.gnu.org/licenses/>.
#
# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
#
# level2.py
# --------------------
# date created : Fri Oct 12 2012
# copyright : (C) 2012 Niels G. W. Serup
# maintained by : Niels G. W. Serup <ngws@metanohi.name>
"""
The second level.
"""
from __future__ import print_function
import os import os
import pygame import pygame
import random import random
import re import re
import itertools
import worldobject
import level import level
import player import player
import trigger
import tile import tile
import block import block
import boulder
import lever import lever
import level_bonus
import fadeout import fadeout
from weight_indicator import WeightIndicator
import logic.colourboxes
def is_sorted(xs):
return all(a <= b for a, b in itertools.izip(xs[:-1], xs[1:]))
class Level2(level.Level): class Level2(level.Level):
def __init__(self, game, graphics_dir, paused=False): def __init__(self, game, graphics_dir, paused=False):
level.Level.__init__(self, game, graphics_dir, size=(64*5, 48*5), level.Level.__init__(self, game, graphics_dir, size=(64*21, 48*20),
paused=paused) paused=paused)
self.dimensions = 5, 5 self.dimensions = 21, 20
for i in range(self.dimensions[0]): self.task_start = (1, 6)
for j in range(self.dimensions[1]):
self.tiles.append( for i, j in self._positions():
tile.Tile(self, i*64, (j + 1)*48, self.tiles.append(
self.imgs['indoor%d' % random.randint(1, 6)])) tile.Tile(self, i * 64, (j + 4) * 48, self.imgs['ground1']))
self.sort_poss = []
for x in range(12):
self.sort_poss.append(((x + 2) * 64, 6 * 48))
self.tiles.append(
tile.Tile(self, (x + 2) * 64, 6 * 48, self.imgs['indoor1']))
self.symbolblocks = []
for x in range(12):
b = block.Block(self, 64 * (x + 2), 48 * 10,
self.imgs['symbolblock%02d' % (x + 1)], movable=True)
self.symbolblocks.append(b)
random.shuffle(self.symbolblocks)
for i in range(len(self.symbolblocks)):
self.symbolblocks[i].n = i
random.shuffle(self.symbolblocks)
self.objects.extend(self.symbolblocks)
self.weighing_poss = ((7 * 64, 12 * 48), (9 * 64, 12 * 48))
for x, y in self.weighing_poss:
self.tiles.append(
tile.Tile(self, x, y, self.imgs['indoor3']))
self.objects.append(
trigger.Trigger(self,
x, y,
[self.weigh],
self.imgs['hole'],
self.symbolblocks,
visible=False,
no_stop=True))
self.draw_background() self.draw_background()
bonus = level_bonus.Level(self.game, self.graphics_dir) self.weight_ind = WeightIndicator(self, 8, 8, 0, links=[])
self.objects.append(self.weight_ind)
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 * 48))
self.objects.append(block.InvisBlock(self, self.size[0], i * 48))
self.bottom_objects.append(worldobject.WithBackground(
self, self.imgs['elevator_top'], 64 * (self.task_start[0] + 1), 48 * (self.task_start[1] + 10)))
self.elevator = worldobject.WithBackground(
self, self.imgs['elevator'], 64 * (self.task_start[0] + 1), 48 * (self.task_start[1] + 11), 48)
self.bottom_objects.append(self.elevator)
self._next_level_trigger = trigger.Trigger(
self, 64 * (self.task_start[0] + 2), 48 * (self.task_start[1] + 10), [lambda _: self.try_goto_next_level()],
self.imgs['hole'],
[self.player], visible=False, no_stop=True)
self.objects.append(self._next_level_trigger)
self.bottom_objects.append(worldobject.WithBackground(
self, self.imgs['elevator_bottom'], 64 * (self.task_start[0] + 1), 48 * (self.task_start[1] + 12)))
def f(): self.player.set_pos(64 * 1, 48 * 4)
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 * 2, 48 * 3,
[lambda setting: f()],
toggling=False,
anim='lever_updown'))
self.player.set_pos(64 * 2, 48 * 1)
self.player.set_init_pos() self.player.set_init_pos()
def weigh(self, x):
a, b = None, None
for bl in self.symbolblocks:
if (bl.x, bl.y) == self.weighing_poss[0]:
a = bl.n
elif (bl.x, bl.y) == self.weighing_poss[1]:
b = bl.n
if a is not None and b is not None:
self.weight_ind.change_state(-1 if a < b else 1 if a > b else 0)
def can_go(self):
calc = [None for i in range(12)]
for bl in self.symbolblocks:
p = (bl.x, bl.y)
if p in self.sort_poss:
calc[self.sort_poss.index(p)] = bl.n
return not None in calc and (is_sorted(calc) or is_sorted(calc[::-1]))
def try_goto_next_level(self):
if not self.can_go():
return
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)
self.update = self.update2
self._start_time = pygame.time.get_ticks()
fadeout.Fadeout(self.game, self.goto_next_level, duration=1500)
def update2(self, e, t, dt):
level.Level.update(self, e, t, dt)
start_offset = (t - self._start_time) / 25
self.elevator.z_px = 48 - start_offset
self.player.y = 48 * (self.task_start[1] + 10) - start_offset
def goto_next_level(self):
self.game.goto_level(3)
def restart(self): def restart(self):
for obj in self.objects: for obj in self.objects:

View File

@ -1,25 +1,23 @@
#!/usr/bin/env python. # This file is part of A Robot's Conundrum.
# -*- coding: utf-8 -*-
# This file is part of A Robot's Conundrum
# #
# A Robot's Conundrum is free software: you can redistribute it and/or modify it # 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 # 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 # Software Foundation, either version 3 of the License, or (at your option) any
# later version. # later version.
# #
# A Robot's Conundrum is distributed in the hope that it will be useful, but WITHOUT ANY # A Robot's Conundrum is distributed in the hope that it will be useful, but
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# A PARTICULAR PURPOSE. See the GNU General Public License for more details. # 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 # You should have received a copy of the GNU General Public License along with
# A Robot's Conundrum. If not, see <http://www.gnu.org/licenses/>. # A Robot's Conundrum. If not, see <http://www.gnu.org/licenses/>.
# #
# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' # ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
# #
# level3.py # level3.py
# -------------------- # --------------------
# date created : Wed Aug 8 2012 # date created : Fri Aug 10 2012
# copyright : (C) 2012 Niels G. W. Serup # copyright : (C) 2012 Niels G. W. Serup
# maintained by : Niels G. W. Serup <ngws@metanohi.name> # maintained by : Niels G. W. Serup <ngws@metanohi.name>
@ -35,53 +33,82 @@ import random
import re import re
import itertools import itertools
import worldobject
import level import level
import player import player
import trigger
import tile import tile
import block import block
import lever import lever
from mirror import Mirror
from laser import Laser
import misc
import worldobject
import fadeout import fadeout
import logic.colourboxes import logic.lasermirror as lm
from logic.direction import *
class Level3(level.Level): class Level3(level.Level):
def __init__(self, game, graphics_dir, paused=False): def __init__(self, game, graphics_dir, paused=False):
level.Level.__init__(self, game, graphics_dir, size=(64*11, 48*20), level.Level.__init__(self, game, graphics_dir,
paused=paused) size=(64 * 17, 48 * 21), paused=paused)
self.dimensions = 11, 20 self.dimensions = 17, 17
# for i in range(self.dimensions[0]): not_ok_pos = set(itertools.product(range(7, 10), range(1, 4)))
# for j in range(self.dimensions[1]): for i, j in filter(lambda p: p not in not_ok_pos, self._positions()):
# self.tiles.append(
# tile.Tile(self, i*64, j*48, self.imgs['ground1']))
self.task_start = (1, 6)
# Abstract "boxes", actually colour fields
boxes = []
for i in range(4):
boxes.extend([(0, 0, 0)] * i + box + [(0, 0, 0)] * (3 - i)
for box in logic.colourboxes.generate_colour_boxes(1, 3))
boxes.extend(logic.colourboxes.generate_random_box(4, 2) for _ in range(20))
boxes.extend([(0, 0, 0)] * 4 for _ in range(10))
random.shuffle(boxes)
pos_colour = {}
for i, j in self._positions():
self.tiles.append( self.tiles.append(
tile.Tile(self, i * 64, (j + 4) * 48, self.imgs['indoor%d' % random.randint(1, 6)])) tile.Tile(self, i * 64, (j + 4) * 48, self.imgs[
('indoor%d' % random.randint(1, 6))
for box, (x, y) in zip(boxes, itertools.product(range(7), range(6))): if random.randrange(6) != 0 else 'ground1']))
# self.tiles.append(tile.Tile(self, 64 * (x + self.task_start[0] + 1),
# 48 * (y + self.task_start[1] + 1),
# self.imgs['indoor%d' % random.randint(1, 6)]))
pos_colour[(x, y)] = box
self.draw_background() 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] top = self.dimensions[0]
for i in range(top): for i in range(top):
if i % 3 == 0: if i % 3 == 0:
@ -93,81 +120,172 @@ class Level3(level.Level):
self.objects.append(block.InvisBlock(self, i * 64, self.objects.append(block.InvisBlock(self, i * 64,
self.size[1])) self.size[1]))
for i in range(self.dimensions[1]): for i in range(self.dimensions[1]):
self.objects.append(block.InvisBlock(self, - 64, i * 48)) self.objects.append(block.InvisBlock(self, - 64, (i + 4) * 48))
self.objects.append(block.InvisBlock(self, self.size[0], i * 48)) self.objects.append(block.InvisBlock(self, self.size[0], (i + 4) * 48))
action_blocks = list(itertools.chain(* self.generate_lasers()
[(block.ActionBlock(self, 64 * self.task_start[0],
48 * (i + 1 + self.task_start[1]),
movable=True),
block.ActionBlock(self, 64 * (self.task_start[0] + 8),
48 * (i + 1 + self.task_start[1]),
movable=True))
for i in range(6)]))
self.objects.extend(action_blocks)
wells = [block.ColorWell(self, self.task_start[0] * 64, self.task_start[1] * 48),
block.ColorWell(self, (self.task_start[0] + 8) * 64, self.task_start[1] * 48),
block.ColorWell(self, self.task_start[0] * 64, (self.task_start[1] + 7) * 48),
block.ColorWell(self, (self.task_start[0] + 8) * 64, (self.task_start[1] + 7) * 48),
]
self.objects.extend(wells)
self.wells = wells
self.bottom_objects.append(worldobject.WithBackground( self.bottom_objects.append(worldobject.WithBackground(
self, self.imgs['elevator_top'], 64 * (self.task_start[0] + 3), 48 * (self.task_start[1] + 10))) self, self.imgs['elevator_top'], 64 * 7, 48 * 5))
self.elevator = worldobject.WithBackground( self.elevator = worldobject.WithBackground(
self, self.imgs['elevator'], 64 * (self.task_start[0] + 3), 48 * (self.task_start[1] + 11), 48) 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(self.elevator)
self._next_level_trigger = trigger.Trigger(
self, 64 * (self.task_start[0] + 4), 48 * (self.task_start[1] + 10), [lambda _: self.try_goto_next_level()],
self.imgs['hole'],
[self.player], visible=False, no_stop=True)
self.objects.append(self._next_level_trigger)
self.bottom_objects.append(worldobject.WithBackground( self.bottom_objects.append(worldobject.WithBackground(
self, self.imgs['elevator_bottom'], 64 * (self.task_start[0] + 3), 48 * (self.task_start[1] + 12))) self, self.imgs['elevator_bottom'], 64 * 7, 48 * 7))
def update_wells(block): self.player.set_pos(64 * 8, 48 * 5)
cur_boxes = []
for block in action_blocks:
box = pos_colour.get((block.x / 64 - self.task_start[0] - 1,
block.y / 48 - self.task_start[1] - 1))
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 well, color in zip(wells, well_colours):
well.set_colour(*color)
for b in action_blocks:
b.action = update_wells
self.player.set_pos(64 * 5, 48 * 4)
self.player.set_init_pos() self.player.set_init_pos()
def try_goto_next_level(self):
# if not all(well.well_colour == (255, 255, 255)
# for well in self.wells):
# return
self.objects.remove(self._next_level_trigger)
self._old_player_update = self.player.update self._old_player_update = self.player.update
self.player.update = lambda e, t, dt: self._old_player_update([], t, dt) self.player.update = lambda e, t, dt: self._old_player_update([], t, dt)
self.update = self.update2
self._start_time = pygame.time.get_ticks() self._start_time = pygame.time.get_ticks()
fadeout.Fadeout(self.game, self.goto_next_level, duration=1500) self.on_completion()
def update2(self, e, t, dt):
level.Level.update(self, e, t, dt)
start_offset = (t - self._start_time) / 25
self.elevator.z_px = 48 - start_offset
self.player.y = 48 * (self.task_start[1] + 10) - start_offset
def goto_next_level(self):
self.game.goto_level(4)
def on_completion(self):
# End game here.
fadeout.Fadeout(self.game, lambda: self.game.goto_level(4), duration=5000)
def restart(self): def restart(self):
for obj in self.objects: for obj in self.objects:
obj.reset_pos() 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

View File

@ -15,275 +15,37 @@
# #
# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' # ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
# #
# level1.py # level4.py
# -------------------- # --------------------
# date created : Fri Aug 10 2012 # date created : Thu Nov 15 2012
# copyright : (C) 2012 Niels G. W. Serup # copyright : (C) 2012 Sakse Dalum
# maintained by : Niels G. W. Serup <ngws@metanohi.name> # maintained by : Sakse Dalum <don_s@hongabar.org>
""" """
The fourth level. The final level.
""" """
from __future__ import print_function
import os
import pygame import pygame
import random import os.path
import re
import itertools
import level import level
import player
import tile
import block
import lever
from mirror import Mirror
from laser import Laser
import misc
import worldobject
import logic.lasermirror as lm
from logic.direction import *
class Level4(level.Level): class Level4(level.Level):
def __init__(self, game, graphics_dir, paused=False): def __init__(self, game, graphics_dir, paused=False):
level.Level.__init__(self, game, graphics_dir, level.Level.__init__(self, game, graphics_dir,
size=(64 * 17, 48 * 21), paused=paused) size=(64 * 17, 48 * 21), paused=paused)
self.background_img = pygame.image.load(os.path.join(self.graphics_dir,
self.dimensions = 17, 17 'main_menu.png'))
self.background_img = pygame.transform.smoothscale(
not_ok_pos = set(itertools.product(range(7, 10), range(1, 4))) self.background_img, self.game.window.get_size())
for i, j in filter(lambda p: p not in not_ok_pos, self._positions()): self.background_img = pygame.transform.rotate(
self.tiles.append( self.background_img, 180)
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 not i % 3:
self.objects.append(block.Block(
self, i * 64, 48 * 3,
self.imgs['wall'],
width=1 if i == top - 1 else 3,
blit_area=(0, 0, 96, 192) if i == top - 1 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.
print('Well done.')
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): def update(self, e, t, dt):
self.update2(e, t, dt) if not self.paused:
start_offset = (t - self._start_time) / 25 for event in e:
if start_offset >= 96: if event.type == pygame.KEYDOWN:
start_offset = 96 if event.key == pygame.K_SPACE:
self.player.y = 48 * 7 - start_offset self.game.quit()
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): def draw(self, window):
self._blit_background(window) window.blit(self.background_img, (0, 0))
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

View File

@ -42,24 +42,33 @@ import fadeout
class Level(level.Level): class Level(level.Level):
def __init__(self, game, graphics_dir, paused=False): def __init__(self, game, graphics_dir, paused=False):
level.Level.__init__(self, game, graphics_dir, size=(64*20, 48*20), level.Level.__init__(self, game, graphics_dir, size=(64*21, 48*20),
paused=paused) paused=paused)
self.dimensions = 20, 20 self.dimensions = 21, 20
for i in range(self.dimensions[0]): for i, j in self._positions():
for j in range(self.dimensions[1]): self.tiles.append(
self.tiles.append( tile.Tile(self, i * 64, (j + 4) * 48, self.imgs['ground1']))
tile.Tile(self, (i+1)*64, j*48, self.imgs['indoor%d' % random.randint(1, 6)]))
self.draw_background() self.draw_background()
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 * 48))
self.objects.append(block.InvisBlock(self, self.size[0], i * 48))
for x, y in misc.pick_random_elements(
list(itertools.product(range(2, 20), range(2, 20))), 150):
self.objects.append(block.Block(self, 64 * x, 48 * y,
self.imgs['block1'], movable=True))
def f(): def go_back():
self._update = self.update self._update = self.update
self.update = lambda *args: None self.update = lambda *args: None
def g(): def g():
@ -69,12 +78,17 @@ class Level(level.Level):
self.objects.append( self.objects.append(
lever.Lever( lever.Lever(
self, 64, 48, self, 0, 48 * 4,
[lambda setting: f()], [lambda setting: go_back()],
toggling=False, toggling=False,
anim='lever_updown')) anim='lever_updown'))
self.player.set_pos(64 * 1, 48 * 2) for x, y in misc.pick_random_elements(
list(itertools.product(range(2, 19), range(2, 14))), 150):
self.objects.append(block.Block(self, 64 * x, 48 * (4 + y),
self.imgs['block1'], movable=True))
self.player.set_pos(0, 48 * 5)
self.player.set_init_pos() self.player.set_init_pos()
def enter(self, root_level): def enter(self, root_level):

View File

@ -42,11 +42,11 @@ class Loader(object):
self.directory, 'tiles', '%s.png' % o)) self.directory, 'tiles', '%s.png' % o))
for o in range(1, 13): for o in range(1, 13):
for i in range(16, 19): for i in ('', '-lifted', '-down'):
self.imgs['symbol%02d-%04d' % (o, i)] = ( s = 'symbolblock%02d%s' % (o, i)
self.imgs[s] = (
pygame.image.load(os.path.join( pygame.image.load(os.path.join(
self.directory, 'symbols', 'blocks', self.directory, 'symbols', 'blocks', s + '.png')))
'block%02d-%04d.png' % (o, i))))
for o in range(13, 18): for o in range(13, 18):
for i in range(16, 20): for i in range(16, 20):
self.imgs['symbol%02d-%04d' % (o, i)] = ( self.imgs['symbol%02d-%04d' % (o, i)] = (
@ -58,9 +58,10 @@ class Loader(object):
self.imgs['indoor%d' % o] = pygame.image.load(os.path.join( self.imgs['indoor%d' % o] = pygame.image.load(os.path.join(
self.directory, 'tiles', 'indoor', 'ground%02d.png' % o)) self.directory, 'tiles', 'indoor', 'ground%02d.png' % o))
l = ['block1', 'block1_lifted', 'block3', '../lasertarget', l = ['block1', 'block1-lifted', 'block3', '../lasertarget',
'../lasersource_up', '../lasersource_down', '../lasersource_right', '../lasersource_up', '../lasersource_down', '../lasersource_right',
'../lasertarget_glow'] '../lasertarget_glow', '../laser_beam_horizontal',
'../laser_beam_vertical']
for o in l: for o in l:
self.imgs[os.path.basename(o)] = pygame.image.load(os.path.join( self.imgs[os.path.basename(o)] = pygame.image.load(os.path.join(
self.directory, 'blocks', '%s.png' % o)) self.directory, 'blocks', '%s.png' % o))
@ -127,6 +128,7 @@ class Loader(object):
('elevating_column', 'elevating_column'), ('elevating_column', 'elevating_column'),
('mirror', 'mirror'), ('mirror', 'mirror'),
('weight_indicator', 'weight_indicator'),
('door', 'door')] ('door', 'door')]
): ):

View File

@ -0,0 +1,71 @@
# 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 <http://www.gnu.org/licenses/>.
#
# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
#
# weight_indicator.py
# --------------------
# date created : Thu Nov 15 2012
# copyright : (C) 2012 Niels G. W. Serup
# maintained by : Niels G. W. Serup <ngws@metanohi.name>
"""
A weight indicator for drawing.
"""
import pygame
import worldobject
class WeightIndicator(worldobject.WorldObject):
def __init__(self, level, x0, y0, state=0, links=[]):
self.__dict__.update(locals())
self.xd, self.yd = self.x0 * 64, (self.y0 + 4) * 48
worldobject.WorldObject.__init__(self, level, self.xd, self.yd)
self.in_rotation = False
self.frame = self.state_to_frame(self.state)
def state_to_frame(self, state):
return state * 9 + 9
def change_state(self, new_state):
if self.in_rotation or new_state == self.state:
return
self.in_rotation = True
self.start_frame = self.frame
self.goal_state = new_state
self.goal_frame = self.state_to_frame(new_state)
self.start_time = pygame.time.get_ticks()
def update(self, e, t, dt):
if self.in_rotation:
d = (t - self.start_time) / 1000.0
self.frame = min(18, int(self.start_frame
+ (self.goal_frame - self.start_frame)
* d))
if self.frame == self.goal_frame:
self.in_rotation = False
self.state = self.goal_state
for link in self.links:
link(self.state)
worldobject.WorldObject.update(self, e, t, dt)
def draw(self, window):
self.img = self.level.imgs['weight_indicator'][self.frame]
window.blit(self.img, (self.xd - 32 - self.level.camera_x,
self.yd - self.img.get_size()[1] + 24
- self.level.camera_y))

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 210 B

After

Width:  |  Height:  |  Size: 210 B

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB