home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
GameStar Special 2004 August
/
GSSH0804.iso
/
Geschicklichkeit
/
Enigma
/
Enigma-081.exe
/
data
/
levels
/
ralf_sokoban.lua
< prev
next >
Wrap
Text File
|
2003-08-16
|
34KB
|
1,139 lines
-- This is the library used by all Sokoban levels
-- Filename: ralf_sokoban.lua
-- Copyright: (C) Mar 2003 Ralf Westram
-- Contact: amgine@reallysoft.de
-- License: GPL v2.0 or above
dofile(enigma.FindDataFile("levels/ralf.lua"))
--debug_mode()
test=0
--if (options.Difficulty==1) then
-- test=1 -- set this to 1 for st-brake testing
--end
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- characters allowed in level:
--
-- '!' space outside level
-- ' ' normalfloor
-- '_' normalfloor (unreachable after pushing all boxes onto targets)
-- 'x' normalfloor (completely unreachable)
-- '#' walls
-- 'o' boxes
-- '.' targets
-- '*' box on target
-- '@' player
-- '+' player (on target)
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
space_faces = { "fl-space", "fl-leaves", "fl-abyss", "fl-water", "fl-sahara" }
space_faces2 = { "", "", "", "", "fl-rough-blue" }
spacewalk = { 1, 1, 0, 0, 1, }
-- do NOT change the number of elements!
floor_faces = { "fl-bluegray", "fl-tigris", "fl-rough", "fl-samba", "fl-wood", "fl-himalaya", "fl-leaves" }
wall_faces = { "st-bluegray", "st-rock6", "st-rock1", "st-rock5", "st-glass", "st-rock4", "st-rock3" }
floor_faces2= { "fl-white", "fl-acblack", "fl-light", "", "", "fl-rough-red", "" }
wall_faces2 = { "st-metal", "st-likeoxydc-open", "st-blue-sand", "", "", "st-glass", "" }
box_faces = { "st-brownie", "st-marble_move", "st-wood", "st-shogun", "st-glass_move", "st-block" }
box_faces2 = { "", "st-rock3_move", "st-wood-growing", "", "st-greenbrown_move", "" }
if ((getn(floor_faces) ~= getn(floor_faces2))
or (getn(wall_faces) ~= getn(wall_faces2))
or (getn(box_faces) ~= getn(box_faces2))
or (getn(space_faces) ~= getn(space_faces2)))
then
error("wrong size in one of the face arrays")
end
door_faces = { "st-door_a", "st-door_b", "st-door_c" }
door_faces2 = { "", "", "st-blocker" }
oxyd_flavors = { "a", "b", "c", "d" }
shogunLaser = 0 -- if set to 1 then lasers are used to activate oxyds (this only works with shogun stones)
swapStyle = 1 -- if set to 0 then swap-stones are never used as boxes (otherwise swapStyle is selected randomly)
useSwapStyle = 1 -- local value of 'swapStyle'
-- Symmetry information:
xsymm = 0
ysymm = 0
psymm = 0
triggers = 0
doors = 0
lasers = 0
space_count = 0 -- count occurances of space (outside level)
triggerstate = "" -- current state of the triggers
shuffle = {} -- contains random permutation (to select trigger targets)
shuffletrig = {} -- contains random permutation (to select triggers)
position = {} -- positions of doors
positions = 0
locked_door_triggered = 0 -- one of the doors is locked until ALL targets are covered
locked_door = 1
boxx = {}
boxy = {}
function init_globals()
triggers = 0
doors = 0
lasers = 0
space_count = 0
triggerstate = ""
shuffle = {}
shuffletrig = {}
position = {}
positions = 0
locked_target_triggered = 0
locked_target = 1
setboxes=0
currtestbox=1
game_lost=0
laser_flickered=0
end
-----------------------------------------
function init_shuffle(targets)
local set = {}
for i=1,targets do set[i] = i end
local left = targets
for count=1,targets do
local sel = random(1,left)
shuffle[count] = set[sel]
set[sel] = set[left]
left = left-1
-- debug("shuffle["..count.."]="..shuffle[count])
end
local settrig = {}
for i=1,triggers do settrig[i] = i end
left = triggers
for count=1,triggers do
local sel = random(1,left)
shuffletrig[count] = settrig[sel]
settrig[sel] = settrig[left]
left = left-1
-- debug("shuffletrig["..count.."]="..shuffletrig[count])
end
end
function unflicker_laser()
if (laser_flickered~=0) then
enigma.SendMessage(enigma.GetNamedObject("laser"..laser_flickered), "on", nil)
laser_flickered=0
end
end
function flicker_lasers()
unflicker_laser()
local sel = random(1,lasers)
if (sel ~= locked_target or not strfind(triggerstate,"0")) then
local laser = enigma.GetNamedObject("laser"..sel)
local state = GetAttrib(laser,"on")
if (state==1) then
laser_flickered=sel
enigma.SendMessage(laser, "off", nil)
end
end
end
function trig_doorlaser(which,is_door,targets)
unflicker_laser()
which = shuffletrig[which]
local state = strsub(triggerstate,which,which)
if (state == "0") then state = "1" else state = "0" end
local trig_target = -1
if (targets == triggers) then -- 1 trigger <-> 1 door
trig_target = which
else
local s0,s1 = 0,0
if (state=="1") then s1 = 1 else s0 = 1 end
local w2 = which-targets
local wlow = which
while (w2 > 0) do
local state2 = strsub(triggerstate,w2,w2)
if (state2=="1") then s1 = s1+1 else s0 = s0+1 end
wlow = w2
w2 = w2-targets
end
w2 = which+targets
while (w2 <= triggers) do
local state2 = strsub(triggerstate,w2,w2)
if (state2=="1") then s1 = s1+1 else s0 = s0+1 end
w2 = w2+targets
end
debug("s0="..s0.." s1="..s1.." wlow="..wlow)
if ((s0==0) or (s0==1 and state=="0")) then
trig_target = wlow
end
end
triggerstate = strsub(triggerstate,1,which-1)..state..strsub(triggerstate,which+1)
local all_covered = not strfind(triggerstate,"0")
debug(triggerstate)
debug("trig_target="..trig_target);
if (trig_target ~= -1) then
if (trig_target ~= locked_target) then
if (is_door==1) then
local msg="open"
if (state=="0") then msg="close" end -- state is the NEW value
enigma.SendMessage(enigma.GetNamedObject("door"..shuffle[trig_target]), msg, nil)
else
local msg="on"
if (state=="0") then msg="off" end -- state is the NEW value
enigma.SendMessage(enigma.GetNamedObject("laser"..shuffle[trig_target]), msg, nil)
end
end
end
if (all_covered) then
if (locked_target_triggered==0) then
if (is_door==1) then
enigma.SendMessage(enigma.GetNamedObject("door"..shuffle[locked_target]), "open", nil)
else
enigma.SendMessage(enigma.GetNamedObject("laser"..shuffle[locked_target]), "on", nil)
end
locked_target_triggered = 1
end
else
if (locked_target_triggered==1) then
if (is_door==1) then
enigma.SendMessage(enigma.GetNamedObject("door"..shuffle[locked_target]), "close", nil)
else
enigma.SendMessage(enigma.GetNamedObject("laser"..shuffle[locked_target]), "off", nil)
end
locked_target_triggered = 0
end
end
debug("locked_target_triggered="..locked_target_triggered)
end
function trig_door(which)
trig_doorlaser(which,1,doors)
end
function trig_laser(which)
trig_doorlaser(which,0,lasers)
end
-- actors position
acx = 0
acy = 0
function set_the_actor(x,y)
acx,acy = x+.5,y+.5
end
function set_soko_trigger(x,y)
triggers = triggers + 1
local funcn = "trig_"..triggers
if (shogunLaser==1) then
dostring(funcn.." = function() trig_laser("..triggers..") end")
else
dostring(funcn.." = function() trig_door("..triggers..") end")
end
set_item(triggerface,x,y,{action="callback", target=funcn})
end
function set_actor_on_trigger(x,y)
set_the_actor(x,y)
set_soko_trigger(x,y)
end
--spacemod = 2
function set_space_floor(x,y)
set_floor(spaceface,x,y)
end
function set_glasswall(x,y)
if (mod(x,2)==mod(y,2)) then
set_floor(floorface,x,y)
else
set_space_floor(x,y)
end
set_stone("st-glass",x,y)
end
function set_spacecell(x,y)
space_count = space_count+1
set_space_floor(x,y)
end
function set_box(x,y)
setboxes=setboxes+1
local boxname = "box"..setboxes
if (test==1) then
set_stone("st-brake",x,y,{name=boxname});
elseif (boxface=="st-glass1") then
set_stone(boxface,x,y,{movable=1,name=boxname});
else
set_stone(boxface,x,y,{name=boxname});
end
end
function init(num)
init_globals()
local first = 0
if (num == -1) then
first = 1
num = 0
end
spaceface = space_faces [mod(num, getn(space_faces))+1]
spaceface2 = space_faces2[mod(num, getn(space_faces))+1]
floorface = floor_faces [mod(num, getn(floor_faces))+1]
floorface2 = floor_faces2[mod(num, getn(floor_faces))+1]
wallface = wall_faces [mod(num, getn(wall_faces))+1]
wallface2 = wall_faces2 [mod(num, getn(wall_faces))+1]
boxface = box_faces [mod(num, getn(box_faces))+1]
boxface2 = box_faces2 [mod(num, getn(box_faces))+1]
doorface = door_faces [mod(num, getn(door_faces))+1]
doorface2 = door_faces2 [mod(num, getn(door_faces))+1]
if ((spaceface2 ~= "") and (mod(floor(num/getn(space_faces)),2)==1)) then
spaceface,spaceface2 = spaceface2,spaceface
end
if ((floorface2 ~= "") and (mod(floor(num/(getn(floor_faces)*2)),2)==1)) then
floorface,floorface2 = floorface2,floorface
end
if ((wallface2 ~= "") and (mod(floor(num/getn(wall_faces)),2)==1)) then
wallface,wallface2 = wallface2,wallface
end
if ((boxface2 ~= "") and (mod(floor(num/getn(box_faces)),2)==1)) then
boxface,boxface2 = boxface2,boxface
end
if ((doorface2 ~= "") and (mod(floor(num/getn(door_faces)),2)==1)) then
doorface,doorface2 = doorface2,doorface
end
-- select style
useSwapStyle=0
if (shogunLaser==1) then -- shogunLaser has been set from level file
boxface = "st-shogun"
elseif (swapStyle==1) then -- if swapStyle is not forbidden
local n=mod(num,17)
if (n==10 or n==16) then -- 2 of 17 levels use swapStyle
useSwapStyle=1
end
elseif (swapStyle==2) then -- swapStyle forced
useSwapStyle=1
end
if (useSwapStyle==1) then
if (options.Difficulty==1) then -- easy
useSwapStyle=0
else
boxface="st-swap"
end
end
oxyd_default_flavor = oxyd_flavors[mod(num, getn(oxyd_flavors))+1]
if (spaceface == floorface) then
if (spaceface == "fl-leaves") then
spaceface = "fl-water"
else
print("warning: [ralf_sokoban.lua]: no replacement texture defined for "..spaceface)
end
end
if (boxface == "st-shogun") then triggerface = "it-shogun"
else triggerface = "it-trigger"
end
cells={}
stonecells={}
if (useSwapStyle==1) then
local grate_faces = { "st-grate1", "st-grate2", }
cells[" "] = cell{floor={face=floorface},stone={face=grate_faces[mod(floor(num/8), getn(grate_faces))+1]}}
cells["#"] = cell{floor="fl-water"}
else
cells[" "] = cell{floor={face=floorface}}
if (wallface=="st-glass") then
cells["#"] = cell{parent=set_glasswall}
else
cells["#"] = cell{parent = cells[" "], stone = {face = wallface}}
end
end
cells["!"] = cell{parent = {set_spacecell}}
cells["_"] = cells[" "]
cells["x"] = cells[" "]
cells["o"] = cells[" "]
cells["."] = cell{parent = {cells[" "], set_soko_trigger}}
cells["@"] = cell{parent = {cells[" "], set_the_actor}}
cells["*"] = cells["."]
cells["+"] = cell{parent = {cells[" "], set_actor_on_trigger}}
stonecells["!"] = cell{}
stonecells[" "] = cell{}
stonecells["_"] = cell{}
stonecells["x"] = cell{}
stonecells["#"] = cell{}
stonecells["o"] = cell{floor={face=floorface},parent={set_box}}
stonecells["."] = cell{}
stonecells["@"] = cell{}
stonecells["*"] = stonecells["o"]
stonecells["+"] = cell{}
end
-- wether there's space outside the level
spl = 0
spr = 0
spt = 0
spb = 0
spaceres = {}
spaceres[0] = '-' -- means: don't use because it's outside the level
spaceres[1] = '!'
-- look at a specifix position of the level
-- supports 1 position outside level into each direction
function peek(x,y)
if (x<1) then return spaceres[spl] end
if (x>mapw) then return spaceres[spr] end
if (y<1) then return spaceres[spt] end
if (y>maph) then return spaceres[spb] end
return strsub(level[y],x,x)
end
function rpeek(rx,ry)
return peek(rx-xlo+1,ry-ylo+1)
end
function adjacent(x1,y1,x2,y2) -- returns TRUE if the two positions are adjacent
local dx,dy = abs(x1-x2),abs(y1-y2)
return (dx==0 and dy==1) or (dx==1 and dy==0)
end
-- locations of state[] indices:
--
-- 3
-- 1 P 2
-- 4
xoff = { -1, 1, 0, 0 }
yoff = { 0, 0, -1, 1 }
function is_in_restricted_area(p)
local xd,yd = position[p][1],position[p][2]
local xo,yo = position[p][3],position[p][4]
local xf,yf = xd+(xd-xo),yd+(yd-yo)
debug("xf="..xf.." yf="..yf)
local c = rpeek(xf,yf)
if (c==' ') then return 0 end
if (c=='_') then return 1 end
return 2 -- unknown
end
--function symmetric_position(x,y,w,h)
-- if (xsymm==1) then return w-(x-xlo)+xlo,y end
-- if (ysymm==1) then return x,h-(y-ylo)+ylo end
-- if (psymm==1) then return h-(y-ylo)+xlo,x-xlo+ylo end
-- return x,y
--end
function symmetric_position(x,y,w,h)
if (xsymm==1) then return w-x+1,y end
if (ysymm==1) then return x,h-y+1 end
if (psymm==1) then return h-y+1,x end
return x,y
end
removed_indices={}
removed = 0
function rs_remove_oxyd(p,w,h)
removed_indices={}
removed = 0
local x1,y1 = position[p][1],position[p][2]
if (xsymm+ysymm+psymm) then
local symmcount = 1
if (psymm==1) then symmcount=3 end
for c=1,symmcount do
local x2,y2 = symmetric_position(x1,y1,w,h)
for q=1,positions do
if ((q~=p) and (x2==position[q][1]) and (y2==position[q][2])) then
tremove(position,q)
removed = removed+1
removed_indices[removed] = q
positions = positions-1
if (q<p) then p = p-1 end
break
end
end
x1,y1 = x2,y2
end
end
tremove(position,p)
removed = removed+1
removed_indices[removed] = p
positions = positions-1
end
maxoxyds = 999
function install_oxyds(w,h) -- uses Nat's method (see nat7.lua)
for x=1,w do
for y=1,h do
if (peek(x,y)=='#') then
local state = {}
state[1] = peek(x-1,y)
state[2] = peek(x+1,y)
state[3] = peek(x,y-1)
state[4] = peek(x,y+1)
local reachable = 0
local spacehere = 0
for n=1,4 do
if (state[n]=='!') then
if (spacehere == 0) then
spacehere = n
else
-- positions with more than 1 adjacent space should never be used as doors
-- (because you may escape there)
if (shogunLaser==0) then
spacehere = -1
else -- does not matter in shogunLaser-mode
-- ensure symmetry
if (xsymm==1 and (x==(w-x+1))) then
spacehere = n -- use oxyd in y direction
end
end
end
end
if (state[n]=='o' or state[n]==' ' or state[n]=='@' or state[n]=='_') then
if (reachable == 0) then
reachable = n
else
-- positions with more than 1 adjacent floor should never be used as doors
-- (because they change the level logic)
reachable = -1
end
end
end
if (((reachable>0) or (shogunLaser==1)) and (spacehere>0)) then
positions = positions+1
position[positions] = {x,y,x+xoff[spacehere], y+yoff[spacehere]}
end
end
end
end
-- delete adjacent oxyds and adjacent doors
-- [first those with two neighbours or more neighbours, then those with one neighbour]
for delif=2,1,-1 do
local deleted = 1
while (deleted==1) do
deleted = 0
for p=1,positions do
local xd1,yd1 = position[p][1],position[p][2]
local xo1,yo1 = position[p][3],position[p][4]
local oneighbours = 0
local dneighbours = 0
for q=1,positions do
if (p~=q) then
local xd2,yd2 = position[q][1],position[q][2]
local xo2,yo2 = position[q][3],position[q][4]
if (xo1==xo2 and yo1==yo2) then -- oxyds use same position
oneighbours = delif
break
elseif (adjacent(xo1,yo1,xo2,yo2)) then
oneighbours = oneighbours+1
if (oneighbours==delif) then break end
end
if (adjacent(xd1,yd1,xd2,yd2)) then
dneighbours = dneighbours+1
if (dneighbours==delif) then break end
end
end
end
if ((oneighbours>=delif) or (dneighbours>=delif)) then
rs_remove_oxyd(p,w,h)
deleted = 1
break
end
end
end
end
-- delete superfluous oxyds
local want_remove = 0
if (maxoxyds>triggers) then maxoxyds = triggers end
if (positions>maxoxyds) then
want_remove = positions-maxoxyds
debug("maxoxyds="..maxoxyds.." want_remove="..want_remove);
end
if (mod(positions-want_remove,2)==1) then want_remove = want_remove+1 end
debug("positions="..positions.." want_remove="..want_remove);
if ((psymm+xsymm+ysymm)>0) then
local isSymmetric = {}
local sym_count = 0
local sym_count_single = 0
local per_remove = 0
for p=1,positions do isSymmetric[p] = 0 end
if (psymm==1) then -- central symmetry
per_remove=4
for p=1,positions do
if (isSymmetric[p]==0) then
local x1,y1 = position[p][1],position[p][2]
local allFound = 1
local index={}
for r=1,3 do
local x2,y2 = symmetric_position(x1,y1,w,h)
local found = 0
for q=1,positions do
if (p~=q and position[q][1]==x2 and position[q][2]==y2) then
local inUse=0
for r2=1,r-1 do
if (index[r2]==q) then inUse=1 break end
end
if (inUse==0) then
found=1
index[r]=q
break
end
end
end
if (found==0) then allFound = 0 break end
x1,y1 = x2,y2
end
if (allFound==1) then
isSymmetric[p]=1
for r=1,3 do isSymmetric[index[r]]=1 end
sym_count = sym_count+4
local array=""
for p=1,positions do array=array..isSymmetric[p] end
debug("isSymmetric="..array);
end
end
end
else
per_remove=2
for p=1,positions do
if (isSymmetric[p]==0) then
local x1,y1 = position[p][1],position[p][2]
local x2,y2 = symmetric_position(x1,y1,w,h)
if (x1==x2 and y1==y2) then
isSymmetric[p] = 2
sym_count_single = sym_count_single+1
else
for q=1,positions do
if (p~=q and position[q][1]==x2 and position[q][2]==y2) then
isSymmetric[p] = 1
isSymmetric[q] = 1
sym_count = sym_count+2
break
end
end
end
end
end
end
local assym_count = positions-(sym_count+sym_count_single)
-- debug("positions="..positions.." sym_count="..sym_count.." sym_count_single="..sym_count_single.." assym_count="..assym_count);
-- first remove assymetric positions
if (assym_count>0 and want_remove>0) then
for p=positions,1,-1 do
if (isSymmetric[p]==0) then
tremove(position,p);
tremove(isSymmetric,p);
want_remove = want_remove-1
positions = positions-1
assym_count = assym_count-1
if (want_remove==0) then break end
end
end
end
-- then remove some symmetric positions
while (want_remove>=per_remove) do
if (sym_count<per_remove) then error("internal counter error") end
local todel = 0
for p=1,positions do -- try randomly
local q = random(1,positions)
if (isSymmetric[q]==1) then todel = q break end
end
if (todel==0) then -- if failed try by sequence
for p=1,positions do
if (isSymmetric[p]==1) then todel=p break end
end
end
if (todel>0) then -- correct isSymmetric
rs_remove_oxyd(todel,w,h)
if (removed ~= per_remove) then error("internal error: removed ~= per_remove") end
sym_count=sym_count-per_remove
want_remove = want_remove-per_remove
for r=1,removed do
tremove(isSymmetric,removed_indices[r]);
end
end
end
-- now remove single symmetric positions
while (want_remove>0 and sym_count_single>0) do
for p=positions,1,-1 do
if (isSymmetric[p]==2) then
tremove(position,p);
tremove(isSymmetric,p);
want_remove = want_remove-1
positions = positions-1
sym_count_single = sym_count_single-1
if (want_remove==0) then break end
end
end
end
end
-- remove random stones for assymetric levels (or if symmetric remove failed)
while (want_remove>0) do
local p = random(1,positions)
tremove(position,p);
want_remove = want_remove-1
positions=positions-1
end
if (mod(positions,2)==1) then -- final check for even oxyd count
tremove(position,random(1,positions))
positions = positions-1
end
-- end of oxyd removal
debug("left oxyds: "..positions);
if (positions == 0) then
error("No oxyds.")
end
-- if (maxoxyds>positions) then
-- maxoxyds = positions
-- end
-- transform to "real" coordinates
for p=1,positions do
position[p][1] = position[p][1]+xlo-1
position[p][2] = position[p][2]+ylo-1
position[p][3] = position[p][3]+xlo-1
position[p][4] = position[p][4]+ylo-1
end
-- now really set oxyds
for p=1,positions do
local xd,yd = position[p][1],position[p][2]
local xo,yo = position[p][3],position[p][4]
enigma.KillStone(xd,yd)
enigma.KillStone(xo,yo)
if (shogunLaser==1) then
lasers = lasers+1
local d
if (xd==xo) then
if (yd>yo) then d = NORTH else d = SOUTH end
else
if (xd>xo) then d = WEST else d = EAST end
end
set_stone("st-laser",xd,yd,{name="laser"..lasers, dir=d,on=0})
else
doors = doors+1
local d = "v"
if (xd==xo) then d = "h" end
set_floor(floorface,xd,yd)
set_stone(doorface,xd,yd, {name="door"..doors, type=d})
end
oxyd(xo,yo)
end
debug("Oxyds set: "..doors.." Triggers: "..triggers)
return 0
end
function correct_locked_target_position()
if (shogunLaser==1) then
locked_target=random(1,lasers)
return 1
end
while (locked_target <= doors) do
local ld = shuffle[locked_target]
local restricted = is_in_restricted_area(ld)
local xd,yd = position[ld][1],position[ld][2]
debug("[locked door] ld="..ld.." xd="..xd.." yd="..yd.." restricted="..restricted)
if (restricted == 0) then return 1 end
locked_target = locked_target+1
end
debug("Could not correct position of locked target. Not playable!");
return 0
end
function correct_multi_trigger_doors()
if (doors == triggers) then return 1 end -- each trigger has exactly one door => no problem
local multiTriggeredDoors = triggers-doors
if (multiTriggeredDoors > doors) then multiTriggeredDoors = doors end
for mtd=1,multiTriggeredDoors do
local ld = shuffle[mtd]
local restricted = is_in_restricted_area(ld)
local xd,yd = position[ld][1],position[ld][2]
debug("[multit door] ld="..ld.." xd="..xd.." yd="..yd.." restricted="..restricted)
if (restricted ~= 0) then return 0 end
end
return 1
end
function correct_target_positions()
local ok = correct_locked_target_position()
if (ok==1 and shogunLaser==0) then
ok = correct_multi_trigger_doors()
end
return ok
end
function choose_symmetry()
if (xsymm==1 and ysymm==1) then ysymm=0 end -- prefer x-symmetry
while ((xsymm+ysymm+psymm) > 1) do
local r = random(1,3)
if (r==1) then xsymm = 0
elseif (r==2) then ysymm = 0
else psymm = 0
end
end
debug("----------------------------------------");
if (xsymm==1) then debug("Symmetry: X") end
if (ysymm==1) then debug("Symmetry: Y") end
if (psymm==1) then debug("Symmetry: central") end
end
-- lost the game?
function stone_at(x,y)
-- result==0 -> no stone (or ignored)
-- result==1 -> yes
-- result==2 -> yes (but movable)
local stone = enigma.GetStone(x,y)
if (not stone) then
-- print("no stone at "..x.."/"..y);
if (useSwapStyle==1) then return 1 end
if (doorface=="st-blocker") then
local item = enigma.GetItem(x,y)
if (item and GetAttrib(item,"kind")=="it-blocker") then return 1 end
end
return 0
end
local kind = enigma.GetAttrib(stone,"kind")
--print("found stone '"..kind.."' at "..x.."/"..y.." boxface="..boxface.." wallface="..wallface)
if (kind == "st-death") then return 1 end
if (kind == boxface) then return 2 end
if (kind == doorface) then return 1 end
if (kind == "borderstone") then return 1 end
if (useSwapStyle==1) then
if (strsub(kind,1,8) == "st-grate") then return 0 end
else
if (kind == wallface) then return 1 end
if (strsub(kind,1,7)=="st-wood" and boxface=="st-wood") then return 2 end
end
print("stone '"..kind.."' was ignored!");
return 0
end
function moveable(st1,st2)
-- result==0 -> yes
-- result==1 -> no
-- result==2 -> maybe
if (st1==0) then
if (st2==0) then return 0
else return st2 end
else
if (st2==0) then
return st1
else
if (st1==1 or st2==1) then return 1
else return 2 end
end
end
end
function lost_game(x,y)
local w = stone_at(x-1,y)
local e = stone_at(x+1,y)
local h = moveable(e,w)
if (h==0) then return 0 end
local n = stone_at(x,y-1)
local s = stone_at(x,y+1)
local v = moveable(n,s)
if (v==0) then return 0 end
if (h==1 and v==1) then return 1 end
-- none is 0 and not both are 1
-- do slower but safe check
-- print("at "..x.."/"..y.." w="..w.." e="..e.." n="..n.." s="..s.." h="..h.." v="..v);
if (h==1) then -- horizontal move is imposible
-- check horizontal move for neighbor box
if (s==2) then -- theres a box south
local se,sw = stone_at(x+1,y+1),stone_at(x-1,y+1)
if (moveable(se,sw)==1) then return 1 end -- which cannot be moved east-west
end
if (n==2) then -- theres a box north
local ne,nw = stone_at(x+1,y-1),stone_at(x-1,y-1)
if (moveable(ne,nw)==1) then return 1 end -- which cannot be moved east-west
end
end
if (v==1) then -- vertical move is imposible
-- check vertical move for neighbor box
if (w==2) then -- theres a box west
local nw,sw = stone_at(x-1,y-1),stone_at(x-1,y+1)
if (moveable(nw,sw)==1) then return 1 end -- which cannot be moved north-south
end
if (e==2) then -- theres a box east
local ne,se = stone_at(x+1,y-1),stone_at(x+1,y+1)
if (moveable(ne,se)==1) then return 1 end -- which cannot be moved north-south
end
end
-- finally check for a box of 4 stones
if (w~=0) then
if (n~=0) then
if (stone_at(x-1,y-1)~=0) then return 1 end
end
if (s~=0) then
if (stone_at(x-1,y+1)~=0) then return 1 end
end
end
if (e~=0) then
if (n~=0) then
if (stone_at(x+1,y-1)~=0) then return 1 end
end
if (s~=0) then
if (stone_at(x+1,y+1)~=0) then return 1 end
end
end
return 0
end
function recheck_all_boxes()
for b=1,setboxes do
local box=enigma.GetNamedObject("box"..b)
boxx[b]=-1
boxy[b]=-1
if (box) then
local x,y = enigma.GetPos(box)
if (x~=-1 and y~=-1) then
local item = enigma.GetItem(x,y)
if (item) then
if (enigma.GetAttrib(item,"kind")==triggerface) then
-- print("no recheck for box at "..x.."/"..y);
boxx[b]=x
boxy[b]=y
end
end
end
end
end
end
skip_timer=0
do_recheck=1
function timer_cb()
if (shogunLaser==1) then
flicker_lasers()
end
if (skip_timer>0) then
skip_timer=skip_timer-1
else
local max_repeat=3 -- to avoid deadlock
local rep=1
local recheck=0
while (rep==1 and max_repeat>0) do
rep = 0
max_repeat=max_repeat-1
for b=1,setboxes do
local box=enigma.GetNamedObject("box"..b)
if (box) then
local x,y = enigma.GetPos(box)
if (x~=boxx[b] or y~=boxy[b]) then -- box position has changed
-- print("Checking pos "..x.."/"..y.." (boxx="..boxx[b]..",boxy="..boxy[b]..")");
local die = 0;
local item = enigma.GetItem(x,y)
if (not item) then die = 1
elseif (enigma.GetAttrib(item,"kind")~=triggerface) then die = 1
end
if (die==1) then
if (lost_game(x,y)==1) then
game_lost = 1
enigma.PlaySound(box,"st-magic");
enigma.KillStone(x,y);
set_stone("st-death",x,y);
-- print("kill stone at "..x.."/"..y);
rep=1
recheck=1
end
else -- box on trigger
if (lost_game(x,y)) then
recheck=1
end
end
boxx[b] = x;
boxy[b] = y;
end
-- else
-- print("box expected");
end
end
if (recheck==1) then recheck_all_boxes() end
end
end
end
function play_sokoban(level,num)
local w,h = get_map_size(level)
local tries = 80
local ok = 0
randomseed(enigma.GetTicks())
choose_symmetry()
while ((tries > 0) and (maxoxyds > 0) and (ok == 0)) do
init(num-1)
randomseed(enigma.GetTicks())
-- rs_create_world(level,cells,spacecell,spacecell)
rs_create_world(level,cells,set_spacecell,set_spacecell)
enigma.ConserveLevel = FALSE
enigma.ShowMoves = TRUE
if (xlo > 0) then
spl = 1 -- there's space at the left side
space_count = space_count + maph
end
if (ylo > 0) then
spt = 1 -- there's space at the top side
space_count = space_count + mapw
end
if ((xlo+mapw) < 20) then
spr = 1 -- there's space at the right side
space_count = space_count + maph
end
if ((ylo+maph) < 13) then
spb = 1 -- there's space at the bottom side
space_count = space_count + mapw
end
debug("Space aside: spl="..spl.." spr="..spr.." spt="..spt.." spb="..spb);
-- install oxyds
install_oxyds(w,h)
local targets=doors
if (shogunLaser==1) then targets=lasers end
init_shuffle(targets) -- mix trigger-door associations
ok = correct_target_positions()
-- ok = 1
if (ok == 0) then
debug("---------------------------------------- re-initializing..");
end
tries = tries - 1
-- if (maxoxyds > 2) then
-- maxoxyds = maxoxyds - 1
-- end
end
if (ok==0) then
error("Sorry - couldn't correct door positions. Avoiding deadlock (try again)");
end
triggerstate = strrep("0",triggers)
display.SetFollowMode(display.FOLLOW_SCROLLING)
draw_map(xlo,ylo,level,stonecells)
set_actor("ac-blackball",acx,acy,{player=0})
oxyd_shuffle()
-- change some names for game_lost
if (boxface=="st-wood-growing") then boxface="st-wood" end
if (shogunLaser==1) then doorface="st-laser" end
-- activate callback:
if (test==0) then
skip_timer=3
for b=1,setboxes do
boxx[b]=-1
boxy[b]=-1
end
set_stone("st-timer", 0,0, {action="callback", target="timer_cb", interval=0.3, invisible=1})
end
end