Now correctly draws lasers behind and in front of mirrors. Phew.

This commit is contained in:
Niels Serup 2012-08-13 16:20:45 +02:00
parent a654540095
commit 7713fbaf98
5 changed files with 127 additions and 45 deletions

View File

@ -29,17 +29,20 @@ import pygame
import worldobject
class Laser(worldobject.WorldObject):
def __init__(self, level, p0, p1, laser_direction):
def __init__(self, level, line):
self.__dict__.update(locals())
worldobject.WorldObject.__init__(self, level, p0[0], max(p0[1], p1[1]) + 48)
(self.x0, self.y0), (self.x1, self.y1) = line
self.x0d, self.y0d, self.x1d, self.y1d \
= self.x0 * 64, (self.y0 + 4) * 48, self.x1 * 64, (self.y1 + 4) * 48
worldobject.WorldObject.__init__(self, level, self.x0d,
max(self.y0d, self.y1d))
def update(self, e, t, dt):
worldobject.WorldObject.update(self, e, t, dt)
def draw(self, window):
(x0, y0), (x1, y1) = self.p0, self.p1
pygame.draw.line(window, (255, 0, 0),
(x0 - self.level.camera_x + 32, y0 + 4 - self.level.camera_y),
(x1 - self.level.camera_x + 32, y1 + 4 - self.level.camera_y), 4)
(self.x0d - self.level.camera_x + 32, self.y0d - 46 - self.level.camera_y),
(self.x1d - self.level.camera_x + 32, self.y1d - 46 - self.level.camera_y), 2)

View File

@ -24,6 +24,8 @@
The fourth level.
"""
from __future__ import print_function
import os
import pygame
import random
@ -35,8 +37,8 @@ import player
import tile
import block
import lever
import mirror
import laser
from mirror import Mirror
from laser import Laser
import misc
import worldobject
@ -60,12 +62,19 @@ class Level4(level.Level):
self.playfield = lm.generate_simple_playfield(16)
# self.playfield = {
# (0, 0): lm.Source(Down),
# (0, 4): lm.MirrorRight,
# (4, 4): lm.MirrorLeft
# }
for (x, y), t in self.playfield.items():
x1, y1 = 64 * x, 48 * (y + 4)
if isinstance(t, lm.Source):
self.objects.append(block.Block(
self, x1, y1,
self.imgs['lasersource_' + t.direction.to_str().lower()],
self.imgs['lasersource_' + t.direction.to_str()],
movable=False))
continue
def mir(b, x1, y1):
@ -76,7 +85,7 @@ class Level4(level.Level):
else lm.MirrorRight
self.generate_lasers()
return g
return mirror.Mirror(self, x1, y1, b, links=[f(x, y)])
return Mirror(self, x, y, b, links=[f(x, y)])
self.objects.append({
lm.MirrorLeft: mir(True, x1, y1),
lm.MirrorRight: mir(False, x1, y1),
@ -90,7 +99,7 @@ class Level4(level.Level):
lm.Blocker: block.Block(self, x1, y1, self.imgs['block1'],
movable=False)
}[t])
mirrors = list(filter(lambda obj: isinstance(obj, mirror.Mirror),
mirrors = list(filter(lambda obj: isinstance(obj, Mirror),
self.objects))
levers = list(filter(lambda obj: isinstance(obj, lever.Lever),
self.objects))
@ -126,17 +135,14 @@ class Level4(level.Level):
def generate_lasers(self):
self.lasers_orig = lm.generate_lasers(self.playfield)
self.lasers = []
for ((x0, y0), (x1, y1)), direc in self.lasers_orig:
self.lasers.append(laser.Laser(
self, (x0 * 64, y0 * 48),
(x1 * 64, y1 * 48), direc))
self.lasers = [Laser(self, line) for line in self.lasers_orig]
def draw(self, window):
self._blit_background(window)
objs = self._sorted_objs(self.objects + self.lasers)
objs = self._after_sort(itertools.groupby(objs, lambda obj: obj.y + obj.z))
objs = self._after_sort(itertools.groupby(
objs, lambda obj: obj.y + obj.z))
for obj in objs:
obj.draw(window)
@ -149,23 +155,88 @@ class Level4(level.Level):
return n_objs
def _after_sort_line(self, objs):
is_special = lambda obj: isinstance(obj, mirror.Mirror) or isinstance(obj, laser.Laser)
specials, nonspecials = filter(is_special, objs), filter(lambda obj: not is_special(obj), objs)
is_special = lambda obj: isinstance(obj, Mirror) \
or isinstance(obj, Laser)
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.Mirror), objs)
lasers = filter(lambda obj: isinstance(obj, laser.Laser), objs)
return mirrors + lasers
def _is_laser_behind_mirror(mirror_direc, laser_direc):
return {
(Left, Up): True,
(Left, Left): True,
(Left, Down): False,
(Left, Right): False,
(Right, Up): True,
(Right, Right): True,
(Right, Down): False,
(Right, Left): False
}[(mirror_direc, laser_direc)]
mirrors = filter(lambda obj: isinstance(obj, Mirror), objs)
lasers = filter(lambda obj: isinstance(obj, Laser), objs)
# for m in mirrors:
# print(m.x0, m.y0)
# print()
# for l in lasers:
# print((l.x0, l.y0), (l.x1, l.y1))
# print()
lasers_back = set(lasers)
sep_ords = []
for mirror in mirrors:
before, after = [], []
for laser in _hit_lasers(mirror, lasers):
lasers_back.discard(laser)
if _mirror_is_behind_laser(mirror, laser):
after.append(laser)
else:
before.append(laser)
# for b in before:
# print(b.x0, b.y0, b.x1, b.y1)
# print(after)
# print()
sep_ords.append(before + [mirror] + after)
mirror_ps = [(mirror.x0, mirror.y0) for mirror in mirrors]
for laser in filter(lambda laser: (laser.x0, laser.y0) in mirror_ps
and (laser.x1, laser.y1) in mirror_ps, 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)
return objs
def _hit_lasers(mirror, lasers):
p = (mirror.x0, mirror.y0)
return filter(lambda laser: (laser.x0, laser.y0) == p
or (laser.x1, laser.y1) == p, lasers)
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)
)
)
)

View File

@ -52,7 +52,8 @@ class Loader(object):
self.directory, 'tiles', 'indoor', 'ground%02d.png' % o))
l = ['block1', 'block1_lifted', 'block3', '../lasertarget',
'../lasersource_up', '../lasersource_down', '../lasersource_right']
'../lasersource_up', '../lasersource_down', '../lasersource_right',
'../lasertarget_glow']
for o in l:
self.imgs[os.path.basename(o)] = pygame.image.load(os.path.join(
self.directory, 'blocks', '%s.png' % o))

View File

@ -157,28 +157,34 @@ def generate_lasers(playfield):
Return [((x, y), direction), ...]
"""
width, height = 16, 16
sources = ((pos, obj.direction) for pos, obj in filter(lambda posobj: isinstance(posobj[1], Source), playfield.items()))
sources = ((pos, obj.direction) for pos, obj
in filter(lambda posobj: isinstance(posobj[1], Source),
playfield.items()))
lasers = []
def add(start, end):
t = (min(start, end), max(start, end))
if not t in lasers:
lasers.append(t)
for start, direc in sources:
end = start
while True:
cur = playfield.get(end)
if cur is Target:
lasers.append(((start, end), direc))
add(start, end)
break
if cur is Blocker:
lasers.append(((start, end), direc))
add(start, end)
break
if cur in (MirrorLeft, MirrorRight):
if (start, end) in ((start, end) for (start, end), direc in lasers):
break
lasers.append(((start, end), direc))
add(start, end)
direc = _mirror_new_direc(cur, direc)
start = end
new_end = direc.next_pos(end)
if new_end[0] < 0 or new_end[1] < 0 or new_end[0] >= width or new_end[1] >= height:
if (start, end) not in lasers:
lasers.append(((start, new_end), direc))
if new_end[0] < 0 or new_end[1] < 0 or \
new_end[0] >= width or new_end[1] >= height:
add(start, new_end)
break
end = new_end
return lasers

View File

@ -29,9 +29,10 @@ import pygame
import worldobject
class Mirror(worldobject.WorldObject):
def __init__(self, level, x, y, left_up=True, links=[]):
def __init__(self, level, x0, y0, left_up=True, links=[]):
self.__dict__.update(locals())
worldobject.WorldObject.__init__(self, level, x, y)
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.left_up_aim = self.left_up
@ -62,6 +63,6 @@ class Mirror(worldobject.WorldObject):
def draw(self, window):
fn = self.frame + 1 if not self.left_up_aim else self.frame + self._half_anim_len
self.img = self.level.imgs['mirror'][int(fn)]
window.blit(self.img, (self.x - 32 - self.level.camera_x,
self.y - self.img.get_size()[1] + 24
window.blit(self.img, (self.xd - 32 - self.level.camera_x,
self.yd - self.img.get_size()[1] + 24
- self.level.camera_y))