Lua Scripting Tutorial for Star Wars EAW / FOC

02 Sep 2015 16:45 #74497 by Imperial
Here's a nice little example script that triggers as soon any unit gets nearby to the Object:


--=================== Definitions ===================
function Definitions()
Define_State("State_Init", State_Init)

-- This is the Radius for triggering
Trigger_Radius = 320


--=================== State_Init ===================
function State_Init(message)
if message == OnEnter then

-- Registering Proximity Event: The Object calls the Function_Name as soon the "Trigger_Radius" was reached
Object.Event_Object_In_Range(Function_Name, Trigger_Radius)


--=================================== Threads ===================================

function Function_Name(Object, Nearby_Unit)
if Nearby_Unit.Get_Owner().Is_Human() then

-- do something:
-- This will terminate the event so it wont trigger more then 1x

--================================= End of File =================================

Please Log in or Create an account to join the conversation.

13 Sep 2015 04:14 #74783 by Imperial
Did you ever tried to write a Blacklist with 40 entries ?

I just needed to write one and my issue was using the conventional way I'd need 40 of these lines:

if TestValid(Unit)
-- And it is none of the Units in the Exclusion List table
and Unit.Get_Type() ~= Find_Object_Type(Exclusion_List[1])
and Unit.Get_Type() ~= Find_Object_Type(Exclusion_List[2])
and Unit.Get_Type() ~= Find_Object_Type(Exclusion_List[3])
and Unit.Get_Type() ~= Find_Object_Type(Exclusion_List[4])
and Unit.Get_Type() ~= Find_Object_Type(Exclusion_List[5]) then

Instead, I wrote a dynamic list that searches for existing Objects on the map, and counts them.
Then finally starts at the counted value and checks down all of them until 0 was reached.
Within the check it is going to set (in my case) the variable "Stolen_Unit" to false if any of the blacklisted units is our Stolen_Unit.

If you try to implement such a blacklist to one of your scripts please be careful with the repeat state, its very fragile and each little change can crash the script.

-- CAUTION: Any mistyping, nonexistent xml Names or forgotten , commas in this table will crash the script !!!
Excluded_Units = {"Unit_1", "Unit_2", "Unit_3", "Unit_4", "Unit_5"}

Stolen_Unit = false

Stolen_Unit = -- (set any unit you like to this variable of interest)

local Exclusions = {}

-- For all units in the Excluded_Units table
for each,String_Name in pairs(Excluded_Units) do
-- Find all units of this type into another table variable
local Found_Exclusions = Find_All_Objects_Of_Type(String_Name)

-- Again for all units, this time not the strings but the actual units
for each,Unit in pairs(Found_Exclusions) do
-- If exist
if TestValid(Unit) then
-- Then we will insert it into the table "Exclusions"
table.insert(Exclusions, Unit)

-- We're going to count the amount of blacklisted Units.
local Exclusion_Count = table.getn(Exclusions)

-- Making sure this only fires if any blacklisted Objects were found on a map
if Exclusion_Count ~= 0 then
-- Repeat this piece of code until all "Exclusions" were checked
-- CAUTION: We need to sleep abit to prevent the game from freezing
Sleep (0.2)
-- Then we check whether the highlighted object is blacklisted in the Excluded_Units table
-- Exclusions[Exclusion_Count] means we start at the highest value (count) and go down to 0

if Stolen_Unit.Get_Type() ~= Exclusions[Exclusion_Count].Get_Type() then
-- Then we count down from the unit count
Exclusion_Count = Exclusion_Count - 1
-- If our target Object, the Stolen_Unit was matched to any of the Found_Exclusions, we drop it
Stolen_Unit_Is_Valid = false
Exclusion_Count = Exclusion_Count - 1
-- We count down until 1 was reached and Found_Exclusions finally reaches 0
until Exclusion_Count == 0

-- Needed the additonal variable Stolen_Unit_Is_Valid because directly changing Stolen_Unit above crashes the script.
if Stolen_Unit_Is_Valid == false then
-- Deleting Stolen unit because it is not a valid target
Stolen_Unit = false
-- Resetting variable for next usage
Stolen_Unit_Is_Valid = true
-- As soon the unit got destroyed, trigger the function, wont work with tables, use getn instead.
Register_Death_Event(Stolen_Unit, Destroy_Docked_Units)

Please Log in or Create an account to join the conversation.

19 Sep 2015 01:00 #74878 by Imperial
How do we use the Camera controls?
Well, this is quite tricky and needs lot of testing. It works for both story scripts and object scripts.

In this post I often use Object, which points at the Unit that executes the script, but instead you could use something like

Target_Object = Find_First_Object("Unit_Name")

The starting position for the camera will be the current screen, unless you define the starting position of the camera to a certain marker object on the map, like this:

-- Defining the positions of Markers objects ( Example hint names 01 and 02 were added to my map using the map editor):
Marker_01 = Find_Hint("ATTACKER ENTRY POSITION", "01")
Marker_02 = Find_Hint("DEFENDING FORCES POSITION", "02")

-- Positon of the Camera (Optional, without it the Camera will start at current screen)
Set_Cinematic_Camera_Key(Marker_01, 100, 40, 0, 1, 0, 0, 0)
-- Rotation of the Camera (Optional, without it the Camera will start at current screen)
Set_Cinematic_Target_Key(Marker_02, 0, 0, 0, 0, 0, 1, 0)

Find_Hint will look for the name of the marker in its "hint" field, written by the Map Editor, you could either search for "" as empty and nameless marker but it will find the first nameless object of this kind. Then you have to add the code below as well.
However, you can leave the code above away and use only the next field. Then the camera will start on the current screen of the player.
This is the simplest setup:

-- Controls need to be locked, or any mouse movement will end the camera movement

-- Transition_Cinematic_Camera_Key(target_position, time, x_distance, y_pitch, z_yaw, euler, pobj, use_object_rotation, cinematic_animation)
-- Rotate the Camera during parameter_2 seconds away from the current position towards parameter_1
Transition_Cinematic_Camera_Key(Object, 4, 1800, 400, 0, 0, 0, 1, 0)

-- You need to sleep for parameter_2 seconds, or the camera deactivates too early using End_Cinematic_Camera() .
-- Rotate the Camera during parameter_2 seconds towards parameter_1
Transition_Cinematic_Target_Key(Object, 4, 0, 0, 0, 0, 0, 1, 0)

-- Unlocking controls again

Transition_Cinematic_Camera_Key() = Rotates away from parameter_1
Transition_Cinematic_Target_Key() = Rotates towards parameter_1
You can use both of them in combination as above, or use only one of them and leave the other line away.

Parameter 3, 4 and 5 of both allows you to move the camera in the x,y and z axis for the value you enter, aviously its a blind guessing of distances if you want to point the camera to a object using these parameters, the devellopers who made this most have been drunken...

And here is an other example with even more tricks:


-- These 2 Are optional, but if you use them, dont forget to deactivate them at the bottom!!:
-- Letter_Box_In displays the 2 black bars at the top and the bottom.
-- Disabling AI so Units wont shoot eachother

-- With this activated, the camera will follow a Unit or spaceship until it was ended using Transition_Cinematic_Camera_Key()
-- Need to sleep as long the camera is following!

-- This is the same code as in the box above:
-- Rotate the Camera during parameter_2 seconds away from the current position towards parameter_1

Transition_Cinematic_Camera_Key(Object, 4, 1800, 400, 0, 0, 0, 1, 0)
-- Rotate the Camera during parameter_2 seconds towards parameter_1
Transition_Cinematic_Target_Key(Object, 4, 0, 0, 0, 0, 0, 1, 0)
-- You need to sleep for parameter_2 seconds, or the camera deactivates too early using Transition_To_Tactical_Camera.

-- Optional: Scrolls the Camera during parameter_1 back to its original position
-- End_Cinematic_Camera is commented because Transition_To_Tactical_Camera() does this
-- End_Cinematic_Camera()


-- These 2 are optional, use only if deactivated above:

And there is also


Which instantly shows that Object.


Zoom_Camera(0.7, 0)

Please Log in or Create an account to join the conversation.

07 May 2016 04:50 #79463 by Imperial
These tags are interesting for the Galactic Conquest Layer:

Planet_01 = Find_First_Object("Planet_Name")
Planet_02 = Find_First_Object("Planet_Name")

Add_Planet_Highlight(Planet_01, "0")
-- It blinks as long as the script sleeps
Sleep (3.0)

Additionally .Get_Planet_Location() is a pretty cool function. You could do funny things with it like the Teleportation function I just figured out for a Object Script that runs on the GC Layer as soon its Dummy Unit was spawned (means if you wish to use it as story script you'd need to replace "Object" with any other unit to get it working.)


--================================= Base Definitions =================================
function Definitions()
Define_State("State_Init", State_Init)


--================================= The Mission begins =================================

function State_Init(message)
if message == OnEnter then

Planet_01 = Find_First_Object("Planet_Name")
Planet_02 = Find_First_Object("Planet_Name")

All_Units = Find_All_Objects_Of_Type("Transport | Fighter | Bomber | Corvette | Frigate | Capital")

Orbital_Fleet = {}

-- Removing any non player Units
for each, Unit in pairs(All_Units) do
if TestValid(Unit) and Unit.Get_Owner() == Object.Get_Owner() then

if Unit.Get_Planet_Location() == Planet_02 then
-- All Nearby Units get duplicated to the destination planet then Deleted
SpawnList({Unit.Get_Type().Get_Name()}, Planet_01, Object.Get_Owner(), true, false)

table.insert(Orbital_Fleet, Unit)

-- Need to despawn seperately using this list in oder to prevent the script form crashing
for each, Unit in pairs(Orbital_Fleet) do

-- The player gets some money to see if the script still runs


This Function teleports all Ships and Transporters it finds in the orbit of one Planet to a second one. Ok, I have to admit it actually kills the units and resurrects them, but fast enough to not be able noticing it. :kawoosh:

Please Log in or Create an account to join the conversation.

20 Jul 2016 23:40 #81607 by Imperial
Ever wished to move Ships and whole Fleets on galactic layer?
Now I am not going to explain how to setup the Galactic Script now (at a later point), but here is the actual code you need to define a fleet for moving. Here, I elaborated multiple ways:

Keeping Simple wrote: Human_Player = Find_Player("Underworld")
Planet_01 = Find_First_Object("Campaign_Planet_A")
Planet_02 = Find_First_Object("Campaign_Planet_B")

-- First parameter can contain one unit only as well
Spawned_Fleet = SpawnList({"Ancient_Cruiser", "Ancient_Battleship"}, Planet_01, Human_Player, true, false)

Planet_03 = Spawned_Fleet[1].Get_Planet_Location() -- Where ever this fleet currently is, you will love this

Fleet_01 = Assemble_Fleet(Spawned_Fleet)
Sleep(1) -- Optional

-- You can either move to the current planet, which switches from one of the 3 slots to another
-- BlockOnCommand(Fleet_01.Move_To(Planet_03))

Being Clever wrote: Human_Player = Find_Player("Underworld")
Planet_01 = Find_First_Object("Campaign_Planet_A")
Planet_02 = Find_First_Object("Campaign_Planet_B")

-- Fancy Oneliner:
Assemble_Fleet(SpawnList(All_Units, Planet_01, Human_Player, true, false)).Move_To(Planet_02)

Being a Maniac wrote: Assemble_Fleet(SpawnList(All_Units, Find_First_Object("Campaign_Planet_A"), Find_Player("Underworld"), true, false)).Move_To(Find_First_Object("Campaign_Planet_B"))

This is how you get a Planet to move all enemy units towards any planet, they move only 1 planet and then you need to again give command to move further. After that you can change faction of that planet to the one of your unit to auto conquer it:

Planet_01 = Find_First_Object("Campaign_Planet_A")
Planet_02 = Find_First_Object("Campaign_Planet_B")

Orbital_Units = {}

-- Getting all Units, don't use Fighters or they will crash the script after assambling fleet
All_Units = Find_All_Objects_Of_Type("Hero | Infantry | Bomber | Corvette | Frigate | Capital")

for each, Unit in pairs(All_Units) do
-- If is located on the target Planet the 1 or 2 Planets will change faction and we put all units there into a variable
if TestValid(Unit) and Unit ~= Object and Unit.Get_Planet_Location() == Planet_01 then
table.insert(Orbital_Units, Unit)

Sleep (0.5)

-- Sending all units away from the conquered planet.
Orbital_Fleet = Assemble_Fleet(Orbital_Units)

-- Careful this line could cause crashes because of Change_Owner
-- The planet will turn neutral unless you instantly spawn or move own units to it.

You also can leave the part with the planet ownership change away so the fleet of a planet moves 1 slot towards the target planet. (Caution you would need to run Orbital_Fleet.Move_To(Planet_02) for each jump, maybe in a for loop.
And be aware that the Assemble_Fleet function or the Move_To one will crash the script if a Unit/Hero with Scout ability is on Planet_01!! It wil also crash if you sent one of your scouts to view a enemy planet which is the target.)

If you just wish to eradicate all enemy fleets from a planet without moving them to the next planet, you can simply use:


Its also possible to define a Table variable that contains a list of planets, the script can conquer all of them for you in no time:

Planet_Exclusion_List = {"Planet_01", "Planet_02", "Planet_03",
"Planet_04", "Planet_05"

-- Specifies if we like to conquer all Planets in this GC that are listed in the table above, or if we reverse it to conquer all of the not listed ones.
Reverse_Planet_Selection = false

for each, Planet in pairs(FindPlanet.Get_All_Planets()) do
-- Only excluded planets wont get conquered
if Reverse_Planet_Selection == false and not Is_Object_In_List(Planet_Exclusion_List, Planet) then
-- Conquering Planet to the Owner faction

-- This causes to conquer only the selected planets, above it conqers all unselected ones
elseif Reverse_Planet_Selection and Is_Object_In_List(Planet_Exclusion_List, Planet) then
-- Little break between each planet conquer so the user can get all conquer messages one by one
Sleep (0.4)

For the code above you additonally will need a function to test whether a planet is contained in the table, paste it to the bottom of your script OUTSIDE of the state above:
Warning: Spoiler! [ Click to expand ]

Or you could utilize functios of the Deathstar to destroy Planets with nice explosion effect and turn them into a Asteroid field. The deathstar spawns, fires and despawn in a few milliseconds and there is no chance the player will see it. The planet just explodes:

-- SGMG Function: Destroy a planet in GC
Death_Star_Fleet_List = SpawnList({"Death_Star"}, Planet, Find_Player("Neutral"), false, false)

Death_Star = Find_First_Object("Death_Star")

-- The Planet is useless wasteland now, careful this line could cause crashes because of Change_Owner
-- Waiting abit before we destroy the next one
Sleep (0.6)

You could either destroy the whole Galaxy like a maniac and watch all of them exploding one by one :evil: :a_yikes: :
Warning: Spoiler! [ Click to expand ]

Enjoy :w_tongue:

Please Log in or Create an account to join the conversation.

13 Aug 2016 04:27 #82432 by Imperial
By the way movement (post above), we can also do some movement on the space layer using Object scripts.
I wrote a smart little script, we can use that for more missions.
What it does is it allows you to specify a table of Waypoints, Relay_A - Relay_C and the script finds all instances of that type on the map and orders the specified Ship types in the table Patrole_Ship to move from Instance to instance of that route:

Warning: Spoiler! [ Click to expand ]

You could also set a single type of Marker into a Relay list, as done above. If more of them are placed on the map the ship will cycle through all of these type in the order they were found by find_all_objects_of_type.

So, what you have to do in order to get it working is simply adding ObjectScript_Patrol_Ships_and_Relay_Hints to the <Lua_Script> tag a variant of your ships, making sure the used markers of the used Relay are on the map
and to adjust some of these bool values, thats it ^^

Further you could use spaceships/structure instances as Relay poins, or add more markers to that patrol lilst to move the Aurora to more points (which works fast now).

Please Log in or Create an account to join the conversation.

24 Aug 2016 15:54 - 24 Aug 2016 16:01 #82742 by Imperial
Winning/Loosing a battle via Lua script:

I was in the situation where I needed to trigger a loose/win condition through a simple object script, but how to actually achieve that?
Usually I would use the story script to trigger a .xml event that has the Victory reward, but you would need to turn each planet on the map into a tactical mission if you want it to work on all planets.

I found a lua script is by far the more elegant solution.
Its rather a work around, filtering all Space Structures from the Enemy units and Despawning them, then causing all bigger ships to Hyperspace away.
In my solution it is the Empire faction who looses, but you could replace that by any faction or use

Enemy_Player = Object.Get_Owner().Get_Enemy()

as universal variable for enemies.

Enemy_Units = Find_All_Objects_Of_Type(Find_Player("Empire"))

for each,Unit in pairs(Enemy_Units) do
-- All structures that cant use the command above to flee need to despawn. Because Starbases have Capital Cathegory
if not Unit.Is_Category("Frigate | Corvette | Hero | Transport")
or Unit.Is_Category("Structure") then


for each,Unit in pairs(Enemy_Units) do
-- If its still there
if Unit ~= nil then
-- Remaining Ships fleet

You could also cause all units to die instead using:

Name_of_Engine_Hardpoint = "Hardpoint_Name" -- Needs to be a Hardpoint that exists in your mod, otherwise wont work
Damage_Amount = 10000

for each,Unit in pairs(Enemy_Units) do
Unit.Take_Damage(Damage_Amount, Name_of_Engine_Hardpoint)

Last edit: 24 Aug 2016 16:01 by Imperial.

Please Log in or Create an account to join the conversation.

Time to create page: 0.422 seconds