Lua unit scripting
Moderator: Moderators
Re: Lua unit scripting
That's what I thought at first too, but that is NOT what smoothAnim=1 does.
What it does is exactly the following:
While executing COB code, any move-now and turn-now that is encountered is stored in a list.
When the thread stops executing because it calls wait-for-move or wait-for-turn for a move/turn that is not finished yet, or because the outermost function returned, this list is flushed: all the move-now and turn-now are executed, as-is, without any extra interpolation.
However, when the thread stops executing because of a sleep more than 30 ms and less than 300 ms, the moves and turns in the list are 'converted' from move-now to move and turn-now to turn, with a speed calculated so the move/turn will finish just before the sleep ends.
For other amounts of sleep nothing special happens.
tl;dr: smoothAnim=1 only converts sequences of move-now/turn-now before a sleep > 30 ms and < 300 ms to move/turn with a speed calculated so they finish just before the sleep ends.
EDIT: wikified
What it does is exactly the following:
While executing COB code, any move-now and turn-now that is encountered is stored in a list.
When the thread stops executing because it calls wait-for-move or wait-for-turn for a move/turn that is not finished yet, or because the outermost function returned, this list is flushed: all the move-now and turn-now are executed, as-is, without any extra interpolation.
However, when the thread stops executing because of a sleep more than 30 ms and less than 300 ms, the moves and turns in the list are 'converted' from move-now to move and turn-now to turn, with a speed calculated so the move/turn will finish just before the sleep ends.
For other amounts of sleep nothing special happens.
tl;dr: smoothAnim=1 only converts sequences of move-now/turn-now before a sleep > 30 ms and < 300 ms to move/turn with a speed calculated so they finish just before the sleep ends.
EDIT: wikified
Re: Lua unit scripting
Yeah, I remember when that change was put in. Originally, it was implemented as I described. However, it was changed because of a bunch of whining about certain OTA scripts going bonkers, because they were written so poorly and nobody wanted to fix them.
I'm just asking if it might be possible, with a brand-new system (where backwards compatibility is no longer a concern, yay), to not follow that precedent. It's such a huge performance boost, and for well-written scripts that do not use wait-for-turn, but instead use sleep timing... it's totally free.
I'm just asking if it might be possible, with a brand-new system (where backwards compatibility is no longer a concern, yay), to not follow that precedent. It's such a huge performance boost, and for well-written scripts that do not use wait-for-turn, but instead use sleep timing... it's totally free.
Re: Lua unit scripting
I'm pretty sure Tobi said already that internally, Spring use radian for angles. So by having directly radian in the lua, you'd remove one multiplication step from the BOS system, not add one.
And let's stop talking about SmoothAnim, I just wish SmoothAnim never existed, it's an hacky engine-side hack attempting to fix broken scripts that result in breaking well-made scripts, and shouldn't enter in any discussion about a clean new animation system.
Other way around. Sheesh, you're unbelievable. SmoothAnim was written because most TA script, including Cavedog's ones, were written poorly, and a S.Y. found it easier to write one engine hack than to hope for thousands of scattered scripts to ever get fixed.
You can use turn .. speed; and sleep ..; to have perfect scripts you know. Without SmoothAnim I mean.
Until you have some repeatable measure about those imagnied performance problem, I will not believe that one or two multiplications per animation are going to have any measurable effect. I think you greatly underestimate the amount of multiplication a simple c<->lua interface will already have.If piece position now == piece position linear * constant + / - some value * constant, that's fairly lengthy float math in Lua.
If you don't get why that has some fairly serious performance ramifications, that's not my problem.
And let's stop talking about SmoothAnim, I just wish SmoothAnim never existed, it's an hacky engine-side hack attempting to fix broken scripts that result in breaking well-made scripts, and shouldn't enter in any discussion about a clean new animation system.
Yeah, I remember when that change was put in. Originally, it was implemented as I described. However, it was changed because of a bunch of whining about certain OTA scripts going bonkers, because they were written so poorly and nobody wanted to fix them.

It's such a huge performance boost, and for well-written scripts that do not use wait-for-turn, but instead use sleep timing... it's totally free.

Re: Lua unit scripting
You're quite right, but the idea of writing scripts with radians is pretty icky. Degrees are a lot easier to visualize.I'm pretty sure Tobi said already that internally, Spring use radian for angles. So by having directly radian in the lua, you'd remove one multiplication step from the BOS system, not add one.
I guess I can wrote every turn command as 1*z, where z is that constant, since Lua will optimize it correctly, though. Just seems like that should be done.
As for SmoothAnim, you're getting it backwards, lol.
What Tobi just reminded me was that SmoothAnim=0 does not operate the way that I originally requested (originally, SmoothAnim was required, but we had a long thread about it at some point years ago).
I am well aware that it was a hack to put interpolation into the engine and keep OTA scripts looking as good as modern ones. I'm saying, "don't implement the current SmoothAnim behavior, do what we originally wanted, which performs a lot better".
I think you need to re-read Tobi's post.You can use turn .. speed; and sleep ..; to have perfect scripts you know. Without SmoothAnim I mean.
Or watch a P.U.R.E. Unit with a walk-cycle, which uses exactly the method you're describing, walk with the engine speed down.
Let's just say that you will not suddenly see really jerky animation.
For example... where RUN_SPEED_FAST = 3
Code: Select all
turn toe_f_r to x-axis <15> speed <5>*RUN_SPEED_FAST;
sleep 1000 / RUN_SPEED_FAST;
Re: Lua unit scripting
FFSYou're quite right, but the idea of writing scripts with radians is pretty icky. Degrees are a lot easier to visualize.

Re: Lua unit scripting
Which is easier to remember and mentally convert and work with? 32768, or 180?
For me, it's definitely 180. I do not relate to radians, and neither do most people, especially artists, for whom "radian" is a foreign word.
At any rate, that's a very minor side issue.
For me, it's definitely 180. I do not relate to radians, and neither do most people, especially artists, for whom "radian" is a foreign word.
At any rate, that's a very minor side issue.
Re: Lua unit scripting
You are the one who is on about efficiency, having the engine do the math for you is adding cost for the end user. As a programmer you are to take on all of that burden.
Re: Lua unit scripting
If you want to use degrees, that's fine. Just use math.rad(my_angle_in_degrees) to convert. Minimal CPU cost.
(Ok, maybe not that minimal, but easy enough to use other simple methods to optimise it)
And the only COB call-in that Spring requires (or at least did require at some point, sorry Tobi, Argh was at least passingly right) was Create().
Might it not be neater for all the weapon-related callins to provide the weapon number rather than have AimWeapon1 etc?
Is there really a need for seperate HitByWeapon and HitByWeaponID callins?
(Ok, maybe not that minimal, but easy enough to use other simple methods to optimise it)
And the only COB call-in that Spring requires (or at least did require at some point, sorry Tobi, Argh was at least passingly right) was Create().
Might it not be neater for all the weapon-related callins to provide the weapon number rather than have AimWeapon1 etc?
Is there really a need for seperate HitByWeapon and HitByWeaponID callins?
Re: Lua unit scripting
I'm very excited about this but I have some concerns about the current implementation:
SetDirection-> WindDirectionChange
SetSpeed -> WindSpeedChange / ExtractorAmountChange
Otherwise the name implies it is a mobile unit changing direction/speed.
setSFXoccupy -> TerrainChange
If this stays TerrainChange is a clearer name.
Go() is a vague and confusing name. It also seems totally unnessesary:
Go -> ExtractorStart
-- or preferably --
ExtractorMetalChange( unit_id ) do
if IsUnitActive(unit_id) do something end
end
UNIT DEF INFO
Most traditional unit callbacks pass unitDef / targetUnitDef arguments as well as unitID. I would prefer a little extra overhead in the callback initialisation over having to probe for the unitdef since the former can be done in C++.
BUILDERS
Please be explicit when a callback has different arguments and behaviour based on type:
StartBuilding -> MobileStartBuilding / FactoryStartBuilding
QueryBuildInfo -> FactoryBuildInfo
This change makes it clearer the functions have different behavior. It may even be possible that one day a unit can have both properties (like the Fatboy in Supreme Commander but with a nanolathe insted of guns)
WEAPONS
I'd also prefer to see the numbered weapon callbacks change format:
AimWeapon1( ... ) -> AimWeapon( weaponID, weaponDefID, ... )
This will allow behaviours to be shared between common weapons (consider a large tank with 10 turretted machineguns). This way you'd only need a single function and you can call specific behaviours with a comparison, switch or array lookup.
TRANSPORTS
I'm not convinced about air and ground transports sharing a callback name when different behaviour is implied. It would be better as:
TransportDrop -> AirTransportDrop / GroundTransportDrop
If nothing else this should make documentation clearer.
Passenger Falling() should have a StartFalling() callback as well since I suspect it will be common to test for this event and doing so in Falling() would require setting up vars and if statements.
I accept many of these issues above are a legacy of COB and changing them would make conversion from COB slightly harder but these changes are clearer and more consistent with other callbacks. Best to fix COB's mistakes now before any serious Lua development takes place.
SetDirection-> WindDirectionChange
SetSpeed -> WindSpeedChange / ExtractorAmountChange
Otherwise the name implies it is a mobile unit changing direction/speed.
setSFXoccupy -> TerrainChange
If this stays TerrainChange is a clearer name.
Go() is a vague and confusing name. It also seems totally unnessesary:
Go -> ExtractorStart
-- or preferably --
ExtractorMetalChange( unit_id ) do
if IsUnitActive(unit_id) do something end
end
UNIT DEF INFO
Most traditional unit callbacks pass unitDef / targetUnitDef arguments as well as unitID. I would prefer a little extra overhead in the callback initialisation over having to probe for the unitdef since the former can be done in C++.
BUILDERS
Please be explicit when a callback has different arguments and behaviour based on type:
StartBuilding -> MobileStartBuilding / FactoryStartBuilding
QueryBuildInfo -> FactoryBuildInfo
This change makes it clearer the functions have different behavior. It may even be possible that one day a unit can have both properties (like the Fatboy in Supreme Commander but with a nanolathe insted of guns)
WEAPONS
I'd also prefer to see the numbered weapon callbacks change format:
AimWeapon1( ... ) -> AimWeapon( weaponID, weaponDefID, ... )
This will allow behaviours to be shared between common weapons (consider a large tank with 10 turretted machineguns). This way you'd only need a single function and you can call specific behaviours with a comparison, switch or array lookup.
TRANSPORTS
I'm not convinced about air and ground transports sharing a callback name when different behaviour is implied. It would be better as:
TransportDrop -> AirTransportDrop / GroundTransportDrop
If nothing else this should make documentation clearer.
Passenger Falling() should have a StartFalling() callback as well since I suspect it will be common to test for this event and doing so in Falling() would require setting up vars and if statements.
I accept many of these issues above are a legacy of COB and changing them would make conversion from COB slightly harder but these changes are clearer and more consistent with other callbacks. Best to fix COB's mistakes now before any serious Lua development takes place.
Last edited by SpliFF on 26 Aug 2009, 04:32, edited 1 time in total.
Re: Lua unit scripting
+2SpliFF wrote:Best to fix COB's mistakes now before any serious Lua development takes place.
Re: Lua unit scripting
I tend to agree with this mindset.Best to fix COB's mistakes now before any serious Lua development takes place.

Re: Lua unit scripting
Cob was flawless and any changes are heresy and admittance of self-gay.
Re: Lua unit scripting
I loo forward to this outcomeCaydr wrote:Cob was flawless and any changes are heresy and admittance of self-gay.
I dont think it's a good idea ot put Air and ground into the names since they both have different uses that could just as easily be used in an aircraft and a ground based unit.
Instead I would prefer the two to be broken down into their constituent parts for greater flexibility
Re: Lua unit scripting
smelling pretty colors is the best gamesmoth wrote:*FRUMPLE*
Re: Lua unit scripting
I agree with this yeah, personally I prefer just getting rid of setSFXOccupy and Go as both are trivial to emulate without the extra call-in.SpliFF wrote:SetDirection-> WindDirectionChange
SetSpeed -> WindSpeedChange / ExtractorAmountChange
Otherwise the name implies it is a mobile unit changing direction/speed.
setSFXoccupy -> TerrainChange
If this stays TerrainChange is a clearer name.
Go() is a vague and confusing name. It also seems totally unnessesary:
Go -> ExtractorStart
-- or preferably --
ExtractorMetalChange( unit_id ) do
if IsUnitActive(unit_id) do something end
end
I don't agree with this for two reasons:UNIT DEF INFO
Most traditional unit callbacks pass unitDef / targetUnitDef arguments as well as unitID. I would prefer a little extra overhead in the callback initialisation over having to probe for the unitdef since the former can be done in C++.
1) Conceptually this call-in mechanism is already quite some different from traditional unit callbacks, as the actual Lua functions that are called are stored per unit instead of globally.
2) Because of 1) and how the framework is implemented, you can just have the unitDef available by doing somewhere at the top of the script:
Code: Select all
local unitDef = UnitDefs[Spring.GetUnitDefID(unitID)]
I'm actually considering dropping unitID too, after all, because the same thing applies to this. (And having the unitID as function argument seems to only be an advantage when you have scripts that have no need for a separate closure per unit. In other words, when the same function object is reused by different units. I haven't found a script yet to which this really applies.)
First thing may be an idea, although what about StopBuilding? For consistency also two variants, or inconsistent but save on the number of call-ins?SpliFF wrote: BUILDERS
Please be explicit when a callback has different arguments and behaviour based on type:
StartBuilding -> MobileStartBuilding / FactoryStartBuilding
QueryBuildInfo -> FactoryBuildInfo
This change makes it clearer the functions have different behavior. It may even be possible that one day a unit can have both properties (like the Fatboy in Supreme Commander but with a nanolathe insted of guns)
I think QueryBuildInfo has the right name now, at best I'd consider QueryFactoryBuildInfo, because the 'Query' prefix is quite consistently used already for functions that should just return some piece number. I don't want to break this convention.
Hmm, good point.SpliFF wrote:WEAPONS
I'd also prefer to see the numbered weapon callbacks change format:
AimWeapon1( ... ) -> AimWeapon( weaponID, weaponDefID, ... )
This will allow behaviours to be shared between common weapons (consider a large tank with 10 turretted machineguns). This way you'd only need a single function and you can call specific behaviours with a comparison, switch or array lookup.
What do others think about this?
I'd be the first to admit that the transport code in the engine is an epic mess, when I was documenting these call-ins my WTF/min rate raised to extraordinary heights.SpliFF wrote:TRANSPORTS
I'm not convinced about air and ground transports sharing a callback name when different behaviour is implied. It would be better as:
TransportDrop -> AirTransportDrop / GroundTransportDrop
If nothing else this should make documentation clearer.

At some point I'll see how I can clean this up and make all loading behaviors consistent, but for that I'll first have to research the use patterns that are in use in BOS currently wrt these call-ins. (ie. whether lots of script depend on some WTF'ed behavior.)
Personally I'd rather work towards consistent behavior wrt call-ins for air and ground transports then to differentiate between the different behaviors by introducing more call-ins.
I'm actually considering to make Falling being called only once. I haven't found a single script yet that depends on Falling being called every frame, and even if there is one it is trivially easy to emulate the current behavior by starting a thread executing something like this:Passenger Falling() should have a StartFalling() callback as well since I suspect it will be common to test for this event and doing so in Falling() would require setting up vars and if statements.
Code: Select all
while (!landed) do ... Sleep(unitID,30) end
Yeah I totally agree. First milestone was just plain porting the COB engine and now my focus in this area will be removing the COB WTFs as soon as possible.SpliFF wrote:I accept many of these issues above are a legacy of COB and changing them would make conversion from COB slightly harder but these changes are clearer and more consistent with other callbacks. Best to fix COB's mistakes now before any serious Lua development takes place.
(Fixed axis directions, rotation directions already for example.)
Thanks SpliFF for your comments; this in particular are the type of comments I was hoping for

Re: Lua unit scripting
In that case it would be nice to see unit (pointer to unit table) unitDef (pointer to unitDef table) and unitDefID (uint) pushed into the state along with unitID or else many scripts will just end up with repetitive boilerplate code at the top setting these.Tobi wrote: 2) Because of 1) and how the framework is implemented, you can just have the unitDef available by doing somewhere at the top of the script:and have the unitDef available as non-local value (upvalue) in all functions.Code: Select all
local unitDef = UnitDefs[Spring.GetUnitDefID(unitID)]
I'm actually considering dropping unitID too, after all, because the same thing applies to this.
Great work on this btw. It looks like now there isn't much (anything?) left that you can't do in lua.
I'm also wondering wether it makes sense to keep refering to these scripts as 'Animations'. In truth they seem able to do a great deal more than that. Perhaps 'Unit Scripts' would be a better term. I had trouble find the info on the wiki because I was searching for 'unit' not 'animation'.
Final note: Keep documenting this stuff as best you can (i've seen the wiki page) as I'll be writing it all up in my Lua guide. I've recently completed a project that had me tied down all year which will give me back time to persue my own projects, namely the lua guide, mod guide, model importer (assimp) and Metalstorm mod. I'd prefer not to get tied down further digging info out of git changelogs (or the ether).
Re: Lua unit scripting
Replaced SetSpeed and SetDirection with a new WindChanged and ExtractionRateChanged. Removed Go.
Also I created a list of the differences between Lua and BOS here:
http://springrts.com/wiki/Animation-CobLuaDifferences
Also I created a list of the differences between Lua and BOS here:
http://springrts.com/wiki/Animation-CobLuaDifferences
Re: Lua unit scripting
I used it in COB, but with Lua you can either emulate it, or just use UnitEnteredAir/EnteredWater.Tobi wrote:I agree with this yeah, personally I prefer just getting rid of setSFXOccupy and Go as both are trivial to emulate without the extra call-in.SpliFF wrote:setSFXoccupy -> TerrainChange
If this stays TerrainChange is a clearer name.
Re: Lua unit scripting
What about transports? AFAIK you can query if a unit is being transported in lua, but there isn't a call-in to tell you when it is picked up - on the passenger side.
Something like
Loaded ( unitID, transporterID ) -> nil
to go along with Falling and Landed call-ins?
Or UnitEnteredTransport?
Something like
Loaded ( unitID, transporterID ) -> nil
to go along with Falling and Landed call-ins?
Or UnitEnteredTransport?
Re: Lua unit scripting
There exist UnitLoaded and UnitUnloaded in the Lua gadget interface.
I'm not entirely sure yet how to deal with the clash between the two interfaces. Should we strive to have every function only in one place, or to have two complete interfaces, that might overlap on a number of calls?
For example, UnitEnteredAir, UnitLeftAir, UnitEnteredWater, UnitLeftWater, UnitLoaded, UnitUnloaded, UnitFinished, UnitCloaked, UnitDecloaked and StockpileChanged would all be good candidates for call-ins for a unit script.
An example of a clash: you can now override engine damage using the UnitPreDamaged gadget call-in, or using the HitByWeaponId unit script call-in. (And if you use both the new damage of one of them will override the new damage of the other of course.)
I'm not entirely sure yet how to deal with the clash between the two interfaces. Should we strive to have every function only in one place, or to have two complete interfaces, that might overlap on a number of calls?
For example, UnitEnteredAir, UnitLeftAir, UnitEnteredWater, UnitLeftWater, UnitLoaded, UnitUnloaded, UnitFinished, UnitCloaked, UnitDecloaked and StockpileChanged would all be good candidates for call-ins for a unit script.
An example of a clash: you can now override engine damage using the UnitPreDamaged gadget call-in, or using the HitByWeaponId unit script call-in. (And if you use both the new damage of one of them will override the new damage of the other of course.)