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.
[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,
DATA_FILES=$(echo $(cd data; \
find . -name "*.json" \
+ -o -name "*.lua" \
-o -name "*.ogg" \
-o -name "*.png" \
-o -name "*.xm" \
--- /dev/null
+
+-- 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
+
+++ /dev/null
-{
- "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
- }
-
- ]
-}
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
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
# 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"
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
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
Moof/RK4.hh \
Moof/Scene.cc \
Moof/Scene.hh \
+ Moof/Script.hh \
Moof/Serializable.cc \
Moof/Serializable.hh \
Moof/Serializer.cc \
namespace Mf {
-Notification::~Notification() {}
-
-
struct Dispatcher::Impl
{
Impl() :
class Notification
{
public:
- virtual ~Notification();
+ virtual ~Notification() {};
};
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;
}
if (!objects.empty())
+ {
aabb_.draw();
+ sphere_.draw();
+ }
}
#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;
}
--- /dev/null
+
+/*******************************************************************************
+
+ 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: *************************************************/
+
{
// 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
}
};
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();
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
logInfo("video context recreated");
#endif
}
- else throw Exception(SDL_GetError());
+ else throw Exception(Exception::SDL_ERROR);
}
}
#include <SDL/SDL.h>
+#include <Moof/Exception.hh>
+
namespace Mf {
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;
+ }
};
};
}
-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"),
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()
Mf::Camera camera;
Mf::OctreeP octree;
- void myFunc(Mf::Timer& timer, Mf::Scalar t);
- Mf::Timer myTimer;
- int myThread();
-
Hud hud;
};