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
    • About Us
    • Top Metin2
    • Github
    • Wiki
    • Pillory
    • Discord
  • Community
    • Member Representations
    • Off Topic
  • Metin2
    • General
    • Private Servers
  • Help Center
    • Questions & Answers
    • File Requests
  • 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
  • Marketplace
    • Join the Marketplace - Sales & Services
    • Searching
  • Miscellaneous
    • Trash
    • Archive
    • Temporary
    • Metin2 Download
  • Forum Bureau of Investigation's Forum

Product Groups

  • Header & Footer
  • Topic & Forum
  • Pack
  • Small Advertisement
  • Large Advertisement

Blogs


Find results in...

Find results that contain...


Date Created

  • Start

    End


Last Updated

  • Start

    End


Filter by number of...

Joined

  • Start

    End


Group


My Message


Country


Nationality


Github


Gitlab


Discord


Skype


Website


Marketplace


Game Server

  1. 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.
  2. 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
  3. Download Metin2 Download [Hidden Content] c++11 is enough.
  4. 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:
  5. M2 Download Center Download Here ( Internal ) Download Here ( GitHub ) [Hidden Content]
  6. 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
  7. 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 and std::view_string, therefore even on the loop: 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
  8. M2 Download Center Download Here ( Internal ) [Hidden Content] Video: Images: Client Side is from 2018 official root. Of course I've made minor changes for slot marking and cheque(if app. checks) You have to add slot marking too. Mysql used to retrieve data during game launch, and to backup(default 1 hour, you can change at conf.txt)
  9. 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
  10. 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.
  11. M2 Download Center Download Here ( Internal ) [Hidden Content] Client side is from 2018 official root. Mount transmutation 2018 didn't exist in root, I made minor edits. However, to use mount transmutation, you need to edit it according to your own mount system. I created a new slot effect so that it doesn't interfere with other systems (highlight) etc. You can use this sloteffect and the diffusecolor function elsewhere. ***Add this before adding this system:
  12. M2 Download Center Download Here ( Internal ) Hello guys.. So much people got problem with this system then i do FULL TuT exclusive for metin2dev All is tested and all works TuT included in all FILES.. New Link: Pass: When you find some bug post it here.. I will try to fix it.. Have Fun //EnZi EDITED: #New Link UPDATE cmd_general.cpp - Added some new code char_battle.cpp - Added some new code ProtoReader.cpp - Added some new code questlua_pc - Repaired code item_proto - Query for navicat Quest added UP LINK UPDATED fixed quest: [Hidden Content]
  13. 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.
  14. 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.
  15. M2 Download Center Download Here ( Internal ) Hi there Devs, I would like to share my "little" system. If you aren't interested in the introduction/preview etc. and you just want to download it and put in to your server, just scroll down until the "[How-To] Set up" subtitle. The story Firstly let me tell this system's story. I've got an idea one year before, that it would be good if the players would be able to put their items into a "global" system where they could see the other player's items, and they could buy it for DC or gold (that time I worked with the latest vanilla core (not with the source)). Then in the following 8 days I made it (it took about 80-90 working hours). Originally the system was created for one of my friend's server. but this server has never started, and nobody used this system. After some mounts I've decided to publish it on the Hungarian forum, because it won't worth to work on it for long hours if nobody uses it and its just collecting dust on my computer. Then I've published it on the 2nd of December, 2014. After some time I've decided to translate it into English and I've got a new idea for a new feature. This feature was: the trade system (I will explain its working later). This idea inspired by one of the players (from a server where this system was able to use). He told me that it would be better if they could set the gold price via an item (what's value is very high). Then with more than 180 working hours (totally) behind my back I'm here. Overview [How-To] Set up Customizing the tradehouse Questions and Answers Notes changelog: 19th of August, 2015: I publicated the tradehouse here. my toDo list: add logging for the system (the released version don't log the actions in the tradehouse) Thanks for reading the topic, if you have any problem/remark feel free to ask it here or write me a PM. Have a good day!
  16. M2 Download Center Download Here ( Internal ) [Hidden Content]- Images & Video: Some Informations From Black: If you have a problem, you can send me a private message. You know me, I reply to all private messages
  17. 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.
  18. 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.
  19. 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
  20. Roti

    Mob Move

    M2 Download Center Download ( Internal ) Download ( mega ) Hi Everyone! There are many functions that can make the mob move after spawning (mainly dungeons). In this way, however, it is possible to move it later and queue more movements in a row. The idea is to spawn a mob by giving it a series of paths and, in case, to queue others. Obviously you will have to keep the monster vid in a variable if you want to move it again.
  21. M2 Download Center Download Here ( Internal ) Link
  22. I solved the problem that how to get the vid from player name if the player is on other map. Its a python-quest communication. quest: quest send_vid begin state start begin function GetPlayerName() cmdchat("init_getInfo") local name = input(cmdchat("getInfo")) cmdchat("ignore_getInfo") return name end when button or info begin local vid = find_pc_by_name(send_vid.GetPlayerName()) cmdchat("sendInfo "..vid) end when login begin cmdchat("saveInfo "..q.getcurrentquestindex()) end end end python(game.py): #GET PLAYER VID FROM SERVER def InitPlayerNameInfo(self): constInfo.IGNORE_SEND_PLAYER_NAME = 1 def IgnorePlayerNameInfo(self): constInfo.IGNORE_SEND_PLAYER_NAME = 0 def SendPlayerNameInfo(self): net.SendQuestInputStringPacket(constInfo.PLAYER_NAME) def GetPlayerVIDInfo(self, vid): constInfo.PLAYER_VID = vid def SavePlayerVIDInfo(self, value): constInfo.get_vid = value #END OF GET PLAYER VID FROM SERVER for example i used this for whisper trade python(uiwhisper.py): def StartExchange(self): constInfo.PLAYER_NAME = self.targetName event.QuestButtonClick(int(constInfo.get_vid)) vid = int(constInfo.PLAYER_VID) if vid != 0: chat.AppendChat(chat.CHAT_TYPE_INFO, localeInfo.EXCHANGE_TARGET.format(self.targetName)) #if player is online net.SendExchangeStartPacket(vid) self.interface.StartExchange(self.targetName) else: chat.AppendChat(chat.CHAT_TYPE_INFO, localeInfo.EXCHANGE_NO_TARGET.format(self.targetName)) #if player is offline def EndExchange(self): self.interface.EndExchange() def RefreshExchange(self): self.interface.RefreshExchange() and of course i connected a button for the function: self.tradeButton.SetEvent(ui.__mem_func__(self.StartExchange))
  23. M2 Download Center Download Here ( Internal ) Download Here ( GitHub ) Original system forked from Mali61 Link Adapted by ASIKOO
  24. M2 Download Center Download Here ( Internal ) Download Here ( GitHub )
×
×
  • 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.