a-robots-conundrum/robotgame/level1.py

601 lines
24 KiB
Python

# This file is part of ROBOTGAME
#
# ROBOTGAME 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.
#
# ROBOTGAME 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
# ROBOTGAME. 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']))
self.player.set_pos(64 * 15, 48 * 30)
self.player.set_init_pos()
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(task5_pos[0] - 64 * (x - 1),
task5_pos[1] - 48 * y,
'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)
def update(self, e, t, dt):
self.img = self.level.imgs['symbol%02d-0016' % (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-0018' % (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))