Elysian Shadows

The Lua Lounge is Born

Originally posted in our Private Development forums on 10.25.10.


Fig-1 -- Lua as fuck

[size=20][b]MENTLEGEN!![/b][/size]

Welcome to your personal Lua library. Here we shall discuss not only currently implemented functionality but also scripts in development, debugging, potential triggers etc.

** If you are serious about giving Lua a try, download Notepad++ for Windows or TextWrangler for Mac. It offers syntax highlighting, and makes reading your Lua script a lot easier.**

http://notepad-plus-plus.org/

http://www.barebones.com/products/textwrangler/

This guide will be broken down into the following parts:

1)Creating/adding a Lua scripting behavior to Entity.txt.

2)Learning some language syntax/rules.

3)Creating your first Lua script.

4)Learning what all is wrapped, and what you can do with Lua in the ES engine.

[color=#0040BF][size=20][u]How do I get started?[/u][/size][/color]

First of all, EVERYTHING in the engine that can possibly be wrapped is… or so I’ve been told. Through Lua, a Scripter has the ability to not only manipulate entities on initial load (OnLoad()) but also at the libGyro level, dynamically at runtime and on every frame through Update() . Below you will find acontinually up to date repository of Lua vs engine functionality. However, before we delve into the inner workings I will describe how to create and include your Lua script in a level or area.

Technically, just like warps, a Lua script is an entity. Therefore, you must include it in the level/area's Entity.txt. Below is a sample Entity.txt file containing three entities. The first one being a NPC, second one being a warp, and the last one (MYLUATEST) being a Lua script.

Entity.txt ------------------------------------   3 WiseOne 100 80 50 32 64 1 1 0 1 1 1 party/Wiseone.png 256 256 32 64 0 5 0 0 4 WalkDown 7 0 0 4 0 0 0 4 1 0 0 4 2 0 0 4 3 0 0 4 4 0 0 4 5 0 0 4 6 WalkUp 7 0 0 4 8 0 0 4 9 0 0 4 10 0 0 4 11 0 0 4 12 0 0 4 13 0 0 4 14 WalkLeft 7 0 0 4 24 0 0 4 25 0 0 4 26 0 0 4 27 0 0 4 28 0 0 4 29 0 0 4 30 WalkRight 7 0 0 4 16 0 0 4 17 0 0 4 18 0 0 4 19 0 0 4 20 0 0 4 21 0 0 4 22 1 0 0 32 32 32 16 2 6 8 3 Harken youngling! What be you doing in mah cave? I have dwelled here for a millenia, so I am very aware of the smell down here. Don't look at me. 0 1 4 Oh! WHEEWL! 1 Monster? 2 Fuck Off! -1 Umm... 0 Listen, kid. I hurd there is a dangerious monster that  lurks down here, you can hear his bitch ass scavenging somewhere below. Watch out, least you slip and die. 0 -1 0 Yeah, totally dude. Here, take this Weasle Claw, it is not safe to go alone. May it aid you in your adventures, and protect your ass from evil and shit. Peace out! 1 -1 0 TestWarpz 150 150 50 32 32 1.0 1.0 0.0 0 2 1 0 0 32 32 8 0 3 250 250 1 level/Cave2/area/Cave2 1 level/Cave2 MYLUATEST 55 55 50 32 32 1.0 1.0 0.0 0 0 1 10 LuaBehavior 87 level/Test_town/area/town/Supersecrets.lua 

(If you are curious as to what all these reasting numbers mean click HERE / ) [spoiler]

[b]Entity.txt[/b] ------------------------------------   3 <------------------- Number of entities created in Entity.txt WiseOne <---------- Name of first entity. 100 <---------------- X position         80 <----------------- Y position 50 <----------------- Z position 32 <----------------- Width of sprite 64 <----------------- Height of sprite       1 <------------------- X scale 1 <------------------- Y scale 0 <------------------- Orientation (radians) 1 <------------------- Is the entity renderable? 0 or 1 (if 0 then disregard next two lines) 1 <------------------- Is it currently being rendered? 0 or 1 (Even if zero, you must still define if it has a texture or not) 1 <------------------- Does it have a texture? 0 or 1 (if 1 then provide texture location and information) party/Wiseone.png <-- texture location 256 <----------------- Sheet width 256 <----------------- Sheet height 32 <------------------ Sprite width 64 <------------------ Sprite height 0 <------------------- Starting frame 5 <------------------- Number of components attached to this entity 0 <------------------- Component (enumerated type from component.h; in this case Animation) 0 <------------------- Prefab (not used yet, needs to be 0) 4 <------------------- Number of animation sequences (Walkdown, Walkup, Walkleft, Walkright) WalkDown <--------- Name of first animation sequence 7 <------------------- Number of frames in WalkDown sequence 0 <------------------- X offset 0 <------------------- Y offset 4 <------------------- wait time in between frames 0 <------------------- Sheet index (not row and column) 0 <------------------- X offset 0 <------------------- Y offset 4 <------------------- Wait time in between frames 1 <------------------- Sheet index 0 <------------------- I think you get the goddamn point 0 4 2 0 0 4 3 0 0 4 4 0 0 4 5 0 0 4 6 WalkUp 7 0 0 4 8 0 0 4 9 0 0 4 10 0 0 4 11 0 0 4 12 0 0 4 13 0 0 4 14 WalkLeft 7 0 0 4 24 0 0 4 25 0 0 4 26 0 0 4 27 0 0 4 28 0 0 4 29 0 0 4 30 WalkRight 7 0 0 4 16 0 0 4 17 0 0 4 18 0 0 4 19 0 0 4 20 0 0 4 21 0 0 4 22 1 0 0 32 32 32 16 2 6 8 <--------------- Component number 8 (conversation) 3 <--------------- Number of different conversation screens entity has Harken youngling! What be you doing in mah cave?   I have dwelled here for a millenia, so I am very        | --- first, and default conversation screen. Will show before options are given. (must be three lines, if less than three, leave a blank newline.) aware of the smell down here. Don't look at me.      / 0 <---------------- Modal (You can move while this screen displays) 1 <---------------- Next Screen 4 <--------------- Number of options Oh! WHEEWL! -- Option 1, takes you to screen 1 (not the default) 1 <---------------/ Monster? ------- Option 2, takes you to screen 2 2 <-------------/ Fuck Off! <---- Option 3 ends the conversation (-1, escape) -1 <-----------/ Umm... ------- Option 4 takes you to screen 0, the default. (Repeat) 0 <------------/ Listen, kid. I hurd there is a dangerious monster that  lurks down here, you can hear his bitch ass scavenging somewhere below. Watch out, least you slip and die. 0 <------------- Modal (You can move) -1<------------ Next screen (escape, or ends sequence) 0 <------------- Number of options Yeah, totally dude. Here, take this Weasle Claw, it is not safe to go alone. May it aid you in your adventures, and protect your ass from evil and shit. Peace out! 1 <------------ Modal (You cannot move during this screen) -1 <---------- Next screen (escape, or ends sequence) 0 <----------- Number of options TestWarpz <--------- Name of Entity 150 <----------------- X position 150 <----------------- Y position 50 <------------------ Z position 32 <------------------ Width 32 <------------------ Height 1.0 <----------------- X scale 1.0 <----------------- Y scale 0.0 <----------------- Orientation (again, in radians) 0 <------------------- Renderable (false, because the engine handles debug rendering of warps) 2 <------------------- Entity has two components (all warps must) 1 <------------------- First component is 1 (Collidable) 0 <------------------- X offset 0 <------------------- Y offset 32 <------------------ X size 32 <------------------ Y size 8 <-------------------- What type of collidable (Warps must be 8, don't fucking ask) 0 <-------------------- Also must be zero, don't ask 3 <-------------------- Second component, 3 is the Warp component  250 <----------------- Sends player to X position 250 <----------------- Sends player to Y position 1 <------------------- Change area? (if 1, or true, then provide file path of next area) level/Cave2/area/Cave2 -------- Area file path 1 <------------------ Change level? (if 1 or true, then provide file path of next level) level/Cave2 <-------------------- Level file path MYLUATEST <-------------------- Entity name 55 <------------------------- all of this is the same as above 55  50 32 32 1.0 1.0 0.0 0 1 10 <------------ Component 10, behavior LuaBehavior <--------- name of behavior (The name of any script is always LuaBehavior) 87 <-------------------- Another bit field for which trigger implements the script... not implemented yet. level/Test_town/area/town/Supersecrets.lua < ---- file path for your script, including script name and the .lua extention[/spoiler] 

(TL;DR - Do not get too used to this way of editing Entities. This chore will be taking place in the Toolkit... as soon as Marcel gets around to it.)

Also, as long as you at least include a single Lua script entity in Entity.txt, the rest of entity creation COULD actually be done in your attached Lua script.

[color=#0040BF][size=20][u]Whar r a Lua?[/u][/size][/color]

[size=20][b]Variables – Naming/creation[/b][/size]

Variables in Lua have similar naming conventions as most programming languages. The following rules apply:

•A variables must start with a letter or underscore, never a number.

•Variables are case sensitive, eg: num1 and Num1 are not the same.

•Only characters, numbers and underscores are allowed in the variable name, so none of this garbage &)(#*&@.

•Assignment goes just like you would think: Num1 = 4;

•While you don’t have to tell Lua what kind of data you wish to store (int, float ect.) if you are storing a string or single character, it must be enclosed in double quotes, ex:

Name = “Tyler”;

•Variables are able to be assigned to boolean values, ex:

Luaisaweome =  true; (or false).

•And lastly, any reserved (syntactically highlighted) words are not able to be used as variable names.

[size=20][b]Variables – Scope[/b][/size]

There are only two scopes to variables in Lua. Global and Local. Global variables are instantiated and/defined at the top of your Lua script and are able to be accessed from anywhere in your script. Local variables only exist within the code block that they were created in. I’ll explain more on blocks in just a second.

[size=20][b]Lua math[/b][/size]

Math operators in Lua are pretty straight forward: -, +, *, /, ^. Nothing fancy here.

[size=20][b]Comparison/logical operators[/b][/size]

If you are familiar with C/C++ there is only one change and one addition. While >, >=, <, <= and == are the same. Instead of using != (or not equal to) in Lua you would use the reserved word ‘not’. However, there is one new comparison operator and it looks like ~=. It is the “Left side is not equal to right side” operator. So something like “LUA” ~= “lua” would return a true value.

Lua recognizes the logical operators: ‘not’, ‘or’ and ‘and’.

[size=20][b]Functions/code blocks, conditional structures and loops[/b][/size]

In lua everything that is actually being ‘done’, will take place inside of a code block or function. These are areas of code that start with something like

function Update()

--Do shit here

End

Or

if num > 2 then 	--Also do shit here end 

A conditional structure would have a syntax of

1)if condition then  	--code end 2)if condition then 	--code else 	--code end 3)if condition1 then 	--code elseif condition2 then 	--code elseif condition3 then 	--code else 	--code end 

[size=20][b]Comments [/b][/size]

Comments in Lua are fairly simple. For a single line you’d use

-– this is a comment, nothing past the double dashes is read by the interpreter

While a multiline comment would look something like this

--[[ this is a multiline comment]]-- 

[color=#0040BF][size=20][u]What can I do with Lua?[/u][/size][/color]

H'okay, so, with your lua script added to the current level/area, you can now start playing around in your new sandbox. I recommend getting ESGamma compiling on your computer instead of just running the .exe, as actually compiling it allows for the creation of your Debug.txt in the project's root folder. After every change to your script you can quickly rerun the build and can instantly test said changes. But don't forget, Debug.txt is overwritten after every run, so if something seems fishy or if you manage to crash the engine build save a copy and post it here!

These are the header files you currently have access to (Just look for the "//tolua_" tags to see what I'm talking about):

Revision 1.0 (date)

elysianShadows.pkg ---------------------------------   //LibGyro $cfile "../libGyro/include/linear_algebra.h" $cfile "../libGyro/include/transform.h" $cfile "../libGyro/include/rect.h" $cfile "../libGyro/include/texture_iface.h" $cfile "../libGyro/include/gl/texture.h" $cfile "../libGyro/include/sprite.h" $cfile "../libGyro/include/video_iface.h" $cfile "../libGyro/include/gl/video_api.h"   //Engine Core $cfile "../include/lua_singleton.h" $cfile "../include/es_system.h" $cfile "../include/debug.h" $cfile "../include/game_input.h" $cfile "../editorAsset/include/asset.h" $cfile "../editorAsset/include/level_manager.h" $cfile "../editorAsset/include/component.h" $cfile "../editorAsset/include/editor_entity.h" $cfile "../include/entity.h"   //Gui $cfile "../include/font.h" $cfile "../include/adventure_log.h"   //Components $cfile "../editorAsset/include/editor_animation.h" $cfile "../include/animation.h" $cfile "../editorAsset/include/editor_collidable.h" $cfile "../include/collidable.h" $cfile "../editorAsset/include/editor_warp.h" $cfile "../include/warp.h" $cfile "../editorAsset/include/editor_item.h" $cfile "../include/item.h" $cfile "../editorAsset/include/editor_character.h" $cfile "../include/character.h" $cfile "../editorAsset/include/editor_friendly.h" $cfile "../include/friendly.h" $cfile "../editorAsset/include/editor_enemy.h" $cfile "../include/enemy.h" $cfile "../editorAsset/include/editor_conversation.h" $cfile "../include/conversation.h"   //Subsystems $cfile "../include/terrain_system.h" $cfile "../include/es_timer_system.h" $cfile "../include/animation_system.h" $cfile "../include/camera_system.h" $cfile "../include/party_system.h" $cfile "../include/character_system.h" $cfile "../include/friendly_system.h" $cfile "../include/enemy_system.h" $cfile "../include/item_system.h" $cfile "../include/warp_system.h" $cfile "../include/collision_system.h" $cfile "../include/conversation_system.h" $cfile "../include/behavior_system.h" $cfile "../include/gui_system.h" 

[color=#0040BF][size=20][u]Remember...[/u][/size][/color]

A few things to keep in mind when creating your .lua file:

1) Anything created at the 'top' of the script (outside of any function definition) is global. Setting constants, grabbing entities by name for manipulation or creating entities is all done here. EX:

y = 1; x = 42.3456; scale = 1; flip = 1; count = 1; print("Global scope, asswad!"); input = LuaSingleton:getInput(); warp = Entity:getByName("TestWarp"); 

2)In order to manipulate an entity from Entity.txt, you have to use the getByName() function.

assdoucher = Entity:getByName("AssDoucher");

3) Function definitions don't require { }s. A simple example would be the Update function. Every script must have an Update and OnLoad function. (even if they are empty) EX:

function Update()   	--do shit here every frame   end   function OnLoad()   	--do shit here only during initial load of script   end 

This is why using Notepad++ or TextWrangler can be very advantageous. As you can see in Fig-2, Notepad++ draws these neat little lines from the module to it's end statement. Now, if you really want to go all the way, click one of the "-" boxes, and you have what we see in Fig-3.

Fig-2 -- Block is open

Fig-3 -- Block is closed

4) If you are having errors or something doesn't seem right, check your debug.txt in the project root folder. After every run from your compiler (don't just use the ESGamma.exe) a debug log is created to help keep track of what is going on, or has gone wrong.

5) You can print to the Adventure Log and Debug.txt by just using print("textgoeshere"); anywhere in your Lua script. This can be useful during troubleshooting!

[color=#0040BF][size=20][u]Using Lua[/u][/size][/color]

With all of this in mind, lets get started! First are going to create our first Lua based entity. Lets call him Fuckmite. First we are going to have to give Fuckmite all of the attributes that you would find in the Entity.txt.

fuckmite = gyro.Sprite:new(); fuckmite.transform:setPosition(168, 320); fuckmite.transform:setOrientation(0); fuckmite.transform:setZ(100.0); fuckmite.transform:setSize(128,1280);      fuckmite.transform:setScale(2, 20);         tex = System.gyroVideo:TexturePNG("item/fireflower.PNG", 32, 32, 32, 32);    fuckmite:setTexture(tex);             

Reasting explanation /

[spoiler]fuckmite = gyro.Sprite:new();		<---- Here we are accessing the gyro namespace to create fuckmite the sprite fuckmite.transform:setPosition(168, 320);	<---- setting position via the transform function setPosition(X,Y); fuckmite.transform:setOrientation(0);		<---- Setting orientation (once again, radians)   fuckmite.transform:setZ(100.0);				<----- Setting Z position fuckmite.transform:setSize(128,1280);		<-------- Setting size via setSize(X, Y); fuckmite.transform:setScale(2, 20);			<--------- Setting scale via setScale(SingleValue) or setScale(xValue, yValue); tex = System.gyroVideo:TexturePNG("item/fireflower.PNG", 32, 32, 32, 32);	<---- Creates a variable called tex, and assigns to it the texture we want Fuckmite to have. TexturePNG("Filepath.extention", sheetWidth, sheetHeight, spriteWidth, spriteHeight); fuckmite:setTexture(tex);					<-------- Sets Fuckmite's texture to tex[/spoiler] 

Now lets say we wanted to make Fuckmite here appear to be spinning, seeming to rotate in 3D space via some fancy 4x4 transformation matrices. (But rather it is just some clever use of the setScale transformation function). Your code would look something like this:

scale = 1; flip = 1; count = 1;   fuckmite = gyro.Sprite:new(); fuckmite.transform:setPosition(168, 320); fuckmite.transform:setOrientation(0); fuckmite.transform:setZ(100.0); fuckmite.transform:setSize(128,1280); fuckmite.transform:setScale(2, 20); tex = System.gyroVideo:TexturePNG("item/fireflower.PNG", 32, 32, 32, 32); fuckmite:setTexture(tex);   function colorChange() 	if count % 2 == 0 then 		fuckmite:setColor(1, 0, 0); 	else 		fuckmite:setColor(0, 0, 1); 	end 	count = count + 1; end   function Spin() 	if spinTime == nil then 		spinTime = os.clock(); 	else 		if os.clock() >= (spinTime + .05) then 			if scale <= 1 and scale >= 0 and flip == 1 then 				scale = scale - 0.1; 				fuckmite.transform:setScale(scale, 1); 				spinTime = os.clock(); 			end -- end of shrinking 		  			if scale <= 1 and scale >= 0 and flip == -1 then 				scale = scale + 0.1; 				fuckmite.transform:setScale(scale, 1); 				spinTime = os.clock(); 			end -- end of growing 		  			if scale > 1 then 				flip = flip * -1; 				scale = 1; 			end -- end of growing reset 		  			if scale < 0 then 				colorChange(); 				flip = flip * -1; 				scale = .05; 			end -- end of shrinking reset 		end -- end of size modification 	end -- end of dumb else/fucking with shit end -- end of function function OnLoad() end function Update()  	fuckmite.transform.updateTransform = true; 	Spin(); 	System.gyroVideo:RenderSprite(fuckmite); end 

Now fuckmite will incrementally 'rotate' or shrink vertically. Once it appeared to flip it would change color, thus revealing the 'other side', and then eventually 'flip' back again.

This post will be edited and updated (hopefully) every night this week as I start at the top of the list, and when improvements to the engine happen or new Lua examples or tutorials are put together. I am also looking forward to seeing sample scripts, ideas or any questions you guys might have!

Falco Girgis
Falco Girgis is the founder and lead software architect of the Elysian Shadows project. He was previously employed in the telecom industry before taking a chance on Kickstarter and quitting his job to live the dream. He is currently pursuing his masters in Computer Engineering with a focus on GPU architecture.