issue-8: add switchbot automation
This commit is contained in:
@@ -216,6 +216,7 @@ void CHARACTER::Initialize()
|
||||
m_pkCheckSpeedHackEvent = NULL;
|
||||
m_pkAutoPickupEvent = NULL;
|
||||
m_pkStoneQueueEvent = NULL;
|
||||
m_pkSwitchbotEvent = NULL;
|
||||
m_speed_hack_count = 0;
|
||||
|
||||
m_pkAffectEvent = NULL;
|
||||
@@ -311,6 +312,15 @@ void CHARACTER::Initialize()
|
||||
m_iStoneQueueSuccessCount = 0;
|
||||
m_iStoneQueueFailCount = 0;
|
||||
m_vecStoneQueueSlots.clear();
|
||||
for (size_t i = 0; i < m_switchbotSlots.size(); ++i)
|
||||
{
|
||||
m_switchbotSlots[i].active = false;
|
||||
m_switchbotSlots[i].itemCell = 0;
|
||||
m_switchbotSlots[i].attrType = 0;
|
||||
m_switchbotSlots[i].minValue = 0;
|
||||
m_switchbotSlots[i].attempts = 0;
|
||||
}
|
||||
m_iSwitchbotSpeedIndex = 1;
|
||||
|
||||
m_dwPolymorphRace = 0;
|
||||
|
||||
@@ -570,6 +580,7 @@ void CHARACTER::Destroy()
|
||||
// END_OF_MINING
|
||||
event_cancel(&m_pkAutoPickupEvent);
|
||||
event_cancel(&m_pkStoneQueueEvent);
|
||||
event_cancel(&m_pkSwitchbotEvent);
|
||||
|
||||
|
||||
for (itertype(m_mapMobSkillEvent) it = m_mapMobSkillEvent.begin(); it != m_mapMobSkillEvent.end(); ++it)
|
||||
@@ -6545,6 +6556,8 @@ void CHARACTER::RefreshAutoPickup()
|
||||
namespace
|
||||
{
|
||||
const int kStoneQueueTick = PASSES_PER_SEC(1);
|
||||
const int kSwitchbotSlotMax = 5;
|
||||
const int kSwitchbotSpeedSeconds[] = { 3, 2, 1 };
|
||||
enum
|
||||
{
|
||||
HYUNIRON_CHN = 1,
|
||||
@@ -6595,6 +6608,61 @@ namespace
|
||||
|
||||
return ch->ProcessStoneQueueTick() ? kStoneQueueTick : 0;
|
||||
}
|
||||
|
||||
bool IsSwitchbotScroll(LPITEM item)
|
||||
{
|
||||
if (!item || item->GetType() != ITEM_USE)
|
||||
return false;
|
||||
|
||||
return item->GetSubType() == USE_CHANGE_ATTRIBUTE || item->GetSubType() == USE_CHANGE_ATTRIBUTE2;
|
||||
}
|
||||
|
||||
bool IsSwitchbotEligibleItem(LPITEM item)
|
||||
{
|
||||
if (!item)
|
||||
return false;
|
||||
|
||||
if (item->IsEquipped() || item->IsExchanging())
|
||||
return false;
|
||||
|
||||
if (item->GetType() == ITEM_COSTUME)
|
||||
return false;
|
||||
|
||||
if (item->GetAttributeSetIndex() == -1 || item->GetAttributeCount() == 0)
|
||||
return false;
|
||||
|
||||
return item->GetType() == ITEM_WEAPON || item->GetType() == ITEM_ARMOR;
|
||||
}
|
||||
|
||||
bool IsSwitchbotTargetReached(LPITEM item, BYTE attrType, short minValue)
|
||||
{
|
||||
if (!item || attrType == 0 || minValue <= 0)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < ITEM_ATTRIBUTE_MAX_NUM; ++i)
|
||||
{
|
||||
if (item->GetAttributeType(i) == attrType && item->GetAttributeValue(i) >= minValue)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
EVENTFUNC(switchbot_event)
|
||||
{
|
||||
char_event_info* info = dynamic_cast<char_event_info*>(event->info);
|
||||
if (!info)
|
||||
{
|
||||
sys_err("switchbot_event> <Factor> Null pointer");
|
||||
return 0;
|
||||
}
|
||||
|
||||
LPCHARACTER ch = info->ch;
|
||||
if (!ch)
|
||||
return 0;
|
||||
|
||||
return ch->ProcessSwitchbotTick() ? PASSES_PER_SEC(ch->GetSwitchbotSpeedSeconds()) : 0;
|
||||
}
|
||||
}
|
||||
|
||||
int CHARACTER::GetStoneQueueMax() const
|
||||
@@ -6702,6 +6770,219 @@ bool CHARACTER::ProcessStoneQueueTick()
|
||||
return true;
|
||||
}
|
||||
|
||||
int CHARACTER::GetSwitchbotActiveCount() const
|
||||
{
|
||||
int activeCount = 0;
|
||||
|
||||
for (size_t i = 0; i < m_switchbotSlots.size(); ++i)
|
||||
{
|
||||
if (m_switchbotSlots[i].active)
|
||||
++activeCount;
|
||||
}
|
||||
|
||||
return activeCount;
|
||||
}
|
||||
|
||||
int CHARACTER::GetSwitchbotSpeedSeconds() const
|
||||
{
|
||||
const int speedIndexMax = sizeof(kSwitchbotSpeedSeconds) / sizeof(kSwitchbotSpeedSeconds[0]);
|
||||
const int safeIndex = MINMAX(0, m_iSwitchbotSpeedIndex, speedIndexMax - 1);
|
||||
return kSwitchbotSpeedSeconds[safeIndex];
|
||||
}
|
||||
|
||||
void CHARACTER::SendSwitchbotState()
|
||||
{
|
||||
int switchItemCount = 0;
|
||||
for (int i = 0; i < INVENTORY_MAX_NUM; ++i)
|
||||
{
|
||||
LPITEM item = GetInventoryItem(i);
|
||||
if (!IsSwitchbotScroll(item))
|
||||
continue;
|
||||
|
||||
switchItemCount += item->GetCount();
|
||||
}
|
||||
|
||||
const int activeCount = GetSwitchbotActiveCount();
|
||||
const int etaSeconds = activeCount > 0 ? (switchItemCount * GetSwitchbotSpeedSeconds()) / activeCount : 0;
|
||||
|
||||
ChatPacket(CHAT_TYPE_COMMAND, "SwitchbotState %d %d %d %d", m_iSwitchbotSpeedIndex, activeCount, switchItemCount, etaSeconds);
|
||||
|
||||
for (size_t i = 0; i < m_switchbotSlots.size(); ++i)
|
||||
{
|
||||
const TSwitchbotSlotState& slot = m_switchbotSlots[i];
|
||||
ChatPacket(
|
||||
CHAT_TYPE_COMMAND,
|
||||
"SwitchbotSlot %d %d %d %d %d %d",
|
||||
static_cast<int>(i),
|
||||
slot.active ? 1 : 0,
|
||||
slot.itemCell,
|
||||
slot.attrType,
|
||||
slot.minValue,
|
||||
slot.attempts);
|
||||
}
|
||||
}
|
||||
|
||||
void CHARACTER::SetSwitchbotSpeed(int speedIndex)
|
||||
{
|
||||
const int speedIndexMax = sizeof(kSwitchbotSpeedSeconds) / sizeof(kSwitchbotSpeedSeconds[0]);
|
||||
m_iSwitchbotSpeedIndex = MINMAX(0, speedIndex, speedIndexMax - 1);
|
||||
|
||||
if (m_pkSwitchbotEvent)
|
||||
{
|
||||
event_cancel(&m_pkSwitchbotEvent);
|
||||
|
||||
if (GetSwitchbotActiveCount() > 0)
|
||||
{
|
||||
char_event_info* info = AllocEventInfo<char_event_info>();
|
||||
info->ch = this;
|
||||
m_pkSwitchbotEvent = event_create(switchbot_event, info, PASSES_PER_SEC(GetSwitchbotSpeedSeconds()));
|
||||
}
|
||||
}
|
||||
|
||||
SendSwitchbotState();
|
||||
}
|
||||
|
||||
bool CHARACTER::StartSwitchbotSlot(int switchbotSlot, BYTE itemCell, BYTE attrType, short minValue)
|
||||
{
|
||||
if (switchbotSlot < 0 || switchbotSlot >= kSwitchbotSlotMax)
|
||||
return false;
|
||||
|
||||
LPITEM item = GetInventoryItem(itemCell);
|
||||
if (!IsSwitchbotEligibleItem(item))
|
||||
return false;
|
||||
|
||||
if (attrType == 0 || minValue <= 0)
|
||||
return false;
|
||||
|
||||
TSwitchbotSlotState& slot = m_switchbotSlots[switchbotSlot];
|
||||
slot.active = true;
|
||||
slot.itemCell = itemCell;
|
||||
slot.attrType = attrType;
|
||||
slot.minValue = minValue;
|
||||
slot.attempts = 0;
|
||||
|
||||
if (!m_pkSwitchbotEvent)
|
||||
{
|
||||
char_event_info* info = AllocEventInfo<char_event_info>();
|
||||
info->ch = this;
|
||||
m_pkSwitchbotEvent = event_create(switchbot_event, info, PASSES_PER_SEC(GetSwitchbotSpeedSeconds()));
|
||||
}
|
||||
|
||||
SendSwitchbotState();
|
||||
return true;
|
||||
}
|
||||
|
||||
void CHARACTER::StopSwitchbotSlot(int switchbotSlot, bool clearConfig)
|
||||
{
|
||||
if (switchbotSlot < 0 || switchbotSlot >= kSwitchbotSlotMax)
|
||||
return;
|
||||
|
||||
TSwitchbotSlotState& slot = m_switchbotSlots[switchbotSlot];
|
||||
slot.active = false;
|
||||
|
||||
if (clearConfig)
|
||||
{
|
||||
slot.itemCell = 0;
|
||||
slot.attrType = 0;
|
||||
slot.minValue = 0;
|
||||
slot.attempts = 0;
|
||||
}
|
||||
|
||||
if (GetSwitchbotActiveCount() == 0)
|
||||
event_cancel(&m_pkSwitchbotEvent);
|
||||
|
||||
SendSwitchbotState();
|
||||
}
|
||||
|
||||
void CHARACTER::StopAllSwitchbotSlots(bool clearConfig)
|
||||
{
|
||||
for (size_t i = 0; i < m_switchbotSlots.size(); ++i)
|
||||
{
|
||||
m_switchbotSlots[i].active = false;
|
||||
|
||||
if (clearConfig)
|
||||
{
|
||||
m_switchbotSlots[i].itemCell = 0;
|
||||
m_switchbotSlots[i].attrType = 0;
|
||||
m_switchbotSlots[i].minValue = 0;
|
||||
m_switchbotSlots[i].attempts = 0;
|
||||
}
|
||||
}
|
||||
|
||||
event_cancel(&m_pkSwitchbotEvent);
|
||||
SendSwitchbotState();
|
||||
}
|
||||
|
||||
bool CHARACTER::ProcessSwitchbotTick()
|
||||
{
|
||||
if (!IsPC() || IsDead())
|
||||
{
|
||||
StopAllSwitchbotSlots();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool stateChanged = false;
|
||||
|
||||
for (size_t i = 0; i < m_switchbotSlots.size(); ++i)
|
||||
{
|
||||
TSwitchbotSlotState& slot = m_switchbotSlots[i];
|
||||
if (!slot.active)
|
||||
continue;
|
||||
|
||||
LPITEM targetItem = GetInventoryItem(slot.itemCell);
|
||||
if (!IsSwitchbotEligibleItem(targetItem))
|
||||
{
|
||||
slot.active = false;
|
||||
stateChanged = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsSwitchbotTargetReached(targetItem, slot.attrType, slot.minValue))
|
||||
{
|
||||
slot.active = false;
|
||||
stateChanged = true;
|
||||
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Switchbot slot %d finished."), static_cast<int>(i) + 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
int switchItemCell = -1;
|
||||
for (int cell = 0; cell < INVENTORY_MAX_NUM; ++cell)
|
||||
{
|
||||
LPITEM switchItem = GetInventoryItem(cell);
|
||||
if (!IsSwitchbotScroll(switchItem))
|
||||
continue;
|
||||
|
||||
switchItemCell = cell;
|
||||
break;
|
||||
}
|
||||
|
||||
if (switchItemCell < 0)
|
||||
{
|
||||
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("No switch items left."));
|
||||
StopAllSwitchbotSlots();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!UseItem(TItemPos(INVENTORY, switchItemCell), TItemPos(INVENTORY, slot.itemCell)))
|
||||
{
|
||||
slot.active = false;
|
||||
stateChanged = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
++slot.attempts;
|
||||
stateChanged = true;
|
||||
}
|
||||
|
||||
if (GetSwitchbotActiveCount() == 0)
|
||||
event_cancel(&m_pkSwitchbotEvent);
|
||||
|
||||
if (stateChanged)
|
||||
SendSwitchbotState();
|
||||
|
||||
return m_pkSwitchbotEvent != NULL;
|
||||
}
|
||||
|
||||
bool CHARACTER::IsGuardNPC() const
|
||||
{
|
||||
return IsNPC() && (GetRaceNum() == 11000 || GetRaceNum() == 11002 || GetRaceNum() == 11004);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef __INC_METIN_II_CHAR_H__
|
||||
#define __INC_METIN_II_CHAR_H__
|
||||
|
||||
#include <array>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "common/stl.h"
|
||||
@@ -765,6 +766,14 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
|
||||
void CancelStoneQueue(bool resetResults = true);
|
||||
void OnStoneQueueRefineResult(bool success);
|
||||
bool ProcessStoneQueueTick();
|
||||
int GetSwitchbotActiveCount() const;
|
||||
int GetSwitchbotSpeedSeconds() const;
|
||||
void SendSwitchbotState();
|
||||
void SetSwitchbotSpeed(int speedIndex);
|
||||
bool StartSwitchbotSlot(int switchbotSlot, BYTE itemCell, BYTE attrType, short minValue);
|
||||
void StopSwitchbotSlot(int switchbotSlot, bool clearConfig = false);
|
||||
void StopAllSwitchbotSlots(bool clearConfig = false);
|
||||
bool ProcessSwitchbotTick();
|
||||
|
||||
bool IsPolymorphed() const { return m_dwPolymorphRace>0; }
|
||||
bool IsPolyMaintainStat() const { return m_bPolyMaintainStat; } // 이전 스텟을 유지하는 폴리모프.
|
||||
@@ -1190,6 +1199,16 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
|
||||
int m_iStoneQueueCurrentIndex;
|
||||
int m_iStoneQueueSuccessCount;
|
||||
int m_iStoneQueueFailCount;
|
||||
struct TSwitchbotSlotState
|
||||
{
|
||||
bool active;
|
||||
BYTE itemCell;
|
||||
BYTE attrType;
|
||||
short minValue;
|
||||
int attempts;
|
||||
};
|
||||
std::array<TSwitchbotSlotState, 5> m_switchbotSlots;
|
||||
int m_iSwitchbotSpeedIndex;
|
||||
|
||||
public:
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -1797,6 +1816,7 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
|
||||
LPEVENT m_pkDestroyWhenIdleEvent;
|
||||
LPEVENT m_pkAutoPickupEvent;
|
||||
LPEVENT m_pkStoneQueueEvent;
|
||||
LPEVENT m_pkSwitchbotEvent;
|
||||
LPEVENT m_pkPetSystemUpdateEvent;
|
||||
|
||||
bool IsWarping() const { return m_pkWarpEvent ? true : false; }
|
||||
|
||||
@@ -211,6 +211,7 @@ ACMD(do_biolog_submit);
|
||||
ACMD(do_teleport_system);
|
||||
ACMD(do_autopickup);
|
||||
ACMD(do_stone_queue);
|
||||
ACMD(do_switchbot);
|
||||
|
||||
ACMD(do_click_mall);
|
||||
|
||||
@@ -471,6 +472,7 @@ struct command_info cmd_info[] =
|
||||
{ "teleport_system", do_teleport_system, 0, POS_DEAD, GM_PLAYER },
|
||||
{ "autopickup", do_autopickup, 0, POS_DEAD, GM_PLAYER },
|
||||
{ "stone_queue", do_stone_queue, 0, POS_DEAD, GM_PLAYER },
|
||||
{ "switchbot", do_switchbot, 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 },
|
||||
|
||||
@@ -2043,6 +2043,87 @@ ACMD(do_stone_queue)
|
||||
ch->StartStoneQueue(slots, static_cast<BYTE>(scrollCell));
|
||||
}
|
||||
|
||||
ACMD(do_switchbot)
|
||||
{
|
||||
char action[256];
|
||||
argument = one_argument(argument, action, sizeof(action));
|
||||
|
||||
if (!*action || !strcmp(action, "sync"))
|
||||
{
|
||||
ch->SendSwitchbotState();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strcmp(action, "stop_all"))
|
||||
{
|
||||
ch->StopAllSwitchbotSlots();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strcmp(action, "speed"))
|
||||
{
|
||||
char speedArg[256];
|
||||
argument = one_argument(argument, speedArg, sizeof(speedArg));
|
||||
|
||||
int speedIndex = 1;
|
||||
str_to_number(speedIndex, speedArg);
|
||||
ch->SetSwitchbotSpeed(speedIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
char slotArg[256];
|
||||
argument = one_argument(argument, slotArg, sizeof(slotArg));
|
||||
|
||||
int switchbotSlot = -1;
|
||||
str_to_number(switchbotSlot, slotArg);
|
||||
if (switchbotSlot < 0 || switchbotSlot >= 5)
|
||||
{
|
||||
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Invalid switchbot slot."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strcmp(action, "stop"))
|
||||
{
|
||||
ch->StopSwitchbotSlot(switchbotSlot);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strcmp(action, "clear"))
|
||||
{
|
||||
ch->StopSwitchbotSlot(switchbotSlot, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(action, "start"))
|
||||
{
|
||||
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Usage: /switchbot [sync|speed|start|stop|clear|stop_all]"));
|
||||
return;
|
||||
}
|
||||
|
||||
char itemArg[256];
|
||||
char attrArg[256];
|
||||
char valueArg[256];
|
||||
argument = one_argument(argument, itemArg, sizeof(itemArg));
|
||||
argument = one_argument(argument, attrArg, sizeof(attrArg));
|
||||
argument = one_argument(argument, valueArg, sizeof(valueArg));
|
||||
|
||||
int itemCell = -1;
|
||||
int attrType = 0;
|
||||
int minValue = 0;
|
||||
str_to_number(itemCell, itemArg);
|
||||
str_to_number(attrType, attrArg);
|
||||
str_to_number(minValue, valueArg);
|
||||
|
||||
if (itemCell < 0 || itemCell >= INVENTORY_MAX_NUM || attrType <= 0 || minValue <= 0)
|
||||
{
|
||||
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Invalid switchbot config."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ch->StartSwitchbotSlot(switchbotSlot, static_cast<BYTE>(itemCell), static_cast<BYTE>(attrType), static_cast<short>(minValue)))
|
||||
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("Unable to start switchbot slot."));
|
||||
}
|
||||
|
||||
ACMD(do_in_game_mall)
|
||||
{
|
||||
if (LC_IsYMIR() == true || LC_IsKorea() == true)
|
||||
|
||||
Reference in New Issue
Block a user