Thanks
. Concerning the scale, it is something I've noticed as well (yes, the stairs are huge lol). While it would be a nuisance to change that level now, it is a problem that is going to be addressed in future levels.
The LUA scripting system was actually inspired in part by the Warcraft III World Editor trigger system. Basically the game engine code features a bunch of functions that are meant to be accessed from LUA (but some are used in other parts of the engine). Every world/unit/item file is actually a LUA script. For example, in addition to a bunch of data labels (i.e. modelfile="Worlds/blahblah.dbo" or scale=1.0) each world file also features two functions: startworld() and loopworld(). Startworld() is run when the world is first loaded, and it allows me to trigger any lighting effects, create the sky/sun, give orders to units, anything the functions defined in the engine allow. Loopworld() takes a parameter and returns the same parameter called "phase", which can be used to tell which "stage" or "phase" the world is in. It runs every loop. Since the LUA variables are local and would just be reset to 0 the next time the function is called, this "phase" variable is used to decide which scripts should be run and which shouldn't, depending on how far into the level the player is. For example, the first cinematic of the second level runs when phase=0. Once the cinematic is all planned out (the cinematics operate on a planned basis rather than a real-time one), phase is set to 1 and once the cinematic is over the phase is set to 2. When the phase is 2, all of the gameplay elements are executed instead. When the second cinematic triggers, phase is set to 3. Since the world scripts operate in this manner, they can end up becoming quite long.
The units operate in a similar way. They feature a loadunit() function and a loopunit() function. Loopunit also takes a "phase" parameter. A simple example of this in action would be the Ashton Town Villager. Here is a portion his script; his face/skin color are randomly selected in the loadunit() function, and the things he says are randomly selected from a list when you talk to him. msg[] contains the messages, while dur[] tells how long they are on the screen for. All of the DBPro functions begin with "FL_" (from LUA) and are called using the LUA plugin's DBPro.Call function. The functions are registered with help from IanM's Matrix1 function pointer commands.
dofile "Data/Internal/CommonFunctions.csd"
dofile "Data/Internal/UnitFunctions.csd"
msg={}
dur={}
msg[1]="Don't you just love this place? What a beautiful town, and just look at that arch... Amazing!"
dur[1]=5000
msg[2]="I wish the Ashtons would move back; they were such nice people."
dur[2]=4400
msg[3]="The sun is out today, cheer up!"
dur[3]=3200
msg[4]="The city is great and all, but sometimes I like to come out here and just relax."
dur[4]=5000
msg[5]="What do you want to talk about? Nothing? Okay. Goodbye!"
dur[5]=4000
msg[6]="Hey there!"
dur[6]=2000
msg[7]="Hello. It's a nice day today, isn't it?"
dur[7]=4000
msg[8]="The rest of the Ashtons moved on to the city and beyond. I don't see what's so bad about this place."
dur[8]=6000
msg[9]="Heh, what a boring day."
dur[9]=3000
msg[10]="Which do you prefer? Urban or rural? Hmmm?"
dur[10]=4000
maxmsg=10
--This gets run right after this unit is loaded.
function loadunit()
unit=DBPro.Call("FL_GetThisUnit")
face=Rand(1,3)
skin=Rand(1,4)
if skin==1 then skini="Brown.bmp" end
if skin==2 then skini="Peach.bmp" end
if skin==3 then skini="Pale.bmp" end
if skin==4 then skini="DarkBrown.bmp" end
DBPro.Call("FL_SetUnitFaceImage",unit,"Faces/Ashton Town/VillagerFace"..face..".png")
DBPro.Call("FL_SetUnitSkinImage",unit,"Skins/"..skini)
item=DBPro.Call("FL_GetItemOfFilename","Clothes/Villager Shirt.csi")
DBPro.Call("FL_GiveUnitItem",unit,item,1)
DBPro.Call("FL_EquipItem",item,unit,1)
end
function loopunit(phase)
unit=DBPro.Call("FL_GetThisUnit")
player=DBPro.Call("FL_GetPlayerUnit")
entity=DBPro.Call("FL_GetUnitEntity",unit)
--Talk to the villager
if phase==0 then
if DBPro.Call("FL_UnitInTalkDistance",unit)==1 then
if DBPro.Call("FL_GetUnitCommand",unit)==0 then
PointUnitAtUnit(unit,player,10.0)
end
else
if DBPro.Call("FL_GetUnitCommand",unit)==0 then
SmoothRotateUnitToEntityNumber(unit,entity,4.0)
end
end
if DBPro.Call("FL_UnitSpokenTo",unit)==1 then
phase=1
DBPro.Call("FL_SetPlayerLockOnTarget",-1)
DBPro.Call("FL_PauseUnit",unit)
DBPro.Call("FL_SetCinematicModeOn")
DBPro.Call("FL_PauseUnit",player)
num=Rand(1,maxmsg)
ShowUnitCinematicMessage(unit,msg[num],0,dur[num])
gv=DBPro.Call("FL_MakeGlobalVariable","Villager"..unit.."Duration")
DBPro.Call("FL_SetGlobalVariableInteger",gv,dur[num])
end
end
if phase==1 then
PointUnitAtUnit(unit,player,4.0)
PointUnitAtUnit(player,unit,4.0)
gv=DBPro.Call("FL_GetGlobalVariableID","Villager"..unit.."Duration")
ii=DBPro.Call("FL_GetGlobalVariableInteger",gv)
if Wait(ii,"Villager"..unit.."IsTalkingWaitPlease")==1 then
phase=0
DBPro.Call("FL_UnpauseUnit",unit)
DBPro.Call("FL_SetCinematicModeOff")
DBPro.Call("FL_UnpauseUnit",player)
DBPro.Call("FL_DeleteGlobalVariable",gv)
end
end
return phase
end
The "entities" are placed in an Entity Editor. This is a level editor of sorts, and allows me to place the player start location, NPCs, enemies, allies, points (cameras pan to these points during the cutscenes), or scriptboxes that trigger a certain script when the player enters them. Thus, every unit has an entity from whence it came
.
The "dofile" commands at the top are like #include files. These other files feature useful scripts (shortcuts, really) for common procedures like pointing one unit at another.
The GlobalVariable commands are members of a custom global variable system, since you can't (or, I don't think you can) make global variables in LUA.
Basically, all of the scripts have avaliable to them the same broad range of commands, which allows for very flexible level design and unique items. That might not have been the best explanation so if you have any questions I'll be happy to answer them.