Jump to content

Metin2 Dev

Bot
  • Posts

    2151
  • Joined

  • Last visited

  • Feedback

    100%

Everything posted by Metin2 Dev

  1. What's the best dedicated server and domain/webhost for a global metin2 server? Is it OVH or Frantech a good solution?
  2. 5 loading images made full hd with Photoshop Beta new Generative AI: [Hidden Content]
  3. This is a nasty bug where a boss can throw you inside a wall if he hits you with a skill that has SKILL_FLAG_CRUSH or SKILL_FLAG_CRUSH_LONG. // char_skill.cpp // Search: if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_CRUSH | SKILL_FLAG_CRUSH_LONG) && !IS_SET(pkChrVictim->GetAIFlag(), AIFLAG_NOMOVE)) { ... } // Replace the if with: if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_CRUSH | SKILL_FLAG_CRUSH_LONG) && !IS_SET(pkChrVictim->GetAIFlag(), AIFLAG_NOMOVE)) { float fCrushSlidingLength = 200; if (m_pkChr->IsNPC()) fCrushSlidingLength = 400; if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_CRUSH_LONG)) fCrushSlidingLength *= 2; float fx, fy; float degree = GetDegreeFromPositionXY(m_pkChr->GetX(), m_pkChr->GetY(), pkChrVictim->GetX(), pkChrVictim->GetY()); if (m_pkSk->dwVnum == SKILL_HORSE_WILDATTACK) { degree -= m_pkChr->GetRotation(); degree = fmod(degree, 360.0f) - 180.0f; if (degree > 0) degree = m_pkChr->GetRotation() + 90.0f; else degree = m_pkChr->GetRotation() - 90.0f; } GetDeltaByDegree(degree, fCrushSlidingLength, &fx, &fy); long startX = (long)(pkChrVictim->GetX()); long startY = (long)(pkChrVictim->GetY()); long endX = (long)(pkChrVictim->GetX() + fx); long endY = (long)(pkChrVictim->GetY() + fy); // We asume all positions between the start and end point are movable bool allPositionsMovable = true; // Calculate the distance between the start and end points double distance = std::sqrt((endX - startX) * (endX - startX) + (endY - startY) * (endY - startY)); // Calculate the step size for each coordinate double stepX = (endX - startX) / distance; double stepY = (endY - startY) / distance; // Check if all points on the trajectory between startX, startY and endX, endY are movable for (double i = 0; i <= distance; ++i) { double currentX = startX + i * stepX; double currentY = startY + i * stepY; long roundedX = static_cast<long>(std::round(currentX)); long roundedY = static_cast<long>(std::round(currentY)); if (!SECTREE_MANAGER::instance().IsMovablePosition(pkChrVictim->GetMapIndex(), roundedX, roundedY)) { allPositionsMovable = false; break; } } if (allPositionsMovable) { sys_log(0, "CRUSH SUCCESS! %s -> %s (%d %d) -> (%d %d)", m_pkChr->GetName(), pkChrVictim->GetName(), pkChrVictim->GetX(), pkChrVictim->GetY(), (long)(pkChrVictim->GetX()+fx), (long)(pkChrVictim->GetY()+fy)); pkChrVictim->Sync(endX, endY); pkChrVictim->Goto(endX, endY); pkChrVictim->CalculateMoveDuration(); } else { sys_log(0, "CRUSH FAIL! %s -> %s (%d %d) -> (%d %d)", m_pkChr->GetName(), pkChrVictim->GetName(), pkChrVictim->GetX(), pkChrVictim->GetY(), (long)(pkChrVictim->GetX()+fx), (long)(pkChrVictim->GetY()+fy)); } if (m_pkChr->IsPC() && m_pkChr->m_SkillUseInfo[m_pkSk->dwVnum].GetMainTargetVID() == (DWORD) pkChrVictim->GetVID()) { SkillAttackAffect(pkChrVictim, 1000, IMMUNE_STUN, m_pkSk->dwVnum, POINT_NONE, 0, AFF_STUN, 4, m_pkSk->szName); } else { if (allPositionsMovable) pkChrVictim->SyncPacket(); } }
  4. There is a nasty bug where sometimes some items are not loaded in the safebox. This problem occurs because the function that interprets the data from the set packet (RecvSafeBoxSetPacket) is called too early. Fix: // PythonNetworkStream.cpp // 1. Search: Set(HEADER_GC_SAFEBOX_SET, CNetworkPacketHeaderMap::TPacketType(sizeof(TPacketGCItemSet), STATIC_SIZE_PACKET)); // 1. Replace with: Set(HEADER_GC_SAFEBOX_SET, CNetworkPacketHeaderMap::TPacketType(sizeof(TPacketGCItemSet2), STATIC_SIZE_PACKET));
  5. Contact me at discord, I can help you
  6. You did something wrong, no one else had this problem... This member thanked me and had no problem with the fake virus alerts
  7. Thank you! I use windows 11 and don't have this problem. Check if it could be a webhost problem.
  8. If you use the code from the leaked Rubinum source, it does not mean you have solved the problem. Also, if you use modern coding techniques from C++ new standards, it does not mean all the code will fix itself. Your vector is still sorted, and the pointer in the map is still pointing to the old vector position. Add this code after the std::sort, and you will be surprised: for (auto it = m_map_itemTableByVnum.begin(); it != m_map_itemTableByVnum.end(); ++it) { DWORD key = it->first; // Access the key TItemTable* value = it->second; // Access the value if(value->dwVnum != key) { sys_err("Map Key %u --- Value vnum %u", key, value->dwVnum); } }
  9. @patcher/TorrentPatch.ico @patcher/TorrentPatch.png
  10. Check if the FOXFS_MAGIC of the packer is the same with the FOXFS_MAGIC of the client.
  11. ? 1. The fix I posted is done because you can spam the database with queries. 2. With your "fix" you can delete all coments from the guild with no permissions :)))
  12. Hello, As you may already be aware, the official source code that was leaked some time ago includes code for saving window status. The code is designed to save various window attributes such as visibility, minimized status, x and y coordinates, and height. However, I have made some adjustments to only save the x and y coordinates. Please note that the logic I have utilized is different from the original planned approach. In order to ensure the proper functioning of the save function, it is imperative to address an issue where the function is called too late and the interfacehandler is null: ## 1. Open UserInterface/PythonSystem.cpp ## 1. Replace the following function: void CPythonSystem::SaveInterfaceStatus() { ... } ## 1. With: void CPythonSystem::SaveInterfaceStatus() { if(m_poInterfaceHandler) // This will always be null when this function is called PyCallClassMemberFunc(m_poInterfaceHandler, "OnSaveInterfaceStatus", Py_BuildValue("()")); FILE* File; File = fopen("interface.cfg", "wb"); if(!File) { TraceError("Cannot open interface.cfg"); return; } fwrite(m_WindowStatus, 1, sizeof(TWindowStatus) * WINDOW_MAX_NUM, File); fclose(File); } Now, let's explore how we can save window attributes, specifically for the inventory window as an example: ## 1. Go to interfaceModule.py: ## 1. Search: if self.wndInventory: self.wndInventory.Hide() self.wndInventory.Destroy() ## 1. Change it like this: if self.wndInventory: xInventory, yInventory = self.wndInventory.GetGlobalPosition() systemSetting.SaveWindowStatus(systemSetting.WINDOW_INVENTORY, False, True, xInventory, yInventory, 0) self.wndInventory.Hide() self.wndInventory.Destroy() ## 2. Now search: self.wndInventory = uiInventory.InventoryWindow() self.wndInventory.BindInterfaceClass(self) ## 2. Change it like this: self.wndInventory = uiInventory.InventoryWindow() self.wndInventory.BindInterfaceClass(self) (_, _, invX, invY, _) = systemSetting.GetWindowStatus(systemSetting.WINDOW_INVENTORY) if invX > 0 and invY > 0 and invX + self.wndInventory.GetWidth() < wndMgr.GetScreenWidth() and invY + self.wndInventory.GetHeight() < wndMgr.GetScreenHeight(): self.wndInventory.SetPosition(invX, invY) I hope you find this information useful. ?
  13. Hello, If you check the algorithm in char.cpp (DetermineDropMetinStone()) you will see that in some cases that is totaly posible. (for example pick randomly number 90 and folow the algorithm with the percentages from your table line 1 (40, 13, 5, 2, 0). If you look closely the last value from the table is not even used because the loop goes from 0 to 3 only (the 0 in your table). To fix this I consider the best way is to use discrete_distribution from stl: #include <random> void CHARACTER::DetermineDropMetinStone() { const int METIN_STONE_NUM = 14; static DWORD c_adwMetin[METIN_STONE_NUM] = { 28030, 28031, 28032, 28033, 28034, 28035, 28036, 28037, 28038, 28039, 28040, 28041, 28042, 28043, }; DWORD stone_num = GetRaceNum(); int idx = std::lower_bound(aStoneDrop, aStoneDrop+STONE_INFO_MAX_NUM, stone_num) - aStoneDrop; if (idx >= STONE_INFO_MAX_NUM || aStoneDrop[idx].dwMobVnum != stone_num) { m_dwDropMetinStone = 0; } else { const SStoneDropInfo & info = aStoneDrop[idx]; m_bDropMetinStonePct = info.iDropPct; std::random_device rd; std::mt19937 gen(rd()); std::discrete_distribution<> d(info.iLevelPct, info.iLevelPct + STONE_LEVEL_MAX_NUM + 1); int randomIndex = d(gen); // This will be a random number in range (0, 4) m_dwDropMetinStone = c_adwMetin[number(0, METIN_STONE_NUM - 1)] + (randomIndex * 100); } }
  14. Hello, This is because the values for hyperlink are sent as hexadecimal values (base 16) and not decimal values (base 10). You can use this converter if you want decimal values: [Hidden Content]
  15. Poor child... Needs to steal in the metin2 scene to eat, complicated life ?
  16. I decided to join the cause because I thought it was funny that he said "find out how to flood metin2.solutions", well... I found how ?
  17. Hello community, If your reputation here in the community was your salary, how much would you earn per month? ? Me: 948€/m
  18. Congratulations on the project! I am willing to contribute to the project if you want to.
  19. Like this: self.item_editline.OnIMEUpdate = ui.__mem_func__(self.OnUpdateItemEditLine) And : def OnUpdateItemEditLine(self): ui.EditLine.OnIMEUpdate(self.item_editline) editLineText = self.item_editline.GetText()
  20. The network should be unlimited, depending on the number of players 100mbps/port is enough.
  21. What is this ? I think the following video will explain everything: [Hidden Content] Please note this is experimental and might have issues. Download: [Hidden Content]
  22. If you work with Python scripts (uiscript files) you probably were in a situation where you needed to change an element property or add a new element based on a condition/binary macro. Here are two new functions I wrote to make this job easier for you: (the code is based on official root from 2018 I posted here) ## ui.py ## 1. Find: class PythonScriptLoader(object): ## 1. Add ABOVE: def FindElementByName(window, name): if isinstance(window, dict): if "name" in window and window["name"] == name: return window for key in window: found = FindElementByName(window[key], name) if found is not None: return found elif isinstance(window, list) or isinstance(window, tuple): for item in window: found = FindElementByName(item, name) if found is not None: return found return None def ChangeElementProperty(window, name, propertyName, propertyValue): element = FindElementByName(window, name) if element: if propertyName in element: element[propertyName] = propertyValue ## 2. Search: self.ScriptDictionary["LOCALE_PATH"] = app.GetLocalePath() ## 2. Add after: self.ScriptDictionary["FindElementByName"] = FindElementByName self.ScriptDictionary["ChangeElementProperty"] = ChangeElementProperty And this is how you can use it: ## The example is based on minimap.py from the official uiscript ## 1. BEFORE: window["children"][0]["children"] = window["children"][0]["children"] + [ { "name" : "InGameEventButton", ... },] ## 1. AFTER: openWindow = FindElementByName(window, "OpenWindow") if openWindow: openWindow["children"] = openWindow["children"] + [ { "name" : "InGameEventButton", ... },] ## 2. BEFORE: window["children"][0]["children"][5]["x"] = 0 window["children"][0]["children"][5]["y"] = 57 ## 2. AFTER: ChangeElementProperty(window, "AtlasShowButton", "x", 0) ChangeElementProperty(window, "AtlasShowButton", "y", 57)
  23. Hello community, I had a problem on my server that after 2 days of being turned on I would get the error "MySQL server has gone away". After 1 week of research and testing I came up with a solution. Step by Step [Hidden Content]
      • 119
      • Metin2 Dev
      • Good
      • Love
      • Eyes
      • Love
  24. no, because !isAttacking()
  25. The method I shared worked for me, I explained in the thread how the function works. Instead of answering "WTF?" you could just say "this function is more efficient" and share your version. I decided to share to help those who need it, I don't understand the point of that kind of comment. Is it to try to come off as "superior"? And if you want the function to work well, you must declare that 1 attack is not being made using !isAttacking() Otherwise you will get something strange when choosing a target
×
×
  • 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.