Jump to content

martysama0134

Honorable Member
  • Posts

    613
  • Joined

  • Last visited

  • Days Won

    96
  • Feedback

    100%

Posts posted by martysama0134

  1. I don't think there are other alternatives than using a class(object) in python2.

    I've edited the code for supporting cython and uiscriptlocale as well:

    class LocaleInfoWrapper(object):
    	def __init__(self, wrapped):
    		self.wrapped = wrapped
    	def __getattr__(self, name):
    		try:
    			return getattr(self.wrapped, name)
    		except AttributeError:
    			dbg.TraceError("Locale attribute not found: {}".format(name))
    			return name
    
    def __hybrid_import(name,globals=None,locals=None,fromlist=None, level=-1):
    	if __USE_CYTHON__ and rootlib.isExist(name):
    		if name in sys.modules:
    			dbg.Tracen('importing from sys.modules %s' % name)
    			return sys.modules[name]
    
    		dbg.Tracen('importing from rootlib %s' % name)
    		newmodule = rootlib.moduleImport(name)
    
    		module_do(newmodule)
    		if name.lower() in ("localeinfo", "uiscriptlocale"):
    			newmodule = LocaleInfoWrapper(newmodule)
    		sys.modules[name] = newmodule
    		return newmodule
    	else:
    		filename = name + '.py'
    
    		if pack.Exist(filename):
    			if name in sys.modules:
    				dbg.Tracen('importing from sys.modules %s' % name)
    				return sys.modules[name]
    
    			dbg.Tracen('importing from pack %s' % name)
    
    			newmodule = _process_result(compile(pack_file(filename,'r').read(),filename,'exec'),name)
    
    			module_do(newmodule)
    			if name.lower() in ("localeinfo", "uiscriptlocale"):
    				newmodule = LocaleInfoWrapper(newmodule)
    				sys.modules[name] = newmodule
    			return newmodule
    		else:
    			dbg.Tracen('importing from lib %s' % name)
    			return old_import(name,globals,locals,fromlist)#, level)

    I'm also returning directly 'name' if not found.

    • Metin2 Dev 1
    • Love 4
  2. I gave a quick glance to your system, and I found a potential issue I reported some time ago to another c++ dungeon:

    Directly storing the character ptr in the dungeon instance is one of the main reasons for instability.

    You should use a function like this:

    LPCHARACTER FindValidCharacter(const VID & vid)
    {
    	if (!vid.GetID())
    		return nullptr;
    	const auto ch = CHARACTER_MANAGER::instance().Find(vid.GetID());
    	return (ch && !ch->IsDead()) ? ch : nullptr;
    }

     

    -------------------------------------------------------------------------

    The reason why I write dungeons in LUA, and functionality in C++ is because:

    • The whole lua environment works fine out of the box
    • I can delegate quick edits to other people who have little to no knowledge
    • It's incredibly easy to crash the whole process in a c++ dungeon, in comparison to lua
    • People don't know how to correctly manage memory in c++, so it would have been better if they sticked to a simpler already-managed environment
    • The maintenance of a c++ dungeon gets incredibly tiresome, in comparison to lua

    -------------------------------------------------------------------------

    I also suggest you to never use magic numbers like [8][2], and to stick with std::array & an enumeration list to prevent repeated code like this:

    image.png

     

    -------------------------------------------------------------------------

    Thanks for sharing.

     

    • Metin2 Dev 1
    • Good 2
    • Love 3
  3. 5 minutes ago, VirusS said:

    so let me understand that the things that tell the truth about certain things are deleted and you try to cover them up, I didn't think so, I wrote something and the mass was deleted, I think it should have been left there for everyone to understand that there are things that do not have the same functions

    "reboot" now is "shutdown -r now". They changed the behavior in the last 7 years 🤣

    • Not Good 1
    • Lmao 1
  4. I slightly refactored. You can also specify the poly vnum now. (I skipped the ReadMonsterDropItemGroup part)

    Here a patch:

    From c5da3c9abf1608347bb5bec5fae06213c75c1ae9 Mon Sep 17 00:00:00 2001
    From: Marty Sama <martysama0134@users.noreply.github.com>
    Date: Thu, 16 Nov 2023 06:19:28 +0100
    Subject: [PATCH] ENABLE_MOB_DROP_POLY
    
     s3ll_server/Srcs/Server/common/CommonDefines.h     |  1 +
     s3ll_server/Srcs/Server/game/src/char_item.cpp     | 44 ++++++++++++++++++----
     s3ll_server/Srcs/Server/game/src/item_manager.h    |  7 +++-
     .../Server/game/src/item_manager_read_tables.cpp   | 10 ++++-
     4 files changed, 50 insertions(+), 12 deletions(-)
    
    diff --git a/s3ll_server/Srcs/Server/common/CommonDefines.h b/s3ll_server/Srcs/Server/common/CommonDefines.h
    index e7bc400c..33b05884 100644
    --- a/s3ll_server/Srcs/Server/common/CommonDefines.h
    +++ b/s3ll_server/Srcs/Server/common/CommonDefines.h
    @@ -91,6 +91,7 @@ enum eCommonDefines {
     ...
     ...
     ...
    +#define ENABLE_MOB_DROP_POLY // enable drop type 'poly' for special_item_group.txt (idx drop mobvnum pct customitemvnum)
     ...
     ...
     ...
    diff --git a/s3ll_server/Srcs/Server/game/src/char_item.cpp b/s3ll_server/Srcs/Server/game/src/char_item.cpp
    index 1900b5c4..a9631838 100644
    --- a/s3ll_server/Srcs/Server/game/src/char_item.cpp
    +++ b/s3ll_server/Srcs/Server/game/src/char_item.cpp
    @@ -2023,11 +2023,16 @@ bool CHARACTER::UseItemEx(LPITEM item, TItemPos DestCell)
     								case CSpecialItemGroup::POISON:
     									ChatPacket(CHAT_TYPE_INFO, LC_TEXT("»óÀÚ¿¡¼­ ³ª¿Â ³ì»ö ¿¬±â¸¦ µéÀ̸¶½ÃÀÚ µ¶ÀÌ ¿Â¸öÀ¸·Î ÆÛÁý´Ï´Ù!"));
     									break;
     								#ifdef ENABLE_WOLFMAN_CHARACTER
     								case CSpecialItemGroup::BLEEDING:
     									ChatPacket(CHAT_TYPE_INFO, LC_TEXT("»óÀÚ¿¡¼­ ³ª¿Â ³ì»ö ¿¬±â¸¦ µéÀ̸¶½ÃÀÚ µ¶ÀÌ ¿Â¸öÀ¸·Î ÆÛÁý´Ï´Ù!"));
     									break;
     								#endif
    +								#ifdef ENABLE_MOB_DROP_POLY
    +								case CSpecialItemGroup::POLY:
    +									ChatPacket(CHAT_TYPE_INFO, LC_TEXT("»óÀÚ¿¡¼­ ¸ó½ºÅÍ°¡ ³ªÅ¸³µ½À´Ï´Ù!"));
    +									break;
    +								#endif
     								case CSpecialItemGroup::MOB_GROUP:
     									ChatPacket(CHAT_TYPE_INFO, LC_TEXT("»óÀÚ¿¡¼­ ¸ó½ºÅÍ°¡ ³ªÅ¸³µ½À´Ï´Ù!"));
     									break;
    @@ -2107,11 +2112,16 @@ bool CHARACTER::UseItemEx(LPITEM item, TItemPos DestCell)
     						case CSpecialItemGroup::POISON:
     							ChatPacket(CHAT_TYPE_INFO, LC_TEXT("»óÀÚ¿¡¼­ ³ª¿Â ³ì»ö ¿¬±â¸¦ µéÀ̸¶½ÃÀÚ µ¶ÀÌ ¿Â¸öÀ¸·Î ÆÛÁý´Ï´Ù!"));
     							break;
     						#ifdef ENABLE_WOLFMAN_CHARACTER
     						case CSpecialItemGroup::BLEEDING:
     							ChatPacket(CHAT_TYPE_INFO, LC_TEXT("»óÀÚ¿¡¼­ ³ª¿Â ³ì»ö ¿¬±â¸¦ µéÀ̸¶½ÃÀÚ µ¶ÀÌ ¿Â¸öÀ¸·Î ÆÛÁý´Ï´Ù!"));
     							break;
     						#endif
    +						#ifdef ENABLE_MOB_DROP_POLY
    +						case CSpecialItemGroup::POLY:
    +							ChatPacket(CHAT_TYPE_INFO, LC_TEXT("»óÀÚ¿¡¼­ ¸ó½ºÅÍ°¡ ³ªÅ¸³µ½À´Ï´Ù!"));
    +							break;
    +						#endif
     						case CSpecialItemGroup::MOB_GROUP:
     							ChatPacket(CHAT_TYPE_INFO, LC_TEXT("»óÀÚ¿¡¼­ ¸ó½ºÅÍ°¡ ³ªÅ¸³µ½À´Ï´Ù!"));
     							break;
    @@ -3728,11 +3738,16 @@ bool CHARACTER::UseItemEx(LPITEM item, TItemPos DestCell)
     											case CSpecialItemGroup::POISON:
     												ChatPacket(CHAT_TYPE_INFO, LC_TEXT("»óÀÚ¿¡¼­ ³ª¿Â ³ì»ö ¿¬±â¸¦ µéÀ̸¶½ÃÀÚ µ¶ÀÌ ¿Â¸öÀ¸·Î ÆÛÁý´Ï´Ù!"));
     												break;
     											#ifdef ENABLE_WOLFMAN_CHARACTER
     											case CSpecialItemGroup::BLEEDING:
     												ChatPacket(CHAT_TYPE_INFO, LC_TEXT("»óÀÚ¿¡¼­ ³ª¿Â ³ì»ö ¿¬±â¸¦ µéÀ̸¶½ÃÀÚ µ¶ÀÌ ¿Â¸öÀ¸·Î ÆÛÁý´Ï´Ù!"));
     												break;
     											#endif
    +											#ifdef ENABLE_MOB_DROP_POLY
    +											case CSpecialItemGroup::POLY:
    +												ChatPacket(CHAT_TYPE_INFO, LC_TEXT("»óÀÚ¿¡¼­ ¸ó½ºÅÍ°¡ ³ªÅ¸³µ½À´Ï´Ù!"));
    +												break;
    +											#endif
     											case CSpecialItemGroup::MOB_GROUP:
     												ChatPacket(CHAT_TYPE_INFO, LC_TEXT("»óÀÚ¿¡¼­ ¸ó½ºÅÍ°¡ ³ªÅ¸³µ½À´Ï´Ù!"));
     												break;
    @@ -7386,14 +7401,27 @@ bool CHARACTER::GiveItemFromSpecialItemGroup(DWORD dwGroupNum, std::vector<DWORD
     					bSuccess = true;
     				}
     				break;
     			#ifdef ENABLE_WOLFMAN_CHARACTER
     			case CSpecialItemGroup::BLEEDING:
     				{
     					AttackedByBleeding(nullptr);
     					bSuccess = true;
     				}
     				break;
     			#endif
    +			#ifdef ENABLE_MOB_DROP_POLY
    +			case CSpecialItemGroup::POLY:
    +				{
    +					const auto polyVnum = iRarePct ? iRarePct : 70104;
    +					item_get = AutoGiveItem(polyVnum, 1, 0);
    +					if (item_get)
    +					{
    +						item_get->SetSocket(0, dwCount);
    +						bSuccess = true;
    +					}
    +				}
    +				break;
    +			#endif
     			case CSpecialItemGroup::MOB_GROUP:
     				{
     					const int sx = GetX() - number(300, 500);
    diff --git a/s3ll_server/Srcs/Server/game/src/item_manager.h b/s3ll_server/Srcs/Server/game/src/item_manager.h
    index 0c1e19ec..d8e604b9 100644
    --- a/s3ll_server/Srcs/Server/game/src/item_manager.h
    +++ b/s3ll_server/Srcs/Server/game/src/item_manager.h
    @@ -39,9 +39,12 @@ class CSpecialItemGroup
     			DRAIN_HP,
     			POISON,
     			MOB_GROUP,
     			#ifdef ENABLE_WOLFMAN_CHARACTER
     			BLEEDING,
     			#endif
    +			#ifdef ENABLE_MOB_DROP_POLY
    +			POLY,
    +			#endif
     		};
    
     		enum ESIGType { NORMAL, PCT, QUEST, SPECIAL };
    diff --git a/s3ll_server/Srcs/Server/game/src/item_manager_read_tables.cpp b/s3ll_server/Srcs/Server/game/src/item_manager_read_tables.cpp
    index 2a921bfb..d7c67863 100644
    --- a/s3ll_server/Srcs/Server/game/src/item_manager_read_tables.cpp
    +++ b/s3ll_server/Srcs/Server/game/src/item_manager_read_tables.cpp
    @@ -239,12 +239,18 @@ bool ITEM_MANAGER::ReadSpecialDropItemFile(const char * c_pszFileName)
     						{
     							dwVnum = CSpecialItemGroup::POISON;
     						}
     						#ifdef ENABLE_WOLFMAN_CHARACTER
     						else if (name == "bleeding")
     						{
     							dwVnum = CSpecialItemGroup::BLEEDING;
     						}
     						#endif
    +						#ifdef ENABLE_MOB_DROP_POLY
    +						else if (name == "poly")
    +						{
    +							dwVnum = CSpecialItemGroup::POLY;
    +						}
    +						#endif
     						else if (name == "group")
     						{
     							dwVnum = CSpecialItemGroup::MOB_GROUP;

     

     

    Result:

    QNsV1vH.png

    LlmKLs2.png

    1a6lKuP.png

     

    • Good 3
    • Love 1
  5. On 1/29/2023 at 12:51 AM, xGalardo said:

    Is it safe to remove or not?

    You're decreasing the level of security of the packets by using a static key. A man in the middle attack is possible to sniff the players' passwords.

     

    If after enabled you're getting DCs, open protocol.h and change this macro to this:

    #define DEFAULT_PACKET_BUFFER_SIZE (150*1024) //@warme015 prevent dc if there are many entities

    The alternative way would be to call buffer_adjust_size in desc.cpp

    • Good 2
    • Love 1
  6. Updated with the following changes:
     

    1)

    """
    Added support for #--# and the relative column aliases
    """
    
            egr = EterGroupReader()
            egr.LoadFromFile('sample.txt')
            node = egr.FindNode("ApplyNumSettings", "Default", "basis")
            if node:
                print("GRADE_NORMAL", "has index", node.GetAliasIndex("GRADE_NORMAL"), "and value", node.GetAliasValue("GRADE_NORMAL"))
                print("GRADE_BRILLIANT", "has index", node.GetAliasIndex("GRADE_BRILLIANT"), "and value", node.GetAliasValue("GRADE_BRILLIANT"))
                print("GRADE_RARE", "has index", node.GetAliasIndex("GRADE_RARE"), "and value", node.GetAliasValue("GRADE_RARE"))
                print("GRADE_ANCIENT", "has index", node.GetAliasIndex("GRADE_ANCIENT"), "and value", node.GetAliasValue("GRADE_ANCIENT"))
                print("GRADE_LEGENDARY", "has index", node.GetAliasIndex("GRADE_LEGENDARY"), "and value", node.GetAliasValue("GRADE_LEGENDARY"))
                print("GRADE_MYTH", "has index", node.GetAliasIndex("GRADE_MYTH"), "and value", node.GetAliasValue("GRADE_MYTH"))
            egr.SaveToFile('sample-out.txt')
    
    
    Result:
        GRADE_NORMAL has index 0 and value 1
    	GRADE_BRILLIANT has index 1 and value 1
    	GRADE_RARE has index 2 and value 1
    	GRADE_ANCIENT has index 3 and value 2
    	GRADE_LEGENDARY has index 4 and value 2
    	GRADE_MYTH has index 5 and value 3
        
        
    From .txt:
    Group	ApplyNumSettings
    {
    	Group	Default
    	{
    		#--#	GRADE_NORMAL	GRADE_BRILLIANT	GRADE_RARE	GRADE_ANCIENT	GRADE_LEGENDARY	GRADE_MYTH
    		basis	1	1	1	2	2	3
    		add_min	0	0	0	0	0	0
    		add_max	0	1	2	2	3	3
    	}
    }

     

    2)

    """
    Added find or create group def
    """
            egr = MobDropItemHelper()
            egr.LoadFromFile('mob_drop_item.txt')
            group = FindOrCreateMobDropGroup(egr, 691, "limit")
            egr.AddIndexElement(group, [27001, 11, 6.12])
            egr.AddIndexElement(group, [27002, 22, 12.34])
            egr.AddIndexElement(group, [27003, 33, 18.56])
            egr.PrintTree(group)
            egr.SaveToFile('mob_drop_item-out.txt')
    
            
    Result:
    Group Mob691TypeLimit:
        type: ['limit']
        mob: [691]
        1: [27001, 11, 6.12]
        2: [27002, 22, 12.34]
        3: [27003, 33, 18.56]

    3)

    """
    Added some functions to edit the MSM files
    """
        	#load a .msm and replace a specific shape from <old> to <new>
            egr = RaceDataHelper()
            egr.LoadFromFile('assassin_m.msm')
            egr.ReplaceShapeIndexValue(44114, 44115)
            egr.SaveToFile('assassin_m-out.msm')

     

    • Metin2 Dev 1
    • Love 5
  7. This is the hidden content, please
     (
    This is the hidden content, please
    )
    I've created a script that processes Eter Group files. It can load, save, edit, repair, mass-update, and more.
    Right now I'm using it for automatically repairing mob_drop_item.txt, and mass-updating the drops at once.

    It also preserves the comments made with '#' in their correct place, except for the comments in the root below other Groups. (they'll be moved on top)

    The tree of a loaded group file will be like:

    ScriptType: ['RaceDataScript']
    BaseModelFileName: "d:/ymir work/pc/warrior/warrior_novice.GR2"
    Group CorDraconis(Mystical):
        Vnum: [51506]
        1: [115000, 1, 1]
        2: [125000, 1, 1]
        3: [135000, 1, 1]
        4: [145000, 1, 1]
        5: [155000, 1, 1]
        6: [165000, 1, 1]
    
    Group BodyChest:
        Vnum: [71203]
        1: [50401, 1, 1]
        2: [50402, 1, 1]
        3: [50403, 1, 1]
        4: [50404, 1, 1]
        5: [50405, 1, 1]
    
    Group MentalChest:
        Vnum: [71205]
        1: [50416, 1, 1]
        2: [50417, 1, 1]
        3: [50418, 1, 1]
        4: [50419, 1, 1]
        5: [50420, 1, 1]
    
    Group Cung_Mok:
        type: ['drop']
        mob: [151]
        1: [71151, 1, 100]
        2: [71151, 1, 100]
        3: [71151, 1, 100]
        4: [71151, 1, 100]
        5: [71151, 1, 100]
        6: [71299, 1, 50]
        7: [71740, 2, 50]
        8: [1, 20000, 50]
        9: [1, 30000, 30]
    
    Group ApplyNumSettings:
        Group Default:
            basis: [1, 1, 1, 2, 2, 3]
            add_min: [0, 0, 0, 0, 0, 0]
            add_max: [0, 1, 2, 2, 3, 3]
    
        Group NotSoDefault:
            basis: [1, 2, 3, 4, 5]
            add_min: [5, 4, 3, 2, 1]
            add_max: [11, 3, 5, 7, 22]
    
        Group MixedTypes:
            1: ['MAX_SP', 500]
            2: ['RESIST_WIND', 10]
            3: ['ENCHANT_WIND', 10]
    
    
    Group HairData:
        PathName: "d:/ymir Work/pc/warrior/"
        HairDataCount: [999]
        Group HairData00:
            HairIndex: [0]
            Model: "hair/hair_1_1.gr2"
            SourceSkin: "hair/hair_1_1.dds"
            TargetSkin: "warrior_hair_01.dds"
    
        Group HairData01:
            HairIndex: [1]
            Model: "hair/hair_1_1.gr2"
            SourceSkin: "hair/hair_1_1.dds"
            TargetSkin: "warrior_hair_01_white.dds"
    
        Group HairData02:
            HairIndex: [2]
            Model: "hair/hair_1_1.gr2"
            SourceSkin: "hair/hair_1_1.dds"
            TargetSkin: "warrior_hair_01_gold.dds"

    Examples:

        if True: # load, print, and save
            egr = EterGroupReader()
            egr.LoadFromFile('sample.txt')
            egr.PrintTree()
            egr.SaveToFile('sample-out.txt')
    
        if True: # find node and print it
            egr = EterGroupReader()
            egr.LoadFromFile('sample.txt')
            node = egr.FindNode("ApplyNumSettings", "Default", "basis")
            if node:
                print("node {} found with value {}".format(node.key, node.value))
    
        if True: # find node and edit it
            egr = EterGroupReader()
            egr.LoadFromFile('sample.txt')
            node = egr.FindNode("ApplyNumSettings", "Default", "basis")
            if node:
                node.value = [11, 22, 33, 44, 55, 66]
            egr.SaveToFile('sample-out.txt')
    
        if True: # iter all groups and sub groups
            egr = EterGroupReader()
            egr.LoadFromFile('sample.txt')
    
            # Create an instance of the iterator
            group_iterator = EterGroupIterator(egr, skipRoot=True)
    
            # Iterate over all groups using a lambda or function
            for group in group_iterator:
                print(f"Group Name: {group.name}")
    
        if True: # load mob_drop_item and print only the metins
            egr = MobDropItemHelper()
            egr.LoadFromFile('mob_drop_item.txt')
            for group in egr.GetGroupsOfMetins():
                egr.PrintTree(group)
                print("HIGHEST", GetGroupHighestIndex(group))
            egr.SaveToFile('mob_drop_item-out.txt')
    
        if True: # load mob_drop_item and add a red potion in the metin drops
            egr = MobDropItemHelper()
            egr.LoadFromFile('mob_drop_item.txt')
            for group in egr.GetGroupsOfMetinsAndDrop():
                egr.AddIndexElement(group, [27001, 1, 6.6])
                egr.PrintTree(group)
            egr.SaveToFile('mob_drop_item-out.txt')
    
        if True: # load mob_drop_item and add a red potion in vnum list
            egr = MobDropItemHelper()
            egr.LoadFromFile('mob_drop_item.txt')
            for group in egr.GetGroupsOf(lambda group: IsVnumInListGroup(group, [101, 105, 1059])):
                egr.AddIndexElement(group, [27001, 1, 6.6])
                egr.PrintTree(group)
            egr.SaveToFile('mob_drop_item-out.txt')
    
        if True: # load mob_drop_item and check for errors
            egr = MobDropItemHelper()
            egr.LoadFromFile('mob_drop_item.txt')
            for group in egr.GetGroups():
                valid, found = CheckValidContinuousGroupIndex(group)
                if not valid:
                    egr.PrintTree(group)
                    print(f"NOT VALID: Error at group '{group.name}' index {found}")
                    break
            egr.SaveToFile('mob_drop_item-out.txt')
    
        if True: # load mob_drop_item and repair for index errors
            egr = MobDropItemHelper()
            egr.LoadFromFile('mob_drop_item.txt')
            for group in egr.GetGroups():
                RepairContinuousGroupIndex(group)
            egr.SaveToFile('mob_drop_item-out.txt')

     

    If you have any suggestions, comment the topic.

     

    • Metin2 Dev 41
    • Eyes 1
    • Good 8
    • Love 1
    • Love 12
  8. I created an auto-refresh feature every 5s:

     

    wBXMpbn.png

    spacer.png

     

    import app
    import ui
    import wndMgr
    import uiToolTip
    import localeInfo
    import dbg
    import sys, imp
    
    REFRESH_TIME = 5.0
    MSG_STRING02 = "Tip: Press F11 to auto refresh: {}"
    
    def LoadModule(name):
    	if name in sys.modules: del sys.modules[name]
    	if name in locals(): del locals()[name]
    	if name in globals(): del globals()[name]
    
    	# Open the module file
    	module_file_path = "{}.py".format(name)
    	module_file = old_open(module_file_path, 'rb')
    
    	# Load the module from the file
    	module = imp.load_module(name, module_file, module_file_path, ('.py', 'rb', imp.PY_SOURCE))
    
    	if not module in locals():
    		locals()[name] = module
    
    	if not module in globals():
    		globals()[name] = module
    
    	return module
    
    class TestScript(ui.BoardWithTitleBar):
    
    	BOARD_WIDTH = 230
    	BOARD_HEIGHT = 80+30
    
    	SLOT_WIDTH = 150
    
    	def __init__(self):
    		ui.BoardWithTitleBar.__init__(self)
    
    		self.wndScript = None
    
    		self.autoRefresh = False
    		self.nextRefresh = 0.0
    
    		## Item ToolTip
    		self.tooltipItem = uiToolTip.ItemToolTip()
    		self.tooltipItem.Hide()
    
    		## Board
    		self.SetSize(self.BOARD_WIDTH, self.BOARD_HEIGHT)
    		self.SetPosition(0, wndMgr.GetScreenHeight() - self.BOARD_HEIGHT)
    
    		self.AddFlag("movable")
    		self.SetTitleName("Test Script")
    		self.SetCloseEvent(self.Hide)
    		self.Show()
    
    		## Board > Slot Bar
    		slotBar = ui.SlotBar()
    		slotBar.SetParent(self)
    		slotBar.SetSize(self.SLOT_WIDTH, 18)
    		slotBar.SetPosition(13, 32)
    		slotBar.Show()
    		self.slotBar = slotBar
    
    		## Board > Slot Bar > EditLine
    		editLine = ui.EditLine()
    		editLine.SetParent(self.slotBar)
    		editLine.SetSize(self.SLOT_WIDTH, 18)
    		editLine.SetPosition(4, 3)
    		editLine.SetMax(30)
    		editLine.SetText("bio")#"uiTest")
    		editLine.Show()
    		self.editLine = editLine
    
    		## Board > Load Button
    		loadButton = ui.Button()
    		loadButton.SetParent(self)
    		loadButton.SetPosition(self.SLOT_WIDTH + 23, 32)
    		loadButton.SetUpVisual("d:/ymir work/ui/public/small_button_01.sub")
    		loadButton.SetOverVisual("d:/ymir work/ui/public/small_button_02.sub")
    		loadButton.SetDownVisual("d:/ymir work/ui/public/small_button_03.sub")
    		loadButton.SetEvent(self.__OnClickLoadButton)
    		loadButton.SetText("Load")
    		loadButton.Show()
    		self.loadButton = loadButton
    
    		## Board > TextLine (Print Mouse Position)
    		self.textLine = ui.TextLine()
    		self.textLine.SetParent(self)
    		self.textLine.SetFontName(localeInfo.UI_DEF_FONT)
    		self.textLine.SetWindowHorizontalAlignCenter()
    		self.textLine.SetHorizontalAlignCenter()
    		self.textLine.SetWindowVerticalAlignBottom()
    		self.textLine.SetVerticalAlignBottom()
    		self.textLine.SetPosition(0, 33)
    		self.textLine.SetText("Tip: Press F10 to load / reload.")
    		self.textLine.Show()
    
    		## Board > TextLine2 (Print Mouse Position)
    		self.textLine2 = ui.TextLine()
    		self.textLine2.SetParent(self)
    		self.textLine2.SetFontName(localeInfo.UI_DEF_FONT)
    		self.textLine2.SetWindowHorizontalAlignCenter()
    		self.textLine2.SetHorizontalAlignCenter()
    		self.textLine2.SetWindowVerticalAlignBottom()
    		self.textLine2.SetVerticalAlignBottom()
    		self.textLine2.SetPosition(0, 13)
    		self.textLine2.SetText(MSG_STRING02.format("OFF"))
    		self.textLine2.Show()
    
    	def OnUpdate(self):
    		if self.autoRefresh:
    			if self.nextRefresh < app.GetTime():
    				self.nextRefresh = app.GetTime() + REFRESH_TIME
    				self.__OnClickLoadButton()
    
    	def __del__(self):
    		ui.BoardWithTitleBar.__del__(self)
    
    		del self.tooltipItem
    		del self.tooltipSkill
    
    		self.slotBar = None
    		self.editLine = None
    		self.loadButton = None
    		self.textLine = None
    
    	def Show(self):
    		ui.BoardWithTitleBar.Show(self)
    
    	def Hide(self):
    		ui.BoardWithTitleBar.Hide(self)
    
    	def OnKeyDown(self, key):
    		if key == app.DIK_F10:
    			self.__OnClickLoadButton()
    		elif key == app.DIK_F11:
    			self.__OnClickToggleAutoRefresh()
    
    	def __OnClickToggleAutoRefresh(self):
    		self.autoRefresh = not self.autoRefresh
    		self.textLine2.SetText(MSG_STRING02.format("ON" if self.autoRefresh else "OFF"))
    
    	def __OnClickLoadButton(self):
    		module_name = self.editLine.GetText()
    		if not module_name:
    			dbg.TraceError("Empty module name")
    			return
    
    		if self.wndScript:
    			self.wndScript.Hide()
    			del self.wndScript
    			self.wndScript = None
    
    		dbg.TraceError("Loading module: {}".format(module_name))
    		try:
    			module = LoadModule(module_name)
    		except Exception as error:
    			dbg.TraceError("Error loading module: {}".format(error))
    			return
    
    		self.wndScript = module.BioWindow()
    		self.wndScript.Open()
    
    wndTestScript = TestScript()
    wndTestScript.Show()

     

    • Metin2 Dev 2
    • Good 1
    • Love 2
    • Love 1
  9. Someone had an issue with it using my client. I made it working, refresh too:

    reload.py

    import app
    import ui
    import wndMgr
    import uiToolTip
    import localeInfo
    import dbg
    import sys, imp
    
    def LoadModule(name):
    	if name in sys.modules: del sys.modules[name]
    	if name in locals(): del locals()[name]
    	if name in globals(): del globals()[name]
    
    	# Open the module file
    	module_file_path = "{}.py".format(name)
    	module_file = old_open(module_file_path, 'r')
    
    	# Load the module from the file
    	module = imp.load_module(name, module_file, module_file_path, ('.py', 'r', imp.PY_SOURCE))
    
    	if not module in locals():
    		locals()[name] = module
    
    	if not module in globals():
    		globals()[name] = module
    
    	return module
    
    class TestScript(ui.BoardWithTitleBar):
    
    	BOARD_WIDTH = 230
    	BOARD_HEIGHT = 80
    
    	SLOT_WIDTH = 150
    
    	def __init__(self):
    		ui.BoardWithTitleBar.__init__(self)
    
    		self.wndScript = None
    
    		## Item ToolTip
    		self.tooltipItem = uiToolTip.ItemToolTip()
    		self.tooltipItem.Hide()
    
    		## Board
    		self.SetSize(self.BOARD_WIDTH, self.BOARD_HEIGHT)
    		self.SetPosition(0, wndMgr.GetScreenHeight() - self.BOARD_HEIGHT)
    
    		self.AddFlag("movable")
    		self.SetTitleName("Test Script")
    		self.SetCloseEvent(self.Hide)
    		self.Show()
    
    		## Board > Slot Bar
    		slotBar = ui.SlotBar()
    		slotBar.SetParent(self)
    		slotBar.SetSize(self.SLOT_WIDTH, 18)
    		slotBar.SetPosition(13, 32)
    		slotBar.Show()
    		self.slotBar = slotBar
    
    		## Board > Slot Bar > EditLine
    		editLine = ui.EditLine()
    		editLine.SetParent(self.slotBar)
    		editLine.SetSize(self.SLOT_WIDTH, 18)
    		editLine.SetPosition(4, 3)
    		editLine.SetMax(30)
    		editLine.SetText("uibio")#"uiTest")
    		editLine.Show()
    		self.editLine = editLine
    
    		## Board > Load Button
    		loadButton = ui.Button()
    		loadButton.SetParent(self)
    		loadButton.SetPosition(self.SLOT_WIDTH + 23, 32)
    		loadButton.SetUpVisual("d:/ymir work/ui/public/small_button_01.sub")
    		loadButton.SetOverVisual("d:/ymir work/ui/public/small_button_02.sub")
    		loadButton.SetDownVisual("d:/ymir work/ui/public/small_button_03.sub")
    		loadButton.SetEvent(self.__OnClickLoadButton)
    		loadButton.SetText("Load")
    		loadButton.Show()
    		self.loadButton = loadButton
    
    		## Board > TextLine (Print Mouse Position)
    		self.textLine = ui.TextLine()
    		self.textLine.SetParent(self)
    		self.textLine.SetFontName(localeInfo.UI_DEF_FONT)
    		self.textLine.SetWindowHorizontalAlignCenter()
    		self.textLine.SetHorizontalAlignCenter()
    		self.textLine.SetWindowVerticalAlignBottom()
    		self.textLine.SetVerticalAlignBottom()
    		self.textLine.SetPosition(0, 13)
    		self.textLine.SetText("Tip: Press F10 to load / reload.")
    		self.textLine.Show()
    
    	def __del__(self):
    		ui.BoardWithTitleBar.__del__(self)
    
    		del self.tooltipItem
    		del self.tooltipSkill
    
    		self.slotBar = None
    		self.editLine = None
    		self.loadButton = None
    		self.textLine = None
    
    	def Show(self):
    		ui.BoardWithTitleBar.Show(self)
    
    	def Hide(self):
    		ui.BoardWithTitleBar.Hide(self)
    
    	def OnKeyDown(self, key):
    		if key == app.DIK_F10:
    			self.__OnClickLoadButton()
    
    	def __OnClickLoadButton(self):
    		module_name = self.editLine.GetText()
    		if not module_name:
    			dbg.TraceError("Empty module name")
    			return
    
    		if self.wndScript:
    			self.wndScript.Hide()
    			del self.wndScript
    			self.wndScript = None
    
    		dbg.TraceError("Loading module: {}".format(module_name))
    		try:
    			module = LoadModule(module_name)
    		except Exception as error:
    			dbg.TraceError("Error loading module: {}".format(error))
    			return
    
    		self.wndScript = module.BioWindow() #EDIT WITH THE CORRECT WINDOW NAME
    		self.wndScript.Open()
    
    wndTestScript = TestScript()
    wndTestScript.Show()
    • Metin2 Dev 1
    • Love 1
  10. 4 hours ago, Jira said:

    item drop penalty

     source/server/game/char_battle.cpp | 12 +++++++++++-
     1 file changed, 11 insertions(+), 1 deletion(-)
    
    diff --git a/source/server/game/char_battle.cpp b/source/server/game/char_battle.cpp
    index 4b0c013..9f9fab6 100644
    --- a/source/server/game/char_battle.cpp
    +++ b/source/server/game/char_battle.cpp
    @@ -1107,8 +1107,18 @@ void CHARACTER::ItemDropPenalty(LPCHARACTER pkKiller)
     		std::vector<BYTE> vec_bSlots;
     
     		for (i = 0; i < INVENTORY_MAX_NUM; ++i)
    -			if (GetInventoryItem(i))
    +		{
    +			if ((pkItem = GetInventoryItem(i)))
    +			{
    +				if (pkItem->GetType() == ITEM_QUEST)
    +				{
    +					if (quest::CQuestManager::instance().GetPCForce(GetPlayerID())->IsRunning() == true)
    +						continue;
    +				}
    +
     				vec_bSlots.push_back(i);
    +			}
    +		}
     
     		if (!vec_bSlots.empty())
     		{

     

    The game is single-thread, so I highly recommend you to add the if (quest::CQuestManager::instance().GetPCForce(GetPlayerID())->IsRunning()) check before the for loop.

    It will be either always true, or always false.

    • Good 3
    • Love 1
  11. How to make it more pretty:

    common/length.h inside SItemPos struct (above IsEquipPosition()), paste:

    	bool IsSamePosition(const SItemPos & other) const
    	{
    		return *this==other
    			|| ((INVENTORY == window_type || EQUIPMENT == window_type)
    				&& (INVENTORY == other.window_type || EQUIPMENT == other.window_type)
    				&& cell == other.cell);
    	}

     

    game/src/char_item.cpp, at the beginning of CHARACTER::MoveItem function:

    
    bool CHARACTER::MoveItem(TItemPos Cell, TItemPos DestCell, uint8_t count)
    {
    	if (Cell.IsSamePosition(DestCell)) // @fixme196
    		return false;

     

    I'd probably rename IsSamePosition to HasSamePosition, but ignore that.

    • Metin2 Dev 2
    • Good 3
    • Love 1
    • Love 10
  12. You're forgetting Mall:

    // PythonNetworkStream.cpp
    // 1. Search:
    			Set(HEADER_GC_MALL_SET,	CNetworkPacketHeaderMap::TPacketType(sizeof(TPacketGCItemSet), STATIC_SIZE_PACKET));
    // 1. Replace with:
    			Set(HEADER_GC_MALL_SET,	CNetworkPacketHeaderMap::TPacketType(sizeof(TPacketGCItemSet2), STATIC_SIZE_PACKET));

     

     

    • Good 1
    • Love 2
  13. Without timers, or quest flags (persistent memory):

    mRyLnkK.png

    XtKYCfa.png

    This is the hidden content, please
     (enumtype branch)

    This is the hidden content, please

    It stores something like 10 bytes per player (when they use it) in an std::unordered_map (hash map with O(1) access time). It's only stored in memory, and not shared to other cores (it's irrelevant for this case).

    • Metin2 Dev 37
    • Good 4
    • Love 1
    • Love 12
  14. On 4/2/2023 at 9:51 AM, Pseudabo said:

    Actually no. I found problem. If you noticed, there are 67 spells hardcoded in the code and the game loads these files regardless of whether the spells are enabled or not. The problem is that the client randomly crashes during teleportation because it reads TGA images as DDS. This is an old bug that has been causing problems for some time now. A good example of this is loading images when moving from locale to etc. Therefore, if you have these files in the game but have 67 spells disabled, it will result in crashes and you won't get any errors because there are technically none. I'm adding a fix. I recommend applying it to any client because it's not just a fix for this thread, but an overall fix for the client and a prevention of crashes.


    EterImageLib/DXTCImage.cpp

    Search:
    
    bool CDXTCImage::LoadHeaderFromMemory(const BYTE * c_pbMap)
    
    Change:
    
    bool CDXTCImage::LoadHeaderFromMemory(const BYTE * c_pbMap, int iSize)
    
    Search:
    
    DWORD dwMagic;
    
    Add under:
    
    if (iSize < sizeof(DWORD))
            return false;
    
    Search:
    
    dwMagic = *(DWORD *) c_pbMap;
        c_pbMap += sizeof(DWORD);
    
    Add under:
    
    iSize -= sizeof(DWORD);
    
    Search:
    
    DDSURFACEDESC2 ddsd;
    
    Add under:
    
    if (iSize < sizeof(DDSURFACEDESC2))
            return false;
    
    Search:
    
    memcpy(&ddsd, c_pbMap, sizeof(DDSURFACEDESC2));
        c_pbMap += sizeof(DDSURFACEDESC2);
    
    Add under:
    
    iSize -= sizeof(DDSURFACEDESC2);
    
    Search:
    
    return LoadFromMemory((const BYTE*) pvMap);
    
    Change:
    
    return LoadFromMemory((const BYTE*) pvMap, mappedFile.Size());
    
    Search:
    
    bool CDXTCImage::LoadFromMemory(const BYTE * c_pbMap)
    
    Change:
    
    bool CDXTCImage::LoadFromMemory(const BYTE * c_pbMap, int iSize)
    
    Search:
    
    if (!LoadHeaderFromMemory(c_pbMap))
    
    Change:
    
    if (!LoadHeaderFromMemory(c_pbMap, iSize))

    EterImageLib/DXTCImage.h

    Search:
    
            bool LoadFromMemory(const BYTE * c_pbMap);
            bool LoadHeaderFromMemory(const BYTE * c_pbMap);
    
    Change:
    
            bool LoadFromMemory(const BYTE * c_pbMap, int iSize);
            bool LoadHeaderFromMemory(const BYTE * c_pbMap, int iSize);

    EterLib/GrpImageTexture.cpp

    Search:
    
    if (image.LoadHeaderFromMemory((const BYTE *) c_pvBuf))
    
    Change:
    
    if (image.LoadHeaderFromMemory((const BYTE *) c_pvBuf, bufSize))

     

    This fixes the fog.tga issue too. I forgot to add this fix on the WE.

    • Love 3
  15. If you want to calculate the size of the stone array automatically, you can use this function

    
    unsigned int BlueDragon_GetDragonStoneCount()
    {
    	lua_State* L = quest::CQuestManager::instance().GetLuaState();
    
    	const int stack_top = lua_gettop(L);
    
    	lua_getglobal(L, "BlueDragonSetting");
    
    	if (false == lua_istable(L, -1))
    	{
    		lua_settop(L, stack_top);
    
    		return 0;
    	}
    
    	lua_pushstring(L, "DragonStone");
    	lua_gettable(L, -2);
    
    	if (false == lua_istable(L, -1))
    	{
    		lua_settop(L, stack_top);
    
    		sys_err("BlueDragon: no required table DragonStone");
    		return 0;
    	}
    
    	const unsigned int count = static_cast<unsigned int>(luaL_getn(L, -1));
    
    	lua_settop(L, stack_top);
    
    	return count;
    }

    ty chatgpt

    tyZyfgu.png

    for

    guAhhO9.png

    • Metin2 Dev 3
    • Good 1
    • Love 1
    • Love 2
  16. I did a small refactory, but I haven't tested it yet:

    diff --git a/s3ll_server/Srcs/Server/game/src/BlueDragon.cpp b/s3ll_server/Srcs/Server/game/src/BlueDragon.cpp
    index f8bfd0f5..7995fc09 100644
    --- a/s3ll_server/Srcs/Server/game/src/BlueDragon.cpp
    +++ b/s3ll_server/Srcs/Server/game/src/BlueDragon.cpp
    @@ -135,9 +135,22 @@ int BlueDragon_Damage (LPCHARACTER me, LPCHARACTER pAttacker, int dam)
     	if (nullptr == me || nullptr == pAttacker)
     		return dam;
    
    -	if (true == pAttacker->IsMonster() && 2493 == pAttacker->GetMobTable().dwVnum)
    +	#ifdef ENABLE_BLUEDRAGON_RENEWAL
    +	if (pAttacker->IsMonster() && BlueDragon::BossVnum == pAttacker->GetMobTable().dwVnum)
     	{
    -		for (int i=1 ; i <= 4 ; ++i)
    +		for (int i=1; i <= BlueDragon::StoneCount; ++i)
    +		{
    +			const auto dwDragonStoneID = BlueDragon_GetIndexFactor("DragonStone", i, "vnum");
    +			const auto cnt = SECTREE_MANAGER::instance().GetMonsterCountInMap( me->GetMapIndex(), dwDragonStoneID);
    +			if (cnt > 0)
    +				return 0;
    +		}
    +	}
    +	#endif
    +
    +	if (pAttacker->IsMonster() && BlueDragon::BossVnum == pAttacker->GetMobTable().dwVnum)
    +	{
    +		for (int i=1; i <= BlueDragon::StoneCount; ++i)
     		{
     			if (ATK_BONUS == BlueDragon_GetIndexFactor("DragonStone", i, "effect_type"))
     			{
    @@ -152,9 +165,9 @@ int BlueDragon_Damage (LPCHARACTER me, LPCHARACTER pAttacker, int dam)
     		}
     	}
    
    -	if (true == me->IsMonster() && 2493 == me->GetMobTable().dwVnum)
    +	if (me->IsMonster() && BlueDragon::BossVnum == me->GetMobTable().dwVnum)
     	{
    -		for (int i=1 ; i <= 4 ; ++i)
    +		for (int i=1; i <= BlueDragon::StoneCount; ++i)
     		{
     			if (DEF_BONUS == BlueDragon_GetIndexFactor("DragonStone", i, "effect_type"))
     			{
    @@ -172,9 +185,9 @@ int BlueDragon_Damage (LPCHARACTER me, LPCHARACTER pAttacker, int dam)
     		}
     	}
    
    -	if (true == me->IsStone() && 0 != pAttacker->GetMountVnum())
    +	if (me->IsStone() && 0 != pAttacker->GetMountVnum())
     	{
    -		for (int i=1 ; i <= 4 ; ++i)
    +		for (int i=1; i <= BlueDragon::StoneCount; ++i)
     		{
     			if (me->GetMobTable().dwVnum == BlueDragon_GetIndexFactor("DragonStone", i, "vnum"))
     			{
    diff --git a/s3ll_server/Srcs/Server/game/src/BlueDragon.h b/s3ll_server/Srcs/Server/game/src/BlueDragon.h
    index 25b7d5b4..d1a44149 100644
    --- a/s3ll_server/Srcs/Server/game/src/BlueDragon.h
    +++ b/s3ll_server/Srcs/Server/game/src/BlueDragon.h
    @@ -1,5 +1,16 @@
    +#ifndef BLUE_DRAGON_H
    +#define BLUE_DRAGON_H
    +#pragma once
    +
    +#define ENABLE_BLUEDRAGON_RENEWAL
    +
    +namespace BlueDragon {
    +	constexpr int BossVnum = 2493;
    +	constexpr int StoneCount = 4;
    +}
    
     extern int BlueDragon_StateBattle (LPCHARACTER);
     extern time_t UseBlueDragonSkill (LPCHARACTER, unsigned int);
     extern int BlueDragon_Damage (LPCHARACTER me, LPCHARACTER attacker, int dam);
    
    +#endif // BLUE_DRAGON_H
    diff --git a/s3ll_server/Srcs/Server/game/src/char.cpp b/s3ll_server/Srcs/Server/game/src/char.cpp
    index 9f564ea6..50692f6e 100644
    --- a/s3ll_server/Srcs/Server/game/src/char.cpp
    +++ b/s3ll_server/Srcs/Server/game/src/char.cpp
    @@ -53,6 +53,7 @@
     #include "BlueDragon_Binder.h"
     #include "skill_power.h"
     #include "buff_on_attributes.h"
    +#include "BlueDragon.h"
    
     #ifdef __PET_SYSTEM__
     #include "PetSystem.h"
    @@ -2349,12 +2350,12 @@ EVENTFUNC(recovery_event)
     		if (ch->IsAffectFlag(AFF_BLEEDING))
     			return PASSES_PER_SEC(MAX(1, ch->GetMobTable().bRegenCycle));
     #endif
    -		if (2493 == ch->GetMobTable().dwVnum)
    +		if (BlueDragon::BossVnum == ch->GetMobTable().dwVnum)
     		{
     			int regenPct = BlueDragon_GetRangeFactor("hp_regen", ch->GetHPPct());
     			regenPct += ch->GetMobTable().bRegenPercent;
    
    -			for (int i=1 ; i <= 4 ; ++i)
    +			for (int i=1; i <= BlueDragon::StoneCount; ++i)
     			{
     				if (REGEN_PECT_BONUS == BlueDragon_GetIndexFactor("DragonStone", i, "effect_type"))
     				{
    @@ -2382,9 +2383,9 @@ EVENTFUNC(recovery_event)
     			return 0;
     		}
    
    -		if (2493 == ch->GetMobTable().dwVnum)
    +		if (BlueDragon::BossVnum == ch->GetMobTable().dwVnum)
     		{
    -			for (int i=1 ; i <= 4 ; ++i)
    +			for (int i=1; i <= BlueDragon::StoneCount; ++i)
     			{
     				if (REGEN_TIME_BONUS == BlueDragon_GetIndexFactor("DragonStone", i, "effect_type"))
     				{
    diff --git a/s3ll_server/Srcs/Server/game/src/char_battle.cpp b/s3ll_server/Srcs/Server/game/src/char_battle.cpp
    index c71e535a..7fdc24b9 100644
    --- a/s3ll_server/Srcs/Server/game/src/char_battle.cpp
    +++ b/s3ll_server/Srcs/Server/game/src/char_battle.cpp
    @@ -1472,7 +1472,7 @@ void CHARACTER::Dead(LPCHARACTER pkKiller, bool bImmediateDead)
     	CloseMyShop();
     	CloseSafebox();
    
    -	if (IsMonster() && 2493 == GetMobTable().dwVnum)
    +	if (IsMonster() && BlueDragon::BossVnum == GetMobTable().dwVnum)
     	{
     		if (pkKiller && pkKiller->GetGuild())
     			CDragonLairManager::instance().OnDragonDead(this, pkKiller->GetGuild()->GetID());
    @@ -2092,6 +2092,13 @@ bool CHARACTER::Damage(LPCHARACTER pAttacker, int dam, EDamageType type) // retu
     		}
    
     		dam = BlueDragon_Damage(this, pAttacker, dam);
    +		#ifdef ENABLE_BLUEDRAGON_RENEWAL
    +		if (!dam)
    +		{
    +			SendDamagePacket(pAttacker, 0, DAMAGE_BLOCK);
    +			return false;
    +		}
    +		#endif
    
     		BYTE damageFlag = 0;
    
    diff --git a/s3ll_server/Srcs/Server/game/src/char_state.cpp b/s3ll_server/Srcs/Server/game/src/char_state.cpp
    index dc5b44ae..aae5dfc9 100644
    --- a/s3ll_server/Srcs/Server/game/src/char_state.cpp
    +++ b/s3ll_server/Srcs/Server/game/src/char_state.cpp
    @@ -1008,7 +1008,7 @@ void CHARACTER::StateBattle()
     	if (m_pkParty)
     		m_pkParty->SendMessage(this, PM_ATTACKED_BY, 0, 0);
    
    -	if (2493 == m_pkMobData->m_table.dwVnum)
    +	if (BlueDragon::BossVnum == m_pkMobData->m_table.dwVnum)
     	{
     		m_dwStateDuration = BlueDragon_StateBattle(this);
     		return;

    To display the changes side by side, paste it in https://diffy.org/ like this:

    P59tpPm.png

    tzAg5zP.png

    @ ASIKOOIt would be great if you could enable the DIFF patch syntax for code blocks.

     

     

     

     

    Edit: if you're using very old src, edit this:

    
    namespace BlueDragon {
    	enum eBlueDragon {
    		BossVnum = 2493,
    		StoneCount = 4,
    	};
    }

     

    • Metin2 Dev 2
    • Love 1
    • Love 1
  17. 1 hour ago, speze012 said:



    WTF Bro?

    	if (rVictim.GetActorType() == TYPE_ENEMY || rVictim.GetActorType() == TYPE_NPC)
    		return FALSE;

     

    That check would remove IsAttacking, this is the correct one:

    	if (rVictim.GetActorType() == TYPE_ENEMY && !isAttacking())
    		return FALSE;

    This check is only for mobs, so ignore npc/pet/door for now.

    I haven't tested the code, so I have no idea if isAttacking would remain true for delayed attacks.

    • Metin2 Dev 2
    • Good 1
  18. 1) You're not using if __name__ == "__main__":

    2) You're not using a function to encapsulate the code #noreusability

    3) You're not skipping the non .quest files

    4) You're including the full path and not the relative one (C:/Users/User/Desktop/Quests/Dungeons/deviltower_zone.quest -> Dungeons/deviltower_zone.quest)

    import os
    
    def create_quest_list(directory, output_file):
        with open(output_file, 'w') as f:
            for root, _, files in os.walk(directory):
                for filename in files:
                    if filename.endswith('.quest'):
                        rel_path = os.path.relpath(os.path.join(root, filename), directory)
                        rel_path = rel_path.replace(os.sep, '/')
                        f.write(rel_path + '\n')
        output_file = output_file.replace(os.sep, '/')
        print(f'File saved in {os.getcwd()}/{output_file}')
    
    if __name__ == "__main__":
        directory = r'C:\Users\User\Desktop\Quests'
        output_file = 'quest_list.txt'
        create_quest_list(directory, output_file)

    ty chatgpt

    Result:

    x4kmaUk.png

     

    Meanwhile in freebsd:

    find ~/Desktop/Quests -name "*.quest" > quest_list.txt
    # without ./
    find ~/Desktop/Quests -name "*.quest" -exec sh -c 'echo "{}" | sed "s|^./||"' \; > quest_list.txt

    ty chatgpt^2

    • Metin2 Dev 1
    • kekw 1
    • Scream 1
    • Love 1
  19. function say_blue ( name ) say ( color256 ( 0 , 0 , 255 ).. name .. color256 ( 0 , 0 , 255 )) end
    function say_red ( name ) say ( color256 ( 255 , 0 , 0 ).. name .. color256 ( 255 , 0 , 0 )) end
    function say_green ( name ) say ( color256 ( 0 , 238 , 0 ).. name .. color256 ( 0 , 238 , 0 )) end
    function say_gold ( name ) say ( color256 ( 255 , 215 , 0 ).. name .. color256 ( 255 , 215 , 0 )) end
    function say_black ( name ) say ( color256 ( 0 , 0 , 0 ).. name .. color256 ( 0 , 0 , 0 )) end
    function say_white ( name ) say ( color256 ( 255 , 255 , 255 ).. name .. color256 ( 255 , 255 , 255 )) end
    function say_yellow ( name ) say ( color256 ( 255 , 255 , 0 ).. name .. color256 ( 255 , 255 , 0 )) end
    function say_blue2 ( name ) say ( color256 ( 0 , 206 , 209 ).. name .. color256 ( 0 , 206 , 209 )) end

    in questlib.lua

     

×
×
  • 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.