Notifications
Clear all

UncleBob's annoying API questions

Page 1 / 3

UncleBob
(@unclebob)
Master Chief Registered
Joined: 15 years ago
Posts: 185
Topic starter  

My endeavours in mission creation has lead to a few questions, as could be expected. I'm getting a hang of LUA, but it will probably be a few projects until I'll really know what I'm doing, but LUA related questions are not really what I'm going to ask here, there's certainly tons of places better suited for that. None the less, some of my perceived API problems might well be because of Syntax errors or similiar, so please be patient.

Anyways, my scout mission is comming along, the only major thing left to do is for the script to notice that I actually completed the mission. If I could handle that by one of the provided events, that wouldn't be a problem, but it's not that simple. The mission gives you a planet you have to scout, and what I have in mind is something like go into high orbit and complete one orbit, then the computer will give a message that you completed your duty and can get your paycheck at the nearest base. Paying up isn't a problem, since I can handle that very simply by the next OnShipDocked event.

But, how do I check my mission parameters? I see there's functions for checking distances, so that will doubtlessly be helpfull. But I need a loop in which this check can be performed. Is there a standard function that can be embeded into a script that gets called on in every frame? that's how orbiter enables you to keep track of stuff, is there something similiar in Pioneer?

Plus, a minor problem, I have been trying to make sure that the planet one gets to scout is uninhabited. I tried this by doing the following:

Code:
local nearbysystems = Game.system:GetNearbySystems(max_delivery_dist, function (s) return #s:GetStationPaths() > 0 end)
if #nearbysystems == 0 then return end
nearbysystem = nearbysystems[Engine.rand:Integer(1,#nearbysystems)]
local dist = nearbysystem:DistanceTo(Game.system)
local nearbystations = nearbysystem:GetBodyPaths()
local HasPop = 1
while HasPop > 0 do
location = nearbystations[Engine.rand:Integer(1,#nearbystations)]
if location.superType() == STARPORT then
HasPop = 0
end
end

Without a doubt you can see that I'm shamelessly ripping off the code of the delivery mission whereever usefull, I'll do some renaming of variables later on when I know the ropes better. Anyways, the line "if location.superType() == STARPORT then..." is not much apreciated by the interpreter. Probably a Syntax error, but I looked at other loops and if's and function calls in the code, and I don't quite get what's wrong with it. It's probably too C++ish... How would I have to write that statement?


Quote
Brianetta
(@brianetta)
Commander Registered
Joined: 13 years ago
Posts: 863
 

STARPORT needs to be in quotes.

Constants.BodySuperType[5] is the string "STARPORT"; you'll find that all of the constants are made this way.


ReplyQuote
UncleBob
(@unclebob)
Master Chief Registered
Joined: 15 years ago
Posts: 185
Topic starter  

Thanks, it's kind of desorienting without clear datatype declaration (and to think that in the beginning I was so annoyed about C++ pedantic insistance on datatype compatibility... ๐Ÿ˜† )

I still get "unable to resolve method or atribut 'superTypeรƒโ€šร‚ยจ", though.

Any suggestions on the other problem?


ReplyQuote
durandal
(@durandal)
Petty Officer Registered
Joined: 13 years ago
Posts: 44
 

nearbystations are paths because of nearbystations=nearbysystem:GetBodyPaths()

http://eatenbyagrue.org/f/pioneer/coded ... SystemPath

and as you can see that class does not have superType attribute.

To get superType attribute you need to get call

station = location.GetSystemBody()

http://eatenbyagrue.org/f/pioneer/coded ... SystemBody

which as you can see have superType attribute

then you just check if

station.superType == 'STARPORT'

Anyway this whole code is pointless because location and all nearbystations are already 'STARPORT'.

but if you check station.type you will know if station is 'STARPORT_ORBITAL' or 'STARPORT_SURFACE'


ReplyQuote
durandal
(@durandal)
Petty Officer Registered
Joined: 13 years ago
Posts: 44
 

And you really should ask such questions on irc because forum is so sloooow.


ReplyQuote
UncleBob
(@unclebob)
Master Chief Registered
Joined: 15 years ago
Posts: 185
Topic starter  
Quote:
nearbystations are paths because of nearbystations=nearbysystem:GetBodyPaths()

Thanks a lot. As I said, it is somewhat disorienting to not have clear type declarations. Will take some getting used to...

That leaves the problem of checking for my distance to the planet, since I can't use an event call (which currently is the bigger concern; if I can't get that to work, I can pretty much forgett about the scouting mission...)

Quote:
And you really should ask such questions on irc because forum is so sloooow.

To be honest, I never used IRC in my whole life... I'll look into it.


ReplyQuote
robn
 robn
(@robn)
Captain Registered
Joined: 13 years ago
Posts: 1035
 

The data model is a bit confusing the first time. We need to document this better, but here's an attempt.

SystemPath is a simple five-value type that acts as a pointer to a single object anywhere in the galaxy. It has no data or logic of its own, so there's basically no overhead in creating lots of these and passing them around.

To actually work with an object, you use the SystemPath and its GetSystemBody() method to obtain a SystemBody. This is generally an expensive operation as it has to generate the starsystem to determine the body's attributes. There is some stuff in place to try to cache the starsystem data to reduce the overhead, but if you're going to be grabbing lots of SystemBodys from the same system you'd be better to get an explicit reference to the starsystem with GetStarSystem(). Don't worry too much about the overhead though - make something that works first, optimise later.

SystemBodys are good for working with object attributes, choosing destinations, etc, but they are only an abstract representation. The "physical" representation of objects are the Body type and its subclasses. Body objects only exist for the current system, as they're part of the physics simulation and only one starsystem is ever being simulated at any time. Bodies are contained with the Space, and you use methods from that interface to obtain and manipulate handles on them. If you have a path to something in the current system, Space.GetBody(path.bodyIndex) will fetch the corresponding physics body. Another one is Game.player, which always holds the physics body that represents the current player. Many events are passed physics bodies in their arguments, for example, onShipDocked gets passed the Ship that docked and the SpaceStation that it docked with.

Now, the event system. The way it works is that the game generates events for all sorts of things that happen, and pushes them onto a queue of events. You attach a function to the event queue, and that function will be called when the event occurs. The EventQueue docs give details of all the events that are available and the arguments they receive.

Right now your module will receive events for every event of that type that happens, even if its happening to something that you don't care about (eg a ship controlled by another module). In the future there will be a filtering system that lets you select the objects you're interested in and only receive events for them.

So, for checking for distance to a body, you might do something like this (untested):

Code:
-- body 0 is always the primary star, so this will work in any system ๐Ÿ™‚
local s = Space.GetBody(0)
print("distance to star: "..s:DistanceTo(Game.player).." metres)

Hope that helps ๐Ÿ™‚


ReplyQuote
Ziusudra
(@ziusudra)
Senior Chief Registered
Joined: 13 years ago
Posts: 61
 
UncleBob wrote:
But I need a loop in which this check can be performed. Is there a standard function that can be embeded into a script that gets called on in every frame? that's how orbiter enables you to keep track of stuff, is there something similiar in Pioneer?

Timer.CallEvery

However, it might be easier to use EventQueue.onFrameChanged to know when the ship enters and exits the planet's frame.


ReplyQuote
fluffyfreak
(@fluffyfreak)
Captain Registered
Joined: 7 years ago
Posts: 1306
 
durandal wrote:
And you really should ask such questions on irc because forum is so sloooow.

maybe, but IRC disappears after a short while, whilst the forums are always searchable ๐Ÿ˜‰


ReplyQuote
robn
 robn
(@robn)
Captain Registered
Joined: 13 years ago
Posts: 1035
 
Ziusudra wrote:
UncleBob wrote:
But I need a loop in which this check can be performed. Is there a standard function that can be embeded into a script that gets called on in every frame? that's how orbiter enables you to keep track of stuff, is there something similiar in Pioneer?

Timer.CallEvery

However, it might be easier to use EventQueue.onFrameChanged to know when the ship enters and exits the planet's frame.

I'd recommend using onFrameChanged if its enough. Timers are not for the faint of heart, and CallEvery places a lot of stress on the engine - so much so that we're considering removing it.


ReplyQuote
UncleBob
(@unclebob)
Master Chief Registered
Joined: 15 years ago
Posts: 185
Topic starter  

@ robn, thanks a lot for the very detailed explanation!

@Ziusudra, Thanks. I saw the onFrameChanged event, but I think it's not enough. If I get this right, the event is called one time only, when my frame of reference changes to the target body, but that is usuall still a pretty far way off. I would like to be able to track the distance, and trigger "mission accomplished" after a certain time has been spent under a certain distance.

Timer.CallEvery seems like a potential solution, but if its support is uncertain and it eats that much performance I'm very reluctant to use it...

Have you guys ever considered some kind of "OnFrameRefresh"-event that's called once every iteration of the render-loop? Or would that burden the engine too much? (Orbiter supports it, but there you plug in dll-libraries, so I imagine script would be somewhat slower...)


ReplyQuote
robn
 robn
(@robn)
Captain Registered
Joined: 13 years ago
Posts: 1035
 

That's essentially what CallEvery is. It does work, and won't call your script more than once per second, but it has some real problems at higher timeaccels. If you feel inclined you should give it a try and see how it goes. If you manage to produce something compelling with it then that will justify its continued existence (or a mechanism like it).


ReplyQuote
UncleBob
(@unclebob)
Master Chief Registered
Joined: 15 years ago
Posts: 185
Topic starter  

Ok, I'll see how it goes. I've finally figured out my major Syntax problems, so maybe now I can make some progress... ๐Ÿ™‚


ReplyQuote
UncleBob
(@unclebob)
Master Chief Registered
Joined: 15 years ago
Posts: 185
Topic starter  

Hmmm, say... the onFrameChanged event gives me a body back. It toock me a while to understand that that wasn't the body of the new frame, but the body that changed frame (so basically, a ship). It was written quite clearly, but the term "body" is a bit confusing.

Anyways, after figuring that out, I figured out how to get the frame of reference, but what I haven't figured out so far is how to identify the body passed to me by onFrameChange. Since the event gets passed for every ship, I have to somehow make sure that it's the player ship. Otherwise someone else might do the mission for me. How do I go about identifying the body passed on from the event? If I would get a ship back I could identify it, but I have not yet found a way to identify the body object.


ReplyQuote
fluffyfreak
(@fluffyfreak)
Captain Registered
Joined: 7 years ago
Posts: 1306
 

I just wanted to say that it's really cool seeing someone learning all this stuff and that people are so willing to lend their time to help ๐Ÿ™‚

Just about all the contracts and jobs coming up in my field are for Lua these days, it really seems to be taking over games coding so doing this stuff well stand anyone in good stead!


ReplyQuote
Brianetta
(@brianetta)
Commander Registered
Joined: 13 years ago
Posts: 863
 
UncleBob wrote:
I figured out how to get the frame of reference, but what I haven't figured out so far is how to identify the body passed to me by onFrameChange. Since the event gets passed for every ship, I have to somehow make sure that it's the player ship.

You want the IsPlayer() method. All Ship objects support it, and it's for exactly this situation. I've pasted the codedoc for your convenience:

IsPlayer

Determines if the ship is the player ship

Code:
isplayer = ship:IsPlayer()

Returns

isplayer true if the ship is the player, false otherwise

Example

Code:
if Game.player:IsPlayer() then
print("this is the player")
end

ReplyQuote
UncleBob
(@unclebob)
Master Chief Registered
Joined: 15 years ago
Posts: 185
Topic starter  
Quote:
You want the IsPlayer() method. All Ship objects support it, and it's for exactly this situation. I've pasted the codedoc for your convenience:

That's exactly the problem, I don't get a ship object from onFocusChanged. I get a body object, and that has no isPlayer member...


ReplyQuote
Brianetta
(@brianetta)
Commander Registered
Joined: 13 years ago
Posts: 863
 
UncleBob wrote:
Quote:
You want the IsPlayer() method. All Ship objects support it, and it's for exactly this situation. I've pasted the codedoc for your convenience:

That's exactly the problem, I don't get a ship object from onFocusChanged. I get a body object, and that has no isPlayer member...

All ships are bodies, so you could use Object.isa() to determine if it "is a" ship, then ask it if it's a player:

Code:
if body:isa("Ship") and body:isPlayer() then
-- Whatever you do to players when frame changes
end

ReplyQuote
UncleBob
(@unclebob)
Master Chief Registered
Joined: 15 years ago
Posts: 185
Topic starter  

so ship is kind of an inherited object of body, and i can access its members even from the parent object? Probably not, must have something to do with the loose way lua handles types. That's confusing the hell out of me... ๐Ÿ˜•

Anyways, thanks for the help!


ReplyQuote
robn
 robn
(@robn)
Captain Registered
Joined: 13 years ago
Posts: 1035
 
UncleBob wrote:
so ship is kind of an inherited object of body, and i can access its members even from the parent object? Probably not, must have something to do with the loose way lua handles types. That's confusing the hell out of me... ๐Ÿ˜•

Its regular class inheritance, as you'd find in any normal OO language. A subclass is an object that builds on a more generic parent object.

The object hierarchy for Pioneer's Lua engine is currently quite shallow. CargoBody, Planet, Ship, SpaceStation and Star all inherit from Body, and so all of Body's methods and attributes can be used against them. Additionally, Player inherits from Ship, so all of Ship's and Body's methods and attributes can be used on the player object.


ReplyQuote
UncleBob
(@unclebob)
Master Chief Registered
Joined: 15 years ago
Posts: 185
Topic starter  
Quote:
A subclass is an object that builds on a more generic parent object.

Yes, and usually when I declare an inherited class I only have access to the members of its parent class(es), not its child class. Unless I did something wrong in C++ all the time, which wouldn't be surprising either.

Anyways, I've got it to work, allthough I don't quite get the logic behind it yet. Know let's see about that CallEvery...

Uhhh, Bummer! It looks that I don't have access to a planet's attributes (like radius), which I guess I would need to set a sensible distance limit... hmmm. Maybe completing the mission a certain time after entering the frame? Not quite satisfying, but the only sensible way to go unless I know how large the planet in question is. ๐Ÿ™


ReplyQuote
robn
 robn
(@robn)
Captain Registered
Joined: 13 years ago
Posts: 1035
 
UncleBob wrote:
Quote:
A subclass is an object that builds on a more generic parent object.

Yes, and usually when I declare an inherited class I only have access to the members of its parent class(es), not its child class. Unless I did something wrong in C++ all the time, which wouldn't be surprising either.

Sorry, I misunderstood. The way the Lua object stuff works is that each object can be accessed for what it is, not what its type is claimed to be. So if an event or method receives or returns a Body, and a Player is passed instead, then you can call Player methods on it. That's why the "isa" method is important.

Quote:
Uhhh, Bummer! It looks that I don't have access to a planet's attributes (like radius), which I guess I would need to set a sensible distance limit... hmmm. Maybe completing the mission a certain time after entering the frame? Not quite satisfying, but the only sensible way to go unless I know how large the planet in question is. ๐Ÿ™

Those attributes are trivial to expose. We just haven't done it yet because much of the Lua API is being extended on demand. Please open a feature request and we'll get right one it ๐Ÿ™‚


ReplyQuote
UncleBob
(@unclebob)
Master Chief Registered
Joined: 15 years ago
Posts: 185
Topic starter  

Ah, silly me. in onFrameChange(body), body is only the name of the variable, not the expected class. So basically, that function could pass me ANYTHING back.

Quote:
Those attributes are trivial to expose. We just haven't done it yet because much of the Lua API is being extended on demand. Please open a feature request and we'll get right one it

Great! Do I have to do this via GitHub, or right here on the forum?


ReplyQuote
robn
 robn
(@robn)
Captain Registered
Joined: 13 years ago
Posts: 1035
 

Github. I have a terrible memory - I may not remember this thread in the morning. If its in the issue tracker then it can't be ignored ๐Ÿ™‚


ReplyQuote
UncleBob
(@unclebob)
Master Chief Registered
Joined: 15 years ago
Posts: 185
Topic starter  

added to Github, but I seem incapable of assigning a type (feature request, bug, etc).


ReplyQuote
Page 1 / 3