issue-6: add autopickup filter system
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user