-
Posts
1295 -
Joined
-
Last visited
-
Days Won
38 -
Feedback
100%
Content Type
Forums
Store
Third Party - Providers Directory
Feature Plan
Release Notes
Docs
Events
Posts posted by Syreldar
-
-
Metin2Dev's community doesn't offer support to leechers.
-
Insert a .quest file anywhere within the /quest folder.
From there you may compile it singularly via terminal (./qc PATH/QUESTNAME.EXT), or you may batch-compile all of them by inserting them into the quest_list and using the make file.
In order for a quest to work you may want to restart the server, however if it's not a new quest and you just made changes to it, you may also use the in-game command "/reload q" without running into issues.
-
special_item_group.txt
Group 스폐셜 { Vnum 10050 -- type special -- 1 71148 100000 1 --Ring of Will Power 2 71149 100001 1 --Ring of Deadly Power 3 71158 100002 1 --Hero's Medal 4 71135 100003 1 --Crescent Moon Ring 5 71136 100004 1 --Mighty Lolly 6 71143 100005 1 --Ring of Joy 7 71145 100006 1 --Amulet of Eternal Love 8 71188 100007 1 --Magic Lolly 9 71199 100008 1 --Chocolate Amulet 10 71202 100009 1 --Nazar Amulet 11 72054 100010 1 --Amulet of the Guardians 12 76030 100011 1 --Power Lolly 13 76047 100011 1 --Power Lolly (3D) 14 72062 100012 1 --Experience Ring +200 15 72062 100012 1 --Experience Ring +200 16 72703 100015 1 --Tiger Bone Earring 17 72709 100015 1 --Tiger Bone Earring 18 72704 100016 1 --Dragon Bone Earring 19 72710 100016 1 --Dragon Bone Earring 20 72705 100017 1 --Tiger Bone Bracelet 21 72711 100017 1 --Tiger Bone Bracelet 22 72706 100018 1 --Dragon Bone Bracelet 23 72712 100018 1 --Dragon Bone Bracelet }
-> 3 71158 100002 1 --Hero's Medal
Group 속성3 { Vnum 100002 -- type ATTR -- 1 66 50 -- 2 64 30 -- 3 7 20 -- 4 9 20 -- 5 69 10 -- 6 70 10 -- effect d:\ymirwork\effect\etc\buff\buff_symbol1.mse -- }
- 1
-
I stumbled upon a customer that had this issue today. I was not aware that you posted a solution for it almost a year ago.
This is the full Lua workaround I used for the guy, posting it here just for the sake of doing it.
From my functions.lua:
Spoiler--[[ Description: Returns an array of ASCII values representing each character in the 'str' string. Arguments: str (string): The string to be converted into an array of ASCII values. Example: string_to_ascii_array("hello") -- Returns {104, 101, 108, 108, 111} Returns: table: An array of ASCII values. Time complexity: O(n), where n is the length of the string. Space complexity: O(n), where n is the length of the string. ]] string_to_ascii_array = function(str) local array = {}; local str_len = string.len(str); for i = 1, str_len do array[i] = string.byte(str, i); end -- for return array; end -- function --[[ Description: Returns a string converted from an array of ASCII values. Arguments: array (table): The array of ASCII values to be converted into a string. Example: ascii_array_to_string({104, 101, 108, 108, 111}) -- Returns "hello" Returns: string: The string representation of the ASCII values. Time complexity: O(n), where n is the length of the array. Space complexity: O(n), where n is the length of the array. ]] ascii_array_to_string = function(array) local str = ""; local array_len = table_get_count(array); for i = 1, array_len do str = str .. string.char(array[i]); end -- for return str; end -- function
New functions:
Spoilerset_dungeon_leader_name = function(name) local array = string_to_ascii_array(name); for index, value in ipairs(array) do d.setf(string.format("leader_name%d", index), value); end -- for end -- function get_dungeon_leader_name = function() local table_ex, i = {}, 1; local flag = d.getf(string.format("leader_name%d", i)); while (flag and flag ~= 0) do table_ex[table.getn(table_ex) + 1] = flag; i = i + 1; flag = d.getf(string.format("leader_name%d", i)); end -- while return ascii_array_to_string(table_ex); end -- function
Usage example:
Spoilerwhen login with Dungeon.InDungeon() begin pc.set_warp_location(--[[outside]]); -- Initialize the dungeon if (d.getf("initialized") == 0) then d.setf("initialized", 1); d.setf("start_time", get_time()); d.regen_file(string.format("%s/dungeon_regen.txt", --[[regen_path]])); server_loop_timer("dungeon_monster_count", 2, d.get_map_index()); end -- if -- Here we set the name of the leader. if (party.is_leader() or not party.is_party()) then d.setf("is_party_dungeon", party.is_leader() and 1 or 0); set_dungeon_leader_name(pc.get_name()); end -- if notice("<Dungeon> Kill all the monsters to complete the dungeon.") end -- when when dungeon_monster_count.server_timer begin local instance_index = get_server_timer_arg(); if (not d.select(instance_index)) then return; end -- if local data = Dungeon.GetData(); local count_monster = d.count_monster(); if (count_monster > 0) then --d.notice(string.format("Remaining monsters: %d.", count_monster)); return; end -- if clear_server_timer("dungeon_monster_count", instance_index); -- Here we get the name we previously memorized for the instance. local leader_name = get_dungeon_leader_name(); notice_all(string.format("[Dungeon] %s has completed `Dungeon Name`.", d.getf("is_party_dungeon") == 1 and string.format("%s's party", leader_name) or leader_name)) end -- when
You could also do this upon d.new_jump* call instead of onLogin.
- 4
-
Contact me on discord.
-
58 minutes ago, Juki said:
quest collect_quest_lv30 begin state start begin when login or levelup with pc.level >= 30 begin set_state(information) end end state information begin when letter begin local v = find_npc_by_vnum(20084) if v != 0 then target.vid("__TARGET__", v, gameforge.collect_herb_lv10._150_sayTitle) end send_letter(gameforge.collect_quest_lv30._10_sendLetter) end when button or info begin say_title(gameforge.collect_quest_lv30._10_sendLetter) say(gameforge.collect_quest_lv30._20_say) end when __TARGET__.target.click or 20084.chat.gameforge.collect_quest_lv30._30_npcChat begin target.delete("__TARGET__") say_title(gameforge.collect_herb_lv10._50_sayTitle) say(gameforge.collect_quest_lv30._40_say) wait() say_title(gameforge.collect_herb_lv10._50_sayTitle) say(gameforge.collect_quest_lv30._50_say) wait() say_title(gameforge.collect_herb_lv10._150_sayTitle) say(gameforge.collect_quest_lv30._60_say) set_state(go_to_disciple) pc.setqf("collect_count",0) pc.setqf("drink_drug",0) end end state go_to_disciple begin when letter begin send_letter(gameforge.collect_quest_lv30._70_sendLetter) end when button or info begin say_title(gameforge.collect_quest_lv30._80_sayTitle) say(gameforge.collect_quest_lv30._90_say) say_item_vnum(30006) say_reward(string.format(gameforge.collect_quest_lv30._100_sayReward, pc.getqf("collect_count"))) end when 71035.use begin if pc.getqf("drink_drug")==1 then say(gameforge.collect_quest_lv30._120_say) return end if pc.count_item(30006)==0 then say_title(gameforge.collect_herb_lv10._50_sayTitle) say(gameforge.collect_quest_lv30._130_say) return end pc.remove_item(71035, 1) pc.setqf("drink_drug",1) end when 601.kill begin local s = number(1, 100) if s <= 5 then pc.give_item2(30006, 1) end end when 20084.chat.gameforge.collect_quest_lv30._140_npcChat with pc.count_item(30006) >0 begin if pc.count_item(30006) >0 then say_title(gameforge.collect_herb_lv10._150_sayTitle) say(gameforge.collect_quest_lv30._150_say) pc.remove_item("30006", 1) wait() local pass_percent if pc.getqf("drink_drug")==0 then pass_percent=60 else if true == pet.is_summon(34003) or true == pet.is_summon(34001) then pass_percent=92 else pass_percent=90 end end notice(pass_percent) local s= number(1,100) if s <= pass_percent then if pc.getqf("collect_count")< 9 then local index =pc.getqf("collect_count")+1 pc.setqf("collect_count",index) say_title(gameforge.collect_herb_lv10._150_sayTitle) say(string.format(gameforge.collect_quest_lv30._160_say, 10-pc.getqf("collect_count"))) pc.setqf("drink_drug",0) return end say_title(gameforge.collect_herb_lv10._50_sayTitle) say(gameforge.collect_quest_lv30._170_say) pc.setqf("collect_count",0) pc.setqf("drink_drug",0) set_state(key_item) return else say_title(gameforge.collect_herb_lv10._50_sayTitle) say(gameforge.collect_quest_lv30._180_say) pc.setqf("drink_drug",0) return end else say_title(gameforge.collect_herb_lv10._50_sayTitle) --say(string.format(gameforge.collect_quest_lv30._190_say, item_name(30006))) say(gameforge.collect_quest_lv30._190_say) return end else say_title(gameforge.collect_herb_lv10._50_sayTitle) say(gameforge.collect_quest_lv30._200_say) return end end end state key_item begin when letter begin send_letter(gameforge.collect_quest_lv30._210_sendLetter) if pc.count_item(30220)>0 then local v = find_npc_by_vnum(20084) if v != 0 then target.vid("__TARGET__", v, gameforge.collect_herb_lv10._150_sayTitle) end end end when button or info begin if pc.count_item(30220) >0 then say_title(gameforge.collect_quest_lv30._220_sayTitle) say_reward(gameforge.collect_quest_lv30._230_sayReward) return end say_title(gameforge.collect_quest_lv30._220_sayTitle) say(gameforge.collect_quest_lv30._240_say) say_item_vnum(30220) say(gameforge.collect_quest_lv30._250_say) end when 631.kill or 632.kill or 633.kill or 634.kill or 635.kill or 636.kill or 637.kill begin local s = number(1, 500) if s == 1 and pc.count_item(30220)==0 then pc.give_item2(30220, 1) send_letter(gameforge.collect_quest_lv30._260_sendLetter) end end when __TARGET__.target.click or 20084.chat.gameforge.collect_quest_lv30._270_npcChat with pc.count_item(30220) > 0 begin target.delete("__TARGET__") if pc.count_item(30220) > 0 then say_title(gameforge.collect_herb_lv10._150_sayTitle) say(gameforge.collect_quest_lv30._280_say) pc.remove_item(30220,1) set_state(__reward) else say_title(gameforge.collect_herb_lv10._150_sayTitle) --say(string.format(gameforge.collect_quest_lv30._290_say, item_name(30220))) say(gameforge.collect_quest_lv30._290_say) return end end end state __reward begin when letter begin send_letter(gameforge.collect_quest_lv30._300_sendLetter) local v = find_npc_by_vnum(20018) if v != 0 then target.vid("__TARGET__", v, gameforge.collect_quest_lv30._310_targetVid) end end when button or info begin say_title(gameforge.collect_quest_lv30._300_sendLetter) say(gameforge.collect_quest_lv30._320_say) end when __TARGET__.target.click or 20018.chat.gameforge.collect_quest_lv30._330_npcChat begin target.delete("__TARGET__") say_title(gameforge.collect_quest_lv30._340_sayTitle) say(gameforge.collect_quest_lv30._350_say) say_reward(gameforge.collect_quest_lv30._360_sayReward) affect.add_collect(apply.MOV_SPEED, 10, 60*60*24*365*60) --60Jahre pc.give_item2(50109) clear_letter() set_quest_state("collect_quest_lv40", "run") set_state(__complete) end end state __giveup__ begin when 20084.chat.gameforge.collect_quest_lv30._370_npcChat begin say_title(gameforge.collect_herb_lv10._50_sayTitle) say(gameforge.collect_quest_lv30._380_say) set_state(start) end end state __complete begin end end
so this is what i did but it doesn't seem to work...
You either aren't compiling your stuff properly or forgot to reset the flag on your character.
- 1
-
pc.setqf("duration", ..)
Remove this line.
- 1
-
Never bought a plane ticket faster.
- 3
- 1
- 2
-
when kill with not npc.is_pc() and math.random(100) < X begin game.drop_item_with_ownership(vnum, quantity); end -- when
- 1
- 1
-
send_letter_ex("Wow, this quest blinks!", "blink,ex", "scroll_open.tga");
-
On 2/23/2024 at 7:34 PM, Rakancito said:
WTF??? YOU ARE WRONG!!
- All languages has advantages and disadvantages.
- C++ is faster than Lua.
- Lua is easier than C++.
- C++ has more good communication with the machine than Lua.
- C++ has debug tools.
- Lua is nice and good, but if you have bugs, you will need go to C++ if you want resolve the problem.
- Memory problems are programming errors, not programming language errors, and are due to poor programming practice or lack of code review.
- The order of the code does not equal the difficulty of understanding code (C++ is harder than Lua).
- You can cause important errors in Lua and memory problems just like in C++
And I can continue with disadvantages or advantages.
If you have more Lua developers it's because Lua is more easier than C++, Lua has one syntax more easy. But if you have good practices and concepts of programming, you will have a good code and secure code.
In my personal think, C++ has advantages because it's more fast and has one more communication with the machine than Lua, but the project will be more expensive and complicated.It is simply to the client's liking. But C++ will always be better than Lua in many ways. Only Lua is easier to learn and anyone can be a Lua developer.
Lua has nice helps for inexpert developers and good practices for codes with small size however C++ has advantages with big codes.
- If you have a big new bug in Lua you go to C++.
- C++ has great tools with debug for codes. If you use a good testing techniques you will have a super-tool.
In summary: Lua and C++ are good, but C++ is harder to learn. For this reason, although C++ is better for big code, because it's more faster and more advantages, it is avoided due to the cost it represents. Not everyone is a C++ developer.
"Lua is nice and good, but if you have bugs, you might need C++ to resolve the problem"
It's true that certain complex bugs might require C++ work (They're so rare that the only example that comes to my mind over 10 years of development is this:)
But you just cannot use that as a matter of fact when such an occurrence is that rare. I literally cannot think of any other issue that can't be solved directly without C++ work.
"You can cause important errors in Lua and memory problems just like in C++"
No. Unless you override entire classes like pc.* by assigning them to global variables and giving them arbitrary values, that's simply not possible. And such a gigantic mistake that can STILL be fixed in the blink of an eye, you can't say the same about C++ core crashes/memory issues. We both know that solving a generic Lua error (which still lets the game run with no issues) is much simpler than solving a core crash caused by shit C++ code made by people like you who force themselves to use it when they can't just cause "C++ is cooler / harder to use".
"C++ is faster than Lua"
Yes, but that doesn't matter on Metin2, due to the game's limitations, speed is not a factor: Two dungeons written in these two languages with identical code functionality will run exactly the same way and at exactly the same pace. Thus, you're wrong, you can't apply generic logic in this case.
Also, while C++ is known for its speed, which again, is not a factor to take into account on Metin2, Lua offers remarkable flexibility, and C++ should be used to that end: To help assemble functions useable in Lua in order to write more complex stuff while maintaining Lua's ease to update/use; that's the whole point of scripting languages and it's the reason why YMIR adopted this design, why do you think they don't make dungeon in C++? The entire quest system has been made exactly to handle such things, and it's very much stable and safe to use, not to mention easy to use, as you said, but apparently for you "easy to use = bad" (?).
Lua's straightforward syntax and semantics allow developers to implement features quickly and with fewer lines of code -> faster development cycles -> more productivity.
Writing something in a harder way doesn't make it better at all. I don't know you but given the message I quoted, this doesn't seem a design choice/preference on your end to me, not as much as it seems a sorry attempt to flex and feel superior.. which you failed at miserably from what I see.
I've coded much more complex stuff than this in Lua for Metin2 during my lifetime, stuff that I'm sure you can't even fathom, (For example, do you even know what a coroutine is and how to apply it on a Metin2 quest?)
While this.. stuff that you made, once I remove the python UIs I get one of the most basic, boring and plain dungeons ever conceived.
Be humble, and try to learn rather than trying to put other people down when you can't even defend your own competences properly.
"Only Lua is easier to learn and anyone can be a Lua developer."
True, it's easier to learn. You might want to start learning it, given your show of C++ knowledge today.
- 2
- 2
- 2
-
define WEAPON_SHOP_DEALER 9001 define ARMOUR_SHOP_DEALER 9002 define TINCTURE_VNUM 71047 define SOCKETS_MAX_NUM 3 quest tincture_stone_add begin state start begin function GetItemSlotsNum() -- end -- function function BuildStoneListForItemType(item_type) -- end -- function function GetStoneFamily(stone_vnum) -- end -- function function IsStoneFamilySocketed(stone_vnum) -- end -- function function BuildAvailableStoneListForItemType(item_type) -- end -- function when WEAPON_SHOP_DEALER.take or ARMOUR_SHOP_DEALER.take begin local npc_vnum = npc.get_race(); say_title(string.format("%s:[ENTER]", mob_name(npc_vnum))) local item_type = item.get_type(); if (npc_vnum == WEAPON_SHOP_DEALER and item_type ~= ITEM_TYPES.WEAPON) then say_reward("I only accept weapons.[ENTER]") return; elseif (npc_vnum == ARMOUR_SHOP_DEALER and item_type ~= ITEM_TYPES.ARMOR) then say_reward("I only accept armors.[ENTER]") return; end -- if/elseif local item_sub_type = item.get_sub_type(); local invalid_weapon_subtypes = { ITEM_SUB_TYPES.WEAPONS.ARROW, ITEM_SUB_TYPES.WEAPONS.MOUNT_SPEAR, ITEM_SUB_TYPES.WEAPONS.QUIVER }; if (npc_vnum == WEAPON_SHOP_DEALER and table_is_in(invalid_weapon_subtypes, item_sub_type)) then say_reward("I only accept valid weapons.[ENTER]") return; elseif (npc_vnum == ARMOUR_SHOP_DEALER and item_sub_type ~= ITEM_SUB_TYPES.ARMORS.BODY) then say_reward("I only accept valid armors.[ENTER]") return; end -- if/elseif if (pc.count_item(TINCTURE_VNUM) == 0) then say_reward("You require:") say_item_vnum(TINCTURE_VNUM); return; end -- if local slots_num = tincture_stone_add.GetItemSlotsNum(); if (slots_num == 0) then say_reward("This item can't be socketed.[ENTER]") return; end -- if say(string.format("This item has %d possible slot%s.[ENTER]", slots_num, (slots_num > 1 and "s" or ""))) local available_socket_ids, socket_vnum = {}, -1; for socket_id = 0, slots_num-1 do socket_vnum = item.get_socket(socket_id); if (socket_vnum > 1) then color_say("lightgreen", string.format("Slot n.%d: %s;", socket_id + 1, item_name(socket_vnum))) else say_reward(string.format("Slot n.%d: No stone;", socket_id + 1)) table.insert(available_socket_ids, socket_id); end -- if/else end -- for if (table.getn(available_socket_ids) == 0) then say_reward("[ENTER]There's already a stone in every available socket.[ENTER]") return; end -- if local available_stones_vnums = tincture_stone_add.BuildAvailableStoneListForItemType(item_type); if (table.getn(available_stones_vnums) == 0) then say_reward("[ENTER]You don't have any stone I can socket in.[ENTER]") return; end -- if say("[ENTER]Which stone do you want to add?") local available_stones_names = {}; table.foreach(available_stones_vnums, function(_, stone_vnum) table.insert(available_stones_names, item_name(stone_vnum)); end -- function ); table.insert(available_stones_names, "Abort"); -- Window length management local available_stones_num = table.getn(available_stones_names); if (available_stones_num > 3) then say_size(350, 300 + 27 * math.min(5, available_stones_num - SOCKETS_MAX_NUM)) else say("") end -- if/else local selected_stone = select_table(available_stones_names); if (selected_stone == table.getn(available_stones_names)) then return; end -- if pc.remove_item(TINCTURE_VNUM, 1); local selected_stone_vnum = available_stones_vnums[selected_stone]; pc.remove_item(selected_stone_vnum, 1); item.set_socket(available_socket_ids[1], selected_stone_vnum); end -- when end -- state end -- quest
This is an old quest I wrote. In order for the logic to work, the socket of index 0 must work.. and it does even on traditional kraizy files.
Are you sure this error is a thing? I'm pretty sure (although I didn't check) that some official quests also use that very same instruction.
- 1
- 1
-
Because.. you are only executing this code on login.
.get
doesn't exist as a trigger.
-
24/01/2024 (last update):
- The func 'is_valid_number' now has an "allow_zero" arg. and default values.
- Added 'count_digits(num)'.
- Added 'get_nth_digit(num, index)'.
- Added 'generate_num_array(limit)'.
- Added 'get_time_after_seconds(sec)'.
- Added 'mirror_shuffle(original_table, shuffled_table, target_table)'
- Renamed BuildSkillList to BuildSkillList2 in order to avoid conflicts with the default func from ymir.
it's been fun.
- 1
- 1
- 1
-
What's this supposed to be?
-
(pc/d).set_warp_location(index, warp_x, warp_y)
- 2
-
13 hours ago, shenhui1986 said:
questlua_pc.cpp: In function 'int quest::pc_in_dungeon(lua_State*)':
questlua_pc.cpp:277:31: error: invalid use of incomplete type 'class CDungeon'
277 | ret = (mapIndex == dungeon->GetOriginalMapIndex());
| ^~
In file included from stdafx.h:57,
from questlua_pc.cpp:1:
typedef.h:78:7: note: forward declaration of 'class CDungeon'
78 | class CDungeon;
| ^~~~~~~~
gmake: *** [Makefile:171: .obj/questlua_pc.o] Error 1This is the solution
#include "dungeon.h"
in questlua_pc.cpp
Bro.
You don't need a C++ function for such a thing.
InDungeon = function(map_index) local pc_index = pc.get_map_index(); return pc.in_dungeon() and pc_index >= ToDungeonIndex(map_index) and pc_index < ToDungeonIndex(map_index+1); end -- function
when MOB_VNUM.kill with InDungeon(DUNGEON_MAP_INDEX) begin
- 1
-
I have updated the function to current @ WeedHex, although you could've checked on my functions file which i constantly keep updated.. this post was almost 6 years old.
This version supports clock and simplified formats.
- 1
-
Mh, doable but unpractical, and also bad from a technical point of view.
The way dungeon instances work is by creating an instanced version of the target map and then warping to it.
Now, you're doing it wrong obviously, but even if you were doing it the right way.. this process requires the target's map to be in the same core of the map from which you try and enter the dungeon.
Meaning either you place ALL the maps in your server in a single core, so you can enter any dungeon from any map, or this is just a bad idea; and I don't think I need to explain why placing all the server's maps in a single core is not good practice.
Given how easy this is to do, the fact that no server decided to apply this approach even with all the QoLs implemented over the years speaks volumes.
- 1
-
Provide the quest you are using.
-
17 minutes ago, Lycawn said:
Actually tested , works for me
Regens in Metin2 works with a constant global timer, meaning they use a single timer to check the state of every mob/group, and respawn it if it's dead, starting from the time when the server has been last restarted. They don't initialize a respawn timer for each dead mob/group.
Let's say your last restart was at 12:00, and you have a boss that respawns once every 4 hours, and it has gets killed at 15:00
You would think it respawns at 19:00, but it actually will respawn at 16:00, because it checks for it once every 4 hours starting from server start time, not mob death time. If you were to kill it at 15:59, it will respawn exactly one minute later.
You can't really say "it works for me", you just didn't test it enough and have no idea how it works, which is fine.
- 1
- 1
-
This is useless due to how regens work. The timer for a mob to respawn doesn't start the moment the monster gets killed.
-
26/09/2023:
- Added num_to_char(num).
- Added char_to_num(string).
- Added string_to_ascii_array(string).
- Added ascii_array_to_string(table).
- Added skill_level_to_id(num).
- Added skill_id_to_level(string).
- Added time_to_sec(num, string). (Kept the old functions as wrapper funcs for backwards compatibility)
- 1
- 1
-
13 hours ago, AndreiS said:
Is any good this solution ?
No. You're effectively changing how the skill works instead of fixing the issue (which I still don't get what it even is)
LUA Questions
in Community Support - Questions & Answers
Posted
Adapt your qc's sourcecode to support functions within trigger chat strings.