mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-06-16 21:34:08 -07:00
[gameplay] Fix chasing range cavalry
This fixes chasing, particularly chasing ranged cavalry. - Standardise the range of melee cav to 4. - Decreases the speed of ranged cavalry slightly to make melee cavalry a better counter & reduce the ability of ranged cavalry to dominate an area. - Fix UnitMotion to better chase units, by increasing direct-range distance and making "from scratch" short paths recompute better paths (by increasing the search range). - Gives some free rotation time for slight angles to units. Angles below 30° take no time to rotate towards. Chasing units that recomputed a lot of paths could be slowed down substantially by minute angle differences. Fixes #5936 Differential Revision: https://code.wildfiregames.com/D3402 This was SVN commit r24708.
This commit is contained in:
parent
bc7977946b
commit
9d82ae15af
18 changed files with 80 additions and 15 deletions
|
|
@ -3,6 +3,7 @@ const JAV_TEMPLATE = "units/mace/infantry_javelineer_b";
|
|||
|
||||
const REG_UNIT_TEMPLATE = "units/athen/infantry_spearman_b";
|
||||
const FAST_UNIT_TEMPLATE = "units/athen/cavalry_swordsman_b";
|
||||
const FAST_UNIT_TEMPLATE_2 = "units/athen/cavalry_javelineer_b";
|
||||
|
||||
const ATTACKER = 2;
|
||||
|
||||
|
|
@ -180,6 +181,27 @@ var manual_formation_dance = function(attacker, target, dance_distance, att_dist
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
* This isn't really dancing, but it can still fail.
|
||||
*/
|
||||
var avoidance = function(attacker, target, att_distance = 10)
|
||||
{
|
||||
return () => {
|
||||
let dancer = QuickSpawn(200, 300, target);
|
||||
for (let i = 0; i < 5; ++i)
|
||||
{
|
||||
WalkTo(300, 400, true, dancer);
|
||||
WalkTo(400, 300, true, dancer);
|
||||
WalkTo(300, 200, true, dancer);
|
||||
WalkTo(200, 300, true, dancer);
|
||||
}
|
||||
|
||||
let attackers = [];
|
||||
attackers.push(Attack(dancer, QuickSpawn(200, 290, attacker, ATTACKER), ATTACKER));
|
||||
return [[dancer], attackers];
|
||||
};
|
||||
};
|
||||
|
||||
experiments.unit_manual_dance_archer = {
|
||||
"spawn": manual_dance(ARCHER_TEMPLATE, REG_UNIT_TEMPLATE, 5)
|
||||
};
|
||||
|
|
@ -280,6 +302,10 @@ experiments.formation_bad_dance_fast_archer = {
|
|||
"spawn": manual_formation_dance(ARCHER_TEMPLATE, FAST_UNIT_TEMPLATE, 50, 50, 5)
|
||||
};
|
||||
|
||||
experiments.fast_on_fast = {
|
||||
"spawn": avoidance(FAST_UNIT_TEMPLATE, FAST_UNIT_TEMPLATE_2)
|
||||
};
|
||||
|
||||
var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
|
||||
|
||||
Trigger.prototype.SetupUnits = function()
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<Pierce>0</Pierce>
|
||||
<Crush>2.3</Crush>
|
||||
</Damage>
|
||||
<MaxRange>3.5</MaxRange>
|
||||
<MaxRange>4</MaxRange>
|
||||
<PrepareTime>500</PrepareTime>
|
||||
<RepeatTime>1000</RepeatTime>
|
||||
<PreferredClasses datatype="tokens">Unit+!Ship</PreferredClasses>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
<Multiplier>1.75</Multiplier>
|
||||
</BonusCavMelee>
|
||||
</Bonuses>
|
||||
<MaxRange>4.5</MaxRange>
|
||||
<MaxRange>4</MaxRange>
|
||||
<PrepareTime>625</PrepareTime>
|
||||
<RepeatTime>1250</RepeatTime>
|
||||
<PreferredClasses datatype="tokens">Human</PreferredClasses>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<Pierce>0</Pierce>
|
||||
<Crush>0</Crush>
|
||||
</Damage>
|
||||
<MaxRange>3.5</MaxRange>
|
||||
<MaxRange>4</MaxRange>
|
||||
<PrepareTime>375</PrepareTime>
|
||||
<RepeatTime>750</RepeatTime>
|
||||
<PreferredClasses datatype="tokens">Unit+!Ship</PreferredClasses>
|
||||
|
|
|
|||
|
|
@ -38,4 +38,7 @@
|
|||
<attack_ranged>attack/weapon/bow_attack.xml</attack_ranged>
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<UnitMotion>
|
||||
<WalkSpeed op="mul">0.9</WalkSpeed>
|
||||
</UnitMotion>
|
||||
</Entity>
|
||||
|
|
|
|||
|
|
@ -38,4 +38,7 @@
|
|||
<attack_impact_ranged>attack/impact/javelin_impact.xml</attack_impact_ranged>
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<UnitMotion>
|
||||
<WalkSpeed op="mul">0.9</WalkSpeed>
|
||||
</UnitMotion>
|
||||
</Entity>
|
||||
|
|
|
|||
|
|
@ -44,4 +44,7 @@
|
|||
<attack_ranged>attack/weapon/bow_attack.xml</attack_ranged>
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<UnitMotion>
|
||||
<WalkSpeed op="mul">0.9</WalkSpeed>
|
||||
</UnitMotion>
|
||||
</Entity>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<Pierce>0</Pierce>
|
||||
<Crush>4.6</Crush>
|
||||
</Damage>
|
||||
<MaxRange>3.5</MaxRange>
|
||||
<MaxRange>4</MaxRange>
|
||||
<PrepareTime>500</PrepareTime>
|
||||
<RepeatTime>1000</RepeatTime>
|
||||
<PreferredClasses datatype="tokens">Unit+!Ship</PreferredClasses>
|
||||
|
|
|
|||
|
|
@ -44,4 +44,7 @@
|
|||
<attack_impact_ranged>attack/impact/javelin_impact.xml</attack_impact_ranged>
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<UnitMotion>
|
||||
<WalkSpeed op="mul">0.9</WalkSpeed>
|
||||
</UnitMotion>
|
||||
</Entity>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<Pierce>6</Pierce>
|
||||
<Crush>0</Crush>
|
||||
</Damage>
|
||||
<MaxRange>4.5</MaxRange>
|
||||
<MaxRange>4</MaxRange>
|
||||
<Bonuses>
|
||||
<BonusCavMelee>
|
||||
<Classes>Cavalry</Classes>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<Pierce>0</Pierce>
|
||||
<Crush>0.0</Crush>
|
||||
</Damage>
|
||||
<MaxRange>3.5</MaxRange>
|
||||
<MaxRange>4</MaxRange>
|
||||
<PrepareTime>375</PrepareTime>
|
||||
<RepeatTime>750</RepeatTime>
|
||||
<PreferredClasses datatype="tokens">Unit+!Ship</PreferredClasses>
|
||||
|
|
|
|||
|
|
@ -44,4 +44,7 @@
|
|||
<attack_ranged>attack/weapon/bow_attack.xml</attack_ranged>
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<UnitMotion>
|
||||
<WalkSpeed op="mul">0.9</WalkSpeed>
|
||||
</UnitMotion>
|
||||
</Entity>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<Pierce>0</Pierce>
|
||||
<Crush>9.2</Crush>
|
||||
</Damage>
|
||||
<MaxRange>3.5</MaxRange>
|
||||
<MaxRange>4</MaxRange>
|
||||
<PrepareTime>500</PrepareTime>
|
||||
<RepeatTime>1000</RepeatTime>
|
||||
<PreferredClasses datatype="tokens">Unit+!Ship</PreferredClasses>
|
||||
|
|
@ -30,4 +30,7 @@
|
|||
<attack_melee>attack/weapon/sword_attack.xml</attack_melee>
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<UnitMotion>
|
||||
<WalkSpeed op="mul">1.1</WalkSpeed>
|
||||
</UnitMotion>
|
||||
</Entity>
|
||||
|
|
|
|||
|
|
@ -44,4 +44,7 @@
|
|||
<attack_impact_ranged>attack/impact/javelin_impact.xml</attack_impact_ranged>
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<UnitMotion>
|
||||
<WalkSpeed op="mul">0.9</WalkSpeed>
|
||||
</UnitMotion>
|
||||
</Entity>
|
||||
|
|
|
|||
|
|
@ -22,4 +22,7 @@
|
|||
<attack_melee>attack/weapon/sword_attack.xml</attack_melee>
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<UnitMotion>
|
||||
<WalkSpeed op="mul">1.1</WalkSpeed>
|
||||
</UnitMotion>
|
||||
</Entity>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<Pierce>12</Pierce>
|
||||
<Crush>0</Crush>
|
||||
</Damage>
|
||||
<MaxRange>4.5</MaxRange>
|
||||
<MaxRange>4</MaxRange>
|
||||
<Bonuses>
|
||||
<BonusCavMelee>
|
||||
<Classes>Cavalry</Classes>
|
||||
|
|
@ -38,4 +38,7 @@
|
|||
<attack_melee>attack/weapon/spear_attack.xml</attack_melee>
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<UnitMotion>
|
||||
<WalkSpeed op="mul">1.1</WalkSpeed>
|
||||
</UnitMotion>
|
||||
</Entity>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<Pierce>0.0</Pierce>
|
||||
<Crush>0.0</Crush>
|
||||
</Damage>
|
||||
<MaxRange>3.5</MaxRange>
|
||||
<MaxRange>4</MaxRange>
|
||||
<PrepareTime>375</PrepareTime>
|
||||
<RepeatTime>750</RepeatTime>
|
||||
<PreferredClasses datatype="tokens">Unit+!Ship</PreferredClasses>
|
||||
|
|
@ -37,4 +37,7 @@
|
|||
<attack_melee>attack/weapon/sword_attack.xml</attack_melee>
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<UnitMotion>
|
||||
<WalkSpeed op="mul">1.1</WalkSpeed>
|
||||
</UnitMotion>
|
||||
</Entity>
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ static const entity_pos_t LONG_PATH_MIN_DIST = entity_pos_t::FromInt(TERRAIN_TIL
|
|||
* If we are this close to our target entity/point, then think about heading
|
||||
* for it in a straight line instead of pathfinding.
|
||||
*/
|
||||
static const entity_pos_t DIRECT_PATH_RANGE = entity_pos_t::FromInt(TERRAIN_TILE_SIZE*4);
|
||||
static const entity_pos_t DIRECT_PATH_RANGE = entity_pos_t::FromInt(TERRAIN_TILE_SIZE*6);
|
||||
|
||||
/**
|
||||
* To avoid recomputing paths too often, have some leeway for target range checks
|
||||
|
|
@ -731,8 +731,9 @@ private:
|
|||
|
||||
/**
|
||||
* Start an asynchronous short path query.
|
||||
* @param extendRange - if true, extend the search range to at least the distance to the goal.
|
||||
*/
|
||||
void RequestShortPath(const CFixedVector2D& from, const PathGoal& goal, bool avoidMovingUnits);
|
||||
void RequestShortPath(const CFixedVector2D& from, const PathGoal& goal, bool extendRange);
|
||||
|
||||
/**
|
||||
* General handler for MoveTo interface functions.
|
||||
|
|
@ -1028,7 +1029,8 @@ bool CCmpUnitMotion::PerformMove(fixed dt, const fixed& turnRate, WaypointPath&
|
|||
}
|
||||
// Rotate towards the next waypoint and continue moving.
|
||||
angle = atan2_approx(offset.X, offset.Y);
|
||||
timeLeft = (maxRotation - absoluteAngleDiff) / turnRate;
|
||||
// Give some 'free' rotation for angles below 0.5 radians.
|
||||
timeLeft = (std::min(maxRotation, maxRotation - absoluteAngleDiff + fixed::FromInt(1)/2)) / turnRate;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1159,7 +1161,7 @@ bool CCmpUnitMotion::HandleObstructedMove(bool moved)
|
|||
// The goal here is to manage to move in the general direction of our target, not to be super accurate.
|
||||
fixed radius = Clamp(skipbeyond/3, fixed::FromInt(TERRAIN_TILE_SIZE), fixed::FromInt(TERRAIN_TILE_SIZE*3));
|
||||
PathGoal subgoal = { PathGoal::CIRCLE, m_LongPath.m_Waypoints.back().x, m_LongPath.m_Waypoints.back().z, radius };
|
||||
RequestShortPath(pos, subgoal, true);
|
||||
RequestShortPath(pos, subgoal, false);
|
||||
return true;
|
||||
}()) return true;
|
||||
|
||||
|
|
@ -1529,6 +1531,7 @@ void CCmpUnitMotion::ComputePathToGoal(const CFixedVector2D& from, const PathGoa
|
|||
if (shortPath)
|
||||
{
|
||||
m_LongPath.m_Waypoints.clear();
|
||||
// Extend the range so that our first path is probably valid.
|
||||
RequestShortPath(from, goal, true);
|
||||
}
|
||||
else
|
||||
|
|
@ -1555,16 +1558,22 @@ void CCmpUnitMotion::RequestLongPath(const CFixedVector2D& from, const PathGoal&
|
|||
m_ExpectedPathTicket.m_Ticket = cmpPathfinder->ComputePathAsync(from.X, from.Y, improvedGoal, m_PassClass, GetEntityId());
|
||||
}
|
||||
|
||||
void CCmpUnitMotion::RequestShortPath(const CFixedVector2D &from, const PathGoal& goal, bool avoidMovingUnits)
|
||||
void CCmpUnitMotion::RequestShortPath(const CFixedVector2D &from, const PathGoal& goal, bool extendRange)
|
||||
{
|
||||
CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity());
|
||||
if (!cmpPathfinder)
|
||||
return;
|
||||
|
||||
entity_pos_t searchRange = ShortPathSearchRange();
|
||||
if (extendRange)
|
||||
{
|
||||
CFixedVector2D dist(from.X - goal.x, from.Y - goal.z);
|
||||
if (dist.CompareLength(searchRange - entity_pos_t::FromInt(1)) >= 0)
|
||||
searchRange = dist.Length() + fixed::FromInt(1);
|
||||
}
|
||||
|
||||
m_ExpectedPathTicket.m_Type = Ticket::SHORT_PATH;
|
||||
m_ExpectedPathTicket.m_Ticket = cmpPathfinder->ComputeShortPathAsync(from.X, from.Y, m_Clearance, searchRange, goal, m_PassClass, avoidMovingUnits, GetGroup(), GetEntityId());
|
||||
m_ExpectedPathTicket.m_Ticket = cmpPathfinder->ComputeShortPathAsync(from.X, from.Y, m_Clearance, searchRange, goal, m_PassClass, true, GetGroup(), GetEntityId());
|
||||
}
|
||||
|
||||
bool CCmpUnitMotion::MoveTo(MoveRequest request)
|
||||
|
|
|
|||
Loading…
Reference in a new issue