Hard Light Productions Forums
Modding, Mission Design, and Coding => The Scripting Workshop => Topic started by: Aginor on July 04, 2012, 01:35:23 pm
-
Hi guys!
Since the link in this thread http://www.hard-light.net/forums/index.php?topic=45554.0
to the scripting.html is dead, could one of you please create a scripting.html from a current build (doesn't have to be the newest one) and post it here?
I only have the WCS build and want to do a little bit of scripting, and unfortunately that build doesn't support the -output_scripting command line parameter it seems.
I know some of the newer features won't work but that doesn't matter, I suppose I'll only need some that are quite old for the scripts I want to test.
Thanks in advance!
-
You'll have to ask this in the WCS thread.
Further, the command line can be run by anybody on any build, so the recent RC's for 3.6.14 can output one.
-
This is scripting.html as of July 2nd
http://blueplanet.fsmods.net/E/scripting.html
btw, you _do_ know that there's nothing preventing you from downloading an FSO build and running it?
-
Thanks a lot, E!
About the FSO build... can it run without all the files? I don't have FS2 on my PC, just WCS (I can't even find my FS2 disks at the moment tbh).
-
It _should_ be able to run long enough to spit out scripting.html, anyway.
-
You can always try running an FSO build on TBP (if you want to download it, that is. It's quite huge).
-
How about BtRL? :)
-
It's too hard to find, I doubt any of the original download locations still has it.
-
Thanks to you guys I have now created my first scripts. :)
What I'm working on at the moment are two scripts to help me with the Strakha cloaking.
I already managed to read the player's ship class and I also can read and write HUD colors. I need that because I want to create the Wing Commander 3 cloaking thing as a script. I already managed to make most of the HUD black and white (except the colors of ships on the radar and the targeting brackets), and next I'll try if it is possible to make the rest happen.
Having some experience with programming/script languages helped me a lot, LUA really isn't hard. It is also quite pleasant sometimes, the syntax is quite easy.
But... I don't like that they decided to call an array "table", that confuses the hell out of me since I work with databases all day :D
And most important: WHO THE F*** thought it was a good idea to let arrays start with one? Fortran was that way and it sucked.
Whatever. It works and I'm now learning about all the objects and how they work.
What I don't understand yet is the hook structure, despite reading the tutorial.
I already got a scripting table that looks like this:
$Global hooks:
$GameInit
[
.. some methods that are used by Saga's HUD scripting
....
]
$HUD:
[
... triggers that use the functions defined above, for making the cool weapons display,
.....
....here I put my stuff that changes the color of all gauges depending on the ship type of the player (if playerShip.class == "Strakha#Inv") etc.
]
#End
Now I want to have that Conditional Hook thingie that triggers when there is a ship collision involving the player. Where do I put it? Everything I tried produced and error saying something like "#End expected" or just did nothing.
-
tables are different from arrays in that elements need not all be the same type. and the indexing starting at 1 is kind of an oddball thing, but you get used to it.
-
Now I want to have that Conditional Hook thingie that triggers when there is a ship collision involving the player. Where do I put it? Everything I tried produced and error saying something like "#End expected" or just did nothing.
If you want to know of collisions involving the player you can use this as a template:
#Conditional Hooks
$On Ship Collision:
[
if (hv.Self == hv.Player) then
-- Add code that should be executed
end
]
#End
Inside that hook you have hv.Self as one ship that collided and hv.Ship as the other. I hope that helps.
-
Thanks m!m, but I already figured that out so far. As far as I have seen the object structure is pretty logical when you are used to OOP. What I don't get is where to put that block (starting with #Conditional Hooks and ending with #End).
Does it go into the same file (scripting.tbl), below the "#End" tag from the Global Hooks? I think I already tried that and the parser said something like "expecting <eof>, found Conditional Hooks".
tables are different from arrays in that elements need not all be the same type.
That's the same as PHP arrays.
-
The #Conditional Hooks part should be located after the #End of the #Global Hooks part but you should consider switching to #Conditional Hooks entirely as that will give you much better control over when the hooks are executed and will make your live easier later.
tables are different from arrays in that elements need not all be the same type.
That's the same as PHP arrays.
I think the goal here is that lua "tables" aren't plain C arrays but can also be used as maps (like PHP arrays) so the term "array" could have been misleading.
-
lua tables are pretty versatile. you can do named keys, use indexes, they can contain sub-tables or any of the other lua data types (including userdata and functions). so a table can do the job of arrays, structs, even objects if your lua fu is strong. note i come from a c/++ background (sort of), so i mean those terms from the perspective of those languages.
-
Yeah, it's really versatile, comparable to PHP. Of course it also shares its weaknesses it seems.
I am not a c or c++ expert (I use mainly script languages and Java) but I understand what you mean, those are also the terms I'm used to most.
A language that isn't strict with datatypes can be quite a shock when you come from a Java- or c background.
It certainly was my impression when I first used PHP. :D
But since I have experience using a couple of such languages already I'm fairly confident I'll get along with LUA quite well, provided a bit of training.
I just couldn't believe it when I saw that they used arrays starting with one. I thought that bad habit had finally died out. :D
-
Found it.
I had to add
$Application: FS2_Open
$State: GS_STATE_GAME_PLAY
before the "$On Ship Collision:[]" block. I don't know exactly why but I read it in one of the examples and it worked :D
-
both of those are conditions that have to be met before the actions below them are executed. i dont know why we need the $application: condition at all since i doubt it would be anything but fs2_open unless they had decided to extend the scripting system to fred or whatever. but i find that ommitting it can lead to stuff not working. essentially what that says is:
if were running fs2_open and the state is GS_STATE_GAME_PLAY then run the following action scripts.
you can stack as many conditions as you want but they all have to be true before the actions are run. all available conditions and actions are listed in the scripting.html right at the top of the file. should probibly say that you probibly shouldnt use global or state hooks. global hooks can only run once, so if you are using modular scripting tables only the one from the first (or last, im not sure) *-sct.tbm will run. i guess they can be useful in tcs and whatnot where youre just using a scripting.tbl. as for state hooks they've mostly been deprecated, i think they were kind of an experimental feature that didnt pan out.
i kinda think the wiki entry for this needs work, i think what we have is left over from 3.6.9/10. its confusing and has a lot of useless or misleading information in it and is kinda vague where it matters.
-
There scripting.html form saga build
[attachment deleted by ninja]
-
?? How did you get that? When I added the command line option nothing happened at all...
-
were you running through the saga launcher? because the launcher removes all flags, also make sure you are adding it to the file in the user documents/Violation/data folder.
-
Volition, not Violation. :)
-
:nervous: Oops
-
No, I added it directly to the file.
but... ...actually I think I should check my PC later, maybe the file is there and I just didn't see it, or the exe wasn't able to save the file because of some strange WinXP Professional 64 (I still curse the day I bought that one) rights issue or something.
-
It's in the directory before the file you modify.
-
Oh man.... turns out it was there all the time, I just never looked in that folder. I somehow assumed it would be in the same folder as the exe, which it of course isn't.
Sorry for stirring everyone up!
<-- feels really stupid now
But while I'm here I got a question:
Is there a possibility to trigger a script even whenever a certain event in the mission becomes true? Maybe by monitoring a SEXP variable?
My problem is the following:
My player cloaking events are working and all, but since I change the player ship's model to another one to have animated cloaking the secondaries reset to the standard missile type and the maximum number.
Until now I just limited the player to that one missile type and stored the number of missiles in a variable. As soon as the event has changed the model it sets the secondary ammo to the stored value.
But since there is no SEXP to set the type of secondary (I think there is one in other builds but not here it seems) I thought I could maybe do it by scripting.
Here's what I tried so far:
-- Mission starts and player flies the Strakha, I read the type of secondary weapon from bank 1 (there is only one)
-- edited it, first version was even worse, didn't work as intended.
misTime = mn.getMissionTime()
if (misTime == 1 and playerShip.Class.Name == "Strakha" and Weapinit == 0) then
orgWeapClass = playerShip.SecondaryBanks[1].WeaponClass.Name
Weapinit = 1
end
-- When the player is cloaking or cloaked OR has just finished decloaking,
-- set the secondary type to the one he is supposed to have
if (
( playerShip.Class.Name == "Strakha#Inv"
or playerShip.Class.Name == "Strakha#Cloak"
or playerShip.Class.Name == "Strakha#Decloak"
)
or
(
playerShip.Class.Name == "Strakha"
and Weapinit == 1
)
) then
playerShip.SecondaryBanks[1].WeaponClass = tb.WeaponClasses[orgWeapClass]
end
I know that code is far from perfect. But worse is: I do that "On Frame" at the moment, which sucks of course. I want to only do that when the player just triggered the cloaking.
Also there is the problem that if the player carries missiles that are smaller than the standard WCS missiles (those are all of the same size) the number is set to the maximum number of standard missiles the ship can carry. That's because when changing the ship model the number of missiles is set to the default number for a short moment before the script changes the type.
Any ideas?
EDIT: Ok, found another error.
-
Is there a possibility to trigger a script even whenever a certain event in the mission becomes true? Maybe by monitoring a SEXP variable?
Create a function in LUA after
$On Mission Start:
or
$On Gameplay Start:
or whatever you want
$Application: FS2_Open
$On Mission Start:
[
function = changestuff(x)
if x=1 then
<do stuff>
...
and call that function in fred with the script-eval sexp like script-eval "changestuff(1)"
You can do it via a changing sexp-variable in Fred but that one would still require $On Frame so I think a function call out of Fred is way better.
-
Ah thanks!
Maybe I can even change some more things in the script so I have to do less in the events. That way mission designers that want to use the Strakha don't have to include six events that have a horrible number of SEXPs in them.
-
Everything you can change by using Fred events can be done within the scripting system as well. It allows you to call every possible SEXP.
And for your missile number problem:
playership.Class = tb.ShipClasses["name"]
playership.SecondaryBanks[x].WeaponClass = tb.WeaponClasses["name"]
playership.SecondaryBanks[x].AmmoLeft = number
in this order
and if it still fails you can add:
playership.SecondaryBanks[x].AmmoMax = number2
before AmmoLeft to overwrite the table setting.
If this doesn't work... in the BtA Demo and my test missions it definetly works.
-
The problem is that I have to determine the number of missiles just before switching the models, but at the moment my script runs when the ship class has already changed (so there is already the wrong number of missiles - the maximum number - in the bank)
But maybe I have the solution for that already. I'll try that. And if it works I think I will put all the stuff into the script instead of the events. Let's see....
-
Change the shipclass within the script instead of FRED and save AmmoLeft before doing that would be the obvious solution.
number = playership.SecondaryBanks[x].AmmoLeft
number2 = playership.SecondaryBanks[x].AmmoMax
playership.Class = tb.ShipClasses["name"]
playership.SecondaryBanks[x].WeaponClass = tb.WeaponClasses["name"]
playership.SecondaryBanks[x].AmmoMax = number2
playership.SecondaryBanks[x].AmmoLeft = number
And if you need a delay between the events do it in 2 different functions sharing the variables or with a delay function in LUA.
-
Yeah, I guess I'll do that.
But first I'll have to fix my script. I somehow destroyed it during one of my last changes :D
-
Ok, most stuff is working now. I had a bit of trouble running SEXPs but the forum search helped me. I never would have guesses it is the "!". The exclamation mark is hardcoded in my brain as a negation already because it is the same in almost every programming language. And of course it isn't the negation in LUA as I had to learn.... :D
Anyway, here's my next question: Admiral MS, you were talking about a LUA delay function or something.
I searched for "delay", "sleep", and "wait" (those are the most common names for such a function in script languages) in the LUA documentation but couldn't find it yet. Can anyone point me to an example?
For those interested in my cloaking script here it is how it looks now:
in the mission there are four events that trigger the "p_CloakOrder" function and keep track of the cloaking status. The player can't cloak or decloak more often than every 5 seconds (because the cloaking and decloaking animations take three seconds), that is done by the events at the moment. The whole other stuff is done by the script below.
I'm using a ghost named cloak_ghost in the mission to store the player's damage. Is there a more elegant way?
-- ------------------------ Cloaking -----------------------------
p_CloakOrder = function(par_order)
playerShip = hv.Player
if (par_order == 1) then
p_Cloaking(0)
end
if (par_order == 2) then
p_Cloaked(0)
end
if (par_order == 3) then
p_Decloaking(0)
end
if (par_order == 0) then
p_Decloaked(0)
end
return 0
end
p_Cloaking = function(x)
mn.runSEXP("play-sound-from-table !0 !0 !0 !144")
orgWeapClass = playerShip.SecondaryBanks[1].WeaponClass.Name
orgAmmo = playerShip.SecondaryBanks[1].AmmoLeft
orgEnergy = playerShip.WeaponEnergyLeft
mn.runSEXP("ship-copy-damage !" .. tostring(playerShip.Name) .. "! !cloak_ghost!")
mn.runSEXP("change-ship-class !Strakha#Cloak! !".. tostring(playerShip.Name) .. "!")
mn.runSEXP("ship-copy-damage !cloak_ghost! !" .. tostring(playerShip.Name) .. "!")
playerShip.SecondaryBanks[1].WeaponClass = tb.WeaponClasses[orgWeapClass]
playerShip.SecondaryBanks[1].AmmoLeft = orgAmmo
playerShip.WeaponEnergyLeft = orgEnergy
mn.runSEXP("lock-primary-weapon !" .. tostring(playerShip.Name) .. "!")
mn.runSEXP("lock-secondary-weapon !" .. tostring(playerShip.Name) .. "!")
mn.runSEXP("hud-set-text !Cinfo! ! Cloaking!")
return 0
end
p_Cloaked = function(x)
orgWeapClass = playerShip.SecondaryBanks[1].WeaponClass.Name
orgAmmo = playerShip.SecondaryBanks[1].AmmoLeft
orgEnergy = playerShip.WeaponEnergyLeft
mn.runSEXP("ship-copy-damage !" .. tostring(playerShip.Name) .. "! !cloak_ghost!")
mn.runSEXP("change-ship-class !Strakha#Inv! !".. tostring(playerShip.Name) .. "!")
mn.runSEXP("ship-copy-damage !cloak_ghost! !" .. tostring(playerShip.Name) .. "!")
playerShip.SecondaryBanks[1].WeaponClass = tb.WeaponClasses[orgWeapClass]
playerShip.SecondaryBanks[1].AmmoLeft = orgAmmo
playerShip.WeaponEnergyLeft = orgEnergy
mn.runSEXP("protect-ship !" .. tostring(playerShip.Name) .. "!")
mn.runSEXP("hud-set-text !Cinfo! ! Cloaked!")
return 0
end
p_Decloaking = function(x)
mn.runSEXP("play-sound-from-table !0 !0 !0 !145")
orgWeapClass = playerShip.SecondaryBanks[1].WeaponClass.Name
orgAmmo = playerShip.SecondaryBanks[1].AmmoLeft
orgEnergy = playerShip.WeaponEnergyLeft
mn.runSEXP("ship-copy-damage !" .. tostring(playerShip.Name) .. "! !cloak_ghost!")
mn.runSEXP("change-ship-class !Strakha#Decloak! !".. tostring(playerShip.Name) .. "!")
mn.runSEXP("ship-copy-damage !cloak_ghost! !" .. tostring(playerShip.Name) .. "!")
playerShip.SecondaryBanks[1].WeaponClass = tb.WeaponClasses[orgWeapClass]
playerShip.SecondaryBanks[1].AmmoLeft = orgAmmo
playerShip.WeaponEnergyLeft = orgEnergy
mn.runSEXP("hud-set-text !Cinfo! ! Decloaking!")
return 0
end
p_Decloaked = function(x)
orgWeapClass = playerShip.SecondaryBanks[1].WeaponClass.Name
orgAmmo = playerShip.SecondaryBanks[1].AmmoLeft
orgEnergy = playerShip.WeaponEnergyLeft
mn.runSEXP("ship-copy-damage !" .. tostring(playerShip.Name) .. "! !cloak_ghost!")
mn.runSEXP("change-ship-class !Strakha! !".. tostring(playerShip.Name) .. "!")
mn.runSEXP("ship-copy-damage !cloak_ghost! !" .. tostring(playerShip.Name) .. "!")
playerShip.SecondaryBanks[1].WeaponClass = tb.WeaponClasses[orgWeapClass]
playerShip.SecondaryBanks[1].AmmoLeft = orgAmmo
playerShip.WeaponEnergyLeft = orgEnergy
mn.runSEXP("unlock-primary-weapon !" .. tostring(playerShip.Name) .. "!")
mn.runSEXP("unlock-secondary-weapon !" .. tostring(playerShip.Name) .. "!")
mn.runSEXP("unprotect-ship !" .. tostring(playerShip.Name) .. "!")
mn.runSEXP("hud-set-text !Cinfo! ! Visible!")
return 0
end
-- ---------------------------------------------------------------
-
You can access and change the hitpoints of the player object using the HitpointsLeft field.
I'm not aware of any functions in LUA (which could be used in the way FSO handles lua scripts) that would work like the sleep functions in other languages.
-
Anyway, here's my next question: Admiral MS, you were talking about a LUA delay function or something.
I searched for "delay", "sleep", and "wait" (those are the most common names for such a function in script languages) in the LUA documentation but couldn't find it yet. Can anyone point me to an example?
I'm not aware of any functions in LUA (which could be used in the way FSO handles lua scripts) that would work like the sleep functions in other languages.
Right - there is no integrated LUA function (as far as I know it would cause the interpreter stop doing things and freeze FSO). So no easy way to delay something.
What I called a "delay function" is a workaround several scripts use. It's a function that checks how much time has passed and activates when time passed is larger than the specified delay.
$Application: FS2_Open
$On Gameplay Start: [
ammodelay = {}
delay = 1
bla = function()
...
AmmoLeft = ...
ammodelay.AmmoLeft = AmmoLeft
ammodelay.starttime = mn.getMissionTime()
end
]
$On Frame:[
if (mn.getMissionTime() - ammodelay.starttime) > delay then
playerShip.SecondaryBanks[1].AmmoLeft = ammodelay.AmmoLeft
end
]
You can use such a delay so set some boolean variables after 5 seconds that enable or disable the cloaking functions.
I'm using a ghost named cloak_ghost in the mission to store the player's damage. Is there a more elegant way?
If you want to copy damage only it's m!m's solution with HitpointsLeft and possibly a loop through the subsystems to get their hitpoints as well. In case you want everything out of shipdata down to Shields, Energy and whatever you can get these values as well and store them - the functions might get a bit longer and complicated though.
-
What I called a "delay function" is a workaround several scripts use. It's a function that checks how much time has passed and activates when time passed is larger than the specified delay.
Ah, ok. It is basically a state machine.
I did it similarly and now I need only two events for player cloaking.
want everything out of shipdata down to Shields, Energy and whatever you can get these values as well and store them - the functions might get a bit longer and complicated though.
I'm doing that at the moment.
Btw: Is there any possibility to get and set the energy management bars? Those are one of the things that reset when changing models. Most of the rest is already in my script now.
-
for delays just use timestamps. some pseudocode:
timestamp = current time + how long you want it to last
if timestamp is more than current time
do stuff
--if we want to do this again at some interval
timestamp = current time + how long you want it to last
end
the timestamp will be handled on the next frame that the if returns true. note that this may result in some lost time, because it will be very unlikely the code will be evaluated exactly when the timestamp expires. so if you need to do something at a regular interval, you need to subtract the current time from the timestamp right after the timestamp expires to get the overtime, then subtract this from the next timestamp.
-
Hi guys!
I have another two questions:
Is there a difference between evaluateSEXP() and runSEXP() ? It is not documented but I _think_ the return values are different. Any information about that?
The other thing is this:
I want to check whether the ship in the variable "nship" has been destroyed or not and act accordingly. So I wrote
destr = mn.evaluateSEXP("is-destroyed-delay !0! !".. tostring(nship.Name) .. "!")
if (not destr) then
-- stuff --
end
But I get only an error: "can't find operator in operator list"
What am I doing wrong?
-
As far as I know runSEXP() just returns whether it was able to do what it should or not.
evaluateSEXP() returns the result of the check like the name indicates but has a slightly different syntax than runSEXP() for some reason:
result = mn.evaluateSEXP("(is-destroyed-delay !0! !"..shipname.."!)")
These functions are a good example of really bad/nonexisting documentation...
-
AH! Thanks a lot, I'll try that once I'm home.
Maybe we should really take a scripting.html from a recent build and comment all the functions, including examples, like you just did, plus common errors or things that look like they were possible with a function but aren't.
Because at the moment a scripting newbie doesn't have it easy. And I am a software developer, I can't even imagine how someone who has no programming/scripting experience yet could do scripts in FS2 based on what documentation we have.
The most useful stuff is in the wiki tutorials and (even more useful) the scripting examples, but the tutorials are very basic and you quickly reach the point where you just don't know how to actually use the functions the scripting.html tells you about. I figured most of it out after a while, but there are many people who don't even understand the error texts or the syntax description. (ok, they could be worse. At least they are not BNF :D )
-
you can edit the function descriptions in lua.cpp so that the game generates more useful instructions automatically. not sure if there is any string length limits there. you might be able to include a couple paragraphs of commentary on a thing and do a way with one liner documentation which seems to plague scripting.html (and much of the wiki). details are important. i tend to fix up any bad documentation every time i screw around in that piece of code. but i make sure i understand how something works before i start spewing out potentially wrong information. events <-> scripting interfaces are kind of a grey area for me since i really never learned how the events system works. think after over a decade of freespace modding this would have been a skill id have picked up by now. :D
-
Maybe we should really take a scripting.html from a recent build and comment all the functions, including examples, like you just did, plus common errors or things that look like they were possible with a function but aren't.
Because at the moment a scripting newbie doesn't have it easy. And I am a software developer, I can't even imagine how someone who has no programming/scripting experience yet could do scripts in FS2 based on what documentation we have.
Guess what my programming experience was before starting with LUA maybe one and a half year ago :rolleyes: Don't know how many LUA functions I had to test with the good old trial and error method to see how their input syntax has to be and what they actually return or (should) do. When a forum search didn't help I had to ask like in the case of evaluateSEXP(). No one without access to and knowledge about FSO code could have figured out how it works.
So if you say
you can edit the function descriptions in lua.cpp so that the game generates more useful instructions automatically.
I start thinking something like: Huh what's this?? Where do I get this .cpp thing and how the heck do I even edit it, not break it and commit changes?
So if I could I would edit the function descriptions with a better description and even example uses for the functions that lack documentation. But as you said - how can I make sure I understand how it really works without checking the actual code and some decent C knowledge?
Complete lack of documentation gets me to another question: What the hell are hook variables, which do exist and what's the difference to normal objects/variables?
-
that was more of a comment for the c coders in here. when somone wants a new function in scripting what you do is get the game source and edit that cpp file and add a function to fetch/write the data from c/lua to lua/c. easiest way is to copy something that does something similar and edit it to operate on the data you need. one of the arguments in this function is, go figure, the documentation that comes out in the scripting.html file. i was basicly stating that when programmers add stuff to it, that they put more than a cryptic line of information into this string. and the same goes for wiki editors who are mostly modders with a minimalist usage of words, or coders without a lot of spare time on their hands.
to be fair scripting.html seems to have pretty good reference info in it. so most of the coders have been making use of the auto-documentation feature there. and through various patches to scripting over the years it seems fairly solid. if your engine is based off of a very old branch then a lot of those scripting functions and documentation fixups just wont be there.
-
I think we should do it this way, it ensures that everybody can help:
We take the scripting.html the E posted on page 1 of this thread (http://blueplanet.fsmods.net/E/scripting.html)
and put it into a wiki article (tbh I never worked with a wiki so I don't know how to create or edit a page, but this is a good reason to learn it I guess)
Then we can edit around in it, discuss, and when we have some descriptions someone (meaning someone who knows how not to break the CPP file) has to just take the stuff and put it into the file.
That's not much work but a huge improvement of the documentation.
Any objections? If not: Someone has to tell me how creating a wiki account works. Now!
-
at least that way you dont have to be a coder to fix documentation shortcomings. so long as our documentation quality is smack dab in between totally vague and outright wankery (like linux man pages).
-
Taking the scripting.html to Wiki and edit it there sounds like a good idea. I guess I should create an account there as well and learn how a wiki works.
to be fair scripting.html seems to have pretty good reference info in it. so most of the coders have been making use of the auto-documentation feature there. and through various patches to scripting over the years it seems fairly solid. if your engine is based off of a very old branch then a lot of those scripting functions and documentation fixups just wont be there.
Without scripting.html it would have been impossible for me to create any useful script and fortunately a lot of functions (especially newer ones or ones that have been edited recently) do have a proper description in there.
-
Ok, then let's do this!
(and hey, Linux man pages are cool!)
Another question related to my scripting problems:
When a ship is destroyed, does it get erased from the Ships[] array or does it stay in? If it is erased: Do the rest of the indices stay the same? Same goes for a ship that is spawned later. Is it in the array all the time (like in the mission file where it has an index already of course) or is mn.Ships[] only an array of ships that are active in the mission at the moment?
I'm asking because I did a loop through the Ships array and use the ship name at the index i for my other array. And somehow my function doesn't work anymore as soon as one ship in the mission is destroyed...
-
It gets removed a bit later or I wouldn't have to do this one:
if (ship:isValid() and (ship:hasShipExploded() == 0)) then
It is still within Ships[] during most of the explosion part.
When it is done exploding, has warped out or is vanished it gets removed from Ships[] and all the other ships may get a different index number. Same may happen when new ships arrive.
So mn.Ships[] is only an array of what is currently active in the mission and every index number up to the number of ships in mission represents one of them. If you need a specific ship save its name rather than its index number and use mn.Ships[shipname].
-
i should also point out that therews
Ok, then let's do this!
(and hey, Linux man pages are cool!)
the idea is cool, the writing talen of those that fill them in, not so much. linux documentation i find is an atrocious read at best.
Another question related to my scripting problems:
When a ship is destroyed, does it get erased from the Ships[] array or does it stay in? If it is erased: Do the rest of the indices stay the same? Same goes for a ship that is spawned later. Is it in the array all the time (like in the mission file where it has an index already of course) or is mn.Ships[] only an array of ships that are active in the mission at the moment?
I'm asking because I did a loop through the Ships array and use the ship name at the index i for my other array. And somehow my function doesn't work anymore as soon as one ship in the mission is destroyed...
use the ship:isValid() function to determine if the handle is still good. i think when a ship is destroyed the handle is marked as invalid. i could be mistaken though. almost all the object handles have this feature so use it when you cant be sure about the validity of something.
-
Ah thanks, that makes it a bit more clear.
...but it seems like I have destroyed my script completely now :D
I'll need some precise help on the following topic: (I guess I didn't get the meta thing completely after all)
I want to create a list (table? array?) of ships that are capable of cloaking. I'll trigger a function that creates that list based on the ship class at the start of the mission.
I also need the information when the ship last cloaked, when it last decloaked, and what its cloaking state is now (visible =0, cloaking=1, cloaked = 2, decloaking=3)
Then I want to loop through those ships every frame to check whether each ship was cloaking for three seconds and set it to cloaked.
The same goes for decloaking. three seconds later it is cloaked.
I had that working already with single variables for each ship (so you could only have 4 cloakable ships you knew the names of) but I just don't get it to work now.
Take your time, I'll go to bed now. It is 01:00h here and my boss wants me to work tomorrow :D
-
you can call ship:getSignature() to get a unique neumeric identifier for that ship. id suggest iterating through the ships. get the signature and then look inside a table to see if it has a key with that value, if it doesnt, then create it (init your list, set default values for sutt, etc) and add it to the table, otherwise pull up the data which was saved on a previous loop. it does help to clean up the table periodically. iterate through it (use a k,v in pairs() loop because the values probibly wont be in sequential order and there will be gaps) look up the objects the table key with mn.getObjectFromSignature(), test if its valid, if it is skip it, otherwise set that table key to nil, this will allow it to be garbage collected. i tend to do this every 10 or so seconds.
-
I think we should do it this way, it ensures that everybody can help:
We take the scripting.html the E posted on page 1 of this thread (http://blueplanet.fsmods.net/E/scripting.html)
and put it into a wiki article (tbh I never worked with a wiki so I don't know how to create or edit a page, but this is a good reason to learn it I guess)
Then we can edit around in it, discuss, and when we have some descriptions someone (meaning someone who knows how not to break the CPP file) has to just take the stuff and put it into the file.
That's not much work but a huge improvement of the documentation.
Any objections? If not: Someone has to tell me how creating a wiki account works. Now!
Go to the Freespace Wiki. Click sign in in the upper right. Create an account. Go to, e.g., the scripting.tbl page (http://www.hard-light.net/wiki/index.php/Scripting.tbl). Click 'edit' at the top of the page. The syntax should be easy enough to learn if you can learn lua. To create a new page, your best bet would be to create a link from an established page like that one to a page that doesn't yet exist, then follow that link.
I'd be very grateful if those in-the-know could expand on the existing hook descriptions and improve upon scripting.html. For example, I see that in January zookeeper listed 'on turret fired' as a 3.6.15 feature, but it shows up in the scripting.html posted above, which identifies itself as produced by a 3.6.13 build. Does that mean the feature exists, but isn't yet ready? Or is the wiki page out of date? Furthermore, while i see a hook variable for the ship as a whole, I'm not clear on whether I can use that to tell which turret was fired. I suppose I could run through the whole ship using hasFired() but I don't know if that would fit for every use case I might come up with.
Anyway, I don't mean to derail Aginor's thread with my questions, I just wanted to give an example of what another scripting noob would find helpful.
-
Thanks for your explanation, Nuke!
it does help to clean up the table periodically. iterate through it (use a k,v in pairs() loop because the values probibly wont be in sequential order and there will be gaps)
Ah! I think that's what I did wrong. At least it would explain the errors.
@Alan Bolte:
Ok, thanks. I think we should call the page "scripting functions" and also redirect to it if someone searches for scripting.html, because the page will basically look like a scripting.html (but hopefully better documented :D )
-
thats why the generic loop format exists. to accomidate the potentially wonkyness that comes with non-sequential use of indices, named keys get iterated through as well. you also can define your own iterator function here to make your loop smarter, but most of the time pairs() just works fine.
-
Ok guys, I just created a page in the wiki. It is called "Scripting functions" and one page already links to it (scripting.tbl)
And before you read it: YES, it looks like crap. And YES, it is only a scripting.html C&P'd into the wiki. And YES, I don't have any clue how work on a wiki normally goes. Free feel to correct me.
EDIT: Also I'm not a design guy. So I would be happy if someone else did that kind of stuff with hierarchy and hints in pretty green boxes and warnings in red and such stuff. Also I think the hierarchy is broken. There are suddenly boxes randomly around stuff in the page. I think it is because of the empty lines or something....
I just wanted to have a point where we could start from. To prove I'm not only talking but actually doing something. Now we can start editing the hell out of this thing.
Here it is:
http://www.hard-light.net/wiki/index.php/Scripting_functions
preparing for edit war / ****storm now :D
-
Ok, sorry for asking again, but I'm stuck somehow. Here's my code:
at mission start a event calls the following function:
p_InitCloakTimers = function(x)
-- set everything to null
naCloakableShips = {} -- table that contains cloakable ships. Index is the Signature, value is the name
naCloakingTimers = {} -- cloaking timer table. Index is the Signature, value the time when the ship cloaked
naDecloakingTimers = {} -- decloaking timer table. Index is the Signature, value the time when the ship decloaked
naCloakStatus = {} -- Status. 0=visible, 1=cloaking, 2=cloaked, 3=decloaking
--loop through ships, get their names and signatures
for i=1, #mn.Ships do
i_ship = mn.Ships[i]
ishipName = tostring(i_ship.Name)
nshipSig = i_ship:getSignature()
-- if they are cloakable (only Strakha for now) set their status and add them to the CloakableShips list
if (i_ship.Class.Name == "Strakha") then
naCloakableShips[nshipSig] = ishipName
naCloakingTimers[nshipSig] = 0
naDecloakingTimers[nshipSig] = 0
naCloakStatus[nshipSig] = 0
end
end
PlayerCloakable = 1 -- I should rename this variable, it has nothing to do with the player
-- HUD color for player cloaking
orgHUDR, orgHUDG, orgHUDB = hu.getHUDGaugeColor(9) -- get HUD colors on mission start
end
The CloakOrder function triggers the cloaking functions based on the first parameter,
the ship name is the second one. Orders 1 and 3 are called by key-pressed events, and they work.
The other two are triggered OnFrame (see code further below)
p_CloakOrder = function(par_order, nship)
destr = mn.evaluateSEXP("(is-destroyed-delay !0! !".. nship .. "!)")
nshipSig = mn.Ships[nship]:getSignature()
if (not destr) then
if (par_order == 1) then
p_Cloaking(nship)
naCloakingTimers[nshipSig] = mn.getMissionTime()
naCloakStatus[nshipSig] = 1
end
if (par_order == 2) then
p_Cloaked(nship)
naCloakStatus[nshipSig] = 2
end
if (par_order == 3) then
p_Decloaking(nship)
naDecloakingTimers[nshipSig] = mn.getMissionTime()
naCloakStatus[nshipSig] = 3
end
if (par_order == 0) then
p_Decloaked(nship)
naCloakStatus[nshipSig] = 0
end
end
return 0
end
The following function should check whether a ship is cloaking or decloaking for 3 seconds,
change the status to cloaked and trigger the cloak functions 2 and 0. The cloak functions themselves work
$On Frame:
[
if PlayerCloakable == 1 then
-- here I loop through my table of cloakable ships to see whether the cloaking or decloaking timers have reached 3 already
for i_shipSig,i_shipName in pairs(naCloakableShips) do
o_ship = mn.getObjectFromSignature(i_shipSig)
if ((i_shipname ~= nil) and (o_ship:isValid()) and (o_ship:hasShipExploded() == 0)) then
if ((mn.getMissionTime() - naCloakingTimers[i_shipSig]) == 3 and naCloakStatus[i_shipSig] == 1) then
p_CloakOrder(2, i_shipName)
elseif ((mn.getMissionTime() - naDecloakingTimers[i_shipSig]) == 3 and naCloakStatus[i_shipSig] == 3) then
p_CloakOrder(0, i_shipName)
else
end
end
end
end
]
I can't find the error. The ships start cloaking when I press the key, and go to decloaking when I press it again,
but they are never cloaked or decloaked. So they newer reach CloakStatus 0 or 2 again.
So I think the first or last block I posted is the problem.
Take your time, I'll work on my campaign missions in the meantime. :)
-
mn.getMissionTime() - naCloakingTimers[i_shipSig]) == 3
Well this one will never get true unless you are really lucky. Mission time is exact down to a lot of decimal places.
Do it like this:
mn.getMissionTime() - naCloakingTimers[i_shipSig]) > 3
and add a true/false variable that is true after the initial cloaking/decloaking and is set false when the second event is activated to prevent repeated activation.
-
Ah, ok. I thought missionTime was like in FRED, an integer value. This part of the code is a remnant of the time when I did the whole thing by events.
The repeated activation should be no problem I think, since the CloakStatus is immediately set by the function p_Cloak order when it is activated.
I'll try it tomorrow, though. Gotta go to bed now.
Thanks for your help!
-
lua doesnt understand integers unfortunately
-
I changed the time thing. But it didn't change the bahaviour. But then I corrected a wrong-case variable name and now it works better.
...better meaning I was back to the situation where everything worked until a ship in the mission is destroyed. As soon as that happens the cloaking was broken.
...and then I realized I am indeed an idiot. Maybe I should read what I write for a change. It happens every time a ship dies. MAYBE it has something to do with what I do OnDeath!
I have a small script that checks OnDeath if it is the player who dies. If he is the script sets the HUD color back to the original one (otherwise it could happen that when a player dies while cloaked the black&white HUD color I use as a cloaking indicator stays.
In that part of the script I used a variable in the wrong IF block. It was set to zero everytime some ship was killed. I corrected it and now the script seems to work :)
I still have a bug though, maybe there is a scripting way to do this:
When the player is using the afterburner and then hits the cloaking button the afterburner sound loops until the player hits the afterburner again. I assume this is a sound thing since there is also a cloaking sound. Maybe they interfere with each other. Does anyone know a solution for that? (except locking the afterburner of course.)