Jump to content

astroNOT

Premium
  • Posts

    48
  • Joined

  • Last visited

  • Days Won

    1
  • Feedback

    0%

Posts posted by astroNOT

  1. There's no LLM atm that can "remember" as many tokens as a full game source requires, even gemini, it will only mean it has a long input stream, not that it remembers much of it, so we'll still have to wait

    Anyhow llms are definitelly good, for me gpts was the best at cpp for example

    • Metin2 Dev 1
  2. Hello, here's a item list generator, for them lazy people (or with really tired eyes)
    Adds at eof, new item upgrades and respective location for icon/textures
    Req python 3.11
     

    import re
    
    
    def generate_item_code(items):
        item_codes = []
        for item in items:
            icon_location = item["icon_location"]
            texture_location = item["texture_location"]
            vnums = item["vnums"]
    
            for item_id in vnums:
                for i in range(10):
                    item_code = f"{item_id + i}\tWEAPON\t{icon_location.replace('*', str(item_id))}\t{texture_location.replace('*', str(item_id))}"
                    item_codes.append(item_code)
        return item_codes
    
    
    def write_to_txt(item_codes, file_path):
        with open(file_path, "r+") as file:
            existing_lines = file.readlines()
            for item_code in item_codes:
                if item_code + "\n" in existing_lines:
                    item_vnum = re.split(r'\t+', item_code)[0]
                    print(f"SKIPPED - Item code {item_vnum} already exists in item list")
                else:
                    file.write(item_code + "\n")
                    print(item_code)
    
    
    if __name__ == "__main__":
        # In order to use the locations params
        # If ur file requires any prefix "0*.tga", before * include ur prefix, also ur desired extension .tga in my case
    
        location_dict = [
            {
                "icon_location": "icon/item/0*.tga",
                "texture_location": "d:/ymir work/item/weapon/0*.gr2",
                "vnums": [8190, 8290]
            }
        ]
        item_codes = generate_item_code(location_dict)
        txt_file_path = r"client_location\item_list.txt"
        write_to_txt(item_codes, txt_file_path)

     

  3. Can confirm that adding any instruction prior to while idle, seems to not give enough time for the maps & mobs to load

    	try
    	{
    		// Hardcoded values
    		const DWORD dwVnum = 101;
    		const int count = 10;
    		const bool isAggressive = false;
    		const int iMapIndex = 352;
    		const int iMapX = 360;
    		const int iMapY = 360;
    
    		// Assume SECTREE_MANAGER and CHARACTER_MANAGER are properly initialized and available
    		PIXEL_POSITION pos;
    		if (!SECTREE_MANAGER::instance().GetMapBasePositionByMapIndex(iMapIndex, pos))
    		{
    			sys_log(0, "PPPPPPQQQ Error: Cannot find base position in this map %d", iMapIndex);
    		}
    
    		const CMob *pMonster = CMobManager::instance().Get(dwVnum);
    		if (pMonster == NULL)
    		{
    			sys_log(0, "PPPPPPQQQ Error: No mob data for VNUM %d", dwVnum);
    		}
    
    		size_t SpawnCount = 0;
    		for (size_t i = 0; i < count; ++i)
    		{
    			LPCHARACTER pSpawnMonster = CHARACTER_MANAGER::instance().SpawnMobRange(
    				dwVnum,
    				iMapIndex,
    				pos.x - number(5, 5) + (iMapX * 100),
    				pos.y - number(5, 5) + (iMapY * 100),
    				pos.x + number(5, 5) + (iMapX * 100),
    				pos.y + number(5, 5) + (iMapY * 100),
    				false,
    				pMonster->m_table.bType == CHAR_TYPE_MONSTER,
    				isAggressive);
    			if (pSpawnMonster != NULL)
    			{
    
    				SpawnCount++;
    			}
    		}
    
    		sys_log(0, "Spawned %u monsters successfully.", SpawnCount);
    	}
    	catch (const std::exception &e)
    	{
    		sys_log(0, "An exception occurred: %s", e.what());
    	}
    
    	while (idle())
    		;

    What it logs is:

    
    ./srv1/chan/ch1/core1/syslog:Apr 17 19:23:11 :: PPPPPPQQQ Error: Cannot find base position in this map 352
    ./srv1/chan/ch1/core1/syslog:Apr 17 19:23:11 :: PPPPPPQQQ Error: No mob data for VNUM 101

    Trial and error continues 😄

  4. Hello!

    Lets say I have the follow scenario:
    At server startup:
    I want to spawn two mobs, as an example function(correctness does not matter)

    void SpawnMobs(DWORD mob_vnum, int count, int map_index, int x, int y)
    {
        for (int i = 0; i < count; ++i)
        {
            LPCHARACTER mob = CHARACTER_MANAGER::instance().SpawnMob(mob_vnum, map_index, x, y, 0, false, -1);
            if (!mob)
                sys_err("Failed to spawn mob VNUM %d on map %d at (%d, %d)", mob_vnum, map_index, x, y);
        }
    }

     

    And at a given date-time, to make the mobs move to a specific location(Again function correctness is probably wrong, but this is just a POCE)

    void MoveMobTo(DWORD mob_vid, int target_x, int target_y)
    {
        using namespace std::chrono;
    
        //April 18, 2024, at 18:00
        std::tm scheduled_time = {};
        scheduled_time.tm_year = 2024
        scheduled_time.tm_mon = 4 - 1;        
        scheduled_time.tm_mday = 18;       
        scheduled_time.tm_hour = 18;          
        scheduled_time.tm_min = 0;          
        scheduled_time.tm_sec = 0;           
    
        auto scheduled_time_t = std::mktime(&scheduled_time);
        system_clock::time_point scheduled_tp = system_clock::from_time_t(scheduled_time_t);
    
        // Get current time
        system_clock::time_point now = system_clock::now();
    
        // Check if the current time matches the scheduled time
        if (now == scheduled_tp)
        {
            LPCHARACTER mob = CHARACTER_MANAGER::instance().Find(mob_vid);
            if (mob)
            {
                mob->Goto(target_x, target_y);
                sys_log(0, "Mob with VID %d moved to (%d, %d) as scheduled", mob_vid, target_x, target_y);
            }
            else
            {
                sys_err("Failed to find mob with VID %d to move", mob_vid);
            }
        }
        else
        {
            sys_log(0, "MoveMobTo called, but it is not the scheduled time yet.");
        }
    }

    I have two main questions
    1. !Where should i be calling these functions within the source game, in main seems like it might not be a good idea
    2. Checking time constantly until desired time is true, seems quite tricky, a while true surely is not good, if u have any basic suggestions it would be more then welcomed 😄

    Basically my goal is to have mobs spawn, if not already spawned, move them at a specific hour to a exact spot on the map, make em fight each other, while fighting pc can't attack, after pc can attack the surviver, but not instantly, spawn another boss, keep it there for a specific time, if not dead, despawn it

    Realistically, if i'd know where to call my functions with the instructions and how to not use while loops to check for specific  states, it would be enough 🙂

    Thank you in advance!


     

  5. The idea is, i want this quest to run regardless of player actions, at the moment it seems to start at player login

    ideally the quest would start at day Y(could have daily flag) hour X so basically a scheduler, so it won't be dependent on any player events
     

    quest test_npcmove begin
        state start begin
    		when letter begin
    			chat("Started")
    			set_state("spawn_monster_in_map_boss_1")
    		end
        end
    
        -- State to check for/spawn boss 1
        state spawn_monster_in_map_boss_1 begin
            when letter begin
                chat("Spawning first boss")
                local mob_vnum = 102  -- Adjust VNUM for boss 1
                local map_index = 352
                local mobs = find_mobs_in_map(mob_vnum, map_index)
                local vid_boss1 = 0
                local mobs_str = "BOSS 1 VIDs: {" .. table.concat(mobs, ", ") .. "}"
                chat(mobs_str)
                chat("Player X: " .. 339 .. " Y: " .. 363)
    
                if table.getn(mobs) > 0 then
                    vid_boss1 = mobs[1]
                    chat("Boss 1 found, using existing with VID: " .. tostring(vid_boss1))
                else
                    local vids = spawn_monster_in_map(mob_vnum, 1, false, map_index, 339, 363 + 10, true)
                    vid_boss1 = vids[1]
    				if vids[1] ~= nil then
    					vid_boss1 = vids[1]
    					chat("Boss 1 spawned with VID: " .. tostring(vid_boss1))
    				else
    					vids = find_mobs_in_map(mob_vnum, map_index)
    					vid_boss1 = vids[1]
    					chat("Boss 2 fetched vid for spwaned mob: " .. tostring(vid_boss1))
    				end
                end
    
                pc.setqf("vid_boss1", vid_boss1)
                set_state("spawn_monster_in_map_boss_2")
            end
        end
    
        -- State to check for/spawn boss 2
        state spawn_monster_in_map_boss_2 begin
            when letter begin
                local mob_vnum = 101  -- Adjust VNUM for boss 2
                local map_index = 352
                local mobs = find_mobs_in_map(mob_vnum, map_index)
                local vid_boss2 = 0
                local mobs_str = "BOSS 2 VIDs: {" .. table.concat(mobs, ", ") .. "}"
                chat(mobs_str)
    
                if table.getn(mobs) > 0 then
                    vid_boss2 = mobs[1]
                    chat("Boss 2 found, using existing with VID: " .. tostring(vid_boss2))
                else
                    local vids = spawn_monster_in_map(mob_vnum, 1, false, map_index, 339, 363 - 10, true)
                    vid_boss2 = vids[1]
    				if vids[1] ~= nil then
    					vid_boss2 = vids[1]
    					chat("Boss 1 spawned with VID: " .. tostring(vid_boss2))
    				else
    					vids = find_mobs_in_map(mob_vnum, map_index)
    					vid_boss2 = vids[1]
    					chat("Boss 2 fetched vid for spwaned mob: " .. tostring(vid_boss2))
    				end
                end
    
                pc.setqf("vid_boss2", vid_boss2)
                set_state("move_bosses")
            end
        end
    
        -- State to move both bosses
        state move_bosses begin
            when letter begin
                local vid_boss1 = pc.getqf("vid_boss1")
                local vid_boss2 = pc.getqf("vid_boss2")
                local target_x = 339
                local target_y = 363
    
                mob_move(vid_boss1, target_x, target_y)
                mob_move(vid_boss2, target_x, target_y)
    
                chat("Both bosses moved to player's position.")
                set_state("bosses_fight")
            end
        end
    
        -- State where bosses fight
    	state bosses_fight begin
    		when letter begin
    			local vid_boss1 = pc.getqf("vid_boss1")
    			local vid_boss2 = pc.getqf("vid_boss2")
    			local map_index = 352
    			chat("Boss 1 VID: " .. tostring(vid_boss1))
    			chat("Boss 2 VID: " .. tostring(vid_boss2))
    			local success2, message2 = set_pc_can_attack_monster(vid_boss1, false, map_index)
    			local success3, message3 = set_pc_can_attack_monster(vid_boss2, false, map_index)
    			chat("Attempt to make boss 1 invinciple " .. tostring(success2) .. " - " .. message2)
    			chat("Attempt to make boss 2 invinciple " .. tostring(success3) .. " - " .. message3)
    
    			-- Attempt to make boss 1 attack boss 2
    			local success1, message1 = attack_mob(vid_boss1, vid_boss2, map_index)
    			if success1 ~= nil and message1 ~= nil then
    				chat("Attack attempt from Boss 1 to Boss 2: " .. tostring(success1) .. " - " .. message1)
    			else
    				chat("Attack attempt from Boss 1 to Boss 2: success or message is nil")
    			end
    
    			-- Attempt to make boss 2 attack boss 1
    			local success2, message2 = attack_mob(vid_boss2, vid_boss1, map_index)
    			if success2 ~= nil and message2 ~= nil then
    				chat("Attack attempt from Boss 2 to Boss 1: " .. tostring(success2) .. " - " .. message2)
    			else
    				chat("Attack attempt from Boss 2 to Boss 1: success or message is nil")
    			end
    
    			chat("Bosses are now set to fight each other.")
    			chat("---------------------------------END------------------------------------")
    			set_state("bosses_alive")
    		end
    	end
    	
    	state bosses_alive begin
    		when letter begin
    			chat("Checking if boss died")
    			local vid_boss1 = pc.getqf("vid_boss1")
    			local vid_boss2 = pc.getqf("vid_boss2")
    			local map_index = 352
    
    			-- Check if Boss 1 is alive
    			local is_boss1_alive, boss1_msg = is_mob_alive_in_map(vid_boss1, map_index)
    			chat("Boss 1 alive check: " .. tostring(is_boss1_alive) .. " - " .. boss1_msg)
    
    			-- Check if Boss 2 is alive
    			local is_boss2_alive, boss2_msg = is_mob_alive_in_map(vid_boss2, map_index)
    			chat("Boss 2 alive check: " .. tostring(is_boss2_alive) .. " - " .. boss2_msg)
    
    			-- Determine the next state based on the bosses' statuses
    			if not is_boss1_alive or not is_boss2_alive then
    				if is_mob_alive_in_map(vid_boss1, map_index) then
    					local success2, message2 = set_pc_can_attack_monster(vid_boss1, true, map_index)
    					chat("Resetting invicibility for boss1 " .. tostring(success2) .. " - " .. message2)
    				end
    				if is_mob_alive_in_map(vid_boss2, map_index) then
    					local success3, message3 = set_pc_can_attack_monster(vid_boss2, true, map_index)
    					chat("Resetting invicibility for boss2 " .. tostring(success3) .. " - " .. message3)
    				end
    				
    				chat("One of the bosses is dead. Resetting quest.")
    				set_state("start")
    			else
    				chat("Both bosses are alive. Continuing the fight.")
    				set_state("bosses_alive")
    
    			end
    		end
    	end
    end

    And the last state, called bosses_alive seems to also reexecute only at player relog (login)

    Is there a way to make it so the states are independent of player events and just execute as soon as sv starts?

  6. Hello,

    So I m trying to find where this vnum.kill function is defined on serverside, seems quite tricky, if anybody knows where it is defined and what name it has, please lmk 😄

    Thanks,
    Gabi

    might be this guy

    	void CQuestManager::Kill(unsigned int pc, unsigned int npc)
    	{
    		//m_CurrentNPCRace = npc;
    		PC * pPC;
    
    		sys_log(0, "CQuestManager::Kill QUEST_KILL_EVENT (pc=%d, npc=%d)", pc, npc);
    
    		if ((pPC = GetPC(pc)))
    		{
    			if (!CheckQuestLoaded(pPC))
    				return;
    
    			// kill call script
    			if (npc >= MAIN_RACE_MAX_NUM) //@fixme109
    				m_mapNPC[npc].OnKill(*pPC); //@warme004
    			m_mapNPC[QUEST_NO_NPC].OnKill(*pPC);
    
    #ifdef ENABLE_PARTYKILL
    			// party_kill call script
    			LPCHARACTER ch = GetCurrentCharacterPtr();
    			LPPARTY pParty = ch->GetParty();
    			LPCHARACTER leader = pParty ? pParty->GetLeaderCharacter() : ch;
    			if (leader)
    			{
    				m_pCurrentPartyMember = ch;
    				if (npc >= MAIN_RACE_MAX_NUM) //@fixme109
    					m_mapNPC[npc].OnPartyKill(*GetPC(leader->GetPlayerID())); //@warme004
    				m_mapNPC[QUEST_NO_NPC].OnPartyKill(*GetPC(leader->GetPlayerID()));
    
    				pPC = GetPC(pc);
    			}
    #endif
    		}
    		else
    			sys_err("QUEST: no such pc id : %d", pc);
    	}

     

  7. ALUA(_spawn_monster_in_map)
    
        {
    
            if (false == lua_isnumber(L, 1) || false == lua_isnumber(L, 2) || false == lua_isboolean(L, 3) ||
    
                false == lua_isnumber(L, 4) || false == lua_isnumber(L, 5) || false == lua_isnumber(L, 6))
    
            {
    
                lua_pushnumber(L, 0); // Return an error in the form of 0 if inputs are incorrect
    
                return 1;
    
            }
    
    
            const DWORD dwVnum = static_cast<DWORD>(lua_tonumber(L, 1));
    
            const size_t count = MINMAX(1, static_cast<size_t>(lua_tonumber(L, 2)), 10);
    
            const bool isAggressive = static_cast<bool>(lua_toboolean(L, 3));
    
            const int iMapIndex = static_cast<int>(lua_tonumber(L, 4));
    
            const int iMapX = static_cast<int>(lua_tonumber(L, 5));
    
            const int iMapY = static_cast<int>(lua_tonumber(L, 6));
    
    
            PIXEL_POSITION pos;
    
            if (!SECTREE_MANAGER::instance().GetMapBasePositionByMapIndex(iMapIndex, pos))
    
            {
    
                sys_err("QUEST _spawn_mob_in_map: cannot find base position in this map %d", iMapIndex);
    
                lua_pushnumber(L, 0); // Push 0 to indicate failure
    
                return 1;
    
            }
    
    
            const CMob *pMonster = CMobManager::instance().Get(dwVnum);
    
            if (pMonster == NULL)
    
            {
    
                sys_err("QUEST _spawn_mob_in_map: no mob data for VNUM %d", dwVnum);
    
                lua_pushnumber(L, 0); // Push 0 to indicate failure
    
                return 1;
    
            }
    
    
            lua_newtable(L); // Create a new table on the stack to hold the VIDs
    
    
            size_t SpawnCount = 0;
    
    
            for (size_t i = 0; i < count; ++i)
    
            {
    
                LPCHARACTER pSpawnMonster = CHARACTER_MANAGER::instance().SpawnMobRange(dwVnum,
    
                                                                                        iMapIndex,
    
                                                                                        pos.x - number(200, 750) + (iMapX * 100),
    
                                                                                        pos.y - number(200, 750) + (iMapY * 100),
    
                                                                                        pos.x + number(200, 750) + (iMapX * 100),
    
                                                                                        pos.y + number(200, 750) + (iMapY * 100),
    
                                                                                        true,
    
                                                                                        pMonster->m_table.bType == CHAR_TYPE_MONSTER,
    
                                                                                        isAggressive);
    
    
                if (pSpawnMonster != NULL)
    
                {
    
                    lua_pushnumber(L, SpawnCount + 1);          // Push the index in the table
    
                    lua_pushnumber(L, pSpawnMonster->GetVID()); // Push the VID
    
                    lua_settable(L, -3);                        // Set the table at index -3 with key at -2 and value at -1
    
                    SpawnCount++;
    
                }
    
            }
    
    
            sys_log(0, "QUEST Spawn Monster: VNUM(%u) COUNT(%u) isAggressive(%b)", dwVnum, SpawnCount, isAggressive);
    
    
            return 1; // Returns the table at the top of the stack
    
        }

    As u can see above i spawn the mob as `CHAR_TYPE_MONSTER` but when I m checking if
     

    		if (attacker->IsNPC() || target->IsNPC())
    		{
    			sys_log(0, "MOB_ATTACK_FUNC Victim is NPC not Monster not attackable");
    			lua_pushboolean(L, 0);
    			lua_pushstring(L, "MOB_ATTACK_FUNC Victim is NPC not Monster not attackable");
    			return 0;
    		}

    This is always returning true, and bcs of it, i cannot set `bool bAttackSuccess1 = attacker->Attack(target, 0);`
    Because apparently a npc cannot attack

    does anybody know how to spawn a mob (Character instance) that is not NPC but mosnter so it can attack other monsters?

     

  8. I m trying to make a mob, attack another mob, by specific attacker vid and victim vid, but can't figure out which Character class method does the actual attack
     

        int game_mob_attack_mob(lua_State *L)
        {
            if (!lua_isnumber(L, 1) || !lua_isnumber(L, 2) || !lua_isnumber(L, 3))
            {
                lua_pushboolean(L, 0);
                lua_pushstring(L, "Invalid argument. Usage: mob_attack_mob(attacker_vid, target_vid, map_index)");
                return 2;
            }
    
            int attacker_vid = (int)lua_tonumber(L, 1);
            int target_vid = (int)lua_tonumber(L, 2);
            int map_index = (int)lua_tonumber(L, 3); // Expected map index
    
            LPCHARACTER attacker = CHARACTER_MANAGER::instance().Find(attacker_vid);
            LPCHARACTER target = CHARACTER_MANAGER::instance().Find(target_vid);
    
            if (!attacker || !attacker->IsMonster())
            {
                lua_pushboolean(L, 0);
                lua_pushstring(L, "Attacker not found or is not a monster.");
                return 2;
            }
    
            if (!target || !target->IsMonster())
            {
                lua_pushboolean(L, 0);
                lua_pushstring(L, "Target not found or is not a monster.");
                return 2;
            }
    
            if (attacker->GetMapIndex() != map_index || target->GetMapIndex() != map_index)
            {
                lua_pushboolean(L, 0);
                lua_pushstring(L, "Either attacker or target is not in the specified map.");
                return 2;
            }
    
            // All checks passed, proceed to set victim
            attacker->SetVictim(target);
            attacker->BeginFight(target);
            lua_pushboolean(L, 1);
            lua_pushstring(L, "Attack set successfully.");
            return 2;
        }

    Basically, "Attack set successfully is triggered but the mobs do not attack each otther, even if they are loading by their vid...

    Updated it to use
     

    		attacker->SetVictim(target);
    		target->SetVictim(attacker);
    
    		// Trigger the initial attack and capture the return values
    		bool bAttackSuccess1 = attacker->Attack(target, 0);
    
    		// Return the result of the first attack attempt to Lua for debugging
    		lua_pushboolean(L, bAttackSuccess1);
    		if (bAttackSuccess1)
    			lua_pushstring(L, "Attack by attacker was successful.");
    		else
    			lua_pushstring(L, "Attack by attacker failed.");
    
    		return 2;
    	}

    But for some reason Attack method does not get triggered at all.. but the Attack by attacker failed is pushed succesfully

  9. Dosen't seem like anything special, just 3 classes from initial implementation of ui.py that had to be added CheckBox, ExpandedButton and CustomScrollBar that I just had to copy pasted.

    But it seems like most actions are defined here, so in terms of accuracy for ui part ur spot on, i expect on launcher side that I might have some problems, even tho i did double check a couple of times and it seems like i p much moved everything where it should be..

    Thank you tho, I'll keep looking

  10. So Back in 2021, i bought @ Ikarus_'s shop offline, as most ppl know, V1 of the shop is not supported by ikarus anymore, so I am on my own out here hahaha

    The idea is back in 2021 I wanted to open a server, long story short svf was compromised and couldn't go further with the idea

    Now, i bought marty's svf, and I want to transfer the shop from my old server to this one

    But i hit a dead end with debugging, can't figure out how a click event is handle

    The problem is:
    giphy.gif

    So problem seems to be that from uinewofflineshop.py, NewOfflineShopBoard.__OnLeftClickShopItem method is not called at all when i click on a item that is already in a shop, and i now do not know what to look for to understand how it works, what function/class handles the mouse over stuff and triggers specific events when an item is clicked, basically to understand how __OnLeftClickShopItem is triggered and when.

    There is a method NewOfflineShopBoard.RefreshOpenShopPage that is triggered and sets this "mouse up" event

    slot.SetOnMouseLeftButtonUpEvent(self.__OnLeftClickShopItem)

    But for some reason it aint working, not sure where to go from here realistically

  11. 
    import os
    
    #no of channels
    channels = 4
    
    #no of cores
    cores = 2
    
    #Channels path
    chan_location = "/usr/home/main/srv1/chan/"
    
    #New ip to replace the old
    ip_value = "123.443.9.1"
    
    try:
        os.mkdir("%sbackup_config" % chan_location)
    except Exception:
        pass
    
    for i in range(1, channels + 1):
        # print(i)
        for core in range(1, cores + 1):
            with open(f"%sch{i}/core{core}/CONFIG" % chan_location, 'r') as cfg:
                count = 0
                proxy_index = 0
                list_of_lines = cfg.readlines()
                with open(f"%sbackup_config/config_channel{i}_core{core}" % chan_location, "w") as bk_file:
                    bk_file.writelines(list_of_lines)
    
                for line in list_of_lines:
                    count += 1
                    # print(line)
                    if "proxy" in line.lower():
                        proxy_index = count - 1
                # print(len(list_of_lines), 'list of lines len')
                print("proxy index ", proxy_index)
                list_of_lines[proxy_index] = "proxy_ip: %s\n" % ip_value
            with open(f"%sch{i}/core{core}/CONFIG" % chan_location, 'w') as cfg_w:
                cfg_w.writelines(list_of_lines)
                print(f"Succesfully updated config for Channel{i}, Core{core}")

     

    • Good 2
    • Love 1
    • Love 1
  12. 22 hours ago, PetePeter said:

    "Because of this stupid line of code `tokens = line[:-1].split("\t")` you gotta have an extra char(space) after 'SA'"

    This is for remove the line break, just add an empty line if it's the last line of your file. Because if you add another line after this one, the script will take your space at function name also

    Indeed it removes the last char, in our chase the /n, aaand if its the last line in the file w/o a nl, then its doomed, updated the topic accordingly, thanks!

  13. Hello,

    When adding the bellow string to locale_game.txt

    APPLY_ANTI_RESIST_MAGIC	Magic Pen: %d%%	SA

    A key error is raised:
     

    0613 21:44:08342 :: 
    system.py(line:287) RunMainScript
    system.py(line:204) execfile
    prototype.py(line:3) <module>
    system.py(line:151) __hybrid_import
    system.py(line:116) _process_result
    localeInfo.py(line:210) <module>
    localeInfo.py(line:167) LoadLocaleFile
    
    Run - <type 'exceptions.KeyError'>:'S'
    
    0613 21:44:08343 :: ============================================================================================================
    0613 21:44:08343 :: Abort!!!!
    
    

    Without the SA at the end, the bonus name is not properly displayed in game
    W/o the SA at the end, the bonus name is displayed as "UNKNOWN_NAME(93), the 93 is the index for the bonus in `enum EApplyTypes` from itemData.h

    Any ideas?
     

    LoadLocalFile py function:

    Spoiler
    def LoadLocaleFile(srcFileName, localeDict):
    
    	def SNA(text):
    		def f(x):
    			return text
    		return f
    
    	def SA(text):
    		def f(x):
    			return text % x
    		return f
    
    	funcDict = {"SA":SA, "SNA":SNA}
    
    	lineIndex = 1
    
    	try:
    		lines = open(srcFileName, "r").readlines()
    	except IOError:
    		import dbg
    		dbg.LogBox("LoadLocaleError(%(srcFileName)s)" % locals())
    		app.Abort()
    
    	for line in lines:
    		try:
    			tokens = line[:-1].split("\t")
    					
    			if len(tokens) == 2:
    				localeDict[tokens[0]] = tokens[1]
    			elif len(tokens) >= 3:
    				type = tokens[2].strip()
    				if type:
    					localeDict[tokens[0]] = funcDict[type](tokens[1]) #---------------- LINE 167 from error traceback
    				else:
    					localeDict[tokens[0]] = tokens[1]
    			else:
    				raise RuntimeError, "Unknown TokenSize"
    
    			lineIndex += 1
    		except:
    			import dbg
    			dbg.LogBox("%s: line(%d): %s" % (srcFileName, lineIndex, line), "Error")
    			raise

     

     

    Thanks!

    EDIT:
    SOLVED:

              Because of this stupid line of code `tokens = line[:-1].split("\t")` you gotta have an extra char(space/new line) after 'SA'  *only if its the last line in the file*
             

     

     

    • Metin2 Dev 2
  14. M2 Download Center

    This is the hidden content, please
    ( Internal )

    The script updates item prices (buy/sell) for an item vnum:

    Considering item vnum is 110, it can update till 119 (or starting vnum 112 can go till 115, or 117, or w/e you desire)
    It can update for a single vnum (upgrade items, aswell), more in comments below 

    Combined with can be pretty efficient

    from halo import Halo
    import bcolors
    
    class Append_new_prices:
        def __init__(self, value_dict, proto_location):
            self.proto_location = proto_location
            self.protoLst = []
            self.value_dict = value_dict
    
        def get_proto_asList(self):
            with open(self.proto_location, 'r', encoding="utf8") as file:
                for line in file.readlines():
                    lineLst = line.split('\t')
                    self.protoLst.append(lineLst)
                    self.modified_protoLst = []
    
        def update_protoLst(self):
            count = 0
            for vnum, prices in self.value_dict.items():
                try:
                    upp_range = prices['uppLimit']
                except KeyError:
                    upp_range = 1
    
                vnum_lst = self.get_item_VnumRange(vnum, upp_range)
    
                # while upp_range >= 0:
                for lineLst in self.protoLst:
    
                    if "buy" not in prices.keys():
                        prices['buy'] = 0
    
                    if "sell" not in prices.keys():
                        prices['sell'] = 0
    
                    # print((lineLst[0]), "  ==  ", vnum_lst)
                    try:
                        line_vnum = int(lineLst[0])
                    except ValueError:
                        line_vnum = "VNUM NOT INT PFFFFF"
    
                    if line_vnum in vnum_lst:
                        lineLst[9] = prices['buy']
                        lineLst[10] = prices['sell']
                        self.protoLst[self.protoLst.index(lineLst)] = lineLst
    
    
        @staticmethod
        def get_item_VnumRange(vnum, range_lim):
            vnum_lst = []
            plus_val = 0
            # if range_lim == 0:
            #     range_lim = 1
    
            vnum = int(vnum)
            for i in range(0, range_lim + 1):
                vnum += plus_val
                vnum_lst.append(vnum)
                plus_val = 1
            return vnum_lst
    
    
        def write_new_proto_fromList(self):
            with open(self.proto_location, 'w', encoding="utf8") as file:
                for line in self.protoLst:
                    file.write('\t'.join(self.str_list(line)))
    
        def str_list(self, lst):
            new_lst = []
            for i in lst:
                new_lst.append(str(i))
            return new_lst
    
    
    if __name__ == "__main__":
    
        #First number (110) is the starting vnum, uppLimit is till when to stop adding prices, upplimit 9 means => 110, 111, 112, 113, 114...119
        #It can have h/e many "item" objects as dictionary elements
        #!!!!!Count starts from 0!
        items = \
    {
       "110": {
          "buy": 55,
          "sell": 43,
          "uppLimit": 1
       },
       # "120":{
       #    "buy":4000,
       #    "sell":400000,
       #    "uppLimit":9
       # }
    }
    
        setPrices = Append_new_prices(value_dict=items,
                                      proto_location=r'C:\Users\itsas\Desktop\M2\versionControl\DumpProto\Release\item_proto.txt')
    
        with Halo(text='Loading', spinner='dots'):
            setPrices.get_proto_asList()
            setPrices.update_protoLst()
            setPrices.write_new_proto_fromList()
        print(f'{bcolors.OKMSG}{bcolors.OK} Done!')
    
    
    
    

    Prereq:
    Libs: Halo, bcoloros
    Python 3.9

    • Metin2 Dev 10
    • Good 7
    • Love 1
    • Love 7
  15. M2 Download Center

    This is the hidden content, please
    ( Internal )

    Hello peeps!

    Got some more utilities that i use to automate boring stuff, so, maybe it can help u guys too
    The script kills the client processes, generates dump_proto and moves it into locale_xx directory (optional), crypts a list of files(root, locale, icon, ...etc) and starts the client.
     

    import os
    import shutil
    import bcolors
    import time
    
    class utilities:
    
        def __init__(self, packer_path, client_path, launcher_Name, dumpProto_path, locale_XX_path):
            self.packer_path = packer_path
            self.client_path = client_path
            self.launcher_Name = launcher_Name
            self.dumpProto_path = dumpProto_path
            self.locale_XX_path = locale_XX_path
    
        def fox_Pack(self, filesList):
            for i in filesList:
                print(f"\t* Will exec :> [{i}]")
                os.chdir(self.packer_path)
                os.system(f"tools\\Archiver.exe make_xml/{i}_create.xml")
                os.system(f"tools\\Archiver.exe xml/{i}_create.xml")
                time.sleep(0.5)
            # os.system(archiver_path)
    
        def killProcess(self):
            import psutil
            for proc in psutil.process_iter():
                # check whether the process name matches
                if proc.name() == self.launcher_Name:
                    proc.kill()
            time.sleep(0.5)
    
    
        def createItemProto(self):
            os.chdir(self.dumpProto_path)
            os.system(f"{self.dumpProto_path}\\dump_proto.exe")
            if os.path.isfile(self.locale_XX_path + r"\item_proto_old"):
                os.remove(self.locale_XX_path + r"\item_proto_old")
    
            os.rename(self.locale_XX_path + "\\item_proto", self.locale_XX_path + "\\item_proto_old")
            shutil.copy2("item_proto", self.locale_XX_path)
    
        def startClient(self):
            os.chdir(self.client_path)
            os.startfile(self.client_path + "\\" + self.launcher_Name)
    
    if __name__ == "__main__":
        start_time = time.time()
        print(f"{bcolors.OKMSG}{bcolors.OK}#-------[START]-------#\n{bcolors.ENDC}")
    
        utils = utilities(
        #packer Location 
        packer_path = r"C:\Users\itsas\Desktop\M2\versionControl\client\Packer",
        #client Location
        client_path = r"C:\Users\itsas\Desktop\M2\versionControl\client\Metin2",
        launcher_Name = "metin2_launcher.exe",
        #dumpProto RELEASE Location
        dumpProto_path = r"C:\Users\itsas\Desktop\M2\versionControl\DumpProto\Release",
        #locale\XX Location
        locale_XX_path = r"C:\Users\itsas\Desktop\M2\versionControl\client\Client\locale_general\ro")
    
        utils.killProcess()
        utils.createItemProto()
        #To pack multiple files, addd in the below list more, directory names
        utils.fox_Pack(["locale_general", "icon", "item"])
        utils.startClient()
    
        print(f"{bcolors.BLUE}--- {bcolors.WAITMSG}{bcolors.BLUE}{(time.time() - start_time)} seconds ---\n{bcolors.ENDC}")
        print(f"{bcolors.OKMSG}{bcolors.OK}#-------[STOP]-------#")

    Prereq:
    Python 3.9
    Libraries: bcolors, shutil

    • Metin2 Dev 44
    • Eyes 1
    • Dislove 1
    • Think 2
    • Good 20
    • Love 5
    • Love 28
  16. Hello,

    Some boring clicking automated that I use, feel free to improve it, add waits, w/e, w/e

    import time, os
    from pywinauto import Desktop, Application
    import bcolors
    from pywinauto.keyboard import send_keys
    import psutil
    
    
    class mySession:
        app = Application(backend='uia')
    
        def __init__(self, vm_Name='', ssh_Name='', cliente_exe_Path='', winSCP_path='', vbox_exe_path=''):
            self.vm_Name = vm_Name
            self.ssh_Name = ssh_Name
            self.cliente_exe_Path = cliente_exe_Path
            pathList = cliente_exe_Path.split('\\')
            self.client_path = '/'.join(pathList[:-1])
            self.exe_name = pathList[-1]
            self.vbox_exe_path = vbox_exe_path
            self.winSCP_path = winSCP_path
    
        def openVM(self, waitServer):
            self.killProcess("VirtualBox.exe")
            self.app.start(self.vbox_exe_path)
            self.app = Application(backend='uia').connect(title='Oracle VM VirtualBox Manager', timeout=10)
            # self.app.Dialog.print_control_identifiers()
            self.app.OracleVMVirtualBoxManager.child_window(title=self.vm_Name, control_type="ListItem").wrapper_object().click_input(button='left', double=True)
            # self.app = Application(backend='uia').connect(title=self.whoAmI('Starting')[0], timeout=10)
            time.sleep(waitServer)
            print(f"\t{bcolors.OK}{bcolors.OKMSG}VM is ready{bcolors.ENDC}")
    
        def openWinSCP(self):
            self.killProcess("WinSCP.exe")
            self.app.start(self.winSCP_path)
            self.app = Application(backend='uia').connect(title=self.whoAmI(keyword='wins')[0], timeout=40)
            time.sleep(1)
    
            if self.app.Dialog.child_window(title=self.ssh_Name + "      ", control_type="TabItem").exists():
                self.app.Dialog.child_window(title="Reconnect Session", control_type="Button").wrapper_object().click_input(
                    button='left')
            else:
                self.app.Dialog.child_window(title="New Session", control_type="TabItem").wrapper_object().click_input(button='left')
    
            auth_win = self.app.Dialog.child_window(title=self.ssh_Name, control_type="TreeItem")
            auth_win.wait('visible', timeout=50)
            auth_win.wrapper_object().click_input(button='left', double=True)
    
            input_pass = self.app.Dialog.child_window(control_type="Edit")
            input_pass.wait('visible', timeout=50)
            input_pass.wrapper_object().type_keys("dev")
    
            self.app.Dialog.child_window(title="OK", control_type="Button").wrapper_object().click_input(button='left')
            time.sleep(2)
            print(f"\t{bcolors.OK}{bcolors.OKMSG}WinScp is ready{bcolors.ENDC}")
    
        def startClient(self, do_login=True):
            os.chdir(self.client_path)
            os.startfile(self.client_path + "\\" + self.exe_name)
            # self.app = Application(backend='uia').connect(title='Metin2', timeout=10)
    
        def whoAmI(self, keyword='', retryLimit=2, isFull=False):
            while retryLimit:
                windows = Desktop(backend="uia").windows()
                windowsList = [w.window_text() for w in windows]
                resultList = []
                for i in windowsList:
                    if keyword.lower() in i.lower():
                        resultList.append(i)
    
                if not len(resultList):
                    time.sleep(0.5)
                    self.whoAmI(keyword=keyword, retryLimit=retryLimit-1, isFull=isFull)
                else:
                    # print('Matches: ', resultList)
                    if not isFull:
                        return resultList
                    else:
                        return windowsList
    
        def killProcess(self, proc_nmame, kill_=True):
            if kill_:
                for proc in psutil.process_iter():
                    # check whether the process name matches
                    if proc.name() == proc_nmame:
                        proc.kill()
                time.sleep(0.5)
    
    if __name__ == "__main__":
        print(f"{bcolors.OK}#########[START]#########{bcolors.ENDC}")
        #Here you have to give the name of ur Virtual Machine(vm_Name), winSCP ssh connetion name(ssh_Name), and ur client executable path
        controller = mySession(vm_Name="11.3_5.5_v4",
                               ssh_Name="alpha_server",
                               cliente_exe_Path=r"C:\Users\itsas\Desktop\M2\versionControl\client\Metin2\metin2_launcher.exe",
                               vbox_exe_path='"C:\Program Files\Oracle\VirtualBox\VirtualBox.exe"',
                               winSCP_path="C:\Program Files (x86)\WinSCP\WinSCP.exe")
                                
        controller.openVM(waitServer=15)
        controller.openWinSCP()
        controller.startClient()
        print(f"{bcolors.OK}#########[END]#########{bcolors.ENDC}")

     

    Prereq:

    Python 3.9

    Libs: pywinauto, psutil, bcolors

    Note: The script does kill existing processes of WinSCP and oracle VM, you can disable it, if u feel the need, search for killProcess method and set default param to False

     

    Edit: Parametrized paths for vbox, winSCP also :)

    • Love 1
  17. Hello, 

    I have this 1 button which is not clickable in the following position:

    Faulty position: https://metin2.download/picture/s0R43DwyDza6rwDugokoZ5sbXG2cLg2F/.gif

    But if i move it higher it works just fine, I not sure what to do, any ideas would be appreciated :D 

    Working position: https://metin2.download/picture/NWzWf64BnX17546rkVJMMmVfxK863eLq/.gif

    The actual ui dictionary:

    Spoiler
    
    
    import uiScriptLocale
    import item
    
    BELT_START_INDEX = item.BELT_INVENTORY_SLOT_START
    BUTTON_ROOT = "d:/ymir work/ui/public/"
    
    window = {
    	"name" : "BeltInventoryWindow",
    	"x" : SCREEN_WIDTH - 176 - 148,
    	"y" : SCREEN_HEIGHT - 37 - 565 + 209 + 32,
    	"width" : 148,
    	"height" : 199,
    
    	"type" : "image",
    	"image" : "d:/ymir work/ui/game/belt_inventory/bg.tga",
    	
    
    	"children" :
    	(
    		## Expand Buttons
    		{
    			"name" : "ExpandBtn",
    			"type" : "button",
    
    			"x" : 2,
    			"y" : 15,
    
    			"default_image" : "d:/ymir work/ui/game/belt_inventory/btn_expand_normal.tga",
    			"over_image" : "d:/ymir work/ui/game/belt_inventory/btn_expand_over.tga",
    			"down_image" : "d:/ymir work/ui/game/belt_inventory/btn_expand_down.tga",
    			"disable_image" : "d:/ymir work/ui/game/belt_inventory/btn_expand_disabled.tga",
    		},
    
    		
    		## Belt Inventory Layer (include minimize button)
    		{
    			"name" : "BeltInventoryLayer",
    #			"type" : "board",
    #			"style" : ("attach", "float"),
    
    			"x" : 5,
    			"y" : 0,
    
    			"width" : 148,
    			"height" : 139,
    
    			"children" :
    			(
    				## Minimize Button
    				{
    					"name" : "MinimizeBtn",
    					"type" : "button",
    
    					"x" : 2,
    					"y" : 15,
    
    					"width" : 10,
    
    					"default_image" : "d:/ymir work/ui/game/belt_inventory/btn_minimize_normal.tga",
    					"over_image" : "d:/ymir work/ui/game/belt_inventory/btn_minimize_over.tga",
    					"down_image" : "d:/ymir work/ui/game/belt_inventory/btn_minimize_down.tga",
    					"disable_image" : "d:/ymir work/ui/game/belt_inventory/btn_minimize_disabled.tga",
    				},
    
    				## Real Belt Inventory Board
    				{
    					"name" : "BeltInventoryBoard",
    					"type" : "board",
    					"style" : ("attach", "float"),
    
    					"x" : 10,
    					"y" : 0,
    
    					"width" : 138,
    					"height" : 179,
    
    					"children" :
    					(
    						## Belt Inventory Slots
    						{
    							"name" : "BeltInventorySlot",
    							"type" : "grid_table",
    
    							"x" : 5,
    							"y" : 5,
    
    							"start_index" : BELT_START_INDEX,
    							"x_count" : 4,
    							"y_count" : 4,
    							"x_step" : 32,
    							"y_step" : 32,
    
    							"image" : "d:/ymir work/ui/public/Slot_Base.sub"
                                
    						},
                            ##Activate all potions button
    						{
    							"name" : "UseBeltItemsButton",
    							"type" : "button",
    							"x" : 0,
    							"y" : 138,
    							"horizontal_align" : "center",
    							"default_image" : BUTTON_ROOT + "AcceptButton00.sub",
    							"over_image" : BUTTON_ROOT + "AcceptButton01.sub",
    							"down_image" : BUTTON_ROOT + "AcceptButton02.sub",
    						},
    					),
    				},
    			)
    		},
    	),
    }

     

     

  18. Hello,

    I'm keen to know, is there a way to increase a specific item texture specular (weapon/armor/etc) beyond the 100 that you can set in specular column? 
    Or better yet to edit the texture to define what 100 specular should be.
    Point is, some items aren't as shiny as they should be at 100 specular

    PS. I assume the specular is tied to the texture.

    Thanks!

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