Add danger-aware companion selection and native survival assistance
Core tests / core (push) Successful in 39s
Core tests / core (push) Successful in 39s
This commit is contained in:
@@ -145,6 +145,9 @@ local function world_payload(player)
|
||||
local p=player_payload(player)
|
||||
local ai=player:IsPlayerbot() and player:GetBotAI() or nil
|
||||
p.humanControlled=ai and ai:HasGameClientMaster() or false
|
||||
p.quests={};if ai then for slot=0,24 do local q=player:GetQuestSlotQuestId(slot);if q and q>0 then p.quests[#p.quests+1]=q end end end
|
||||
if ai then p.combatRole=player:IsHealer() and 'healer' or player:IsTank() and 'tank' or 'damage' end
|
||||
if AzerothAISurvivalState then p.dangerAt,p.survivalAction,p.survivalAt=AzerothAISurvivalState(player) end
|
||||
local g=player:GetGroup()
|
||||
p.groupId=g and tostring(g:GetGUID()) or ""
|
||||
local members={}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
-- Survival assistance for registered living bots only. No teleport, heal, revive or stat changes.
|
||||
local registry,observed,ownedMasters={}, {}, {}
|
||||
local registryAt=0
|
||||
local enabled,enabledAt=true,0
|
||||
function AzerothAISetSurvivalEnabled(value) enabled=value==true;enabledAt=os.time() end
|
||||
local function refresh(now)
|
||||
if now-registryAt<60 then return end
|
||||
local q=AuthDBQuery("SELECT guid,name,account_id FROM azerothai_lifecycle WHERE state='active'")
|
||||
if not q then registry={};return end
|
||||
local rows={};repeat rows[q:GetUInt32(0)]={name=q:GetString(1),account=q:GetUInt32(2)} until not q:NextRow()
|
||||
registry=rows;registryAt=now
|
||||
end
|
||||
local function managed(p)
|
||||
if not p or not p:IsPlayerbot() or p:IsDead() then return false end
|
||||
local r=registry[p:GetGUIDLow()]
|
||||
return r and r.name==p:GetName() and r.account==p:GetAccountId()
|
||||
end
|
||||
local function safeGroup(p)
|
||||
local g=p:GetGroup();if not g or g:IsRaidGroup() or #g:GetMembers()~=g:GetMembersCount() then return nil end
|
||||
for _,m in pairs(g:GetMembers()) do if not managed(m) or not m:GetBotAI() or m:GetBotAI():HasGameClientMaster() then return nil end end
|
||||
return g
|
||||
end
|
||||
function AzerothAISurvivalState(p)
|
||||
local r=observed[p:GetGUIDLow()]
|
||||
if not r then return 0,'',0 end
|
||||
return r.dangerAt or 0,r.action or '',r.actionAt or 0
|
||||
end
|
||||
-- Prevent slower model intentions from undoing an immediate safety action.
|
||||
function AzerothAISurvivalOverride(p)
|
||||
local r=observed[p:GetGUIDLow()]
|
||||
return r and os.time()-(r.actionAt or 0)<20 and (r.action=='retreat requested' or r.action=='recovering health' or r.action=='regroup requested') or false
|
||||
end
|
||||
local function sample()
|
||||
local now=os.time();refresh(now)
|
||||
for _,p in pairs(GetPlayersInWorld() or {}) do
|
||||
if managed(p) then
|
||||
local id=p:GetGUIDLow();local ai=p:GetBotAI();local r=observed[id] or {};observed[id]=r
|
||||
local hp=p:GetHealthPct()
|
||||
if p:IsInCombat() and (hp<40 or r.hp and r.hp-hp>=25) then r.dangerAt=now end
|
||||
r.hp=hp;r.seen=now
|
||||
local master=ai and ai:GetMaster()
|
||||
if ownedMasters[id] and (not p:GetGroup() or not master or not master:GetGroup() or master:GetGroup()~=p:GetGroup()) then
|
||||
if ai and master and master:IsPlayerbot() and master:GetGUIDLow()==ownedMasters[id] and not ai:HasGameClientMaster() then ai:SetMaster(nil) end
|
||||
ownedMasters[id]=nil
|
||||
end
|
||||
if ai and not ai:HasGameClientMaster() and not p:IsTaxi() and not p:IsCasting() and not p:IsRooted() and not ai:IsMovementImpaired() then
|
||||
local g=safeGroup(p)
|
||||
-- Never intervene in a party containing humans, unregistered bots, or offline members.
|
||||
if not p:GetGroup() or g then
|
||||
local peers=g and g:GetMembers() or {p};local weak=0
|
||||
for _,m in pairs(peers) do if m:GetMapId()==p:GetMapId() and p:GetDistance(m)<40 and m:GetHealthPct()<40 then weak=weak+1 end end
|
||||
local enemy=p:GetVictim()
|
||||
local overmatched=enemy and enemy:GetLevel()>=p:GetLevel()+4 and hp<65
|
||||
if p:IsInCombat() and (hp<30 or overmatched or #peers>1 and weak>=math.max(2,math.ceil(#peers/2))) and now-(r.retreatAt or 0)>=20 then
|
||||
r.dangerAt=now;r.retreatAt=now;r.action='retreat requested';r.actionAt=now
|
||||
r.accepted=ai:DoSpecificAction('flee',true)
|
||||
elseif not p:IsInCombat() and hp<65 and now-(r.dangerAt or 0)<600 and now-(r.restAt or 0)>=20 then
|
||||
r.restAt=now;r.action='recovering health';r.actionAt=now
|
||||
ai:DoSpecificAction('eat',true);ai:DoSpecificAction('drink',true)
|
||||
elseif enabled and now-enabledAt<180 and g and not p:IsInCombat() 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)>18 and p:GetDistance(leader)<90 and hp>=65 and now-(r.followAt or 0)>=20 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)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
for id,r in pairs(observed) do if now-(r.seen or 0)>600 then observed[id]=nil;ownedMasters[id]=nil end end
|
||||
end
|
||||
CreateLuaEvent(function()local ok,err=pcall(sample);if not ok then PrintError('[AzerothAI survival] '..tostring(err)) end end,10000,0)
|
||||
PrintInfo('[AzerothAI] Survival assistance enabled: danger memory, native retreat and nearby regrouping')
|
||||
Reference in New Issue
Block a user