Jump to content

Search the Community

Showing results for tags 'lua'.

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


Forums

  • Metin2 Dev
    • Announcements
  • Community
    • Member Representations
    • Off Topic
  • Miscellaneous
    • Metin2
    • Showcase
    • File Requests
    • Community Support - Questions & Answers
    • Paid Support / Searching / Recruiting
  • Metin2 Development
  • Metin2 Development
    • Basic Tutorials / Beginners
    • Guides & HowTo
    • Binaries
    • Programming & Development
    • Web Development & Scripts / Systems
    • Tools & Programs
    • Maps
    • Quests
    • 3D Models
    • 2D Graphics
    • Operating Systems
    • Miscellaneous
  • Private Servers
    • Private Servers
  • Uncategorized
    • Drafts
    • Trash
    • Archive
    • Temporary
    • Metin2 Download

Product Groups

  • Small Advertisement
  • Large Advertisement
  • Advertising

Categories

  • Third Party - Providers Directory

Categories

  • Overview
  • Pages
    • Overview
    • File Formats
    • Network
    • Extensions

Find results in...

Find results that contain...


Date Created

  • Start

    End


Last Updated

  • Start

    End


Filter by number of...

Joined

  • Start

    End


Group


Pillory


Marketplace


Game Server


Country


Nationality


Github


Gitlab


Discord


Skype


Website

  1. Hello. I saw a similar question earlier on this forum, and since it's not a big task, I quickly made this snippet. It doesn't require much explanation, you can send a whisper message from a guest using a function. Let's see: I. Open the "game/src/questlua_pc.cpp" and you have to look for the following function:: int pc_set_skill_level(lua_State* L) { ... } II. and after that whole function lines add the following function lines: int pc_send_whisper(lua_State* L) { LPCHARACTER lpCH = CQuestManager::instance().GetCurrentCharacterPtr(); int iArgIndex = 1; const char * c_szSenderName = "System"; if (lua_gettop(L) > 1) { if(!lua_isstring(L, iArgIndex)) { sys_err("QUEST : wrong argument"); lua_pushboolean(L, false); return 1; } c_szSenderName = lua_tostring(L, iArgIndex); if (strlen(c_szSenderName) == 0) { sys_err("QUEST : empty argument"); lua_pushboolean(L, false); return 1; } iArgIndex++; } if (!lua_isstring(L, iArgIndex)) { sys_err("QUEST : wrong argument"); lua_pushboolean(L, false); return 1; } const char * c_szMessage = lua_tostring(L, iArgIndex); const size_t c_size = (c_szMessage) ? strlen(c_szMessage) : 0; if (c_size == 0) { sys_err("QUEST : empty argument"); lua_pushboolean(L, false); return 1; } LPDESC lpDesc; if (!(lpDesc = lpCH->GetDesc())) { lua_pushboolean(L, false); return 1; } TPacketGCWhisper lPack; lPack.bHeader = HEADER_GC_WHISPER; lPack.wSize = sizeof(TPacketGCWhisper) + c_size; lPack.bType = WHISPER_TYPE_NORMAL; strlcpy(lPack.szNameFrom, c_szSenderName, sizeof(lPack.szNameFrom)); TEMP_BUFFER lTmpBuf; lTmpBuf.write(&lPack, sizeof(lPack)); lTmpBuf.write(c_szMessage, c_size); lpDesc->Packet(lTmpBuf.read_peek(), lTmpBuf.size()); lua_pushboolean(L, true); return 1; } III. and now, look for the following line in the "RegisterPCFunctionTable" function: { "set_skill_level", pc_set_skill_level }, IV. and before that line add the following line: { "send_whisper", pc_send_whisper }, We are done now ! Important: You have to check your own whisper packet structure, because mine and yours can be different maybe. Don't forget to add this new quest function name to the quest function list file! (If you have one, that is.) - pc.send_whisper Usage example: (The default whisper target is always the current selected player by the quest system.) -- To send message by default sender name as "System": pc.send_whisper("Hi there! :)") -- To send message by a given sender name: pc.send_whisper("Biologist", "Biolog cooldown is done, you can submit next item.") -- To check the success of send: if pc.send_whisper("Hi there! :)") then -- The whisper message has been sent successfully. else -- Failed to send the whisper message. end
  2. Some of the quests used in OLDSGODSMT2
  3. Hi, [Hidden Content] [Hidden Content] This quest is responsible for granting basic costumes to players when they reach certain levels in the game. The quest is triggered when a player logs in or levels up in the game. It checks the player's level and grants various cosmetic items if certain conditions are met. Basic Costume: Players receive a basic costume based on their gender. Basic Hairstyle: Players receive a basic hairstyle and an additional cosmetic item. Basic Pet: Players receive a basic pet at a certain level. Basic Weapon: Players receive a basic weapon based on their class. Basic Mount: Players receive a basic mount at a certain level.
  4. Hi, I've made a quest for the apprentice quest and I thought I'd leave it here because from what I've seen there are some pretty ugly quests. I'm attaching a model for a chest. Continue with what you want. [Hidden Content] quest apprentice_chest begin state start begin when 50187.use with pc.get_level() >= 1 begin if pc.get_empty_inventory_count() <= 9 then syschat("Your inventory is too full to open the chest.") return end if pc.getqf("apprentice_chest_lv1") == 1 then syschat("You have already opened a chest on this account.") return end local items = {{50188, 1}, {71027, 5}, {71028, 5}, {71029, 5}, {71030, 5}, {71044, 5}, {71045, 5}, {27102, 200}, {27105, 200} } for _, item in ipairs(items) do pc.give_item2(item[1], item[2]) end pc.remove_item(50187, 1) pc.setqf("apprentice_chest_lv1", 1) end end end
  5. Download Alternative download links → Github Hi, This is all interface about SungmaHee Tower Like official servers and the first floor like Official Servers, In this files we have 2k or 3k LOC, maybe more, I'm tired and I don't have information about all floor's tower, but if you want complete this system, you can contact with some-developer and providing the information about the floors or modifying all floors like your concept for Metin2, you'll be a nice dungeon. I already did the hardest part. You can extract all visual part with the official patchs from this forum. This dungeon is Full C++, Python and SQL. PLEASE: Not more messages for my person if you don't want to pay money, I only sell systems "not personal systems" you need understand about my time, my help, code or resolve some problem, needs time. If you want something (not offlineshop, this world has different options with this system) you can contact me but, you will need understand that I will charge you. I'm a nice person (I think), but this world needs money and if I invest time in one project, I will cancel other projects and is overmoney.
  6. Hello Metin2 Dev Community. I'm here to present a function to check and return the monster rank. I'm creating my biolog that's why i had to create this function, i know there are other ways to do this but i decided to do it this way. I'm sharing in case someone needs. With best regards, Doose.
  7. Sharing a simple timer quest that send a notice_all when a preferred mob dies NOTE : This code will not work for really long respawn times NOTE : This code will not work for really long respawn times quest bosskillred begin state start begin when 2291.kill begin timer("spawn", 250) notice_all("Red Dragon has been killed") notice_all("Will be spawned again in 4 hours") end when spawn.timer begin notice_all("Red Dragon Spawned.") end end end Changing .kill with preferred mob Vnum & 250 is how many seconds to set the timer for the next notice. In case i scripted something wrong feel free to correct me
  8. Download [Hidden Content] Hello Metin2 Dev. I'm here to share 2 of my hunting missions. How do they work? The player receive a scroll with the mission details. It will give the player 2 options. After complete the mission, the player receives his reward. The second mission only appears after concluding the first one. It will show on mission panel how many left is there to kill. As always is a pleasure to work with this community. Any error or problem, text here or contact me in private. With best Regards, Doose.
  9. Hi This feature allows you to make an arena with a player from the target, not just from the NPC. The position of where they start the battle is also saved so that they return to the same place and not always next to Yu-Hwan. The code communicates quest/lua with client (python), and server (C++) with quest. GIF: [Hidden Content] Feature created by Camilo (caanmasu) [Hidden Content] This feature allows you to make an arena with a player from the target, not just from the NPC. The position of where they start the battle is also saved so that they return to the same place and not always next to Yu-Hwan. The code communicates quest/lua with Python, and server (C++) with quest. Clientside root: constinfo.py ENABLE_ARENA_MANAGER_TARGET = True if ENABLE_ARENA_MANAGER_TARGET: QUEST_arena_manager_INDEX = 0 QUEST_arena_manager_TARGET = 0 game.py 1: Above: self.serverCommander=stringCommander.Analyzer() Add: if constInfo.ENABLE_ARENA_MANAGER_TARGET: serverCommandList.update({"StartArenaManager" : self.QUEST_arena_manager}) serverCommandList.update({"GetTargetArenaManager" : self.QUEST_get_target_arena_manager}) 2: Add this functions: if constInfo.ENABLE_ARENA_MANAGER_TARGET: def QUEST_arena_manager(self, quest_index): constInfo.QUEST_arena_manager_INDEX = quest_index def QUEST_get_target_arena_manager(self): net.SendQuestInputStringPacket(str(constInfo.QUEST_arena_manager_TARGET)) uitarget.py 1: import event 2: Above: GRADE_NAME = { Add: if constInfo.ENABLE_ARENA_MANAGER_TARGET: BUTTON_NAME_LIST.append(localeInfo.TARGET_BUTTON_ARENA_MANAGER) 3: Above: self.buttonDict["VOTE_BLOCK_CHAT"].SetEvent(ui.__mem_func__(self.__OnVoteBlockChat)) Add: if constInfo.ENABLE_ARENA_MANAGER_TARGET: self.buttonDict[localeInfo.TARGET_BUTTON_ARENA_MANAGER].SAFE_SetEvent(self.__OnArena) 4: Above: if player.IsPartyMember(self.vid): Add: if constInfo.ENABLE_ARENA_MANAGER_TARGET: if player.GetStatus(player.LEVEL) >= constInfo.PVPMODE_PROTECTED_LEVEL: self.__ShowButton(localeInfo.TARGET_BUTTON_ARENA_MANAGER) 5: Add this function: if constInfo.ENABLE_ARENA_MANAGER_TARGET: def __OnArena(self): constInfo.QUEST_arena_manager_TARGET = chr.GetNameByVID(self.vid) event.QuestButtonClick(int(constInfo.QUEST_arena_manager_INDEX)) locale_game.txt TARGET_BUTTON_ARENA_MANAGER Arena If you do not have input ignore, then do it: constinfo.py Add anywhere: INPUT_IGNORE = 0 game.py 1: Find this function: def OpenQuestWindow(self, skin, idx): self.interface.OpenQuestWindow(skin, idx) Replace for: def OpenQuestWindow(self, skin, idx): if constInfo.INPUT_IGNORE == 1: return else: self.interface.OpenQuestWindow(skin, idx) 2: Below of: # PRIVATE_SHOP_PRICE_LIST "MyShopPriceList" : self.__PrivateShop_PriceList, # END_OF_PRIVATE_SHOP_PRICE_LIST Add: "quest_input_ignore" : self.questInputIgnore, Add this function: def questInputIgnore(self, var): constInfo.INPUT_IGNORE = int(var) interfacemondule.py Find: def OpenQuestWindow(self, skin, idx): wnds = () Replace for: def OpenQuestWindow(self, skin, idx): if constInfo.INPUT_IGNORE == 1: return else: wnds = () Quest: arena_manager.cpp 1: Add those when: when login begin cmdchat(string.format("StartArenaManager %s", q.currentquestindex())) end when logout begin -- if pc.get_map_index() == 112 then pc.delqf("saved_x") pc.delqf("saved_y") end end when button begin --clientside related cmdchat("quest_input_ignore 1") --Note: If you set my input_ignore then this is correct. If you already had your own input_ignore, put the correct command. local victim_name = input(cmdchat("GetTargetArenaManager")) cmdchat("quest_input_ignore 0") -- if game.get_event_flag("arena_close") > 0 then say(gameforge.arena_manager._30_say) --token 42 return end local useMinLevel = game.get_event_flag("arena_use_min_level") if useMinLevel == 0 then useMinLevel = 25 ; end if pc.get_level() < useMinLevel then say(string.format(gameforge.arena_manager._50_say, useMinLevel)) --token 44 return else local sname = victim_name local opp_vid = find_pc_by_name(sname) local old = pc.select(opp_vid , opp_vid) local opp_level = pc.level -- Save the position (opponent) from where the arena was started pc.setf("arena_manager", "saved_x", pc.x) --BUG FIXED: si no se especifica el nombre de la quest, se tomará la quest del id anterior (la quest que se compiló antes de ésta en el quest_list) pc.setf("arena_manager", "saved_y", pc.y) -- pc.select(old, old) if opp_level < useMinLevel then say(string.format(gameforge.arena_manager._110_say, useMinLevel)) --token 28 return end local a = arena.is_in_arena(opp_vid) if a == 0 then say_reward(string.format(gameforge.arena_manager._130_say, sname)) --token 30 return end chat(string.format(gameforge.arena_manager._85_say, pc.name, sname)) --token 48 local agree = confirm(opp_vid, string.format(gameforge.arena_manager._87_say, sname, pc.name), 30) --token 49 (text very long) if agree!= CONFIRM_OK then say(string.format(gameforge.arena_manager._150_say, sname)) --token 32 return end -- Save the position (current player) from where the arena was started pc.setqf("saved_x", pc.x) -- pc.setqf("saved_y", pc.y) -- local s = arena.start_duel(sname, 3) if s == 0 then say(gameforge.arena_manager._160_say) --token 33 elseif s == 2 then say(gameforge.arena_manager._170_say) --token 34 elseif s == 3 then say(gameforge.arena_manager._180_say) --token 35 end end end 2: Find: if table.getn(arena_observer) >= s then Add: -- Save the position (observer player) from where the arena was started pc.setqf("saved_x", pc.x) -- pc.setqf("saved_y", pc.y) -- Serverside arena.cpp 1. Find: LPCHARACTER playerB = GetPlayerB(); Add: quest::PC* pPC_A = quest::CQuestManager::instance().GetPC(playerA->GetPlayerID()); quest::PC* pPC_B = quest::CQuestManager::instance().GetPC(playerB->GetPlayerID()); int saved_A_x = pPC_A->GetFlag("arena_manager.saved_x"); int saved_A_y = pPC_A->GetFlag("arena_manager.saved_y"); int saved_B_x = pPC_B->GetFlag("arena_manager.saved_x"); int saved_B_y = pPC_B->GetFlag("arena_manager.saved_y"); 2: Find: playerA->WarpSet(ARENA_RETURN_POINT_X(playerA->GetEmpire()), ARENA_RETURN_POINT_Y(playerA->GetEmpire())); Replace for: if (saved_A_x != 0 && saved_A_y != 0) playerA->WarpSet(saved_A_x*100, saved_A_y*100); else playerA->WarpSet(ARENA_RETURN_POINT_X(playerA->GetEmpire()), ARENA_RETURN_POINT_Y(playerA->GetEmpire())); 3: Find: playerB->WarpSet(ARENA_RETURN_POINT_X(playerB->GetEmpire()), ARENA_RETURN_POINT_Y(playerB->GetEmpire())); Replace for: if (saved_B_x != 0 && saved_B_y != 0) playerB->WarpSet(saved_B_x*100, saved_B_y*100); else playerB->WarpSet(ARENA_RETURN_POINT_X(playerB->GetEmpire()), ARENA_RETURN_POINT_Y(playerB->GetEmpire())); 4: Find: LPCHARACTER pChar = CHARACTER_MANAGER::instance().FindByPID(iter->first); if (pChar != NULL) { pChar->WarpSet(ARENA_RETURN_POINT_X(pChar->GetEmpire()), ARENA_RETURN_POINT_Y(pChar->GetEmpire())); } Replace for: LPCHARACTER pChar = CHARACTER_MANAGER::instance().FindByPID(iter->first); if (pChar != NULL) { quest::PC* pPC = quest::CQuestManager::instance().GetPC(pChar->GetPlayerID()); int saved_x = pPC->GetFlag("arena_manager.saved_x"); int saved_y = pPC->GetFlag("arena_manager.saved_y"); if (saved_x != 0 && saved_y != 0) pChar->WarpSet(saved_x*100, saved_y*100); else pChar->WarpSet(ARENA_RETURN_POINT_X(pChar->GetEmpire()), ARENA_RETURN_POINT_Y(pChar->GetEmpire())); } Regards.
  10. Hello I have reworked the get function calls without parameters. I mean when you use pc.empire instead of pc.get_empire() This time I have extended to more features and added the quest features. Tested in Marty Sama. Explanation: To add more functions you must know which ones. Functions must return a value and must not receive any parameters. pc.get_empire() is very simple, it returns a number and no parameter is sent. Its "alias" would be pc.empire or whatever name best suits it. item.get_value() does not work. Its alias would be item.value but the server will not know which value it refers to, since it needs to specify the index of that value. This structure does not work if the function requires parameters. When you need to use q.getcurrentquestindex(), you just put q.index. Now it's simpler. Steps: questlib.lua. Remove this code: Remove: npc_index_table = { ['race'] = npc.getrace, ['empire'] = npc.get_empire, } pc_index_table = { ['weapon'] = pc.getweapon, ['level'] = pc.get_level, ['hp'] = pc.gethp, ['maxhp'] = pc.getmaxhp, ['sp'] = pc.getsp, ['maxsp'] = pc.getmaxsp, ['exp'] = pc.get_exp, ['nextexp'] = pc.get_next_exp, ['job'] = pc.get_job, ['money'] = pc.getmoney, ['gold'] = pc.getmoney, ['name'] = pc.getname, ['playtime'] = pc.getplaytime, ['leadership'] = pc.getleadership, ['empire'] = pc.getempire, ['skillgroup'] = pc.get_skill_group, ['x'] = pc.getx, ['y'] = pc.gety, ['local_x'] = pc.get_local_x, ['local_y'] = pc.get_local_y, } item_index_table = { ['vnum'] = item.get_vnum, ['name'] = item.get_name, ['size'] = item.get_size, ['count'] = item.get_count, ['type'] = item.get_type, ['sub_type'] = item.get_sub_type, ['refine_vnum'] = item.get_refine_vnum, ['level'] = item.get_level, } function npc_index(t,i) local npit = npc_index_table if npit[i] then return npit[i]() else return rawget(t,i) end end function pc_index(t,i) local pit = pc_index_table if pit[i] then return pit[i]() else return rawget(t,i) end end function item_index(t, i) local iit = item_index_table if iit[i] then return iit[i]() else return rawget(t, i) end end setmetatable(pc,{__index=pc_index}) setmetatable(npc,{__index=npc_index}) setmetatable(item,{__index=item_index}) Add this code: index_tables = { pc = { ['weapon'] = pc.getweapon, ['level'] = pc.get_level, ['hp'] = pc.gethp, ['maxhp'] = pc.getmaxhp, ['sp'] = pc.getsp, ['maxsp'] = pc.getmaxsp, ['exp'] = pc.get_exp, ['nextexp'] = pc.get_next_exp, ['job'] = pc.get_job, ['money'] = pc.getmoney, ['gold'] = pc.getmoney, ['name'] = pc.getname, ['playtime'] = pc.getplaytime, ['leadership'] = pc.getleadership, ['empire'] = pc.getempire, ['skillgroup'] = pc.get_skill_group, ['x'] = pc.getx, ['y'] = pc.gety, ['local_x'] = pc.get_local_x, ['local_y'] = pc.get_local_y, ['mapindex'] = pc.get_map_index, ['guild'] = pc.get_guild, ['sex'] = pc.get_sex, ['gmlevel'] = pc.get_gm_level, ['ip'] = pc.get_ip0, ['hwid'] = pc.get_hwid, }, npc = { ['race'] = npc.getrace, ['empire'] = npc.get_empire, ['vid'] = npc.get_vid, ['level'] = npc.get_level0, ['name'] = npc.get_name0, ['pid'] = npc.get_pid0, ['type'] = npc.get_type0, ['hwid'] = npc.get_hwid, }, item = { ['id'] = item.get_id, ['cell'] = item.get_cell, ['vnum'] = item.get_vnum, ['name'] = item.get_name, ['size'] = item.get_size, ['count'] = item.get_count, ['type'] = item.get_type, ['sub_type'] = item.get_sub_type, ['refine_vnum'] = item.get_refine_vnum, ['level'] = item.get_level, ['levellimit'] = item.get_level_limit, ['wearflag'] = item.get_wearflag0, ['antiflag'] = item.get_antiflag0, ['immuneflag'] = item.get_immuneflag0, }, q = { ['index'] = q.getcurrentquestindex, ['name'] = q.getcurrentquestname, } --add your new space } function generic_index(t, i) local it = index_tables[t] return it[i] and it[i]() or rawget(t, i) end setmetatable(pc, { __index = function(t, i) return generic_index("pc", i) end }) setmetatable(npc, { __index = function(t, i) return generic_index("npc", i) end }) setmetatable(item, { __index = function(t, i) return generic_index("item", i) end }) setmetatable(q, { __index = function(t, i) return generic_index("q", i) end }) --quest --add your new space
  11. Hello community, I created this quest to use on my PvP server and decided to share it. The anti-farm in relation to killing characters is by windows guid, you must add this check in the source. If you don't want to add it, you can replace it with the existing standard IP check function. [Hidden Content]
  12. Hello community, I created some time this method to reduce the use of bots on my server, this code was written for anti metins farm but can be adapted for monsters too. The code was made in a few hours and could be improved a lot more, but as it stands it works perfectly. Logic When the player destroys 30 metins, the captcha will appear and the player will be stunned at the same time. The player has only 3 attempts, if he fails all 3 times he is teleported to the city. The stun is only removed when the captcha is successfully completed. As I said, this is not a WOW solution, but it can help as it has helped me on my server. For those who really want to spend time creating a more effective solution, I recommend creating a captcha in c++ with encrypted connection client -> server. Don’t forget to update the values in the quest according to your item_proto. Lua [Hidden Content] C++ [Hidden Content] quest_functions[Hidden Content]
  13. (This topic addresses to the most common crashes that occur using server_timers in dungeons) In this short tutorial i'm gonna explain why are the crashes happening and how we can deal with them along with a use case of this. The difference between the Normal Timers and Server Timers Normal Timers: - Directly tied to a character pointer. - Timer execution halts if the associated character logs out. - Implicitly dependent on the character's in-game presence. Server Timers: - Operate independently of character pointers. - Continue to execute even if the initiating character exits the game. - Offers persistent timing functionalities. You might ponder on the need for server timers, especially when normal timers are present. Here's why: 1. Party Dynamics: In a multiplayer setting, if a party member possessing the timer logs out, gameplay can be disrupted, leading to a halted dungeon instance. Server timers mitigate this risk. 2. Dungeon Persistence: Players often exit and re-enter dungeons. With a normal timer, exiting erases the timer, jeopardizing the dungeon's progression. Server timers ensure continuity. Why do crashes occur? Server timers, by virtue of their independence, lack character pointers upon invocation. This absence becomes problematic when: Distributing or dropping items. Executing specific chats or commands. Running functions reliant on character pointers. The game struggles to reference a non-existent character, which can either lead to functional anomalies or outright crashes. How can we solve the problem? My way of solving this issue is simple. I've created a global function that when called is selecting the character pointer of the specified PID and returns a boolean of whether it selected it or not. // questlua_global.cpp int _select_pid(lua_State* L) { DWORD dwPID = static_cast<DWORD>(lua_tonumber(L, 1)); quest::PC* pPC = CQuestManager::instance().GetPC(dwPID); if(pPC) { LPCHARACTER lpSelectedChar = CQuestManager::instance().GetCurrentCharacterPtr(); lua_pushboolean(L, (lpSelectedChar ? 1 : 0)); return 1; } lua_pushboolean(L, false); return 1; } { "select_pid", _select_pid }, Now we set the leaderPID as a dungeon flag upon entering the instance for the first time. (if it's not a group, it will set the pid of the player that entered) when login with <PC_IN_DUNGEON> begin if (((party.is_party() and party.is_leader()) or not party.is_party()) and d.getf("leaderPID") < 1) then d.setf("leaderPID", pc.get_player_id()) end end Now when we call the server_timer, we first select the leader and check if we succeeded or not. when give_item.server_timer begin if (d.select(get_server_timer_arg())) then if (select_pid(d.getf("leaderPID"))) then pc.give_item2(19, 1) else -- handle what happends if the selection was unsuccessful end end end That's basically it. You can now call any function in server_timer. How can this get very useful? Let's say we want to create an update timer that constantly updates different information on players client. I've made a function that is pushing each dungeon's member PID. (only if it's in dungeon) #include <functional> struct FDungeonPIDCollector { std::vector<DWORD> vecPIDs; void operator () (LPCHARACTER ch) { if (ch) vecPIDs.push_back(ch->GetPlayerID()); } }; int dungeon_get_member_pids(lua_State* L) { LPDUNGEON pDungeon = CQuestManager::instance().GetCurrentDungeon(); if (!pDungeon) return 0; FDungeonPIDCollector collector; pDungeon->ForEachMember(std::ref(collector)); for (const auto& pid : collector.vecPIDs) { lua_pushnumber(L, pid); } return collector.vecPIDs.size(); } { "get_member_pids", dungeon_get_member_pids }, Using this, we can just update each dungeon's member informations: when dungeon_update_info.server_timer begin if (d.select(get_server_timer_arg())) then local dungeonMemberIds = {d.get_member_pids()}; for index, value in ipairs(dungeonMemberIds) do if (select_pid(value)) then cmdchat(string.format("UpdateDungeonInformation %d %d", 1, 2)) -- pc.update_dungeon_info() else -- handle the negative outcome end end end end
  14. With these modifications, you will enable colors in notice board, like this: Image: Tutorial: Open uitip.py and add this to the start of the file: import re Now find class TipBoard(ui.Bar):, and replace it with this: class TipBoard(ui.Bar): SCROLL_WAIT_TIME = 3.0 TIP_DURATION = 5.0 STEP_HEIGHT = 17 def __init__(self): ui.Bar.__init__(self) self.AddFlag("not_pick") self.tipList = [] self.curPos = 0 self.dstPos = 0 self.nextScrollTime = 0 self.width = 370 self.SetPosition(0, 70) self.SetSize(370, 20) self.SetColor(grp.GenerateColor(0.0, 0.0, 0.0, 0.5)) self.SetWindowHorizontalAlignCenter() self.__CreateTextBar() def __del__(self): ui.Bar.__del__(self) def __CreateTextBar(self): x, y = self.GetGlobalPosition() self.textBar = TextBar(370, 300) self.textBar.SetParent(self) self.textBar.SetPosition(3, 5) self.textBar.SetClipRect(0, y, wndMgr.GetScreenWidth(), y+18) self.textBar.Show() def __CleanOldTip(self): leaveList = [] for tip in self.tipList: madeTime = tip[0] if app.GetTime() - madeTime > self.TIP_DURATION: pass else: leaveList.append(tip) self.tipList = leaveList if not leaveList: self.textBar.ClearBar() self.Hide() return self.__RefreshBoard() def __RefreshBoard(self): self.textBar.ClearBar() index = 0 for tip in self.tipList: text = tip[1] rgb = tip[2] if rgb != (0,0,0): self.textBar.SetTextColor(rgb[0],rgb[1],rgb[2]) self.textBar.TextOut(0, index*self.STEP_HEIGHT, text) self.textBar.SetTextColor(255,255,255) index += 1 def SetTip(self, text): if not app.IsVisibleNotice(): return rgb = (0,0,0) mat = re.search("\|cFF([a-zA-Z0-9]+)\|h", text) if mat and mat.group(1): hexd = mat.group(1) rgb = tuple(int(hexd[i:i+2], 16) for i in (0, 2, 4)) curTime = app.GetTime() self.tipList.append((curTime, text, rgb)) self.__RefreshBoard() self.nextScrollTime = app.GetTime() + 1.0 if not self.IsShow(): self.curPos = -self.STEP_HEIGHT self.dstPos = -self.STEP_HEIGHT self.textBar.SetPosition(3, 5 - self.curPos) self.Show() def OnUpdate(self): if not self.tipList: self.Hide() return if app.GetTime() > self.nextScrollTime: self.nextScrollTime = app.GetTime() + self.SCROLL_WAIT_TIME self.dstPos = self.curPos + self.STEP_HEIGHT if self.dstPos > self.curPos: self.curPos += 1 self.textBar.SetPosition(3, 5 - self.curPos) if self.curPos > len(self.tipList)*self.STEP_HEIGHT: self.curPos = -self.STEP_HEIGHT self.dstPos = -self.STEP_HEIGHT self.__CleanOldTip() Example of use: --- in quests: notice_all("|cFF29BFBF|hColored text Example#1") notice("|cFFFF0000|hColored text Example #2") --- ingame: "n |cFF29BFBF|h Hello World" That's all
  15. Doose

    Tournament PvP

    Download Alternative download links → Mega Hello Metin2 Dev. I'm here to present a PvP Tournament. How does it work? Each player receives a medalion once he/she enters tournament map. (You can decide which map to use.) When a player kills inside the map, he receives a point which is saved in player.player database. You are able to upgrade your medalion to bigger versions, once you reach the "last" medalion, you're able to "absorve" it so you receive some bonuses. Player can't use auto-attack while inside tournament pvp map. Player can't use certain items such as sun elixir or moon elixir inside tournament map. You can see how many points you have if you check the medalion. DISCLAIMER. This system is more usefull for PvP servers, but you can convert it into PvM too. In case of any problem, report here and i'll fix it.
  16. Download GitHub Repository Temporarily boosts the character's physical attack and / or skill damage. You can learn more about the system from the official wiki page. Thanks to @blackdragonx61 for the reversed effect position.
  17. Hey folks, Well, I won't go in details, but the official use this for some quests, including their over9refine. I noticed we missed that, it was added somewhere between 2014 and 2015, probably along the new armors. I am aware that the tooltip is a bit crazy, but it is like this on the official too so I share it as is. NOTE: This is not reversed. Only the root part is official. Have a nice day
  18. Download Alternative download links → GitHub I couldn't find any of the events on the forum to avoid a double post so I decided to post it anyway in case it could be of use to someone. I will also update this topic with new events as needed and if requested. Quest Events / Triggers Fishing Mining Selling Buying Crafting Emoting See example below of how to use them. Usage Example
  19. Download Alternative download links → Github I like Aeldra's Nilay Dungeon so I made it. Allows you to hit a metinstone up to 50% health and start a stage within the stage. The logic is simple, do this to bosses etc. You can also adapt it. I added an example dungeon quest usage, you can easily understand the logic.
  20. Hi guys. This is my goodbye, I had 10 years working for Metin2 but in the last days I finished my all contracts with Metin2. New year, new projects. I hope you continue to improve your codes. I give special thanks for my unic friend on Metin2 "Capone" because he was the one who showed me that true friendship exists. I know that Metin2 has nice people "Community Developers" and Big Developers: VegaS and Mali, you don't know about me, but I observed your works and is very cool. I wish you a beautiful new Year and good luck in your future projects. I leave with my last contribution "GIFT" for the community. Basic Dungeons: [C++] Nemere and Flame Dungeon like Official servers: Dungeons with Conqueror of Yohara stats: [C++] White Dragon [C++] Queen Nethis NOTE: If I forgot visuals parts, you can extract from the official client. Maybe I will have connections in the forum but just of curiosity. I love you guys, I hope you are very well with my gifts. Good bye.
  21. [Hidden Content] [Hidden Content] Reversed from 22.2.7.0 Official Binary. + Football Metin Stone From 22.5.7.0: Functions were in different modules, I gathered them on a single module. Do not forget to get proto and other files for metin stones from official packs.
  22. Download By Aslan Display of the stage as a green progress bar and text (here E2 e.g.) An info text what you have to do as a slider Progress bar can be used for remaining monster count or remaining time Example (For the rest please see the quest example) -- If you set a mob counter, do it in front of it. If you ask why the second dungeon flag is set, -- ihow counts "d.count_monster()" weird if you always print it out when mobs are killed. d.setf("minimap_dungeoninfo_start_mob_count", d.count_monster()) d.setf("minimap_dungeoninfo_killed_mob_count", d.count_monster()) d.set_minimap_dungeoninfo_status(1) -- enable dungeon info for dungeon instance d.set_minimap_dungeoninfo_stage(aktuelle stage, max stage) -- I always set maxstage higher than you have stages so that stage complete is displayed after the bosskill, so to speak -- the progress bar can be controlled as follows (3 options): d.set_minimap_dungeoninfo_gauge(0, 0) -- Disables the counter completely d.set_minimap_dungeoninfo_gauge(1, (un)gekillte mobanzahl, gesamtmobanzahl) -- activates the mob counter d.set_minimap_dungeoninfo_gauge(2, zeit in sekunden) -- activates the time counter d.set_minimap_dungeoninfo_notice("Töte alle Monster") -- Insert your info text here. Text output is also possible for multilang via the Translate.lua.
  23. Download By Aslan The World Lottery System is comparable to the real lottery in the real world. However, in order to make the jackpot a little easier to win, you don't play 6out of 49 but 4out of 30. So the chance of winning is 1:27,405. But of course you also win with 1 identical, 2 identical and 3 identical numbers. The winning amount can be determined as a percentage of the jackpot and additionally by a cap. Features If you buy a lottery ticket, x% of the purchase price is added to the lottery pot and is active in the next draw. With each new draw, all lottery tickets for the corresponding draw are evaluated and, if you win, the winnings are spilled. Players' winning totals are deducted from the jackpot for the next draw. If a player has won the jackpot or if the value of the jackpot is below the sum X due to the payouts, the jackpot starts at the specified sum from the configs. If a player has won with his lottery ticket, the "Collect prize" button must be pressed. The prize then ends up in Yangspeicher and from there it can be collected in the correct inventory. Miscellaneous In addition to the basic system, there are 3 more windows: Information board The last X lottery draws Lotto ranking list Options_lottery.h #define ENABLE_ASLAN_LOTTERY #ifdef ENABLE_ASLAN_LOTTERY // General Options #define NEW_LOTTERY_NUMBERS_ACTIVATE 1 // New draws take place #define LOTTO_TICKET_COST 5000000 // How much a new ticket costs #define LOTTO_PUT_TICKET_COST_PERC_TO_JACKPOT 75 // What percentage of the ticket cost goes into the next jackpot // Percent per same number (1 = 0,01% || 10 = 0,1% || 100 = 1% > 5% from 100.000.000 = 5.000.000) #define PERC_REWARD_FOR_1_SAME_NUMBER 3 // Percent reward of Jackpot for 1 same number #define MAX_REWARD_FOR_1_SAME_NUMBER 5000000 // Max reward of Jackpot for 1 same number ( Deactivate when is 0 ) #define PERC_REWARD_FOR_2_SAME_NUMBER 10 // Percent reward of Jackpot for 2 same number #define MAX_REWARD_FOR_2_SAME_NUMBER 0 // Max reward of Jackpot for 2 same number ( Deactivate when is 0 ) #define PERC_REWARD_FOR_3_SAME_NUMBER 100 // Percent reward of Jackpot for 3 same number #define MAX_REWARD_FOR_3_SAME_NUMBER 0 // Max reward of Jackpot for 3 same number ( Deactivate when is 0 ) //#define TEST_MODE #ifdef TEST_MODE #define LOTTO_NUMBER_1 1 #define LOTTO_NUMBER_2 1 #define LOTTO_NUMBER_3 1 #define LOTTO_NUMBER_4 1 #define GEN_NEXT_NUMBER_TIME_SEC 60 #endif // TEST_MODE #define MIN_JACKPOT_MONEY 250000000 #define GENERATE_NEW_LOTTO_NUMBERS_PULSE_MIN 2 // The intervals at which new lottery numbers are generated #endif
  24. This vulnerability should affect every server. You can duplicate item rewards, and also crash the server through dangling pointers. The danger of this bug escalates to how many custom systems, and how many crafting quests (for example, the vitality ore quest, not the cube system) you have in your server. How to trigger it: Any quest that uses select & wait, and the item lua module after that is vulnerable. After the server uses select() or wait(), the player's quest state is suspended. After the player replies using the CG packet, the quest state is recovered. So what's wrong with it? It doesn't verify if the stored quest item ptr expired. You basically need to destroy the selected item ptr in order to dupe the rewards of the quest. After some tries, you may get a core crash in the game. (dangling pointers often cause crashes only after that memory sector has been rewritten) In my files, I've checked (since several years ago) if the quest state was suspended for the the default windows such as exchange, cube, shop. This bug can work very easily on offline shops or other new systems that don't check that. After the select() or wait() is called, you send the selected item to the (e.g.) offlineshop system window. It will delete the item ptr in the game. Now, you can press "Ok" on the quest, and the quest will proceed as if the item still existed. The item still exists in the offlineshop, but not the item ptr anymore. The item won't be deleted by the quest even after item.remove() is called. This is the fix: diff --git a/s3ll_server/Srcs/Server/game/src/char.cpp b/s3ll_server/Srcs/Server/game/src/char.cpp index 0ea307fa..65b1dd65 100644 --- a/s3ll_server/Srcs/Server/game/src/char.cpp +++ b/s3ll_server/Srcs/Server/game/src/char.cpp @@ -303,7 +303,10 @@ void CHARACTER::Initialize() m_dwQuestNPCVID = 0; m_dwQuestByVnum = 0; - m_pQuestItem = NULL; + m_dwQuestItemVID = 0; m_dwUnderGuildWarInfoMessageTime = get_dword_time()-60000; @@ -6123,33 +6126,37 @@ LPCHARACTER CHARACTER::GetQuestNPC() const void CHARACTER::SetQuestItemPtr(LPITEM item) { - m_pQuestItem = item; + m_dwQuestItemVID = (item) ? item->GetVID() : 0; } void CHARACTER::ClearQuestItemPtr() { - m_pQuestItem = NULL; + m_dwQuestItemVID = 0; } LPITEM CHARACTER::GetQuestItemPtr() const { - return m_pQuestItem; + if (!m_dwQuestItemVID) + return nullptr; + return ITEM_MANAGER::Instance().FindByVID(m_dwQuestItemVID); } diff --git a/s3ll_server/Srcs/Server/game/src/char.h b/s3ll_server/Srcs/Server/game/src/char.h index cc4da2bb..74b3470e 100644 --- a/s3ll_server/Srcs/Server/game/src/char.h +++ b/s3ll_server/Srcs/Server/game/src/char.h @@ -1674,9 +1674,9 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider private: DWORD m_dwQuestNPCVID; DWORD m_dwQuestByVnum; - LPITEM m_pQuestItem; + DWORD m_dwQuestItemVID{}; // @fixme304 (LPITEM -> DWORD) // Events To unlock the client in order to move your windows with a quest open, you edit this from interfacemodule.py: You can even Hide the TopBar and BottomBar from uiQuest.py for a better result. Important: after this fix, the item ptr may be nullptr after they press enter, so you need to check if the item ptr is still valid by using this function: ALUA(item_is_available) { auto item = CQuestManager::instance().GetCurrentItem(); lua_pushboolean(L, item != nullptr); return 1; } ... { "is_available", item_is_available }, // [return lua boolean] By simply doing: when 169.take begin local s1=select("Yes", "No") if s1==2 then return end if not item.is_available() then return end end If you want to tryhard, and be sure that the item ptr didn't get swapped, you can do as following via quest:
×
×
  • 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.