Jump to content

Recommended Posts

Hello developers, I am once again seeking your help. I need the feature of automatically using items, which can now handle the logic of using items with attack speed and movement speed very well. However, when encountering items with sustained recovery, it is difficult to make effective and correct judgments, such as NEW-AFFERCT-AUTO-HP-RECOVER=534, NEW-AFFERCT-AUTO-SP-RECOVER=535

What I need is for 72723 or 73727 or other continuous recovery items to automatically open the next bottle when their capacity runs out and they automatically close

client_src /pythonplayer.cpp

void CPythonPlayer::AddAutoUseItem(DWORD dwVnum, BYTE byThreshold, WORD wCoolTime)
{
	TraceError("CPythonPlayer::AddAutoUseItem - vnum:%d threshold:%d cooltime:%d", dwVnum, byThreshold, wCoolTime);
	m_AutoUseItems[dwVnum] = std::make_pair(byThreshold, wCoolTime);
	m_AutoUseLastTime[dwVnum] = 0; 
}

void CPythonPlayer::RemoveAutoUseItem(DWORD dwVnum)
{
	m_AutoUseItems.erase(dwVnum);
	m_AutoUseLastTime.erase(dwVnum);
}

void CPythonPlayer::ClearAutoUseItems()
{
	m_AutoUseItems.clear();
	m_AutoUseLastTime.clear();
}

bool CPythonPlayer::IsAutoUseItem(DWORD dwVnum)
{
	return m_AutoUseItems.find(dwVnum) != m_AutoUseItems.end();
}



bool CPythonPlayer::IsValidUseItem(DWORD dwVnum)
{
	CItemManager& rkItemMgr = CItemManager::Instance();
	CItemData* pItem;
	if (!rkItemMgr.GetItemDataPointer(dwVnum, &pItem))
	{
		TraceError("IsValidUseItem - Cannot get item data for vnum: %d", dwVnum);
		return false;
	}

	if (pItem->GetType() != 3)
	{
		TraceError("IsValidUseItem - Item %d is not use type", dwVnum);
		return false;
	}

	BYTE subType = pItem->GetSubType();
	TraceError("IsValidUseItem - Checking item: %d, subType: %d", dwVnum, subType);

	switch (subType)
	{
	case 0: // 药水
	{
		DWORD curHP = (DWORD)GetStatus(POINT_HP);
		DWORD maxHP = (DWORD)GetStatus(POINT_MAX_HP);
		DWORD curSP = (DWORD)GetStatus(POINT_SP);
		DWORD maxSP = (DWORD)GetStatus(POINT_MAX_SP);

		if (pItem->GetValue(0) > 0) // HP药水
		{
			TraceError("IsValidUseItem - HP potion check: cur=%d, max=%d", curHP, maxHP);
			if (curHP >= maxHP)
				return false;
		}
		else if (pItem->GetValue(1) > 0) // MP药水
		{
			TraceError("IsValidUseItem - MP potion check: cur=%d, max=%d", curSP, maxSP);
			if (curSP >= maxSP)
				return false;
		}
		break;
	}

	case 7: // 辅助性药剂
	{
		long affect_type = pItem->GetValue(0);
		TraceError("IsValidUseItem - Buff item: %d, affect_type: %d", dwVnum, affect_type);

		CInstanceBase* pkInstMain = CPythonCharacterManager::Instance().GetMainInstancePtr();
		if (!pkInstMain)
			return false;

		DWORD dwAffectType = 0;
		if (affect_type == 7)
			dwAffectType = CInstanceBase::AFFECT_ATT_SPEED_POTION;
		else if (affect_type == 8)
			dwAffectType = CInstanceBase::AFFECT_MOV_SPEED_POTION;

		if (pkInstMain->IsAffect(dwAffectType))
		{
			TraceError("IsValidUseItem - Already has affect %d for item %d", dwAffectType, dwVnum);
			return false;
		}
		break;
	}

	case 10: // 持续恢复类物品(水龙/火龙)
	{
		CInstanceBase* pkInstMain = CPythonCharacterManager::Instance().GetMainInstancePtr();
		if (!pkInstMain)
			return false;

		if (dwVnum == 72727) // 水龙
		{
			const SAutoPotionInfo& potInfo = GetAutoPotionInfo(AUTO_POTION_TYPE_SP);
			if (potInfo.bActivated)  // 修改 isActivated 为 bActivated
			{
				TraceError("IsValidUseItem - SP AutoPotion already activated - Current: %d Total: %d Slot: %d",
					potInfo.currentAmount, potInfo.totalAmount, potInfo.inventorySlotIndex);
				return false;
			}
		}
		else if (dwVnum == 72723) // 火龙
		{
			const SAutoPotionInfo& potInfo = GetAutoPotionInfo(AUTO_POTION_TYPE_HP);
			if (potInfo.bActivated)  // 修改 isActivated 为 bActivated
			{
				TraceError("IsValidUseItem - HP AutoPotion already activated - Current: %d Total: %d Slot: %d",
					potInfo.currentAmount, potInfo.totalAmount, potInfo.inventorySlotIndex);
				return false;
			}
		}
		break;
	}

	case 11: // 立即恢复类物品
	{
		DWORD curHP = (DWORD)GetStatus(POINT_HP);
		DWORD maxHP = (DWORD)GetStatus(POINT_MAX_HP);

		if (pItem->GetValue(0) > 0) // 立即满血效果
		{
			TraceError("IsValidUseItem - Instant full HP recovery check: cur=%d, max=%d", curHP, maxHP);
			if (curHP >= maxHP)
			{
				TraceError("IsValidUseItem - Already at full HP, cannot use item %d", dwVnum);
				return false;
			}
		}
		break;
	}

	default:
		TraceError("IsValidUseItem - Unknown subType: %d for item: %d", subType, dwVnum);
		break;
	}

	return true;
}

bool CPythonPlayer::UseItemByVnum(DWORD dwVnum)//使用物品
{
	// 直接使用 IsValidUseItem 进行检查
	if (!IsValidUseItem(dwVnum))
		return false;

	// 查找和使用物品
	for (BYTE pos = 0; pos < c_Inventory_Count; ++pos)
	{
		const TItemData* pItem = GetItemData(TItemPos(INVENTORY, pos));
		if (pItem && pItem->vnum == dwVnum)
		{
			CPythonNetworkStream::Instance().SendItemUsePacket(TItemPos(INVENTORY, pos));
			return true;
		}
	}

	return false;
}

void CPythonPlayer::CheckAutoUseItems()//定期检查物品
{
	static DWORD s_lastCheckTime = ELTimer_GetMSec();
	DWORD currentTime = ELTimer_GetMSec();

	if (currentTime - s_lastCheckTime < 200)
		return;

	s_lastCheckTime = currentTime;

	// 直接调用UseItemByVnum(内部会调用IsValidUseItem)
	for (const auto& item : m_AutoUseItems)
	{
		UseItemByVnum(item.first);
	}
}

client_src/pythonplayermodule.cpp

PyObject* playerCheckAutoUseItems(PyObject* poSelf, PyObject* poArgs)
{
	CPythonPlayer::Instance().CheckAutoUseItems();
	return Py_BuildValue("i", 1);
}

PyObject* playerAddAutoUseItem(PyObject* poSelf, PyObject* poArgs)
{
	int iVnum;
	if (!PyTuple_GetInteger(poArgs, 0, &iVnum))
		return Py_BuildException();

	int iThreshold = 0;
	if (PyTuple_Size(poArgs) > 1)
		if (!PyTuple_GetInteger(poArgs, 1, &iThreshold))
			return Py_BuildException();

	int iCooldown = 0;
	if (PyTuple_Size(poArgs) > 2)
		if (!PyTuple_GetInteger(poArgs, 2, &iCooldown))
			return Py_BuildException();

	CPythonPlayer::Instance().AddAutoUseItem(iVnum, iThreshold, iCooldown);
	return Py_BuildNone();
}

PyObject* playerRemoveAutoUseItem(PyObject* poSelf, PyObject* poArgs)
{
	int iVnum;
	if (!PyTuple_GetInteger(poArgs, 0, &iVnum))
		return Py_BuildException();

	CPythonPlayer::Instance().RemoveAutoUseItem(iVnum);
	return Py_BuildNone();
}

PyObject* playerClearAutoUseItems(PyObject* poSelf, PyObject* poArgs)
{
	CPythonPlayer::Instance().ClearAutoUseItems();
	return Py_BuildNone();
}

PyObject* playerIsAutoUseItem(PyObject* poSelf, PyObject* poArgs)
{
	int iVnum;
	if (!PyTuple_GetInteger(poArgs, 0, &iVnum))
		return Py_BuildException();

	return Py_BuildValue("i", CPythonPlayer::Instance().IsAutoUseItem(iVnum));
}

PyObject* playerUseItemByVnum(PyObject* poSelf, PyObject* poArgs)
{
	int iVnum;
	if (!PyTuple_GetInteger(poArgs, 0, &iVnum))
		return Py_BuildException();

	return Py_BuildValue("i", CPythonPlayer::Instance().UseItemByVnum(iVnum));
}

python

class AutoUseManager(ui.BoardWithTitleBar):
	"""自动使用管理器 - 自动狩猎系统的子功能"""
	
	# 物品分类配置
	ITEM_CATEGORIES = {
		"AUTO_HP_MP": {
			"name": "自动药水",
			"items": [27001,27002,27003,27051,27004,27005,27006,27052],  # HP/MP药水
			"tooltip": "自动使用HP/MP药水"
		},
		"AUTO_DRAGON_FIRE": {
			"name": "自动火龙",
			"items": [72723,72724,72725,72726,39037,39038,39039],
			"tooltip": "自动使用火龙药水"
		},
		"AUTO_DRAGON_WATER": {
			"name": "自动水龙",
			"items": [72727,72728,72729,72730,39040,39041,39042],
			"tooltip": "自动使用水龙药水"
		},
		"AUTO_DRAGON_GOD": {
			"name": "自动龙神",
			"items": [71027,71028,71029,71030],
			"tooltip": "自动使用龙神药水"
		},
		"AUTO_ATT_SPEED": {
			"name": "自动攻速",
			"items": [27053,27110,27101,27102,27111,27115,27116],
			"tooltip": "自动使用攻速药水"
		},
		"AUTO_MOV_SPEED": {
			"name": "自动移速",
			"items": [27054,27103,27104,27105,27112,27113,27114],
			"tooltip": "自动使用移速药水"
		},
		"AUTO_DOUBLE": {
			"name": "自动双破",
			"items": [39024,39025,71044,71045,72025,72026,72027],
			"tooltip": "自动使用双破药水"
		},
		"AUTO_RELEASE": {
			"name": "自动释放",
			"items": [39031,71101,71102,76003],
			"tooltip": "自动使用释放药水"
		},
	}

	def __init__(self, parentWnd = None, toolTipWnd = None, itemManager = None):
		ui.BoardWithTitleBar.__init__(self)
		
		self.parentWnd = parentWnd
		self.toolTipWnd = toolTipWnd
		self.itemManager = itemManager
		
		self.categoryButtons = {}
		self.enabledVNums = []
		self.ignoredVNums = []
		
		self.SetSize(250, 300)
		self.SetTitleName("自动使用管理")
		self.SetCenterPosition()
		self.AddFlag("movable")
		self.AddFlag("float")
		
		self.__CreateUI()
		
	def __del__(self):
		ui.BoardWithTitleBar.__del__(self)
		self.parentWnd = None
		self.toolTipWnd = None
		self.itemManager = None
		self.categoryButtons = {}

	def __CreateUI(self):
		"""创建界面"""
		y = 35
		for category, info in self.ITEM_CATEGORIES.items():
			dbg.TraceError("Creating checkbox for %s" % info["name"])
			# 创建分类复选框
			checkbox = CheckBox(
				parent=self,
				title=info["name"],
				x=20,
				y=y,
				event=lambda category=category: self.__OnCategoryToggle(category)
			)
			
			if "tooltip" in info:
				checkbox.SetToolTipText(info["tooltip"])
				
			self.categoryButtons[category] = checkbox
			y += 30

	def OpenWindow(self):
		"""打开窗口"""
		if self.IsShow():
			self.Close()
		else:
			self.Show()
			self.SetTop()
			self.__LoadAllStates()

	def __OnCategoryToggle(self, category):
		"""处理分类开关"""
		checkbox = self.categoryButtons[category]
		info = self.ITEM_CATEGORIES[category]
		
		dbg.TraceError("Toggle %s" % info["name"])
		current_state = checkbox.IsChecked()
		dbg.TraceError("Current state before toggle: %s" % current_state)
		
		# 反转状态
		new_state = not current_state
		dbg.TraceError("Setting new state to: %s" % new_state)
		
		if new_state:
			dbg.TraceError("%s is being enabled" % info["name"])
			# 检查玩家是否有该分类的物品
			hasItems = False
			for vnum in info["items"]:
				itemCount = player.GetItemCountByVnum(vnum)
				if itemCount > 0:
					hasItems = True
					dbg.TraceError("Found item: %d count: %d" % (vnum, itemCount))
					break
					
			# 告知玩家状态
			if not hasItems:
				chat.AppendChat(chat.CHAT_TYPE_INFO, "%s: 当前没有可用物品,获得后会自动使用" % info["name"])
				
			# 先设置状态
			checkbox.Toggle()  # 使用Toggle代替SetCheck
			
			# 无论是否有物品都添加到启用列表
			for vnum in info["items"]:
				if vnum not in self.enabledVNums:
					self.enabledVNums.append(vnum)
				if vnum in self.ignoredVNums:
					self.ignoredVNums.remove(vnum)
				# 如果有物品就直接启用
				if player.GetItemCountByVnum(vnum) > 0:
					threshold = 70 if category == "AUTO_HP_MP" else 0
					player.AddAutoUseItem(vnum, threshold)
					dbg.TraceError("Added auto use: %d threshold: %d" % (vnum, threshold))
		else:
			dbg.TraceError("%s is being disabled" % info["name"])
			# 先设置状态
			checkbox.Toggle()  # 使用Toggle代替SetCheck
			
			# 取消自动使用
			for vnum in info["items"]:
				if vnum in self.enabledVNums:
					self.enabledVNums.remove(vnum)
				if vnum not in self.ignoredVNums:
					self.ignoredVNums.append(vnum)
				if player.IsAutoUseItem(vnum):
					player.RemoveAutoUseItem(vnum)
					dbg.TraceError("Removed auto use: %d" % vnum)

	def Show(self):
		ui.BoardWithTitleBar.Show(self)
		self.__LoadAllStates()

	def Hide(self):
		ui.BoardWithTitleBar.Hide(self)

	def __LoadAllStates(self):
		"""加载所有分类的当前状态"""
		dbg.TraceError("Loading all states")
		for category, info in self.ITEM_CATEGORIES.items():
			checkbox = self.categoryButtons[category]
			# 检查该分类是否有物品在自动使用中
			isActive = False
			for vnum in info["items"]:
				if player.IsAutoUseItem(vnum):
					isActive = True
					dbg.TraceError("%s has active item: %d" % (info["name"], vnum))
					break
			checkbox.SetCheck(isActive)
			dbg.TraceError("Set %s state to: %s" % (info["name"], isActive))

	def SetAutoUseList(self, itemList, ignoredList):
		"""设置自动使用列表和忽略列表"""
		self.enabledVNums = itemList
		self.ignoredVNums = ignoredList
		
		# 更新UI状态
		for category, info in self.ITEM_CATEGORIES.items():
			checkbox = self.categoryButtons[category]
			# 检查该分类是否有物品在启用列表中
			isActive = False
			for vnum in info["items"]:
				if vnum in self.enabledVNums:
					isActive = True
					break
			checkbox.SetCheck(isActive)

	def OnTick(self):
		"""定时检查并使用物品"""
		if not self.IsShow():
			return
				
		# 遍历所有启用的分类
		for category, info in self.ITEM_CATEGORIES.items():
			checkbox = self.categoryButtons[category]
			if not checkbox.IsChecked():
				continue
					
			# 检查该分类中的物品
			for vnum in info["items"]:
				if player.GetItemCountByVnum(vnum) > 0:
					player.UseItemByVnum(vnum)  # C++端会处理所有的状态检查

	def GetSendStatus(self, slot):
		"""获取发送状态"""
		return True  # 允许玩家手动使用物品
		
	def SendInvSlot(self, slot):
		"""发送物品使用请求"""
		if not self.GetSendStatus(slot):
			return
			
		# 检查物品类型
		vnum = player.GetItemIndex(slot)
		if self.__IsEquipSlotItem(vnum):
			return True  # 允许装备类物品使用
			
		# 其他物品按原逻辑处理
		return False

	def OnUpdate(self):
		if self.IsShow():
			player.CheckAutoUseItems()  # 定期检查自动使用物品

	def OnPressEscapeKey(self):
		self.Close()
		return True

	def Close(self):
		self.Hide()

	def Destroy(self):
		self.Hide()
		for checkbox in self.categoryButtons.values():
			checkbox = None
		self.categoryButtons = {}
		self.parentWnd = None

I use the source of Martysama 5.8, and if anyone can help me, I am willing to pay for the knowledge 

Edited by shenhui1986
  • Replies 0
  • Created
  • Last Reply

Top Posters In This Topic

Popular Days

Top Posters In This Topic

Popular Days

Don't use any images from : imgur, turkmmop, freakgamers, inforge, hizliresim... Or your content will be deleted without notice...
Use : https://metin2.download/media/add/

Please use https://metin2.download/ when uploading files smaller than 100MB, otherwise the approval will take longer due to manual upload.

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
×
×
  • Create New...

Important Information

Terms of Use / Privacy Policy / Guidelines / We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.