Changeset View
Changeset View
Standalone View
Standalone View
binaries/data/mods/public/simulation/components/Attack.js
Show First 20 Lines • Show All 491 Lines • ▼ Show 20 Lines | Attack.prototype.GetRange = function(type) | ||||
return { "max": max, "min": min, "elevationBonus": elevationBonus }; | return { "max": max, "min": min, "elevationBonus": elevationBonus }; | ||||
}; | }; | ||||
/** | /** | ||||
* Attack the target entity. This should only be called after a successful range check, | * Attack the target entity. This should only be called after a successful range check, | ||||
* and should only be called after GetTimers().repeat msec has passed since the last | * and should only be called after GetTimers().repeat msec has passed since the last | ||||
* call to PerformAttack. | * call to PerformAttack. | ||||
* | |||||
* @param probableDancer - if true, assume the target is moving erratically ("dancing") | |||||
*/ | */ | ||||
Attack.prototype.PerformAttack = function(type, target) | Attack.prototype.PerformAttack = function(type, target, probableDancer = false) | ||||
{ | { | ||||
let attackerOwner = Engine.QueryInterface(this.entity, IID_Ownership).GetOwner(); | let attackerOwner = Engine.QueryInterface(this.entity, IID_Ownership).GetOwner(); | ||||
// If this is a ranged attack, then launch a projectile | // If this is a ranged attack, then launch a projectile | ||||
if (type == "Ranged") | if (type == "Ranged") | ||||
{ | { | ||||
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); | let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); | ||||
let turnLength = cmpTimer.GetLatestTurnLength()/1000; | let turnLength = cmpTimer.GetLatestTurnLength()/1000; | ||||
Show All 12 Lines | if (type == "Ranged") | ||||
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 previousTargetPosition = Engine.QueryInterface(target, IID_Position).GetPreviousPosition(); | ||||
let targetVelocity = Vector3D.sub(targetPosition, previousTargetPosition).div(turnLength); | let targetVelocity = Vector3D.sub(targetPosition, previousTargetPosition).div(turnLength); | ||||
// To work around 'dancing' units, we'll sometimes revert our prediction, assuming that the unit | |||||
// will turn around. This avoids the trap where exact velocity prediction never pan out. | |||||
if (probableDancer) | |||||
{ | |||||
let choice = Math.random(); | |||||
if (choice < 0.33) | |||||
targetVelocity *= -1; | |||||
else if (choice > 0.66) | |||||
targetVelocity = 0.0; | |||||
} | |||||
bb: This makes one big assumption: a unit is either walking forward or turn 180 degrees. I presume… | |||||
PalaxinUnsubmitted Not Done Inline ActionsVery interesting approach, because as you would expect in real life, archers learn from their mistakes, try something else and make better predictions... Palaxin: Very interesting approach, because as you would expect in real life, archers learn from their… | |||||
bbUnsubmitted Not Done Inline ActionsThe idea on responding to failed actions can be worthwhile. I only think just directing arrows in some other direction is too naive. Maybe choosing a different target on enough fails helps enough? Or prefering targets which don't move? Also note that players might still dance: wasting 5 arrows alone from all enemies can win some battles. bb: The idea on responding to failed actions can be worthwhile. I only think just directing arrows… | |||||
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; | let predictedPosition = (timeToTarget !== false) ? Vector3D.mult(targetVelocity, timeToTarget).add(targetPosition) : 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(); | ||||
▲ Show 20 Lines • Show All 99 Lines • Show Last 20 Lines |
Wildfire Games · Phabricator
This makes one big assumption: a unit is either walking forward or turn 180 degrees. I presume everyone now will triangle dance...