Jump to content

[Tutorial] The Entity():addTurret Function


BAMcSH_

Recommended Posts

Could be a tutorial or just more of a knowledge dump.

 

This function caught my eye as something that would be useful, and I've worked with it extensively the past day or so, so I thought I would share my findings with the community and give a small refresher/tutorial about matrix transformations in regards to this operation. If you wish to only know how to use the function, there's a specific example of what is likely its most usable functionality at the bottom.

 

I am going to assume that you know what a vector is, and to understand these explanations you may need some knowledge of linear algebra. If you don't know these things it will be rather difficult to understand how this function operates.

 

First, some pre-emptive facts about the Matrix object:

 

It contains 6 fields: right, up, look, translation, pos, and position.

Right, up, and look are all unit vectors for those corresponding rotational directions. In Avorion it seems like the 3-dimensional plane maps right the x value, up the y value, and z the forward value (look value). A demonstration of this is seen and explained in the first screenshot.

 

Translation, pos, and position all get changed whenever any of the properties are modified, and they generally remain identical. I suspect that they are repeated to allow for matrix operations.

 

The transformNormal and transformCoord functions perform a matrix multiplication between a 3x3 section of the matrix object and the passed vector. The first operates on the rotational unit vectors, the second on the positional coordinates. I have not tested the functionality of the Matrix:transform(vec3) function, and it shows up absolutely nowhere in the base scripts for the game.

 

After some initial attempts where I provided the values of an original, already place turret, it first became clear that the function was using the passed position value from a matrix in a relative way rather than an absolute way, likely because the entity's position is being updated every frame by an offset to the ship's position, since turrets are locked to the ship.

 

I proceeded to perform this test, which proved quite revealing:

 

FwlDArA.png

 

The rotation vectors of the generated turret's matrix are directly proportional to the ship's when the passed unit vectors are the base vectors for right, up, and forward (look). This suggests that the rotation of the turret is also determined in relation to the ship's every frame.

 

Further, note that the magnitude of the passed positional offset is maintained in the magnitude of the offset of the generated turret, even though the new offset is not the same.

 

Finally, note that the new positional offset is equivalent to the magnitude of the passed positional offset multiplied by the magnitude of the ship's "right" unit vector. This provides us the knowledge that the offset is determined by applying the magnitude of the passed position's x, y and z to the directional unit vectors right, up, and forward of the ship in order to determine the generated turret's position.

 

Now that these facts are observed, they meet logical expectations for how a turret's position would need to be determined every frame, so it would seem the mystery is solved. Now all that's left is some math, but first some confirmation on our observations regarding the rotation vectors:

 

0FMHGDi.png

 

We can observe that abnormal rotation values had no effect on the magnitude of the positional offset. We can also see from the passed right rotational value that given rotational values are multiplied to the ship's. The passed look is the unit vector of the passed up, and indeed, the produced vectors for up and look are different lengths in the exact same direction.

 

Generated rotation vectors are seen to be directly proportional to the passed vectors multiplied by the Ship's without any extra shenanigans.

 

Specifically, the x value of the generated turret's right vector is determined by multiplying the passed right vector's x, y, and z values to the ship's right.x, up.x, and look.x values and then adding them all together. This process may sound familiar to you: it's matrix multiplication.

 

For the uninitiated, the product of two matrices A * B is equivalent to the sum of the products of every row of A and every column of B. The resulting generated values of the turret's rotations are the product of the passed vectors as rows of the first matrix A and the ship's vectors as rows in the second matrix B.

 

This also reveals that the position vector is repeated 3 times to allow for a matrix multiplication to determine the new position. The generated turret's position value is the result of a matrix multiplication between the passed translation, pos, and position vectors and the ship's right, up, and look vectors to obtain an offset, which is added to the ship's position to acquire the turret's final position.

 

Wikipedia gives very proficient explanations of matrix multiplication as well as matrix transformations, if you're looking to understand them. This isn't exactly required to make use of this function however.

 

Now that we understand exactly where the produced values are coming from, lets examine a potential use of this function: Replacing an existing turret with a new turret. This is a helpful process for a couple reasons, one of which being that modifications to a turret's template do not effect the existing turret, and in order to instantiate the change we must remove that turret and respawn it.

 

So lets develop a function for this process, along with how we can use some matrix operations to make it simpler on ourselves.

 

Given an existing turret, we know values for its rotation and position from its own position matrix, since it is an entity. These values, however, are absolute values that are derived from the ship it is attached to each frame in the engine. If we want to find the correct matrix values to pass to the addTurret function, we have to perform a division.

 

Matrix division, if I'm being strictly correct, does not exist. It is possible however to emulate using a matrix inverse. Thankfully, Avorion's matrix object provides this operation to us, so I don't need to explain it here.

 

We can determine that, since the generated turret position is the result of A x B where A and B are the right, up, and look vectors of the passed matrix and the ship's matrix respectively, if we multiply an existing turret's matrix by the inverse of the ship's matrix, we obtain a matrix that will reproduce the existing turret's values when passed to the addTurret function. This results in an incredibly simple sub-operation to get what we want:

 

function swapTurrets(ship, oldTurret, newTurret)
     ship:addTurret(CreateTemplateFromTurret(newTurret), oldTurret.position * ship.position:getInverse(), oldTurret:getAttachedBlockIndex())
end

 

You can also remove the old turret from the ship by calling the Sector():deleteEntity function, quite pain-free.

 

Now, most of this post has been discussing the matrix variable of the input, but there are two others with simpler explanations. The first input is the turret template you want to instantiate on the ship. As seen in the code snippet, you can generate this from an existing turret via the global function CreateTemplateFromTurret, but there are other ways to come by it. I'll leave those to you.

 

The third input of the function determines the block index the turret is attached to. It does not need to be in physical contact or even anywhere remotely close to this block. In fact, all this seems to do is determine when the turret is destroyed. Since turrets themselves do not take damage and have no durability, a turret is only destroyed when the block specified by this index is destroyed.

 

I believe that concludes this little tutorial, so allow me to sum up my findings in a littler TL;DR:

 

  • The addTurret function performs a matrix multiplication between the passed matrix and the ship's position matrix to determine the position of the new turret, requiring the position to be given as an offset from the ship's.
  • Retrieving the position matrix property from a turret entity will give you the absolute values of its position and rotations, instead of the relative values required by the addTurret function.
  • The provided block index does not effect the turret's position, and only causes the turret to be destroyed when the block is destroyed.
  • Using the multiplication operator on two matrices causes two separate 3x3 multiplication operations between the right, up, and look values as well as the translation, pos, and position values of the matrices.

 

 

 

 

 

 

 

 

 

Link to comment
Share on other sites

Well, the old turret will still be there if you don't delete it via the function from Sector, but using that command doesn't cause floating loot, which is nice. My first attempt at replacing a turret involved temporarily deleting the block it was attached to and then quickly grabbing and deleting the resulting loot object, and that was uhh, less than ideal.

 

EDIT: Oh, but also, you don't have to delete the old turret until after the new one is added. In fact, you can stack however many turrets you want in the same spot, their collisions don't matter.

Link to comment
Share on other sites

Well, the old turret will still be there if you don't delete it via the function from Sector, but using that command doesn't cause floating loot, which is nice. My first attempt at replacing a turret involved temporarily deleting the block it was attached to and then quickly grabbing and deleting the resulting loot object, and that was uhh, less than ideal.

 

EDIT: Oh, but also, you don't have to delete the old turret until after the new one is added. In fact, you can stack however many turrets you want in the same spot, their collisions don't matter.

On that topic, have you figured out if you can grab a turret when its removed from the ship (or shot off)? I'd like to start making weapon upgrade Systems, but that's a serious requirement to prevent it from being abused.

Link to comment
Share on other sites

I didn't get around to testing it, but a strategy I considered was using Entity():isCollectable to find loot that was freshly dropped by the player, who wouldn't be able to pick it up for a few seconds. Since it's possible to determine when turrets are destroyed via callbacks, as well as when the player's own ship's plan is modified (blocks destroyed), I feel like that route could bear fruit for you.

 

I'm currently testing the viability of automatically placing/resetting turrets when the player enters the build menu to prevent duping via the script I'm working on, so I'll post later with how that goes.

Link to comment
Share on other sites

I didn't get around to testing it, but a strategy I considered was using Entity():isCollectable to find loot that was freshly dropped by the player, who wouldn't be able to pick it up for a few seconds. Since it's possible to determine when turrets are destroyed via callbacks, as well as when the player's own ship's plan is modified (blocks destroyed), I feel like that route could bear fruit for you.

 

I'm currently testing the viability of automatically placing/resetting turrets when the player enters the build menu to prevent duping via the script I'm working on, so I'll post later with how that goes.

 

I looked into blockplan, but I believe that's only for blocks - turrets don't count, as they're not part of the blockplan AFAIK.

 

Now if there's a way to detect when the player goes into the build menu, well, that works fine :)

Link to comment
Share on other sites

You can't detect the turret as a block, but you can determine the index of the block the turret is attached to via getAttachedBlockIndex called on the turret, and when that block index is destroyed on the ship, the turret is ejected into space.

 

Yeah, my setValue on the ship seems to be getting eaten though, and invoking the script doesn't preserve local values. I'll probably have to jank this a bit.

Link to comment
Share on other sites

You can't detect the turret as a block, but you can determine the index of the block the turret is attached to via getAttachedBlockIndex called on the turret, and when that block index is destroyed on the ship, the turret is ejected into space.

 

Yeah, my setValue on the ship seems to be getting eaten though, and invoking the script doesn't preserve local values. I'll probably have to jank this a bit.

 

Something a person in discord brought up also, is maybe modding all turrets the player has, and then looking for a way to block out mailing, etc. Didn't see anything related to mailing though.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...