Jump to content

Search the Community

Showing results for tags 'lua'.

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


Forums

  • Metin2 Dev
    • Announcements
  • Community
    • Member Representations
    • Off Topic
  • Miscellaneous
    • Metin2
    • Showcase
    • File Requests
    • Community Support - Questions & Answers
    • Paid Support / Searching / Recruiting
  • Metin2 Development
  • Metin2 Development
    • Basic Tutorials / Beginners
    • Guides & HowTo
    • Binaries
    • Programming & Development
    • Web Development & Scripts / Systems
    • Tools & Programs
    • Maps
    • Quests
    • 3D Models
    • 2D Graphics
    • Operating Systems
    • Miscellaneous
  • Private Servers
    • Private Servers
  • Uncategorized
    • Drafts
    • Trash
    • Archive
    • Temporary
    • Metin2 Download

Product Groups

  • Small Advertisement
  • Large Advertisement
  • Advertising

Categories

  • Third Party - Providers Directory

Categories

  • Overview
  • Pages
    • Overview
    • File Formats
    • Network
    • Extensions

Find results in...

Find results that contain...


Date Created

  • Start

    End


Last Updated

  • Start

    End


Filter by number of...

Joined

  • Start

    End


Group


Pillory


Marketplace


Game Server


Country


Nationality


Github


Gitlab


Discord


Skype


Website

  1. Download Metin2 Download [Hidden Content] c++11 is enough.
  2. Hello community, I created this quest to use on my PvP server and decided to share it. The anti-farm in relation to killing characters is by windows guid, you must add this check in the source. If you don't want to add it, you can replace it with the existing standard IP check function. [Hidden Content]
  3. Hello community, I created some time this method to reduce the use of bots on my server, this code was written for anti metins farm but can be adapted for monsters too. The code was made in a few hours and could be improved a lot more, but as it stands it works perfectly. Logic When the player destroys 30 metins, the captcha will appear and the player will be stunned at the same time. The player has only 3 attempts, if he fails all 3 times he is teleported to the city. The stun is only removed when the captcha is successfully completed. As I said, this is not a WOW solution, but it can help as it has helped me on my server. For those who really want to spend time creating a more effective solution, I recommend creating a captcha in c++ with encrypted connection client -> server. Don’t forget to update the values in the quest according to your item_proto. Lua [Hidden Content] C++ [Hidden Content] quest_functions[Hidden Content]
  4. M2 Download Center Download Here ( Internal ) good day I want to copy this system metin2 Hispano i work the quest and python lack the effects, c/u what does it his way quest duelo begin state start begin when kill with npc.is_pc() begin pc.setqf("estado", pc.getqf("estado")+1) if pc.getqf("estado") == 1 then pc.setqf("asesinatos", pc.getqf("asesinatos")+1) chat("He tio, pero si has hecho un *FristKill*") cmdchat("Fris") elseif pc.getqf("estado") == 2 then pc.setqf("asesinatos", pc.getqf("asesinatos")+2) chat("He tio, pero si has hecho un *DoubleKill*") cmdchat("Double") elseif pc.getqf("estado") == 3 then pc.setqf("asesinatos", pc.getqf("asesinatos")+4) chat("He tio, pero si has hecho un *TripleKill*") cmdchat("Triple") elseif pc.getqf("estado") == 4 then pc.setqf("asesinatos", pc.getqf("asesinatos")+7) chat("He tio, pero si has hecho un *MultiKill*") cmdchat("Multi") elseif pc.getqf("estado") == 5 then pc.setqf("asesinatos", pc.getqf("asesinatos")+15) chat("He tio, pero si has hecho un *UltraKill*") cmdchat("Ultra") pc.setqf("estado", 0) chat("Se han reseteado tus kills con exito.") end end end end game.py # sistema de rachas jfirewall "Fris" : self.__Rachas, "Double" : self.__double, "Triple" : self.__triple, "Multi" : self.__multi, "Ultra" : self.__ultra, # sistema de rachas end ultimately def __Rachas(self): net.SendChatPacket("(racha)") def __double(self): net.SendChatPacket("(racha2)") def __triple(self): net.SendChatPacket("(racha3)") def __multi(self): net.SendChatPacket("(racha4)") def __ultra(self): net.SendChatPacket("(racha5)") playersettingmodule.py chrmgr.RegisterEffect(chrmgr.EFFECT_EMOTICON+12, "", EmoticonStr+"name.mse") net.RegisterEmoticonString("(racha)") chrmgr.RegisterEffect(chrmgr.EFFECT_EMOTICON+13, "", EmoticonStr+"name.mse") net.RegisterEmoticonString("(racha2)") chrmgr.RegisterEffect(chrmgr.EFFECT_EMOTICON+14, "", EmoticonStr+"name.mse") net.RegisterEmoticonString("(racha3)") chrmgr.RegisterEffect(chrmgr.EFFECT_EMOTICON+15, "", EmoticonStr+"name.mse") net.RegisterEmoticonString("(racha4)") chrmgr.RegisterEffect(chrmgr.EFFECT_EMOTICON+16, "", EmoticonStr+"name.mse") net.RegisterEmoticonString("(racha5)") paste the efect in: d:/ymir work/effect/etc/emoticon/ credtis: python and quest jfirewall thank Tico correct quest pd: c/u designs its effects
  5. This vulnerability should affect every server. You can duplicate item rewards, and also crash the server through dangling pointers. The danger of this bug escalates to how many custom systems, and how many crafting quests (for example, the vitality ore quest, not the cube system) you have in your server. How to trigger it: Any quest that uses select & wait, and the item lua module after that is vulnerable. After the server uses select() or wait(), the player's quest state is suspended. After the player replies using the CG packet, the quest state is recovered. So what's wrong with it? It doesn't verify if the stored quest item ptr expired. You basically need to destroy the selected item ptr in order to dupe the rewards of the quest. After some tries, you may get a core crash in the game. (dangling pointers often cause crashes only after that memory sector has been rewritten) In my files, I've checked (since several years ago) if the quest state was suspended for the the default windows such as exchange, cube, shop. This bug can work very easily on offline shops or other new systems that don't check that. After the select() or wait() is called, you send the selected item to the (e.g.) offlineshop system window. It will delete the item ptr in the game. Now, you can press "Ok" on the quest, and the quest will proceed as if the item still existed. The item still exists in the offlineshop, but not the item ptr anymore. The item won't be deleted by the quest even after item.remove() is called. This is the fix: diff --git a/s3ll_server/Srcs/Server/game/src/char.cpp b/s3ll_server/Srcs/Server/game/src/char.cpp index 0ea307fa..65b1dd65 100644 --- a/s3ll_server/Srcs/Server/game/src/char.cpp +++ b/s3ll_server/Srcs/Server/game/src/char.cpp @@ -303,7 +303,10 @@ void CHARACTER::Initialize() m_dwQuestNPCVID = 0; m_dwQuestByVnum = 0; - m_pQuestItem = NULL; + m_dwQuestItemVID = 0; m_dwUnderGuildWarInfoMessageTime = get_dword_time()-60000; @@ -6123,33 +6126,37 @@ LPCHARACTER CHARACTER::GetQuestNPC() const void CHARACTER::SetQuestItemPtr(LPITEM item) { - m_pQuestItem = item; + m_dwQuestItemVID = (item) ? item->GetVID() : 0; } void CHARACTER::ClearQuestItemPtr() { - m_pQuestItem = NULL; + m_dwQuestItemVID = 0; } LPITEM CHARACTER::GetQuestItemPtr() const { - return m_pQuestItem; + if (!m_dwQuestItemVID) + return nullptr; + return ITEM_MANAGER::Instance().FindByVID(m_dwQuestItemVID); } diff --git a/s3ll_server/Srcs/Server/game/src/char.h b/s3ll_server/Srcs/Server/game/src/char.h index cc4da2bb..74b3470e 100644 --- a/s3ll_server/Srcs/Server/game/src/char.h +++ b/s3ll_server/Srcs/Server/game/src/char.h @@ -1674,9 +1674,9 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider private: DWORD m_dwQuestNPCVID; DWORD m_dwQuestByVnum; - LPITEM m_pQuestItem; + DWORD m_dwQuestItemVID{}; // @fixme304 (LPITEM -> DWORD) // Events To unlock the client in order to move your windows with a quest open, you edit this from interfacemodule.py: You can even Hide the TopBar and BottomBar from uiQuest.py for a better result. Important: after this fix, the item ptr may be nullptr after they press enter, so you need to check if the item ptr is still valid by using this function: ALUA(item_is_available) { auto item = CQuestManager::instance().GetCurrentItem(); lua_pushboolean(L, item != nullptr); return 1; } ... { "is_available", item_is_available }, // [return lua boolean] By simply doing: when 169.take begin local s1=select("Yes", "No") if s1==2 then return end if not item.is_available() then return end end If you want to tryhard, and be sure that the item ptr didn't get swapped, you can do as following via quest:
  6. M2 Download Center Download Here ( Internal ) open game/char.h Add: void CostumeBonusTransfer(DWORD cell1, DWORD cell2); open game/char.cpp Add: void CHARACTER::CostumeBonusTransfer(DWORD cell1, DWORD cell2) { int CostumeTransferBonusItemVnum = 76025; // costume bonus transfer item vnum if ((GetExchange() || IsOpenSafebox() || GetShopOwner()) || IsCubeOpen() || IsDead()) { ChatPacket(CHAT_TYPE_INFO, "Alisveris durumunda kostum bonusu aktarilamaz !"); return; } LPITEM costume1 = GetInventoryItem(cell1); LPITEM costume2 = GetInventoryItem(cell2); if (!costume1){ ChatPacket(CHAT_TYPE_INFO, "Kostum bulunamadi hata !"); return; } if (costume1->GetType() != ITEM_COSTUME || costume1->GetType() == ITEM_COSTUME && costume1->GetSubType() != ARMOR_BODY) { ChatPacket(CHAT_TYPE_INFO, "Bu islem sadece zirh kostumune yapilabilir!"); return; } if (!costume2){ ChatPacket(CHAT_TYPE_INFO, "Kostum bulunamadi hata!"); return; } if (costume2->GetType() != ITEM_COSTUME || costume2->GetType() == ITEM_COSTUME && costume2->GetSubType() != ARMOR_BODY) { ChatPacket(CHAT_TYPE_INFO, "Sadece kostumun bonusu aktarilabilir !"); return; } if (CountSpecifyItem(CostumeTransferBonusItemVnum) < 1){ ChatPacket(CHAT_TYPE_INFO, "Bonus transfer esyasi bulunamadi"); return; } if (costume2->GetAttributeCount() < 1){ ChatPacket(CHAT_TYPE_INFO, "Bonusu olmayan bir kostumun efsunu aktarilamaz!"); return; } RemoveSpecifyItem(CostumeTransferBonusItemVnum, 1); costume1->ClearAttribute(); for (int i = 0; i < costume2->GetAttributeCount(); i++){ costume1->SetForceAttribute(i, costume2->GetAttributeType(i), costume2->GetAttributeValue(i)); } costume2->RemoveFromCharacter(); ChatPacket(CHAT_TYPE_INFO, "Kostum bonus aktarimi basarili"); } open cmd.cpp: Search: ACMD(do_block_chat); Add: ACMD(do_costume_bonus_transfer); open cmd_gm.cpp Search: ACMD(do_block_chat) { // GM이 아니거나 block_chat_privilege가 없는 사람은 명령어 사용 불가 if (ch && (ch->GetGMLevel() < GM_HIGH_WIZARD && ch->GetQuestFlag("chat_privilege.block") <= 0)) { ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("그런 명령어는 없습니다")); return; } char arg1[256]; argument = one_argument(argument, arg1, sizeof(arg1)); if (!*arg1) { if (ch) ch->ChatPacket(CHAT_TYPE_INFO, "Usage: block_chat <name> <time> (0 to off)"); return; } const char* name = arg1; long lBlockDuration = parse_time_str(argument); if (lBlockDuration < 0) { if (ch) { ch->ChatPacket(CHAT_TYPE_INFO, "잘못된 형식의 시간입니다. h, m, s를 붙여서 지정해 주십시오."); ch->ChatPacket(CHAT_TYPE_INFO, "예) 10s, 10m, 1m 30s"); } return; } sys_log(0, "BLOCK CHAT %s %d", name, lBlockDuration); LPCHARACTER tch = CHARACTER_MANAGER::instance().FindPC(name); if (!tch) { CCI * pkCCI = P2P_MANAGER::instance().Find(name); if (pkCCI) { TPacketGGBlockChat p; p.bHeader = HEADER_GG_BLOCK_CHAT; strlcpy(p.szName, name, sizeof(p.szName)); p.lBlockDuration = lBlockDuration; P2P_MANAGER::instance().Send(&p, sizeof(TPacketGGBlockChat)); } else { TPacketBlockChat p; strlcpy(p.szName, name, sizeof(p.szName)); p.lDuration = lBlockDuration; db_clientdesc->DBPacket(HEADER_GD_BLOCK_CHAT, ch ? ch->GetDesc()->GetHandle() : 0, &p, sizeof(p)); } if (ch) ch->ChatPacket(CHAT_TYPE_INFO, "Chat block requested."); return; } if (tch && ch != tch) tch->AddAffect(AFFECT_BLOCK_CHAT, POINT_NONE, 0, AFF_NONE, lBlockDuration, 0, true); } Add: ACMD(do_costume_bonus_transfer) { char arg1[256], arg2[256]; DWORD cell1, cell2; two_arguments(argument, arg1, sizeof(arg1), arg2, sizeof(arg2)); if (!*arg1 || !*arg2) return; str_to_number(cell1, arg1); str_to_number(cell2, arg2); if (cell1 < 0 || cell1 > INVENTORY_MAX_NUM || cell2 < 0 || cell2 > INVENTORY_MAX_NUM || cell1 == cell2) return; ch->CostumeBonusTransfer(cell1, cell2); } open root create uibonustransfer.py add: import ui import player import event import uiToolTip import exception import item import uiCommon import mouseModule import chat import net class BonusTransfer(ui.ScriptWindow): def __init__(self): ui.ScriptWindow.__init__(self) self.buttons, self.grids, self.costumes = {}, {}, {300 : {},303 : {},} self.__Load() self.tooltipItem = uiToolTip.ItemToolTip() self.tooltipItem.Hide() def __del__(self): ui.ScriptWindow.__del__(self) self.Close() def __Load_LoadScript(self, fileName): try: pyScriptLoader = ui.PythonScriptLoader() pyScriptLoader.LoadScriptFile(self, fileName) except: import exception exception.Abort("BonusTransfer.__Load_LoadScript") def __Load_BindObject(self): try: self.titleBar = self.GetChild("TitleBar") self.grids[300] = self.GetChild("Costume1") self.grids[303] = self.GetChild("Costume2") self.grids[2] = self.GetChild("Costume3") self.grids[3] = self.GetChild("Item") self.buttons[0] = self.GetChild("Button1") self.buttons[1] = self.GetChild("Button2") except: import exception exception.Abort("BonusTransfer.__Load_BindObject") self.titleBar.SetCloseEvent(ui.__mem_func__(self.Close)) self.grids[300].SetOverInItemEvent(ui.__mem_func__(self.OverInItem)) self.grids[300].SetOverOutItemEvent(ui.__mem_func__(self.OverOutItem)) self.grids[300].SetSelectEmptySlotEvent(ui.__mem_func__(self.SelectEmptySlot)) self.grids[300].SetUnselectItemSlotEvent(ui.__mem_func__(self.UnselectItemSlot)) self.grids[303].SetOverInItemEvent(ui.__mem_func__(self.OverInItem)) self.grids[303].SetOverOutItemEvent(ui.__mem_func__(self.OverOutItem)) self.grids[303].SetSelectEmptySlotEvent(ui.__mem_func__(self.SelectEmptySlot)) self.grids[303].SetUnselectItemSlotEvent(ui.__mem_func__(self.UnselectItemSlot)) self.grids[2].SetOverInItemEvent(ui.__mem_func__(self.OverInItem)) self.grids[2].SetOverOutItemEvent(ui.__mem_func__(self.OverOutItem)) self.grids[3].SetOverInItemEvent(ui.__mem_func__(self.OverInItem)) self.grids[3].SetOverOutItemEvent(ui.__mem_func__(self.OverOutItem)) self.grids[3].SetSelectEmptySlotEvent(ui.__mem_func__(self.SelectEmptySlot)) self.grids[3].SetUnselectItemSlotEvent(ui.__mem_func__(self.UnselectItemSlot)) self.buttons[0].SetEvent(self.TransferDialog) self.buttons[1].SetEvent(self.Close) def __Load(self): self.__Load_LoadScript("uiscript/bonustransfer.py") self.__Load_BindObject() def OnPressEscapeKey(self): self.Close() return TRUE def Shows(self): ui.ScriptWindow.Show(self) def Close(self): self.Hide() return TRUE def TransferDialog(self): self.ConfirmEkran = uiCommon.QuestionDialog() self.ConfirmEkran.SetText("Kostüm bonusu aktarılsın mı ?") self.ConfirmEkran.SetAcceptEvent(self.Transfer) self.ConfirmEkran.SetCancelEvent(self.NoTransfer) self.ConfirmEkran.Open() def Transfer(self): self.ConfirmEkran.Close() if self.costumes[300][0] is None or self.costumes[303][0] is None: return self.grids[3].ClearSlot(3) self.grids[3].RefreshSlot() self.grids[303].ClearSlot(303) self.grids[303].RefreshSlot() net.SendChatPacket("/costume_bonus_transfer "+str(self.costumes[300][0])+" "+str(self.costumes[303][0])) self.Close() def NoTransfer(self): self.ConfirmEkran.Close() def OverInItem(self, index): target = 303 if index == 400: itemVnum = player.GetItemIndex(self.costumes[300][0]) if self.costumes[303][0] is None: target = 300 stones = [player.GetItemMetinSocket(self.costumes[target][0], i) for i in xrange(player.METIN_SOCKET_MAX_NUM)] attr = [player.GetItemAttribute(self.costumes[target][0], i) for i in xrange(player.ATTRIBUTE_SLOT_MAX_NUM)] self.tooltipItem.SetControledToolTip(itemVnum, stones, attr) else: if self.costumes[index] is None: return self.tooltipItem.SetInventoryItem(self.costumes[index][0]) def OverOutItem(self): if self.tooltipItem: self.tooltipItem.HideToolTip() def SelectEmptySlot(self, selectedSlotPos): if mouseModule.mouseController.isAttached(): attachedSlotType = mouseModule.mouseController.GetAttachedType() attachedSlotPos = mouseModule.mouseController.GetAttachedSlotNumber() itemVnum = player.GetItemIndex(attachedSlotPos) itemCount = player.GetItemCount(attachedSlotPos) item.SelectItem(itemVnum) itemType = item.GetItemType() itemSubType = item.GetItemSubType() if selectedSlotPos == 301: selectedSlotPos = 300 if selectedSlotPos == 304: selectedSlotPos = 303 if selectedSlotPos == 403: if itemVnum == 76025: # costume bonus transfer item kod self.grids[3].SetItemSlot(403, itemVnum, itemCount) else: return mouseModule.mouseController.DeattachObject() if player.SLOT_TYPE_INVENTORY == attachedSlotType: if itemType != item.ITEM_TYPE_COSTUME: mouseModule.mouseController.DeattachObject() return elif itemType == item.ITEM_TYPE_COSTUME and itemSubType == 0: pass if attachedSlotPos in self.costumes: mouseModule.mouseController.DeattachObject() return self.grids[selectedSlotPos].SetItemSlot(selectedSlotPos, itemVnum) self.grids[selectedSlotPos].RefreshSlot() self.costumes[selectedSlotPos][0] = attachedSlotPos if selectedSlotPos == 300: self.grids[2].SetItemSlot(400, itemVnum) mouseModule.mouseController.DeattachObject() self.OverOutItem() def UnselectItemSlot(self, selectedSlotPos): isAttached = mouseModule.mouseController.isAttached() if not isAttached: self.costumes[selectedSlotPos][0] = 0 self.grids[selectedSlotPos].ClearSlot(selectedSlotPos) self.grids[selectedSlotPos].RefreshSlot() if selectedSlotPos == 300: self.grids[2].ClearSlot(400) self.grids[2].RefreshSlot() open game.py Search: "MyShopPriceList" : self.__PrivateShop_PriceList, Add: "kostumekran" : self.OpenBonusTransferWindow, game.py endline add: def OpenBonusTransferWindow(self): import uibonustransfer self.BonusTransfers = uibonustransfer.BonusTransfer() self.BonusTransfers.Show() open uiscript create bonustransfer.py Add: window = { "name" : "Bonusessssssss", "style" : ("movable", "float",), "x":(SCREEN_WIDTH - 188+8+8) / 2, "y":(SCREEN_HEIGHT - 335) / 2, "width" : 188+8+8, "height" : 335+42, "children" : ( { "name" : "Board", "type" : "board", "style" : ("attach",), "x" : 0, "y" : 0, "width" : 188+8+8, "height" : 335+42, "children" : ( { "name" : "TitleBar", "type" : "titlebar", "style" : ("attach",), "x" : 8, "y" : 8, "width" : 188+8+8-16, "color" : "gray", "children" : ( { "name":"TitleName", "type":"text", "x":0, "y":4, "text" : "Bonus Transfer", "horizontal_align":"center", "text_horizontal_align":"center" }, ), }, { "name" : "Background", "type" : "image", "x" : 8, "y" : 28, "image" : "comb1.tga", "children" : ( { "name" : "Costume1", "type" : "grid_table", "x" : 28, "y" : 67, "x_count" : 1, "y_count" : 3, "x_step" : 32, "y_step" : 32, "start_index" : 300, }, { "name" : "Costume2", "type" : "grid_table", "x" : 128, "y" : 67, "x_count" : 1, "y_count" : 3, "x_step" : 32, "y_step" : 32, "start_index" : 303, }, { "name" : "Costume3", "type" : "grid_table", "x" : 80, "y" : 185, "x_count" : 1, "y_count" : 3, "x_step" : 32, "y_step" : 32, "start_index" : 400, }, { "name" : "Item", "type" : "grid_table", "x" : 80, "y" : 14, "x_count" : 1, "y_count" : 1, "x_step" : 32, "y_step" : 32, "start_index" : 403, }, ), }, { "name" : "Button1", "type" : "button", "x" : 34, "y" : 342, "text" : "Aktar", "default_image" : "d:/ymir work/ui/public/middle_button_01.sub", "over_image" : "d:/ymir work/ui/public/middle_button_02.sub", "down_image" : "d:/ymir work/ui/public/middle_button_03.sub", }, { "name" : "Button2", "type" : "button", "x" : 34+70, "y" : 342, "text" : "İptal", "default_image" : "d:/ymir work/ui/public/middle_button_01.sub", "over_image" : "d:/ymir work/ui/public/middle_button_02.sub", "down_image" : "d:/ymir work/ui/public/middle_button_03.sub", }, ), }, ), } Lua: quest costumbonus begin state start begin when 20087.chat."Bonus Transfer" begin cmdchat("kostumekran") setskin(NOWINDOW) end end end uiscript add: comb1.tga [Hidden Content]
  7. With these modifications, you will enable colors in notice board, like this: Image: Tutorial: Open uitip.py and add this to the start of the file: import re Now find class TipBoard(ui.Bar):, and replace it with this: class TipBoard(ui.Bar): SCROLL_WAIT_TIME = 3.0 TIP_DURATION = 5.0 STEP_HEIGHT = 17 def __init__(self): ui.Bar.__init__(self) self.AddFlag("not_pick") self.tipList = [] self.curPos = 0 self.dstPos = 0 self.nextScrollTime = 0 self.width = 370 self.SetPosition(0, 70) self.SetSize(370, 20) self.SetColor(grp.GenerateColor(0.0, 0.0, 0.0, 0.5)) self.SetWindowHorizontalAlignCenter() self.__CreateTextBar() def __del__(self): ui.Bar.__del__(self) def __CreateTextBar(self): x, y = self.GetGlobalPosition() self.textBar = TextBar(370, 300) self.textBar.SetParent(self) self.textBar.SetPosition(3, 5) self.textBar.SetClipRect(0, y, wndMgr.GetScreenWidth(), y+18) self.textBar.Show() def __CleanOldTip(self): leaveList = [] for tip in self.tipList: madeTime = tip[0] if app.GetTime() - madeTime > self.TIP_DURATION: pass else: leaveList.append(tip) self.tipList = leaveList if not leaveList: self.textBar.ClearBar() self.Hide() return self.__RefreshBoard() def __RefreshBoard(self): self.textBar.ClearBar() index = 0 for tip in self.tipList: text = tip[1] rgb = tip[2] if rgb != (0,0,0): self.textBar.SetTextColor(rgb[0],rgb[1],rgb[2]) self.textBar.TextOut(0, index*self.STEP_HEIGHT, text) self.textBar.SetTextColor(255,255,255) index += 1 def SetTip(self, text): if not app.IsVisibleNotice(): return rgb = (0,0,0) mat = re.search("\|cFF([a-zA-Z0-9]+)\|h", text) if mat and mat.group(1): hexd = mat.group(1) rgb = tuple(int(hexd[i:i+2], 16) for i in (0, 2, 4)) curTime = app.GetTime() self.tipList.append((curTime, text, rgb)) self.__RefreshBoard() self.nextScrollTime = app.GetTime() + 1.0 if not self.IsShow(): self.curPos = -self.STEP_HEIGHT self.dstPos = -self.STEP_HEIGHT self.textBar.SetPosition(3, 5 - self.curPos) self.Show() def OnUpdate(self): if not self.tipList: self.Hide() return if app.GetTime() > self.nextScrollTime: self.nextScrollTime = app.GetTime() + self.SCROLL_WAIT_TIME self.dstPos = self.curPos + self.STEP_HEIGHT if self.dstPos > self.curPos: self.curPos += 1 self.textBar.SetPosition(3, 5 - self.curPos) if self.curPos > len(self.tipList)*self.STEP_HEIGHT: self.curPos = -self.STEP_HEIGHT self.dstPos = -self.STEP_HEIGHT self.__CleanOldTip() Example of use: --- in quests: notice_all("|cFF29BFBF|hColored text Example#1") notice("|cFFFF0000|hColored text Example #2") --- ingame: "n |cFF29BFBF|h Hello World" That's all
  8. Doose

    Tournament PvP

    Download Alternative download links → Mega Hello Metin2 Dev. I'm here to present a PvP Tournament. How does it work? Each player receives a medalion once he/she enters tournament map. (You can decide which map to use.) When a player kills inside the map, he receives a point which is saved in player.player database. You are able to upgrade your medalion to bigger versions, once you reach the "last" medalion, you're able to "absorve" it so you receive some bonuses. Player can't use auto-attack while inside tournament pvp map. Player can't use certain items such as sun elixir or moon elixir inside tournament map. You can see how many points you have if you check the medalion. DISCLAIMER. This system is more usefull for PvP servers, but you can convert it into PvM too. In case of any problem, report here and i'll fix it.
  9. Download Alternative download links → GitHub I couldn't find any of the events on the forum to avoid a double post so I decided to post it anyway in case it could be of use to someone. I will also update this topic with new events as needed and if requested. Quest Events / Triggers Fishing Mining Selling Buying Crafting Emoting See example below of how to use them. Usage Example
  10. [Hidden Content] [Hidden Content] Reversed from 22.2.7.0 Official Binary. + Football Metin Stone From 22.5.7.0: Functions were in different modules, I gathered them on a single module. Do not forget to get proto and other files for metin stones from official packs.
  11. M2 Download Center Download Here ( Internal ) [Hidden Content] The client part is from 2018 official root. Includes net.SendItemCombinationPacketCancel(), which was added later. Don't forget to add the slot marking.
  12. Download Metin2 Download The files contain the server side and quests example. Video: [Hidden Content] (yes, at the end the Biologist doesn't give me the rewards because I forgot to change a condition in the quest and I didn't wanna try to record it, again) So where is the interface file? I am sorry but I don't have any way to set up an interface for the original sources of the Client. I can share the logic I used: import ui import app import localeInfo import item import colorInfo import net import uiCommon import player import chat class uiResearchWindow(ui.ScriptWindow): BIOLOGIST_QUEST_DICT = { 0 : { 'item_wanted': 30006, 'pct' : 90, 'required': 20 }, 1 : { 'item_wanted': 30055, 'pct' : 90, 'required': 20 }, 2 : { 'item_wanted': 30047, 'pct' : 90, 'required': 20 }, 3 : { 'item_wanted': 30324, 'pct' : 90, 'required': 25 }, 4 : { 'item_wanted': 30015, 'pct' : 90, 'required': 20 }, 5 : { 'item_wanted': 30050, 'pct' : 90, 'required': 25 }, 6 : { 'item_wanted': 30198, 'pct' : 44, 'required': 30 }, 7 : { 'item_wanted': 30166, 'pct' : 42, 'required': 35 }, 8 : { 'item_wanted': 30495, 'pct' : 30, 'required': 15 }, 9 : { 'item_wanted': 100029, 'pct' : 38, 'required': 40 }, 10 : { 'item_wanted': 30329, 'pct' : 36, 'required': 45 }, 11 : { 'item_wanted': 100039, 'pct' : 34, 'required': 50 }, } MAX_ITEM_QUANTITY = 5 PCT_BONUS = 10 PCT_BONUS_PLUS = 30 BIOLOGIST_PCT_ADDER_VNUM = 102529 BIOLOGIST_PCT_ADDER_PLUS_VNUM = 102616 BIOLOGIST_TIME_DELETER_VNUM = 102517 def __init__(self): ui.ScriptWindow.__init__(self) self.remainTime = 0 self.receivedTime = app.GetTime() self.remainTimeText = None self.questID = 0 self.itemRequestedText = None self.pctSuccessText = None self.lastCountDownRefresh = 0 self.itemToDeliverCount = 1 self.itemToDeliverVnum = 0 self.questionDialog = None self.popupDialog = None self.deliveredItemsQty = 0 self.isOpened = False self.LoadWindow() def __del__(self): self.remainTime = 0 self.questID = 0 self.itemRequestedText = None self.pctSuccessText = None self.lastCountDownRefresh = 0 self.itemToDeliverVnum = 0 self.questionDialog = None self.popupDialog = None self.isOpened = False self.tooltipItem = 0 ui.ScriptWindow.__del__(self) def LoadWindow(self): try: PythonScriptLoader = ui.PythonScriptLoader() PythonScriptLoader.LoadScriptFile(self, "UIScript/researchwindow.py") except: import exception exception.Abort("ResearchWindow.LoadWindow.LoadUIScriptFiles") try: self.board = self.GetChild("board") self.remainTimeText = self.GetChild("ItemCDText") self.itemRequestedText = self.GetChild("ItemNametext") self.pctSuccessText = self.GetChild("ItemPctText") self.buttonTimeDeleterUP = self.GetChild("UpButton1") self.itemTimeDeleterTextqty = self.GetChild("ItemSlotbar1Text") self.buttonTimeDeleterDown = self.GetChild("DownButton1") self.buttonElixirUP = self.GetChild("UpButton2") self.buttonElixirTextQty = self.GetChild("ItemSlotbar2Text") self.buttonElixirDown = self.GetChild("DownButton2") self.buttonElixirPlusUP = self.GetChild("UpButton3") self.buttonElixirPlusTextQty = self.GetChild("ItemSlotbar3Text") self.buttonElixirPlusDown = self.GetChild("DownButton3") self.itemWanted = self.GetChild("current_item") self.itemWantedButtonUP = self.GetChild("UpButton0") self.itemWantedButtonText = self.GetChild("ItemSlotbar0Text") self.itemWantedButtonDown = self.GetChild("DownButton0") self.deliverItemButton = self.GetChild("exam_button") self.deliveredItemsText = self.GetChild("textItemDeliverdQty") self.notifySlot = self.GetChild("NotifySlot") self.elixirSlot = self.GetChild("elisir_10_slot") self.elixirPlusSlot = self.GetChild("elisir_30_slot") self.timeDeleterSlot = self.GetChild("time_deleter_slot") except: import exception exception.Abort("ResearchWindow.LoadWindow.BindObject") self.board.SetCloseEvent(ui.__mem_func__(self.Close)) self.buttonTimeDeleterUP.SAFE_SetEvent(self.__OnClickButtonTimeDeleterUP) self.buttonTimeDeleterDown.SAFE_SetEvent(self.__OnClickButtonTimeDeleterDown) self.buttonElixirUP.SAFE_SetEvent(self.__OnClickbuttonElixirUP) self.buttonElixirDown.SAFE_SetEvent(self.__OnClickbuttonElixirDown) self.buttonElixirPlusUP.SAFE_SetEvent(self.__OnClickbuttonElixirPlusUP) self.buttonElixirPlusDown.SAFE_SetEvent(self.__OnClickbuttonElixirPlusDown) self.itemWantedButtonUP.SAFE_SetEvent(self.__OnClickButtonItemWantedUP) self.itemWantedButtonDown.SAFE_SetEvent(self.__OnClickButtonItemWantedDown) self.deliverItemButton.SAFE_SetEvent(self.__OnClickDeliverButton) item.SelectItem(self.BIOLOGIST_PCT_ADDER_VNUM) itemName = item.GetItemName() self.pctAdderName = itemName item.SelectItem(self.BIOLOGIST_PCT_ADDER_PLUS_VNUM) itemName = item.GetItemName() self.pctAdderPlusName = itemName item.SelectItem(self.BIOLOGIST_TIME_DELETER_VNUM) itemName = item.GetItemName() self.timeDeleterName = itemName def SendDeliverPacket(self): net.SendBiologistManagerDeliverCommand(self.questID, self.itemToDeliverCount,\ int(self.buttonElixirTextQty.GetText()), int(self.buttonElixirPlusTextQty.GetText()),\ int(self.itemTimeDeleterTextqty.GetText())) def CanSendRequest(self): elixirPlayerCount = player.GetItemCountByVnum(self.BIOLOGIST_PCT_ADDER_VNUM) elixirPlusPlayerCount = player.GetItemCountByVnum(self.BIOLOGIST_PCT_ADDER_PLUS_VNUM) timeDeleterPlayerCount = player.GetItemCountByVnum(self.BIOLOGIST_TIME_DELETER_VNUM) itemToDeliverPlayerCount = player.GetItemCountByVnum(self.itemToDeliverVnum) if self.itemToDeliverCount > itemToDeliverPlayerCount or \ int(self.buttonElixirTextQty.GetText()) > elixirPlayerCount or\ int(self.buttonElixirPlusTextQty.GetText()) > elixirPlusPlayerCount or\ int(self.itemTimeDeleterTextqty.GetText()) > timeDeleterPlayerCount: self.popupDialog = uiCommon.PopupDialog() self.popupDialog.SetText(localeInfo.BIOLOGIST_MANAGER_OVERFLOW_ERROR) self.popupDialog.SetAcceptEvent(self.__OnClosePopupDialog) self.popupDialog.Open() return False elif self.GetRemainTime() > 0 and int(self.itemTimeDeleterTextqty.GetText()) <= 0: self.popupDialog = uiCommon.PopupDialog() self.popupDialog.SetText(localeInfo.BIOLOGIST_MANAGER_TIME_ERROR) self.popupDialog.SetAcceptEvent(self.__OnClosePopupDialog) self.popupDialog.Open() else: return True def __OnClickDeliverButton(self): if self.pct > 100: self.questionDialog = uiCommon.QuestionDialog() self.questionDialog.Open() self.questionDialog.SetText(localeInfo.BIOLOGIST_MANAGER_CHECK_PCT) self.questionDialog.SetAcceptText(localeInfo.UI_ACCEPT) self.questionDialog.SetCancelText(localeInfo.UI_DENY) self.questionDialog.SetAcceptEvent(lambda arg = True: self.QuestionDialogAnswer(arg)) self.questionDialog.SetCancelEvent(lambda arg = False: self.QuestionDialogAnswer(arg)) self.questionDialog.SetTop() else: if self.CanSendRequest() == True: self.SendDeliverPacket() def __OnClosePopupDialog(self): self.popupDialog = None def QuestionDialogAnswer(self, answer): if not self.questionDialog: return if answer == True: if self.CanSendRequest() == True: self.SendDeliverPacket() self.questionDialog.Close() self.questionDialog = None def __OnClickButtonItemWantedUP(self): if self.itemToDeliverCount >= self.MAX_ITEM_QUANTITY: chat.AppendChat(chat.CHAT_TYPE_INFO, localeInfo.BIOLOGIST_MANAGER_ITEMS_OVERFLOW % (str(self.MAX_ITEM_QUANTITY), self.itemName)) return if self.itemToDeliverCount + 1 > player.GetItemCountByVnum(self.itemToDeliverVnum): chat.AppendChat(chat.CHAT_TYPE_INFO, localeInfo.BIOLOGIST_manager_NOT_ENOUGH_ITEMS % (str(self.MAX_ITEM_QUANTITY), self.itemName)) return self.itemToDeliverCount += 1 self.itemWantedButtonText.SetText(str(self.itemToDeliverCount)) def __OnClickButtonItemWantedDown(self): if self.itemToDeliverCount <= 1: return self.itemToDeliverCount -= 1 self.itemWantedButtonText.SetText(str(self.itemToDeliverCount)) def __OnClickButtonTimeDeleterUP(self): number = int(self.itemTimeDeleterTextqty.GetText()) if number >= self.MAX_ITEM_QUANTITY: chat.AppendChat(chat.CHAT_TYPE_INFO, localeInfo.BIOLOGIST_MANAGER_ITEMS_OVERFLOW % (str(self.MAX_ITEM_QUANTITY), self.timeDeleterName)) return if number + 1 > player.GetItemCountByVnum(self.BIOLOGIST_TIME_DELETER_VNUM): chat.AppendChat(chat.CHAT_TYPE_INFO, localeInfo.BIOLOGIST_manager_NOT_ENOUGH_ITEMS % (str(self.MAX_ITEM_QUANTITY), self.timeDeleterName)) return self.itemTimeDeleterTextqty.SetText(str(number+1)) self.UpdatePct() def __OnClickButtonTimeDeleterDown(self): number = int(self.itemTimeDeleterTextqty.GetText()) if number <= 0: return self.itemTimeDeleterTextqty.SetText(str(number-1)) self.UpdatePct() def __OnClickbuttonElixirUP(self): number = int(self.buttonElixirTextQty.GetText()) if number >= self.MAX_ITEM_QUANTITY: chat.AppendChat(chat.CHAT_TYPE_INFO, localeInfo.BIOLOGIST_MANAGER_ITEMS_OVERFLOW % (str(self.MAX_ITEM_QUANTITY), self.pctAdderName)) return if number + 1 > player.GetItemCountByVnum(self.BIOLOGIST_PCT_ADDER_VNUM): chat.AppendChat(chat.CHAT_TYPE_INFO, localeInfo.BIOLOGIST_manager_NOT_ENOUGH_ITEMS % (str(self.MAX_ITEM_QUANTITY), self.pctAdderName)) return self.buttonElixirTextQty.SetText(str(number+1)) self.UpdatePct() def __OnClickbuttonElixirDown(self): number = int(self.buttonElixirTextQty.GetText()) if number <= 0: return self.buttonElixirTextQty.SetText(str(number-1)) self.UpdatePct() def __OnClickbuttonElixirPlusUP(self): number = int(self.buttonElixirPlusTextQty.GetText()) if number >= self.MAX_ITEM_QUANTITY: chat.AppendChat(chat.CHAT_TYPE_INFO, localeInfo.BIOLOGIST_MANAGER_ITEMS_OVERFLOW % (str(self.MAX_ITEM_QUANTITY), self.pctAdderPlusName)) return if number + 1 > player.GetItemCountByVnum(self.BIOLOGIST_PCT_ADDER_PLUS_VNUM): chat.AppendChat(chat.CHAT_TYPE_INFO, localeInfo.BIOLOGIST_manager_NOT_ENOUGH_ITEMS % (str(self.MAX_ITEM_QUANTITY), self.pctAdderPlusName)) return self.buttonElixirPlusTextQty.SetText(str(number+1)) self.UpdatePct() def __OnClickbuttonElixirPlusDown(self): number = int(self.buttonElixirPlusTextQty.GetText()) if number <= 0: return self.buttonElixirPlusTextQty.SetText(str(number-1)) self.UpdatePct() def GetRemainTime(self): return self.remainTime - (app.GetTime() - self.receivedTime) def ResearchRequestInfo(self, deliversCounter, successDeliveries, failedDeliveries, timeDeleterUses, elixirUses, elixirPlusUses, remainingTime): self.remainTime = remainingTime self.deliveredItemsQty += successDeliveries self.deliveredItemsText.SetText(str(self.deliveredItemsQty) + "/" + str(self.BIOLOGIST_QUEST_DICT[self.questID]['required'])) self.itemWanted.SetItemSlot(0, self.itemToDeliverVnum, player.GetItemCountByVnum(self.itemToDeliverVnum)) text = "" if failedDeliveries == 1: text = localeInfo.BIOLOGIST_SYSTEM_MESSAGE_SINGLE_FAIL % (deliversCounter, self.itemName, successDeliveries, failedDeliveries, self.pctAdderName, elixirUses, self.pctAdderPlusName, elixirPlusUses, self.timeDeleterName, timeDeleterUses) else: text = localeInfo.BIOLOGIST_SYSTEM_MESSAGE_MULTIPLE_FAIL % (deliversCounter, self.itemName, successDeliveries, failedDeliveries, self.pctAdderName, elixirUses, self.pctAdderPlusName, elixirPlusUses, self.timeDeleterName, timeDeleterUses) self.multiHolder = ui.GenerateMultiLine(self.notifySlot, text, 300, colorInfo.GOLD) for i in self.multiHolder: totalH = self.multiHolder[0].GetTextSize()[1] * len(self.multiHolder) + 3 * (len(self.multiHolder) - 1) i.SetPosition(3, 80 / 2 - totalH / 2 + 3 * self.multiHolder.index(i) + i.GetTextSize()[1] * self.multiHolder.index(i)) self.UpdateSlots() def UpdateInfo(self, questID, remain_time, deliveredItems): self.remainTime = remain_time self.questID = questID self.itemToDeliverVnum = self.BIOLOGIST_QUEST_DICT[questID]['item_wanted'] self.deliveredItemsQty = deliveredItems self.deliveredItemsText.SetText(str(self.deliveredItemsQty) + "/" + str(self.BIOLOGIST_QUEST_DICT[self.questID]['required'])) item.SelectItem(self.itemToDeliverVnum) self.itemName = item.GetItemName() self.itemRequestedText.SetText(self.itemName) self.itemWanted.SetItemSlot(0, self.itemToDeliverVnum, player.GetItemCountByVnum(self.itemToDeliverVnum)) self.UpdatePct() self.UpdateTimer() def UpdatePct(self): elixir_qty = int(self.buttonElixirTextQty.GetText()) elixir_plus_qty = int(self.buttonElixirPlusTextQty.GetText()) if elixir_qty > 0 and elixir_plus_qty > 0: self.pct = self.BIOLOGIST_QUEST_DICT[self.questID]['pct'] + self.PCT_BONUS + self.PCT_BONUS_PLUS elif elixir_qty > 0: self.pct = self.BIOLOGIST_QUEST_DICT[self.questID]['pct'] + self.PCT_BONUS elif elixir_plus_qty > 0: self.pct = self.BIOLOGIST_QUEST_DICT[self.questID]['pct'] + self.PCT_BONUS_PLUS elif elixir_qty <= 0 and elixir_plus_qty <= 0: self.pct = self.BIOLOGIST_QUEST_DICT[self.questID]['pct'] elif elixir_qty <= 0: self.pct -= self.PCT_BONUS elif elixir_plus_qty <= 0: self.pct -= self.PCT_BONUS_PLUS self.pctSuccessText.SetText(str(self.pct) + "%") def OnUpdate(self): if app.GetTime() <= self.lastCountDownRefresh + 1.0: return self.lastCountDownRefresh = app.GetTime() self.UpdateTimer() def UpdateTimer(self): remain_time = self.GetRemainTime() if remain_time <= 0: self.remainTimeText.SetPackedFontColor(colorInfo.GREEN) self.remainTimeText.SetText(localeInfo.BIOLOGIST_MANAGER_CAN_DELIVER) else: self.remainTimeText.SetPackedFontColor(colorInfo.RED) self.remainTimeText.SetText("%s" % (str(localeInfo.SecondsToClock(remain_time)))) def SetItemToolTip(self, tooltipItem): self.tooltipItem = tooltipItem def OverInItem(self, itemVnum): if not self.tooltipItem: return self.tooltipItem.ClearToolTip() metinSlot = [] for i in range(player.METIN_SOCKET_MAX_NUM): metinSlot.append(0) attrSlot = [] for i in range(player.ATTRIBUTE_SLOT_MAX_NUM): attrSlot.append((0, 0, 0)) realValue = [] for i in range(player.REAL_VALUES_MAX_NUM): realValue.append(0) self.tooltipItem.AddItemData(itemVnum, metinSlot, attrSlot, realValue) def OverOutItem(self): if 0 != self.tooltipItem: self.tooltipItem.HideToolTip() def UpdateSlots(self): self.elixirSlot.SetItemSlot(0, self.BIOLOGIST_PCT_ADDER_VNUM, player.GetItemCountByVnum(self.BIOLOGIST_PCT_ADDER_VNUM)) self.elixirPlusSlot.SetItemSlot(0, self.BIOLOGIST_PCT_ADDER_PLUS_VNUM, player.GetItemCountByVnum(self.BIOLOGIST_PCT_ADDER_PLUS_VNUM)) self.timeDeleterSlot.SetItemSlot(0, self.BIOLOGIST_TIME_DELETER_VNUM, player.GetItemCountByVnum(self.BIOLOGIST_TIME_DELETER_VNUM)) self.itemWanted.SetItemSlot(0, self.itemToDeliverVnum, player.GetItemCountByVnum(self.itemToDeliverVnum)) def SetSlotsEvents(self): self.elixirSlot.SetEvent(ui.SLOT_OVER_IN_ITEM, self.OverInItem, self.BIOLOGIST_PCT_ADDER_VNUM) self.elixirPlusSlot.SetEvent(ui.SLOT_OVER_IN_ITEM, self.OverInItem, self.BIOLOGIST_PCT_ADDER_PLUS_VNUM) self.timeDeleterSlot.SetEvent(ui.SLOT_OVER_IN_ITEM, self.OverInItem, self.BIOLOGIST_TIME_DELETER_VNUM) self.elixirSlot.SetEvent(ui.SLOT_OVER_OUT_ITEM, self.OverOutItem) self.elixirPlusSlot.SetEvent(ui.SLOT_OVER_OUT_ITEM, self.OverOutItem) self.timeDeleterSlot.SetEvent(ui.SLOT_OVER_OUT_ITEM, self.OverOutItem) self.itemWanted.SetEvent(ui.SLOT_OVER_IN_ITEM, self.OverInItem, self.itemToDeliverVnum) self.itemWanted.SetEvent(ui.SLOT_OVER_OUT_ITEM, self.OverOutItem) def Open(self, questID, remainTime, deliveredItems): self.UpdateInfo(questID, remainTime, deliveredItems) self.UpdateSlots() self.SetSlotsEvents() self.Show() self.isOpened = True def IsOpened(self): return self.isOpened def Close(self, isOpened = True): if self.tooltipItem: self.tooltipItem.ClearToolTip() self.isOpened = isOpened self.Hide() def Destroy(self): self.ClearDictionary() if self.tooltipItem: self.tooltipItem.ClearToolTip() self.remainTime = 0 self.remainTimeText = None self.questID = 0 self.itemRequestedText = None self.pctSuccessText = None self.lastCountDownRefresh = 0 self.itemToDeliverVnum = 0 self.questionDialog = None self.tooltipItem = 0 self.popupDialog = None def OnPressEscapeKey(self): self.Close() return True Don't just copy and paste it, it won't work because of: realValue = [] for i in range(player.REAL_VALUES_MAX_NUM): realValue.append(0) self.tooltipItem.AddItemData(itemVnum, metinSlot, attrSlot, realValue) or the whole SetSlotsEvents function: def SetSlotsEvents(self): self.elixirSlot.SetEvent(ui.SLOT_OVER_IN_ITEM, self.OverInItem, self.BIOLOGIST_PCT_ADDER_VNUM) self.elixirPlusSlot.SetEvent(ui.SLOT_OVER_IN_ITEM, self.OverInItem, self.BIOLOGIST_PCT_ADDER_PLUS_VNUM) self.timeDeleterSlot.SetEvent(ui.SLOT_OVER_IN_ITEM, self.OverInItem, self.BIOLOGIST_TIME_DELETER_VNUM) self.elixirSlot.SetEvent(ui.SLOT_OVER_OUT_ITEM, self.OverOutItem) self.elixirPlusSlot.SetEvent(ui.SLOT_OVER_OUT_ITEM, self.OverOutItem) self.timeDeleterSlot.SetEvent(ui.SLOT_OVER_OUT_ITEM, self.OverOutItem) self.itemWanted.SetEvent(ui.SLOT_OVER_IN_ITEM, self.OverInItem, self.itemToDeliverVnum) self.itemWanted.SetEvent(ui.SLOT_OVER_OUT_ITEM, self.OverOutItem) If you make one, please feel free to share, I'll update the topic and give you credits. Why do you require to go back to the biologist instead of doing everything from the interface? Two reasons: it's based on my server where we want the player to go back to the biologist. there's already people selling that type of interface, if you so prefer The system requires c++20 (serverside) But I have C++17, what do I do then? Remove the constness from std::array(older version) and std::view_string, therefore even on the loop(older version): for(const auto& val : BiologistQuestNames) //here remove const { if(IsQuestStatusFindItem(ch, val) == true){ SendBiologistManagerOpenCommand(ch, iterVal); return true; } ++iterVal; } But I have C++14/C+11, what do I do then? Remove std::view_string and use either std::string or const char* and replace: utils::view_to_string(BiologistQuestStateCollect); with whatever is the way to convert a const char* to std::string (if you use const char*) [Hidden Content] Remove also the namespace on top: //TO REMOVE namespace utils{ std::string string_view_concat(std::string_view a, std::string_view b){ std::string rv(a.size() + b.size(), '\0'); std::copy(b.begin(), b.end(), std::copy(a.begin(), a.end(), rv.begin())); return rv; } std::string view_to_string(std::string_view a){ std::string st = {a.begin(), a.end()}; return st; } } the concat function can be replaced with the + operator (both have to be strings, not const* char): std::string new_string = old_string + string_to_concat; I still use C++98/03 Don't bother downloading these files. I want to set the cooldown even when some one fails, not only on success, how can I do that? Replace: else{ //set cooldown for success delivery pPC->SetFlag(timerFlag, get_global_time() + BiologistQuestCooldowns.at(questID)); ++successDeliveries; } } else{ //fail, but we don't set the cooldown for failing itemToDeliver->SetCount(itemToDeliver->GetCount() - 1); //remove the item to deliver pPC->SetFlag(timerFlag, 0); ++failedDeliveries; } with: else{ //set cooldown for success delivery pPC->SetFlag(timerFlag, get_global_time() + BiologistQuestCooldowns.at(questID)); ++successDeliveries; } } else{ //fail, but we don't set the cooldown for failing itemToDeliver->SetCount(itemToDeliver->GetCount() - 1); //remove the item to deliver pPC->SetFlag(timerFlag, get_global_time() + BiologistQuestCooldowns.at(questID)); ++failedDeliveries; } What if I don't wanna use elixirs, time deleter or something else? Try yourself first, if you encounter any error during your endeavour, just ask (don't be afraid to ask, people might help you). Why the double checks in the client AND the server????? I'll never forget: "what if they craft the packet?" "...fudge. Unlikely, but you are right." Is it bug free??? I'll never make such claims, if you find some, please report them
  13. Hey folks, Well, I won't go in details, but the official use this for some quests, including their over9refine. I noticed we missed that, it was added somewhere between 2014 and 2015, probably along the new armors. I am aware that the tooltip is a bit crazy, but it is like this on the official too so I share it as is. NOTE: This is not reversed. Only the root part is official. Have a nice day
  14. M2 Download Center Download Here ( Internal ) Download Here ( GitHub ) [Hidden Content]
  15. M2 Download Center Download Here ( Internal ) open pvp.cpp add in ending bool CPVPManager::IsPvP(LPCHARACTER pkChr) { CPVPSetMap::iterator it = m_map_pkPVPSetByID.find(pkChr->GetPlayerID()); if (it == m_map_pkPVPSetByID.end()) return false; TR1_NS::unordered_set<CPVP*>::iterator it2 = it->second.begin(); while (it2 != it->second.end()) { CPVP * pkPVP = *it2++; if (pkPVP->IsFight()) return true; } return false; } open pvp.h find void Process(); add after bool IsPvP(LPCHARACTER pkChr); open questlua_pc.cpp add #include #include "pvp.h" code int is_pvp(lua_State* L) { LPCHARACTER ch = CQuestManager::instance().GetCurrentCharacterPtr(); lua_pushboolean(L, CPVPManager::instance().IsPvP(ch)); return 1; } quest quest tested begin state start begin when dead or login or logout begin if pc.is_pvp() then say("test") return end end end end i tested
  16. Download Alternative download links → Github I like Aeldra's Nilay Dungeon so I made it. Allows you to hit a metinstone up to 50% health and start a stage within the stage. The logic is simple, do this to bosses etc. You can also adapt it. I added an example dungeon quest usage, you can easily understand the logic.
  17. Download By Aslan Display of the stage as a green progress bar and text (here E2 e.g.) An info text what you have to do as a slider Progress bar can be used for remaining monster count or remaining time Example (For the rest please see the quest example) -- If you set a mob counter, do it in front of it. If you ask why the second dungeon flag is set, -- ihow counts "d.count_monster()" weird if you always print it out when mobs are killed. d.setf("minimap_dungeoninfo_start_mob_count", d.count_monster()) d.setf("minimap_dungeoninfo_killed_mob_count", d.count_monster()) d.set_minimap_dungeoninfo_status(1) -- enable dungeon info for dungeon instance d.set_minimap_dungeoninfo_stage(aktuelle stage, max stage) -- I always set maxstage higher than you have stages so that stage complete is displayed after the bosskill, so to speak -- the progress bar can be controlled as follows (3 options): d.set_minimap_dungeoninfo_gauge(0, 0) -- Disables the counter completely d.set_minimap_dungeoninfo_gauge(1, (un)gekillte mobanzahl, gesamtmobanzahl) -- activates the mob counter d.set_minimap_dungeoninfo_gauge(2, zeit in sekunden) -- activates the time counter d.set_minimap_dungeoninfo_notice("Töte alle Monster") -- Insert your info text here. Text output is also possible for multilang via the Translate.lua.
  18. Download By Aslan The World Lottery System is comparable to the real lottery in the real world. However, in order to make the jackpot a little easier to win, you don't play 6out of 49 but 4out of 30. So the chance of winning is 1:27,405. But of course you also win with 1 identical, 2 identical and 3 identical numbers. The winning amount can be determined as a percentage of the jackpot and additionally by a cap. Features If you buy a lottery ticket, x% of the purchase price is added to the lottery pot and is active in the next draw. With each new draw, all lottery tickets for the corresponding draw are evaluated and, if you win, the winnings are spilled. Players' winning totals are deducted from the jackpot for the next draw. If a player has won the jackpot or if the value of the jackpot is below the sum X due to the payouts, the jackpot starts at the specified sum from the configs. If a player has won with his lottery ticket, the "Collect prize" button must be pressed. The prize then ends up in Yangspeicher and from there it can be collected in the correct inventory. Miscellaneous In addition to the basic system, there are 3 more windows: Information board The last X lottery draws Lotto ranking list Options_lottery.h #define ENABLE_ASLAN_LOTTERY #ifdef ENABLE_ASLAN_LOTTERY // General Options #define NEW_LOTTERY_NUMBERS_ACTIVATE 1 // New draws take place #define LOTTO_TICKET_COST 5000000 // How much a new ticket costs #define LOTTO_PUT_TICKET_COST_PERC_TO_JACKPOT 75 // What percentage of the ticket cost goes into the next jackpot // Percent per same number (1 = 0,01% || 10 = 0,1% || 100 = 1% > 5% from 100.000.000 = 5.000.000) #define PERC_REWARD_FOR_1_SAME_NUMBER 3 // Percent reward of Jackpot for 1 same number #define MAX_REWARD_FOR_1_SAME_NUMBER 5000000 // Max reward of Jackpot for 1 same number ( Deactivate when is 0 ) #define PERC_REWARD_FOR_2_SAME_NUMBER 10 // Percent reward of Jackpot for 2 same number #define MAX_REWARD_FOR_2_SAME_NUMBER 0 // Max reward of Jackpot for 2 same number ( Deactivate when is 0 ) #define PERC_REWARD_FOR_3_SAME_NUMBER 100 // Percent reward of Jackpot for 3 same number #define MAX_REWARD_FOR_3_SAME_NUMBER 0 // Max reward of Jackpot for 3 same number ( Deactivate when is 0 ) //#define TEST_MODE #ifdef TEST_MODE #define LOTTO_NUMBER_1 1 #define LOTTO_NUMBER_2 1 #define LOTTO_NUMBER_3 1 #define LOTTO_NUMBER_4 1 #define GEN_NEXT_NUMBER_TIME_SEC 60 #endif // TEST_MODE #define MIN_JACKPOT_MONEY 250000000 #define GENERATE_NEW_LOTTO_NUMBERS_PULSE_MIN 2 // The intervals at which new lottery numbers are generated #endif
  19. Download Metin2 Download You can enchant your items directly from the chosen NPC. *You can set the average & skill dmg from the quest. Preview: [Hidden Content] Download: MEGA VirusTotal
  20. M2 Download Center Download Here ( Internal ) Hello! TELEPORT SYSTEM - HOW TO How To: Unpack root.eix/.epk open game.py add: import uiteleport Search: self.__ProcessPreservedServerCommand() Add under: self.teleport = uiteleport.TeleportWindow() Search: __ServerCommand_Build Add under: "Teleport" : self.Teleport, Search: OpenQuestWindow(self, skin, idx): Replace: def OpenQuestWindow(self, skin, idx): if constInfo.CApiSetHide == 1: net.SendQuestInputStringPacket(str(constInfo.SendString)) constInfo.CApiSetHide = 0 return self.interface.OpenQuestWindow(skin, idx) Add new function in game.py: def Teleport(self, getString): if getString.find("index") != -1: self.teleport.UpdateIndex(int(getString.split("x")[1])) else: constInfo.SendString = str(self.teleport.SendToServer) constInfo.CApiSetHide = 1 To give a button function to open it you can, for example, how I did it use the J button. For this you need to edit the following function: Fals you want to use a different button, you can do it in the game.py following command to open / close: self.teleport.Open() And finally the constInfo.py open and the following reinschreiben above: CApiSetHide = 0 SendString = "" The following quest under the Notes "teleport_edit.zip" it adds in euerm orders Quest (Quest by Kilroy.). If it does not work you have it entered incorrectly say the system. Ingame start (STRG+J) root.rar LINK DELETED. teleport_edit.rar [Hidden Content] BTW: Sorry for my bad English. Kind Regards, .Avua By: Ace / CryPrime
  21. Download GitHub or Mega Hi guys! As we all know, the only trigger for quests on metin2 that is activated upon receipt of an item is pickup trigger (when x.pickup begin). Unfortunately, this only covers the case where you pick up an item from the ground, so it is very limited. Some people were asking to improve this, so I created a new trigger, with which you can cover other cases. For now, it works in these cases: Trade Buy from npc shop Buy from npc shopEx Buy from pc shop Pick up from ground /item command (for testing) Example: Spoiler This is just a draft, it already works, but if you have systems that interact with items, you will have to call it up under those systems as well. You can report to me below any new cases of getting items for new systems, I will help you.
  22. M2 Download Center Download Here ( GitHub ) Download Here ( MEGA ) Download Here ( Internal ) I came across this new insert event by quests which I had to use while I was developing the 6th & 7th Attribute System so I decided to share this little feature and it could be useful in some situations.
  23. M2 Download Center Download Here ( Internal ) Download Here ( GitHub ) Hi, I made this system not so long ago, benefited the script from the official server and worked on the C++, just decided to share this system with everyone because I no longer care much about it. It’s not really a big of a system and most likely it could be leaked from some traitor so honestly, I rather share it myself. Instead of adding the tutorial here I will link a repository to the guidelines. Demonstration The inventory slot marking works best with @xP3NG3Rx's release. I do not support the implementation of the system neither does do author of the inventory slot marking, I believe.
  24. Hello everyone. Back in WoM2, at some point in time, there was this task we had to collect the locations of all monsters featured in hunting quests. One could possibly think it'd be an easy task, but I cannot imagine any of us going through all data from all quests and note down every single one of those locations. This was a particular issue for WoM2 since it ran with something we called "The Dynasties". The kingdoms or empires as we know them would have been removed, mainly due to very bad seasons with the player count. As such, the concept of Dynasty fit the game just well enough. For those who don't know, we got rid of the standard first maps and built a whole new one, making it one common first village for all dynasties. Now what kinda problems would this bring? I'd the say the hardest to wrap our heads on is adaptation. As a new map comes in, knowing the best spots, the best spawns and where to find certain enemies, come to play for a player about to explore the new area their favorite server brought for them. However, we're talking about adapting a completely new server start - which is a very big deal and not always a very good idea. As a server that possessed many risky concepts and quests of many different kinds, guiding the player through that painful and dislikeable start was a must. And on this case, to, at least, facilitate the search of the monsters the quest was telling the player to slay. This little python module I named make_monster_location.py, iterates over all the maps in the map folder, reads their regen, collects groups and groups of groups, and, ultimately, notes down every single location for every monster identified in said map. This is later saved the proper Lua multidimensional table, to be used by quest-functions show_mob_location() and clear_mob_location(). Upon finishing, a Lua module named monster_location.lua is generated. This one must be loaded (through dofile()) in one of your bootload libraries (i.e: settings.lua, questlib.lua, ...). In the spoiler below there is a sample of the generated Lua file: I talked about two new quest-functions previously. Those are the ones you should use in your quests, each one of them works as you can possibly figure: show_mob_location(mob_vnum, map_index, map_x, map_y) -- Displays the locations of the given monster, as long as the player is in the specified map. clear_mob_location() -- Clears the signals inserted by the previous function call. And below, there is the module: As it follows one single file, I saw no purpose in uploading it - but if I see it fits, sure can do and edit the post later. All credits for the idea go to Shogun.
  25. M2 Download Center Download Here ( Internal ) You could use this code to create the relative functions in lua: mysql_direct_query, get_table_postfix, mysql_escape_string. The mysql_direct_query returns two values: the first one contains the count of how many rows had been affected (works fine for select, insert, update queries, and so on) and the second one a table containing all the information retrieved by a select query (empty if not). The field type will be auto-detected, which means: A numeric field will be pushed as lua number A BLOB one will be pushed as table byte per byte A NULL field will be pushed as nil (not displayed in iteration) The other ones will be pushed as strings (be aware of this) Example Example 1 Example 2 Example 3 How To questlua_global.cpp #include "db.h" int _get_table_postfix(lua_State* L) { lua_pushstring(L, get_table_postfix()); return 1; } #ifdef _MSC_VER #define INFINITY (DBL_MAX+DBL_MAX) #define NAN (INFINITY-INFINITY) #endif int _mysql_direct_query(lua_State* L) { if (!lua_isstring(L, 1)) return 0; int i=0, m=1; MYSQL_ROW row; MYSQL_FIELD * field; MYSQL_RES * result; std::auto_ptr<SQLMsg> pMsg(DBManager::instance().DirectQuery("%s", lua_tostring(L, 1))); if (pMsg.get()) { // ret1 (number of affected rows) lua_pushnumber(L, pMsg->Get()->uiAffectedRows); //-1 if error such as duplicate occurs (-2147483648 via lua) // if wrong syntax error occurs (4294967295 via lua) // ret2 (table of affected rows) lua_newtable(L); if ((result = pMsg->Get()->pSQLResult) && !(pMsg->Get()->uiAffectedRows == 0 || pMsg->Get()->uiAffectedRows == (uint32_t)-1)) { while((row = mysql_fetch_row(result))) { lua_pushnumber(L, m); lua_newtable(L); while((field = mysql_fetch_field(result))) { lua_pushstring(L, field->name); if (!(field->flags & NOT_NULL_FLAG) && (row[i]==NULL)) { // lua_pushstring(L, "NULL"); lua_pushnil(L); } else if (IS_NUM(field->type)) { double val = NAN; lua_pushnumber(L, (sscanf(row[i],"%lf",&val)==1)?val:NAN); } else if (field->type == MYSQL_TYPE_BLOB) { lua_newtable(L); for (DWORD iBlob=0; iBlob < field->max_length; iBlob++) { lua_pushnumber(L, row[i][iBlob]); lua_rawseti(L, -2, iBlob+1); } } else lua_pushstring(L, row[i]); lua_rawset(L, -3); i++; } mysql_field_seek(result, 0); i=0; lua_rawset(L, -3); m++; } } } else {lua_pushnumber(L, 0); lua_newtable(L);} return 2; } int _mysql_escape_string(lua_State* L) { char szQuery[1024] = {0}; if (!lua_isstring(L, 1)) return 0; DBManager::instance().EscapeString(szQuery, sizeof(szQuery), lua_tostring(L, 1), strlen(lua_tostring(L, 1))); lua_pushstring(L, szQuery); return 1; } { "get_table_postfix", _get_table_postfix }, { "mysql_direct_query", _mysql_direct_query }, { "mysql_escape_string", _mysql_escape_string }, Author: Marty
×
×
  • 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.