Support persistent residents with normal resurrection and closer companions
Core tests / core (push) Successful in 2m54s
Core tests / core (push) Successful in 2m54s
This commit is contained in:
@@ -1,6 +1,13 @@
|
||||
-- Persistent hardcore retirement. The native random-bot manager fills vacancies
|
||||
-- from its factory-created, unused starting-level reserve; identities are never reset.
|
||||
-- Install only after deploy/playable/install-lifecycle.py has seeded the registry.
|
||||
-- The persisted mode is synchronized from dashboard settings by operations-agent.
|
||||
AuthDBExecute("CREATE TABLE IF NOT EXISTS azerothai_realm_rules (id TINYINT PRIMARY KEY,hardcore TINYINT NOT NULL)")
|
||||
AuthDBExecute("INSERT IGNORE INTO azerothai_realm_rules VALUES (1,1)")
|
||||
function AzerothAIHardcoreEnabled()
|
||||
local q=AuthDBQuery("SELECT hardcore FROM azerothai_realm_rules WHERE id=1")
|
||||
return not q or q:GetUInt32(0)~=0
|
||||
end
|
||||
local residents = {}
|
||||
local ready = false
|
||||
local vetoAt = {}
|
||||
@@ -68,6 +75,7 @@ local function identity(player)
|
||||
end
|
||||
|
||||
local function recordDeath(player)
|
||||
if not AzerothAIHardcoreEnabled() then return end
|
||||
local row,guid=identity(player)
|
||||
if not row or row.state=="dead" or row.state=="blocked" then return end
|
||||
row.state="dead"
|
||||
@@ -77,6 +85,7 @@ local function recordDeath(player)
|
||||
end
|
||||
|
||||
local function enforce()
|
||||
local hardcore=AzerothAIHardcoreEnabled()
|
||||
if not ready then reload() end
|
||||
-- The dashboard may receive a verified death from an older transport backlog
|
||||
-- after this process seeded its registry. Import terminal states only; never
|
||||
@@ -114,7 +123,7 @@ local function enforce()
|
||||
for _,player in pairs(players) do
|
||||
local guid=player:GetGUIDLow()
|
||||
local row=residents[guid]
|
||||
if row and row.state=="active" and row.account==player:GetAccountId() and row.name==player:GetName() and not player:IsDead() then
|
||||
if row and row.state=="active" and row.account==player:GetAccountId() and row.name==player:GetName() and (not hardcore or not player:IsDead()) then
|
||||
returning[#returning+1]={player=player,row=row,guid=guid}
|
||||
end
|
||||
end
|
||||
@@ -203,4 +212,4 @@ end,1000,0)
|
||||
|
||||
reload()
|
||||
enforce()
|
||||
PrintInfo("[AzerothAI] Persistent hardcore retirement and reserve replacement enabled")
|
||||
PrintInfo("[AzerothAI] Persistent residents and faction cap enabled; hardcore="..tostring(AzerothAIHardcoreEnabled()))
|
||||
|
||||
@@ -54,7 +54,7 @@ local function attempt(r)
|
||||
if not a or not b then return false,'Resident offline' end
|
||||
if a:GetGUIDLow()~=r.a.guid or a:GetAccountId()~=r.a.account or b:GetGUIDLow()~=r.b.guid or b:GetAccountId()~=r.b.account then return nil,'Identity changed' end
|
||||
if not resident(a) or not resident(b) then return nil,'Resident retired or human-controlled' end
|
||||
if a:GetTeam()~=b:GetTeam() or math.abs(a:GetLevel()-b:GetLevel())>10 then return nil,'Faction or level mismatch' end
|
||||
if a:GetTeam()~=b:GetTeam() or math.abs(a:GetLevel()-b:GetLevel())>5 then return nil,'Faction or level mismatch' end
|
||||
if not available(a) or not available(b) then return false,'Waiting for combat, cast, or taxi to finish' end
|
||||
if a:GetMapId()~=b:GetMapId() or a:GetPhaseMask()~=b:GetPhaseMask() or a:GetDistance(b)>60 then return false,'Waiting for a nearby meeting within 60 yards' end
|
||||
local g=a:GetGroup()
|
||||
|
||||
@@ -51,9 +51,11 @@ local function dangerous(p,enemy)
|
||||
local rank=creature and creature:GetRank() or 0
|
||||
return enemy:GetLevel()>=p:GetLevel()+2 or rank==1 or rank==2 or rank==3
|
||||
end
|
||||
local function dangerousSelection(p)
|
||||
local function dangerousSelection(p,r,now)
|
||||
local target=p:GetSelection()
|
||||
if not target or not dangerous(p,target) then return false end
|
||||
if not target then return false end
|
||||
local key=tostring(target:GetGUID())
|
||||
if not dangerous(p,target) and not (r.avoidTargets and (r.avoidTargets[key] or 0)>now) then return false end
|
||||
for _,enemy in pairs(p:GetUnfriendlyUnitsInRange(40)) do
|
||||
if enemy:GetGUID()==target:GetGUID() then return true end
|
||||
end
|
||||
@@ -124,9 +126,12 @@ local function sample()
|
||||
local attackers=0;for _ in pairs(p:GetAttackers()) do attackers=attackers+1 end
|
||||
local enemy=p:GetVictim()
|
||||
local overmatched=dangerous(p,enemy)
|
||||
r.avoidTargets=r.avoidTargets or {}
|
||||
for key,expires in pairs(r.avoidTargets) do if expires<=now then r.avoidTargets[key]=nil end end
|
||||
if enemy and (overmatched or attackers>support+1 or falling and hp<80) then r.avoidTargets[tostring(enemy:GetGUID())]=now+600 end
|
||||
local outnumbered=attackers>support+1 and hp<80
|
||||
local unsafe=p:IsInCombat() and (hp<(support==0 and 55 or 50) or overmatched or outnumbered or falling and hp<80 or #peers>1 and weak>=math.max(2,math.ceil(#peers/2)))
|
||||
local avoid=not p:IsInCombat() and dangerousSelection(p)
|
||||
local avoid=not p:IsInCombat() and dangerousSelection(p,r,now)
|
||||
if unsafe then r.recoverUntil=now+15 end
|
||||
if hp<85 or mana<35 then r.resting=true end
|
||||
if hp>=95 and mana>=65 then r.resting=false end
|
||||
@@ -155,13 +160,13 @@ local function sample()
|
||||
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)>12 and p:GetDistance(leader)<200 and hp>=95 and mana>=65 and now-(r.followAt or 0)>=8 then
|
||||
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
|
||||
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>=80 then
|
||||
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)>25 and p:GetDistance(m)<200 then
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user