Changeset View
Standalone View
binaries/data/mods/public/simulation/components/UnitMotionFlying.js
// (A serious implementation of this might want to use C++ instead of JS | // (A serious implementation of this might want to use C++ instead of JS | ||||
// for performance; this is just for fun.) | // for performance; this is just for fun.) | ||||
const SHORT_FINAL = 2.5; | const SHORT_FINAL = 2.5; | ||||
function UnitMotionFlying() {} | function UnitMotionFlying() {} | ||||
UnitMotionFlying.prototype.Schema = | UnitMotionFlying.prototype.Schema = | ||||
"<element name='MaxSpeed'>" + | "<element name='MaxSpeed'>" + | ||||
"<ref name='nonNegativeDecimal'/>" + | "<ref name='nonNegativeDecimal'/>" + | ||||
"</element>" + | "</element>" + | ||||
"<element name='TakeoffSpeed'>" + | "<element name='TakeoffSpeed'>" + | ||||
"<ref name='nonNegativeDecimal'/>" + | "<ref name='nonNegativeDecimal'/>" + | ||||
"</element>" + | "</element>" + | ||||
"<optional>" + | |||||
"<element name='StationaryDistance' a:help='Allows the object to be stationary when reaching a target. Value defines the maximum distance at which a target is considered reached.'>" + | |||||
Silier: I would say by the name it is boolean value no range value | |||||
Done Inline ActionsYeah at first I wanted it to be a lone flag any idea for the name? Stan: Yeah at first I wanted it to be a lone flag any idea for the name? | |||||
Done Inline Actionsmaybe stationaryRange, stationaryDistance Silier: maybe stationaryRange, stationaryDistance | |||||
Done Inline ActionsIn English “stationary” has a single n. Also don't forget to end sentences with a full stop. Nescio: In English “stationary” has a single n. Also don't forget to end sentences with a full stop. | |||||
"<ref name='positiveDecimal'/>" + | |||||
"</element>" + | |||||
"</optional>" + | |||||
"<element name='LandingSpeed'>" + | "<element name='LandingSpeed'>" + | ||||
"<ref name='nonNegativeDecimal'/>" + | "<ref name='nonNegativeDecimal'/>" + | ||||
"</element>" + | "</element>" + | ||||
"<element name='AccelRate'>" + | "<element name='AccelRate'>" + | ||||
"<ref name='nonNegativeDecimal'/>" + | "<ref name='nonNegativeDecimal'/>" + | ||||
"</element>" + | "</element>" + | ||||
"<element name='SlowingRate'>" + | "<element name='SlowingRate'>" + | ||||
"<ref name='nonNegativeDecimal'/>" + | "<ref name='nonNegativeDecimal'/>" + | ||||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | UnitMotionFlying.prototype.OnUpdate = function(msg) | ||||
var cmpPosition = Engine.QueryInterface(this.entity, IID_Position); | var cmpPosition = Engine.QueryInterface(this.entity, IID_Position); | ||||
var pos = cmpPosition.GetPosition(); | var pos = cmpPosition.GetPosition(); | ||||
var angle = cmpPosition.GetRotation().y; | var angle = cmpPosition.GetRotation().y; | ||||
var cmpTerrain = Engine.QueryInterface(SYSTEM_ENTITY, IID_Terrain); | var cmpTerrain = Engine.QueryInterface(SYSTEM_ENTITY, IID_Terrain); | ||||
var cmpWaterManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_WaterManager); | var cmpWaterManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_WaterManager); | ||||
var ground = Math.max(cmpTerrain.GetGroundLevel(pos.x, pos.z), cmpWaterManager.GetWaterLevel(pos.x, pos.z)); | var ground = Math.max(cmpTerrain.GetGroundLevel(pos.x, pos.z), cmpWaterManager.GetWaterLevel(pos.x, pos.z)); | ||||
var newangle = angle; | var newangle = angle; | ||||
var canTurn = true; | var canTurn = true; | ||||
let distanceToTargetSquared = Math.euclidDistance2DSquared(pos.x, pos.z, this.targetX, this.targetZ); | |||||
if (this.landing) | if (this.landing) | ||||
{ | { | ||||
if (this.speed > 0 && this.onGround) | if (this.speed > 0 && this.onGround) | ||||
{ | { | ||||
if (pos.y <= cmpWaterManager.GetWaterLevel(pos.x, pos.z) && this.template.DiesInWater == "true") | if (pos.y <= cmpWaterManager.GetWaterLevel(pos.x, pos.z) && this.template.DiesInWater == "true") | ||||
this.waterDeath = true; | this.waterDeath = true; | ||||
this.pitch = 0; | this.pitch = 0; | ||||
// Deaccelerate forwards...at a very reduced pace. | // Deaccelerate forwards...at a very reduced pace. | ||||
Show All 29 Lines | else if (this.speed == 0 && this.onGround) | ||||
var mapRadius = terrainSize/2; | var mapRadius = terrainSize/2; | ||||
var x = pos.x - mapRadius; | var x = pos.x - mapRadius; | ||||
var z = pos.z - mapRadius; | var z = pos.z - mapRadius; | ||||
var div = (mapRadius - 12) / Math.sqrt(x*x + z*z); | var div = (mapRadius - 12) / Math.sqrt(x*x + z*z); | ||||
if (div < 1) | if (div < 1) | ||||
{ | { | ||||
pos.x = mapRadius + x*div; | pos.x = mapRadius + x*div; | ||||
pos.z = mapRadius + z*div; | pos.z = mapRadius + z*div; | ||||
newangle += Math.PI; | newangle += Math.PI; | ||||
distanceToTargetSquared = Math.euclidDistance2DSquared(pos.x, pos.z, this.targetX, this.targetZ); | |||||
Done Inline Actionsrecompute distanceToTargetSquared here Silier: recompute distanceToTargetSquared here | |||||
} | } | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
pos.x = Math.max(Math.min(pos.x, terrainSize - 12), 12); | pos.x = Math.max(Math.min(pos.x, terrainSize - 12), 12); | ||||
pos.z = Math.max(Math.min(pos.z, terrainSize - 12), 12); | pos.z = Math.max(Math.min(pos.z, terrainSize - 12), 12); | ||||
newangle += Math.PI; | newangle += Math.PI; | ||||
distanceToTargetSquared = Math.euclidDistance2DSquared(pos.x, pos.z, this.targetX, this.targetZ); | |||||
Done Inline Actionsrecompute distanceToTargetSquared here Silier: recompute distanceToTargetSquared here | |||||
} | } | ||||
Done Inline Actionsrecompute distanceToTargetSquared here Silier: recompute distanceToTargetSquared here | |||||
Done Inline Actions(ignore pls this one, i forgot to delete) Silier: (ignore pls this one, i forgot to delete) | |||||
} | } | ||||
Done Inline Actionsbut would need recompute distance here, but this is only when unit landed currently Silier: but would need recompute distance here, but this is only when unit landed currently | |||||
Done Inline ActionsWhat does this refer to? Stan: What does this refer to? | |||||
Done Inline Actionsthis = if/else branch, so it would be recomputed less frequently as now Silier: this = if/else branch, so it would be recomputed less frequently as now | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
// Final Approach | // Final Approach | ||||
// We need to slow down to land! | // We need to slow down to land! | ||||
this.speed = Math.max(this.template.LandingSpeed, this.speed - turnLength * this.template.SlowingRate); | this.speed = Math.max(this.template.LandingSpeed, this.speed - turnLength * this.template.SlowingRate); | ||||
canTurn = false; | canTurn = false; | ||||
var targetHeight = ground; | var targetHeight = ground; | ||||
Show All 12 Lines | else | ||||
this.onGround = true; | this.onGround = true; | ||||
if (targetHeight == cmpWaterManager.GetWaterLevel(pos.x, pos.z) && this.template.DiesInWater) | if (targetHeight == cmpWaterManager.GetWaterLevel(pos.x, pos.z) && this.template.DiesInWater) | ||||
this.waterDeath = true; | this.waterDeath = true; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
if (this.template.StationaryDistance && distanceToTargetSquared <= +this.template.StationaryDistance * +this.template.StationaryDistance) | |||||
Done Inline Actionswhat if someone wants range 0? Silier: what if someone wants range 0? | |||||
Done Inline ActionsWell try range 0 it doesn't work... Stan: Well try range 0 it doesn't work... | |||||
Done Inline Actionsthen there should be something like positive decimal in template Silier: then there should be something like positive decimal in template | |||||
Done Inline Actionscould you please compare squared distance as elexis pointed on ircs instead root distance ? thnx Silier: could you please compare squared distance as elexis pointed on ircs instead root distance ? thnx | |||||
{ | |||||
cmpPosition.SetXZRotation(0, 0); | |||||
Done Inline Actionsbtw, you need to reset this.pitch and this.roll Silier: btw, you need to reset `this.pitch` and `this.roll` | |||||
this.pitch = 0; | |||||
Done Inline Actionsmaybe cmpPosition.TurnTo(cmpPosition.GetPosition2D().angleTo(targetPosition)); Silier: maybe cmpPosition.TurnTo(cmpPosition.GetPosition2D().angleTo(targetPosition)); | |||||
Done Inline ActionsI don't have access to to targetPos only X, Y. Stan: I don't have access to to targetPos only X, Y. | |||||
this.roll = 0; | |||||
Done Inline Actionsthis return is wrong, if already reached new target because in range of stationary distance, event is never sent Silier: this return is wrong, if already reached new target because in range of stationary distance… | |||||
Done Inline ActionsSo... should I send event before returning? Stan: So... should I send event before returning? | |||||
cmpPosition.TurnTo(Math.atan2(this.targetX - pos.x, this.targetZ - pos.z)); | |||||
ngine.PostMessage(this.entity, MT_MotionUpdate, { "updateString": "likelySuccess" }); | |||||
FreagarachUnsubmitted Done Inline ActionsE+ Freagarach: `E`+ | |||||
return; | |||||
SilierUnsubmitted Done Inline Actionsthis.reachedTarget = true before return Silier: this.reachedTarget = true before return | |||||
Done Inline ActionsIsn't UnitAI expecting: { "likelySuccess": true }? Freagarach: Isn't UnitAI expecting: `{ "likelySuccess": true }`? | |||||
Done Inline ActionsL225? Silier: L225? | |||||
} | |||||
// If we haven't reached max speed yet then we're still on the ground; | // If we haven't reached max speed yet then we're still on the ground; | ||||
// otherwise we're taking off or flying | // otherwise we're taking off or flying | ||||
// this.onGround in case of a go-around after landing (but not fully stopped) | // this.onGround in case of a go-around after landing (but not fully stopped) | ||||
if (this.speed < this.template.TakeoffSpeed && this.onGround) | if (this.speed < this.template.TakeoffSpeed && this.onGround) | ||||
{ | { | ||||
if (cmpGarrisonHolder) | if (cmpGarrisonHolder) | ||||
cmpGarrisonHolder.AllowGarrisoning(false,"UnitMotionFlying"); | cmpGarrisonHolder.AllowGarrisoning(false,"UnitMotionFlying"); | ||||
Show All 26 Lines | else | ||||
{ | { | ||||
pos.y = Math.max(targetHeight, pos.y - turnLength * this.template.ClimbRate); | pos.y = Math.max(targetHeight, pos.y - turnLength * this.template.ClimbRate); | ||||
this.pitch = -1 * this.pitch; | this.pitch = -1 * this.pitch; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// If we're in range of the target then tell people that we've reached it | // If we're in range of the target then tell people that we've reached it | ||||
// (TODO: quantisation breaks this) | // (TODO: quantisation breaks this) | ||||
var distFromTarget = Math.euclidDistance2D(pos.x, pos.z, this.targetX, this.targetZ); | if (!this.reachedTarget && this.targetMinRange * this.targetMinRange <= distanceToTargetSquared && distanceToTargetSquared <= this.targetMaxRange * this.targetMaxRange) | ||||
Done Inline Actionsnuke this ? move squared version before if else and use that one here? Silier: nuke this ? move squared version before if else and use that one here?
As this is going to be… | |||||
Done Inline ActionseuclidDistance2DSquared != euclidDistance2D? Stan: euclidDistance2DSquared != euclidDistance2D?
So I either have to sqrt it, or square the rest? | |||||
Done Inline ActionsdistFromTarget is used in two ifs below, they can be easily changed to use squared version Silier: distFromTarget is used in two ifs below, they can be easily changed to use squared version | |||||
if (!this.reachedTarget && this.targetMinRange <= distFromTarget && distFromTarget <= this.targetMaxRange) | |||||
{ | { | ||||
this.reachedTarget = true; | this.reachedTarget = true; | ||||
Engine.PostMessage(this.entity, MT_MotionUpdate, { "updateString": "likelySuccess" }); | Engine.PostMessage(this.entity, MT_MotionUpdate, { "updateString": "likelySuccess" }); | ||||
} | } | ||||
// If we're facing away from the target, and are still fairly close to it, | // If we're facing away from the target, and are still fairly close to it, | ||||
// then carry on going straight so we overshoot in a straight line | // then carry on going straight so we overshoot in a straight line | ||||
var isBehindTarget = ((this.targetX - pos.x) * Math.sin(angle) + (this.targetZ - pos.z) * Math.cos(angle) < 0); | var isBehindTarget = ((this.targetX - pos.x) * Math.sin(angle) + (this.targetZ - pos.z) * Math.cos(angle) < 0); | ||||
// Overshoot the target: carry on straight | // Overshoot the target: carry on straight | ||||
if (isBehindTarget && distFromTarget < this.template.MaxSpeed * this.template.OvershootTime) | if (isBehindTarget && distanceToTargetSquared < this.template.MaxSpeed * this.template.MaxSpeed * this.template.OvershootTime * this.template.OvershootTime) | ||||
canTurn = false; | canTurn = false; | ||||
if (canTurn) | if (canTurn) | ||||
{ | { | ||||
// Turn towards the target | // Turn towards the target | ||||
var targetAngle = Math.atan2(this.targetX - pos.x, this.targetZ - pos.z); | var targetAngle = Math.atan2(this.targetX - pos.x, this.targetZ - pos.z); | ||||
var delta = targetAngle - angle; | var delta = targetAngle - angle; | ||||
// Wrap delta to -pi..pi | // Wrap delta to -pi..pi | ||||
▲ Show 20 Lines • Show All 120 Lines • Show Last 20 Lines |
I would say by the name it is boolean value no range value