Updated filenames and file contents to fit the title of the game. Made proper readme.

This commit is contained in:
2012-10-12 13:50:02 +02:00
parent 9943e87f4a
commit 9b7d4279ce
35 changed files with 1398 additions and 273 deletions

View File

@@ -0,0 +1,3 @@
# init file.
import game

317
arobotsconundrum/block.py Normal file
View File

@@ -0,0 +1,317 @@
# 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/>.
#
# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
#
# block.py
# --------------------
# date created : Tue Aug 7 2012
# copyright : (C) 2012 Sakse Dalum
# maintained by : Sakse Dalum <don_s@hongabar.org>
"""
A generic block. And other block derivatives.
"""
import pygame
import itertools
import worldobject
import copy
class Block(worldobject.WorldObject):
def __init__(self, level, x, y, img_str='block1',
movable=False, visible=True,
width=1, height=1, blit_area=None,
blocking=True, z=0):
self.__dict__.update(locals())
worldobject.WorldObject.__init__(self, level, x, y, z=z,
movable=movable, visible=visible,
blit_area=blit_area, blocking=blocking)
for i in range(width):
for j in range(height):
if width != 1 or height != 1:
self.level.objects.append(
InvisBlock(self.level,
self.x + 64 * i,
self.y - 48 * j))
self.load()
# self.orig_alphas = {
# 'normal':
# pygame.surfarray.array_alpha(self.level.imgs[self.img_str]),
# 'lifted':
# pygame.surfarray.array_alpha(
# self.level.imgs[self.img_str + '_lifted'])}
# self.orig_alpha = self.orig_alphas['normal']
def load(self):
if not isinstance(self.img_str, str):
self.img_str = self.level.reverse_imgs[self.img_str]
self.img = self.level.imgs[self.img_str]
# def update_alpha(self):
# be_opaque = not (self.y + self.z >= self.level.player.move_y
# + self.level.player.move_z)
# if be_opaque and not self.is_currently_opaque:
# self.is_currently_opaque = True
# self.set_alpha(1)
# elif not be_opaque and self.is_currently_opaque:
# self.is_currently_opaque = False
# self.set_alpha(0.5)
def use(self, obj):
if self.movable:
if obj == self.holder:
obj.holding = None
self.holder = None
if hasattr(self, 'img_str'):
self.img = self.level.imgs[self.img_str]
# self.orig_alpha = self.orig_alphas['normal']
else:
obj.holding = self
self.holder = obj
if hasattr(self, 'img_str'):
self.img = self.level.imgs[self.img_str + '_lifted']
# self.orig_alpha = self.orig_alphas['lifted']
def draw(self, window):
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), self.blit_area)
class InvisBlock(Block):
def __init__(self, level, x, y):
self.__dict__.update(locals())
worldobject.WorldObject.__init__(self, level, x, y, visible=False)
class ArrowBlock(Block):
def __init__(self, level, x, y, direction, movable=True, is_up=True):
self.__dict__.update(locals())
worldobject.WorldObject.__init__(self, level, x, y,
direction=direction, movable=movable)
self.frame = 0
self.anim_speed = 15
self.setting = False
if direction == (1, 0):
self.anim = 'arrow_right'
if direction == (-1, 0):
self.anim = 'arrow_left'
if direction == (0, -1):
self.anim = 'arrow_up'
if direction == (0, 1):
self.anim = 'arrow_down'
self.img_str = self.anim
def activate(self, setting):
self.movable = True
if not self.share_tile(worldobject.WorldObject):
self.is_up = not setting
self.movable = False
def update(self, e, t, dt):
# Update the animation
if not self.is_up:
self.frame = min(self.frame + self.anim_speed * dt,
len(self.level.imgs[self.anim]) - 1)
else:
self.frame = max(self.frame - self.anim_speed * dt,
0)
if self.frame == len(self.level.imgs[self.anim]) - 1:
self.movable = self.blocking = False
self.z = -48
if self.holder:
self.holder.holding = None
self.holder = None
else:
self.movable = self.blocking = True
self.z = 0
worldobject.WorldObject.update(self, e, t, dt)
def draw(self, window):
if not self.holder:
self.img = self.level.imgs[self.anim][int(self.frame)]
Block.draw(self, window)
class RisingBlock(Block):
def __init__(self, level, x, y, movable=False, is_up=True):
self.__dict__.update(locals())
worldobject.WorldObject.__init__(self, level, x, y, movable=movable)
self.frame = 0
self.anim_speed = 15
self.setting = [0, 0]
self.init_setting = self.setting
self.anim = 'arrow_right'
def set_init_pos(self):
worldobject.WorldObject.set_init_pos(self)
self.init_setting = [self.setting[0], self.setting[1]]
def reset_pos(self):
worldobject.WorldObject.reset_pos(self)
self.setting = [self.init_setting[0], self.init_setting[1]]
self.is_up = self.setting[1] == self.setting[0]
def activate(self, setting):
if setting in [2, 3]:
self.setting[1] = setting - 2
else:
self.setting[0] = setting
if not self.share_tile(worldobject.WorldObject):
self.is_up = self.setting[1] == self.setting[0]
def update(self, e, t, dt):
# Update the animation
if not self.is_up:
self.frame = min(self.frame + self.anim_speed * dt,
len(self.level.imgs[self.anim]) - 1)
else:
self.frame = max(self.frame - self.anim_speed * dt,
0)
if self.frame == len(self.level.imgs[self.anim]) - 1:
self.blocking = False
self.z = -48
else:
self.blocking = True
self.z = 0
worldobject.WorldObject.update(self, e, t, dt)
def draw(self, window):
self.img = self.level.imgs[self.anim][int(self.frame)]
Block.draw(self, window)
class ActionBlock(Block):
def __init__(self, level, x, y, action=None, movable=True):
self.__dict__.update(locals())
Block.__init__(
self, level, x, y, 'block1', movable=movable)
self._last_pos = None
def update(self, e, t, dt):
if (self.x, self.y) != self._last_pos and not self.is_moving:
self.action(self)
self._last_pos = self.x, self.y
worldobject.WorldObject.update(self, e, t, dt)
class ColorWell(Block):
def __init__(self, level, x, y, img_str='well'):
self.__dict__.update(locals())
worldobject.WorldObject.__init__(
self, level, x, y, movable=False)
Block.load(self)
self.col_img = pygame.Surface((32, 32))
def set_colour(self, r, g, b):
self.well_colour = r * 255, g * 255, b * 255
self.col_img.fill(self.well_colour)
def draw(self, window):
if self.visible:
window.blit(self.col_img, (self.x - self.level.camera_x + 16,
self.y - self.img.get_size()[1] + 52
- self.level.camera_y))
Block.draw(self, window)
class Door(Block):
def __init__(self, level, x, y, movable=False, setting=False):
self.__dict__.update(locals())
worldobject.WorldObject.__init__(self, level, x, y,
z=32, movable=movable)
self.frame = 0
self.anim_speed = 15
self.init_setting = self.setting
self.anim = 'door'
self.level.objects.append(InvisBlock(self.level, self.x + 64, self.y))
self.level.objects.append(InvisBlock(self.level, self.x - 64, self.y))
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 activate(self, setting):
self.setting = setting
def update(self, e, t, dt):
# Update the animation
if self.setting:
self.frame = min(self.frame + self.anim_speed * dt,
len(self.level.imgs[self.anim]) - 1)
else:
self.frame = max(self.frame - self.anim_speed * dt,
0)
if self.frame == len(self.level.imgs[self.anim]) - 1:
self.blocking = False
else:
self.blocking = True
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 + 64) - self.level.camera_x,
self.y - self.img.get_size()[1] + 24
- self.level.camera_y))
class LaserSource(Block):
def __init__(self, level, x1, y1, source_direction):
self.__dict__.update(locals())
Block.__init__(self, level, x1, y1,
'lasersource_' + source_direction.to_str(),
movable=False)
class LaserTarget(Block):
def __init__(self, level, x0, y0):
self.__dict__.update(locals())
self.xd, self.yd = self.x0 * 64, (self.y0 + 4) * 48
Block.__init__(self, level, self.xd, self.yd,
'lasertarget', movable=False)
self.glows = False
def new_playfield_update(self):
self.glows, self.img_str = (True, 'lasertarget_glow') \
if (self.x0, self.y0) in itertools.chain(*self.level.lasers_orig) \
else (False, 'lasertarget')
self.load()

View File

@@ -0,0 +1,99 @@
# 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/>.
#
# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
#
# boulder.py
# --------------------
# date created : Wed Aug 8 2012
# copyright : (C) 2012 Sakse Dalum
# maintained by : Sakse Dalum <don_s@hongabar.org>
"""
A rolling boulder.
"""
import pygame
import worldobject
import block
import player
class Boulder(worldobject.WorldObject):
def __init__(self, level, x, y, direction=(1, 0),
movable=True, rolling=False):
self.__dict__.update(locals())
worldobject.WorldObject.__init__(self, level, x, y, direction=direction,
movable=movable, speed=4)
self.falling = False
self.anim = 'boulder_right'
self.frame = 0
self.anim_speed = 60
self.ignore_list.append(player.Player)
def fall(self, setting):
if setting and not self.falling:
self.frame = 0
self.falling = True
def activate(self, setting):
self.rolling = True
def reset_pos(self):
worldobject.WorldObject.reset_pos(self)
self.frame = 0
self.rolling = False
self.falling = False
def update(self, e, t, dt):
# Update the animation
if self.rolling:
self.frame = ((self.frame + self.anim_speed * dt) %
len(self.level.imgs[self.anim]))
if self.falling:
self.frame = min(self.frame + self.anim_speed * dt,
len(self.level.imgs[self.anim]) - 1)
if self.falling:
self.anim = 'boulder_falling'
if self.frame == len(self.level.imgs[self.anim]) - 1:
self.reset_pos()
elif self.direction == (1, 0):
self.anim = 'boulder_right'
elif self.direction == (0, 1):
self.anim = 'boulder_down'
elif self.direction == (-1, 0):
self.anim = 'boulder_left'
elif self.direction == (0, -1):
self.anim = 'boulder_up'
if self.rolling and not self.falling:
if not self.is_moving:
tile_sharer = self.share_tile(block.ArrowBlock)
if tile_sharer:
self.direction = tile_sharer.direction
if not self.move(*self.direction) and not self.is_moving:
self.reset_pos()
worldobject.WorldObject.update(self, e, t, dt)
def draw(self, window):
self.img = self.level.imgs[self.anim][int(self.frame)]
window.blit(self.img, (self.x - 32 - self.level.camera_x,
self.y - self.img.get_size()[1] + 24
- self.level.camera_y))

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/>.
#
# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
#
# fadeout.py
# --------------------
# date created : Thu Aug 9 2012
# copyright : (C) 2012 Niels G. W. Serup
# maintained by : Niels G. W. Serup <ngws@metanohi.name>
"""
Fade out and in.
"""
import os
import pygame
class Fadeout(object):
def __init__(self, game, function, duration=500):
self.__dict__.update(locals())
self.game.objs.append(self)
self.img = pygame.Surface(self.game.window.get_size())
self._start_time = pygame.time.get_ticks()
self._middle_time = self._start_time + duration
self._end_time = self._middle_time + duration
self.img.set_alpha(0)
self.img.fill((0, 0, 0))
self._has_run = False
def update(self, e, t, dt):
if t < self._middle_time:
self.img.set_alpha(255 * (t - self._start_time) / self.duration)
else:
if not self._has_run:
self.function()
self._has_run = True
if t < self._end_time:
self.img.set_alpha(255 * (self.duration - (t - self._middle_time)) / self.duration)
else:
self.game.objs.remove(self)
self.update = lambda *xs: None
def draw(self, window):
window.blit(self.img, (0, 0))
class Darkness(Fadeout):
def __init__(self, game, darkness):
self.__dict__.update(locals())
self.set_darkness(darkness)
def set_darkness(self, darkness):
self.darkness = darkness
self.img = pygame.Surface(self.game.window.get_size())
self.img.set_alpha(int(darkness * 255))
self.img.fill((0, 0, 0))
def update(self, *xs):
pass

140
arobotsconundrum/game.py Normal file
View File

@@ -0,0 +1,140 @@
# 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/>.
#
# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
#
# game.py
# --------------------
# date created : Tue Aug 7 2012
# copyright : (C) 2012 Sakse Dalum
# maintained by : Sakse Dalum <don_s@hongabar.org>
"""
The game. Handles everything.
"""
import os
import pygame
import arobotsconundrum
import jukebox
import level
import main_menu
import game_menu
import loader
class Game(object):
"""Create an object to handle the game."""
def __init__(self, window, directory, disable_music,
running=False, speed=30, initial_goto_level=None,
level_num=0):
self.__dict__.update(locals())
self.objs = []
self.clock = pygame.time.Clock()
self.ticks = self.prev_ticks = pygame.time.get_ticks()
self.load()
def start(self):
self.running = True
self.run()
def stop(self):
self.running = False
def load(self):
graphics_dir = os.path.join(self.directory, "resources", "graphics")
self.loader = loader.Loader(self, graphics_dir)
self.loader.load()
self.level = None
self.jukebox = jukebox.Jukebox(
os.path.join(self.directory, "resources", "music"),
["basshit.ogg"])
self.jukebox.stop()
if not self.disable_music:
self.jukebox.play()
if not self.initial_goto_level:
self.menu = main_menu.MainMenu(self, graphics_dir)
else:
self.goto_level(self.initial_goto_level)
def run(self):
t = pygame.time.get_ticks()
dt = 0
while self.running:
dt = float(pygame.time.get_ticks() - t) / 1000
t = pygame.time.get_ticks()
self.update(t, dt)
self.draw()
self.clock.tick(self.speed)
def goto_level(self, level):
if self.level in self.objs:
self.objs.remove(self.level)
self.level_num = level
graphics_dir = os.path.join(self.directory, "resources", "graphics")
self.menu = game_menu.GameMenu(self, graphics_dir)
exec 'from level%d import Level%d as level' % (level, level)
self.level = level(self, graphics_dir)
self.objs.insert(0, self.level)
def restart_level(self):
self.goto_level(self.level_num)
def update(self, t, dt):
"""
Update all game objects.
"""
# Retrieve and flush all events since last update call (this prevents
# event "bottlenecking"/lock-ups)
e = pygame.event.get()
for event in e:
# Stop the game when closing the window
if event.type == pygame.QUIT:
self.stop()
# Keep the music playing!
if not pygame.mixer.music.get_busy() and not self.disable_music:
self.jukebox.play()
# Update all objects
for obj in self.objs[:]:
if hasattr(obj, 'update'):
obj.update(e, t, dt)
self.menu.update(e, t, dt)
def draw(self):
"""
Draw all game objects.
"""
self.window.fill((0, 0, 0))
for obj in self.objs[:]:
if hasattr(obj, 'draw'):
obj.draw(self.window)
self.menu.draw(self.window)
pygame.display.flip()

View File

@@ -0,0 +1,90 @@
# 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/>.
#
# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
#
# game_menu.py
# --------------------
# date created : Tue Aug 7 2012
# copyright : (C) 2012 Sakse Dalum
# maintained by : Sakse Dalum <don_s@hongabar.org>
"""
The in-game menu.
"""
import os
import pygame
class GameMenu(object):
def __init__(self, game, img_dir, active=False, selection=0):
self.__dict__.update(locals())
self.menu = ['restart_level', 'quit']
self.load()
def load(self):
screen_size = self.game.window.get_size()
for item in self.menu:
setattr(self, '%s_imgs' % item, [
pygame.image.load(os.path.join(self.img_dir,
'%s-%s.png' % (item, end)))
for end in ['inactive', 'selected']])
img_size = getattr(self, '%s_imgs' % item)[0].get_size()
factors = (float(img_size[0]) / 1920, float(img_size[1]) / 1280)
setattr(self, '%s_imgs' % item, [
pygame.transform.smoothscale(
img,
(int(screen_size[0]*factors[0]),
int(screen_size[1]*factors[1])))
for img in getattr(self, '%s_imgs' % item)])
def toggle_menu(self):
self.game.level.paused = self.active = not self.active
def update(self, e, t, dt):
for event in e:
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
self.toggle_menu()
if self.active:
if event.key in [pygame.K_SPACE, pygame.K_RETURN]:
if self.menu[self.selection] == 'restart_level':
#self.game.level.restart()
self.game.restart_level()
self.toggle_menu()
if self.menu[self.selection] == 'quit':
self.game.stop()
if event.key == pygame.K_UP:
self.selection = max(self.selection - 1, 0)
if event.key == pygame.K_DOWN:
self.selection = min(self.selection + 1,
len(self.menu) - 1)
def draw(self, window):
if self.active:
screen_size = self.game.window.get_size()
for i in range(len(self.menu)):
s = i == self.selection
img = getattr(self, '%s_imgs' % self.menu[i])[s]
window.blit(img,
(int((screen_size[0] - img.get_size()[0]) / 2),
int(screen_size[1] / 2)
- (int(screen_size[1]*0.13)
* (len(self.menu) / 2 - i))))

View File

@@ -0,0 +1,45 @@
# 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/>.
#
# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
#
# jukebox.py
# --------------------
# date created : Tue Aug 7 2012
# copyright : (C) 2012 Sakse Dalum
# maintained by : Sakse Dalum <don_s@hongabar.org>
"""
The jukebox. Handles playback of background music.
"""
import os
import pygame
class Jukebox(object):
"""Create an object to handle music playback."""
def __init__(self, music_dir, music_files, volume=100, playing=True):
self.__dict__.update(locals())
self.iterator = 0
def play(self):
pygame.mixer.music.load(
os.path.join(self.music_dir, self.music_files[self.iterator]))
pygame.mixer.music.play()
self.iterator = (self.iterator + 1) % len(self.music_files)
def stop(self):
pygame.mixer.music.stop()

103
arobotsconundrum/laser.py Normal file
View File

@@ -0,0 +1,103 @@
# 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/>.
#
# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
#
# laser.py
# --------------------
# date created : Sun Aug 12 2012
# copyright : (C) 2012 Niels G. W. Serup
# maintained by : Niels G. W. Serup <ngws@metanohi.name>
"""
A laser for drawing.
"""
import pygame
import worldobject
import arobotsconundrum.logic.lasermirror as lm
class Laser(worldobject.WorldObject):
def __init__(self, level, line, first_laser=False):
self.__dict__.update(locals())
(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
if self.y0d > self.y1d:
self.y0d, self.y1d = self.y1d, self.y0d
if self.x0d > self.x1d:
self.x0d, self.x1d = self.x1d, self.x0d
worldobject.WorldObject.__init__(self, level, self.x0d,
max(self.y0d, self.y1d))
x0de = 31
y0de = -48
x1de = 31
y1de = -48
if self.x0 < 0:
x0de += 32
if self.y0 < 0:
y0de += 24
if self.x1d >= self.level.size[0]:
x1de -= 32
if self.first_laser and self.y0 == 0 and self.y0 != self.y1:
y0de += 6
if self.level.playfield.get((self.x0, self.y0)) is lm.Target and self.x0 < self.x1:
x0de += 20
if self.level.playfield.get((self.x1, self.y1)) is lm.Target and self.x0 < self.x1:
x1de -= 20
self.x0d += x0de
self.y0d += y0de
self.x1d += x1de
self.y1d += y1de
self.start_dark = 0
self.surf = pygame.Surface(self.level.game.window.get_size(),
pygame.SRCALPHA)
def update(self, e, t, dt):
self.start_dark = (t % 200) / 100
worldobject.WorldObject.update(self, e, t, dt)
def draw(self, window):
self.surf.fill((0, 0, 0, 0))
colors = [(155, 0, 0), (255, 0, 0)]
c = self.start_dark
if self.x0d != self.x1d:
length = self.x1d - self.x0d
for i in range(0, length, 8):
x0d = self.x0d + i
pygame.draw.line(self.surf, colors[c],
(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:
length = self.y1d - self.y0d
for i in range(0, length, 8):
y0d = self.y0d + i
pygame.draw.line(self.surf, colors[c],
(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))

110
arobotsconundrum/level.py Normal file
View File

@@ -0,0 +1,110 @@
# 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/>.
#
# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
#
# level.py
# --------------------
# date created : Tue Aug 7 2012
# copyright : (C) 2012 Sakse Dalum
# maintained by : Sakse Dalum <don_s@hongabar.org>
"""
A generic level.
"""
import pygame
import itertools
import player
import fadeout
class Level(object):
def __init__(self, game, graphics_dir, size=(0, 0), paused=False):
self.__dict__.update(locals())
self.tiles = []
self.objects = []
self.bottom_objects = [] # Special tiles
self.imgs = game.loader.imgs
self.reverse_imgs = (
dict([(val, key) for (key, val)
in self.imgs.iteritems() if not isinstance(val, list)]))
self.camera_x = 0
self.camera_y = 0
self.player = player.Player(self, 0, 0)
self.objects.append(self.player)
self.darkness = fadeout.Darkness(self.game, 0)
self.load()
def load(self):
pass
def set_darkness(self, darkness):
self.darkness.set_darkness(darkness)
def draw_background(self):
self.background = pygame.Surface(self.size)
for tile in self.tiles:
tile.draw(self.background)
def restart(self):
pass
def update(self, e, t, dt):
if not self.paused:
for obj in self.objects:
obj.update(e, t, dt)
if self.player.is_moving:
obj.update_alpha()
screen_size = self.game.window.get_size()
self.camera_x = max(0, min(self.size[0] - screen_size[0],
(self.player.x - screen_size[0] / 2 + 32)))
self.camera_y = max(0, min(self.size[1] - screen_size[1] - 48,
(self.player.y - screen_size[1] / 2 - 24)))
def _positions(self):
return ((x, y) for (y, x) in itertools.product(xrange(self.dimensions[1]),
xrange(self.dimensions[0])))
def _blit_background(self, window):
window.blit(self.background, (0 - self.camera_x, 0 - self.camera_y))
def _sorted_objs(self, objs=None):
return sorted(self.objects if objs is None else objs,
key=lambda obj: (obj.y + obj.z))
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 ...")
for obj in self._sorted_objs():
try:
obj.draw(window)
except IndexError:
print("Skipping frames ...")
self.darkness.draw(window)

View File

@@ -0,0 +1,41 @@
# 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/>.
#
# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
#
# level0.py
# --------------------
# date created : Tue Aug 7 2012
# copyright : (C) 2012 Sakse Dalum
# maintained by : Sakse Dalum <don_s@hongabar.org>
"""
The zeroth level.
"""
import pygame
import level
class Level0(level.Level):
def update(self, e, t, dt):
if not self.paused:
for event in e:
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
self.game.goto_level(1)
def draw(self, window):
window.blit(self.imgs['intro-screen'], (0, 0))

620
arobotsconundrum/level1.py Normal file
View File

@@ -0,0 +1,620 @@
# 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/>.
#
# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
#
# level1.py
# --------------------
# date created : Tue Aug 7 2012
# copyright : (C) 2012 Sakse Dalum
# maintained by : Sakse Dalum <don_s@hongabar.org>
"""
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 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.objects.append(
trigger.Trigger(self, 64 * door_x, 48 * 4,
[self.game.goto_level],
self.imgs['indoor1'],
[self.player]))
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))
# 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()
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))

View File

@@ -0,0 +1,54 @@
import os
import pygame
import random
import re
import level
import player
import tile
import block
import boulder
import lever
import level_bonus
import fadeout
class Level2(level.Level):
def __init__(self, game, graphics_dir, paused=False):
level.Level.__init__(self, game, graphics_dir, size=(64*5, 48*5),
paused=paused)
self.dimensions = 5, 5
for i in range(self.dimensions[0]):
for j in range(self.dimensions[1]):
self.tiles.append(
tile.Tile(self, i*64, (j + 1)*48,
self.imgs['indoor%d' % random.randint(1, 6)]))
self.draw_background()
bonus = level_bonus.Level(self.game, self.graphics_dir)
def f():
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()
def restart(self):
for obj in self.objects:
obj.reset_pos()

173
arobotsconundrum/level3.py Normal file
View File

@@ -0,0 +1,173 @@
#!/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/>.
#
# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
#
# level3.py
# --------------------
# date created : Wed Aug 8 2012
# copyright : (C) 2012 Niels G. W. Serup
# maintained by : Niels G. W. Serup <ngws@metanohi.name>
"""
The third level.
"""
from __future__ import print_function
import os
import pygame
import random
import re
import itertools
import worldobject
import level
import player
import trigger
import tile
import block
import lever
import fadeout
import logic.colourboxes
class Level3(level.Level):
def __init__(self, game, graphics_dir, paused=False):
level.Level.__init__(self, game, graphics_dir, size=(64*11, 48*20),
paused=paused)
self.dimensions = 11, 20
# for i in range(self.dimensions[0]):
# for j in range(self.dimensions[1]):
# 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(
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))):
# 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()
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))
action_blocks = list(itertools.chain(*
[(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, self.imgs['elevator_top'], 64 * (self.task_start[0] + 3), 48 * (self.task_start[1] + 10)))
self.elevator = worldobject.WithBackground(
self, self.imgs['elevator'], 64 * (self.task_start[0] + 3), 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] + 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, self.imgs['elevator_bottom'], 64 * (self.task_start[0] + 3), 48 * (self.task_start[1] + 12)))
def update_wells(block):
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()
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.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(4)
def restart(self):
for obj in self.objects:
obj.reset_pos()

289
arobotsconundrum/level4.py Normal file
View File

@@ -0,0 +1,289 @@
# 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/>.
#
# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
#
# level1.py
# --------------------
# date created : Fri Aug 10 2012
# copyright : (C) 2012 Niels G. W. Serup
# maintained by : Niels G. W. Serup <ngws@metanohi.name>
"""
The fourth 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 logic.lasermirror as lm
from logic.direction import *
class Level4(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 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):
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

@@ -0,0 +1,92 @@
# 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/>.
#
# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
#
# level_bonus.py
# --------------------
# date created : Thu Aug 9 2012
# copyright : (C) 2012 Niels G. W. Serup
# maintained by : Niels G. W. Serup <ngws@metanohi.name>
"""
Fun bonus level.
"""
import os
import pygame
import random
import re
import itertools
import misc
import level
import player
import tile
import block
import boulder
import lever
import fadeout
class Level(level.Level):
def __init__(self, game, graphics_dir, paused=False):
level.Level.__init__(self, game, graphics_dir, size=(64*20, 48*20),
paused=paused)
self.dimensions = 20, 20
for i in range(self.dimensions[0]):
for j in range(self.dimensions[1]):
self.tiles.append(
tile.Tile(self, (i+1)*64, j*48, self.imgs['indoor%d' % random.randint(1, 6)]))
self.draw_background()
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():
self._update = self.update
self.update = lambda *args: None
def g():
self.update = self._update
self.exit()
fadeout.Fadeout(self.game, g)
self.objects.append(
lever.Lever(
self, 64, 48,
[lambda setting: f()],
toggling=False,
anim='lever_updown'))
self.player.set_pos(64 * 1, 48 * 2)
self.player.set_init_pos()
def enter(self, root_level):
self.__dict__.update(locals())
self.game.objs.remove(root_level)
self.game.objs.insert(0, self)
self.game.level = self
def exit(self):
self.game.objs.remove(self)
self.game.objs.insert(0, self.root_level)
def restart(self):
for obj in self.objects:
obj.reset_pos()

95
arobotsconundrum/lever.py Normal file
View File

@@ -0,0 +1,95 @@
# 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/>.
#
# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
#
# lever.py
# --------------------
# date created : Wed Aug 8 2012
# copyright : (C) 2012 Sakse Dalum
# maintained by : Sakse Dalum <don_s@hongabar.org>
"""
A generic lever.
"""
import pygame
import worldobject
class Lever(worldobject.WorldObject):
def __init__(self, level, x, y, links,
anim='lever_leftright', toggling=False, signal=[0, 1],
setting=False):
self.__dict__.update(locals())
worldobject.WorldObject.__init__(self, level, x, y)
self.init_setting = setting
self.at_rest = True
self.send_signal = False
self.frame = 0
self.anim_speed = 15
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 use(self, obj):
self.setting = not self.setting
self.at_rest = False
self.send_signal = True
def update(self, e, t, dt):
# Update the animation
if not self.setting:
self.frame = min(self.frame + self.anim_speed * dt,
len(self.level.imgs[self.anim]) - 1)
else:
self.frame = max(self.frame - self.anim_speed * dt,
0)
if not self.toggling:
if (self.frame - self.anim_speed * dt <= 0):
self.setting = 0
self.at_rest = self.frame == len(self.level.imgs[self.anim]) - 1
else:
self.at_rest = self.frame in [len(self.level.imgs[self.anim]) - 1,
0]
if (((self.frame <= int(len(self.level.imgs[self.anim]) / 3)
and not self.toggling)
or (self.frame <= int(len(self.level.imgs[self.anim]) / 3)
and self.setting and self.toggling)
or (self.frame >= int(2 * len(self.level.imgs[self.anim]) / 3)
and not self.setting and self.toggling))
and self.send_signal):
for link in self.links:
link(self.signal[self.setting])
self.send_signal = False
worldobject.WorldObject.update(self, e, t, dt)
def draw(self, window):
self.img = self.level.imgs[self.anim][int(self.frame)]
window.blit(self.img, (self.x - 32 - self.level.camera_x,
self.y - self.img.get_size()[1] + 24
- self.level.camera_y))

237
arobotsconundrum/loader.py Normal file
View File

@@ -0,0 +1,237 @@
# 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/>.
#
# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
#
# loader.py
# --------------------
# date created : Fri Aug 10 2012
# copyright : (C) 2012 Sakse Dalum
# maintained by : Sakse Dalum <don_s@hongabar.org>
"""
A loader object.
"""
import pygame
import os
import re
class Loader(object):
def __init__(self, game, directory):
self.__dict__.update(locals())
self.imgs = {}
def load(self):
"""Load all game resources."""
l = ['ground1', 'ground2']
for o in l:
self.imgs[o] = pygame.image.load(os.path.join(
self.directory, 'tiles', '%s.png' % o))
for o in range(1, 13):
for i in range(16, 19):
self.imgs['symbol%02d-%04d' % (o, i)] = (
pygame.image.load(os.path.join(
self.directory, 'symbols', 'blocks',
'block%02d-%04d.png' % (o, i))))
for o in range(13, 18):
for i in range(16, 20):
self.imgs['symbol%02d-%04d' % (o, i)] = (
pygame.image.load(os.path.join(
self.directory, 'symbols', 'blocks',
'block%02d-%04d.png' % (o, i))))
for o in range(1, 7):
self.imgs['indoor%d' % o] = pygame.image.load(os.path.join(
self.directory, 'tiles', 'indoor', 'ground%02d.png' % o))
l = ['block1', 'block1_lifted', 'block3', '../lasertarget',
'../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))
self.imgs['lasersource_left'] = pygame.transform.flip(
self.imgs['lasersource_right'], 1, 0)
l = ['hole', 'well', 'wall', 'wall_outside', 'intro-screen',
'spacecraft']
for o in l:
self.imgs[o] = pygame.image.load(os.path.join(
self.directory, '%s.png' % o))
# Special treatment
screen_size = self.game.window.get_size()
img_size = self.imgs['intro-screen'].get_size()
factors = (float(img_size[0]) / 1920, float(img_size[1]) / 1440)
self.imgs['intro-screen'] = pygame.transform.smoothscale(
self.imgs['intro-screen'],
(int(screen_size[0]*factors[0]),
int(screen_size[1]*factors[1])))
### Moat
l = ['moat_corner_north',
'moat_corner_south',
'moat_corner_north_flip',
'moat_corner_south_flip',
'moat_end_horizontal',
'moat_horizontal',
'moat_vertical']
for o in l:
self.imgs[o] = pygame.image.load(os.path.join(
self.directory, 'moat', '%s.png' % o))
# Special treatment
self.imgs['moat_end_horizontal_flip'] = pygame.transform.flip(
self.imgs['moat_end_horizontal'], 1, 0)
### Elevator
for n, p in (('elevator', 'elevator'),
('elevator_bottom', 'bottom_corners'),
('elevator_top', 'top_corners')):
self.imgs[n] = pygame.image.load(os.path.join(
self.directory, 'elevator', p + '.png'))
# Load animations
for anim, directory in (
[('boulder_up', os.path.join('boulder', 'up')),
('boulder_down', os.path.join('boulder', 'down')),
('boulder_right', os.path.join('boulder', 'right')),
('boulder_left', os.path.join('boulder', 'right')),
('boulder_falling', os.path.join('boulder_fall')),
('lever_updown', os.path.join('lever', 'down-up')),
('lever_leftright', os.path.join('lever', 'left-right')),
('arrow_up', os.path.join('matt', 'up')),
('arrow_right', os.path.join('matt', 'right')),
('arrow_down', os.path.join('matt', 'down')),
('arrow_left', os.path.join('matt', 'right')),
('stairs', 'stairs'),
('elevating_column', 'elevating_column'),
('mirror', 'mirror'),
('door', 'door')]
):
self.imgs[anim] = []
# Find all image files for the given animation
anim_files = []
for root, dirs, files in os.walk(os.path.join(
self.directory, directory)):
for f in files:
if re.match(r"^.*\.(png)$", '/'.join([root, f])):
anim_files.append('/'.join([root, f]))
# Sort and load the files
for f in sorted(anim_files):
img = pygame.image.load(f)
# Special treatment:
if anim in ('arrow_left',):
img = pygame.transform.flip(img, 1, 0)
if (anim in ('arrow_left', 'arrow_right',
'arrow_up', 'arrow_down')
and len(self.imgs[anim]) >= 16):
self.imgs[anim + '_lifted'] = img
continue
self.imgs[anim].append(img)
####### PLAYER #######
for anim, directory in (
(('idle_up', os.path.join('robot_idle', 'up')),
('idle_down', os.path.join('robot_idle', 'down')),
('idle_right', os.path.join('robot_idle', 'right')),
('idle_left', os.path.join('robot_idle', 'right')),
('carry_up', os.path.join('robot_carry', 'up')),
('carry_down', os.path.join('robot_carry', 'down')),
('carry_right', os.path.join('robot_carry', 'right')),
('carry_left', os.path.join('robot_carry', 'right')),
# Lever
('lever_down_right', os.path.join('robot_lever', 'horizontal',
'down_left')),
('lever_down_left', os.path.join('robot_lever', 'horizontal',
'down_left')),
('lever_down_up', os.path.join('robot_lever', 'vertical',
'down_down')),
('lever_down_down', os.path.join('robot_lever', 'vertical',
'down_down')),
('lever_up_right', os.path.join('robot_lever', 'horizontal',
'up_right')),
('lever_up_left', os.path.join('robot_lever', 'horizontal',
'up_right')),
('lever_up_up', os.path.join('robot_lever', 'vertical',
'up_up')),
('lever_up_down', os.path.join('robot_lever', 'vertical',
'up_up')),
('lever_right_right', os.path.join('robot_lever', 'horizontal',
'right_right')),
('lever_right_left', os.path.join('robot_lever', 'horizontal',
'right_right')),
('lever_right_up', os.path.join('robot_lever', 'vertical',
'left_up')),
('lever_right_down', os.path.join('robot_lever', 'vertical',
'right_down')),
('lever_left_right', os.path.join('robot_lever', 'horizontal',
'right_right')),
('lever_left_left', os.path.join('robot_lever', 'horizontal',
'left_left')),
('lever_left_up', os.path.join('robot_lever', 'vertical',
'left_up')),
('lever_left_down', os.path.join('robot_lever', 'vertical',
'right_down'))
)):
self.imgs['robot_' + anim] = []
# Find all image files for the given animation
anim_files = []
for root, dirs, files in os.walk(os.path.join(
self.directory, directory)):
for f in files:
if re.match(r"^.*\.(png)$", '/'.join([root, f])):
anim_files.append('/'.join([root, f]))
# Sort and load the files
for f in sorted(anim_files):
img = pygame.image.load(f)
# Special treatment:
if anim in ['idle_left', 'carry_left',
'lever_down_right', 'lever_left_down',
'lever_up_left', 'lever_right_up',
'lever_left_right']:
img = pygame.transform.flip(img, 1, 0)
if anim in ['lever_right_left', 'lever_left_right',
'lever_down_up', 'lever_up_down']:
self.imgs['robot_' + anim].insert(0, img)
else:
self.imgs['robot_' + anim].append(img)

View File

View File

@@ -0,0 +1,141 @@
#!/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/>.
#
# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
#
# colourboxes.py
# --------------------
# date created : Wed Aug 8 2012
# copyright : (C) 2012 Niels G. W. Serup
# maintained by : Niels G. W. Serup <ngws@metanohi.name>
"""
Colour boxes.
"""
from __future__ import print_function
import random
import itertools
def generate_colour_boxes(nwells, nboxes):
"""
Generate colour boxes that can be used to make all wells white. None of the
generated colour boxes are white.
Arguments:
nwells -- number of wells
nboxes -- maximum number of boxes needed to make all wells white.
Return [[(r, g, b)]]
where r : 0|1, g : 0|1, b : 0|1
"""
nbits = nwells * 3
data = [[0 for _ in range(nboxes)] for _ in range(nbits)]
def insert_1(x):
t = random.randrange(0, nboxes)
x0, x1 = _get_oxs(x)
for y in range(t, nboxes) + range(0, t):
if data[x][y] == 0 and not (data[x0][y] == 1 and data[x1][y] == 1):
data[x][y] = 1
break
else:
raise Exception("Cannot maintain no 111s invariant.")
def insert_two_1(x):
t = random.randrange(0, nboxes)
x0, x1 = _get_oxs(x)
if len(list(filter(lambda y: data[x][y] == 0 and not (data[x0][y] == 1 and data[x1][y] == 1),
range(nboxes)))) < 2:
return
for _ in range(2):
for y in range(t, nboxes) + range(0, t):
if data[x][y] == 0 and not (data[x0][y] == 1 and data[x1][y] == 1):
data[x][y] = 1
break
for x in range(len(data)):
insert_1(x)
for x in range(len(data)):
for _ in range(random.randrange(0, (nboxes + 1) / 2)):
insert_two_1(x)
boxes = []
for y in range(nboxes):
box = []
boxes.append(box)
for x in range(0, nbits, 3):
r = data[x][y]
g = data[x + 1][y]
b = data[x + 2][y]
box.append((r, g, b))
return boxes
def _get_oxs(x):
return (x + 1, x + 2) if x % 3 == 0 \
else (x - 1, x + 1) if x % 3 == 1 \
else (x - 2, x - 1)
def generate_random_box(nwells, min_nonblacks=0):
"""
Generate a box that triggers nwells wells, with random colors except white
(111).
Arguments:
min_nonblacks -- minimum number of well colours in a box required not to be
black.
"""
def gen_wc():
wc = [random.choice((0, 1)) for i in range(3)]
if all(b == 1 for b in wc):
wc[random.randrange(3)] = 0
return wc
def gen_wc_nonblack():
wc = gen_wc()
if all(b == 0 for b in wc):
wc[random.randrange(3)] = 1
return wc
colours = [tuple(gen_wc()) for _ in range(nwells)]
nonblack = lambda t: any(n == 1 for n in t)
missing_nonblacks = min_nonblacks - len(list(filter(nonblack, colours)))
i = 0
while missing_nonblacks > 0:
if not nonblack(colours[i]):
colours[i] = gen_wc_nonblack()
missing_nonblacks -= 1
i += 1
return colours
def get_colours(boxes):
colours = []
for i in range(len(boxes[0])):
r, g, b = boxes[0][i]
for j in range(1, len(boxes)):
r1, g1, b1 = boxes[j][i]
r ^= r1
g ^= g1
b ^= b1
colours.append((r, g, b))
return colours
def makes_all_wells_white(boxes):
"""
Determine if the boxes make all wells white when XOR'ed together.
"""
return all(c == 1 for c in itertools.chain(*get_colours(boxes)))

View File

@@ -0,0 +1,87 @@
#!/usr/bin/env python
# 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/>.
#
# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
#
# direction.py
# --------------------
# date created : Tue Aug 7 2012
# copyright : (C) 2012 Niels G. W. Serup
# maintained by : Niels G. W. Serup <ngws@metanohi.name>
"""Directions."""
class Direction(object):
@staticmethod
def next_pos(pos):
raise NotImplementedError
@staticmethod
def to_str():
raise NotImplementedError
@staticmethod
def from_sakse(p):
return {(0, -1): Up,
(0, 1): Down,
(-1, 0): Left,
(1, 0): Right}[p]
class Up(Direction):
@staticmethod
def next_pos(pos):
x, y = pos
return x, y - 1
@staticmethod
def to_str():
return 'up'
class Right(Direction):
@staticmethod
def next_pos(pos):
x, y = pos
return x + 1, y
@staticmethod
def to_str():
return 'right'
class Down(Direction):
@staticmethod
def next_pos(pos):
x, y = pos
return x, y + 1
@staticmethod
def to_str():
return 'down'
class Left(Direction):
@staticmethod
def next_pos(pos):
x, y = pos
return x - 1, y
@staticmethod
def to_str():
return 'left'
all_directions = [Up, Right, Down, Left]
_sp = lambda n: lambda d: all_directions[(all_directions.index(d) + n) % 4]
succ, pred = _sp(1), _sp(-1)
isDirection = all_directions.__contains__

View File

@@ -0,0 +1,218 @@
#!/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/>.
#
# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
#
# lasermirror.py
# --------------------
# date created : Tue Aug 7 2012
# copyright : (C) 2012 Niels G. W. Serup
# maintained by : Niels G. W. Serup <ngws@metanohi.name>
"""
Management of lasers in rooms of mirrors and targets.
"""
from __future__ import print_function
import math
import random
import itertools
from arobotsconundrum.logic.direction import *
import arobotsconundrum.logic.rollingstone as rstone
from arobotsconundrum.logic.rollingstone import Blocker
import arobotsconundrum.misc as misc
class MirrorLeft(object):
pass
class MirrorRight(object):
pass
class Lever(object):
pass
class Target(object):
pass
class Source(object):
def __init__(self, direction):
self.__dict__.update(locals())
def generate_simple_playfield(nmirrors):
"""
Generate a completable 17x17 playfield where:
* there are four laser sources, one in each corner
+ the one in the upper left corner (0, 0) starts in (0, -1) heading down
+ the one in the upper right corner (16, 0) starts in (17, 0), heading left
+ the one in the lower right corner (16, 16) starts in (16, 17), heading up
+ the one in the lower left corner (0, 16) starts in (-1, 16), heading right
* there are four laser targets
* there are nmirrors mirrors
* there are nmirrors levers
* all levers are at the wall
Return playfield : {(x, y):
Target | MirrorLeft | MirrorRight | rstone.Blocker | Lever}
"""
width, height = 17, 17
playfield = {(0, 0): Source(Down),
(width - 1, 0): Source(Left),
(width - 1, height - 1): Source(Up),
(0, height - 1): Source(Right),
(6, 6): Target,
(10, 6): Target,
(6, 10): Target,
(10, 10): Target,
(7, 7): rstone.Blocker,
(7, 8): rstone.Blocker,
(7, 9): rstone.Blocker,
(8, 7): rstone.Blocker,
(8, 8): rstone.Blocker,
(8, 9): rstone.Blocker,
(9, 7): rstone.Blocker,
(9, 8): rstone.Blocker,
(9, 9): rstone.Blocker,
}
succs = lambda d: d
source_direc = Up
nlevers = nmirrors
for missing in range(4, 0, -1):
nm = nmirrors / missing
nmirrors -= nm
stone_playfield, _ = rstone.generate_simple_playfield(
7, 7, nm, 0, False, False)
for pos, direc in stone_playfield.items():
playfield[_adjust(source_direc, width - 1, height - 1, *pos)] \
= random.choice((MirrorLeft, MirrorRight))
succs = (lambda s: lambda d: succ(s(d)))(succs)
source_direc = succ(source_direc)
occup = set(playfield.keys())
is_empty = lambda x, y: (x, y) not in occup
ok_a = lambda y: is_empty(1, y)
ok_b = lambda y: is_empty(width - 2, y)
ok_c = lambda x: is_empty(x, 1)
ok_d = lambda x: is_empty(x, height - 2)
no_block = lambda x, y: \
all((ok_a(y) if x == 0 else True,
ok_b(y) if x == width - 1 else True,
ok_c(x) if y == 0 else True,
ok_d(x) if y == height - 1 else True))
emptys = set([(0, y) for y in filter(ok_a, range(height))]
+ [(width - 1, y) for y in filter(ok_b, range(height))]
+ [(x, 0) for x in filter(ok_c, range(width))]
+ [(x, height - 1) for x in filter(ok_d, range(width))]) - occup
emptys_full = set(itertools.product(range(width), range(height))) - occup
emptys = list(emptys)
random.shuffle(emptys)
emptys = set(emptys)
is_empty = lambda x, y: (x, y) in emptys_full
levers = []
for _ in range(nlevers):
while True:
pos = next(iter(emptys))
emptys.remove(pos)
emptys_full.remove(pos)
if no_block(*pos):
playfield[pos] = Lever
if not all(no_block(*pos) for pos in levers):
del playfield[pos]
else:
levers.append(pos)
break
return playfield
def _adjust(source_direc, w, h, x, y):
return {
Up: lambda x, y: (x, y),
Right: lambda x, y: (w - y, x),
Down: lambda x, y: (w - x, h - y),
Left: lambda x, y: (y, h - x),
}[source_direc](x, y)
def generate_lasers(playfield):
"""
Generate laser paths.
Return [((x, y), direction), ...]
"""
width, height = 17, 17
sources = ((pos, obj.direction) for pos, obj
in filter(lambda posobj: isinstance(posobj[1], Source),
playfield.items()))
lasers, lasers_flat = [], set()
def add(start, end):
t = (min(start, end), max(start, end))
if not t in lasers_flat:
laser.append(t)
lasers_flat.add(t)
for start, direc in sources:
end = start
laser = []
lasers.append(laser)
while True:
cur = playfield.get(end)
if cur is Target:
add(start, end)
break
if cur is Blocker:
add(start, end)
break
if cur in (MirrorLeft, MirrorRight):
if (start, end) in ((start, end) for (start, end), direc in lasers_flat):
break
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:
add(start, new_end)
break
end = new_end
return lasers
def _mirror_new_direc(mirror_type, old_direc):
return {Down: (Left, Right),
Left: (Down, Up),
Up: (Right, Left),
Right: (Up, Down)}[old_direc][
0 if mirror_type is MirrorLeft else 1]
def print_playfield(playfield, width, height, hide_directions=False):
text = [['·' for _ in range(width)] for _ in range(height)]
for (x, y), val in playfield.items():
if isDirection(val) and hide_directions:
continue
text[y][x] = '%' if val is rstone.Blocker \
else 'x' if val is Mirror \
else 'L' if val is Lever \
else 'T' if val is Target else 'N'
print('\n'.join(''.join(line) for line in text))

View File

@@ -0,0 +1,223 @@
#!/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/>.
#
# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
#
# rollingstone.py
# --------------------
# date created : Tue Aug 7 2012
# copyright : (C) 2012 Niels G. W. Serup
# maintained by : Niels G. W. Serup <ngws@metanohi.name>
"""
Logic for a rolling stone on a playfield of movement-stopping stones and
direction-changing turns. Also has a pseudo-random playfield generator.
"""
from __future__ import print_function
import math
import random
import itertools
from arobotsconundrum.logic.direction import *
import arobotsconundrum.misc as misc
class Blocker(object):
pass
def step(playfield, width, height, old_pos, direc):
"""
Return a new (position, direction) tuple based on the location on the
playfield.
"""
pos = direc.next_pos(old_pos)
x, y = pos
if playfield.get(pos) is Blocker or x < 0 or x >= width \
or y < 0 or y >= height:
pos = old_pos
elif isDirection(playfield.get(pos)):
direc = playfield[pos]
return pos, direc
def reaches_goal(playfield, width, height, max_steps, start_pos, goal_pos):
"""
Determine if the rolling stone reaches the goal within max_steps steps.
playfield[start_pos] must contain either a Turn(Down) or a Turn(Right)
object, or the rolling stone will not roll.
"""
pos = start_pos
direc = playfield[pos]
for _ in range(max_steps):
new_pos, new_direc = step(playfield, width, height, pos, direc)
if new_pos == goal_pos:
return True
if new_pos == pos:
return False
pos, direc = new_pos, new_direc
return False
def generate_simple_playfield(width, height, nturns, nstones,
do_transpose=None, start_inside=True):
"""
Generate a completable playfield where:
* the starting position is in the upper left corner
* the goal is in the lower right corner
* the playfield is completable in nturns or less
* the playfield has at most nstones stones
Return (playfield : {(x, y): Direction | Blocker},
steps : int)
where (x, y) : (int, int)
The returned playfield contains Direction objects which can be used with
the step function to move towards the goal. The solution denoted by the
Direction objects is not necessarily the only solution.
'steps' is the number of steps used by the generated solution. It is not
necessarily the lowest number of steps the playfield can be completed in.
This generator favours increasing the turn density the closer to the goal
it gets.
"""
min_width, min_height = _min_play_size(nturns)
if width < min_width or height < min_height:
nturns = min(2 * (width - 1), 2 * (height - 1) - 1)
min_width, min_height = _min_play_size(nturns)
if do_transpose is None:
do_transpose = random.choice((True, False))
if do_transpose:
width, height = height, width
min_width, min_height = min_height, min_width
turns = [((0, 0), None)]
stones = []
x, y = (0, 0)
not_allowed_y = []
offset_x = 0
while True:
missing = nturns - len(turns) + 1
if missing == 1:
turns[-1] = (turns[-1][0], Down)
turns.append(((x, height - 1), Right))
break
elif missing == 0:
turns[-1] = ((width - 1, turns[-1][0][1]), Down)
break
else:
allowed = set(range(0, height)) - set(not_allowed_y)
if missing <= 3:
allowed -= set((height - 1,))
if missing == nturns:
allowed -= set((0,))
y1 = random.choice(list(allowed))
turns[-1] = (turns[-1][0], Down if y1 > y else Up)
not_allowed_y.append(y1)
if len(not_allowed_y) == 3:
del not_allowed_y[0]
turns.append(((x, y1), Right))
x1p = random.randint(0, width - min_width - offset_x)
offset_x += x1p
x1 = x + x1p + 1
turns.append(((x1, y1), None))
x, y = x1, y1
turns.append(((width - 1, height - 1), None))
if not start_inside:
del turns[0]
if do_transpose:
turns[:] = [((y, x), {
Down: Right,
Right: Down,
Up: Left,
}.get(d)) for ((x, y), d) in turns]
width, height = height, width
min_width, min_height = min_height, min_width
used_fields = _fields_from_turns(turns)
playfield = {}
del turns[-1]
for p, d in turns:
playfield[p] = d
for pos in misc.pick_random_elements(
list(set(itertools.product(range(width), range(height)))
- set(used_fields)), nstones):
playfield[pos] = Blocker
return playfield, len(used_fields) - 1
def generate_simple_unsolved_solvable_playfield(*args, **kwds):
"""
Return a tuple of a playfield without direction objects, its number of
steps, and a list of the direction objects.
"""
playfield, steps = generate_simple_playfield(*args, **kwds)
new_playfield, directions = {}, []
for pos, val in playfield.items():
if val is Blocker:
new_playfield[pos] = val
else:
directions.append(val)
return new_playfield, steps, directions
def generate_simple_unsolved_solvable_extra(*args, **kwds):
"""
Do the same as generate_simple_unsolved_solvable, but throw in some copies
of the direction object not returned by that function. You probably want to
use this in your game.
"""
playfield, steps, directions = generate_simple_unsolved_solvable_playfield(
*args, **kwds)
missing_dir = list(set(all_directions) - set(directions))[0]
return playfield, steps, directions + \
[missing_dir] * (len(directions) / 3) + [Right]
def print_playfield(playfield, width, height, hide_directions):
text = [['·' for _ in range(width)] for _ in range(height)]
for (x, y), val in playfield.items():
if isDirection(val) and hide_directions:
continue
text[y][x] = '%' if val is Blocker else repr(val).rsplit('.', 1)[1][0] \
if isDirection(val) else 'G'
print('\n'.join(''.join(line) for line in text))
def _cells_upto(fields, start, direc, end):
(x0, y0), (x2, y2) = start, end
if direc in (Up, Down):
t = -1 if direc == Up else 1
for y in range(y0 + t, y2 + t, t):
fields.append((x0, y))
else:
t = -1 if direc == Left else 1
for x in range(x0 + t, x2 + t, t):
fields.append((x, y0))
def _fields_from_turns(turns):
fields = [(0, 0)]
prev_pos, prev_direc = turns[0]
for (pos, direc) in turns:
_cells_upto(fields, prev_pos, prev_direc, pos)
prev_pos, prev_direc = pos, direc
return fields
def _min_play_size(nturns):
return (int(math.ceil(nturns / 2.0)) + 1,
int(math.ceil((nturns + 1) / 2.0)) + 1)

View File

@@ -0,0 +1,144 @@
#!/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/>.
#
# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
#
# teleportermap.py
# --------------------
# date created : Mon Aug 13 2012
# copyright : (C) 2012 Niels G. W. Serup
# maintained by : Niels G. W. Serup <ngws@metanohi.name>
"""
Logic for a map with invisible teleporters.
"""
from __future__ import print_function
import math
import random
import itertools
from arobotsconundrum.logic.direction import *
import arobotsconundrum.misc as misc
class Empty(object):
pass
class Forbidden(object):
pass
class StrictlyForbidden(object):
pass
class Visited(object):
pass
def generate_teleporter_map(width, height):
m = [[Empty for _ in range(height)]
for _ in range(width)]
def _get(p):
if p[0] < 0 or p[0] >= width or p[1] < 0 or p[1] >= height:
return StrictlyForbidden
return m[p[0]][p[1]]
def _set(p, val):
try:
m[p[0]][p[1]] = val
except IndexError:
pass
def insert_random_point_on_line(x):
t = random.randrange(height)
for y in itertools.chain(xrange(t), xrange(t + 1, height)):
_set((x, y), Forbidden)
_set((x, t), Visited)
return (x, t)
pos = insert_random_point_on_line(width - 1)
while True:
while True:
if pos[0] == 1 and random.randint(0, height) == 0:
break
t = random.randrange(4)
found = None
alldirs = all_directions
if random.choice((True, False)):
alldirs.reverse()
for direc in alldirs[t:] + alldirs[:t]:
npos = direc.next_pos(pos)
if found is None and npos[0] != 0 and _get(npos) is Empty and (
(direc is Right and
len(filter(lambda c: c is Empty, m[pos[0]][:pos[1]] if
opos[1] > pos[1]
else m[pos[0]][pos[1] + 1:])) >= 2)
or direc is not Right):
found = npos
_set(npos, Visited)
else:
if _get(npos) is Empty:
_set(npos, Forbidden)
if found is None:
break
opos = pos
pos = found
if pos[0] == 1:
break
_set(pos, StrictlyForbidden)
for direc in all_directions:
npos = direc.next_pos(pos)
if _get(npos) is Visited:
pos = npos
break
for direc in all_directions:
npos = direc.next_pos(pos)
if _get(npos) is Forbidden:
occup_direc = succ(succ(direc))
if all(ndirec is occup_direc
or _get(ndirec.next_pos(npos)) in (Forbidden, Empty)
for ndirec in all_directions):
_set(npos, Empty)
_set((0, pos[1]), Visited)
return m
def generate_teleporter_map2(width, height):
tmap = generate_teleporter_map(width, height)
res = set()
for y in range(len(tmap[0])):
for x in range(len(tmap)):
if tmap[x][y] is Visited:
res.add((x, y))
return res
def generate_teleporter_map3(width, height):
return set(itertools.product(range(width), range(height))) \
- generate_teleporter_map2(width, height)
def print_map(tmap):
for y in range(len(tmap[0])):
for x in range(len(tmap)):
c = tmap[x][y]
print({Empty: '%',
Forbidden: '#',
StrictlyForbidden: '!',
Visited: '·'}[c], end='')
print()

View File

@@ -0,0 +1,72 @@
# 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/>.
#
# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
#
# main_menu.py
# --------------------
# date created : Tue Aug 7 2012
# copyright : (C) 2012 Sakse Dalum
# maintained by : Sakse Dalum <don_s@hongabar.org>
"""
The main menu.
"""
import os
import pygame
import level1
import game_menu
class MainMenu(object):
def __init__(self, game, img_dir):
self.__dict__.update(locals())
self.load()
def load(self):
self.background_img = pygame.image.load(os.path.join(self.img_dir,
'main_menu.png'))
self.background_img = pygame.transform.smoothscale(
self.background_img, self.game.window.get_size())
self.space_img = pygame.image.load(os.path.join(
self.img_dir,
'press_space_to_start.png'))
screen_size = self.game.window.get_size()
img_size = self.space_img.get_size()
factors = (float(img_size[0]) / 1920, float(img_size[1]) / 1280)
self.space_img = pygame.transform.smoothscale(
self.space_img,
(int(screen_size[0]*factors[0]),
int(screen_size[1]*factors[1])))
def update(self, e, t, dt):
for event in e:
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
self.game.goto_level(0)
if event.key == pygame.K_ESCAPE:
self.game.stop()
def draw(self, window):
window.blit(self.background_img, (0, 0))
screen_size = self.game.window.get_size()
window.blit(self.space_img,
(0, screen_size[1] - self.space_img.get_size()[1]))

View File

@@ -0,0 +1,69 @@
# 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/>.
#
# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
#
# mirror.py
# --------------------
# date created : Fri Aug 10 2012
# copyright : (C) 2012 Niels G. W. Serup
# maintained by : Niels G. W. Serup <ngws@metanohi.name>
"""
A generic mirror for drawing.
"""
import pygame
import worldobject
class Mirror(worldobject.WorldObject):
def __init__(self, level, x0, y0, left_up=True, 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.left_up_aim = self.left_up
self.frame = 7
self.anim_speed = 12
self._half_anim_len = len(self.level.imgs['mirror']) / 2
def rotate(self):
if self.in_rotation:
return
self.in_rotation = True
self.left_up_aim = not self.left_up_aim
self.frame = 0
def update(self, e, t, dt):
if self.in_rotation:
top = self._half_anim_len - 1
self.frame = min(self.frame + self.anim_speed * dt, top)
if self.frame == top:
self.in_rotation = False
self.left_up = not self.left_up
for link in self.links:
link(self.left_up)
worldobject.WorldObject.update(self, e, t, dt)
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.xd - 32 - self.level.camera_x,
self.yd - self.img.get_size()[1] + 24
- self.level.camera_y))

13
arobotsconundrum/misc.py Normal file
View File

@@ -0,0 +1,13 @@
from __future__ import print_function
import random
def pick_random_elements(xs, n):
for i in range(min(n, len(xs))):
i1 = random.randrange(i, len(xs))
yield xs[i1]
xs[i1] = xs[i]
def manhattan_dist(p0, p1):
x0, y0 = p0
x1, y1 = p1
return abs(x1 - x0) + abs(y1 - y0)

156
arobotsconundrum/player.py Normal file
View File

@@ -0,0 +1,156 @@
# 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/>.
#
# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
#
# player.py
# --------------------
# date created : Tue Aug 7 2012
# copyright : (C) 2012 Sakse Dalum
# maintained by : Sakse Dalum <don_s@hongabar.org>
"""
The player.
"""
import pygame
import re
import os
import worldobject
import boulder
import lever
class Player(worldobject.WorldObject):
def __init__(self, level, x, y, z=1, movable=True):
self.__dict__.update(locals())
worldobject.WorldObject.__init__(self, level, x, y,
z=z, movable=movable)
self.anim_root = 'idle'
self.anim = 'right'
self.frame = 0
self.anim_speed = 15
self.working = False
self.ignore_list.append(boulder.Boulder)
self.imgs = dict([(k[6:], v) for (k, v) in self.level.imgs.iteritems()
if k.startswith('robot_')])
def direction_as_string(self):
if self.direction == (1, 0):
return "right"
if self.direction == (-1, 0):
return "left"
if self.direction == (0, -1):
return "up"
if self.direction == (0, 1):
return "down"
def touch(self, touch_x, touch_y):
for obj in self.level.objects:
if (obj.x == self.x + touch_x * self.tile_x
and obj.y == self.y + touch_y * self.tile_y
and obj != self):
# Concerning levers
if type(obj) == lever.Lever:
if not obj.at_rest:
return
obj.use(self)
self.working = True
# List all possible combinations ...
lever_direction = (
'up' if obj.anim == 'lever_updown' and not obj.setting
else
'right' if obj.anim == 'lever_leftright'
and not obj.setting
else
'down' if obj.anim == 'lever_updown' and obj.setting
else
'left')
self.anim_root = 'lever'
self.frame = 0
self.anim = (
self.direction_as_string() + '_' + lever_direction)
else:
obj.use(self)
def update(self, e, t, dt):
if not self.working:
self.anim_root = 'carry' if self.holding else 'idle'
keys = pygame.key.get_pressed()
if keys[pygame.K_UP]:
if not self.holding and not self.is_moving:
self.direction = (0, -1)
self.anim = self.direction_as_string()
self.move(0, -1)
elif keys[pygame.K_DOWN]:
if not self.holding and not self.is_moving:
self.direction = (0, 1)
self.anim = self.direction_as_string()
self.move(0, 1)
elif keys[pygame.K_RIGHT]:
if not self.holding and not self.is_moving:
self.direction = (1, 0)
self.anim = self.direction_as_string()
self.move(1, 0)
elif keys[pygame.K_LEFT]:
if not self.holding and not self.is_moving:
self.direction = (-1, 0)
self.anim = self.direction_as_string()
self.move(-1, 0)
for event in e:
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
self.touch(*self.direction)
else:
self.working = not (self.frame == len(
self.imgs[self.anim_root + '_' + self.anim]) - 1)
if not self.working:
self.anim = self.direction_as_string()
self.anim_root = 'carry' if self.holding else 'idle'
# Update the animation
if not self.working:
self.frame = ((self.frame + self.anim_speed * dt) %
len(self.imgs[self.anim_root + '_' + self.anim]))
else:
self.frame = min(
self.frame + self.anim_speed * dt,
len(self.imgs[self.anim_root + '_' + self.anim]) - 1)
worldobject.WorldObject.update(self, e, t, dt)
def draw(self, window):
self.img = self.imgs[self.anim_root + '_' + self.anim][int(self.frame)]
self.img.set_alpha(128)
offset_x = (96 if self.anim_root == 'lever' and self.anim in
['left_up', 'left_left', 'left_down', 'left_right',
'down_right', 'up_left']
else 32)
window.blit(self.img, (self.x - offset_x - self.level.camera_x,
self.y - self.img.get_size()[1] + 24
- self.level.camera_y))

47
arobotsconundrum/tile.py Normal file
View File

@@ -0,0 +1,47 @@
# 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/>.
#
# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
#
# tile.py
# --------------------
# date created : Tue Aug 7 2012
# copyright : (C) 2012 Sakse Dalum
# maintained by : Sakse Dalum <don_s@hongabar.org>
"""
A generic tile.
"""
import worldobject
class Tile(worldobject.WorldObject):
def __init__(self, level, x, y, img):
self.__dict__.update(locals())
worldobject.WorldObject.__init__(self, level, x, y)
def draw(self, window):
window.blit(self.img, (self.x - 32 - self.level.camera_x,
self.y - self.img.get_size()[1] + 24
- self.level.camera_y))
class NaiveTile(object):
def __init__(self, x, y, img):
self.__dict__.update(locals())
def draw(self, window):
window.blit(self.img, (self.x - 32,
self.y - self.img.get_size()[1] + 24))

View File

@@ -0,0 +1,65 @@
# 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/>.
#
# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
#
# trigger.py
# --------------------
# date created : Thu Aug 9 2012
# copyright : (C) 2012 Sakse Dalum
# maintained by : Sakse Dalum <don_s@hongabar.org>
"""
A generic trigger pad.
"""
import pygame
import worldobject
class Trigger(worldobject.WorldObject):
def __init__(self, level, x, y, links, img, trigger_objs,
toggling=False, signal=[0, 1],
setting=False, blocking=False,
visible=True, no_stop=False):
self.__dict__.update(locals())
worldobject.WorldObject.__init__(self, level, x, y, z=-48,
blocking=blocking, visible=visible)
self.frame = 0
self.anim_speed = 15
def trigger(self, setting, obj):
if self.setting != setting or self.no_stop:
self.setting = setting
for link in self.links:
link(self.signal[setting])
def update(self, e, t, dt):
for obj in self.trigger_objs:
if self.x == obj.x and self.y == obj.y:
self.trigger(True, obj)
break
if self.toggling:
self.trigger(False, obj)
worldobject.WorldObject.update(self, e, t, dt)
def draw(self, window):
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))

View File

@@ -0,0 +1,154 @@
# 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/>.
#
# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
#
# worldobject.py
# --------------------
# date created : Tue Aug 7 2012
# copyright : (C) 2012 Sakse Dalum
# maintained by : Sakse Dalum <don_s@hongabar.org>
"""
A generic world object.
"""
import pygame
import numpy
import copy
class WorldObject(object):
def __init__(self, level, x, y, z=0, z_px=0, direction=(1, 0), speed=4,
tile_x=64, tile_y=48,
movable=False, blocking=True, is_moving=False, visible=True,
blit_area=None):
self.__dict__.update(locals())
self.init_x = self.move_x = self.x = x - (x % self.tile_x)
self.init_y = self.move_y = self.y = y - (y % self.tile_y)
self.init_direction = self.move_direction = self.direction
self.holding = None
self.holder = None
self.ignore_list = [WithBackground]
self.is_currently_opaque = True
if hasattr(self, 'img'):
self.img = copy.copy(self.img)
def set_pos(self, x, y):
self.move_x = self.x = x - (x % self.tile_x)
self.move_y = self.y = y - (y % self.tile_y)
def set_init_pos(self):
self.init_x, self.init_y = self.x, self.y
self.init_direction = self.direction
def reset_pos(self):
self.x, self.y = self.move_x, self.move_y = self.init_x, self.init_y
self.direction = self.init_direction
def share_tile(self, obj_type):
for obj in self.level.objects:
if (obj.x - (obj.x % self.tile_x) == self.x - (self.x % self.tile_x)
and obj.y - (obj.y % self.tile_y) == self.y - (self.y
% self.tile_y)
and obj is not self and isinstance(obj, obj_type)):
return obj
return None
def check_move(self, move_x, move_y):
if self.move_x == self.x and self.move_y == self.y and self.movable:
for obj in self.level.objects:
if (obj.x == self.x + move_x * self.tile_x
and obj.y == self.y + move_y * self.tile_y
and obj is not self and obj is not self.holder
and obj is not self.holding and obj.blocking
and type(obj) not in self.ignore_list):
return False
return True
return False
def move(self, move_x, move_y):
if self.check_move(move_x, move_y):
if self.holding:
if not self.holding.check_move(move_x, move_y):
return False
self.move_x += move_x * self.tile_x
self.move_y += move_y * self.tile_y
if self.holding:
self.holding.move(move_x, move_y)
self.move_direction = self.move_x, self.move_y
return True
return False
def use(self, obj):
pass
def activate(self, setting):
pass
def update_alpha(self):
pass
def set_alpha(self, value):
"""
Set the relative translucency of the per-pixel-alpha image.
Value between 0 - 1, where 0 is completely transparent and 1 is
completely opaque.
"""
if hasattr(self, 'img') and hasattr(self, 'orig_alpha'):
alpha = pygame.surfarray.pixels_alpha(self.img)
alpha[:] = (self.orig_alpha * value).astype(numpy.uint8)
def update(self, e, t, dt):
if self.x > self.move_x:
self.x -= min(self.speed * dt * self.tile_x,
abs(self.x - self.move_x))
if self.x < self.move_x:
self.x += min(self.speed * dt * self.tile_x,
abs(self.x - self.move_x))
if self.y > self.move_y:
self.y -= min(self.speed * dt * self.tile_y,
abs(self.y - self.move_y))
if self.y < self.move_y:
self.y += min(self.speed * dt * self.tile_y,
abs(self.y - self.move_y))
self.is_moving = self.x != self.move_x or self.y != self.move_y
self.x, self.y = int(self.x), int(self.y)
class WithBackground(WorldObject):
def __init__(self, level, img, x, y, z_px=0, blit_area=None):
self.__dict__.update(locals())
WorldObject.__init__(self, level, x, y, z_px=z_px, blit_area=blit_area)
def draw(self, window):
window.blit(self.img, (self.x - 32 - self.level.camera_x,
self.y - self.img.get_size()[1] + 24
- self.level.camera_y + self.z_px),
self.blit_area)