Hold independent work while companions regroup and leaders wait
Core tests / core (push) Successful in 36s
Core tests / core (push) Successful in 36s
This commit is contained in:
+32
-14
@@ -136,7 +136,30 @@ local function sample()
|
||||
if hp<85 or mana<35 then r.resting=true end
|
||||
if hp>=95 and mana>=65 then r.resting=false end
|
||||
local resting=not p:IsInCombat() and (r.resting or now<(r.recoverUntil or 0))
|
||||
if unsafe or avoid or resting then holdPlans(p,ai,r,now) else releasePlans(p,r) end
|
||||
-- Decide cohesion before restoring work flags, so native RPG cannot undo a wait/follow.
|
||||
local leader=g and ai:GetGroupLeader() or nil
|
||||
local regroup,waiting=false,false
|
||||
local cohesion=enabled and now-enabledAt<180 and g and not p:IsInCombat() and not p:IsCasting() and not unsafe and not avoid and not resting
|
||||
if cohesion and leader and managed(leader) and not leader:IsTaxi() then
|
||||
if leader~=p and leader:GetMapId()==p:GetMapId() and leader:GetPhaseMask()==p:GetPhaseMask() then
|
||||
local distance=p:GetDistance(leader)
|
||||
-- Hysteresis prevents alternating independent work and follow at the boundary.
|
||||
if distance<=6 or distance>=200 then r.catchingUp=false
|
||||
elseif distance>12 then r.catchingUp=true end
|
||||
regroup=r.catchingUp and hp>=95 and mana>=65 and not leader:IsInCombat()
|
||||
elseif leader==p then
|
||||
local separated=false
|
||||
for _,m in pairs(peers) do
|
||||
if m~=p and not m:IsTaxi() and m:GetMapId()==p:GetMapId() and m:GetPhaseMask()==p:GetPhaseMask() and p:GetDistance(m)<200 and (p:GetDistance(m)>15 or m:GetHealthPct()<85 or (m:GetPowerType()==0 and m:GetPowerPct(0)<35)) then separated=true;break end
|
||||
end
|
||||
if separated then
|
||||
r.waitSince=r.waitSince or now
|
||||
waiting=now-r.waitSince<20
|
||||
if now-r.waitSince>60 then r.waitSince=now;waiting=true end
|
||||
else r.waitSince=nil end
|
||||
end
|
||||
else r.catchingUp=false;r.waitSince=nil end
|
||||
if unsafe or avoid or resting or regroup or waiting then holdPlans(p,ai,r,now) else releasePlans(p,r) end
|
||||
if unsafe and now-(r.retreatAt or 0)>=8 then
|
||||
r.dangerAt=now;r.retreatAt=now;r.action='retreat requested';r.actionAt=now
|
||||
if p:IsCasting() then p:StopSpellCast() end
|
||||
@@ -158,22 +181,17 @@ local function sample()
|
||||
ai:DoSpecificAction('eat',true);ai:DoSpecificAction('drink',true)
|
||||
end
|
||||
end
|
||||
elseif enabled and now-enabledAt<180 and g and not p:IsInCombat() and not p:IsCasting() then
|
||||
local leader=ai:GetGroupLeader()
|
||||
if leader and leader~=p and managed(leader) and not leader:IsInCombat() and not leader:IsTaxi() and leader:GetMapId()==p:GetMapId() and leader:GetPhaseMask()==p:GetPhaseMask() and p:GetDistance(leader)>8 and p:GetDistance(leader)<200 and hp>=95 and mana>=65 and now-(r.followAt or 0)>=8 then
|
||||
elseif regroup then
|
||||
ai:SetMaster(leader);ownedMasters[id]=leader:GetGUIDLow()
|
||||
ai:ChangeStrategy('-stay',1)
|
||||
r.followAt=now;r.action='regroup requested';r.actionAt=now;r.accepted=ai:DoSpecificAction('follow',true)
|
||||
elseif leader==p and hp>=95 then
|
||||
for _,m in pairs(peers) do
|
||||
if m~=p and not m:IsInCombat() and not m:IsTaxi() and m:GetMapId()==p:GetMapId() and m:GetPhaseMask()==p:GetPhaseMask() and (p:GetDistance(m)>15 or m:GetHealthPct()<85) and p:GetDistance(m)<200 then
|
||||
r.waitSince=r.waitSince or now
|
||||
if now-r.waitSince<20 then p:MoveStop();r.action='waiting for companion';r.actionAt=now
|
||||
elseif now-r.waitSince>60 then r.waitSince=nil end
|
||||
break
|
||||
end
|
||||
end
|
||||
r.action='regroup requested';r.actionAt=now
|
||||
if now-(r.followAt or 0)>=8 then
|
||||
r.followAt=now;r.accepted=ai:DoSpecificAction('follow',true)
|
||||
if now-(r.cohesionAuditAt or 0)>=30 then audit(p,'regroup-request','accepted='..tostring(r.accepted)..'; distance='..math.floor(p:GetDistance(leader)));r.cohesionAuditAt=now end
|
||||
end
|
||||
elseif waiting then
|
||||
p:MoveStop();r.action='waiting for companion';r.actionAt=now
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -72,3 +72,15 @@ native AI to flee if that hostile target is selected again within range. This
|
||||
memory is local to the running Lua process, expires, and is not a pathfinding
|
||||
barrier or a guarantee that the bot can escape. No automatic gear, talent, or level
|
||||
boosts are applied.
|
||||
|
||||
## Cohesion holds
|
||||
|
||||
Catch-up begins beyond 12 yards and remains active until within 6 yards, with a
|
||||
200-yard maximum. During catch-up, the existing durable strategy lease pauses
|
||||
independent questing, grinding, gathering and looting; following and defensive
|
||||
combat remain native. Leaders use the same lease during their bounded 20-second
|
||||
wait, rather than only stopping movement while RPG immediately restarts it.
|
||||
A reunion resets the wait timer. Nearby low-mana companions also trigger a wait.
|
||||
Safety/recovery still wins, incomplete or human parties are excluded, and no
|
||||
teleport or forced cross-map reunion is attempted. Regroup requests record the
|
||||
native acceptance result and distance at most once per 30 seconds per bot.
|
||||
|
||||
@@ -61,3 +61,7 @@ l=new();l.execute("A.hp=80;step(0);A.hp=100;local query=AuthDBQuery;function Aut
|
||||
print('Readiness holds, mana recovery, outnumbered/rapid-loss retreat, and original-strategy restoration passed')
|
||||
l=new();l.execute("A.combat=true;A.hp=75;A.attackers={1,2};A.enemy={GetGUID=function()return 999 end,GetLevel=function()return 5 end,ToCreature=function()return nil end};step(0);A.selected=A.enemy;A.combat=false;A.hp=100;A.attackers={};step(20);assert(A.attackStopped and calls[#calls]=='flee');A.attackStopped=false;step(601);assert(not A.attackStopped)")
|
||||
print('Dangerous encounter target avoidance expires after ten minutes')
|
||||
l=new();l.execute("group();A.distance=50;B.distance=50;A.strategies={rpg=true,grind=true};B.strategies={rpg=true,gather=true};step(0);assert(A.waited and not A.strategies.rpg and not B.strategies.gather);assert(AzerothAISurvivalOverride(A) and AzerothAISurvivalOverride(B));B.distance=9;step(8);assert(AzerothAISurvivalOverride(B));B.distance=5;A.distance=5;step(30);assert(not AzerothAISurvivalOverride(B),'Caught-up bot must release the hold')")
|
||||
l=new();l.execute("group();B.distance=50;B.combat=true;B.hp=20;step(0);assert(calls[1]=='flee','Survival outranks regrouping');assert(not B.master)")
|
||||
l=new();l.execute("group();A.distance=40;step(0);A.waited=false;step(22);assert(not A.waited,'Leader waiting must remain bounded');A.distance=5;step(2);A.distance=40;step(2);assert(A.waited,'A fresh separation starts a fresh wait')")
|
||||
print('Cohesion suppresses competing work, keeps catch-up hysteresis, yields to danger, and resets bounded waits')
|
||||
|
||||
Reference in New Issue
Block a user