Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/binaries/data/mods/public/simulation/components/Attack.js
Show First 20 Lines • Show All 509 Lines • ▼ Show 20 Lines | if (type == "Ranged") | ||||
// In the future this could be extended: | // In the future this could be extended: | ||||
// * Obstacles like trees could reduce the probability of the target being hit | // * Obstacles like trees could reduce the probability of the target being hit | ||||
// * Obstacles like walls should block projectiles entirely | // * Obstacles like walls should block projectiles entirely | ||||
let horizSpeed = +this.template[type].Projectile.Speed; | let horizSpeed = +this.template[type].Projectile.Speed; | ||||
let gravity = +this.template[type].Projectile.Gravity; | let gravity = +this.template[type].Projectile.Gravity; | ||||
// horizSpeed /= 2; gravity /= 2; // slow it down for testing | // horizSpeed /= 2; gravity /= 2; // slow it down for testing | ||||
// We will try to estimate the position of the target, where we can hit it. | |||||
// We first estimate the time-till-hit by extrapolating linearly the movement | |||||
// of the last turn. We compute the time till an arrow will intersect the target. | |||||
let cmpPosition = Engine.QueryInterface(this.entity, IID_Position); | let cmpPosition = Engine.QueryInterface(this.entity, IID_Position); | ||||
if (!cmpPosition || !cmpPosition.IsInWorld()) | if (!cmpPosition || !cmpPosition.IsInWorld()) | ||||
return; | return; | ||||
let selfPosition = cmpPosition.GetPosition(); | let selfPosition = cmpPosition.GetPosition(); | ||||
let cmpTargetPosition = Engine.QueryInterface(target, IID_Position); | let cmpTargetPosition = Engine.QueryInterface(target, IID_Position); | ||||
if (!cmpTargetPosition || !cmpTargetPosition.IsInWorld()) | if (!cmpTargetPosition || !cmpTargetPosition.IsInWorld()) | ||||
return; | return; | ||||
let targetPosition = cmpTargetPosition.GetPosition(); | let targetPosition = cmpTargetPosition.GetPosition(); | ||||
let previousTargetPosition = Engine.QueryInterface(target, IID_Position).GetPreviousPosition(); | let targetVelocity = Vector3D.sub(targetPosition, cmpTargetPosition.GetPreviousPosition()).div(turnLength); | ||||
let targetVelocity = Vector3D.sub(targetPosition, previousTargetPosition).div(turnLength); | |||||
let timeToTarget = PositionHelper.PredictTimeToTarget(selfPosition, horizSpeed, targetPosition, targetVelocity); | let timeToTarget = PositionHelper.PredictTimeToTarget(selfPosition, horizSpeed, targetPosition, targetVelocity); | ||||
let predictedPosition = (timeToTarget !== false) ? Vector3D.mult(targetVelocity, timeToTarget).add(targetPosition) : targetPosition; | |||||
// 'Cheat' and use UnitMotion to predict the position in the near-future. | |||||
// This avoids 'dancing' issues with units zigzagging over very short distances. | |||||
// However, this could fail if the player gives several short move orders, so | |||||
// occasionally fall back to basic interpolation. | |||||
let predictedPosition = targetPosition; | |||||
if (timeToTarget !== false) | |||||
{ | |||||
// Don't predict too far in the future, but avoid threshold effects. | |||||
// After 1 second, always use the 'dumb' interpolated past-motion prediction. | |||||
let useUnitMotion = randBool(Math.max(0, 0.75 - timeToTarget / 1.333)); | |||||
if (useUnitMotion) | |||||
{ | |||||
let cmpTargetUnitMotion = Engine.QueryInterface(target, IID_UnitMotion); | |||||
let cmpTargetUnitAI = Engine.QueryInterface(target, IID_UnitAI); | |||||
if (cmpTargetUnitMotion && (!cmpTargetUnitAI || !cmpTargetUnitAI.IsFormationMember())) | |||||
{ | |||||
let pos2D = cmpTargetUnitMotion.EstimateFuturePosition(timeToTarget); | |||||
predictedPosition.x = pos2D.x; | |||||
predictedPosition.z = pos2D.y; | |||||
} | |||||
else | |||||
predictedPosition = Vector3D.mult(targetVelocity, timeToTarget).add(targetPosition); | |||||
} | |||||
else | |||||
predictedPosition = Vector3D.mult(targetVelocity, timeToTarget).add(targetPosition); | |||||
} | |||||
// Add inaccuracy based on spread. | // Add inaccuracy based on spread. | ||||
let distanceModifiedSpread = ApplyValueModificationsToEntity("Attack/Ranged/Spread", +this.template[type].Projectile.Spread, this.entity) * | let distanceModifiedSpread = ApplyValueModificationsToEntity("Attack/Ranged/Spread", +this.template[type].Projectile.Spread, this.entity) * | ||||
predictedPosition.horizDistanceTo(selfPosition) / 100; | predictedPosition.horizDistanceTo(selfPosition) / 100; | ||||
let randNorm = randomNormal2D(); | let randNorm = randomNormal2D(); | ||||
let offsetX = randNorm[0] * distanceModifiedSpread; | let offsetX = randNorm[0] * distanceModifiedSpread; | ||||
let offsetZ = randNorm[1] * distanceModifiedSpread; | let offsetZ = randNorm[1] * distanceModifiedSpread; | ||||
▲ Show 20 Lines • Show All 97 Lines • Show Last 20 Lines |
Wildfire Games · Phabricator