Jump to content

Search the Community

Showing results for tags 'python'.

  • 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. I made this script because I was too lazy to do it from the source... The title says it all. Steps are in readme.
  2. Download GitHub (Click Here) / Mega (Click Here) To implement this feature, you need the Mythical Grade expansion implemented on your server! Google is your friend. Exchange of Dragon Stone Bonuses "Change Attribute" To change the bonuses of a mythical dragon stone unlike the different cases, you will have to select another option from the alchemist, that is "Mythical Dragon Stone: Bonus Change". You will need the stone that you intend to modify and the variable number of Dragon Flames depending on the clarity of the stone you intend to modify. According to the Wiki's, this is the table of variables for each clarity. SIDE NOTE I'm not sure where they got these numbers so I just hard coded them in a header file. They have also added a new group in the "dragon_soul_table.txt" parser file but for what I understood, it's just another probability table. I didn't find it necessary to follow their logic because the "ApplyNumSettings" group already has probabilities of 0 to 3 for adding additional bonuses. Video Preview If you find any problems, please let me know! Have fun!
  3. Hello, Today I will present you two new features for affect. Real time affects: Long time ago a client of mine wanted some of his affects to be real time. As you may know the affects in metin2 are consumed only if you are online (except premium, but that is another story). The easier way to achieve this was to create a new variable inside the affect called bIsRealTime, set it to 1 and store the duration as unix time instead of seconds. You can use this new functionality by adding True at the end of the AddAffect function call and everything will work automaticaly. Update affect: In a version some time ago the official servers added this new functionality to only update an affect. This is used for the Summer Event Roulette minigame to update the collected souls count in real time in client. I didn't create a easy way to use this because it depends on what you update for the affect. Here is an example of how I used it on this event to update lApplyValue: CAffect * pAffect = pkChar->FindAffect(AFFECT_LATE_SUMMER_EVENT_BUFF); if(pAffect) { pAffect->lApplyValue += 1; pAffect->bIsUpdate = true; // Update the client SendAffectAddPacket(pkChar->GetDesc(), pAffect); } Download link: [Hidden Content]
  4. Hi Maybe not many of you need this, but since I made it, it may help someone. It's a simple script made in python, which checks both files (item_names.txt and item_proto.txt) for missing VNUM differences in one of the files. For example: I used it because I rebuilt both files, deleted items I will never use and restructured some data. In the end, I realized that I have quite a few missing lines from the item_proto.txt file but have them present in item_names.txt. So I made this script, which checked every VNUM present in item_names.txt but missing from item_proto.txt. This made it much easier for me to solve this problem. Atention: For the GUI one, you will need to install PyQt5 via PIP There is a version of just code, so u gonna modify in the code the file names, then run it via terminal And there is a GUI version, when u run it into a terminal, it will open a GUI and u can chose the files from anywhere, check vnums and also lines
  5. 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
  6. Hi, I will share with you the solution of a general problem that occurs in infrastructures such as MartySama that use an account recording system over REGISTRY. When you save your account, the data is saved in the Windows Registry, as shown in the example below. Everything is great, the system is exactly what we wanted. Account information is no longer saved in files in the client. However, there is a problem. Players have this problem. If you save your character information using Turkish characters, the client does not open and gives an error. It states that you have a problem with characters used on a system that supports ASCII encoding. Check This ; LoginWindow.__LoadScript.BindObject - <type 'exceptions.UnicodeEncodeError'>:'ascii' codec can't encode character u'\xe7' in position 8: ordinal not in range(128) To resolve this, follow the steps below. Root/Intrologin.py find in "def SAB_Click_Save(self, slot):" if len(id) == 0: self.PopupNotifyMessage(localeInfo.LOGIN_INPUT_ID, self.SetIDEditLineFocus) return Add this just above the code if any(ord(char) > 127 for char in id + pwd): self.PopupNotifyMessage(localeInfo.TURKISH_WORDS) return It will be enough to add the following message anywhere in locale_game.txt TURKISH_WORDS Please do not use Turkish characters when registering your account! It's done! A few things I want to point out. 1. I am not a perfect developer, I have Python knowledge and this is how I found a solution. It's short and it works. 2. If you have a better solution suggestion, please specify. Instead of making fun of me, I ask you to respect the way I try to be useful to the community. 3. I had another account on Metin2.dev but I forgot the information so I created a new account. Moderators please understand me ^^ Kind Regards.
  7. M2 Download Center Download Here ( Internal ) little detail not mob only players. Download VT
  8. Hello everyone. Here is a little snippet for the chat history. I made this to turn back the "All" state if you turn off every each button.
  9. Hi devs ! I was getting bored during my vacation, so i had to keep myself occupied... At the request of @ Gurgarath :3, I created a small Python script that allows you to clean up unused lines from your locale_game.txt and locale_interface.txt. It is coded with Python 3.11, compatibility with earlier versions is not guaranteed. It also uses the chardet module, which is specified in the requirements.txt. For any additional information (such as the How To), please refer to the README.md. This script currently does not support imports like import LocaleInfo as li. It also does not support dynamically constructing variable names. It only detects raw constants like localeInfo.MY_CONSTANT. Downloads : Download Find this tool on my Github or M2DL Exemple of output : Takuma.
  10. Reversed From 23.1.4.0 It saves camera's close/far mode option [Hidden Content] [Hidden Content]
  11. 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]
  12. M2 Download Center Download Here ( Internal ) Download Here ( GitHub )
  13. 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
  14. M2 Download Center Download Here ( Internal ) Download Here ( GitHub ) Hello, came by to share something simple yet helpful for players, someone requested me this feature that allows you to input money with k format on the pick money dialog window, this will enable you to input “1kk” instead of “1000000” Hope it comes in handy for who is planning to use it.
  15. M2 Download Center Download Here ( Internal ) Download Here ( GitHub ) Informations about system : When you will open a chest with drop , will appear this window. In this window you can preview drop of chest and you can make a setting how many chests you want open. [Hidden Content]
  16. Download Metin2 Download It compares 2 diffrent locale_game (One is the language you want to convert and the other is the original) You can also use as locale_interface [Hidden Content] [Hidden Content]
  17. M2 Download Center Download Here ( Internal ) Introduction Hey, since I'm often asked if I can make an example for this or that in Python, I just post some of these examples in this thread from time to time. Usually they just end up in my trash (that's why the collection is small for now) but maybe it will help some of you to learn something. You are also welcome to post examples here and I will add them to the startpost. You have a wish for a certain example? Then write it in here. PS: These examples are designed very simple, so that they can be understood. Examples ui.ComoBox() ui.ScrollBar() with text ui.AniImageBox() as loading bar ui.ListBox() ui.ToggleButton() ui.RadioButtonGroup() ui.DragButton() ui.Bar(), ui.Box(), ui.Line() ui.Gauge(), ui.SliderBar() ui.TextLine(), ui.EditLine() Pagination Tabs DropDown Tree Category Navigation ListBox Search TextLineScrollable Class Collapsible window
  18. Hello friends and enemies, ladies and gentlemans, I was playing with my introEmpire.py and as always I got an obstacle in my way, the "select empire phase" was not working properly. BEFORE(ymir): - Client was receiving a random number(1,3) if you didn't had an empire selected. - Packet that sends "simple information" to the client was called after changing the phase to "select phase". AFTER: - Client is receiving correctly the empire ID. - Client is initializing the "select empire" phase if you have no characters on your account (if pid columns in table player.player_index are set on 0) NOTE: - Other bugs not tested yet but everything seems to be OK for now ... - Need to do something about the remaining row in player.player_index table after deleting all the characters on the account.(too lazy, too sleepy now 6:33 AM ...) Here is the FIX(minimalist tutorial): root/introSelect.py (disabling the reselectempire phase) game/src/input_db.cpp
  19. Disable loading BGM music/sounds when the player has the Volume set on 0.0 I speak about this ones when you open the client, login, select character, loading. 0706 17:55:00831 :: BGM/login_window.mp3 > 304 kb 0706 17:55:08657 :: BGM/characterselect.mp3 > 472 kb 0706 17:55:19083 :: BGM/enter_the_east.mp3 > 3.14 mb But you can add the maps with specific songs to the list... 0706 18:26:21453 :: BGM/desert.mp3 > 3.61 mb 0706 18:36:17583 :: BGM/follow_war_god.mp3 > 4.63 mb 0706 18:38:00674 :: BGM/save_me.mp3 > 2.90 mb ... and more Inside the Client Srcs file: SoundManager.cpp (MilesLib) Search for the function and make the changes like bellow: void CSoundManager::PlayMusic(DWORD dwIndex, const char * c_szFileName, float fVolume, float fVolumeSpeed) { # add this if (GetMusicVolume() == 0.0) return; # add this if (dwIndex >= CSoundManagerStream::MUSIC_INSTANCE_MAX_NUM) return; ... } The only one bug problem is that when an player changes an song with the volume 0 it will not load (ty @ Karbust) this can be fixed from python (root/uisystemoption.py) When the player is choosing any song set the volume to 5/10% then load the mp3 file. def __OnChangeMusic(self, fileName): # you can try snd.SetMusicVolume(0.1 * net.GetFieldMusicVolume()) systemSetting.SetMusicVolume(0.1) Sorry if it did not help you, and however to see the files loading without any use... It bothers me. If you have an better way post it bellow and i will update the topic.
  20. M2 Download Center Download Here ( Internal ) [Hidden Content] [Hidden Content] [Hidden Content]
  21. Hello community, I recently created two scripts to promote my project and decided to share with the community. The scripts are written in python and are easy to understand. Screenhot Guide [Hidden Content] Download [Hidden Content]
  22. Hello everyone, Since I've decided to remove all the Korean texts from my source, I wrote a really simple python program that allow to check if the syntax of the locale_string.txt is correct. As you know, just one misplaced comma can essentially screw up your locale_string. How it works is really easy to understand, however I'm attaching a screenshot to remove any doubts. I hope this can be useful to someone. Screenshot import re import tkinter as tk from tkinter import filedialog def check_format(string): pattern = r'^"[^"]+";$' return re.match(pattern, string) def check_file(): file_path = filedialog.askopenfilename(filetypes=[('Text files', '*.txt')]) if file_path: count_respect_format = 0 count_do_not_respect_format = 0 invalid_strings = [] with open(file_path, 'r') as file: content = file.read() pairs = content.split('\n\n') for pair in pairs: string1, string2 = pair.strip().split('\n') if check_format(string1.strip()) and check_format(string2.strip()): count_respect_format += 1 else: count_do_not_respect_format += 1 invalid_strings.append((string1.strip(), string2.strip())) result_text.delete('1.0', 'end') result_text.insert('end', f"Number of pairs that respect the format: {count_respect_format}\n" f"Number of pairs that do not respect the format: {count_do_not_respect_format}\n\n") if count_do_not_respect_format > 0: result_text.insert('end', "Invalid strings:\n\n") for invalid_string in invalid_strings: result_text.insert('end', f"{invalid_string[0]}\n{invalid_string[1]}\n\n") else: result_text.delete('1.0', 'end') result_text.insert('end', "No file selected.") # Window gui window = tk.Tk() window.title("Locale String Format Checker - Exodus v.1.0.0") window.geometry("500x300") window.resizable(False, False) # button select_button = tk.Button(window, text="Select File", command=check_file) select_button.pack() # Widget used for whow text result_text = tk.Text(window) result_text.pack() ## window.mainloop()
  23. Hey M2DEV! Today I will show you how to fix one unpleasant error related to the auto attack. When you attack a monster automatically and for example want to move away from the monster, what do you do? Probably you will turn off the auto attack and try to move away from the monster using the WASD keys? Yes, but it won't work because the target doesn't reset when you try to disable the auto-attack. Before fix: [Hidden Content] After fix: [Hidden Content] Open file: UserInterface\PythonPlayer.h Make "__ClearAutoAttackTargetActorID();" public! Open file: UserInterface\PythonPlayerModulec.cpp We need to create a new method PyObject * playerClearAutoAttackTargetActorID(PyObject* poSelf, PyObject* poArgs) { CPythonPlayer::Instance().__ClearAutoAttackTargetActorID(); return Py_BuildNone(); } add method to s_methods[] { "ClearAutoAttackTargetActorID", playerClearAutoAttackTargetActorID, METH_VARARGS }, Compile bin! Open file: root/uitaskbar.py search elif self.EVENT_MOVE_AND_ATTACK == event: replace this part: elif self.EVENT_MOVE_AND_ATTACK == event: btn = self.mouseModeButtonList[dir].GetChild("button_move_and_attack") func = player.MBF_SMART player.ClearAutoAttackTargetActorID() tooltip_text = localeInfo.TASKBAR_ATTACK pack root file! P.S Please forgive me for my English, I use a GoogleTranslate
  24. M2 Download Center Download Here ( Internal ) Download Here ( GitHub ) Idea is from 2014 but I think this is good feature like discord. For open gui your name must start with "[" like [GM].
  25. Premise: I don't know whether you have this system from a free release elsewhere or you bought it and I have no idea if they are the same or not (even though the same version can be found in the first server files of Rubinum). There are two issues I was concerned with: -creating an item when you request the infos -putting the mob data dictionary in constinfo The first is if you have, in CreateDropItemVector function something like this: if (item) vec_item.push_back(item); So, we create an item and we do nothing with it, staying in memory for no purpose. Instead, we can use a vector of pairs. Open item_manager.h Replace bool CreateDropItemVector(LPCHARACTER pkChr, LPCHARACTER pkKiller, std::vector<LPITEM> & vec_item); with: bool CreateDropItemVector(LPCHARACTER pkChr, LPCHARACTER pkKiller, std::vector<std::pair<int,int> > & vec_item); Now, open item_manager.cpp Replace bool ITEM_MANAGER::CreateDropItemVector(LPCHARACTER pkChr, LPCHARACTER pkKiller, std::vector<LPITEM> & vec_item) With bool ITEM_MANAGER::CreateDropItemVector(LPCHARACTER pkChr, LPCHARACTER pkKiller, std::vector<std::pair<int,int> > & vec_item) and remove LPITEM item = NULL; and edit the function accordingly. EX for common_drop_item, replace: std::vector<CItemDropInfo>::iterator it = g_vec_pkCommonDropItem[bRank].begin(); while (it != g_vec_pkCommonDropItem[bRank].end()) { const CItemDropInfo & c_rInfo = *(it++); if (iLevel < c_rInfo.m_iLevelStart || iLevel > c_rInfo.m_iLevelEnd) continue; TItemTable * table = GetTable(c_rInfo.m_dwVnum); if (!table) continue; item = NULL; if (table->bType == ITEM_POLYMORPH) { if (c_rInfo.m_dwVnum == pkChr->GetPolymorphItemVnum()) { item = CreateItem(c_rInfo.m_dwVnum, 1, 0, true); if (item) item->SetSocket(0, pkChr->GetRaceNum()); } } else item = CreateItem(c_rInfo.m_dwVnum, 1, 0, true); if (item) vec_item.push_back(item); } with std::vector<CItemDropInfo>::iterator it = g_vec_pkCommonDropItem[bRank].begin(); while (it != g_vec_pkCommonDropItem[bRank].end()) { const CItemDropInfo & c_rInfo = *(it++); if (iLevel < c_rInfo.m_iLevelStart || iLevel > c_rInfo.m_iLevelEnd) continue; TItemTable * table = GetTable(c_rInfo.m_dwVnum); if (!table) continue; if(c_rInfo.m_dwVnum > 70103 && c_rInfo.m_dwVnum < 70108) { if (c_rInfo.m_dwVnum != pkChr->GetPolymorphItemVnum()) { continue; } } vec_item.push_back(std::make_pair(c_rInfo.m_dwVnum, 1)); } (edit 70103 and 70108 accordingly with your Polymorph Marble vnums The whole function should look like this: bool ITEM_MANAGER::CreateDropItemVector(LPCHARACTER pkChr, LPCHARACTER pkKiller, std::vector<std::pair<int,int> > & vec_item) { if (pkChr->IsPolymorphed() || pkChr->IsPC()) { return false; } int iLevel = pkKiller->GetLevel(); BYTE bRank = pkChr->GetMobRank(); std::vector<CItemDropInfo>::iterator it = g_vec_pkCommonDropItem[bRank].begin(); while (it != g_vec_pkCommonDropItem[bRank].end()) { const CItemDropInfo & c_rInfo = *(it++); if (iLevel < c_rInfo.m_iLevelStart || iLevel > c_rInfo.m_iLevelEnd) continue; TItemTable * table = GetTable(c_rInfo.m_dwVnum); if (!table) continue; if(c_rInfo.m_dwVnum > 70103 && c_rInfo.m_dwVnum < 70108) { if (c_rInfo.m_dwVnum != pkChr->GetPolymorphItemVnum()) { continue; } } vec_item.push_back(std::make_pair(c_rInfo.m_dwVnum, 1)); } // Drop Item Group { itertype(m_map_pkDropItemGroup) it; it = m_map_pkDropItemGroup.find(pkChr->GetRaceNum()); if (it != m_map_pkDropItemGroup.end()) { typeof(it->second->GetVector()) v = it->second->GetVector(); for (DWORD i = 0; i < v.size(); ++i) { vec_item.push_back(std::make_pair(v[i].dwVnum, v[i].iCount)); } } } // MobDropItem Group { itertype(m_map_pkMobItemGroup) it; it = m_map_pkMobItemGroup.find(pkChr->GetRaceNum()); if ( it != m_map_pkMobItemGroup.end() ) { CMobItemGroup* pGroup = it->second; if (pGroup && !pGroup->IsEmpty()) { const CMobItemGroup::SMobItemGroupInfo& info = pGroup->GetOne(); vec_item.push_back(std::make_pair(info.dwItemVnum, info.iCount)); } } } // Level Item Group { itertype(m_map_pkLevelItemGroup) it; it = m_map_pkLevelItemGroup.find(pkChr->GetRaceNum()); if ( it != m_map_pkLevelItemGroup.end() ) { if ( it->second->IsInLevelRange((DWORD)iLevel) ) { typeof(it->second->GetVector()) v = it->second->GetVector(); for ( DWORD i=0; i < v.size(); i++ ) { DWORD dwVnum = v[i].dwVNum; vec_item.push_back(std::make_pair(dwVnum, v[i].iCount)); } } } } // ETC DropItem if (pkChr->GetMobDropItemVnum()) { itertype(m_map_dwEtcItemDropProb) it = m_map_dwEtcItemDropProb.find(pkChr->GetMobDropItemVnum()); if (it != m_map_dwEtcItemDropProb.end()) { vec_item.push_back(std::make_pair(pkChr->GetMobDropItemVnum(), 1)); } } //Metin if (pkChr->IsStone()) { if (pkChr->GetDropMetinStoneVnum()) { vec_item.push_back(std::make_pair(pkChr->GetDropMetinStoneVnum(), 1)); } } return vec_item.size(); } Note: level item group and drop item group might be different because in my files i have a level_limit_max token and, for drop_item_group, I don't expect to put a Polymorph Marble. Also it's missing the buyertheifgloves item group, but you can clearly see the pattern and modify accordingly. Edit : I found a bug in the mob_drop_item infos. If you don't know, with the type "kill", you drop only one of the items you put in the list, so the info drop, well, will show only one, and then another, and another one, every time you reopen it. To fix this issue, go to item_manager.h and search for: const SMobItemGroupInfo& GetOne() const { return m_vecItems[GetOneIndex()]; } Add after: std::vector<std::pair<int,int>> GetVector() { std::vector<std::pair<int,int>> item_list; for(auto &x : m_vecItems) item_list.emplace_back(std::make_pair(x.dwItemVnum,x.iCount)); return item_list; } then, in item_manager.cpp, instead of: if (pGroup && !pGroup->IsEmpty()) { const CMobItemGroup::SMobItemGroupInfo& info = pGroup->GetOne(); vec_item.push_back(std::make_pair(info.dwItemVnum, info.iCount)); } replace it with: if (pGroup && !pGroup->IsEmpty()) { auto vec_items = pGroup->GetVector(); for(auto &x : vec_items) vec_item.push_back(std::make_pair(x.first,x.second)); } Side Note (how to add drop of the Stones from Metins server side): Before return vec_item.size(); you can also organize the vector as you want or create another one with a certain structure, like, first the common drop items, then the advance items or whatever, you can also clear it for what I care. Me, for example, I sort it to have all the vnums from the smallest to the largest. You can do that by: std::sort(vec_item.begin(), vec_item.end(), std::less<std::pair<int,int> >()); Now, let's go to input_main.cpp Remove: LPITEM pkInfoItem; We don't use an item anymore, remember? So now replace: static std::vector<LPITEM> s_vec_item; with: static std::vector<std::pair<int,int> > s_vec_item; Then replace: if (ITEM_MANAGER::instance().CreateDropItemVector(m_pkChrTarget, ch, s_vec_item) && (m_pkChrTarget->IsMonster() || m_pkChrTarget->IsStone())) with: if ((m_pkChrTarget->IsMonster() || m_pkChrTarget->IsStone()) && ITEM_MANAGER::instance().CreateDropItemVector(m_pkChrTarget, ch, s_vec_item)) This way also, if you target a player or a NPC, it will not trigger the CreateDropItemVector function. Remove everything inside the function if (s_vec_item.size() == 0); //this is useless else if (s_vec_item.size() == 1) //even more useless Just put: for(std::vector<std::pair<int,int> >::const_iterator iter = s_vec_item.begin(); iter != s_vec_item.end();++iter) { pInfo.dwVID = m_pkChrTarget->GetVID(); pInfo.race = m_pkChrTarget->GetRaceNum(); pInfo.dwVnum = iter->first; pInfo.count = iter->second; ch->GetDesc()->Packet(&pInfo, sizeof(TPacketGCTargetInfo)); } That's it. Now the constinfo issue. So, open constinfo.py and remove: import app if app.ENABLE_SEND_TARGET_INFO: MONSTER_INFO_DATA = {} Open uitarget.py and after the imports add: MONSTER_INFO_DATA = {} and replace whatever declaration with constinfo.MONSTER_INFO_DATA with just MONSTER_INFO_DATA Now open game.py. Even here, replace constinfo.MONSTER_INFO_DATA with uiTarget.MONSTER_INFO_DATA Last little fix, open intrologin.py Search for: self.stream.SetLoginInfo(id, pwd) the one in: def __OnClickLoginButton(self): add before uiTarget.MONSTER_INFO_DATA.clear() This should fix the issue when you don't close the client and, if you change the drop, you still see the older drop. Now you may ask, why all the other servers who may have this have never crashed or imploded? To be fair, I don't know and I don't care. Just know, if your system is similar to this, be aware of the issues and that this is one but definitely not the only fix possible.
×
×
  • 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.