Page 1 of 1

A beginner's attempts at LUA

Posted: Thu Feb 26, 2009 11:14 pm
by Fiodis
I've decided to learn how to LUA code. So I read through the shipped doc, which (as usual) explains very little but I get the general gist of how things are named (I know what an event is, an actor, etc.), however I do not know how to set a script up. So I tried to make a basic script. Upon the death of a certain object (a turret), I would respawn the object. I set up my LUA so:
Hidden/Spoiler:
[code]--
-- Copyright (c) 2005 Pandemic Studios, LLC. All rights reserved.
--

-- load the gametype script
ScriptCB_DoFile("ObjectiveConquest")
ScriptCB_DoFile("setup_teams")

-- REP Attacking (attacker is always #1)
REP = 1;
CIS = 2;
-- These variables do not change
ATT = REP;
DEF = CIS;


function ScriptPostLoad()


--This defines the CPs. These need to happen first
cp1 = CommandPost:New{name = "cp1"}
cp2 = CommandPost:New{name = "cp2"}
cp3 = CommandPost:New{name = "cp3"}
cp4 = CommandPost:New{name = "cp4"}



--This sets up the actual objective. This needs to happen after cp's are defined
conquest = ObjectiveConquest:New{teamATT = ATT, teamDEF = DEF,
textATT = "game.modes.con",
textDEF = "game.modes.con2",
multiplayerRules = true}

--This adds the CPs to the objective. This needs to happen after the objective is set up
conquest:AddCommandPost(cp1)
conquest:AddCommandPost(cp2)
conquest:AddCommandPost(cp3)
conquest:AddCommandPost(cp4)

conquest:Start()

EnableSPHeroRules()

end

testfunction = OnObjectKill (script_test_thing, character)
(
RespawnObject (script_test_thing)
)
end


---------------------------------------------------------------------------
-- FUNCTION: ScriptInit
-- PURPOSE: This function is only run once
-- INPUT:
-- OUTPUT:
-- NOTES: The name, 'ScriptInit' is a chosen convention, and each
-- mission script must contain a version of this function, as
-- it is called from C to start the mission.
---------------------------------------------------------------------------
function ScriptInit()

ReadDataFile("ingame.lvl")


SetMaxFlyHeight(30)
SetMaxPlayerFlyHeight (30)

SetMemoryPoolSize ("ClothData",20)
SetMemoryPoolSize ("Combo",50) -- should be ~ 2x number of jedi classes
SetMemoryPoolSize ("Combo::State",650) -- should be ~12x #Combo
SetMemoryPoolSize ("Combo::Transition",650) -- should be a bit bigger than #Combo::State
SetMemoryPoolSize ("Combo::Condition",650) -- should be a bit bigger than #Combo::State
SetMemoryPoolSize ("Combo::Attack",550) -- should be ~8-12x #Combo
SetMemoryPoolSize ("Combo::DamageSample",6000) -- should be ~8-12x #Combo::Attack
SetMemoryPoolSize ("Combo::Deflect",100) -- should be ~1x #combo

ReadDataFile("sound\\yav.lvl;yav1cw")
ReadDataFile("SIDE\\rep.lvl",
"rep_inf_ep3_rifleman",
"rep_inf_ep3_rocketeer",
"rep_inf_ep3_engineer",
"rep_inf_ep3_sniper",
"rep_inf_ep3_officer",
"rep_inf_ep3_jettrooper",
"rep_hover_fightertank",
"rep_hero_anakin",
"rep_hover_barcspeeder")
ReadDataFile("SIDE\\cis.lvl",
"cis_inf_rifleman",
"cis_inf_rocketeer",
"cis_inf_engineer",
"cis_inf_sniper",
"cis_inf_officer",
"cis_inf_droideka",
"cis_hero_darthmaul",
"cis_hover_aat")


ReadDataFile("DC:SIDE\\tur.lvl",
"tur_thing_turret01")

SetupTeams{
rep = {
team = REP,
units = 40,
reinforcements = 1500,
soldier = { "rep_inf_ep3_rifleman",9, 25},
assault = { "rep_inf_ep3_rocketeer",1, 4},
engineer = { "rep_inf_ep3_engineer",1, 4},
sniper = { "rep_inf_ep3_sniper",1, 4},
officer = {"rep_inf_ep3_officer",1, 4},
special = { "rep_inf_ep3_jettrooper",1, 4},

},
cis = {
team = CIS,
units = 40,
reinforcements = 1500,
soldier = { "cis_inf_rifleman",9, 25},
assault = { "cis_inf_rocketeer",1, 4},
engineer = { "cis_inf_engineer",1, 4},
sniper = { "cis_inf_sniper",1, 4},
officer = {"cis_inf_officer",1, 4},
special = { "cis_inf_droideka",1, 4},
}
}

SetHeroClass(CIS, "cis_hero_darthmaul")
SetHeroClass(REP, "rep_hero_anakin")


-- Level Stats
-- ClearWalkers()
AddWalkerType(0, 4) -- special -> droidekas
AddWalkerType(1, 0) -- 1x2 (1 pair of legs)
AddWalkerType(2, 0) -- 2x2 (2 pairs of legs)
AddWalkerType(3, 0) -- 3x2 (3 pairs of legs)
local weaponCnt = 1024
SetMemoryPoolSize("Aimer", 75)
SetMemoryPoolSize("AmmoCounter", weaponCnt)
SetMemoryPoolSize("BaseHint", 1024)
SetMemoryPoolSize("EnergyBar", weaponCnt)
SetMemoryPoolSize("EntityCloth", 32)
SetMemoryPoolSize("EntityFlyer", 32)
SetMemoryPoolSize("EntityHover", 32)
SetMemoryPoolSize("EntityLight", 200)
SetMemoryPoolSize("EntitySoundStream", 4)
SetMemoryPoolSize("EntitySoundStatic", 32)
SetMemoryPoolSize("MountedTurret", 32)
SetMemoryPoolSize("Navigator", 128)
SetMemoryPoolSize("Obstacle", 1024)
SetMemoryPoolSize("PathNode", 1024)
SetMemoryPoolSize("SoundSpaceRegion", 64)
SetMemoryPoolSize("TreeGridStack", 1024)
SetMemoryPoolSize("UnitAgent", 128)
SetMemoryPoolSize("UnitController", 128)
SetMemoryPoolSize("Weapon", weaponCnt)

SetSpawnDelay(10.0, 0.25)
--ReadDataFile("dc:SCR\\SCR.lvl", "SCR_conquest")
ReadDataFile("dc:SCR\\SCR.lvl", "SCR_conquest")
SetDenseEnvironment("false")




-- Sound

SetSoundEffect("ScopeDisplayZoomIn", "binocularzoomin")
SetSoundEffect("ScopeDisplayZoomOut", "binocularzoomout")

voiceSlow = OpenAudioStream("sound\\global.lvl", "rep_unit_vo_slow")
AudioStreamAppendSegments("sound\\global.lvl", "cis_unit_vo_slow", voiceSlow)
AudioStreamAppendSegments("sound\\global.lvl", "global_vo_slow", voiceSlow)

voiceQuick = OpenAudioStream("sound\\global.lvl", "rep_unit_vo_quick")
AudioStreamAppendSegments("sound\\global.lvl", "cis_unit_vo_quick", voiceQuick)

OpenAudioStream("sound\\global.lvl", "cw_music")
-- OpenAudioStream("sound\\global.lvl", "global_vo_quick")
-- OpenAudioStream("sound\\global.lvl", "global_vo_slow")
OpenAudioStream("sound\\yav.lvl", "yav1")
OpenAudioStream("sound\\yav.lvl", "yav1")
OpenAudioStream("sound\\yav.lvl", "yav1_emt")

SetBleedingVoiceOver(REP, REP, "rep_off_com_report_us_overwhelmed", 1)
SetBleedingVoiceOver(REP, CIS, "rep_off_com_report_enemy_losing", 1)
SetBleedingVoiceOver(CIS, REP, "cis_off_com_report_enemy_losing", 1)
SetBleedingVoiceOver(CIS, CIS, "cis_off_com_report_us_overwhelmed", 1)

SetOutOfBoundsVoiceOver(2, "cisleaving")
SetOutOfBoundsVoiceOver(1, "repleaving")

SetAmbientMusic(REP, 1.0, "rep_yav_amb_start", 0,1)
SetAmbientMusic(REP, 0.8, "rep_yav_amb_middle", 1,1)
SetAmbientMusic(REP, 0.2, "rep_yav_amb_end", 2,1)
SetAmbientMusic(CIS, 1.0, "cis_yav_amb_start", 0,1)
SetAmbientMusic(CIS, 0.8, "cis_yav_amb_middle", 1,1)
SetAmbientMusic(CIS, 0.2, "cis_yav_amb_end", 2,1)

SetVictoryMusic(REP, "rep_yav_amb_victory")
SetDefeatMusic (REP, "rep_yav_amb_defeat")
SetVictoryMusic(CIS, "cis_yav_amb_victory")
SetDefeatMusic (CIS, "cis_yav_amb_defeat")

SetSoundEffect("ScopeDisplayZoomIn", "binocularzoomin")
SetSoundEffect("ScopeDisplayZoomOut", "binocularzoomout")
--SetSoundEffect("BirdScatter", "birdsFlySeq1")
--SetSoundEffect("WeaponUnableSelect", "com_weap_inf_weaponchange_null")
--SetSoundEffect("WeaponModeUnableSelect", "com_weap_inf_modechange_null")
SetSoundEffect("SpawnDisplayUnitChange", "shell_select_unit")
SetSoundEffect("SpawnDisplayUnitAccept", "shell_menu_enter")
SetSoundEffect("SpawnDisplaySpawnPointChange", "shell_select_change")
SetSoundEffect("SpawnDisplaySpawnPointAccept", "shell_menu_enter")
SetSoundEffect("SpawnDisplayBack", "shell_menu_exit")


--OpeningSateliteShot
AddCameraShot(0.908386, -0.209095, -0.352873, -0.081226, -45.922508, -19.114113, 77.022636);

AddCameraShot(-0.481173, 0.024248, -0.875181, -0.044103, 14.767292, -30.602322, -144.506851);
AddCameraShot(0.999914, -0.012495, -0.004416, -0.000055, 1.143253, -33.602314, -76.884430);
AddCameraShot(0.839161, 0.012048, -0.543698, 0.007806, 19.152437, -49.802273, 24.337317);
AddCameraShot(0.467324, 0.006709, -0.883972, 0.012691, 11.825212, -49.802273, -7.000720);
AddCameraShot(0.861797, 0.001786, -0.507253, 0.001051, -11.986043, -59.702248, 23.263165);
AddCameraShot(0.628546, -0.042609, -0.774831, -0.052525, 20.429928, -48.302277, 9.771714);
AddCameraShot(0.765213, -0.051873, 0.640215, 0.043400, 57.692474, -48.302277, 16.540724);
AddCameraShot(0.264032, -0.015285, -0.962782, -0.055734, -16.681797, -42.902290, 129.553268);
AddCameraShot(-0.382320, 0.022132, -0.922222, -0.053386, 20.670977, -42.902290, 135.513001);
end

[/code]
with my makeshift code tucked in at the very end of ScriptPostLoad(). It seemed very short to me, and I felt something was missing, but I can't place my finger on what. In ZE the object is name script_test_thing.

Munging the code got the mungelog:
Hidden/Spoiler:
[code]C:\BF2_ModTools\data_SCR\_BUILD\..\..\ToolsFL\Bin\luac.exe: ..\..\common\scripts\SCR\SCRc_con.lua:47: ambiguous syntax (function call x new statement) near `('
ERROR[scriptmunge scripts\SCR\SCRc_con.lua]:Could not read input file.ERROR[scriptmunge scripts\SCR\SCRc_con.lua]:Could not read input file. [continuing]
2 Errors 0 Warnings

WARNING[PC_modelmunge msh\ME_ball_turret.msh]:ME_ball_turret has 4710 vertices and NO COLLISION GEOMETRY (WILL BE SLOW)!
0 Errors 1 Warnings

[/code]
I'm not sure what the word ambiguous means, or syntax; but neither sound good, and I'm sure it's somehow related to my problem (well, gee, look at that!). What am I missing?

Re: A beginner's attempts at LUA

Posted: Thu Feb 26, 2009 11:20 pm
by Maveritchell
As a general rule, you can't respawn most objects once you've killed them if they don't have some sort of RespawnTime set in the odf - and even then, it's better to "respawn" them with SetProperty(objectname, "CurHealth", numericalvalueofobject'smaxhealth).

Oh and also your code should go in ScriptPostLoad.

Re: A beginner's attempts at LUA

Posted: Fri Feb 27, 2009 5:17 pm
by Fiodis
But I thought it was in ScriptPostLoad.

Re: A beginner's attempts at LUA

Posted: Fri Feb 27, 2009 5:21 pm
by Maveritchell
function ScriptPostLoad()
|
|
|
|
V
end

That is ScriptPostLoad. Understand how functions nest (function - end). If you need help with this, Notepad ++ keeps track of your nesting for you.

Re: A beginner's attempts at LUA

Posted: Fri Feb 27, 2009 5:49 pm
by Fiodis
Oh, I see.

Ok, so I've downloaded Notepad++ and it looks rather good, pointing out all sorts of things. My current script, minus the things below ScriptInt:
Hidden/Spoiler:
[code]--
-- Copyright (c) 2005 Pandemic Studios, LLC. All rights reserved.
--

-- load the gametype script
ScriptCB_DoFile("ObjectiveConquest")
ScriptCB_DoFile("setup_teams")

-- REP Attacking (attacker is always #1)
REP = 1;
CIS = 2;
-- These variables do not change
ATT = REP;
DEF = CIS;


function ScriptPostLoad()


--This defines the CPs. These need to happen first
cp1 = CommandPost:New{name = "cp1"}
cp2 = CommandPost:New{name = "cp2"}
cp3 = CommandPost:New{name = "cp3"}
cp4 = CommandPost:New{name = "cp4"}



--This sets up the actual objective. This needs to happen after cp's are defined
conquest = ObjectiveConquest:New{teamATT = ATT, teamDEF = DEF,
textATT = "game.modes.con",
textDEF = "game.modes.con2",
multiplayerRules = true}

--This adds the CPs to the objective. This needs to happen after the objective is set up
conquest:AddCommandPost(cp1)
conquest:AddCommandPost(cp2)
conquest:AddCommandPost(cp3)
conquest:AddCommandPost(cp4)

conquest:Start()

EnableSPHeroRules()

testfunction = OnObjectKill (script_test_thing, character)
(
AddDeathRegion("luatrap")
)

end



[/code]
I'm trying now to, upon the destruction of a certain object (named script_test_thing in ZE) to activate a death region that will kill everyone around it (named luatrap).


The mungelog still contains that syntax error, though:
Hidden/Spoiler:
[code]C:\BF2_ModTools\data_SCR\_BUILD\..\..\ToolsFL\Bin\luac.exe: ..\..\common\scripts\SCR\SCRc_con.lua:45: ambiguous syntax (function call x new statement) near `('
ERROR[scriptmunge scripts\SCR\SCRc_con.lua]:Could not read input file.ERROR[scriptmunge scripts\SCR\SCRc_con.lua]:Could not read input file. [continuing]
2 Errors 0 Warnings


[/code]
What does the syntax error mean?

Re: A beginner's attempts at LUA

Posted: Fri Feb 27, 2009 6:23 pm
by Maveritchell
Oh I suppose I should have mentioned that your formatting was wrong too, didn't pay that much attention to the first post. It should look like this:

Code: Select all

testfunction = OnObjectKill (
	function(object, killer)
		if GetEntityName(object) == "script_test_thing" then
--I don't actually know if you want this line, it's hard to tell if you meant a specific object or not. In any case this is the ZE object name of the object you kill
   			AddDeathRegion("luatrap")
		end
	end
)
If you're not looking for a specific object, delete the "if" statement (and its corresponding "end"). I would suggest that you take a look at actual luas to see some formatting, because it seems like that's what's eluding you at the moment.

Re: A beginner's attempts at LUA

Posted: Fri Feb 27, 2009 7:28 pm
by Fiodis
Hmm. Ok, I think I've refined my code to the point it should work; but it doesn't seem to do so. The munge log:
Hidden/Spoiler:
[code]C:\BF2_ModTools\data_SCR\_BUILD\..\..\ToolsFL\Bin\luac.exe: ..\..\common\scripts\SCR\SCRc_con.lua:48: unexpected symbol near `)'
ERROR[scriptmunge scripts\SCR\SCRc_con.lua]:Could not read input file.ERROR[scriptmunge scripts\SCR\SCRc_con.lua]:Could not read input file. [continuing]
2 Errors 0 Warnings

[/code]
It's complaining about an unexcpected symbol near ")", on line 48.
Hidden/Spoiler:
[code]
42- EnableSPHeroRules()
43-
44- testfunction = OnObjectKill (
45- function (script_test_thing, character)
46- if GetEntityName(object) == "script_test_thing, character" then
47- AddDeathRegion("luatrap")
48- )
49-
50- end[/code]
That is the segment of my code. There should be an unexcpected symbol near the ")" on line 48, but ) is the only thing on line 48. What is it complaining about, then?

Re: A beginner's attempts at LUA

Posted: Fri Feb 27, 2009 7:38 pm
by Frisbeetarian
You need to have to "end"s before the ), that's why it's saying unexpected symbol. You need these ends to close the if statement and the function you just created before you close off the OnObjectKill with the ).

Re: A beginner's attempts at LUA

Posted: Fri Feb 27, 2009 7:52 pm
by Fiodis
Oh, I see. I thought the ) would close it.

Anyway, I tidied it up further and got a clean munge with no errors, but in-game it never works. There are quite a few statements in the error logthat are repeated over and over again. They are:
Hidden/Spoiler:
[code]Message Severity: 3
.\Source\LuaHelper.cpp(312)
CallProc failed: bad argument #1 to `GetEntityName' (string expected, got nil)
stack traceback:
[C]: in function `GetEntityName'
(none): in function <(none):45>


and


Message Severity: 3
.\Source\LuaHelper.cpp(312)
CallProc failed: bad argument #1 to `GetEntityName' (string expected, got nil)
stack traceback:
[C]: in function `GetEntityName'
(none): in function <(none):45>
[C]: in function `KillObject'
(none): in function <(none):493>
(none): in function `uf_applyFunctionOnTeamUnits'
(none): in function `uf_killUnits'
(none): in function `run'
(none): in function <(none):1973>

uf_applyFunctionOnTeamUnits(): Team, Unit: 1 6
[/code]
My current set of code:
Hidden/Spoiler:
[code] EnableSPHeroRules()

testfunction = OnObjectKill (
function (script_test_thing, character)
if GetEntityName(object) == "script_test_thing" then
AddDeathRegion("luatrap")
end
end
)

end[/code]


Originally I had accidentally put "script_test_thing, character" instead of "script_test_thing"; I caught that, fixed it, and got the same severity: 3's. The quotes around the name define it as a string, right? So why should it be seen as a nil?

Re: A beginner's attempts at LUA

Posted: Fri Feb 27, 2009 7:57 pm
by Maveritchell
Why didn't you just use what I wrote and work off of that? When you write:

function(script_test_thing, character)

you're giving an object and its actor (this is covered in the scripting_system doc). The actor is "character" and the object being acted upon is "script_test_thing." Those are now your two variables that represent the object and the actor for the rest of the function.

If a name is written in quotes, like this:
"script_test_thing"
you're referring to a string - sometimes it's something from an .odf, sometimes it's a ZE name, but it's never a variable, it's a string (it can be equal to a variable but it is not variable).

So when you write:
GetEntityName(object) == "script_test_thing"
You've got it trying to find the name of the variable "object" (which doesn't exist, because you never defined the object of the function as "object") and test if it's equal to the string "script_test_thing," which may or may not exist but it is irrelevant because your equality has no meaning with an undefined variable.

Re: A beginner's attempts at LUA

Posted: Fri Feb 27, 2009 8:34 pm
by Fiodis
Oh, now I get it. Let me see if have it straight:
Maveritchell wrote:
Hidden/Spoiler:
testfunction = OnObjectKill (
function(object, killer) object is the object being acted on, in this case killed
if GetEntityName(object) == "script_test_thing" then object here refers to the object of the function above, OnObjectKill
--I don't actually know if you want this line, it's hard to tell if you meant a specific object or not. In any case this is the ZE object name of the object you kill
AddDeathRegion("luatrap")
end
end These two ends are necesary to close the function. But why two?
)
So the correct code, I think, is
Hidden/Spoiler:
[quote]
testfunction = OnObjectKill (
function (script_test_thing, character) script_test_thing is the ZE name of the object being killed
if GetEntityName(script_test_thing) == "script_test_thing" then script_test_thing in the parentheses by GetEntityName tells the LUA to look to see if this variable (the object in OnObjectKill) has a ZE name equal to "script_test_thing"
AddDeathRegion("luatrap")
end
end
)
[/quote]

EDIT - It worked!!! :D

Now I have a basic grasp of how things are organized. I'll try to write a few from scratch and see what I get. Thanks, everyone!


EDIT 2 - I ran into something that confused me.

I tried to set up a code where, on entering a region, an elevator lifts the player up to a platform, then goes back down by itself. Then I added a second set of code that would call the elevator back up if the player enters another region, on top of the platform. The two codes:
Hidden/Spoiler:
[code] testfunction2 = OnEnterRegion (
function (enter_region, player)
if IsCharacterHuman(player) then
PlayAnimation (elavator)
end
end,
"enter_region"
)

testfunction3 = OnEnterRegion (
function (enter_region2, player)
if IsCharacterHuman (player) then
PlayAnimation (elavator)
end
end,
"enter_region2"
)[/code]
What confuses me is that it doesn't work, but I don't see anything relating to it in the error log.



Hold on - I think I see it. The animation's name isn't in quotes. Let me fix that.


EDIT ....um, 3 - That didn't seem to fix it. Now I really am confused.


EDIT...lol, 4. I think I've forgotten ActivateRegion lines. Oops.

EDIT 5 - Ok, here is the big confusion. I added in the necesary lines, and in-game it works, or at least the first OnEnterRegion does. The second one doesn't. Also, when I hopped off the platform and tried to do the first OnEnterRegion again, it didn't work. That's strange; I thought code in ScriptPostLoad wasn't single-run only.

Why does it stall like that?

Re: A beginner's attempts at LUA

Posted: Sat Feb 28, 2009 12:23 am
by Frisbeetarian
Based on what you wrote in blue in the hidden areas, I don't think you understand how functions work. (This is to help you out, not answer your question.) You wrote

Code: Select all

function (script_test_thing, character)
if GetEntityName(script_test_thing) == "script_test_thing" then
when you could have just as easily written, as Mav suggested, and the scripting guide suggests,

Code: Select all

function (object, killer)
if GetEntityName(object) == "script_test_thing" then
The word object in this case is something called a local variable. The way functions work is that (in the case of using OnObjectKill) every time an object is killed, the object killed is passed into function and assigned to the local variable object. The killer of this object killed is then assigned to the local variable killer. In your case, all you did was called the local variables something different, script_test_thing and character respectively. It just so happened that you called the local variable the same thing as the object killed, but it doesn't have to be called that. Also, as a side note, the definition of a local variable is such that you can't access is outside of the function in which it is established. I hope you learned a little something about programming.

In the case of the two ends, which you questioned the necessity, look to what I said before, I told you why you needed two of them:
Frisbeetarian wrote:You need these ends to close the if statement and the function

Re: A beginner's attempts at LUA

Posted: Sat Feb 28, 2009 12:23 pm
by Fiodis
Oh. I thought that the statement was part of the function. I think I understand it now.

And I've fixed that problem with the anim, I added some PauseAnimation and RewindAnimation lines.