Page 1 of 4

Adding ships - I don’t get it.

Posted: Sun Apr 01, 2007 6:36 pm
by JensAyton
Looking at the wiki page on the JavaScript object model, I see we have:

Code: Select all

void addShips(role, number)
void addSystemShips(role, number, position)
void addShipsAt(role, number, coordScheme, x, y, z)
void addShipsAtPrecisely(role, number, coordScheme, x, y, z)
void addShipsWithinRadius(role, number, coordScheme, x, y, z, radius)
void spawn(role, number)
void spawnShip(shipdataKey)
All of these correspond directly to an old scripting model method, and most were introduced to address some shortcoming in one of the others… but what do they do? What are the differences between them?

I hope we can work out a way to express all their functionality in one or a few methods, but this requires knowing what they all do. Also, what don’t they do? What annoying limitations do they have?

In order not to “waste” a useful name, I’ve prepended “legacy_” to all their names for the upcoming 1.68 release. Hopefully we can have a less confusing alternative by 1.69.

Posted: Sun Apr 01, 2007 8:07 pm
by Flying_Circus
I take it they don't inherit from each other - or, indeed, some MotherOfAllSpawns class?

Re: Adding ships - I don’t get it.

Posted: Sun Apr 01, 2007 10:08 pm
by Charlie
Ahruman wrote:

Code: Select all

void addShips(role, number)
void addSystemShips(role, number, position)
void addShipsAt(role, number, coordScheme, x, y, z)
void addShipsAtPrecisely(role, number, coordScheme, x, y, z)
void addShipsWithinRadius(role, number, coordScheme, x, y, z, radius)
void spawn(role, number)
void spawnShip(shipdataKey)
What annoying limitations do they have?
FWIW, for me the 'annoying limitations' are that I just don't seem to get the coordinates system at all!
I read the Wiki, I understand...
When I try to place an entity, my brain flips to: 'It's in orbit about one of the large bodies in the system' - ERROR, does not compute, kernel pannic!

Not quite what you asked ( and possibly only me asking for this ) but if you're looking to do an updated placing command I'd do something like:

Code: Select all

add: <entity / role / string> <body / place / entity> <distance> <number> <fixed> | <angle> <declanation> <withinradius> <orbiting>

Useable in: shipdata, script, AI.plists, etc so you can place anything near anything under whatever circumstances...

<entity / role / string>: What your placing: String being useful for on-the-fly events in scripts.
<body / place / entity>: What your coodinates are centred on...
<distance>: How far...
<number>: How many? Can accept a string variable.
<fixed>: Y / N Does it hold it's relative distance to the <body / place / entity> regardless of what that is doing?
| Optional stuff:
<angle>: 0-359deg, defaults to 0, 0 = Line drawn between the <body / place / entity> & the next largest body. ie: Witchpoint->Plannet->Star would be the 0deg lines. Entities ( inc moons ) would  default to Z axis.
<declanation>: 0-359deg, defaults to 0. Angle to the 'galactic plane' so you're not stuck placing everything in 2D... Entities ( inc moons ) would default to X axis.
<withinradius>: Defaults to 0. Object is placed at a random position within a sphere of this size centred on the previous coordinates.
<orbiting>: Defaults to 0. 0 = No. +ve / -ve numbers dictate speed & direction...
None of this assumes any attempt at making a 'realistic star-system'. Not needed in my opinion. It's just a placing system.

Something like that would about cover it ( I think ) & would make MUCH more sence to me. ( if nobody else! ) An added bonus would be the extra flexability of use:- Imagine the ships / fleets / stations / systems you could create with this command!! :shock: :D

Rant over...
Back on topic...
If you don't have any other takers: I'll post an explantion of what each of the different commands in your question do - but can't give a reliable answer to the coordinates bit! :oops:

Posted: Sun Apr 01, 2007 10:55 pm
by JensAyton
I don’t think I understand you. Some examples might help. Something looking like JavaScript syntax would be a bonus, but not strictly required. :-)

Re: Adding ships - I don’t get it.

Posted: Mon Apr 02, 2007 8:47 am
by Selezen
void addShips(role, number)
The standard method - add ships in a random place in the system.

void addSystemShips(role, number, position)
Add specified ships between the witchpoint and the planet (i.e. on the normal flight route).

void addShipsAt(role, number, coordScheme, x, y, z)
At ships within 500 units of the point specified (prevents entity collisions)

void addShipsAtPrecisely(role, number, coordScheme, x, y, z)
Add ships at the precise co-ordinates (i.e. no buffer zone) - only really should be used for single entities since multiple entities may collide.

void addShipsWithinRadius(role, number, coordScheme, x, y, z, radius)
I think that one speaks for itself.

void spawn(role, number)
void spawnShip(shipdataKey)
Not sure on these ones.

Hope that helps some.

Posted: Mon Apr 02, 2007 10:19 am
by Arexack_Heretic
the spawn methods are called generally by another object in its script_actions, setup_actions, death_actions or AI.

They add the spawn near the location of the target of the method.

Alternatively it can also be used to place objects from by script with specific facings as described in the spawned-object's shipdata.
(See LordOfTheRings_OXP by Giles for example)

I am not sure about what is inherited from the entity that is the subject of the method.
The coordinates are, maybe the movement vector too.
However in my experience, when an attacking ship spawns missiles, they do not automatically attack the PrimaryAggressor or the spawner's active target.

spawnShip functions the same, but instead of choosing the spawn by role it looks for the object's unique shipdata key.

---
It would be good to combine the addShips and spawn methods into one method. For example by defining a spawn in a addShipmethod by coordsystem 'O'.
This would have the added benefit of including radius etc to the spawn method.

But that is probably too precice, too old method and anyhow possibly already what you are planning for Java-scripting.
;)

Re: Adding ships - I don’t get it.

Posted: Mon Apr 02, 2007 11:18 am
by Commander McLane
I don't think Selezen has got them all right. Cf wiki (and my experiences) it is like this:

1. void addShips(role, number): adds the specified number of ships with the specified role somewhere close to (but not exactly at) the witchpoint. (In which radius I can't say, and that goes for the following methods as well.)

2. void addSystemShips(role, number, position): adds the specified number of ships with the specified role somewhere close to a point on the line between witchpoint and main station. 0.0 means at the witchpoint, 1.0 means at the station. Position can be any fraction between those. As far as I can see addShips(role, number) acts as one specific case of addSystemShips(role, number, position), namely addSystemShips(role, number, 0.0). So addShips doesn't need to be used at all.

3. void addShipsAt(role, number, coordScheme, x, y, z): adds the specified number of ships with the specified role somewhere close to (but not exactly at) the position defined by the coordScheme and the coords. Obviously this is another piece of cake as the former two, because unless those two--which are confined to the witchpoint-station axis--addShipsAt can add ships anywhere in the system. But again you can see the former two as a specific case of addShipsAt and therefore superfluous, because addSystemShips(role, number, 0.0..1.0) does the same as addShipsAt(role, number, wpu, 0, 0, 0.0..1.0).

4. void addShipsAtPrecisely(role, number, coordScheme, x, y, z): adds the specified number of ships with the specified role as close as possible to the position defined by the coordScheme and the coords. "As close as possible" is just limited by the mathematical capabilities of the engine, I guess. Now for the practical use of addShipsAt and addShipsAtPrecisely: With addShipsAt and a number > 1 you can add a small cluster of objects with the same role close to each other. With addShipsAtPrecisely however any number > 1 obviously is impossible, as adding several entities at exactly the same coordinates makes no sense, except for blowing these entities up immediatly, because of them colliding. Usually however even addShipsAt will be used with a very small number only, in order to avoid a collision of the added entities.

5. void addShipsWithinRadius(role, number, coordScheme, x, y, z, radius): adds the specified number of ships with the specified role somewhere in a radius around a point close to (but not exactly at) the position defined by the coordScheme and the coords (is that understandable? I'm trying to keep the definitions as similar as possible). So AFAIK in terms of precision it is like addShipsAt, not like addShipsAtPrecisely. But this time it gives the opportunity to produce bigger (custom-sized) clusters of objects with the same role. The most obvious and usual use is for creating asteroid fields, where you want to have many objects with role "asteroid" close to each other.

6. void spawn(role, number): adds the specified number of ships with the specified role as close as possible to the calling entity. Its main use (and the only one that really makes sense) is in the death_actions-part of shipdata. Via this method an exploding entity can create something else that takes its location, e.g. a wreck. So you could script a station that can be blown up, spawning a clone of itself with a texture that has it half destroyed, only the skeleton left. The player would see the station exploding, and after the explosion only the skeleton of the station would still be visible. The same goes for any entity. I assume the engine uses this method to create all the rubble like the short-lived "wreckage" after an entity blows up. The basic difference between this method and the former ones (and the main advantage of this one) is that it is the only method available that adds an entity relative to another entity (instead of relative to a coord system). Its limitation is that it adds the new entity very close to the calling one, so it only makes sense when the calling entity disappears. A useful new method would be adding or spawning entities somewhere close to (but not exactly at) the location of the calling entity.

7. void spawnShip(shipdataKey): adds one ship (instead of a number) with the specific shipdataKey (instead of role) at a point that has to be specified in the shipdata-entry of this entity. The code for that is:

Code: Select all

<key>spawn</key><!-- data for when ship is spawned --> 
<dict> 
<key>position</key><!-- optional, sets the spawn position --> 
<string>psm 0 0 200000</string> 
<key>facing_position</key><!-- optional, sets the direction faced --> 
<string>psm 0 0 0</string> 
<key>spawn_actions</key><!-- optional, works like launch_actions --> 
<array/><!-- no actions (empty array) for now --> 
</dict> 
Obviously this method is different to all the others in (a) that it can create only one entity and (b) that it does not call this entity by a role, but by the specific shipdataKey (is that used anywhere else in calling something?). I am not sure why Giles made it that way. Perhaps because the spawn-entry in shipdata is needed, so using spawnShip with one of the standard roles wouldn't do anything, because the standard ships have no spawn-entry. For using this method of adding a ship you always have to create a new shipdata-entry first, specifying the coords you want to use. The advantage of this method above all the others is that you can specify a facing direction other than the default or a random one for the entity you want to create. It is the only method that allows for this.

-----

The bottom line is:

a) 1. and 2. can easily be deprecated, because they are only special cases of 3.

b) I am not sure how useful the differentiation between 3., 4. and 5. really is. 3. seems to be the same as--or at least very similar to--5. with a small radius. So those two could be combined into one method.

c) 6. has its very own meaning and use, so it shouldn't be abandoned. Instead it could be differentiated according to 3. and 4. (or better 4. and 5.). So what is now spawn could become spawnAtPrecisely, while another one could be added along the lines drawn above, adding entities close to, but not exactly at the location of the caller.

d) 7. is even now working somehow out of the system. IMO it should be deprecated and substituted by the spawn-entry (containing facing_position) only. So no calling by shipdataKey any more. It is as easy to give my entity a unique role and a spawn-entry. If I then call it by its role through one of the remaining methods it would be added according to the coords in this method, facing the direction specified in the spawn-entry.

e) Or if facing_position could become one of the usual shipdata-keys there would be no need for this method at all anymore.

-----

So there would only four methods be left:

1) void addShipsAtPrecisely(role, number, coordScheme, x, y, z): adding an entity at a precise point according to a coordScheme

2) void addShipsWithinRadius(role, number, coordScheme, x, y, z, radius): adding a number of entities close to a point acording to a coordScheme

3) void spawnAtPrecisely(role, number): adding an entity at the precise location of the calling entity (usually replacing the caller)

4) void spawnWithinRadius(role, number, radius): adding a number of entities close to the calling entity

-----

And to simplify it even further: in the "AtPrecisely"-variants we don't need a number, it should by default be 1. So we are left with:
  • void addShipsAtPrecisely(role, coordScheme, x, y, z)
    void addShipsWithinRadius(role, number, coordScheme, x, y, z, radius)
    void spawnAtPrecisely(role)
    void spawnWithinRadius(role, number, radius)
-----

And, yes, before you mention it: All of that is formulated along the old scripting model, but I have not the slightest idea about javascript, and I'm sure you can easily translate it into a fitting syntax for javascript.

And I hope this was helpful. :)

Posted: Mon Apr 02, 2007 2:58 pm
by JensAyton
But that was JavaScript syntax, McLane. :-)

However, I think I’ll change it around a bit. It looks like addShipsAt: and addShipsAtPrecisely: can be considered special cases of addShipsWithinRadius:. So we get:

Code: Select all

addShips(role, number, coordScheme, x, y, z [, radius]);
addShip(role, coordScheme, x, y, z [, radius]); /* Convenience for a single ship */
The square brackets indicate that radius is optional; if it’s not provided, some default (say, 500 metres) is be used.

addShips: is a special case of addSystemShips(), which can generalize to:

Code: Select all

addShipsBetween(role, count, startPoint, endPoint [, minStart [, maxStart [, radius]]]);
addShipBetween(role, startPoint, endPoint [, minStart [, maxStart [, radius]]]);
StartPoint and endPoint would be names of points, such as "station" and "witchpoint". When the JS object model is suitably developed, it will be possible to pass entities here instead of names. MinStart and maxStart specify a range in which generation is allowable. If maxStart is excluded, it is the same as minStart. If minStart is also excluded, 0.5 is used. Thus:

Code: Select all

system.addShipsBetween("pirate", 4, "station", "witchpoint", 0.0, 1.0, 2000); // Anywhere along the main route, widely dispersed
system.addShipsBetween("pirate", 3, "station", "witchpoint", 0.33); // One third of the way from the station
system.addShipBetween("pirate", "station", "witchpoint"); // One ship halfway between station and witchpoint
For clarity, the first example would work as follows: choose a random number between 0.0 and 1.0; find the corresponding point along the station-witchpoint line; add a spherical cluster of ships with radius 2000. To add four ships each at a random position, make four separate calls.

For spawning, I’m not sure yet, except to provide a radius parameter.

Anyone see any problems with this? At any rate, it won’t be in 1.68, so there’s time for refinement.

Posted: Mon Apr 02, 2007 3:15 pm
by JensAyton
Come to think of it, I don’t think a spawnShip: equivalent will be needed. The spawned ship’s script will be able to handle set-up.

Posted: Mon Apr 02, 2007 3:30 pm
by reills
Not being any sort of a programmer I see the methods reduced to two:

void addShips(role, number, coordScheme, x, y, z, radius)
void spawn(role, radius)

Make the radius a sufficently small number, and you've covered the other two.

Posted: Mon Apr 02, 2007 4:06 pm
by JensAyton
Well, addShipsBetween() could be implemented in JavaScript in terms of addShips(), but it’d require a way to get the locations of various objects… then again, for the things that you can use in a coordScheme, that’s already known. It was my intention that addShipsBetween() would be able to work with any entites, but then, it should be possible to get the position of any entity and do the work yourself.

Ideally, addShips() would return a list of the objects created, in which case you’d be able to send messages to their script. If your ship’s script, upon death, could do:

Code: Select all

var spawnedShips = system.addShips("spawnedThing", 1, "global", myShip.position);
spawnedShips[0].script.youJustGotSpawnedBy(myShip);
you probably wouldn’t need a separate spawn. Thus, we’re down to addShips() as a single primitive method, with the option of various convenience wrappers based on it. (It may be possible to do away with the .script bit, and just forward messages the game doesn’t handle to the entity’s script.)

This snippet also suggests the ability to get an object’s position (as a vector) and use that instead of an x, y, z triplet. This definitely should happen. It also suggests a "global" co-ordinate space, which’d be the space used internally by Oolite and by all vectors.

In fact, it would probably make more sense for addShips() to take a global-space vector all the time, and provide functions to convert from the traditional scripting co-ordinate spaces to vectors. Thus, to simulate addSystemShips: and addSystemShipsAtPrecisely:, you might have:

Code: Select all

var where = PointBetween(system.witchPoint.position, system.mainStation.position, 0.9);
system.addShips("rickshaw", 2, where);
where = ConvertCoordinates(system.mainPlanet, system.witchPoint, "meters", -35626, 5903, 360622);
system.addShips("xyzstat", 1, where, 0);
…and on further consideration, the methods could be smart enough that when you pass an entity instead of a vector, it uses the entity’s position, so this would be equivalent:

Code: Select all

var where = PointBetween(system.witchPoint, system.mainStation, 0.9);
system.addShips("rickshaw", 2, where);
The ConvertCoordinates() function would technically be redundant, since you could achieve the same effect by doing the maths yourself:

Code: Select all

where = system.mainPlanet.position * -35626 + system.witchPoint.position * 360622;
where.y += 5903;
system.addShip("xyzstat", where, 0);
…but I think the function may be useful. :-)

Posted: Mon Apr 02, 2007 4:08 pm
by Arexack_Heretic
If you are going to do away with spawn, don't forget to add a coordinatesceme centered on the script.target.
(AFAIK orientation is inherited from the script.target or defined in the shipdata.)

Posted: Mon Apr 02, 2007 4:38 pm
by JensAyton
I’m essentially deprecating the coordinate schemes. Instead, the equivalent of spawing will be:
  • Create an entity at your current position.
  • Set up the spawned ship. This setup can copy anything from the parent it wants – orientation, target, velocity.
Going back to the snippet I gave, cleaned up a bit:

Code: Select all

var spawnedShip = system.addShip("spawnedThing", "global", myShip.position); 
spawnedShip.youJustGotSpawnedBy(myShip);
The first line creates a new ship at the same position as “myShip”, which is intended to be the ship that the script is attached to. (I’ll have to work this one out. It’ll probably end up being “this”, meaning that the ship and the script are considered one and the same.)
The second line sends a message to the newly-created ship’s script, telling it to set itself up, and passing the parent (the dying ship) as a parameter. The spawned ship’s script might have:

Code: Select all

this.youJustGotSpawnedBy = function(parentShip)
{
    this.orientation = parentShip.orientation;
    this.velocity = parentShip.velocity;
    this.target = parentShip.target;
}
Equivalently, but arguably less elegantly, the dying ship could do the setup:

Code: Select all

var spawnedShip = system.addShip("spawnedThing", "global", this.position); 
spawnedShip.orientation = this.orientation;
spawnedShip.velocity = this.velocity;
spawnedShip.target = this.target;
(The this. bits aren’t strictly needed.)

This implies that scripts will be able to bypass the physics simulation to set things like orientation, velocity, even position of their ship – and other ships – directly. This certainly won’t be reccomended as a general method, but it will provide a great deal of flexibility for scripting specialized weirdness.

Posted: Mon Apr 02, 2007 5:15 pm
by Arexack_Heretic
sounds good. :D


Anyone know some good 'beginners guide to Java' or 'Java for dummies' links to post in the Expansion forum?


Are you a strict changelog keeper Ahruman?
If so, you may want to post it on the wiki, to function as the base of wiki-documentation for J-Oolite.

Posted: Mon Apr 02, 2007 5:28 pm
by reills
Java, or Javascript? Two completely different languages. Javascript was originally called Livescript, but was renamed to catch onto some of the hype when Java was new.