issue-6: add autopickup filter system

This commit is contained in:
server
2026-04-16 19:28:16 +02:00
parent d935f92975
commit 9f5851dac6
6 changed files with 259 additions and 0 deletions

View File

@@ -214,6 +214,7 @@ void CHARACTER::Initialize()
m_pkPoisonEvent = NULL;
m_pkFireEvent = NULL;
m_pkCheckSpeedHackEvent = NULL;
m_pkAutoPickupEvent = NULL;
m_speed_hack_count = 0;
m_pkAffectEvent = NULL;
@@ -561,6 +562,7 @@ void CHARACTER::Destroy()
// MINING
event_cancel(&m_pkMiningEvent);
// END_OF_MINING
event_cancel(&m_pkAutoPickupEvent);
for (itertype(m_mapMobSkillEvent) it = m_mapMobSkillEvent.begin(); it != m_mapMobSkillEvent.end(); ++it)
@@ -6476,6 +6478,63 @@ void CHARACTER::SetBlockModeForce(BYTE bFlag)
ChatPacket(CHAT_TYPE_COMMAND, "setblockmode %d", m_pointsInstant.bBlockMode);
}
bool CHARACTER::IsAutoPickupEnabled() const
{
return GetQuestFlag("autopickup.enabled") > 0;
}
int CHARACTER::GetAutoPickupFilterMode() const
{
return GetQuestFlag("autopickup.mode") > 0 ? 1 : 0;
}
int CHARACTER::GetAutoPickupFilterMask() const
{
return GetQuestFlag("autopickup.mask") & 31;
}
bool CHARACTER::HasAutoPickupVip() const
{
return GetPremiumRemainSeconds(PREMIUM_AUTOLOOT) > 0 || IsEquipUniqueGroup(UNIQUE_GROUP_AUTOLOOT);
}
int CHARACTER::GetAutoPickupRadius() const
{
return HasAutoPickupVip() ? 300 : 220;
}
void CHARACTER::SendAutoPickupState()
{
ChatPacket(CHAT_TYPE_COMMAND,
"AutoPickupState %d %d %d %d",
IsAutoPickupEnabled() ? 1 : 0,
GetAutoPickupFilterMode(),
GetAutoPickupFilterMask(),
HasAutoPickupVip() ? 1 : 0);
}
void CHARACTER::StopAutoPickupEvent()
{
event_cancel(&m_pkAutoPickupEvent);
}
void CHARACTER::RefreshAutoPickup()
{
if (GetQuestFlag("autopickup.initialized") <= 0)
{
SetQuestFlag("autopickup.initialized", 1);
SetQuestFlag("autopickup.mode", 0);
SetQuestFlag("autopickup.mask", 31);
}
if (IsAutoPickupEnabled())
StartAutoPickupEvent();
else
StopAutoPickupEvent();
SendAutoPickupState();
}
bool CHARACTER::IsGuardNPC() const
{
return IsNPC() && (GetRaceNum() == 11000 || GetRaceNum() == 11002 || GetRaceNum() == 11004);

View File

@@ -749,6 +749,16 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
void SetBlockMode(BYTE bFlag);
void SetBlockModeForce(BYTE bFlag);
bool IsBlockMode(BYTE bFlag) const { return (m_pointsInstant.bBlockMode & bFlag)?true:false; }
void SendAutoPickupState();
void RefreshAutoPickup();
void StartAutoPickupEvent();
void StopAutoPickupEvent();
bool IsAutoPickupEnabled() const;
int GetAutoPickupFilterMode() const;
int GetAutoPickupFilterMask() const;
bool HasAutoPickupVip() const;
int GetAutoPickupRadius() const;
bool ShouldAutoPickupItem(LPITEM item) const;
bool IsPolymorphed() const { return m_dwPolymorphRace>0; }
bool IsPolyMaintainStat() const { return m_bPolyMaintainStat; } // 이전 스텟을 유지하는 폴리모프.
@@ -1774,6 +1784,7 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
LPEVENT m_pkWarpEvent;
LPEVENT m_pkCheckSpeedHackEvent;
LPEVENT m_pkDestroyWhenIdleEvent;
LPEVENT m_pkAutoPickupEvent;
LPEVENT m_pkPetSystemUpdateEvent;
bool IsWarping() const { return m_pkWarpEvent ? true : false; }

View File

@@ -46,6 +46,7 @@
namespace
{
constexpr int kRefineNpcMaxDistance = 2000;
const int kAutoPickupTick = passes_per_sec / 4;
constexpr DWORD kPickupPatternWindowMs = 8 * 60 * 1000;
constexpr int kPickupPatternMinSamples = 12;
constexpr int kPickupPatternMaxIntervalSpreadMs = 180;
@@ -102,6 +103,117 @@ namespace
return true;
}
enum EAutoPickupFilterBits
{
AUTO_PICKUP_FILTER_WEAPON = 1 << 0,
AUTO_PICKUP_FILTER_ARMOR = 1 << 1,
AUTO_PICKUP_FILTER_YANG = 1 << 2,
AUTO_PICKUP_FILTER_STONE = 1 << 3,
AUTO_PICKUP_FILTER_MATERIAL = 1 << 4,
};
int GetAutoPickupFilterBit(LPITEM item)
{
if (!item)
return 0;
switch (item->GetType())
{
case ITEM_WEAPON:
return AUTO_PICKUP_FILTER_WEAPON;
case ITEM_ARMOR:
return AUTO_PICKUP_FILTER_ARMOR;
case ITEM_ELK:
return AUTO_PICKUP_FILTER_YANG;
case ITEM_METIN:
return AUTO_PICKUP_FILTER_STONE;
case ITEM_MATERIAL:
return AUTO_PICKUP_FILTER_MATERIAL;
case ITEM_RESOURCE:
if (item->GetSubType() == RESOURCE_STONE || item->GetSubType() == RESOURCE_METIN)
return AUTO_PICKUP_FILTER_STONE;
return AUTO_PICKUP_FILTER_MATERIAL;
}
return 0;
}
struct AutoPickupScan
{
LPCHARACTER ch;
LPITEM bestItem;
int bestDistance;
int radius;
AutoPickupScan(LPCHARACTER character, int maxDistance)
: ch(character), bestItem(NULL), bestDistance(maxDistance + 1), radius(maxDistance)
{
}
void operator () (LPENTITY entity)
{
if (!entity || !entity->IsType(ENTITY_ITEM))
return;
LPITEM item = static_cast<LPITEM>(entity);
if (!item || !item->GetSectree() || !item->IsOwnership(ch))
return;
if (!ch->ShouldAutoPickupItem(item))
return;
const int distance = DISTANCE_APPROX(item->GetX() - ch->GetX(), item->GetY() - ch->GetY());
if (distance > radius || distance >= bestDistance)
return;
bestDistance = distance;
bestItem = item;
}
};
EVENTFUNC(autopickup_event)
{
char_event_info* info = dynamic_cast<char_event_info*>(event->info);
if (!info)
{
sys_err("autopickup_event> <Factor> Null pointer");
return 0;
}
LPCHARACTER ch = info->ch;
if (!ch)
return 0;
if (!ch->GetSectree() || !ch->IsPC() || ch->IsDead())
{
ch->m_pkAutoPickupEvent = NULL;
return 0;
}
if (!ch->IsAutoPickupEnabled())
{
ch->m_pkAutoPickupEvent = NULL;
return 0;
}
if (!ch->CanHandleItem() || ch->IsObserverMode())
return MAX(1, kAutoPickupTick);
AutoPickupScan scan(ch, ch->GetAutoPickupRadius());
ch->GetSectree()->ForEachAround(scan);
if (scan.bestItem)
ch->PickupItem(scan.bestItem->GetVID());
return MAX(1, kAutoPickupTick);
}
void ResetPickupPatternWindow(LPCHARACTER ch, DWORD now, long x, long y)
{
ch->m_dwPickupPatternWindowStart = now;
@@ -6222,6 +6334,30 @@ bool CHARACTER::PickupItem(DWORD dwVID)
return false;
}
bool CHARACTER::ShouldAutoPickupItem(LPITEM item) const
{
const int mask = GetAutoPickupFilterMask();
const int filterBit = GetAutoPickupFilterBit(item);
if (GetAutoPickupFilterMode() == 0)
return filterBit != 0 && (mask & filterBit) != 0;
if (filterBit == 0)
return true;
return (mask & filterBit) == 0;
}
void CHARACTER::StartAutoPickupEvent()
{
if (m_pkAutoPickupEvent || !IsPC() || !IsAutoPickupEnabled())
return;
char_event_info* info = AllocEventInfo<char_event_info>();
info->ch = this;
m_pkAutoPickupEvent = event_create(autopickup_event, info, MAX(1, kAutoPickupTick));
}
bool CHARACTER::SwapItem(BYTE bCell, BYTE bDestCell)
{
if (!CanHandleItem())

View File

@@ -209,6 +209,7 @@ ACMD(do_dice);
ACMD(do_special_item);
ACMD(do_biolog_submit);
ACMD(do_teleport_system);
ACMD(do_autopickup);
ACMD(do_click_mall);
@@ -467,6 +468,7 @@ struct command_info cmd_info[] =
{ "cube", do_cube, 0, POS_DEAD, GM_PLAYER },
{ "biolog_submit", do_biolog_submit, 0, POS_DEAD, GM_PLAYER },
{ "teleport_system", do_teleport_system, 0, POS_DEAD, GM_PLAYER },
{ "autopickup", do_autopickup, 0, POS_DEAD, GM_PLAYER },
{ "siege", do_siege, 0, POS_DEAD, GM_LOW_WIZARD },
{ "temp", do_temp, 0, POS_DEAD, GM_IMPLEMENTOR },
{ "frog", do_frog, 0, POS_DEAD, GM_HIGH_WIZARD },

View File

@@ -1913,6 +1913,56 @@ ACMD(do_teleport_system)
quest::CQuestManager::instance().QuestButton(ch->GetPlayerID(), questIndex);
}
ACMD(do_autopickup)
{
char arg1[256];
char arg2[256];
argument = one_argument(argument, arg1, sizeof(arg1));
one_argument(argument, arg2, sizeof(arg2));
if (!*arg1 || !strcmp(arg1, "sync"))
{
ch->SendAutoPickupState();
return;
}
if (!strcmp(arg1, "enable"))
{
int enabled = 0;
str_to_number(enabled, arg2);
ch->SetQuestFlag("autopickup.enabled", enabled > 0 ? 1 : 0);
ch->RefreshAutoPickup();
return;
}
if (!strcmp(arg1, "mode"))
{
int mode = 0;
if (!strcmp(arg2, "blacklist"))
mode = 1;
else if (strcmp(arg2, "whitelist"))
{
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Unknown autopickup mode."));
return;
}
ch->SetQuestFlag("autopickup.mode", mode);
ch->SendAutoPickupState();
return;
}
if (!strcmp(arg1, "mask"))
{
int mask = 0;
str_to_number(mask, arg2);
ch->SetQuestFlag("autopickup.mask", MAX(0, MIN(31, mask)));
ch->SendAutoPickupState();
return;
}
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Usage: /autopickup [sync|enable|mode|mask]"));
}
ACMD(do_in_game_mall)
{
if (LC_IsYMIR() == true || LC_IsKorea() == true)

View File

@@ -552,6 +552,7 @@ void CInputLogin::Entergame(LPDESC d, const char * data)
ch->StartSaveEvent();
ch->StartRecoveryEvent();
ch->StartCheckSpeedHackEvent();
ch->RefreshAutoPickup();
CPVPManager::instance().Connect(ch);
CPVPManager::instance().SendList(d);