------------------------------------------------------------------------
-- Copyright (C) 2002,2003,2004,2005 Daniel Heck
--
-- This program 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 2
-- of the License, or (at your option) any later version.
--
-- This program 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 this program; if not, write to the Free Software Foundation, Inc.,
-- 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
--
-- $Id: init.lua,v 1.31 2004/05/01 16:07:58 dheck Exp $
------------------------------------------------------------------------
TRUE = 1
FALSE = 0
EASY = 1
HARD = 2
difficult = options.Difficulty==2
function Tick (deltatime)
-- do nothing by default
end
----------------------
-- Global variables --
----------------------
level_width = 0
level_height = 0
oxyd_default_flavor = "b" -- Default flavor for oxyd stones.
oxyd_count = 0
oxyd_current_color = 0
DefaultAttribs = {}
---------------------
-- Helper routines --
---------------------
function PrintTable(t)
for i,v in t do
if type(v)=="table" then
print (i.." - "..v[1]..","..v[2])
else
print (i.." - "..v)
end
end
end
function Require(filename)
dofile(enigma.FindDataFile(filename))
end
----------------------------------
-- Routines for creating levels --
----------------------------------
function CreateWorld(w, h)
oxyd_default_flavor = "b"
oxyd_count = 0
oxyd_current_color = 0
DefaultAttribs = {}
level_width = w
level_height = h
world.Resize(w, h)
end
function SetDefaultAttribs(objname, attribs)
local list = DefaultAttribs[objname]
if list then
for key,val in attribs do list[key] = val end
else
DefaultAttribs[objname]= attribs
end
end
function SetAttrib(obj, key,val)
if key == "name" then
enigma.NameObject(obj, val)
end
enigma.SetAttrib(obj, key, val)
end
GetAttrib = enigma.GetAttrib
GetKind = enigma.GetKind
function set_attribs(obj, attrs)
if not attrs then return end
for key,val in attrs do
set_attrib(obj, key, val)
end
end
function MakeObject(name, attrs)
local obj = enigma.MakeObject(name)
local a=DefaultAttribs[name]
if a then set_attribs(obj, a) end
set_attribs(obj, attrs)
return obj
end
function set_floor(name, x, y, attrs)
local obj = make_object(name, attrs)
enigma.SetFloor(x,y,obj)
return obj
end
function set_item(name, x, y, attrs)
local obj = make_object(name, attrs)
enigma.SetItem(x,y,obj)
return obj
end
function set_stone(name, x,y, attrs)
local obj=make_object(name, attrs)
enigma.SetStone(x,y,obj)
return obj
end
kill_stone = enigma.KillStone
kill_item = enigma.KillItem
function SendMessage (target, msg, arg)
if type(target) == "string" then
enigma.SendMessage (enigma.GetNamedObject (target), msg, arg)
else
enigma.SendMessage (target, msg, arg)
end
end
function SetAttribs (target, attribs)
if type (target) == "string" then
set_attribs(enigma.GetNamedObject(target), attribs)
else
set_attribs(target, attribs)
end
end
function AddRubberBand(obj1,obj2, strength,length, minlen)
enigma.AddRubberBand(obj1,obj2, strength or 10, length or 2, minlen or 0)
end
function fill_floor(name, x0,y0, w,h)
if x0 == nil then x0 = 0 end
if y0 == nil then y0 = 0 end
if w == nil then w = level_width end
if h == nil then h = level_height end
for y=y0,y0+h-1 do
for x=x0,x0+w-1 do
set_floor(name, x, y)
end
end
end
function fill_items(name, x0,y0,w,h)
for y=y0,y0+h-1 do
for x=x0,x0+w-1 do set_item(name, x, y) end
end
end
function fill_stones(name, x0,y0, w,h)
for y=y0,y0+h-1 do
for x=x0,x0+w-1 do set_stone(name, x, y) end
end
end
function draw_floor(name, xy0, xystep, n, attrs)
local x,y = xy0[1],xy0[2]
for i=1,n do
set_floor(name, x, y, attrs)
x = x+xystep[1]
y = y+xystep[2]
end
end
function draw_checkerboard_floor (name1, name2, x, y, w, h, attrs)
for i=1,h do
for j=1,w do
if mod(i,2) == mod(j,2) then
set_floor (name1, x+j-1, y+i-1, attrs )
else
set_floor (name2, x+j-1, y+i-1, attrs )
end
end
end
end
function draw_items(name, xy0, xystep, n, attrs)
local x,y = xy0[1],xy0[2]
for i=1,n do
set_item(name, x, y, attrs)
x = x+xystep[1]
y = y+xystep[2]
end
end
function draw_stones(name, xy0, xystep, n, attrs)
local x,y = xy0[1],xy0[2]
for i=1,n do
set_stone(name, x, y, attrs)
x = x+xystep[1]
y = y+xystep[2]
end
end
function draw_border(stonename, x0, y0, w, h)
if x0 == nil then x0 = 0 end
if y0 == nil then y0 = 0 end
if w == nil then w = level_width end
if h == nil then h = level_height end
draw_stones(stonename, {x0,y0}, {1,0}, w)
draw_stones(stonename, {x0,y0+h-1},{1,0}, w)
draw_stones(stonename, {x0,y0}, {0,1}, h)
draw_stones(stonename, {x0+w-1,y0},{0,1}, h)
end
function set_stones(name, poslist, attrs)
for i,xy in poslist do
set_stone(name, xy[1], xy[2], attrs)
end
end
function SetActor(name, x, y, attrs)
local obj=make_object(name, attrs)
enigma.SetActor(x, y,obj)
return obj
end
---------------------------------------------
-- Creation of particular kinds of objects --
---------------------------------------------
function fakeoxyd(x,y)
return set_stone("st-fakeoxyd",x,y)
end
-- Create an oxyd stone with the current default flavor.
function oxyd(x, y, flavor, color)
local f = flavor or oxyd_default_flavor
local c = color or oxyd_current_color
local a = {flavor=f, color=""..c}
local obj = set_stone("st-oxyd", x, y, a)
-- if "color" argument not provided, use next available color
if not color then
oxyd_count = oxyd_count +1
if oxyd_count == 2 then
oxyd_count = 0
oxyd_current_color = oxyd_current_color + 1
end
end
return obj
end
-- Shuffle the colors of all oxyd stones in the current landscape.
function oxyd_shuffle()
enigma.SendMessage(enigma.GetObjectTemplate("st-oxyd"), "shuffle", nil)
end
-- Close all oxyd stones in the current landscape
function oxyd_closeall()
enigma.SendMessage(enigma.GetObjectTemplate("st-oxyd"), "closeall", nil)
end
function oneway(x,y,orient)
return set_stone("st-oneway",x,y,{orientation=orient})
end
function laser(x,y,is_on,dir)
return set_stone("st-laser",x,y,{on=is_on, dir=dir})
end
function mirrorp(x,y,movable, transp,orient)
return set_stone("st-pmirror", x, y, {movable=movable, transparent=transp,
orientation=orient})
end
function mirror3(x,y,movable, transp, orient)
return set_stone("st-3mirror", x, y, {movable=movable, transparent=transp,
orientation = orient})
end
PUZ_0000=1 -- hollow
PUZ_0001=2 -- w
PUZ_0010=3 -- s
PUZ_0011=4 -- sw
PUZ_0100=5 -- e
PUZ_0101=6 -- ew
PUZ_0110=7 -- es
PUZ_0111=8 -- esw
PUZ_1000=9 -- n
PUZ_1001=10 -- nw
PUZ_1010=11 -- ns
PUZ_1011=12 -- nsw
PUZ_1100=13 -- ne
PUZ_1101=14 -- new
PUZ_1110=15 -- nes
PUZ_1111=16 -- nesw
-- functions using puzzle-style tiles:
function puzzle(x, y, conn)
return set_stone("st-puzzle", x,y, {connections=conn})
end
function puzzle2(x, y, conn)
return set_stone("st-puzzle", x,y, {connections=conn,oxyd=1})
end
function bigbrick(x, y, conn)
return set_stone("st-bigbrick",x,y,{connections=conn})
end
--
function switch(x,y,target,action)
return set_stone("st-switch", x,y, {target=target, action=action})
end
function abyss(x,y) set_floor("fl-abyss",x,y) end
-----------
-- ITEMS --
-----------
function hollow(x,y) set_item("it-hollow", x,y) end
function Document(x,y,t) set_item("it-document", x, y, {text=t}) end
function hammer(x,y) set_item("it-hammer",x,y) end
function dynamite(x,y) set_item("it-dynamite",x,y) end
function bomb(x,y) set_item("it-blackbomb",x,y) end
function keya(x,y) set_item("it-key", x,y, {keycode=1.0}) end
function keyb(x,y) set_item("it-key", x,y, {keycode=2.0}) end
function keyc(x,y) set_item("it-key", x,y, {keycode=3.0}) end
function shogundot1(x,y,attrs) set_item("it-shogun-s", x, y, attrs) end
function shogundot2(x,y,attrs) set_item("it-shogun-m", x, y, attrs) end
function shogundot3(x,y,attrs) set_item("it-shogun-l", x, y, attrs) end
function Wormhole(x,y,targetx, targety, attribs)
local attrs = attribs or {}
attrs.targetx = targetx
attrs.targety = targety
set_item("it-wormhole", x,y, attrs)
end
function Doorh(x,y,attrs)
local attrs = attrs or {}
attrs.type="h"
set_stone("st-door",x,y,attrs)
end
function Doorv(x,y,attrs)
local attrs = attrs or {}
attrs.type="v"
set_stone("st-door",x,y,attrs)
end
EAST = enigma.EAST
NORTH = enigma.NORTH
SOUTH = enigma.SOUTH
WEST = enigma.WEST
---------------
-- GRADIENTS --
---------------
SLOPE_S = 1
SLOPE_N = 2
SLOPE_E = 3
SLOPE_W = 4
SLOPE_LARGE_SE = 5
SLOPE_LARGE_SW = 6
SLOPE_LARGE_NE = 7
SLOPE_LARGE_NW = 8
SLOPE_SMALL_SE = 9
SLOPE_SMALL_NE = 10
SLOPE_SMALL_SW = 11
SLOPE_SMALL_NW = 12
SLOPE_S_FORCE_W = 13
SLOPE_N_FORCE_W = 14
SLOPE_S_FORCE_E = 15
SLOPE_N_FORCE_E = 16
SLOPE_E_FORCE_N = 17
SLOPE_W_FORCE_N = 18
SLOPE_E_FORCE_S = 19
SLOPE_W_FORCE_S = 20
FLAT_FORCE_S = 21
FLAT_FORCE_N = 22
FLAT_FORCE_E = 23
FLAT_FORCE_W = 24
function Gradient( x, y, type )
if (type==nil) then error("Illegal gradient type"); end
if (type>=1 and type<=24) then
set_floor( "fl-gradient", x, y, {type=type} )
else
error("Unknown gradient type '"..type.."'");
end
end
Signal = enigma.AddSignal
SetItem = set_item
----------------------------------------------------
-- Define lowercase aliases for various functions --
----------------------------------------------------
create_world = CreateWorld
get_attrib = GetAttrib
get_kind = GetKind
set_attrib = SetAttrib
make_object = MakeObject
set_actor = SetActor
document = Document
doorh = Doorh
doorv = Doorv
gradient = Gradient
wormhole = Wormhole
------------------------
-- Loading XML levels --
------------------------
function XML_parseargs (s)
local arg = {}
gsub(s, "(%w+)=([\"'])(.-)%2", function (w, _, a)
%arg[w] = a
end)
return arg
end
function XML_collect (s)
local stack = {n=0}
local top = {n=0}
tinsert(stack, top)
local ni,c,label,args, empty
local i, j = 1, 1
while 1 do
ni,j,c,label,args, empty = strfind(s, "<(%/?)(%w+)(.-)(%/?)>", j)
if not ni then break end
local text = strsub(s, i, ni-1)
if not strfind(text, "^%s*$") then
tinsert(top, text)
end
if empty == "/" then -- empty element tag
tinsert(top, {n=0, label=label, args=XML_parseargs(args), empty=1})
elseif c == "" then -- start tag
top = {n=0, label=label, args=XML_parseargs(args)}
tinsert(stack, top) -- new level
else -- end tag
local toclose = tremove(stack) -- remove top
top = stack[stack.n]
if stack.n < 1 then
error("nothing to close with "..label)
end
if toclose.label ~= label then
error("trying to close "..toclose.label.." with "..label)
end
tinsert(top, toclose)
end
i = j+1
end
local text = strsub(s, i)
if not strfind(text, "^%s*$") then
tinsert(stack[stack.n], text)
end
if stack.n > 1 then
error("unclosed "..stack[stack.n].label)
end
return stack[1]
end
function enigma.LoadDataFile (filename)
local fname = enigma.FindDataFile (filename)
if fname then
local fh = openfile (fname, "r")
local data = read (fh, "*a")
closefile (fh)
return data
end
return nil
end
o_shuffle = "YES"
o_reset = "NO"
function LoadLevelXML (xmldata)
function assert (val, msg)
if not(val) then
error (msg or "Invalid XML file")
end
end
function parseattribs(a)
if a then
return dostring("return {"..a.."}")
end
end
function parseactor (xml)
local a = xml.args
local attribs = parseattribs(a.attribs) or {player = player}
local player = a.player
if player then
attribs.player = player
end
-- if not attribs.player then
-- error ("player number missing for actor")
-- end
local ac = SetActor(a.kind, a.x, a.y, attribs)
if a.num then
enigma.NameObject(ac, "xmlac"..a.num)
end
end
function split (str)
local t = {}
local b, e, c
b = 1
while 1 do
b,e,c = strfind (str, "([^%s]+)", b)
if not b then break end
tinsert (t, c)
b = e+1
end
return t
end
function parserow (y, str, prefix, func)
local x = 0
local t=split (str)
for i=1, getn(t) do
local e=t[i]
local n=tonumber (e)
if n then
if n < 0 then x = x-n
elseif n > 0 then
-- repeat last object n times
for j=1,n do
func (lastobj, x, y)
x = x+1
end
end
else
if e == "#" then func (lastobj, x, y); x = x+1
elseif e == "-" then x = x+1
else
lastobj = prefix .. e
func (lastobj, x, y)
x = x+1
end
end
end
end
function parsestone (xml)
local l = xml.label
local a = xml.args
if l == 'oxyd' then
oxyd (a.x, a.y, a.flavor, a.color)
elseif l == 'stone' then
set_stone (a.kind, a.x, a.y, parseattribs(a.attribs))
elseif l == 'border' then
draw_border (a.kind)
elseif l == 'row' then
parserow (a.y, xml[1], "st-", set_stone)
end
end
function parseitem (xml)
local l = xml.label
local a = xml.args
if l == 'item' then
set_item (a.kind, a.x, a.y, parseattribs(a.attribs))
elseif l == 'document' then
elseif l == 'row' then
parserow (a.y, xml[1], "it-", set_item)
end
end
function parseoption (xml)
local a = xml.args
if a.name=="oxydflavor" then
oxyd_default_flavor = a.value
elseif a.name == "shuffle" then
o_shuffle = a.value
elseif a.name == "reset" then
o_reset = a.value
elseif a.name == "scrolling" then
display.SetFollowMode (tonumber (a.value))
elseif a.name == "brittleness" then
enigma.Brittleness = a.value
elseif a.name == "slopeforce" then
enigma.SlopeForce = a.value
elseif a.name == "flatforce" then
enigma.FlatForce = a.value
elseif a.name == "frictionfactor" then
enigma.FrictionFactor = a.value
elseif a.name == "electricforce" then
enigma.ElectricForce = a.value
elseif a.name == "bumperforce" then
enigma.BumperForce = a.value
elseif a.name == "magnetforce" then
enigma.MagnetForce = a.value
elseif a.name == "magnetrange" then
enigma.MagnetRange = a.value
elseif a.name == "wormholeforce" then
enigma.WormholeForce = a.value
elseif a.name == "wormholerange" then
enigma.WormholeRange = a.value
elseif a.name == "holeforce" then
enigma.HoleForce = a.value
end
end
function parsefloor (xml)
local l = xml.label
local a = xml.args
if l == "fill" then
fill_floor (a.kind, 0, 0, level_width, level_height)
elseif l == "floor" then
set_floor (a.kind, a.x, a.y, parseattribs(a.attribs))
elseif l == 'row' then
parserow (a.y, xml[1], "fl-", set_floor)
end
end
--
--
--
--
--
function parsesignal (xml)
local l = xml.label
local a = xml.args
enigma.AddSignal (a.from, a.to)
end
--
-- Syntax of rubberband description:
--
--
--
--
--
function parseband (xml)
local l = xml.label
local a = xml.args
local obj1 = enigma.GetNamedObject (a.from)
local obj2 = enigma.GetNamedObject (a.to)
enigma.AddRubberBand (obj1, obj2, a.strength or 10, a.length or 2, a.minlen or 0)
end
function check_difficulty (d)
return ((not d)
or (d == 'hard' and options.Difficulty==HARD)
or (d == 'easy' and options.Difficulty==EASY))
end
x = XML_collect(xmldata)[1]
assert (x, "Error parsing XML file")
assert (x.label == "level")
CreateWorld (x.args.width, x.args.height)
local luacode = nil
for i = 1,getn(x) do
local xx = x[i]
local l = xx.label
if l == "lua" then
if luacode then error("Only one tag is allowed") end
luacode = xx[1]
elseif l == "actors" then
for i = 1,getn(xx) do
parseactor (xx[i])
end
elseif l == "option" then
parseoption (xx)
elseif l == "floors" then
for i=1,getn(xx) do
parsefloor (xx[i])
end
elseif l == "items" then
if check_difficulty (xx.args.difficulty) then
for i = 1,getn(xx) do
parseitem (xx[i])
end
end
elseif l == "stones" then
if check_difficulty (xx.args.difficulty) then
for i = 1,getn(xx) do
parsestone (xx[i])
end
end
elseif l == "signals" then
if check_difficulty (xx.args.difficulty) then
for i = 1,getn(xx) do
parsesignal (xx[i])
end
end
elseif l == "rubberbands" then
if check_difficulty (xx.args.difficulty) then
for i = 1,getn(xx) do
parseband (xx[i])
end
end
end
end
if o_shuffle == "YES" then
oxyd_shuffle()
end
if o_reset == "NO" then
enigma.ConserveLevel = TRUE
else
enigma.ConserveLevel = FALSE
end
if luacode then
dostring (luacode)
end
end