Jump to content

PetePeter

Member
  • Posts

    106
  • Joined

  • Last visited

  • Feedback

    0%

Posts posted by PetePeter

  1. 1 hour ago, Rakancito said:

    I apologize to the guys above if I took the comments too personally, I was a little tired from the amount of messages I received that day, it wasn't good and I understand that I may have had a logic error in the code. I honestly hadn't looked at it before months and as I mentioned I was a little tired. But I suppose that more than one person will find what I shared useful to complete their Yohara.

    Regarding the contribution, if anyone gives me information about the floors of the tower I can finish it without any problem, correct the errors that were mentioned to me in the forum and share it. I have no problem with the tower being complete.

    .png

     

    I was not in the mood.

    I'm going to steal your way of replying.

    I am going to respond to you because you told me things that made me cry...

    Do you know the difference between doing what @ martysama0134 or @ Amun did and what they did? They came with everything,

    And about humility, maybe I don't have it, but it's horrible that I came with the best intentions to share and they only attacked when I myself indicated that it wasn't finished and that they could finish it without any problem. Just take the important parts and delete the bad parts, it's not that hard.

    And about the coroutines, I don't think it's right that you're trying to tell me that I don't know such basic programming concepts, I can ask about encapsulation, polymorphism or overloading and I'm sure that with at least one you will search what I'm talking about.

    • I said repeatedly that C++ will always be faster than Lua and that each of the languages has its advantages and disadvantages.
    • I even commented that Lua has simpler syntax and is easier to learn than C++.
    • I also mentioned that it is not bad to program in Lua or C++ with good programming practices, that is fine, it is up to personal taste "in this case". It depends on the cost you want to suffer between each development and the lyfe cycle.
    • I repeat, in Lua you can still cause serious memory errors if you don't follow good programming practices. I have no problems doing things in Lua, I still program in Lua and do things in Lua.
    • I repeat: In both Lua and C++ you can cause problems, in reality this focuses on good programming practices.

    I have already received corrections before and I receive them with kindness as long as they are corrections and not attacks because they want everything for free and in many cases they do not have basic programming concepts.

    No developer is exempt from making code errors, "many around here should understand", it is a human quality and that is why concepts such as Software development lifecycle (SDLC) have been introduced. I have them, you have them, we all have them and the larger the code, the greater the number of errors. For this reason, there are techniques to reduce the number of code errors.

    It just happened, just as this time I had a logic error that I honestly didn't review, I have had many other contributions in this forum that I suppose many have used. So don't talk to me about humility when I'm only trying to collaborate and the only thing I receive is two or three people with problems and they don't provide arguments with a code correction that I don't mind receiving.

    NOTE: I don't believe I'm superior to people, but it is always better to say that something can be optimized and provide feedback than to come and attack with new accounts and have others take things personally.

     

    I was not in the mood.

     

     

    Your "mood" is not an excuse anyway. Use this link to get all informations: https://it-wiki.metin2.gameforge.com/index.php/Inferna_Sung_Mahi

  2. That's cool thanks ! What if we use Python's `ast` (Abstract Syntax Tree) module to handle the substitution of dynamic variable names in the code? I mean, we could technically parse the Python source file and replace all possible variables. Check this out, I've added a function that handles dynamic names in simple cases, specifically looking for Call nodes and Attribute nodes that could resemble a dynamic variable name access. Though it's not a perfect solution, it can cover many simple scenarios:

     

    from ast import parse, Import, ImportFrom
    ...
    
    
    class SourceFile:
        ...
    
        def read(self):
            ...
            if file_name.lower() == module.lower():
                self.regex_pattern = self.create_regex_pattern(content, file_name)
                self.modules_constants[module] = findall(
                  self.regex_pattern, content, IGNORECASE
                )
            ...
    
        @staticmethod
        def create_regex_pattern(content: str, module: str) -> str:
            """Create regex patterns for different types of imports"""
            module_mapping = SourceFile.get_import_aliases(content)
    
            if module in module_mapping:
                return r"\b" + escape(module_mapping[module]) + r"\.([a-zA-Z_]\w*)(?![\[\(])\b"
            else:
                return r"\b" + escape(module) + r"\.([a-zA-Z_]\w*)(?![\[\(])\b"
    
        @staticmethod
        def get_import_aliases(content: str) -> dict[str, str]:
            """Parse the file and get the aliases for imports"""
            root = parse(content)
            import_aliases = {}
    
            for node in ast.walk(root): # Use the python built-in library AST to parse the python file
                if isinstance(node, Import): # check for import statements
                    for name in node.names:
                        module = name.name
                        alias = name.asname if name.asname else name.name
                        
                        import_aliases[module] = alias
    
                elif isinstance(node, ImportFrom): # check for from import statements
                    for name in node.names:
                        module = node.module
                        alias = name.asname if name.asname else name.name
    
                        import_aliases[module] = alias
            return import_aliases
    import ast
    
    class SourceFile:
        ...
    
        @staticmethod
        def handle_dynamic_names(content: str, module: str):
            root = ast.parse(content)
    
            dynamic_names = []
    
            for node in ast.walk(root):
                if isinstance(node, ast.Call) and getattr(node.func, "id", "") == "getattr":
                    module_obj = node.args[0]
                    attr_obj = node.args[1]
                    
                    if isinstance(module_obj, ast.Subscript) and isinstance(attr_obj, ast.Str):
                        if module_obj.value.id == "globals" and attr_obj.s == module:
                            dynamic_names.append(node.func.attr)
    
            return dynamic_names
    
        def read(self):
    
            ...
    
            dynamic_names = self.handle_dynamic_names(content, module)
            for dynamic_name in dynamic_names:
                self.modules_constants[module].append(dynamic_name)
    
            ...
    

    Now, this might not always work perfectly, especially for more complex cases. For example, situations where attribute names are generated through loops, or via mathematical operations could throw a spanner in the works. Basically, when the attribute name ain't directly visible in the script, this approach could miss it. But all in all, this seems like a step in the right direction. Curious to hear your thoughts on this.

    • Metin2 Dev 1
  3. 56 minutes ago, Mafuyu said:

    everything in spanish dude cmon

    WITH items AS (
    SELECT *,
    md5(CONCAT_WS(',',attrtype0, attrvalue0, attrtype1, attrvalue1, attrtype2, attrvalue2, attrtype3, attrvalue3, attrtype4, attrvalue4)) unique_key
    FROM player.item
    WHERE attrvalue2>0 AND attrvalue3>0 
    AND `window` not IN ('SAFEBOX','MALL') 
    AND attrtype0 IN (71,72)
    ),
    
    duplicates AS (
    SELECT 
    unique_key, COUNT(*) clones
    FROM items 
    GROUP BY unique_key
    HAVING COUNT(*)>1
    ),
    
    owners AS (
    SELECT 
    a.login, p.last_play, p.name player_name,
    ii.empire, g.name guild, gm.is_general, pp.locale_name, i.unique_key hash_, d.clones duplicates,
    i.* 
    FROM items i 
    INNER JOIN player.item_proto pp ON pp.vnum = i.vnum
    INNER JOIN player.player p ON p.id = i.owner_id
    INNER JOIN duplicates d ON d.unique_key = i.unique_key
    INNER JOIN player.guild_member gm ON gm.pid = p.id
    INNER JOIN player.guild g ON g.id = gm.guild_id
    INNER JOIN `account`.account a ON a.id = p.account_id
    INNER JOIN player.player_index ii ON ii.id = a.id
    )
    
    SELECT * FROM owners;

     

     

    But I suggest something like that instead:

     

    SELECT 
    a.login, p.last_play, p.name player_name,
    ii.empire, g.name guild, gm.is_general, pp.locale_name, i.unique_key hash_, d.clones duplicates,
    i.* 
    FROM (
      SELECT *,
      md5(CONCAT_WS(',', attrtype0, attrvalue0, attrtype1, attrvalue1, attrtype2, attrvalue2, attrtype3, attrvalue3, attrtype4, attrvalue4)) unique_key
      FROM player.item
      WHERE attrvalue2 > 0 AND attrvalue3 > 0 
      AND `window` NOT IN ('SAFEBOX', 'MALL') 
      AND attrtype0 IN (71, 72)
    ) i
    INNER JOIN player.item_proto pp ON pp.vnum = i.vnum
    INNER JOIN player.player p ON p.id = i.owner_id
    INNER JOIN (
      SELECT unique_key, COUNT(*) clones
      FROM (
        SELECT 
        md5(CONCAT_WS(',', attrtype0, attrvalue0, attrtype1, attrvalue1, attrtype2, attrvalue2, attrtype3, attrvalue3, attrtype4, attrvalue4)) unique_key
        FROM player.item
        WHERE attrvalue2 > 0 AND attrvalue3 > 0 
        AND `window` NOT IN ('SAFEBOX', 'MALL') 
        AND attrtype0 IN (71, 72)
      ) tmp
      GROUP BY unique_key
      HAVING COUNT(*) > 1
    ) d ON d.unique_key = i.unique_key
    INNER JOIN player.guild_member gm ON gm.pid = p.id
    INNER JOIN player.guild g ON g.id = gm.guild_id
    INNER JOIN `account`.account a ON a.id = p.account_id
    INNER JOIN player.player_index ii ON ii.id = a.id;

     

    • Love 2
    • Love 1
  4. I suggest you to do like that instead of create many state, as the vnum have a pattern:

     

    			local item1, item2
    			local levelgive = math.floor((pc.get_level() - 10) / 10) * 10
    			if pc.get_race() == WARRIOR_M or pc.get_race() == WARRIOR_W then
    				item1 = 21900
    				item2 = 21903
    			elseif pc.get_race() == NINJA_W or pc.get_race() == NINJA_M then
    				item1 = 21901
    				item2 = 21902
    			elseif pc.get_race() == SURA_M or pc.get_race() == SURA_W then
    				item1 = 21900
    			elseif pc.get_race() == SHAMAN_W or pc.get_race() == SHAMAN_M then
    				item1 = 21904
    				item2 = 21905
    			end
    			local totalitem = item2 and 2 or 1
    			if item1 then
    				item1 = item1 + levelgive
    			end
    			if item2 then
    				item2 = item2 + levelgive
    			end
    			say()
    			if item1 then
    				say_item_vnum_inline(item1, 0, totalitem)
    			end
    			if item2 then
    				say_item_vnum_inline(item2, 1, totalitem)
    			end

     

  5. It's not about "normal" ways.
     

    1. You didn't add any return to the function after the player is kicked, so the rest of the function is executed normally
    2. You never reset the counter, it's only reset after a kick as the script continue his path and reach the "if (NewFixGuildCommente >= 3)" again
    3. (Optionnal) Consider using smart pointers (like std::unique_ptr) instead of raw pointers for better memory management and exception safety. I know the sql function is not made by you, but if we edit this function we can optimise everything also


     

    • kekw 1
    • Love 2
  6. 2 hours ago, Draveniou1 said:

    My post updated

    i add version 3 full fix 

    Changelog

    1. Fix quest pick in quest 

    2. Fix delayed desc kick

    Now it works stable without problems in version 3

     

    thanks a lot

    Did you recheck your code ? Here is a possible solution:

     

    void CGuild::DeleteComment(LPCHARACTER ch, DWORD comment_id)
    {
        const int maxAttempts = 3;
        const int resetIntervalMinutes = 10;  // Change this to the desired interval in minutes
    
        time_t currentTime = time(0);
        int attempts = ch->GetQuestFlag("Newfixxed.Newguildcomment");
        time_t lastAttemptTime = ch->GetQuestFlag("Newfixxed.LastAttemptTime");
    
        // If the user's last attempt was longer than the reset interval ago, reset their attempts
        if (difftime(currentTime, lastAttemptTime) >= resetIntervalMinutes * 60)
        {
            attempts = 0;
        }
    
        // Check if the user has reached the maximum number of attempts
        if (attempts >= maxAttempts)
        {
            ch->GetDesc()->DelayedDisconnect(0);
            return;
        }
    
        std::unique_ptr<SQLMsg> pmsg;
    
        // Execute the appropriate query based on the user's guild grade
        if (GetMember(ch->GetPlayerID())->grade == GUILD_LEADER_GRADE)
        {
            pmsg.reset(DBManager::instance().DirectQuery("DELETE FROM guild_comment WHERE id = %u AND guild_id = %u", comment_id, m_data.guild_id));
        }
        else
        {
            pmsg.reset(DBManager::instance().DirectQuery("DELETE FROM guild_comment WHERE id = %u AND guild_id = %u AND name = '%s'", comment_id, m_data.guild_id, ch->GetName()));
        }
    
        uint32_t affectedRows = pmsg->Get()->uiAffectedRows;
    
        // Check if the comment was successfully deleted
        if (affectedRows == 0 || affectedRows == (uint32_t)-1)
        {
            ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 삭제할 수 없는 글입니다."));
        }
        else
        {
            RefreshCommentForce(ch->GetPlayerID());
        }
    
        // Update the user's attempt count and last attempt time
        ch->SetQuestFlag("Newfixxed.Newguildcomment", attempts + 1);
        ch->SetQuestFlag("Newfixxed.LastAttemptTime", currentTime);
    }

     

    • kekw 1
  7. Or maybe something like that instead no ?

     

    void CGuild::AddComment(LPCHARACTER ch, const std::string& str)
    {
        if (str.length() > GUILD_COMMENT_MAX_LEN || str.length() == 0) // Added string null verification
            return;
    
        char text[GUILD_COMMENT_MAX_LEN * 2 + 1];
        DBManager::instance().EscapeString(text, sizeof(text), str.c_str(), str.length());
    
        // Fetch the number of existing comments and delete the oldest one if there are 12 comments.
        DBManager::instance().FuncQuery(std::bind(&CGuild::HandleCommentCount, this, ch, text, std::placeholders::_1),
            "SELECT COUNT(*), MIN(time) FROM guild_comment%s WHERE guild_id = %u",
            get_table_postfix(), m_data.guild_id);
    }
    
    void CGuild::HandleCommentCount(LPCHARACTER ch, const char* text, MYSQL_RES* result)
    {
        MYSQL_ROW row = mysql_fetch_row(result);
    
        if (!row)
            return;
    
        int commentCount = 0;
        sscanf(row[0], "%d", &commentCount);
    
        if (commentCount >= 12) {
            // Delete the oldest comment
            DBManager::instance().Query("DELETE FROM guild_comment%s WHERE guild_id = %u AND time = '%s'",
                get_table_postfix(), m_data.guild_id, row[1]);
        }
    
        // Insert the new comment
        DBManager::instance().FuncAfterQuery(void_bind(std::bind1st(std::mem_fun(&CGuild::RefreshCommentForce), this), ch->GetPlayerID()),
            "INSERT INTO guild_comment%s(guild_id, name, notice, content, time) VALUES(%u, '%s', %d, '%s', NOW())",
            get_table_postfix(), m_data.guild_id, ch->GetName(), (text[0] == '!') ? 1 : 0, text);
    }

     

    • kekw 1
    • Good 1
  8. 1 minute ago, Syreldar said:
    quest drops begin
        state start begin
            function GetDropData(vnum)
                return ({
                    [8002] = {
                        {["vnum"] = 101051, ["quantity"] = 1, ["perc"] = 100},
                        {["vnum"] = 101001, ["quantity"] = 1, ["perc"] = 100},
                        {["vnum"] = 71151, ["quantity"] = 5, ["perc"] = 100},
                        -- more drops here.
                    },
                    -- more monsters here.
                })[vnum];
            end -- function
    
            when kill with not npc.is_pc() and npc.get_race() == 8002 and math.abs(pc.get_level() - npc.get_level()) <= 15 begin
                local drop_data = drops.GetDropData(npc.get_race());
                for _, item_data in ipairs(drop_data) do
                    if (math.random(100) <= item_data["perc"]) then
                        game.drop_item_with_ownership(item_data["vnum"], item_data["quant"]);
                        --break; -- Uncomment if you want to limit to one drop per monster kill.
                    end -- if
                end -- for
            end -- when
        end -- state
    end -- quest

     

    UUEcBK3.png

    Have a reason if the whole game is not in LUA

    • kekw 4
    • Lmao 1
  9. 3 minutes ago, Maseda said:

    And example for metin.txt , mob.txt, tanaka.txt you can show us??

    Man it's been 9 years lol

    Also don't use the quest like that, there is not GM check on the button/info trigger which can be start by everyone

  10. 33 minutes ago, kaJaMrSimple said:

    https://metin2.download/video/LbSJGPh5DOoRj4Mr431vNvz2N6kAp9jQ/.mp4 

    Is the situation in this video related to what you said then? because it was not after the shutdown, but in the flowing game for many players.

    This is different situation, I'm speaking about the monsters not moving at all. This must be a sync problem because of the mount, the clientside speed and serverside speed are different

  11. 1 hour ago, kaJaMrSimple said:

    Hi,
    It's a very rare situation. mobs don't move at all. but they keep hitting. fixed by turning the client off and on.

    gif : https://metin2.download/picture/4bKgLtxayPTt6CZEw4PK88tA8DXHdII6/.gif

    no client and server syserr. 

    Is there anywhere I should check?

    Nothing to worry about, it's happen only when you not restart your client after a server shutdown. It's not happen to players as the shutdown close every client expect for GameMaster

  12. Just use the OnUpdate built in function, example:

     

    def SetSleepFunction(self, time):
    	self.function_sleep_time = app.GetGlobalTimeStamp() + time
        
    def OnUpdate(self):
    	if self.function_sleep_time and self.function_sleep_time < app.GetGlobalTimeStamp():
    		self.function_sleep_time = None
            	# Your action

     

    I write the code very fast for you understand the idea, but you need to declare the variable also and adapt it for you

  13. 2 minutes ago, Mefarious said:

    Hello, is there anyone who knows what can cause a problem with mob animation?
    I mean, when i attack mob hes visibkle model doesnt move but i recieve damage, its like his invisible object chasing me but his visible model/animation stays in place.
    It started to occur today, i never had such a problem.
    I deleted all quest because i thought they could caused it but even reboot/restart server wont help me.

    Restart your client, it's happen after some shutdown while the client is open

  14. 1 hour ago, caanmasu said:

    Alternative (no C++, only lua)

     

    party.give_item2 = function(item_vnum, item_count)
    	if party.is_party() then
    		local pids = {party.get_member_pids()}
    		for _, pid in next, pids, nil do
    			q.begin_other_pc_block(pid)
    			pc.give_item2(item_vnum, item_count)
    			q.end_other_pc_block()
    		end
    		party.get_member_pids()
    	else
    		pc.give_item2(item_vnum, item_count)
    	end
    end

     

    Advantage:

    You can to use in dungeon or not.

     

    Usage (example):

    party.give_item2(27001, 200)

     

    quest_functions: party.give_item2

     

    ¡Saludos!

    Not true, work only with party member. If you have any special dungeon it's will not work (Like guild or public). And it's really not a good way to use "q.begin_other_pc_block" everytime

    • Cry 1
  15. 3 hours ago, TAUMP said:

    I dont see what is fixed, wtf

     

    1 hour ago, Theboost said:

    I have seen your gif but can you explain ?

    When you use this skill with a warrior you can see your HP dropping and coming back to normal

    • Facepalm 1
    • Love 1
  16. questlua_dungeon.cpp
     

    	ALUA(dungeon_give_item)
    	{
    		if (!lua_tonumber(L, 1))
    			return 0;
    		
    		int count = 1;
    		
    		if (lua_tonumber(L, 2) > 1)
    			count = lua_tonumber(L, 2);
    		
    		CQuestManager& q = CQuestManager::instance();
    		
    		LPDUNGEON pDungeon = q.GetCurrentDungeon();
    
    		if (pDungeon)
    			pDungeon->GiveItem(lua_tonumber(L, 1), count);
    
    		return 0;
    	}
    	
    
    --------------------------------------------------
    			{"give_item2", dungeon_give_item},

     

    dungeon.cpp
     

    namespace
    {
    	struct FGiveItem
    	{
    		FGiveItem(int vnum, int count) : m_vnum(vnum), m_count(count)
    		{
    		}
    
    		void operator() (LPENTITY ent)
    		{
    			if (ent->IsType(ENTITY_CHARACTER))
    			{
    				LPCHARACTER ch = (LPCHARACTER) ent;
    				if (ch && ch->IsPC())
    				{
    					int iCount = m_count;
    					//ch->ChatPacket(CHAT_TYPE_COMMAND, "%s", m_psz);
    					const auto& item = ITEM_MANAGER::instance().CreateItem(m_vnum, m_count, 0, true);
    					if (item->IsStackable())
    					{
    						ch->AutoGiveItem(m_vnum, m_count);
    					}
    					else
    					{
    						while (iCount > 0) {
    							ch->AutoGiveItem(m_vnum, 1);
    							iCount -= 1;
    						}
    					}
    					ITEM_MANAGER::instance().DestroyItem(item);
    				}
    			}
    		}
    
    		int m_vnum;
    		int m_count;
    	};
    }
    
    void CDungeon::GiveItem(int vnum, int count)
    {
    	sys_log(0, "XXX Dungeon GiveItem %p %d %d", this, vnum, count);
    	LPSECTREE_MAP pMap = SECTREE_MANAGER::instance().GetMap(m_lMapIndex);
    
    	if (!pMap)
    	{
    		sys_err("cannot find map by index %d", m_lMapIndex);
    		return;
    	}
    	
    	FGiveItem f(vnum, count);
    	pMap->for_each(f);
    }

    dungeon.h

    	public:
    		void	GiveItem(int vnum, int count);


    quest_function

    d.give_item2

    And finally in your quest

    d.give_item2(vnum, count)

     

    • Love 1
  17. 43 minutes ago, bunker said:

    Hmm I haven't touched the InventoryWindow.py

    In my client I edited:

    • added icon/72319.tga
    • added icon/72320.tga
    • edited locale/de/item_list.txt
    • edited locale/de/itemdesc.txt
    • edited locale/de/locale_game.txt
    • edited root/game.py
    • edited root/interfacemodule.py
    • edited root/uicommon.py
    • edited root/uiinventory.py
    • added etc/ymir work/ui/ex_inven_cover_button_close.sub
    • added etc/ymir work/ui/ex_inven_cover_button_open.sub
    • replaced etc/ymir work/ui/windows.dss

     

    EDIT: ohh.. how do I edit the item_proto in my locale/de/ folder?

    EDIT2: Got it to work (dump_proto) now it looks like this - much better:

    J4a4zgh.gif

     

    So you have to edit the "InventoryWindow.py" this is the ui (icons, positions, etc ...)

  18. 5 minutes ago, bunker said:

    Thank you for your answer. I still couldn't figure it out. 😕 

    I checked the item_proto, everything seems fine to me with that.

    For the other part, I'm not quite sure what you mean.

    Attach your uiinventory.py here so I can show you what I mean. The item_proto is not only the .txt by the way, you have to compile "item_proto.txt" / "item_names.txt" to "item_proto" file

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