Jump to content

[C++ & Py] Multiple split items - on special storage and normal inv


Recommended Posts

  • Active Member

The multi-split system is publicly available on the internet, but here you have it adapted for the special inventory and with a fix for the crash-core issue.
Previously, using command 

/split_items 0 0 0 0

could crash any server running this system, but this vulnerability has now been resolved in the fixed version provided here.

 

uipickitem.py

 

import wndMgr
import ui
import ime
import localeInfo

class PickItemDialog(ui.ScriptWindow):
	def __init__(self):
		ui.ScriptWindow.__init__(self)

		self.unitValue = 1
		self.maxValue = 0
		self.eventAccept = 0
		self.doAll = False
		
		
	def __del__(self):
		ui.ScriptWindow.__del__(self)

	def LoadDialog(self):
		try:
			pyScrLoader = ui.PythonScriptLoader()
			pyScrLoader.LoadScriptFile(self, "UIScript/PickItemDialog.py")
		except:
			import exception
			exception.Abort("MoneyDialog.LoadDialog.LoadScript")

		try:
			self.board = self.GetChild("board")
			self.maxValueTextLine = self.GetChild("max_value")
			self.pickValueEditLine = self.GetChild("money_value")
			self.acceptButton = self.GetChild("accept_button")
			self.cancelButton = self.GetChild("cancel_button")
		except:
			import exception
			exception.Abort("MoneyDialog.LoadDialog.BindObject")

		self.pickValueEditLine.SetReturnEvent(ui.__mem_func__(self.OnAccept))
		self.pickValueEditLine.SetEscapeEvent(ui.__mem_func__(self.Close))
		self.acceptButton.SetEvent(ui.__mem_func__(self.OnAccept))
		self.cancelButton.SetEvent(ui.__mem_func__(self.Close))
		self.board.SetCloseEvent(ui.__mem_func__(self.Close))
		
		self.checkBox = ui.CheckBoxClassic()
		self.checkBox.SetParent(self)
		self.checkBox.SetPosition(25, 50)
		self.checkBox.SetWindowVerticalAlignBottom()
		self.checkBox.SetEvent(ui.__mem_func__(self.SetSplitFunction), "ON_CHECK", True)
		self.checkBox.SetEvent(ui.__mem_func__(self.SetSplitFunction), "ON_UNCKECK", False)
		self.checkBox.SetCheckStatus(self.doAll)
		self.checkBox.SetTextInfo("Multiple Split")
		self.checkBox.Show()
	
	def SplitClear(self):
		self.doAll = False
		self.checkBox.SetCheckStatus(self.doAll)
		
	def SetSplitFunction(self, checkType, autoFlag):
		self.doAll = autoFlag
	
	def IsSplitAll(self):
		return self.doAll
		
	def Destroy(self):
		self.ClearDictionary()
		self.eventAccept = 0
		self.maxValue = 0
		self.pickValueEditLine = 0		
		self.acceptButton = 0
		self.cancelButton = 0
		self.board = None
		self.doAll = False

	def SetTitleName(self, text):
		self.board.SetTitleName(text)

	def SetAcceptEvent(self, event):
		self.eventAccept = event

	def SetMax(self, max):
		self.pickValueEditLine.SetMax(max)

	def Open(self, maxValue, unitValue=1):

		if localeInfo.IsYMIR() or localeInfo.IsCHEONMA() or localeInfo.IsHONGKONG():
			unitValue = ""

		width = self.GetWidth()
		(mouseX, mouseY) = wndMgr.GetMousePosition()

		if mouseX + width/2 > wndMgr.GetScreenWidth():
			xPos = wndMgr.GetScreenWidth() - width
		elif mouseX - width/2 < 0:
			xPos = 0
		else:
			xPos = mouseX - width/2

		self.SetPosition(xPos, mouseY - self.GetHeight() - 20)

		if localeInfo.IsARABIC():
			self.maxValueTextLine.SetText("/" + str(maxValue))
		else:
			self.maxValueTextLine.SetText(" / " + str(maxValue))

		self.pickValueEditLine.SetText(str(unitValue))
		self.pickValueEditLine.SetFocus()

		ime.SetCursorPosition(1)

		self.unitValue = unitValue
		self.maxValue = maxValue
		self.Show()
		self.SetTop()

	def Close(self):
		self.pickValueEditLine.KillFocus()
		self.Hide()

	def OnAccept(self):

		text = self.pickValueEditLine.GetText()

		if len(text) > 0 and text.isdigit():

			money = int(text)
			money = min(money, self.maxValue)

			if money > 0:
				if self.eventAccept:
					self.eventAccept(money)

		self.Close()

in uiinventory.py

replace

import uiPickMoney

with

import uiPickItem

replace

dlgPickMoney = uiPickMoney.PickMoneyDialog()

with

dlgPickMoney = uiPickItem.PickItemDialog()

search:
 

	def SelectEmptySlot(self, selectedSlotPos):

in this function search for:
 

if player.SLOT_TYPE_INVENTORY == attachedSlotType:

and replace:

self.__SendMoveItemPacket(attachedSlotPos, selectedSlotPos, attachedCount)

with:
 

                if self.dlgPickMoney and self.dlgPickMoney.IsSplitAll():
                    net.SendChatPacket("/split_items %d %d %d 0" % (attachedSlotPos, attachedCount, selectedSlotPos))
                    self.dlgPickMoney.SplitClear()
                else:
                    self.__SendMoveItemPacket(attachedSlotPos, selectedSlotPos, attachedCount)

 

uispecialstorage.py:

inside

def __init__(self):

add

self.dlgSplitItems = None

before

        self.SetInventoryPage(0)
        self.SetCategoryPage(0)
        self.RefreshItemSlot()
        self.RefreshBagSlotWindow()

add

        self.dlgSplitItems = uiPickItem.PickItemDialog()
        self.dlgSplitItems.LoadDialog()
        self.dlgSplitItems.Hide()

inside

def Destroy(self):

add

self.dlgSplitItems.Destroy()
self.dlgSplitItems = None

inside

def Close(self):

add

if self.dlgSplitItems:
	self.dlgSplitItems.Close()    

replace

    def OnPickItem(self, count):

with this:

    def OnPickItem(self, count):
        itemSlotIndex = self.dlgSplitItems.itemGlobalSlotIndex
        selectedItemVNum = player.GetItemIndex(self.SLOT_WINDOW_TYPE[self.categoryPageIndex]["window"], itemSlotIndex)
        mouseModule.mouseController.AttachObject(self, self.SLOT_WINDOW_TYPE[self.categoryPageIndex]["slot"], itemSlotIndex, selectedItemVNum, count)

search

    def SelectItemSlot(self, itemSlotIndex):

 

before

            else:
                mouseModule.mouseController.AttachObject(self, self.SLOT_WINDOW_TYPE[self.categoryPageIndex]["slot"], itemSlotIndex, selectedItemVNum, itemCount)
                self.wndItem.SetUseMode(False)
                snd.PlaySound("sound/ui/pick.wav")

add

 

            elif app.IsPressed(app.DIK_LSHIFT):
                if itemCount > 1:
                    self.dlgSplitItems.SetTitleName(localeInfo.PICK_ITEM_TITLE)
                    self.dlgSplitItems.SetAcceptEvent(ui.__mem_func__(self.OnPickItem))
                    self.dlgSplitItems.Open(itemCount)
                    self.dlgSplitItems.itemGlobalSlotIndex = itemSlotIndex

inside

    def SelectEmptySlot(self, selectedSlotPos):

replace 

            elif player.RESERVED_WINDOW != attachedInvenType:

with
 

            elif player.RESERVED_WINDOW != attachedInvenType:
                itemCount = player.GetItemCount(attachedInvenType, attachedSlotPos)
                attachedCount = mouseModule.mouseController.GetAttachedItemCount()
                if self.dlgSplitItems and self.dlgSplitItems.IsSplitAll():
                    net.SendChatPacket("/split_items %d %d %d %d" % (attachedSlotPos, attachedCount, selectedSlotPos, self.categoryPageIndex+1))
                    self.dlgSplitItems.SplitClear()
                else:
                    self.__SendMoveItemPacket(attachedInvenType, attachedSlotPos, self.SLOT_WINDOW_TYPE[self.categoryPageIndex]["window"], selectedSlotPos, attachedCount)

now server source part:

in cmd.cpp

search and after

ACMD(do_item);

add

ACMD(do_split_items);

search and after

    { "item",        do_item,        0,            POS_DEAD,    GM_IMPLEMENTOR        },

add

{ "split_items", do_split_items, 0, POS_DEAD, GM_PLAYER },

 

in cmd_general.cpp

add

ACMD(do_split_items)
{
    if (!ch)
        return;

    char arg1[256], arg2[256], arg3[256], arg4[256];
    four_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2), arg3, sizeof(arg3), arg4, sizeof(arg4));

    if (!*arg1 || !*arg2 || !*arg3 || !*arg4)
    {
        ch->ChatPacket(CHAT_TYPE_INFO, "Usage: /split_items <cell> <count> <destCell> <window>");
        return;
    }

    if (!ch->CanWarp())
    {
        ch->ChatPacket(CHAT_TYPE_INFO, "Close all windows and wait a few seconds before using this.");
        return;
    }

    int cell = 0, destCell = 0, window = 0;
    int count = 0;

    str_to_number(cell, arg1);
    str_to_number(count, arg2);
    str_to_number(destCell, arg3);
    str_to_number(window, arg4);

    if (cell < 0 || cell >= INVENTORY_MAX_NUM ||
        destCell < 0 || destCell >= INVENTORY_MAX_NUM)
    {
        ch->ChatPacket(CHAT_TYPE_INFO, "Invalid cell index.");
        return;
    }

    if (count <= 0 || count > 200)
    {
        ch->ChatPacket(CHAT_TYPE_INFO, "Invalid item count.");
        return;
    }

    if (window < 0 || window > 4)
    {
        ch->ChatPacket(CHAT_TYPE_INFO, "Invalid inventory window.");
        return;
    }

    // ch->SetIgnoreMoveItemCooldown(true);

    auto TrySplit = [&](LPITEM item, int invType, int (*getEmpty)(CHARACTER*, LPITEM)) -> void
    {
        if (!item)
        {
            ch->ChatPacket(CHAT_TYPE_INFO, "No item found in that slot.");
            return;
        }

        WORD itemCount = item->GetCount();
        if (itemCount <= 1)
        {
            ch->ChatPacket(CHAT_TYPE_INFO, "Item cannot be split.");
            return;
        }

        const BYTE itemSize = item->GetSize();
        int loops = 0;

        while (item && itemCount > 1)
        {
            if (++loops > 500)
                break;

            if (count > itemCount)
                count = itemCount;

            int emptyPos = -1;
            if (getEmpty)
                emptyPos = getEmpty(ch, item);
            else
                emptyPos = ch->GetEmptyInventoryFromIndex(destCell, itemSize);

            if (emptyPos < 0)
            {
                ch->ChatPacket(CHAT_TYPE_INFO, "No empty slot found.");
                break;
            }

            if (!ch->MoveItem(TItemPos(invType, cell), TItemPos(invType, emptyPos), count))
            {
                ch->ChatPacket(CHAT_TYPE_INFO, "Failed to move item.");
                break;
            }

            item = nullptr;
            switch (invType)
            {
                case INVENTORY:
                    item = ch->GetInventoryItem(cell);
                    break;
                case UPGRADE_INVENTORY:
                    item = ch->GetUpgradeInventoryItem(cell);
                    break;
                case BOOK_INVENTORY:
                    item = ch->GetBookInventoryItem(cell);
                    break;
                case STONE_INVENTORY:
                    item = ch->GetStoneInventoryItem(cell);
                    break;
                case CHEST_INVENTORY:
                    item = ch->GetChestInventoryItem(cell);
                    break;
            }

            if (!item)
                break;

            itemCount = item->GetCount();
        }
    };

    switch (window)
    {
    case 0:
        TrySplit(ch->GetInventoryItem(cell), INVENTORY, nullptr);
        break;
    case 1:
        TrySplit(ch->GetUpgradeInventoryItem(cell), UPGRADE_INVENTORY,
            [](CHARACTER* c, LPITEM i) { return c->GetEmptyUpgradeInventory(i); });
        break;
    case 2:
        TrySplit(ch->GetBookInventoryItem(cell), BOOK_INVENTORY,
            [](CHARACTER* c, LPITEM i) { return c->GetEmptyBookInventory(i); });
        break;
    case 3:
        TrySplit(ch->GetStoneInventoryItem(cell), STONE_INVENTORY,
            [](CHARACTER* c, LPITEM i) { return c->GetEmptyStoneInventory(i); });
        break;
    case 4:
        TrySplit(ch->GetChestInventoryItem(cell), CHEST_INVENTORY,
            [](CHARACTER* c, LPITEM i) { return c->GetEmptyChestInventory(i); });
        break;
    default:
        ch->ChatPacket(CHAT_TYPE_INFO, "Invalid window index.");
        break;
    }

}

 

  • Love 1

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.