Expand auto-hunt pursuit ranges and show server bonus effects
This commit is contained in:
@@ -46,6 +46,7 @@ const BYTE c_byMobRankBoss = 4; // PAWN, S_PAWN, KNIGHT, S_KNIGHT, BOSS, KING
|
||||
const DWORD c_dwGoldVnum = 1;
|
||||
const float c_fAutoHuntPullCooldown = 10.0f;
|
||||
const float c_fAutoHuntPullReplyTimeout = 3.0f;
|
||||
const float c_fAutoHuntPullApproachTimeout = 8.0f;
|
||||
const int c_iAutoHuntGatherHPPercent = 50; // gather: stop pulling below this
|
||||
|
||||
// Trace: stderr with MT_AUTOHUNT_TRACE=1, logcat (tag mt_autohunt) on Android.
|
||||
@@ -130,7 +131,7 @@ bool CPythonPlayer::AutoHunt_IsGathering()
|
||||
|
||||
int CPythonPlayer::AutoHunt_GetGatheredCount()
|
||||
{
|
||||
return m_kSet_dwAutoHuntGathered.empty() ? 0 : m_iAutoHuntGatheredGroups;
|
||||
return AutoHunt_IsEnabled() ? m_iAutoHuntGatheredGroups : 0;
|
||||
}
|
||||
|
||||
bool CPythonPlayer::AutoHunt_HasNoPotion(bool isHP)
|
||||
@@ -147,8 +148,16 @@ const CPythonPlayer::SAutoHuntConfig& CPythonPlayer::AutoHunt_GetConfig()
|
||||
void CPythonPlayer::AutoHunt_SetConfig(const SAutoHuntConfig& c_rkConfig)
|
||||
{
|
||||
m_isAutoHuntConfigLoaded = true;
|
||||
if (AutoHunt_IsEnabled() && (m_kAutoHuntConfig.iGatherCount != c_rkConfig.iGatherCount ||
|
||||
m_kAutoHuntConfig.bAttackBoss != c_rkConfig.bAttackBoss || m_kAutoHuntConfig.iRange != c_rkConfig.iRange)) {
|
||||
__AutoHunt_CancelPull();
|
||||
++m_dwAutoHuntPullRequest; // invalidate replies for the previous selection
|
||||
m_isAutoHuntGathering = false;
|
||||
m_kSet_dwAutoHuntGathered.clear();
|
||||
m_iAutoHuntGatheredGroups = 0;
|
||||
}
|
||||
m_kAutoHuntConfig = c_rkConfig;
|
||||
m_kAutoHuntConfig.iRange = MINMAX(500, m_kAutoHuntConfig.iRange, 5000);
|
||||
m_kAutoHuntConfig.iRange = MINMAX(3500, m_kAutoHuntConfig.iRange, 15000);
|
||||
m_kAutoHuntConfig.iHPPercent = MINMAX(0, m_kAutoHuntConfig.iHPPercent, 95);
|
||||
m_kAutoHuntConfig.iSPPercent = MINMAX(0, m_kAutoHuntConfig.iSPPercent, 95);
|
||||
m_kAutoHuntConfig.iPickup = MINMAX(0, m_kAutoHuntConfig.iPickup, int(AUTO_HUNT_PICKUP_GOLD_EQUIP));
|
||||
@@ -199,8 +208,19 @@ void CPythonPlayer::__AutoHunt_SaveConfig()
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
void CPythonPlayer::__AutoHunt_CancelPull()
|
||||
{
|
||||
if (AutoHunt_IsEnabled() && m_kAutoHuntConfig.iGatherCount > 0) {
|
||||
char command[96];
|
||||
snprintf(command, sizeof(command), "/autohunt_pull 0 %d %d %u", m_kAutoHuntConfig.iRange,
|
||||
m_kAutoHuntConfig.bAttackBoss ? 1 : 0, unsigned(m_dwAutoHuntPullRequest));
|
||||
CPythonNetworkStream::Instance().SendChatPacket(command);
|
||||
}
|
||||
}
|
||||
|
||||
void CPythonPlayer::__AutoHunt_Stop(int eReason)
|
||||
{
|
||||
__AutoHunt_CancelPull();
|
||||
if (AUTO_HUNT_ENGAGE == m_eAutoHuntState || AUTO_HUNT_LOOT == m_eAutoHuntState || AUTO_HUNT_RETURN == m_eAutoHuntState)
|
||||
{
|
||||
__ClearAutoAttackTargetActorID();
|
||||
@@ -250,31 +270,56 @@ void CPythonPlayer::__AutoHunt_Engage(DWORD dwVID, float fNow)
|
||||
__SetAutoAttackTargetActorID(dwVID);
|
||||
}
|
||||
|
||||
// PORT: stones have their own option; their mob_proto rank must not count as a boss.
|
||||
bool CPythonPlayer::__AutoHunt_IsTargetAllowed(CInstanceBase& main, CInstanceBase& target)
|
||||
{
|
||||
if (&main == &target || target.IsDead() || target.IsInSafe() || !main.IsAttackableInstance(target))
|
||||
return false;
|
||||
if (target.IsStone()) return m_kAutoHuntConfig.bAttackStone;
|
||||
if (!target.IsEnemy()) return false;
|
||||
const auto* mob = CPythonNonPlayer::Instance().GetTable(target.GetRace());
|
||||
return m_kAutoHuntConfig.bAttackBoss || !mob || mob->bRank < c_byMobRankBoss;
|
||||
}
|
||||
|
||||
// PORT: cloak collection and the server pursuit leash follow the hunting range.
|
||||
bool CPythonPlayer::__AutoHunt_RequestPull(CInstanceBase& main, float now)
|
||||
{
|
||||
const int maxHP = GetStatus(POINT_MAX_HP);
|
||||
if (m_kAutoHuntConfig.iGatherCount <= 0 || m_isAutoHuntGathering || now < m_fAutoHuntNextPullTime ||
|
||||
main.IsInSafe() || maxHP <= 0 || GetStatus(POINT_HP) * 100 < maxHP * c_iAutoHuntGatherHPPercent)
|
||||
return false;
|
||||
if (m_kAutoHuntConfig.bAttackStone) {
|
||||
auto* target = NEW_FindActorPtr(__AutoHunt_FindTarget(main, now));
|
||||
if (target && target->IsStone()) return false;
|
||||
}
|
||||
const int radius = m_kAutoHuntConfig.iRange;
|
||||
char command[128];
|
||||
++m_dwAutoHuntPullRequest;
|
||||
snprintf(command, sizeof(command), "/autohunt_pull %d %d %d %u",
|
||||
m_kAutoHuntConfig.iGatherCount, radius, m_kAutoHuntConfig.bAttackBoss ? 1 : 0,
|
||||
unsigned(m_dwAutoHuntPullRequest));
|
||||
m_fAutoHuntNextPullTime = now + c_fAutoHuntPullCooldown;
|
||||
if (!CPythonNetworkStream::Instance().SendChatPacket(command)) return false;
|
||||
m_kSet_dwAutoHuntGathered.clear();
|
||||
m_iAutoHuntGatheredGroups = 0;
|
||||
m_isAutoHuntGathering = true;
|
||||
m_fAutoHuntGatherStartTime = now;
|
||||
AutoHuntTrace("cloak request: %d groups, radius %d", m_kAutoHuntConfig.iGatherCount, radius);
|
||||
return true;
|
||||
}
|
||||
|
||||
DWORD CPythonPlayer::__AutoHunt_FindTarget(CInstanceBase& rkInstMain, float fNow)
|
||||
{
|
||||
CPythonCharacterManager& rkChrMgr = CPythonCharacterManager::Instance();
|
||||
CPythonNonPlayer& rkNonPlayer = CPythonNonPlayer::Instance();
|
||||
|
||||
DWORD dwBestVID = 0;
|
||||
float fBestDistance = 1e9f;
|
||||
bool bestStone = false;
|
||||
for (CPythonCharacterManager::CharacterIterator i = rkChrMgr.CharacterInstanceBegin(); i != rkChrMgr.CharacterInstanceEnd(); ++i)
|
||||
{
|
||||
CInstanceBase* pkInst = *i;
|
||||
if (!pkInst || pkInst == &rkInstMain || pkInst->IsDead())
|
||||
continue;
|
||||
|
||||
if (!pkInst->IsEnemy() && !(m_kAutoHuntConfig.bAttackStone && pkInst->IsStone()))
|
||||
continue;
|
||||
|
||||
if (!rkInstMain.IsAttackableInstance(*pkInst) || pkInst->IsInSafe())
|
||||
continue;
|
||||
|
||||
if (!m_kAutoHuntConfig.bAttackBoss)
|
||||
{
|
||||
const CPythonNonPlayer::TMobTable* pMobTable = rkNonPlayer.GetTable(pkInst->GetRace());
|
||||
if (pMobTable && pMobTable->bRank >= c_byMobRankBoss)
|
||||
continue;
|
||||
}
|
||||
if (!pkInst || !__AutoHunt_IsTargetAllowed(rkInstMain, *pkInst))
|
||||
continue;
|
||||
|
||||
std::map<DWORD, float>::iterator f = m_kMap_dwAutoHuntSkipVID.find(pkInst->GetVirtualID());
|
||||
if (f != m_kMap_dwAutoHuntSkipVID.end() && fNow < f->second)
|
||||
@@ -286,8 +331,10 @@ DWORD CPythonPlayer::__AutoHunt_FindTarget(CInstanceBase& rkInstMain, float fNow
|
||||
continue;
|
||||
|
||||
const float fDistance = rkInstMain.GetDistance(pkInst);
|
||||
if (fDistance < fBestDistance)
|
||||
const bool stone = pkInst->IsStone();
|
||||
if ((stone && !bestStone) || (stone == bestStone && fDistance < fBestDistance))
|
||||
{
|
||||
bestStone = stone;
|
||||
fBestDistance = fDistance;
|
||||
dwBestVID = pkInst->GetVirtualID();
|
||||
}
|
||||
@@ -303,7 +350,7 @@ DWORD CPythonPlayer::__AutoHunt_FindGatheredTarget(CInstanceBase& rkInstMain)
|
||||
for (DWORD dwVID : m_kSet_dwAutoHuntGathered)
|
||||
{
|
||||
CInstanceBase* pkInst = NEW_FindActorPtr(dwVID);
|
||||
if (!pkInst || pkInst->IsDead() || pkInst->IsInSafe() || !rkInstMain.IsAttackableInstance(*pkInst))
|
||||
if (!pkInst || !__AutoHunt_IsTargetAllowed(rkInstMain, *pkInst))
|
||||
continue;
|
||||
const auto skipped = m_kMap_dwAutoHuntSkipVID.find(dwVID);
|
||||
if (skipped != m_kMap_dwAutoHuntSkipVID.end() && CTimer::Instance().GetCurrentSecond() < skipped->second)
|
||||
@@ -324,7 +371,8 @@ void CPythonPlayer::__AutoHunt_PurgeGathered()
|
||||
for (std::set<DWORD>::iterator i = m_kSet_dwAutoHuntGathered.begin(); i != m_kSet_dwAutoHuntGathered.end();)
|
||||
{
|
||||
CInstanceBase* pkInst = NEW_FindActorPtr(*i);
|
||||
if (!pkInst || pkInst->IsDead())
|
||||
if ((pkInst && pkInst->IsDead()) ||
|
||||
(!pkInst && CTimer::Instance().GetCurrentSecond() - m_fAutoHuntGatherStartTime >= std::max(c_fAutoHuntPullApproachTimeout, m_kAutoHuntConfig.iRange / 100.0f)))
|
||||
i = m_kSet_dwAutoHuntGathered.erase(i);
|
||||
else
|
||||
++i;
|
||||
@@ -622,6 +670,11 @@ void CPythonPlayer::__Update_AutoHunt()
|
||||
|
||||
const float fNow = CTimer::Instance().GetCurrentSecond();
|
||||
__AutoHunt_UpdatePotion(rkInstMain, fNow);
|
||||
if (m_isAutoHuntGathering && fNow - m_fAutoHuntGatherStartTime >= c_fAutoHuntPullReplyTimeout) {
|
||||
m_isAutoHuntGathering = false;
|
||||
AutoHuntTrace("cloak pull reply timed out; continue ordinary hunt");
|
||||
}
|
||||
__AutoHunt_PurgeGathered();
|
||||
|
||||
if (IsOpenPrivateShop() || IsObserverMode() || rkInstMain.IsFishingMode())
|
||||
return;
|
||||
@@ -646,45 +699,33 @@ void CPythonPlayer::__Update_AutoHunt()
|
||||
return;
|
||||
m_fAutoHuntNextActionTime = fNow + 0.25f;
|
||||
|
||||
// A cloak-style pull stays in place while the server starts the nearby groups.
|
||||
if (m_isAutoHuntGathering) {
|
||||
if (fNow - m_fAutoHuntGatherStartTime < c_fAutoHuntPullReplyTimeout) return;
|
||||
m_isAutoHuntGathering = false;
|
||||
m_kSet_dwAutoHuntGathered.clear();
|
||||
AutoHuntTrace("cloak pull reply timed out; continue ordinary hunt");
|
||||
if (m_isAutoHuntGathering) return;
|
||||
const DWORD preferred = __AutoHunt_FindTarget(rkInstMain, fNow);
|
||||
CInstanceBase* preferredActor = NEW_FindActorPtr(preferred);
|
||||
if (preferredActor && preferredActor->IsStone()) {
|
||||
__AutoHunt_Engage(preferred, fNow);
|
||||
return;
|
||||
}
|
||||
if (__AutoHunt_RequestPull(rkInstMain, fNow)) {
|
||||
__ClearAutoAttackTargetActorID();
|
||||
__ClearReservedAction();
|
||||
rkInstMain.NEW_Stop();
|
||||
return;
|
||||
}
|
||||
__AutoHunt_PurgeGathered();
|
||||
const int maxHP = GetStatus(POINT_MAX_HP);
|
||||
if (m_kAutoHuntConfig.iGatherCount > 0 && m_kSet_dwAutoHuntGathered.empty() &&
|
||||
fNow >= m_fAutoHuntNextPullTime && !rkInstMain.IsInSafe() &&
|
||||
maxHP > 0 && GetStatus(POINT_HP) * 100 >= maxHP * c_iAutoHuntGatherHPPercent &&
|
||||
__AutoHunt_FindTarget(rkInstMain, fNow)) {
|
||||
char command[128];
|
||||
++m_dwAutoHuntPullRequest;
|
||||
snprintf(command, sizeof(command), "/autohunt_pull %d %d %d %u",
|
||||
m_kAutoHuntConfig.iGatherCount, m_kAutoHuntConfig.iRange,
|
||||
m_kAutoHuntConfig.bAttackBoss ? 1 : 0, unsigned(m_dwAutoHuntPullRequest));
|
||||
m_fAutoHuntNextPullTime = fNow + c_fAutoHuntPullCooldown;
|
||||
if (CPythonNetworkStream::Instance().SendChatPacket(command)) {
|
||||
__ClearAutoAttackTargetActorID();
|
||||
__ClearReservedAction();
|
||||
rkInstMain.NEW_Stop();
|
||||
m_isAutoHuntGathering = true;
|
||||
m_iAutoHuntGatheredGroups = 0;
|
||||
m_fAutoHuntGatherStartTime = fNow;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (DWORD dwVID = __AutoHunt_FindGatheredTarget(rkInstMain))
|
||||
{
|
||||
CInstanceBase* pulled = NEW_FindActorPtr(dwVID);
|
||||
if (pulled && rkInstMain.GetDistance(pulled) > c_fAutoHuntSkillDistance &&
|
||||
fNow - m_fAutoHuntGatherStartTime < 8.0f && GetStatus(POINT_HP) * 100 >= maxHP * c_iAutoHuntGatherHPPercent)
|
||||
fNow - m_fAutoHuntGatherStartTime < c_fAutoHuntPullApproachTimeout && GetStatus(POINT_HP) * 100 >= maxHP * c_iAutoHuntGatherHPPercent)
|
||||
return; // let the server-controlled monsters approach the stationary player
|
||||
__AutoHunt_Engage(dwVID, fNow);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_kSet_dwAutoHuntGathered.empty() &&
|
||||
fNow - m_fAutoHuntGatherStartTime < c_fAutoHuntPullApproachTimeout) return;
|
||||
|
||||
if (DWORD dwVID = __AutoHunt_FindTarget(rkInstMain, fNow))
|
||||
{
|
||||
__AutoHunt_Engage(dwVID, fNow);
|
||||
@@ -742,7 +783,7 @@ void CPythonPlayer::__Update_AutoHunt()
|
||||
case AUTO_HUNT_ENGAGE:
|
||||
{
|
||||
CInstanceBase* pkInstVictim = NEW_FindActorPtr(m_dwAutoHuntTargetVID);
|
||||
if (!pkInstVictim || pkInstVictim->IsDead())
|
||||
if (!pkInstVictim || !__AutoHunt_IsTargetAllowed(rkInstMain, *pkInstVictim))
|
||||
{
|
||||
__ClearAutoAttackTargetActorID();
|
||||
m_dwAutoHuntTargetVID = 0;
|
||||
@@ -756,6 +797,17 @@ void CPythonPlayer::__Update_AutoHunt()
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_kAutoHuntConfig.bAttackStone && !pkInstVictim->IsStone()) {
|
||||
const DWORD preferred = __AutoHunt_FindTarget(rkInstMain, fNow);
|
||||
CInstanceBase* actor = NEW_FindActorPtr(preferred);
|
||||
if (actor && actor->IsStone()) {
|
||||
__AutoHunt_Engage(preferred, fNow);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Keep the configured pack supplied even while attacking, without interrupting it.
|
||||
if (!pkInstVictim->IsStone()) __AutoHunt_RequestPull(rkInstMain, fNow);
|
||||
|
||||
const float fDistance = rkInstMain.GetDistance(pkInstVictim);
|
||||
if (fDistance < m_fAutoHuntBestDistance - 30.0f)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user