DoWait() working
So I was able to get DoWait() working. It's not 100% ideal because you have to piggy back off another entities Update() method because of the limitation of not being able to have any global values. If you would like to try it out I'll give the steps here.
1. Make a file under the /Scripts directory named cr.lua and paste this code in it:
tblWait = {}
waitID = 1
clsWait = {}
clsWait.__index = clsWait
function clsWait.create(tm, func)
local wait = {}
setmetatable(wait, clsWwait)
wait.interval = tm
wait.cr = func
wait.createTime = AppTime()
return wait
end
function Init()
-- clear the table
for i=1, table.getn(tblWait) do
	table.remove(tblWait, i)
end
SetGlobalNumber("game", 2)
end
function RunScript(func)
cr = coroutine.create(func)
coroutine.resume(cr, cr)
end
function DoWait(cr, interval)
a = clsWait.create(interval, cr)
tblWait[waitID] = a
waitID = waitID + 1
coroutine.yield()
end
function UpdateCR()
local tblDeleteWait = {}
local i = 1
for k, v in pairs(tblWait) do
	if(AppTime() - v.createTime > v.interval) then
		coroutine.resume(v.cr, v.cr)
		tblDeleteWait[i] = i
	end
	i = i + 1
end
for k, v in pairs(tblDeleteWait) do
	table.remove(tblDeleteWait, v)
end
end
2. Replace your fpscontroller.lua code with the below code: (note backup your file  )
 )
All I did was create 2 global numbers and changed how the loop ends. You can end it with an escape if you like
dofile("Scripts/constants/collision_const.lua")
dofile("Scripts/constants/engine_const.lua")
dofile("Scripts/LinkedList.lua")
dofile("Scripts/filesystem.lua")
dofile("Scripts/math.lua")
dofile("scripts/classes/bullet.lua")
fw=GetGlobalObject("framewerk")
if fw==nil then
Notify("Null framewerk object.",1)
return
end
--Variables
dx=0.0
dy=0.0
camerapitch=0.0
camerayaw=0.0
move=0.0
strafe=0.0
--Create a player controller
controller=CreateController(1.8,0.45,0.25,45)
controller:SetCollisionType(COLLISION_CHARACTER,0)
controller:SetPositionf(0,2,0,0)
controller:SetMass(10)
controller:SetPosition(fw.main.camera.position)
camerapitch=fw.main.camera.rotation.x
camerayaw=fw.main.camera.rotation.y
controller:Move(Vec3(0,-0.9,0))
local gunscale=0.6
local vwep = LoadMesh("abstract::vwep_hands.gmf")
LoadMesh("abstract::vwep_gun.gmf",vwep)
vwep:SetParent(fw.main.camera,0)
vwep:SetPosition(Vec3(-0.18*gunscale,-0.03*gunscale,0.37*gunscale),0)
vwep:SetScale(Vec3(0.04*gunscale,0.04*gunscale,0.04*gunscale))
local gundisplayposition = vwep:GetPosition()
sound_gunshot = LoadSound("abstract::gunshot.ogg")
source_gunshot = CreateSource(sound_gunshot)
source_gunshot:SetVolume(0.5)
vwep :SetShadowMode(0,1)
local displayposition=Vec3(-0.26/2.0,-0.03,0.19)
local muzzleflash = CreatePointLight(3)
muzzleflash:SetParent( vwep )
muzzleflash:SetColor(Vec4(1,0.6,0.0,1.0))
muzzleflash:SetPosition( displayposition )
muzzleflash:SetShadowMode(0)
HideMouse()
MoveMouse(GraphicsWidth()/2,GraphicsHeight()/2)
FlushKeys()
FlushMouse()
local pick
local camera = fw.main.camera
local remainingtime
local starttime=AppTime()
local gameduration=2--length of game in minutes
local gamemode=0
gunpos = vwep.position:Copy()
local smoothedgunswayamplitude=0.0
local smoothedgunswayspeed	=0.0
local guntime = 0.0
local recoil = 0.0
local lastfiretime=0.0
local smoothrecoil=0.0
local swaydamping=0.0
local smoothswaydamping=0.0
local lightsmoothing =0.0
local gunlight = 0.0
--Flashlight
flashlight = {}
flashlight.light = CreateSpotLight(8)
flashlight.light:Hide()
flashlight.sound_switch = LoadSound("abstract::switch.wav")
flashlight.state=0
flashlight.light:SetConeAngles(30,35)
flashlight.light:SetRotation(Vec3(5,0,0))
flashlight.light:SetShadowmapSize(512)
flashlight.light:Paint(LoadMaterial("abstract::flashlight.mat"))
function flashlight:SetState( state )
if state~=self.state then
	self.state=state
	if state==0 then
		self.light:Hide()
	else
		self.light:Show()
	end
	if self.sound_switch~=nil then
		self.sound_switch:Play()
	end
end
end
function ShootBullet( position, direction )
--	local speed=100.0
--	local pick = LinePick( position, Vec3(position.x+direction.x * speed) )
end
SetGlobalNumber("game", 1)
SetGlobalNumber("runGame", 1)
--main function
--while KeyHit(KEY_ESCAPE)==0 do
while GetGlobalNumber("runGame") == 1 do
jump=KeyHit(KEY_SPACE)*6.0
if controller:IsAirborne()==1 then jump=0 end
if KeyHit(KEY_ESCAPE) == 1 then
	SetGlobalNumber("runGame", 2)
end
local time = AppTime()/3200.0
local frame = time*(179.0-96.0)+96.0
frame=clamp( frame, 96, 179 )
vwep:Animate(96,1,0,1)
--Camera look
gx=round(GraphicsWidth()/2)
gy=round(GraphicsHeight()/2)
dx=Curve((MouseX()-gx)/4.0,dx,3.0/AppSpeed())
dy=Curve((MouseY()-gy)/4.0,dy,3.0/AppSpeed())
MoveMouse(gx,gy)
camerapitch=camerapitch+dy
camerayaw=camerayaw-dx
camerapitch=math.min(camerapitch,90)
camerapitch=math.max(camerapitch,-90)
fw.main.camera:SetRotationf(camerapitch,camerayaw,0,1)
movespeed=6
movesmoothing=10
if controller:IsAirborne()==1 then
	movesmoothing=200
end
--Player movement
move=Curve( (KeyDown(KEY_W)-KeyDown(KEY_S))*movespeed,move,movesmoothing)
strafe=Curve( (KeyDown(KEY_D)-KeyDown(KEY_A))*movespeed,strafe,movesmoothing)
--Use objects
if KeyHit(KEY_E)==1 then
	pick=CameraPick(camera,Vec3(GraphicsWidth()/2,GraphicsHeight()/2,2.0),0,0)
	if pick~=nil then
		repeat
			if pick.entity:GetClass()==ENTITY_MODEL then
				break
			end
			pick.entity=pick.entity.parent
		until pick.entity==nil
		if pick.entity~=nil then
			pick.entity:SendMessage("use",controller,0)
		end
	end
end
--Update controller
controller:Update(camerayaw,move,strafe,jump,40,10)
fw:Update()
if KeyHit(KEY_F)==1 then
	flashlight:SetState(1-flashlight.state)
end
--Position camera
camera:SetPositionf(controller.position.x,controller.position.y+0.8,controller.position.z,1)
time=AppTime()
gunfirefrequency=80
gunswayspeed=0.001*20.0
gunoffset = gunpos:Copy()
gunswayamplitude = 0.02
if KeyDown(KEY_W)==1 or KeyDown(KEY_D)==1 or KeyDown(KEY_A)==1 or KeyDown(KEY_S)==1 then
	gunswayamplitude = 0.03
	gunswayspeed=0.005*20.0
end
smoothedgunswayamplitude = Curve( gunswayamplitude, smoothedgunswayamplitude,8.0  )
smoothedgunswayspeed = Curve( gunswayspeed, smoothedgunswayspeed,8.0 )
if smoothrecoil<0.001 then
	guntime = guntime + AppSpeed() * smoothedgunswayspeed * math.max(0.0,1.0 - smoothswaydamping)
end
gunoffset.z = gunoffset.z - smoothrecoil * 0.05
--smoothedgunswayamplitude = smoothedgunswayamplitude * (1.0 - smoothswaydamping)
gunoffset.x = gunoffset.x + math.sin( guntime ) * smoothedgunswayamplitude * gunscale
gunoffset.y = gunoffset.y + (1.0-math.cos( guntime*2.0 )) * 0.005 * gunscale-- * math.min(1.0,1.0 - smoothswaydamping))
vwep:SetPosition( gunoffset )
recoil = recoil-0.1
swaydamping = math.max( swaydamping - 0.05, 0.0 )
recoil = math.max(recoil,0.0)
smoothrecoil=Curve(recoil,smoothrecoil,3.0)
smoothswaydamping = inc( swaydamping ,smoothswaydamping,0.01 )
gunlight = math.max( gunlight- 0.2, 0.0 )
lightsmoothing =gunlight-- Curve(gunlight,lightsmoothing,8.0)
muzzleflash:SetColor(Vec4(1.0*lightsmoothing,0.6*lightsmoothing,0.0,1.0))
if lightsmoothing <0.01 then
	muzzleflash:Hide()
end
if MouseDown(1)==1 then
	if AppTime()-lastfiretime>gunfirefrequency then
		recoil = 1.0
		lastfiretime=AppTime()+math.random(0,20)
		gunswayspeed=0.0
		gunlight = 1.0
		source_gunshot:Play()
		source_gunshot:SetPitch(1.0 + (math.random()-0.5)*0.05 )
		swaydamping = 1.0
		muzzleflash:Show()
		CreateBullet(vwep:GetPosition(1) , fw.main.camera.mat:K():Scale(300))
	end
end
UpdateBullets()
flashlight.light:SetPosition(fw.main.camera:GetPosition(1))
flashlight.light:SetRotationf( curveangle( fw.main.camera.rotation.x, flashlight.light.rotation.x, 3.0/AppSpeed() ), curveangle( fw.main.camera.rotation.y, flashlight.light.rotation.y, 3.0/AppSpeed() ) )
flashlight.light:Movef(-0.07,-0.04,0.02)
print("Testing")
fw:Render()
Flip(0)
end
controller:Free()
ShowMouse()
3. Open up the editor.
4. Create a terrain.
5. Drop the Monstertruck model class into your scene.
6. Open up the monsterstruck lua file from the editor and replace the code. Note, you may want to backup this file also.
dofile("scripts/base.lua")
dofile("scripts/cr.lua")
function Spawn(model)
local entity=base_Spawn(model)
local pivot
local suspensionlength=0.25
local springconstant=30.0
local springdamper=100.0
Print("Inside Spawn")
init = false
Init()
entity.vehicle=CreateVehicle(model)
entity.tire={}
entity.model.userdata = entity.vehicle
pivot=entity.model:FindChild("tireL1")
if pivot~=nil then
	tireradius=pivot.aabb.h/2.0
	entity.vehicle:AddTire(TFormPoint(pivot.position,pivot.parent,model),tireradius,suspensionlength,springconstant,springdamper)
	entity.tire[0]=LoadMesh("abstract::vehicle_monstertruck_tire_left.gmf")
	pivot:Hide()
end
pivot=entity.model:FindChild("tireL2")
if pivot~=nil then
	tireradius=pivot.aabb.h/2.0
	entity.vehicle:AddTire(TFormPoint(pivot.position,pivot.parent,model),tireradius,suspensionlength,springconstant,springdamper)
	entity.tire[1]=LoadMesh("abstract::vehicle_monstertruck_tire_left.gmf")
	pivot:Hide()
end
pivot=entity.model:FindChild("tireR1")
if pivot~=nil then
	tireradius=pivot.aabb.h/2.0
	entity.vehicle:AddTire(TFormPoint(pivot.position,pivot.parent,model),tireradius,suspensionlength,springconstant,springdamper)
	entity.tire[2]=LoadMesh("abstract::vehicle_monstertruck_tire_right.gmf")
	pivot:Hide()
end
pivot=entity.model:FindChild("tireR2")
if pivot~=nil then
	tireradius=pivot.aabb.h/2.0
	entity.vehicle:AddTire(TFormPoint(pivot.position,pivot.parent,model),tireradius,suspensionlength,springconstant,springdamper)
	entity.tire[3]=LoadMesh("abstract::vehicle_monstertruck_tire_right.gmf")
	pivot:Hide()
end
function entity:UpdateTires()
	for n=0,3 do
		if self.tire[n]~=nil then
			self.tire[n]:SetMatrix( self.vehicle:GetTireMatrix(n) )
		end
	end
end
entity.model:SetKey("collisiontype",COLLISION_PROP)
entity.model:SetKey("mass",1.0)
return entity
end
function Kill(model)
local entity
entity=entitytable[model]
if entity~=nil then
	if entity.tire[0]~=nil then entity.tire[0]:Free() end
	if entity.tire[1]~=nil then entity.tire[1]:Free() end
	if entity.tire[2]~=nil then entity.tire[2]:Free() end
	if entity.tire[3]~=nil then entity.tire[3]:Free() end
end
end
function MyScript(cr)
Print("Inside MyScript() before DoWait()")
DoWait(cr, 10000)	-- wait 5 seconds
Print("Inside MyScript() after DoWait()")
SetGlobalNumber("runGame", 2)	-- exit the game
end
function Update(model)
local model
local entity
for model,entity in pairs(entitytable) do
	entity:UpdateTires()
end
if(init == false) then
	if(GetGlobalNumber("game") == 1) then
		Print("Running MyScript")
		init = true
		RunScript(MyScript)
	end
end
UpdateCR()
--Print("Inside monster truck")
end
7. Run the game from the editor and wait 10 seconds. The game will automatically end and go back to the editor after 5 seconds.
So what should you be looking for? in the monstertruck lua file I include cr.lua. This exposes some method and creates some tables. In the monster truck lua file I added UpdateCR() inside the Update() method. This is what I mean by piggy backing off another entities Update() method. If we would be able to have global variables you wouldn't need to do this step. In monstertruck I create a sort of init method. It makes it so when running in the game mode and the first time it calls Update() it'll run a function that defines the sequence you want to do. In this case it runs MyScript(). This is where the magic happens. It calls DoWait() and passes in 10000 (in ms 10 seconds). This is where it gets complicated to understand how it really works. If you take it at face value without knowing anything about programming it's easy. It means wait for 10 seconds but continue to run the game. Don't block the game for 10 seconds, that would be worthless.
If you study cr.lua you can see how coroutines work. Basically I assigned MyScript() as a coroutine. This allows me to jump out of MyScript() at any time with a call to coroutine.yield() when that's called from inside MyScript(). So then the question is how would we get back inside MyScript()? The answer is inside DoWait() & UpdateCR(). DoWait() adds an entry to the a table that stores the coroutine created, how long to wait, and the started app time. Inside UpdateCR() it loops through all entries in this table to see if the interval time has past. When it does it uses the coroutine stored to start up the MyScript() function again at where it left off.
So you can see how you can expand this to other functions. You can do a DoSound() that comes back after the sound file is completed. You can do a DoMove() which could move 1 entity to another at a given step and only come back into your sequence function when it reached it's destination. These 3 functions alone open up the door to so many interactive options that are easy to script.
 
		 
	
5 Comments
Recommended Comments