new lua scripting for scene loading
authorCharles McGarvey <chazmcgarvey@brokenzipper.com>
Wed, 7 Oct 2009 02:03:49 +0000 (20:03 -0600)
committerCharles McGarvey <chazmcgarvey@brokenzipper.com>
Wed, 7 Oct 2009 02:03:49 +0000 (20:03 -0600)
20 files changed:
README
configure.ac
data/scenes/Classic.lua [new file with mode: 0644]
data/scenes/Test.json [deleted file]
extra/yoink.ebuild
extra/yoink.spec.in
make-win32-installer.sh.in
src/Makefile.am
src/Moof/Dispatcher.cc
src/Moof/Dispatcher.hh
src/Moof/Octree.hh
src/Moof/Scene.cc
src/Moof/Script.hh [new file with mode: 0644]
src/Moof/Sound.cc
src/Moof/Sound.hh
src/Moof/Sphere.cc
src/Moof/Video.cc
src/Moof/Video.hh
src/YoinkApp.cc
src/YoinkApp.hh

diff --git a/README b/README
index b645786aa9a59f10e8a0c50aa49c9f9e42a72fb7..a8cfd9d525eb310c87115526553c62b49fc1d972 100644 (file)
--- a/README
+++ b/README
@@ -13,19 +13,21 @@ details.
 Dependencies:
 
 boost headers
+freealut
+libvorbis
+Lua
+OpenAL
 OpenGL (libGL, libGL or opengl32, glu32)
 SDL
 SDL_image (with libpng support)
-libvorbisfile
-OpenAL
-freealut
 
 
 Notes regarding the code:
 
-I've made some effort to put the more generic or reusable code into a separate
-library called Moof.  I've also made an effort to incorporate 3rd-party code
-that happened to fit well into what I needed.  So, generally, the source code is
+The code is a complete rewrite, containing none of the original code.  I've made
+some effort to put the more generic or reusable code into a separate library
+called Moof.  I've also made an effort to incorporate 3rd-party code that
+happened to fit well into what I needed.  So, generally, the source code is
 separated into these three categories:
 
 1. Yoink-specific code.
index a2db4cfda05fafd56e8bad35c71e07388144e272..47ee0baef14494680d9803a5cb60494e34b6238d 100644 (file)
@@ -274,6 +274,15 @@ AC_SEARCH_LIBS([ov_open], [vorbisfile],,
                           [missing=yes
                                echo "***** Missing libvorbisfile ($website) *****"])
 
+##### liblua #####
+website="http://www.lua.org/"
+AC_CHECK_HEADERS([lua.h],,
+                                [missing=yes
+                                 echo "***** Missing lua headers ($website) *****"])
+AC_SEARCH_LIBS([lua_load], [lua],,
+                          [missing=yes
+                               echo "***** Missing liblua ($website) *****"])
+
 ##### librt (optional) #####
 AC_SEARCH_LIBS([clock_gettime], [rt],
                           [AC_DEFINE([HAVE_CLOCK_GETTIME], 1,
@@ -292,6 +301,7 @@ fi
 
 DATA_FILES=$(echo $(cd data; \
                                        find . -name "*.json" \
+                                               -o -name "*.lua" \
                                                -o -name "*.ogg" \
                                                -o -name "*.png" \
                                                -o -name "*.xm" \
diff --git a/data/scenes/Classic.lua b/data/scenes/Classic.lua
new file mode 100644 (file)
index 0000000..62b8a97
--- /dev/null
@@ -0,0 +1,974 @@
+
+-- Scene: Classic Yoink
+-- created by Neil Carter
+-- converted to Lua by Charles McGarvey
+
+-- Scene API:
+--
+-- Functions:
+-- SetPlayfieldBounds(point1, point2)
+-- SetMaximumBounds(point1, point2)
+-- ResetTransform()
+-- Translate(x, y, z)
+-- Scale(x, y, z) or Scale(xyz)
+-- Rotate(axis, degree) or Rotate(x, y, z)
+-- SetTexture(name)
+-- MakeTilemap({width = ..., surface_type = ..., tiles = {}})
+-- MakeBillboard({tile = ..., u_scale = ...})
+--
+-- Globals:
+-- detail - level of detail of the scene
+
+
+SetPlayfieldBounds({0, 0, -100}, {1280, 500, 100})
+SetMaximumBounds({-160, 0, -192}, {1440, 480, 224})
+
+
+-- Left end tower block
+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+-- Front
+
+ResetTransform()
+Translate(-5, 0, 5)
+Scale(32)
+SetTexture("TowerBlock1")
+MakeTilemap({
+       width = 5,
+       tiles = {
+               2,      2,      2,      2,      2,
+               1,      0,      0,      1,      0,
+               1,      0,      0,      1,      0,
+               1,      0,      0,      1,      0,
+               1,      0,      0,      1,      0,
+               1,      0,      0,      1,      0,
+               1,      0,      0,      1,      0,
+               1,      0,      0,      1,      0,
+               1,      0,      0,      1,      0,
+               1,      0,      0,      1,      0,
+               1,      0,      0,      1,      0,
+               1,      0,      0,      1,      0,
+               1,      0,      0,      1,      0,
+               1,      0,      0,      1,      0,
+               4,      4,      4,      4,      4
+       }
+})
+
+-- Right side
+
+ResetTransform()
+Rotate('y', 90)
+Translate(0, 0, 5)
+Scale(32)
+MakeTilemap({
+       width = 5,
+       surface_type = 'right',
+       tiles = {
+               2,      2,      2,      2,      2,
+               0,      0,      0,      0,      0,
+               0,      0,      0,      0,      0,
+               0,      0,      0,      0,      0,
+               0,      0,      0,      0,      0,
+               0,      0,      0,      0,      0,
+               0,      0,      0,      0,      0,
+               0,      0,      0,      0,      0,
+               0,      0,      0,      0,      0,
+               0,      0,      0,      0,      0,
+               0,      0,      0,      0,      0,
+               0,      0,      0,      0,      0,
+               0,      0,      0,      0,      0,
+               0,      0,      0,      0,      6,
+               4,      5,      5,      5,      4
+       }
+})
+               
+-- Top
+
+ResetTransform()
+Rotate('x', 90)
+Translate(-5, 15, 0)
+Scale(32)
+MakeTilemap({
+       width = 5,
+       surface_type = 'top',
+       tiles = {
+               3,      3,      3,      3,      3,
+               3,      3,      3,      3,      3,
+               3,      3,      3,      3,      3,
+               3,      3,      3,      3,      3,
+               3,      3,      3,      3,      3
+       }
+})
+
+-- Leftmost background tower block
+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+               
+-- Front
+
+if detail > 1 then
+       ResetTransform()
+       Scale(32)
+       MakeTilemap({
+               width = 7,
+               tiles = {
+                       2,      2,      2,      2,      2,      2,      2,
+                       0,      1,      0,      0,      0,      1,      0,
+                       0,      1,      0,      0,      0,      1,      0,
+                       0,      1,      0,      0,      6,      1,      0,
+                       0,      1,      0,      0,      0,      1,      0,
+                       0,      1,      0,      0,      0,      1,      0,
+                       0,      1,      0,      0,      0,      1,      0,
+                       4,      4,      5,      5,      5,      4,      4
+               }
+       })
+
+       -- Right side
+
+       ResetTransform()
+       Rotate('y', 90)
+       Translate(7, 0, 0)
+       Scale(32)
+       MakeTilemap({
+               width = 6,
+               tiles = {
+                       2,      2,      2,      2,      2,      2,
+                       0,      1,      0,      0,      1,      0,
+                       0,      1,      0,      0,      1,      0,
+                       0,      1,      0,      0,      1,      0,
+                       0,      1,      0,      0,      1,      0,
+                       0,      1,      0,      0,      1,      0,
+                       0,      1,      0,      0,      1,      0,
+                       4,      4,      4,      4,      4,      4
+               }
+       })
+
+       -- Top
+
+       ResetTransform()
+       Rotate('x', 90)
+       Translate(-2, 8, -6)
+       Scale(32)
+       MakeTilemap({
+               width = 9,
+               tiles = {
+                       3,      3,      3,      3,      3,      3,      3,      3,      3,
+                       3,      3,      3,      3,      3,      3,      3,      3,      3,
+                       3,      3,      3,      3,      3,      3,      3,      3,      3,
+                       3,      3,      3,      3,      3,      3,      3,      3,      3,
+                       3,      3,      3,      3,      3,      3,      3,      3,      3,
+                       3,      3,      3,      3,      3,      3,      3,      3,      3
+               }
+       })
+end
+
+-- Foreground building with pitched roof
+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+               
+-- Left wall
+
+ResetTransform()
+Rotate('y', -90)
+Translate(10, 0, 1)
+Scale(32)
+SetTexture("Building")
+MakeTilemap({
+       width = 4,
+       surface_type = 'left',
+       tiles = {
+               -1,     9,      11,     -1,
+               9,      10,     12,     11,
+               15,     7,      7,      16,
+               3,      5,      6,      4,
+               3,      6,      5,      4
+       }
+})
+
+-- Right wall
+
+ResetTransform()
+Rotate('y', -90)
+Translate(13, 0, 1)
+Scale(32)
+MakeTilemap({
+       width = 4,
+       surface_type = 'right',
+       tiles = {
+               -1,     9,      11,     -1,
+               9,      10,     12,     11,
+               15,     7,      7,      16,
+               3,      5,      6,      4,
+               3,      8,      5,      4
+       }
+})
+
+-- Front wall
+
+ResetTransform()
+Translate(10, 0, 5)
+Scale(32)
+MakeTilemap({
+       width = 3,
+       tiles = {
+               15,     7,      16,
+               3,      5,      4,
+               3,      6,      4
+       }
+})
+               
+-- Pitched roof
+
+ResetTransform()
+Rotate('x', 135)
+Scale(1, 1.5, 1.5)
+Translate(10, 5, 3)
+Scale(32)
+MakeTilemap({
+       width = 3,
+       tiles = {
+               13,     13,     13,
+               13,     13,     13
+       }
+})
+
+-- Finial
+
+ResetTransform()
+Translate(10, 5, 3)
+Scale(32)
+MakeTilemap({
+       width = 3,
+       tiles = {
+               18,     18,     18
+       }
+})
+
+-- Cheaty invisible platform
+
+ResetTransform()
+Translate(10, 4, 3)
+Scale(32)
+MakeTilemap({
+       width = 3,
+       surface_type = 'top',
+       tiles = {
+               -1,     -1,     -1
+       }
+})
+
+-- The ground
+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+               
+-- Courtyard
+
+ResetTransform()
+Rotate('x', 90)
+Translate(-3, 0, 0)
+Scale(32)
+SetTexture("Scenery")
+MakeTilemap({
+       width = 13,
+       surface_type = 'top',
+       tiles = {
+               1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,
+               1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,
+               -1,     -1,     -1,     1,      0,      0,      0,      0,      0,      0,      0,      0,      1,
+               -1,     -1,     -1,     1,      0,      0,      0,      0,      0,      0,      0,      0,      1,
+               -1,     -1,     -1,     1,      0,      0,      0,      0,      0,      0,      0,      0,      1,
+               -1,     -1,     -1,     1,      0,      0,      0,      0,      0,      0,      0,      0,      1,
+               -1,     -1,     -1,     1,      1,      1,      1,      1,      1,      1,      1,      1,      1
+       }
+})
+
+-- Front grass
+
+if detail > 2 then
+       ResetTransform()
+       Scale(8, 1, 1)
+       Translate(1, -0.5, 5)
+       Scale(32)
+       MakeBillboard({
+               tile = 2,
+               u_scale = 8
+       })
+
+       -- Back grass
+
+       ResetTransform()
+       Scale(8, 1, 1)
+       Translate(1, -0.5, 1)
+       Scale(32)
+       MakeBillboard({
+               tile = 2,
+               u_scale = 8
+       })
+
+       -- Left grass
+
+       ResetTransform()
+       Scale(4, 1, 1)
+       Rotate('y', -90)
+       Translate(1, -0.5, 1)
+       Scale(32)
+       MakeBillboard({
+               tile = 2,
+               u_scale = 4
+       })
+
+       -- Right grass
+
+       ResetTransform()
+       Scale(4, 1, 1)
+       Rotate('y', -90)
+       Translate(9, -0.5, 1)
+       Scale(32)
+       MakeBillboard({
+               tile = 2,
+               u_scale = 4
+       })
+
+       -- Fence behind house
+
+       ResetTransform()
+       Scale(11, 1, 1)
+       Translate(7, 0, 0)
+       Scale(32)
+       MakeBillboard({
+               tile = 4,
+               u_scale = 11
+       })
+end
+
+-- Background building with pitched roof
+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+               
+-- Front wall
+
+if detail > 1 then
+       ResetTransform()
+       Translate(19, 0, 0)
+       Scale(32)
+       SetTexture("Building")
+       MakeTilemap({
+               width = 4,
+               tiles = {
+                       -1,     9,      11,     -1,
+                       9,      10,     12,     11,
+                       15,     7,      7,      16,
+                       3,      6,      5,      4,
+                       3,      5,      6,      4,
+                       3,      8,      5,      4
+               }
+       })
+
+       -- Left wall
+
+       ResetTransform()
+       Rotate('y', -90)
+       Translate(19, 0, -3)
+       Scale(32)
+       MakeTilemap({
+               width = 3,
+               tiles = {
+                       15,     1,      16,
+                       3,      7,      4,
+                       3,      5,      4,
+                       3,      0,      4
+               }
+       })
+
+       -- Right wall
+
+       ResetTransform()
+       Rotate('y', -90)
+       Translate(23, 0, -3)
+       Scale(32)
+       MakeTilemap({
+               width = 3,
+               tiles = {
+                       15,     0,      16,
+                       3,      7,      4,
+                       3,      6,      4,
+                       3,      2,      4
+               }
+       })
+
+       -- Left pitched roof
+
+       ResetTransform()
+       Rotate('x', 135)
+       Scale(1, 1.5, 1.5)
+       Rotate('y', -90)
+       Translate(21, 6, -3)
+       Scale(32)
+       MakeTilemap({
+               width = 3,
+               tiles = {
+                       13,     13,     13,
+                       13,     13,     13
+               }
+       })
+
+       -- Right pitched roof
+
+       ResetTransform()
+       Rotate('x', -135)
+       Scale(1, 1.5, 1.5)
+       Rotate('y', -90)
+       Translate(21, 6, -3)
+       Scale(32)
+       MakeTilemap({
+               width = 3,
+               tiles = {
+                       13,     13,     13,
+                       13,     13,     13
+               }
+       })
+
+       -- Finial
+
+       ResetTransform()
+       Rotate('y', -90)
+       Translate(21, 6, -3)
+       Scale(32)
+       MakeTilemap({
+               width = 3,
+               tiles = {
+                       18,     18,     18
+               }
+       })
+end
+
+-- More ground to the right
+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+               
+-- Ground under house
+
+ResetTransform()
+Rotate('x', 90)
+Translate(10, 0, 0)
+Scale(32)
+SetTexture("Scenery")
+MakeTilemap({
+       width = 3,
+       surface_type = 'top',
+       tiles = {
+               1,      1,      1,
+               1,      1,      1,
+               -1,     -1,     -1,
+               -1,     -1,     -1,
+               -1,     -1,     -1,
+               -1,     -1,     -1,
+               1,      1,      1
+       }
+})
+
+-- Left part of center courtyard
+
+ResetTransform()
+Rotate('x', 90)
+Translate(13, 0, 0)
+Scale(32)
+MakeTilemap({
+       width = 8,
+       surface_type = 'top',
+       tiles = {
+               1,      1,      1,      1,      1,      1,      1,      1,
+               1,      1,      1,      1,      1,      1,      1,      1,
+               1,      0,      0,      0,      0,      0,      0,      0,
+               1,      0,      0,      0,      0,      0,      1,      1,
+               0,      0,      0,      0,      0,      0,      1,      1,
+               1,      0,      0,      0,      0,      0,      0,      0,
+               1,      1,      1,      1,      1,      0,      0,      0
+       }
+})
+
+-- Front grass
+
+if detail > 2 then
+       ResetTransform()
+       Scale(12, 1, 1)
+       Translate(14, -0.5, 5)
+       Scale(32)
+       MakeBillboard({
+               tile = 2,
+               u_scale = 12
+       })
+
+       -- Back grass
+
+       ResetTransform()
+       Scale(4, 1, 1)
+       Translate(14, -0.5, 1)
+       Scale(32)
+       MakeBillboard({
+               tile = 2,
+               u_scale = 4
+       })
+
+       -- Front grass next to door
+
+       ResetTransform()
+       Translate(13, -0.5, 3)
+       Scale(32)
+       MakeBillboard({
+               tile = 2,
+               u_scale = 1
+       })
+
+       -- Back grass next to door
+
+       ResetTransform()
+       Translate(13, -0.5, 2)
+       Scale(32)
+       MakeBillboard({
+               tile = 2,
+               u_scale = 1
+       })
+
+       -- Left grass
+
+       ResetTransform()
+       Rotate('y', -90)
+       Translate(14, -0.5, 1)
+       Scale(32)
+       MakeTilemap({
+               width = 4,
+               tiles = {
+                       2,      -1,     2,      2
+               }
+       })
+
+       -- Grass left of house
+
+       ResetTransform()
+       Rotate('y', -90)
+       Translate(18, -0.5, 0)
+       Scale(32)
+       MakeBillboard({
+               tile = 2,
+               u_scale = 1
+       })
+
+       -- Grass right of house
+
+       ResetTransform()
+       Rotate('y', -90)
+       Translate(24, -0.5, 0)
+       Scale(32)
+       MakeBillboard({
+               tile = 2,
+               u_scale = 1
+       })
+
+       -- Front grass in center
+
+       ResetTransform()
+       Scale(4, 1, 1)
+       Translate(19, -0.5, 4)
+       Scale(32)
+       MakeBillboard({
+               tile = 2,
+               u_scale = 4
+       })
+
+       -- Back grass in center
+
+       ResetTransform()
+       Scale(4, 1, 1)
+       Translate(19, -0.5, 2)
+       Scale(32)
+       MakeBillboard({
+               tile = 2,
+               u_scale = 4
+       })
+
+       -- Left grass in center
+
+       ResetTransform()
+       Scale(2, 1, 1)
+       Rotate('y', -90)
+       Translate(19, -0.5, 2)
+       Scale(32)
+       MakeBillboard({
+               tile = 2,
+               u_scale = 2
+       })
+
+       -- Right grass in center
+
+       ResetTransform()
+       Scale(2, 1, 1)
+       Rotate('y', -90)
+       Translate(23, -0.5, 2)
+       Scale(32)
+       MakeBillboard({
+               tile = 2,
+               u_scale = 2
+       })
+end
+
+-- Right part of center courtyard
+
+ResetTransform()
+Rotate('x', 90)
+Translate(21, 0, 0)
+Scale(32)
+MakeTilemap({
+       width = 7,
+       surface_type = 'top',
+       tiles = {
+               1,      1,      1,      1,      1,      0,      0,
+               1,      1,      1,      1,      1,      0,      0,
+               0,      0,      0,      0,      0,      0,      0,
+               1,      1,      0,      0,      0,      0,      0,
+               1,      1,      0,      0,      0,      0,      0,
+               0,      0,      0,      0,      0,      0,      0,
+               0,      0,      0,      1,      1,      1,      1
+       }
+})
+
+-- Fence to right of back house
+
+if detail > 2 then
+       ResetTransform()
+       Scale(4, 1, 1)
+       Translate(24, 0, 0)
+       Scale(32)
+       MakeBillboard({
+               tile = 4,
+               u_scale = 4
+       })
+
+       -- Grass in front of fence
+
+       ResetTransform()
+       Scale(4, 1, 1)
+       Translate(24, -0.5, 1)
+       Scale(32)
+       MakeBillboard({
+               tile = 2,
+               u_scale = 4
+       })
+                       
+       -- Grass to left of tower block
+
+       ResetTransform()
+       Scale(2, 1, 1)
+       Rotate('y', -90)
+       Translate(26, -0.5, 5)
+       Scale(32)
+       MakeBillboard({
+               tile = 2,
+               u_scale = 2
+       })
+
+       -- Grass to right of tower block
+
+       ResetTransform()
+       Scale(2, 1, 1)
+       Rotate('y', -90)
+       Translate(35, -0.5, 5)
+       Scale(32)
+       MakeBillboard({
+               tile = 2,
+               u_scale = 2
+       })
+
+       -- Next bit of grass
+
+       ResetTransform()
+       Scale(5, 1, 1)
+       Translate(35, -0.5, 5)
+       Scale(32)
+       MakeBillboard({
+               tile = 2,
+               u_scale = 5
+       })
+
+       -- Back grass
+
+       ResetTransform()
+       Scale(6, 1, 1)
+       Translate(34, -0.5, 1)
+       Scale(32)
+       MakeBillboard({
+               tile = 2,
+               u_scale = 6
+       })
+
+       -- Extra bit of back grass
+
+       ResetTransform()
+       Rotate('y', -90)
+       Translate(34, -0.5, 0)
+       Scale(32)
+       MakeBillboard({
+               tile = 2,
+               u_scale = 1
+       })
+end
+
+-- Ground around tower block
+
+ResetTransform()
+Rotate('x', 90)
+Translate(28, 0, 4)
+Scale(32)
+MakeTilemap({
+       width = 5,
+       surface_type = 'top',
+       tiles = {
+               0,      0,      0,      0,      0,
+               0,      0,      0,      0,      0,
+               0,      0,      0,      0,      0
+       }
+})
+
+-- Rightmost ground
+
+ResetTransform()
+Rotate('x', 90)
+Translate(33, 0, 0)
+Scale(32)
+MakeTilemap({
+       width = 10,
+       surface_type = 'top',
+       tiles = {
+               0,      0,      1,      1,      1,      1,      1,      1,      1,      1,
+               0,      0,      1,      1,      1,      1,      1,      1,      1,      1,
+               0,      0,      0,      0,      0,      0,      0,      -1,     -1,     -1,
+               0,      0,      0,      0,      0,      0,      0,      -1,     -1,     -1,
+               0,      0,      0,      0,      0,      0,      0,      -1,     -1,     -1,
+               0,      0,      0,      0,      0,      0,      0,      -1,     -1,     -1,
+               0,      1,      1,      1,      1,      1,      1,      -1,     -1,     -1
+       }
+})
+
+-- Right foreground tower block
+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+               
+-- Front
+
+ResetTransform()
+Translate(28, 0, 4)
+Scale(32)
+SetTexture("TowerBlock1")
+MakeTilemap({
+       width = 5,
+       tiles = {
+               2,      2,      2,      2,      2,
+               0,      0,      0,      0,      0,
+               0,      0,      0,      0,      6,
+               0,      0,      0,      0,      0,
+               0,      0,      0,      0,      0,
+               0,      0,      0,      6,      0,
+               4,      4,      4,      4,      4
+       }
+})
+
+-- Right side
+
+ResetTransform()
+Rotate('y', 90)
+Translate(33, 0, 4)
+Scale(32)
+MakeTilemap({
+       width = 6,
+       surface_type = 'right',
+       tiles = {
+               2,      2,      2,      2,      2,      2,
+               0,      1,      0,      0,      1,      0,
+               0,      1,      0,      0,      1,      0,
+               0,      1,      0,      0,      1,      0,
+               0,      1,      0,      0,      1,      0,
+               0,      1,      0,      0,      1,      0,
+               5,      4,      5,      5,      4,      5
+       }
+})
+
+-- Left side
+
+ResetTransform()
+Rotate('y', 90)
+Translate(28, 0, 4)
+Scale(32)
+MakeTilemap({
+       width = 6,
+       surface_type = 'left',
+       tiles = {
+               2,      2,      2,      2,      2,      2,
+               0,      1,      6,      0,      1,      0,
+               0,      1,      0,      0,      1,      0,
+               0,      1,      0,      0,      1,      0,
+               0,      1,      0,      0,      1,      0,
+               0,      1,      0,      0,      1,      0,
+               5,      4,      5,      5,      4,      5
+       }
+})
+
+-- Top
+
+ResetTransform()
+Rotate('x', 90)
+Translate(28, 7, -2)
+Scale(32)
+MakeTilemap({
+       width = 5,
+       surface_type = 'top',
+       tiles = {
+               3,      3,      3,      3,      3,
+               3,      3,      3,      3,      3,
+               3,      3,      3,      3,      3,
+               3,      3,      3,      3,      3,
+               3,      3,      3,      3,      3,
+               3,      3,      3,      3,      3
+       }
+})
+
+-- Right end tower block
+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+-- Front
+
+ResetTransform()
+Translate(40, 0, 5)
+Scale(32)
+MakeTilemap({
+       width = 5,
+       tiles = {
+               2,      2,      2,      2,      2,
+               0,      1,      0,      0,      1,
+               0,      1,      0,      0,      1,
+               0,      1,      0,      0,      1,
+               0,      1,      0,      0,      1,
+               6,      1,      0,      0,      1,
+               0,      1,      0,      0,      1,
+               0,      1,      0,      0,      1,
+               0,      1,      0,      0,      1,
+               0,      1,      0,      0,      1,
+               0,      1,      0,      0,      1,
+               6,      1,      0,      0,      1,
+               0,      1,      0,      0,      1,
+               0,      1,      0,      0,      1,
+               4,      4,      4,      4,      4
+       }
+})
+
+-- Left side
+
+ResetTransform()
+Rotate('y', 90)
+Translate(40, 0, 5)
+Scale(32)
+MakeTilemap({
+       width = 5,
+       surface_type = 'left',
+       tiles = {
+               2,      2,      2,      2,      2,
+               6,      0,      0,      0,      0,
+               0,      0,      0,      0,      0,
+               0,      0,      0,      0,      0,
+               0,      0,      0,      6,      0,
+               0,      0,      0,      0,      0,
+               0,      0,      0,      0,      0,
+               0,      0,      6,      0,      0,
+               0,      0,      0,      0,      0,
+               0,      0,      0,      0,      0,
+               0,      0,      0,      0,      0,
+               0,      0,      0,      0,      0,
+               0,      0,      0,      0,      0,
+               0,      0,      0,      0,      0,
+               4,      5,      5,      5,      4
+       }
+})
+
+-- Top
+
+ResetTransform()
+Rotate('x', 90)
+Translate(40, 15, 0)
+Scale(32)
+MakeTilemap({
+       width = 5,
+       surface_type = 'top',
+       tiles = {
+               3,      3,      3,      3,      3,
+               3,      3,      3,      3,      3,
+               3,      3,      3,      3,      3,
+               3,      3,      3,      3,      3,
+               3,      3,      3,      3,      3
+       }
+})
+
+-- Background
+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ResetTransform()
+Translate(-0.3, -0.17, -900)
+Scale(3200, 1600, 1)
+SetTexture("BackgroundFar")
+MakeBillboard()
+
+Translate(0, 0, 300)
+SetTexture("BackgroundNear")
+MakeBillboard({
+       blend = detail > 1 and true or false
+})
+
+-- Trees
+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+SetTexture("Trees")
+
+-- Left courtyard
+
+if detail > 1 then
+       ResetTransform()
+       Scale(96)
+       Translate(250, -2.5, 16)
+       MakeBillboard({
+               tile = 1
+       })
+end
+
+-- Center courtyard
+
+ResetTransform()
+Scale(96)
+Translate(610, -2.5, 85)
+MakeBillboard({
+       tile = 0
+})
+
+ResetTransform()
+Scale(96)
+Translate(650, -2.5, 115)
+MakeBillboard({
+       tile = 1
+})
+
+-- Right courtyard
+
+if detail > 1 then
+       ResetTransform()
+       Scale(96)
+       Translate(1080, -2.5, 10)
+       MakeBillboard({
+               tile = 1
+       })
+
+       ResetTransform()
+       Scale(96)
+       Translate(1120, -2.5, -15)
+       MakeBillboard({
+               tile = 0
+       })
+
+       ResetTransform()
+       Scale(96)
+       Translate(1220, -2.5, -30)
+       MakeBillboard({
+               tile = 1
+       })
+end
+
diff --git a/data/scenes/Test.json b/data/scenes/Test.json
deleted file mode 100644 (file)
index b6aac66..0000000
+++ /dev/null
@@ -1,1115 +0,0 @@
-{
-       "playfield_bounds": [0, 0, -100, 1280, 500, 100],
-       "maximum_bounds": [-160, 0, -192, 1440, 480, 224],
-       "instructions":
-       [
-
-       /* Left end tower block */
-
-               /* Front */
-
-               "reset_transform",
-               "translate", [-5, 0, 5],
-               "scale", [32],
-               "texture", "TowerBlock1",
-               "tilemap",
-               {
-                       "width": 5,
-                       "tiles":
-                       [
-                               2,      2,      2,      2,      2,
-                               1,      0,      0,      1,      0,
-                               1,      0,      0,      1,      0,
-                               1,      0,      0,      1,      0,
-                               1,      0,      0,      1,      0,
-                               1,      0,      0,      1,      0,
-                               1,      0,      0,      1,      0,
-                               1,      0,      0,      1,      0,
-                               1,      0,      0,      1,      0,
-                               1,      0,      0,      1,      0,
-                               1,      0,      0,      1,      0,
-                               1,      0,      0,      1,      0,
-                               1,      0,      0,      1,      0,
-                               1,      0,      0,      1,      0,
-                               4,      4,      4,      4,      4
-                       ]
-               },
-
-               /* Right side */
-
-               "reset_transform",
-               "rotate", ["y", 90],
-               "translate", [0, 0, 5],
-               "scale", [32],
-               "texture", "TowerBlock1",
-               "tilemap",
-               {
-                       "width": 5,
-                       "surface_type": "right",
-                       "tiles":
-                       [
-                               2,      2,      2,      2,      2,
-                               0,      0,      0,      0,      0,
-                               0,      0,      0,      0,      0,
-                               0,      0,      0,      0,      0,
-                               0,      0,      0,      0,      0,
-                               0,      0,      0,      0,      0,
-                               0,      0,      0,      0,      0,
-                               0,      0,      0,      0,      0,
-                               0,      0,      0,      0,      0,
-                               0,      0,      0,      0,      0,
-                               0,      0,      0,      0,      0,
-                               0,      0,      0,      0,      0,
-                               0,      0,      0,      0,      0,
-                               0,      0,      0,      0,      6,
-                               4,      5,      5,      5,      4
-                       ]
-               },
-               
-               /* Top */
-
-               "reset_transform",
-               "rotate", ["x", 90],
-               "translate", [-5, 15, 0],
-               "scale", [32],
-               "texture", "TowerBlock1",
-               "tilemap",
-               {
-                       "width": 5,
-                       "surface_type": "top",
-                       "tiles":
-                       [
-                               3,      3,      3,      3,      3,
-                               3,      3,      3,      3,      3,
-                               3,      3,      3,      3,      3,
-                               3,      3,      3,      3,      3,
-                               3,      3,      3,      3,      3
-                       ]
-               },
-
-       /* Leftmost background tower block */
-               
-               /* Front */
-
-               "reset_transform",
-               "scale", [32],
-               "texture", "TowerBlock1",
-               "tilemap",
-               {
-                       "width": 7,
-                       "detail": 1,
-                       "tiles":
-                       [
-                               2,      2,      2,      2,      2,      2,      2,
-                               0,      1,      0,      0,      0,      1,      0,
-                               0,      1,      0,      0,      0,      1,      0,
-                               0,      1,      0,      0,      6,      1,      0,
-                               0,      1,      0,      0,      0,      1,      0,
-                               0,      1,      0,      0,      0,      1,      0,
-                               0,      1,      0,      0,      0,      1,      0,
-                               4,      4,      5,      5,      5,      4,      4
-                       ]
-               },
-
-               /* Right side */
-
-               "reset_transform",
-               "rotate", ["y", 90],
-               "translate", [7, 0, 0],
-               "scale", [32],
-               "texture", "TowerBlock1",
-               "tilemap",
-               {
-                       "width": 6,
-                       "detail": 1,
-                       "tiles":
-                       [
-                               2,      2,      2,      2,      2,      2,
-                               0,      1,      0,      0,      1,      0,
-                               0,      1,      0,      0,      1,      0,
-                               0,      1,      0,      0,      1,      0,
-                               0,      1,      0,      0,      1,      0,
-                               0,      1,      0,      0,      1,      0,
-                               0,      1,      0,      0,      1,      0,
-                               4,      4,      4,      4,      4,      4
-                       ]
-               },
-
-               /* Top */
-
-               "reset_transform",
-               "rotate", ["x", 90],
-               "translate", [-2, 8, -6],
-               "scale", [32],
-               "texture", "TowerBlock1",
-               "tilemap",
-               {
-                       "width": 9,
-                       "detail": 1,
-                       "tiles":
-                       [
-                               3,      3,      3,      3,      3,      3,      3,      3,      3,
-                               3,      3,      3,      3,      3,      3,      3,      3,      3,
-                               3,      3,      3,      3,      3,      3,      3,      3,      3,
-                               3,      3,      3,      3,      3,      3,      3,      3,      3,
-                               3,      3,      3,      3,      3,      3,      3,      3,      3,
-                               3,      3,      3,      3,      3,      3,      3,      3,      3
-                       ]
-               },
-
-       /* Foreground building with pitched roof */
-               
-               /* Left wall */
-
-               "reset_transform",
-               "rotate", ["y", -90],
-               "translate", [10, 0, 1],
-               "scale", [32],
-               "texture", "Building",
-               "tilemap",
-               {
-                       "width": 4,
-                       "surface_type": "left",
-                       "tiles":
-                       [
-                               -1,     9,      11,     -1,
-                               9,      10,     12,     11,
-                               15,     7,      7,      16,
-                               3,      5,      6,      4,
-                               3,      6,      5,      4
-                       ]
-               },
-
-               /* Right wall */
-
-               "reset_transform",
-               "rotate", ["y", -90],
-               "translate", [13, 0, 1],
-               "scale", [32],
-               "texture", "Building",
-               "tilemap",
-               {
-                       "width": 4,
-                       "surface_type": "right",
-                       "tiles":
-                       [
-                               -1,     9,      11,     -1,
-                               9,      10,     12,     11,
-                               15,     7,      7,      16,
-                               3,      5,      6,      4,
-                               3,      8,      5,      4
-                       ]
-               },
-
-               /* Front wall */
-
-               "reset_transform",
-               "translate", [10, 0, 5],
-               "scale", [32],
-               "texture", "Building",
-               "tilemap",
-               {
-                       "width": 3,
-                       "tiles":
-                       [
-                               15,     7,      16,
-                               3,      5,      4,
-                               3,      6,      4
-                       ]
-               },
-               
-               /* Pitched roof */
-
-               "reset_transform",
-               "rotate", ["x", 135],
-               "scale", [1, 1.5, 1.5],
-               "translate", [10, 5, 3],
-               "scale", [32],
-               "texture", "Building",
-               "tilemap",
-               {
-                       "width": 3,
-                       "tiles":
-                       [
-                               13,     13,     13,
-                               13,     13,     13
-                       ]
-               },
-
-               /* Finial */
-
-               "reset_transform",
-               "translate", [10, 5, 3],
-               "scale", [32],
-               "texture", "Building",
-               "tilemap",
-               {
-                       "width": 3,
-                       "tiles":
-                       [
-                               18,     18,     18
-                       ]
-               },
-
-               /* Cheaty invisible platform */
-
-               "reset_transform",
-               "translate", [10, 4, 3],
-               "scale", [32],
-               "texture", "Building",
-               "tilemap",
-               {
-                       "width": 3,
-                       "surface_type": "top",
-                       "tiles":
-                       [
-                               -1,     -1,     -1
-                       ]
-               },
-
-       /* The ground */
-               
-               /* Courtyard */
-
-               "reset_transform",
-               "rotate", ["x", 90],
-               "translate", [-3, 0, 0],
-               "scale", [32],
-               "texture", "Scenery",
-               "tilemap",
-               {
-                       "width": 13,
-                       "surface_type": "top",
-                       "tiles":
-                       [
-                               1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,
-                               1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,
-                               -1,     -1,     -1,     1,      0,      0,      0,      0,      0,      0,      0,      0,      1,
-                               -1,     -1,     -1,     1,      0,      0,      0,      0,      0,      0,      0,      0,      1,
-                               -1,     -1,     -1,     1,      0,      0,      0,      0,      0,      0,      0,      0,      1,
-                               -1,     -1,     -1,     1,      0,      0,      0,      0,      0,      0,      0,      0,      1,
-                               -1,     -1,     -1,     1,      1,      1,      1,      1,      1,      1,      1,      1,      1
-                       ]
-               },
-
-               /* Front grass */
-
-               "reset_transform",
-               "scale", [8, 1, 1],
-               "translate", [1, -0.5, 5],
-               "scale", [32],
-               "texture", "Scenery",
-               "billboard",
-               {
-                       "tile": 2,
-                       "u_scale": 8,
-                       "detail": 2
-               },
-
-               /* Back grass */
-
-               "reset_transform",
-               "scale", [8, 1, 1],
-               "translate", [1, -0.5, 1],
-               "scale", [32],
-               "texture", "Scenery",
-               "billboard",
-               {
-                       "tile": 2,
-                       "u_scale": 8,
-                       "detail": 2
-               },
-
-               /* Left grass */
-
-               "reset_transform",
-               "scale", [4, 1, 1],
-               "rotate", ["y", -90],
-               "translate", [1, -0.5, 1],
-               "scale", [32],
-               "texture", "Scenery",
-               "billboard",
-               {
-                       "tile": 2,
-                       "u_scale": 4,
-                       "detail": 2
-               },
-
-               /* Right grass */
-
-               "reset_transform",
-               "scale", [4, 1, 1],
-               "rotate", ["y", -90],
-               "translate", [9, -0.5, 1],
-               "scale", [32],
-               "texture", "Scenery",
-               "billboard",
-               {
-                       "tile": 2,
-                       "u_scale": 4,
-                       "detail": 2
-               },
-
-               /* Fence behind house */
-
-               "reset_transform",
-               "scale", [11, 1, 1],
-               "translate", [7, 0, 0],
-               "scale", [32],
-               "texture", "Scenery",
-               "billboard",
-               {
-                       "tile": 4,
-                       "u_scale": 11,
-                       "detail": 2
-               },
-
-       /* Background building with pitched roof */
-               
-               /* Front wall */
-
-               "reset_transform",
-               "translate", [19, 0, 0],
-               "scale", [32],
-               "texture", "Building",
-               "tilemap",
-               {
-                       "width": 4,
-                       "detail": 1,
-                       "tiles":
-                       [
-                               -1,     9,      11,     -1,
-                               9,      10,     12,     11,
-                               15,     7,      7,      16,
-                               3,      6,      5,      4,
-                               3,      5,      6,      4,
-                               3,      8,      5,      4
-                       ]
-               },
-
-               /* Left wall */
-
-               "reset_transform",
-               "rotate", ["y", -90],
-               "translate", [19, 0, -3],
-               "scale", [32],
-               "texture", "Building",
-               "tilemap",
-               {
-                       "width": 3,
-                       "surface_type": "left",
-                       "detail": 1,
-                       "tiles":
-                       [
-                               15,     1,      16,
-                               3,      7,      4,
-                               3,      5,      4,
-                               3,      0,      4
-                       ]
-               },
-
-               /* Right wall */
-
-               "reset_transform",
-               "rotate", ["y", -90],
-               "translate", [23, 0, -3],
-               "scale", [32],
-               "texture", "Building",
-               "tilemap",
-               {
-                       "width": 3,
-                       "surface_type": "right",
-                       "detail": 1,
-                       "tiles":
-                       [
-                               15,     0,      16,
-                               3,      7,      4,
-                               3,      6,      4,
-                               3,      2,      4
-                       ]
-               },
-
-               /* Left pitched roof */
-
-               "reset_transform",
-               "rotate", ["x", 135],
-               "scale", [1, 1.5, 1.5],
-               "rotate", ["y", -90],
-               "translate", [21, 6, -3],
-               "scale", [32],
-               "texture", "Building",
-               "tilemap",
-               {
-                       "width": 3,
-                       "detail": 1,
-                       "tiles":
-                       [
-                               13,     13,     13,
-                               13,     13,     13
-                       ]
-               },
-
-               /* Right pitched roof */
-
-               "reset_transform",
-               "rotate", ["x", -135],
-               "scale", [1, 1.5, 1.5],
-               "rotate", ["y", -90],
-               "translate", [21, 6, -3],
-               "scale", [32],
-               "texture", "Building",
-               "tilemap",
-               {
-                       "width": 3,
-                       "detail": 1,
-                       "tiles":
-                       [
-                               13,     13,     13,
-                               13,     13,     13
-                       ]
-               },
-
-               /* Finial */
-
-               "reset_transform",
-               "rotate", ["y", -90],
-               "translate", [21, 6, -3],
-               "scale", [32],
-               "texture", "Building",
-               "tilemap",
-               {
-                       "width": 3,
-                       "detail": 1,
-                       "tiles":
-                       [
-                               18,     18,     18
-                       ]
-               },
-
-       /* More ground to the right */
-               
-               /* Ground under house */
-
-               "reset_transform",
-               "rotate", ["x", 90],
-               "translate", [10, 0, 0],
-               "scale", [32],
-               "texture", "Scenery",
-               "tilemap",
-               {
-                       "width": 3,
-                       "surface_type": "top",
-                       "tiles":
-                       [
-                               1,      1,      1,
-                               1,      1,      1,
-                               -1,     -1,     -1,
-                               -1,     -1,     -1,
-                               -1,     -1,     -1,
-                               -1,     -1,     -1,
-                               1,      1,      1
-                       ]
-               },
-
-               /* Left part of center courtyard */
-
-               "reset_transform",
-               "rotate", ["x", 90],
-               "translate", [13, 0, 0],
-               "scale", [32],
-               "texture", "Scenery",
-               "tilemap",
-               {
-                       "width": 8,
-                       "surface_type": "top",
-                       "tiles":
-                       [
-                               1,      1,      1,      1,      1,      1,      1,      1,
-                               1,      1,      1,      1,      1,      1,      1,      1,
-                               1,      0,      0,      0,      0,      0,      0,      0,
-                               1,      0,      0,      0,      0,      0,      1,      1,
-                               0,      0,      0,      0,      0,      0,      1,      1,
-                               1,      0,      0,      0,      0,      0,      0,      0,
-                               1,      1,      1,      1,      1,      0,      0,      0
-                       ]
-               },
-
-               /* Front grass */
-
-               "reset_transform",
-               "scale", [12, 1, 1],
-               "translate", [14, -0.5, 5],
-               "scale", [32],
-               "texture", "Scenery",
-               "billboard",
-               {
-                       "tile": 2,
-                       "u_scale": 12,
-                       "detail": 2
-               },
-
-               /* Back grass */
-
-               "reset_transform",
-               "scale", [4, 1, 1],
-               "translate", [14, -0.5, 1],
-               "scale", [32],
-               "texture", "Scenery",
-               "billboard",
-               {
-                       "tile": 2,
-                       "u_scale": 4,
-                       "detail": 2
-               },
-
-               /* Front grass next to door */
-
-               "reset_transform",
-               "scale", [1, 1, 1],
-               "translate", [13, -0.5, 3],
-               "scale", [32],
-               "texture", "Scenery",
-               "billboard",
-               {
-                       "tile": 2,
-                       "u_scale": 1,
-                       "detail": 2
-               },
-
-               /* Back grass next to door */
-
-               "reset_transform",
-               "scale", [1, 1, 1],
-               "translate", [13, -0.5, 2],
-               "scale", [32],
-               "texture", "Scenery",
-               "billboard",
-               {
-                       "tile": 2,
-                       "u_scale": 1,
-                       "detail": 2
-               },
-
-               /* Left grass */
-
-               "reset_transform",
-               "rotate", ["y", -90],
-               "translate", [14, -0.5, 1],
-               "scale", [32],
-               "texture", "Scenery",
-               "tilemap",
-               {
-                       "width": 4,
-                       "detail": 2,
-                       "tiles":
-                       [
-                               2,      -1,     2,      2
-                       ]
-               },
-
-               /* Grass left of house */
-
-               "reset_transform",
-               "rotate", ["y", -90],
-               "scale", [1, 1, 1],
-               "translate", [18, -0.5, 0],
-               "scale", [32],
-               "texture", "Scenery",
-               "billboard",
-               {
-                       "tile": 2,
-                       "u_scale": 1,
-                       "detail": 2
-               },
-
-               /* Grass right of house */
-
-               "reset_transform",
-               "rotate", ["y", -90],
-               "scale", [1, 1, 1],
-               "translate", [24, -0.5, 0],
-               "scale", [32],
-               "texture", "Scenery",
-               "billboard",
-               {
-                       "tile": 2,
-                       "u_scale": 1,
-                       "detail": 2
-               },
-
-               /* Front grass in center */
-
-               "reset_transform",
-               "scale", [4, 1, 1],
-               "translate", [19, -0.5, 4],
-               "scale", [32],
-               "texture", "Scenery",
-               "billboard",
-               {
-                       "tile": 2,
-                       "u_scale": 4,
-                       "detail": 2
-               },
-
-               /* Back grass in center */
-
-               "reset_transform",
-               "scale", [4, 1, 1],
-               "translate", [19, -0.5, 2],
-               "scale", [32],
-               "texture", "Scenery",
-               "billboard",
-               {
-                       "tile": 2,
-                       "u_scale": 4,
-                       "detail": 2
-               },
-
-               /* Left grass in center */
-
-               "reset_transform",
-               "scale", [2, 1, 1],
-               "rotate", ["y", -90],
-               "translate", [19, -0.5, 2],
-               "scale", [32],
-               "texture", "Scenery",
-               "billboard",
-               {
-                       "tile": 2,
-                       "u_scale": 2,
-                       "detail": 2
-               },
-
-               /* Right grass in center */
-
-               "reset_transform",
-               "scale", [2, 1, 1],
-               "rotate", ["y", -90],
-               "translate", [23, -0.5, 2],
-               "scale", [32],
-               "texture", "Scenery",
-               "billboard",
-               {
-                       "tile": 2,
-                       "u_scale": 2,
-                       "detail": 2
-               },
-
-       /* Still more ground */
-               
-               /* Right part of center courtyard */
-
-               "reset_transform",
-               "rotate", ["x", 90],
-               "translate", [21, 0, 0],
-               "scale", [32],
-               "texture", "Scenery",
-               "tilemap",
-               {
-                       "width": 7,
-                       "surface_type": "top",
-                       "tiles":
-                       [
-                               1,      1,      1,      1,      1,      0,      0,
-                               1,      1,      1,      1,      1,      0,      0,
-                               0,      0,      0,      0,      0,      0,      0,
-                               1,      1,      0,      0,      0,      0,      0,
-                               1,      1,      0,      0,      0,      0,      0,
-                               0,      0,      0,      0,      0,      0,      0,
-                               0,      0,      0,      1,      1,      1,      1
-                       ]
-               },
-
-               /* Fence to right of back house */
-
-               "reset_transform",
-               "scale", [4, 1, 1],
-               "translate", [24, 0, 0],
-               "scale", [32],
-               "texture", "Scenery",
-               "billboard",
-               {
-                       "tile": 4,
-                       "u_scale": 4,
-                       "detail": 2
-               },
-
-               /* Grass in front of fence */
-
-               "reset_transform",
-               "scale", [4, 1, 1],
-               "translate", [24, -0.5, 1],
-               "scale", [32],
-               "texture", "Scenery",
-               "billboard",
-               {
-                       "tile": 2,
-                       "u_scale": 4,
-                       "detail": 2
-               },
-               
-               /* Grass to left of tower block */
-
-               "reset_transform",
-               "scale", [2, 1, 1],
-               "rotate", ["y", -90],
-               "translate", [26, -0.5, 5],
-               "scale", [32],
-               "texture", "Scenery",
-               "billboard",
-               {
-                       "tile": 2,
-                       "u_scale": 2,
-                       "detail": 2
-               },
-
-               /* Grass to right of tower block */
-
-               "reset_transform",
-               "scale", [2, 1, 1],
-               "rotate", ["y", -90],
-               "translate", [35, -0.5, 5],
-               "scale", [32],
-               "texture", "Scenery",
-               "billboard",
-               {
-                       "tile": 2,
-                       "u_scale": 2,
-                       "detail": 2
-               },
-
-               /* Next bit of grass */
-
-               "reset_transform",
-               "scale", [5, 1, 1],
-               "translate", [35, -0.5, 5],
-               "scale", [32],
-               "texture", "Scenery",
-               "billboard",
-               {
-                       "tile": 2,
-                       "u_scale": 5,
-                       "detail": 2
-               },
-
-               /* Back grass */
-
-               "reset_transform",
-               "scale", [6, 1, 1],
-               "translate", [34, -0.5, 1],
-               "scale", [32],
-               "texture", "Scenery",
-               "billboard",
-               {
-                       "tile": 2,
-                       "u_scale": 6,
-                       "detail": 2
-               },
-
-               /* Extra bit of back grass */
-
-               "reset_transform",
-               "scale", [1, 1, 1],
-               "rotate", ["y", -90],
-               "translate", [34, -0.5, 0],
-               "scale", [32],
-               "texture", "Scenery",
-               "billboard",
-               {
-                       "tile": 2,
-                       "u_scale": 1,
-                       "detail": 2
-               },
-
-               /* Ground around tower block */
-
-               "reset_transform",
-               "rotate", ["x", 90],
-               "translate", [28, 0, 4],
-               "scale", [32],
-               "texture", "Scenery",
-               "tilemap",
-               {
-                       "width": 5,
-                       "surface_type": "top",
-                       "tiles":
-                       [
-                               0,      0,      0,      0,      0,
-                               0,      0,      0,      0,      0,
-                               0,      0,      0,      0,      0
-                       ]
-               },
-
-               /* Rightmost ground */
-
-               "reset_transform",
-               "rotate", ["x", 90],
-               "translate", [33, 0, 0],
-               "scale", [32],
-               "texture", "Scenery",
-               "tilemap",
-               {
-                       "width": 10,
-                       "surface_type": "top",
-                       "tiles":
-                       [
-                               0,      0,      1,      1,      1,      1,      1,      1,      1,      1,
-                               0,      0,      1,      1,      1,      1,      1,      1,      1,      1,
-                               0,      0,      0,      0,      0,      0,      0,      -1,     -1,     -1,
-                               0,      0,      0,      0,      0,      0,      0,      -1,     -1,     -1,
-                               0,      0,      0,      0,      0,      0,      0,      -1,     -1,     -1,
-                               0,      0,      0,      0,      0,      0,      0,      -1,     -1,     -1,
-                               0,      1,      1,      1,      1,      1,      1,      -1,     -1,     -1
-                       ]
-               },
-
-       /* Right foreground tower block */
-               
-               /* Front */
-
-               "reset_transform",
-               "translate", [28, 0, 4],
-               "scale", [32],
-               "texture", "TowerBlock1",
-               "tilemap",
-               {
-                       "width": 5,
-                       "tiles":
-                       [
-                               2,      2,      2,      2,      2,
-                               0,      0,      0,      0,      0,
-                               0,      0,      0,      0,      6,
-                               0,      0,      0,      0,      0,
-                               0,      0,      0,      0,      0,
-                               0,      0,      0,      6,      0,
-                               4,      4,      4,      4,      4
-                       ]
-               },
-
-               /* Right side */
-
-               "reset_transform",
-               "rotate", ["y", 90],
-               "translate", [33, 0, 4],
-               "scale", [32],
-               "texture", "TowerBlock1",
-               "tilemap",
-               {
-                       "width": 6,
-                       "surface_type": "right",
-                       "tiles":
-                       [
-                               2,      2,      2,      2,      2,      2,
-                               0,      1,      0,      0,      1,      0,
-                               0,      1,      0,      0,      1,      0,
-                               0,      1,      0,      0,      1,      0,
-                               0,      1,      0,      0,      1,      0,
-                               0,      1,      0,      0,      1,      0,
-                               5,      4,      5,      5,      4,      5
-                       ]
-               },
-
-               /* Left side */
-
-               "reset_transform",
-               "rotate", ["y", 90],
-               "translate", [28, 0, 4],
-               "scale", [32],
-               "texture", "TowerBlock1",
-               "tilemap",
-               {
-                       "width": 6,
-                       "surface_type": "left",
-                       "tiles":
-                       [
-                               2,      2,      2,      2,      2,      2,
-                               0,      1,      6,      0,      1,      0,
-                               0,      1,      0,      0,      1,      0,
-                               0,      1,      0,      0,      1,      0,
-                               0,      1,      0,      0,      1,      0,
-                               0,      1,      0,      0,      1,      0,
-                               5,      4,      5,      5,      4,      5
-                       ]
-               },
-
-               /* Top */
-
-               "reset_transform",
-               "rotate", ["x", 90],
-               "translate", [28, 7, -2],
-               "scale", [32],
-               "texture", "TowerBlock1",
-               "tilemap",
-               {
-                       "width": 5,
-                       "surface_type": "top",
-                       "tiles":
-                       [
-                               3,      3,      3,      3,      3,
-                               3,      3,      3,      3,      3,
-                               3,      3,      3,      3,      3,
-                               3,      3,      3,      3,      3,
-                               3,      3,      3,      3,      3,
-                               3,      3,      3,      3,      3
-                       ]
-               },
-
-       /* Right end tower block */
-
-               /* Front */
-
-               "reset_transform",
-               "translate", [40, 0, 5],
-               "scale", [32],
-               "texture", "TowerBlock1",
-               "tilemap",
-               {
-                       "width": 5,
-                       "tiles":
-                       [
-                               2,      2,      2,      2,      2,
-                               0,      1,      0,      0,      1,
-                               0,      1,      0,      0,      1,
-                               0,      1,      0,      0,      1,
-                               0,      1,      0,      0,      1,
-                               6,      1,      0,      0,      1,
-                               0,      1,      0,      0,      1,
-                               0,      1,      0,      0,      1,
-                               0,      1,      0,      0,      1,
-                               0,      1,      0,      0,      1,
-                               0,      1,      0,      0,      1,
-                               6,      1,      0,      0,      1,
-                               0,      1,      0,      0,      1,
-                               0,      1,      0,      0,      1,
-                               4,      4,      4,      4,      4
-                       ]
-               },
-
-               /* Left side */
-
-               "reset_transform",
-               "rotate", ["y", 90],
-               "translate", [40, 0, 5],
-               "scale", [32],
-               "texture", "TowerBlock1",
-               "tilemap",
-               {
-                       "width": 5,
-                       "surface_type": "left",
-                       "tiles":
-                       [
-                               2,      2,      2,      2,      2,
-                               6,      0,      0,      0,      0,
-                               0,      0,      0,      0,      0,
-                               0,      0,      0,      0,      0,
-                               0,      0,      0,      6,      0,
-                               0,      0,      0,      0,      0,
-                               0,      0,      0,      0,      0,
-                               0,      0,      6,      0,      0,
-                               0,      0,      0,      0,      0,
-                               0,      0,      0,      0,      0,
-                               0,      0,      0,      0,      0,
-                               0,      0,      0,      0,      0,
-                               0,      0,      0,      0,      0,
-                               0,      0,      0,      0,      0,
-                               4,      5,      5,      5,      4
-                       ]
-               },
-
-               /* Top */
-
-               "reset_transform",
-               "rotate", ["x", 90],
-               "translate", [40, 15, 0],
-               "scale", [32],
-               "texture", "TowerBlock1",
-               "tilemap",
-               {
-                       "width": 5,
-                       "surface_type": "top",
-                       "tiles":
-                       [
-                               3,      3,      3,      3,      3,
-                               3,      3,      3,      3,      3,
-                               3,      3,      3,      3,      3,
-                               3,      3,      3,      3,      3,
-                               3,      3,      3,      3,      3
-                       ]
-               },
-
-       /* Background */
-
-               "reset_transform",
-               "translate", [-0.3, -0.17, -900],
-               "scale", [3200, 1600, 1],
-               "texture", "BackgroundFar",
-               "billboard", null,
-
-               "translate", [0, 0, 300],
-               "texture", "BackgroundNear",
-               "billboard",
-               {
-                       "blend": true
-               },
-
-       /* Trees */
-
-               "texture", "Trees",
-
-               /* Left courtyard */
-
-               "reset_transform",
-               "scale", [96],
-               "translate", [250, -2.5, 16],
-               "billboard",
-               {
-                       "tile": 1,
-                       "detail": 1
-               },
-
-               /* Center courtyard */
-
-               "reset_transform",
-               "scale", [96],
-               "translate", [610, -2.5, 85],
-               "billboard",
-               {
-                       "tile": 0
-               },
-               "reset_transform",
-               "scale", [96],
-               "translate", [650, -2.5, 115],
-               "billboard",
-               {
-                       "tile": 1
-               },
-
-               /* Right courtyard */
-
-               "reset_transform",
-               "scale", [96],
-               "translate", [1080, -2.5, 10],
-               "billboard",
-               {
-                       "tile": 1,
-                       "detail": 1
-               },
-               "reset_transform",
-               "scale", [96],
-               "translate", [1120, -2.5, -15],
-               "billboard",
-               {
-                       "tile": 0,
-                       "detail": 1
-               },
-               "reset_transform",
-               "scale", [96],
-               "translate", [1220, -2.5, -30],
-               "billboard",
-               {
-                       "tile": 1,
-                       "detail": 1
-               }
-
-       ]
-}
index ca287ba65a32db632ebaee55d82ede8370d33ee5..1b3b1ba59f3d22a2dc75c3890bb97bfb74a88d00 100644 (file)
@@ -16,7 +16,8 @@ SLOT="0"
 KEYWORDS="amd64 ~ppc x86"
 IUSE="debug profile"
 
-RDEPEND="media-libs/freealut
+RDEPEND="dev-lang/lua
+       media-libs/freealut
        media-libs/libsdl[opengl]
        media-libs/libvorbis
        media-libs/openal
index ace7081e8a8efcf5a4b81caabf4cdc59c60b02e7..f59f9d6cc84013602451f4cebdac23fb27a753ab 100644 (file)
@@ -7,17 +7,19 @@ Group: Amusements/Games
 Source0: http://www.dogcows.com/yoink/%{name}-%{version}.tar.bz2
 BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
 BuildRequires: boost-devel
-BuildRequires: SDL-devel
-BuildRequires: SDL_image-devel
+BuildRequires: freealut-devel
 BuildRequires: libvorbis-devel
+BuildRequires: lua-devel
 BuildRequires: mesa-libGL-devel
 BuildRequires: openal-devel
-BuildRequires: freealut-devel
-Requires: SDL
-Requires: SDL_image
+BuildRequires: SDL-devel
+BuildRequires: SDL_image-devel
+Requires: freealut
 Requires: libvorbis
+Requires: lua
 Requires: openal
-Requires: freealut
+Requires: SDL
+Requires: SDL_image
 %description
 Leap tall buildings!  Crush stupid robots beneath your feet!  Wield your
 extra-terrestrial powers in the defence of humanity, and send those alien
index b5c4d9a13e676476f2652400eed53b4dd0f5d68a..6cf54b138cc77381ac2e8c1982376ea0e9e3386d 100644 (file)
@@ -21,7 +21,7 @@ OUT_FILE=${1:-yoinksetup-@VERSION@.exe}
 
 # DLL dependencies
 DLLS="SDL SDL_image zlib1 libpng12-0 OpenAL32 libalut-0 libvorbis-0 libogg-0"
-DLLS="$DLLS libvorbisfile-3"
+DLLS="$DLLS libvorbisfile-3 lua51"
 
 # Prepare
 ${STRIP:-strip} "$ROOT_DIR/src/yoink.exe"
@@ -324,15 +324,6 @@ SetOverwrite on
 CreateShortCut "\$DESKTOP\\Yoink.lnk" "\$INSTDIR\\yoink.exe" \
        "" "\$INSTDIR\\yoink.exe" 0
 SetOverwrite off
-SectionEnd
-
-       ; Yoink shortcut in start menu
-Section "$SEC_SHORTCUT2" SecStartMenuShortcut
-SetOverwrite on
-!insertmacro CreateDirectoryOnce "\$SMPROGRAMS\\Yoink"
-CreateShortCut "\$SMPROGRAMS\\Yoink\\Play Yoink!.lnk" \
-       "\$INSTDIR\\yoink.exe" "" "\$INSTDIR\\yoink.exe" 0
-SetOverwrite off
 SectionEnd
 
        ; Yoink uninstall shortcut in start menu
@@ -346,6 +337,15 @@ SetOverwrite on
 CreateShortCut  "\$SMPROGRAMS\\Yoink\\Uninstall.lnk" \
        "\$INSTDIR\\uninstall.exe" "" "\$INSTDIR\\uninstall.exe" 0
 SetOverwrite off
+SectionEnd
+
+       ; Yoink shortcut in start menu
+Section "$SEC_SHORTCUT2" SecStartMenuShortcut
+SetOverwrite on
+!insertmacro CreateDirectoryOnce "\$SMPROGRAMS\\Yoink"
+CreateShortCut "\$SMPROGRAMS\\Yoink\\Play Yoink!.lnk" \
+       "\$INSTDIR\\yoink.exe" "" "\$INSTDIR\\yoink.exe" 0
+SetOverwrite off
 SectionEnd
 
 SectionGroupEnd
index 384fa0a55c1fb9801902834431829d02d074cae0..80372e2487f91a01fc5b96c17d35d64fbb348768 100644 (file)
@@ -54,6 +54,7 @@ libmoof_a_SOURCES = \
                                        Moof/RK4.hh \
                                        Moof/Scene.cc \
                                        Moof/Scene.hh \
+                                       Moof/Script.hh \
                                        Moof/Serializable.cc \
                                        Moof/Serializable.hh \
                                        Moof/Serializer.cc \
index 2d9d3377011a5d6eef4f9984633f17da738b1c0c..369d4515ffe401aa75b8956cdecedb93638475ed 100644 (file)
@@ -34,9 +34,6 @@
 namespace Mf {
 
 
-Notification::~Notification() {}
-
-
 struct Dispatcher::Impl
 {
        Impl() :
index 14d8998bccd10392b3dc4cb4dd46fb678cb32eb0..bbce87872e67e1afdcf0b0c21b19f9ff56a83fd3 100644 (file)
@@ -46,7 +46,7 @@ namespace Mf {
 class Notification
 {
 public:
-       virtual ~Notification();
+       virtual ~Notification() {};
 };
 
 
@@ -61,6 +61,9 @@ class Dispatcher
 
 public:
 
+       // TODO - the Handler would be even better as an object which automagically
+       // removes itself from the dispatcher on destruction, so users don't have to
+       // worry about forgetting
        typedef void* Handler;
        typedef boost::function<void(const Notification*)> Function;
 
index 186961f00e7b3d28a55ea7e6bef9a69c7aceb945..e69e1edf817b67e7b4567ac508e88883ea7de52c 100644 (file)
@@ -98,7 +98,10 @@ struct OctreeNode : public Entity
                }
 
                if (!objects.empty())
+               {
                        aabb_.draw();
+                       sphere_.draw();
+               }
        }
 
 
index 418c0512e3c690e4f29406181cac8346c67ada80..2e1a79363302ebb527e8a0c9d7ce8a13480495f3 100644 (file)
 
 #include "Aabb.hh"
 #include "Camera.hh"
-#include "Deserializer.hh"
 #include "Entity.hh"
 #include "Log.hh"
 #include "Math.hh"
 #include "Scene.hh"
-#include "Serializable.hh"
+#include "Script.hh"
+#include "Settings.hh"
 #include "Tilemap.hh"
 
 
 namespace Mf {
 
 
-static void loadBox(Aabb& theBox, SerializableP obj)
+static std::string getPath(const std::string& name)
 {
-       Serializable::Array numbers;
-
-       if (obj->get(numbers) && numbers.size() == 6)
-       {
-               Serializable::Float num;
-
-               if (numbers[0]->getNumber(num)) theBox.min[0] = Scalar(num);
-               if (numbers[1]->getNumber(num)) theBox.min[1] = Scalar(num);
-               if (numbers[2]->getNumber(num)) theBox.min[2] = Scalar(num);
-               if (numbers[3]->getNumber(num)) theBox.max[0] = Scalar(num);
-               if (numbers[4]->getNumber(num)) theBox.max[1] = Scalar(num);
-               if (numbers[5]->getNumber(num)) theBox.max[2] = Scalar(num);
-       }
+       return Resource::getPath("scenes/" + name + ".lua");
 }
 
 
-static void loadTilemap(SerializableP root, const Matrix4& transform,
-               const std::string& texture, OctreeP octree)
+struct Meh
 {
-       Serializable::Map rootObj;
-       Serializable::Map::iterator it;
+       Matrix4         transform;
+       std::string     texture;
+
+       OctreeP         octree;
 
-       if (!root->get(rootObj))
+       Meh()
        {
-               logError("invalid tilemap instruction");
-               return;
+               octree = Octree::alloc(Aabb());
        }
 
-       long width = 1;
-       long height = 1;
-       std::vector< std::vector<Tilemap::Index> > indices;
-
-       if ((it = rootObj.find("width")) != rootObj.end())
+       static int loadBox(Script& script, Aabb& aabb)
        {
-               (*it).second->get(width);
+               Script::Value table[] = {script[1], script[2]};
+
+               if (!table[0].isTable() || !table[1].isTable())
+               {
+                       logWarning("wrong arguments to setPlayfieldBounds; ignoring...");
+                       return 0;
+               }
+
+               for (int i = 0; i <= 1; ++i)
+               {
+                       for (int j = 1; j <= 3; ++j)
+                       {
+                               script.push((long)j);
+                               table[i].pushField();
+                       }
+               }
+
+               script[3].get(aabb.min[0]);
+               script[4].get(aabb.min[1]);
+               script[5].get(aabb.min[2]);
+               script[6].get(aabb.max[0]);
+               script[7].get(aabb.max[1]);
+               script[8].get(aabb.max[2]);
+
+               return 0;
        }
-       else
+
+       int setPlayfieldBounds(Script& script)
        {
-               logError("missing required field width for tilemap instruction");
-               return;
+               Aabb bounds;
+               return loadBox(script, bounds);
        }
 
-       Serializable::Array tiles;
-
-       if ((it = rootObj.find("tiles")) != rootObj.end() &&
-                       (*it).second->get(tiles) &&
-                       tiles.size() % width == 0)
+       int setMaximumBounds(Script& script)
        {
-               Serializable::Array::iterator jt;
-               int w, h;
+               Aabb bounds;
+               int ret = loadBox(script, bounds);
+               octree = Octree::alloc(bounds);
+               return ret;
+       }
 
-               height = tiles.size() / width;
-               indices.resize(height);
+       int resetTransform(Script& script)
+       {
+               transform.identity();
+               return 0;
+       }
 
-               // the indices are stored upside-down in the scene file so that they
-               // are easier to edit as text, so we'll need to load them last row
-               // first
+       int translate(Script& script)
+       {
+               Script::Value x = script[1];
+               Script::Value y = script[2];
+               Script::Value z = script[3];
 
-               for (h = height - 1, jt = tiles.begin(); jt != tiles.end(); --h)
+               if (!x.isNumber() || !y.isNumber() || !z.isNumber())
                {
-                       std::vector<Tilemap::Index> row;
+                       logWarning("wrong arguments to translate; ignoring...");
+                       return 0;
+               }
 
-                       for (w = 0; w < width && jt != tiles.end(); ++w, ++jt)
-                       {
-                               Serializable::Integer index;
+               Vector3 vec;
+               x.get(vec[0]);
+               y.get(vec[1]);
+               z.get(vec[2]);
 
-                               if ((*jt)->get(index))
-                               {
-                                       row.push_back(Tilemap::Index(index));
-                               }
-                       }
+               Matrix4 translation;
+               cml::matrix_translation(translation, vec);
+               transform = translation * transform;
 
-                       indices[h] = row;
-               }
+               return 0;
        }
-       else
+
+       int scale(Script& script)
        {
-               logError("invalid tiles in tilemap instruction");
-               return;
-       }
+               if (script.getSize() == 3)
+               {
+                       Vector3 vec;
+                       script[1].get(vec[0]);
+                       script[2].get(vec[1]);
+                       script[3].get(vec[2]);
+
+                       Matrix4 scaling;
+                       cml::matrix_scale(scaling, vec);
+                       transform = scaling * transform;
+               }
+               else if (script.getSize() == 1)
+               {
+                       Scalar value = 1.0;
+                       script[1].get(value);
 
-       Vector4 vertices[height+1][width+1];
+                       Matrix4 scaling;
+                       cml::matrix_uniform_scale(scaling,
+                                       Scalar(value));
+                       transform = scaling * transform;
+               }
+               else
+               {
+                       logWarning("wrong arguments to scale; ignoring...");
+               }
 
-       Matrix4 transposedTransform = transform;
-       transposedTransform.transpose();
+               return 0;
+       }
 
-       for (int h = 0; h <= height; ++h)
+       int rotate(Script& script)
        {
-               for (int w = 0; w <= width; ++w)
+               Script::Value a = script[1];
+               Script::Value d = script[2];
+
+               if (!a.isString() || !d.isNumber())
                {
-                       vertices[h][w] = Vector4(Scalar(w), Scalar(h), 0.0, 1.0) *
-                               transposedTransform;
+                       logWarning("wrong arguments to rotate; ignoring...");
+                       return 0;
                }
+
+               std::string axis;
+               a.get(axis);
+
+               size_t index = 0;
+               if (axis == "x")      index = 0;
+               else if (axis == "y") index = 1;
+               else if (axis == "z") index = 2;
+
+               Scalar value;
+               d.get(value);
+
+               cml::matrix_rotate_about_world_axis(transform,
+                               index, cml::rad(Scalar(value)));
+
+               return 0;
        }
 
-       for (int h = 0; h < height; ++h)
+       int setTexture(Script& script)
        {
-               for (int w = 0; w < width; ++w)
-               {
-                       if (indices[h][w] == Tilemap::NO_TILE) continue;
+               Script::Value t = script[1];
 
-                       Vector3 quadVertices[4];
+               if (t.isString()) t.get(texture);
+               else logWarning("wrong arguments to setTexture; ignoring...");
 
-                       demoteVector(quadVertices[0], vertices[h][w]);
-                       demoteVector(quadVertices[1], vertices[h][w+1]);
-                       demoteVector(quadVertices[2], vertices[h+1][w+1]);
-                       demoteVector(quadVertices[3], vertices[h+1][w]);
+               return 0;
+       }
 
-                       Quad* quad = new Quad(quadVertices, texture, indices[h][w]);
-                       boost::shared_ptr<Quad> quadPtr(quad);
+       int makeTilemap(Script& script)
+       {
+               Script::Value table = script[1];
+               Script::Value top = script[-1];
 
-                       octree->insert(quadPtr);
+               if (!table.isTable())
+               {
+                       logWarning("wrong arguments to makeTilemap; ignoring...");
+                       return 0;
                }
-       }
-}
 
-static void loadBillboard(SerializableP root, const Matrix4& transform,
-               const std::string& texture, OctreeP octree)
-{
-       Serializable::Map rootObj;
-       Serializable::Map::iterator it;
+               long width = 1;
+               long height = 1;
 
-       Tilemap::Index  index = 0;
-       long                    width = 1;
-       bool                    blending = false;
-       bool                    fog = false;
+               table.pushField("width");
+               top.get(width);
 
-       if (root->get(rootObj))
-       {
-               if ((it = rootObj.find("tile")) != rootObj.end())
+               long nTiles = 0;
+
+               table.pushField("tiles");
+               Script::Value tiles = script.getTop();
+               nTiles = tiles.getLength();
+
+               std::vector< std::vector<Tilemap::Index> > indices;
+
+               if (nTiles % width == 0)
                {
-                       Serializable::Integer value;
-                       if ((*it).second->get(value))
+                       int i, w, h;
+
+                       height = nTiles / width;
+                       indices.resize(height);
+
+                       // the indices are stored upside-down in the scene file so that they
+                       // are easier to edit as text, so we'll need to load them last row
+                       // first
+
+                       i = 1;
+                       for (h = height - 1; h >= 0; --h)
                        {
-                               index = Tilemap::Index(value);
+                               std::vector<Tilemap::Index> row;
+
+                               for (w = 0; w < width; ++w, ++i)
+                               {
+                                       script.checkStack(2);
+                                       script.push(long(i));
+                                       tiles.pushField();
+
+                                       long index;
+                                       top.get(index);
+
+                                       row.push_back(Tilemap::Index(index));
+                               }
+
+                               indices[h] = row;
                        }
                }
-
-               if ((it = rootObj.find("u_scale")) != rootObj.end())
+               else
                {
-                       (*it).second->get(width);
+                       logError("invalid tiles in tilemap instruction");
+                       return 0;
                }
 
-               if ((it = rootObj.find("blend")) != rootObj.end())
-               {
-                       (*it).second->get(blending);
-               }
+               Vector4 vertices[height+1][width+1];
+
+               Matrix4 transposedTransform = transform;
+               transposedTransform.transpose();
 
-               if ((it = rootObj.find("fog")) != rootObj.end())
+               for (int h = 0; h <= height; ++h)
                {
-                       (*it).second->get(fog);
+                       for (int w = 0; w <= width; ++w)
+                       {
+                               vertices[h][w] = Vector4(Scalar(w), Scalar(h), 0.0, 1.0) *
+                                       transposedTransform;
+                       }
                }
-       }
 
+               for (int h = 0; h < height; ++h)
+               {
+                       for (int w = 0; w < width; ++w)
+                       {
+                               if (indices[h][w] == Tilemap::NO_TILE) continue;
 
-       Vector4 vertices[2][width+1];
+                               Vector3 quadVertices[4];
 
-       Matrix4 transposedTransform = transform;
-       transposedTransform.transpose();
+                               demoteVector(quadVertices[0], vertices[h][w]);
+                               demoteVector(quadVertices[1], vertices[h][w+1]);
+                               demoteVector(quadVertices[2], vertices[h+1][w+1]);
+                               demoteVector(quadVertices[3], vertices[h+1][w]);
 
-       Scalar xf;
-       Scalar increment = 1.0 / Scalar(width);
+                               Quad* quad = new Quad(quadVertices, texture, indices[h][w]);
+                               boost::shared_ptr<Quad> quadPtr(quad);
 
-       for (int h = 0; h <= 1; ++h)
-       {
-               xf = 0.0;
-               for (int w = 0; w <= width; ++w, xf += increment)
-               {
-                       vertices[h][w] = Vector4(xf, Scalar(h), 0.0, 1.0) *
-                               transposedTransform;
+                               octree->insert(quadPtr);
+                       }
                }
+
+               return 0;
        }
 
-       for (int w = 0; w < width; ++w)
+       int makeBillboard(Script& script)
        {
-               Vector3 quadVertices[4];
-
-               demoteVector(quadVertices[0], vertices[0][w]);
-               demoteVector(quadVertices[1], vertices[0][w+1]);
-               demoteVector(quadVertices[2], vertices[1][w+1]);
-               demoteVector(quadVertices[3], vertices[1][w]);
+               Script::Value table = script[1];
+               Script::Value top = script[-1];
 
-               Quad* quad = new Quad(quadVertices, texture, index);
-               quad->setBlending(blending);
-               quad->setFog(fog);
+               long    index = 0;
+               long    width = 1;
+               bool    blending = false;
+               bool    fog = false;
 
-               boost::shared_ptr<Quad> quadPtr(quad);
+               if (table.isTable())
+               {
+                       table.pushField("tile");
+                       if (top.isNumber()) top.get(index);
 
-               octree->insert(quadPtr);
-       }
-}
+                       table.pushField("u_scale");
+                       if (top.isNumber()) top.get(width);
 
+                       table.pushField("blend");
+                       if (top.isBoolean()) top.get(blending);
 
-static void loadInstructions(SerializableP root, OctreeP octree)
-{
-       Serializable::Array rootObj;
-       Serializable::Array::iterator it;
+                       table.pushField("fog");
+                       if (top.isBoolean()) top.get(fog);
+               }
 
-       if (!root->get(rootObj))
-       {
-               logError("scene instructions must be an array");
-               return;
-       }
+               Vector4 vertices[2][width+1];
 
-       Matrix4         transform;
-       std::string     texture;
+               Matrix4 transposedTransform = transform;
+               transposedTransform.transpose();
 
-       for (it = rootObj.begin(); it != rootObj.end(); ++it)
-       {
-               std::string instruction;
+               Scalar xf;
+               Scalar increment = 1.0 / Scalar(width);
 
-               if ((*it)->get(instruction))
+               for (int h = 0; h <= 1; ++h)
                {
-                       if (instruction == "reset_transform")
+                       xf = 0.0;
+                       for (int w = 0; w <= width; ++w, xf += increment)
                        {
-                               transform.identity();
+                               vertices[h][w] = Vector4(xf, Scalar(h), 0.0, 1.0) *
+                                       transposedTransform;
                        }
-                       else if (instruction == "translate")
-                       {
-                               Serializable::Array values;
+               }
 
-                               ++it;
-                               if ((*it)->get(values))
-                               {
-                                       Vector3 vec;
+               for (int w = 0; w < width; ++w)
+               {
+                       Vector3 quadVertices[4];
 
-                                       for (size_t i = 0; i < values.size(); ++i)
-                                       {
-                                               Serializable::Float value;
+                       demoteVector(quadVertices[0], vertices[0][w]);
+                       demoteVector(quadVertices[1], vertices[0][w+1]);
+                       demoteVector(quadVertices[2], vertices[1][w+1]);
+                       demoteVector(quadVertices[3], vertices[1][w]);
 
-                                               if (values[i]->getNumber(value))
-                                               {
-                                                       vec[i] = value;
-                                               }
-                                       }
+                       Quad* quad = new Quad(quadVertices, texture, Tilemap::Index(index));
+                       quad->setBlending(blending);
+                       quad->setFog(fog);
 
-                                       Matrix4 translation;
-                                       cml::matrix_translation(translation, vec);
-                                       transform = translation * transform;
-                               }
-                       }
-                       else if (instruction == "scale")
-                       {
-                               Serializable::Array values;
+                       boost::shared_ptr<Quad> quadPtr(quad);
 
-                               ++it;
-                               if ((*it)->get(values))
-                               {
-                                       if (values.size() == 1)
-                                       {
-                                               Serializable::Float value = 1.0;
-
-                                               values[0]->getNumber(value);
-
-                                               Matrix4 scaling;
-                                               cml::matrix_uniform_scale(scaling,
-                                                               Scalar(value));
-                                               transform = scaling * transform;
-                                       }
-                                       else if (values.size() == 3)
-                                       {
-                                               Vector3 vec;
-
-                                               for (size_t i = 0; i < values.size(); ++i)
-                                               {
-                                                       Serializable::Float value;
-
-                                                       if (values[i]->getNumber(value))
-                                                       {
-                                                               vec[i] = value;
-                                                       }
-                                               }
-
-                                               Matrix4 scaling;
-                                               cml::matrix_scale(scaling, vec);
-                                               transform = scaling * transform;
-                                       }
-                               }
-                       }
-                       else if (instruction == "rotate")
-                       {
-                               Serializable::Array values;
+                       octree->insert(quadPtr);
+               }
 
-                               ++it;
-                               if ((*it)->get(values))
-                               {
-                                       if (values.size() == 2)
-                                       {
-                                               std::string axis;
-                                               size_t index = 0;
-                                               Serializable::Float value = 0.0;
-
-                                               if (values[0]->get(axis))
-                                               {
-                                                       if (axis == "x")      index = 0;
-                                                       else if (axis == "y") index = 1;
-                                                       else if (axis == "z") index = 2;
-
-                                                       values[1]->getNumber(value);
-                                               }
-
-                                               cml::matrix_rotate_about_world_axis(transform,
-                                                               index, cml::rad(Scalar(value)));
-                                       }
-                               }
-                       }
-                       else if (instruction == "texture")
-                       {
-                               ++it;
-                               (*it)->get(texture);
-                       }
-                       else if (instruction == "tilemap")
-                       {
-                               ++it;
-                               loadTilemap(*it, transform, texture, octree);
-                       }
-                       else if (instruction == "billboard")
-                       {
-                               ++it;
-                               loadBillboard(*it, transform, texture, octree);
-                       }
+               return 0;
+       }
+};
+
+
+static int luaPrint(Script& script)
+{
+       Script::Value param = script[1];
+
+       while (!param.isNone())
+       {
+               if (param.isString())
+               {
+                       std::string str;
+                       param.get(str);
+                       logInfo("lua: %s", str.c_str());
+               }
+               else if (param.isBoolean())
+               {
+                       if (param) logInfo("lua: true");
+                       else logInfo("lua: false");
+
+               }
+               else if (param.isNil())
+               {
+                       logInfo("lua: nil");
                }
+               else
+               {
+                       logInfo("lua: %s (%X)", param.getTypeName().c_str(),
+                                       param.getIdentifier());
+               }
+
+               param.index++;
        }
-}
 
+       return 0;
+}
 
-static std::string getPath(const std::string& name)
+static void importScriptBindings(Script& script, Meh& scene)
 {
-       return Resource::getPath("scenes/" + name + ".json");
+       script.importFunction("SetPlayfieldBounds",
+                       boost::bind(&Meh::setPlayfieldBounds, &scene, _1));
+       script.importFunction("SetMaximumBounds",
+                       boost::bind(&Meh::setMaximumBounds, &scene, _1));
+       script.importFunction("ResetTransform",
+                       boost::bind(&Meh::resetTransform, &scene, _1));
+       script.importFunction("Translate",
+                       boost::bind(&Meh::translate, &scene, _1));
+       script.importFunction("Scale",
+                       boost::bind(&Meh::scale, &scene, _1));
+       script.importFunction("Rotate",
+                       boost::bind(&Meh::rotate, &scene, _1));
+       script.importFunction("SetTexture",
+                       boost::bind(&Meh::setTexture, &scene, _1));
+       script.importFunction("MakeTilemap",
+                       boost::bind(&Meh::makeTilemap, &scene, _1));
+       script.importFunction("MakeBillboard",
+                       boost::bind(&Meh::makeBillboard, &scene, _1));
+       script.importFunction("print", luaPrint);
 }
 
+
 OctreeP loadScene(const std::string& name)
 {
        std::string filePath = getPath(name);
 
-       Deserializer    deserializer(filePath, true);
-       SerializableP   root = deserializer.deserialize();
-
-       Serializable::Map rootObj;
-       Serializable::Map::iterator it;
-
-       if (!root || !root->get(rootObj))
-       {
-               logError("no root map in scene file");
-               return OctreeP();
-       }
+       Script script;
+       script.importStandardLibraries();
 
-       Aabb playfieldBounds;
-       Aabb maximumBounds;
+       Meh cool;
+       importScriptBindings(script, cool);
 
-       if ((it = rootObj.find("playfield_bounds")) != rootObj.end())
-       {
-               loadBox(playfieldBounds, (*it).second);
-       }
-       if ((it = rootObj.find("maximum_bounds")) != rootObj.end())
-       {
-               loadBox(maximumBounds, (*it).second);
-       }
-       else
-       {
-               logError("missing required maximum bounds");
-               return OctreeP();
-       }
+       long detail = 3;
+       Settings::getInstance().getNumber("game.detail", detail);
 
-       // create the tree to store the quads
-       OctreeP octree = Octree::alloc(maximumBounds);
+       script.push(detail);
+       script.set("detail");
 
-       if ((it = rootObj.find("instructions")) != rootObj.end())
+       logInfo("doing file...");
+       if (script.doFile(filePath) != 0)
        {
-               loadInstructions((*it).second, octree);
+               std::string str;
+               script[-1].get(str);
+               logError("lua error: %s", str.c_str());
        }
+       logInfo("done");
 
-       octree->sort();
-
-       return octree;
+       cool.octree->sort();
+       return cool.octree;
 }
 
 
diff --git a/src/Moof/Script.hh b/src/Moof/Script.hh
new file mode 100644 (file)
index 0000000..b48f5f4
--- /dev/null
@@ -0,0 +1,670 @@
+
+/*******************************************************************************
+
+ Copyright (c) 2009, Charles McGarvey
+ All rights reserved.
+ Redistribution   and   use  in  source  and  binary  forms,  with  or  without
+ modification, are permitted provided that the following conditions are met:
+   * Redistributions  of  source  code  must retain the above copyright notice,
+     this list of conditions and the following disclaimer.
+   * Redistributions  in binary form must reproduce the above copyright notice,
+     this  list of conditions and the following disclaimer in the documentation
+     and/or other materials provided with the distribution.
+ THIS  SOFTWARE  IS  PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND  ANY  EXPRESS  OR  IMPLIED  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED.  IN  NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ FOR  ANY  DIRECT,  INDIRECT,  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES  (INCLUDING,  BUT  NOT  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES;  LOSS  OF  USE,  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED  AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*******************************************************************************/
+
+#ifndef _MOOF_SCRIPT_HH_
+#define _MOOF_SCRIPT_HH_
+
+/**
+ * @file Script.hh
+ * A thin wrapper over Lua.  This is not meant as a complicated binding package
+ * between C++ and Lua.  It does not try to make the boundary invisible.  It
+ * does not hide the concept of the Lua stack, but rather provides that
+ * mechanism with a certain level of abstraction while also providing a cleaner,
+ * more consistent API.
+ */
+
+#include <list>
+#include <string>
+
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <lua.hpp>
+
+#include <Moof/Exception.hh>
+
+
+namespace Mf {
+
+
+class Script;
+typedef boost::shared_ptr<Script> ScriptP;
+
+
+struct Script
+{
+       typedef boost::function<int(Script&)> Function;
+
+       enum TYPE
+       {
+               NONE                    = LUA_TNONE,
+               NIL                             = LUA_TNIL,
+               BOOLEAN                 = LUA_TBOOLEAN,
+               LIGHTUSERDATA   = LUA_TLIGHTUSERDATA,
+               NUMBER                  = LUA_TNUMBER,
+               STRING                  = LUA_TSTRING,
+               TABLE                   = LUA_TTABLE,
+               FUNCTION                = LUA_TFUNCTION,
+               USERDATA                = LUA_TUSERDATA,
+               THREAD                  = LUA_TTHREAD
+       };
+
+       enum STATUS
+       {
+               SUCCESS                 = 0,
+               YIELD                   = LUA_YIELD,
+               RUNTIME_ERROR   = LUA_ERRRUN,
+               SYNTAX_ERROR    = LUA_ERRSYNTAX,
+               MEMORY_ERROR    = LUA_ERRMEM,
+               HANDLER_ERROR   = LUA_ERRERR,
+               FILE_ERROR              = LUA_ERRFILE
+       };
+
+       enum PSEUDO_INDEX
+       {
+               REGISTRY                = LUA_REGISTRYINDEX,
+               ENVIRONMENT             = LUA_ENVIRONINDEX,
+               GLOBALS                 = LUA_GLOBALSINDEX
+       };
+
+       /**
+        * This is the most noticeable abstraction on top of the standard Lua API.
+        * A Value object represents a value on the stack.  More specifically, it
+        * represents a position on the stack.  The distinction is only important
+        * when values are moved around on the stack or if the Value represents a
+        * negative index on the stack (the value of which will change as things are
+        * pushed onto and popped from the stack).
+        */
+
+       struct Value
+       {
+               /**
+                * You have direct access to the index of the value on the stack being
+                * represented.
+                */
+
+               int index;
+
+
+               /**
+                * A default-constructed Value is invalid until a valid Value is
+                * assigned to it.  The only method that should be called on such a
+                * Value is isValid(), otherwise chaos may ensue.  In this case, the
+                * Value will be invalid even if index is manually changed to a valid
+                * index.  You have to index the script itself to get a valid Value.
+                */
+               Value() :
+                       index(0),
+                       state(0) {}
+
+               Value(lua_State* s, int i) :
+                       index(i),
+                       state(s) {}
+
+               // check the type of the value
+               bool isBoolean() const   { return (bool)lua_isboolean(state, index); }
+               bool isFunction() const  { return (bool)lua_isfunction(state, index); }
+               bool isNil() const       { return (bool)lua_isnil(state, index); }
+               bool isNone() const      { return (bool)lua_isnone(state, index); }
+               bool isValid() const     { return state != 0 && !isNone(); }
+               bool isNoneOrNil() const { return (bool)lua_isnoneornil(state, index); }
+               bool isNumber() const    { return (bool)lua_isnumber(state, index); }
+               bool isString() const    { return (bool)lua_isstring(state, index); }
+               bool isTable() const     { return (bool)lua_istable(state, index); }
+               bool isThread() const    { return (bool)lua_isthread(state, index); }
+               bool isData() const      { return (bool)lua_isuserdata(state, index); }
+               bool isLightData() const { return (bool)lua_islightuserdata(state, index); }
+
+               /**
+                * Get the type of the value.
+                */
+
+               TYPE getType() const
+               {
+                       return (TYPE)lua_type(state, index);
+               }
+
+               /**
+                * Get the name of the type of the value as a string.
+                */
+
+               std::string getTypeName() const
+               {
+                       return std::string(lua_typename(state, (int)getType()));
+               }
+
+               /**
+                * Get a pointer value (for userdata, tables, threads, and functions).
+                */
+
+               const void* getIdentifier() const
+               {
+                       return lua_topointer(state, index);
+               }
+
+
+               bool operator == (const Value& rhs) const
+               {
+                       return (bool)lua_equal(state, index, rhs.index);
+               }
+               bool operator != (const Value& rhs) const
+               {
+                       return !(*this == rhs);
+               }
+               bool operator < (const Value& rhs) const
+               {
+                       return (bool)lua_lessthan(state, index, rhs.index);
+               }
+               bool operator <= (const Value& rhs) const
+               {
+                       return *this < rhs || *this == rhs;
+               }
+               bool operator > (const Value& rhs) const
+               {
+                       return !(*this <= rhs);
+               }
+               bool operator >= (const Value& rhs) const
+               {
+                       return !(*this < rhs);
+               }
+               operator bool () const
+               {
+                       return (bool)lua_toboolean(state, index);
+               }
+
+               Value& operator = (const Value& rhs)
+               {
+                       rhs.pushCopy();
+                       replaceWithTop();
+                       return *this;
+               }
+
+
+               /**
+                * Get the length of the value according to the definition given by Lua.
+                */
+
+               size_t getLength() const
+               {
+                       return lua_objlen(state, index);
+               }
+
+
+               /**
+                * Convert the underlying value to a C++ type.
+                */
+
+               template <typename T>
+               void get(T& value) const
+               {
+                       value = (T)lua_tointeger(state, index);
+               }
+
+               void get(bool& value) const
+               {
+                       value = (bool)lua_toboolean(state, index);
+               }
+
+               void get(float& value) const
+               {
+                       value = (float)lua_tonumber(state, index);
+               }
+               void get(double& value) const
+               {
+                       value = (double)lua_tonumber(state, index);
+               }
+
+               void get(std::string& value) const
+               {
+                       size_t size;
+                       const char* str = lua_tolstring(state, index, &size);
+                       value.assign(str, size);
+               }
+
+
+               void set(std::string& value)
+               {
+               }
+
+               //template <typename T>
+               //void get(const std::string& field, T& value) const
+               //{
+                       ////lua_getfield(state_, field.c_str());
+                       //pushField(field);
+                       //get(-1, value);
+                       //lua_pop(state_, 1);
+               //}
+               
+
+               /**
+                * Copy the value and push the copy to the stack.
+                */
+
+               void pushCopy() const
+               {
+                       lua_pushvalue(state, index);
+               }
+
+               /**
+                * Replace this value with the value at the top of the stack.
+                */
+
+               void replaceWithTop()
+               {
+                       lua_replace(state, index);
+               }
+
+               void remove()
+               {
+                       lua_remove(state, index);
+               }
+
+               /**
+                * Inserts the top-most value on the stack at position index, shifting other
+                * values as needed.
+                */
+
+               void insertTopHere()
+               {
+                       lua_insert(state, index);
+               }
+
+               
+               void pushMetatable() const
+               {
+                       lua_getmetatable(state, index);
+               }
+
+               void pushField() const
+               {
+                       lua_gettable(state, index);
+               }
+
+               void pushField(const std::string& name) const
+               {
+                       lua_getfield(state, index, name.c_str());
+               }
+
+
+       private:
+
+               lua_State* state;
+       };
+
+
+       Script() :
+               state_(luaL_newstate())
+       {
+               lua_pushlightuserdata(state_, this);
+               lua_setfield(state_, LUA_REGISTRYINDEX, "_script_obj");
+       }
+
+       ~Script()
+       {
+               if (isMainThread_) lua_close(state_);
+       }
+
+
+       static ScriptP alloc()
+       {
+               return ScriptP(new Script);
+       }
+
+
+       void importStandardLibraries()
+       {
+               luaL_openlibs(state_);
+       }
+
+       void importFunction(const std::string& name, const Function& function)
+       {
+               push(function);
+               lua_setglobal(state_, name.c_str());
+       }
+
+
+       STATUS doString(const std::string& commands)
+       {
+               return (STATUS)luaL_dostring(state_, commands.c_str());
+       }
+
+       STATUS doFile(const std::string& file)
+       {
+               return (STATUS)luaL_dofile(state_, file.c_str());
+       }
+
+
+       /**
+        * Thread-handling methods.
+        */
+
+       Script pushNewThread()
+       {
+               return Script(state_);
+       }
+
+       void pushThread()
+       {
+               lua_pushthread(state_);
+       }
+
+       STATUS resume(int nargs)
+       {
+               return (STATUS)lua_resume(state_, nargs);
+       }
+
+       STATUS getStatus() const
+       {
+               return (STATUS)lua_status(state_);
+       }
+
+       int yield(int results)
+       {
+               return lua_yield(state_, results);
+       }
+
+       bool isMainThread() const
+       {
+               return isMainThread_;
+       }
+
+
+       /**
+        * Get significant values.
+        */
+
+       Value getGlobalTable() const
+       {
+               return Value(state_, GLOBALS);
+       }
+
+       Value getRegistryTable() const
+       {
+               return Value(state_, REGISTRY);
+       }
+
+       Value getEnvironmentTable() const
+       {
+               return Value(state_, ENVIRONMENT);
+       }
+
+       Value getTop() const
+       {
+               return Value(state_, lua_gettop(state_));
+       }
+
+       /**
+        * Get the size of the stack; this is also the index of the top-most value.
+        */
+
+       int getSize() const
+       {
+               return lua_gettop(state_);
+       }
+
+       void setSize(int size)
+       {
+               lua_settop(state_, size);
+       }
+
+       void clear()
+       {
+               setSize(0);
+       }
+
+
+       /**
+        * Makes sure there is at least extra more places on the stack.  Returns
+        * false if space couldn't be created.  Just like with the regular Lua API,
+        * you are responsible to make sure the stack is big enough to hold whatever
+        * you want to push on it.  This is usually only an issue if you're pushing
+        * stuff in a loop.
+        */
+
+       bool checkStack(int extra)
+       {
+               return (bool)lua_checkstack(state_, extra);
+       }
+
+
+       /**
+        * Concatenates the top-most n values on the stack.
+        */
+
+       void concat(int n)
+       {
+               lua_concat(state_, n);
+       }
+
+
+       /**
+        * Push some values onto the stack.
+        */
+
+       template <typename T>
+       void push(T value)
+       {
+               lua_pushinteger(state_, lua_Integer(value));
+       }
+
+       void push(bool value)
+       {
+               lua_pushboolean(state_, int(value));
+       }
+
+       void push(float value)
+       {
+               lua_pushnumber(state_, (lua_Number)value);
+       }
+       void push(double value)
+       {
+               lua_pushnumber(state_, (lua_Number)value);
+       }
+
+       void push(const std::string& value)
+       {
+               lua_pushlstring(state_, value.c_str(), value.length());
+       }
+       void push(const char* value, size_t length)
+       {
+               lua_pushlstring(state_, value, length);
+       }
+
+       void push(const Function& function)
+       {
+               functions_.push_back(function);
+
+               lua_pushlightuserdata(state_, (void*)&functions_.back());
+               lua_pushcclosure(state_, dispatchCall, 1);
+       }
+
+       void push(void* data)
+       {
+               lua_pushlightuserdata(state_, data);
+       }
+
+       void pushNil()
+       {
+               lua_pushnil(state_);
+       }
+
+       void pushFromThread(Script& thread, int n)
+       {
+               lua_xmove(thread.state_, state_, n);
+       }
+
+       STATUS pushCode(const std::string& filename)
+       {
+               return (STATUS)luaL_loadfile(state_, filename.c_str());
+       }
+
+       STATUS pushCode(const std::string& name, const char* buffer, size_t size)
+       {
+               return (STATUS)luaL_loadbuffer(state_, buffer, size, name.c_str());
+       }
+
+       void* pushNewData(size_t size)
+       {
+               return lua_newuserdata(state_, size);
+       }
+
+       void pushNewTable()
+       {
+               lua_newtable(state_);
+       }
+
+
+       /**
+        * Call a function on the stack.  The correct procedure is to push a
+        * function onto the stack followed by nargs arguments.  This method will
+        * pop them off upon return, leaving up to nresults return values (default
+        * is any number of return values, depending on the callee).
+        */
+
+       STATUS call(int nargs, int nresults = LUA_MULTRET)
+       {
+               return (STATUS)lua_pcall(state_, nargs, nresults, 0);
+       }
+
+
+       /**
+        * Pops n values from the top of the stack.
+        */
+
+       void pop(int n)
+       {
+               lua_pop(state_, n);
+       }
+
+
+       /**
+        * Index into the stack to get a Value.
+        */
+
+       Value operator [] (int index) const
+       {
+               return Value(state_, index);
+       }
+
+
+       /**
+        * Getting and setting fields of a table.
+        */
+
+       void get(const std::string& field,  int index = GLOBALS) const
+       {
+               lua_getfield(state_, index, field.c_str());
+       }
+
+       void set(const std::string& field, int index = GLOBALS)
+       {
+               lua_setfield(state_, index, field.c_str());
+       }
+
+
+       /**
+        * Control over the garbage collection process.
+        */
+
+       void collectAll()
+       {
+               lua_gc(state_, LUA_GCCOLLECT, 0);
+       }
+
+       void stopCollector()
+       {
+               lua_gc(state_, LUA_GCSTOP, 0);
+       }
+
+       void restartCollector()
+       {
+               lua_gc(state_, LUA_GCRESTART, 0);
+       }
+
+       int getUsedMemory() const
+       {
+               // in kilobytes
+               return lua_gc(state_, LUA_GCCOUNT, 0);
+       }
+
+       void collectStep(int step)
+       {
+               lua_gc(state_, LUA_GCSTEP, step);
+       }
+
+       void tuneCollector(int pause, int step)
+       {
+               lua_gc(state_, LUA_GCSETPAUSE, pause);
+               lua_gc(state_, LUA_GCSETSTEPMUL, step);
+       }
+
+
+
+       struct Exception : public Mf::Exception
+       {
+               explicit Exception(unsigned error) :
+                       Mf::Exception(error) {}
+
+               void raise()
+               {
+                       throw *this;
+               }
+       };
+
+
+private:
+
+       Script(lua_State* state) :
+               state_(lua_newthread(state)),
+               isMainThread_(false) {}
+
+       static int dispatchCall(lua_State* state)
+       {
+               const Function* function = (const Function*)lua_touserdata(state,
+                               lua_upvalueindex(1));
+
+               lua_getfield(state, LUA_REGISTRYINDEX, "_script_obj");
+               Script* script = (Script*)lua_touserdata(state, -1);
+               lua_pop(state, 1);
+
+               return (*function)(*script);
+       }
+
+       lua_State*                      state_;
+       bool                            isMainThread_;
+       std::list<Function>     functions_;
+};
+
+
+} // namespace Mf
+
+#endif // _MOOF_SCRIPT_HH_
+
+/** vim: set ts=4 sw=4 tw=80: *************************************************/
+
index 460f7bb24a1054d2a18e0e579f08e8386320e06c..d9a673c58d58698545904c23b05e7c15e093d9de 100644 (file)
@@ -457,6 +457,8 @@ struct Sound::Impl
        {
                // don't let the music die!
                update();
+               // TODO - might be nice to also allow using threads for streaming rather
+               // than a timer, probably as a compile-time option
        }
 };
 
index 26c2b424f94321e7e007bde9ecf854a45ef4d614..c9929cedeae2169b4c61bedf7d557802395f47ba 100644 (file)
@@ -66,6 +66,10 @@ public:
        void play();
        void stream();
 
+       // TODO - i don't like how there are two different methods that essentially
+       // do the same thing; the API should be exactly the same for both types of
+       // sounds; need a different way to distinguish how to handle the sound
+
        void stop();
        void pause();
        void resume();
index 97298323a61efb9590c67ca7b0e84571403485d2..5e43e0d42e414a60c7ab394e7ae1804203648322 100644 (file)
@@ -41,7 +41,17 @@ void Sphere::encloseVertices(const Vector3 vertices[], unsigned count)
 
 void Sphere::draw(Scalar alpha) const
 {
-       // TODO
+       GLUquadricObj* sphereObj = gluNewQuadric();
+       gluQuadricDrawStyle(sphereObj, GLU_LINE);
+
+       glPushMatrix();
+
+       glTranslate(point[0], point[1], point[2]);
+       gluSphere(sphereObj, (GLdouble)radius, 16, 16);
+
+       glPopMatrix();
+
+       gluDeleteQuadric(sphereObj);
 }
 
 bool Sphere::isVisible(const Frustum& frustum) const
index 1ed24c75fee3e3e877cea604eab0261c9028b0cf..8610a7b8be499e1106d741433d98f7beab9cb513 100644 (file)
@@ -138,7 +138,7 @@ void Video::setVideoMode(const long mode[3])
                        logInfo("video context recreated");
 #endif
                }
-               else throw Exception(SDL_GetError());
+               else throw Exception(Exception::SDL_ERROR);
        }
 }
 
index 0dca8bbfeb5b36a79ead18a0760a89964c55a19c..d640ad2386ce7a4fefa4bdda725fb35485302297 100644 (file)
@@ -35,6 +35,8 @@
 
 #include <SDL/SDL.h>
 
+#include <Moof/Exception.hh>
+
 
 namespace Mf {
 
@@ -127,10 +129,15 @@ public:
        void swap();
 
 
-       struct Exception : public std::runtime_error
+       struct Exception : public Mf::Exception
        {
-               explicit Exception(const std::string& what_arg) :
-                       std::runtime_error(what_arg) {}
+               explicit Exception(unsigned error) :
+                       Mf::Exception(error) {}
+
+               void raise()
+               {
+                       throw *this;
+               }
        };
 };
 
index a9b8f1328068029f42442cfa16630234db76f3a3..a8a06cc82f6d1e6f9f694fda7114c3c7f322ef08 100644 (file)
@@ -99,28 +99,6 @@ static std::string iconFile()
 }
 
 
-void YoinkApp::myFunc(Mf::Timer& timer, Mf::Scalar t)
-{
-       std::cout << "timer: " << t << std::endl;
-       
-       //timer.invalidate();
-}
-
-int YoinkApp::myThread()
-{
-       Mf::Scalar timer = Mf::Timer::getTicks();
-
-       for (;;)
-       {
-               std::cout << "thread awake: " << Mf::Timer::getTicks() << std::endl;
-
-               timer += 3.0;
-               Mf::Timer::sleep(timer, true);
-       }
-       return 0;
-}
-
-
 YoinkApp::YoinkApp(int argc, char* argv[]) :
        Mf::Engine(argc, argv, configFiles(), PACKAGE_STRING, iconFile()),
        music("NightFusionIntro"),
@@ -137,19 +115,14 @@ YoinkApp::YoinkApp(int argc, char* argv[]) :
        heroine = Character::alloc("RobotTrooper");
        heroine->getAnimation().startSequence("Run");
 
-       Mf::Scalar a[6] = {0.0, 1.5, -0.5, 3.0, -1.5, 1.0};
+       Mf::Scalar a[6] = {0.0, 1.5, -0.5, 3.0, -2.0, 1.0};
        interp.init(a, 2.0, Mf::Interpolator::OSCILLATE);
 
        Mf::Scalar b[2] = {1.0, 0.0};
        fadeIn.init(b, 1.0);
 
-       octree = Mf::loadScene("Test");
+       octree = Mf::loadScene("Classic");
        heroine->treeNode = octree->insert(heroine);
-
-       //myTimer.init(boost::bind(&YoinkApp::myFunc, this, _1, _2),
-                       //0.0, Mf::Timer::REPEAT);
-       //Mf::Thread thread = Mf::detachFunction(boost::bind(&YoinkApp::myThread, this));
-       //std::cout << "thread " << thread << " detached." << std::endl;
 }
 
 YoinkApp::~YoinkApp()
index 24865c93fa741089fdd4b5fefdae6b097f7dfe77..950d31983002443dd83abe9c3c70d4a57321f1b8 100644 (file)
@@ -77,10 +77,6 @@ private:
        Mf::Camera camera;
        Mf::OctreeP octree;
 
-       void myFunc(Mf::Timer& timer, Mf::Scalar t);
-       Mf::Timer myTimer;
-       int myThread();
-
        Hud hud;
 };
 
This page took 0.108355 seconds and 4 git commands to generate.