Jump to content

caanmasu

Banned
  • Posts

    90
  • Joined

  • Last visited

  • Feedback

    0%

Everything posted by caanmasu

  1. Put TEST_SERVER: 1 in CONFIG file from your core and check lua errors
  2. You are welcome! Hmmm my bad. Elements in table should be splitted by comma, so: local weapons = { [0] = {21900, 21903}, [1] = {21900, 21901, 21902}, [2] = {21900}, [3] = {21904, 21905}, }
  3. Hi @backtop I have this quest identical to how it is seen on the official server and even improved (fixes). I can't send it to you for free but I can help you with some tips. 1. Seeing in game how the quest works is the most important part. If you realize, at level 1 they give a weapon, then at level 10, then at 20, and so on until 70. I'm not sure if when you get to level 30 (example) and you didn't claim the one from level 20, you won't be able to claim it anymore, only the one from 30 onwards. Another thing to note is that it will show you the types of weapons that the character can use. For example, the sura only uses a sword, while the ninja uses a dagger, bow and sword. 2. The way to program it. I think you're new to quest, so I'll make your life easier by making static code. (I never use static code in my quests, only dynamic code but the difficulty is much higher) I will not use a data structure in this case either, I will only declare variables as it comes out. (I always use data structure in my quests, it is a higher difficulty) You should also think about validating if the character has already claimed the weapon and sending it to the next weapon. So in this case you can use several states (each state would be each level where the weapon is claimed) or qf variables of the character that would serve as flags. In this case to make your life easier, I will do it with states (static code) 3. The interface You have said that you want it as the official one, because you have to think about how to display the inline icons and radio buttons. In this part many people get confused. About the texts, the indices of the locale texts are: 82, 84 and 85. Maybe at higher levels there are other texts like 86 but at the moment I'm not sure. Let us begin: Establish a structure for the quest. quest young_weapon_heroes begin state start begin when login begin set_state(level_1) end end state level_1 begin when letter with pc.level >= 1 and pc.level < 10 begin --limit level send_letter(locale(82)) end when button or info begin say_title(locale(82)) say(locale(84)) wait() say(locale(85)) --GUI part --in case the character has accepted a weapon, then this is done: set_state(level_10) end end --The same but with level_10 :) state level_10 begin when letter with pc.level >= 10 and pc.level < 20 begin --limit level send_letter(locale(82)) end when button or info begin say_title(locale(82)) say(locale(84)) wait() say(locale(85)) --GUI part --in case the character has accepted a weapon, then this is done: set_state(level_20) --now change here to 20 end end --Do the same until level 70. state level_70 begin when letter with pc.level >= 70 begin --limit level send_letter(locale(82)) end when button or info begin say_title(locale(82)) say(locale(84)) wait() say(locale(85)) --GUI part --in case the character has accepted a weapon, then this is done: set_state(__COMPLETE__) --This is so they don't claim infinite times the last weapon end end state __COMPLETE__ begin end end We will now do the data part. You must have listed the vnum of the weapons. The ones of level 1 go from 21900 to 21906, but you have to take into account the type of weapon to be able to show the correct ones in the corresponding races. We will create simple tables to store the weapons (level 1) that correspond to each race: local weapons = { [0] = {21900, 21903} [1] = {21900, 21901, 21902} [2] = {21900} [3] = {21904, 21905} [4] = {21906} } The indices in the table correspond to the breeds. war = 0 ninja = 1 sura = 2 shaman = 3 wolfman = 4 If you notice, the buttons are almost ready, just need to do a little fix. We will create the select: local t_select = {} for i = 1, table.getn(weapons[pc.job]) do local weapon_vnum = weapons[pc.job][i] table.insert(t_select, item_name(weapon_vnum)) end table.insert(t_select, "Close") I have simply created a table of each weapon vnum converted to its name. when button or info begin say_title(locale(82)) say(locale(84)) wait() say(locale(85)) --GUI part local weapons = { [0] = {21900, 21903} [1] = {21900, 21901, 21902} [2] = {21900} [3] = {21904, 21905} [4] = {21906} } local t_select = {} for i = 1, table.getn(weapons[pc.job]) do local weapon_vnum = weapons[pc.job][i] table.insert(t_select, item_name(weapon_vnum)) end table.insert(t_select, "Close") --in case the character has accepted a weapon, then this is done: set_state(level_10) end Not bad. We already have the buttons. Now we must activate the event when a weapon is chosen. local sel = select_table(t_select) if sel == table.getn(t_select) then return end pc.give_item2(weapons[pc.job][sel]) Note: select_table() is the same as select(), with the difference that select_table receives a table as a parameter, and in this way we make it more dynamic. I admit, I got a little dynamic on some things. In the last option (close) we go to the return so that nothing happens You may be confused in the pc.give_item2. Well, we go to the table of the weapons of our race, and the variable sel is equivalent to the position of the element. In this case, the position of the element corresponds to the weapon. Thus we have our when: when button or info begin say_title(locale(82)) say(locale(84)) wait() say(locale(85)) --GUI part local weapons = { [0] = {21900, 21903} [1] = {21900, 21901, 21902} [2] = {21900} [3] = {21904, 21905} [4] = {21906} } local t_select = {} for i = 1, table.getn(weapons[pc.job]) donde local weapon_vnum = weapons[pc.job][i] table.insert(t_select, item_name(weapon_vnum)) end table.insert(t_select, "Close") local sel = select_table(t_select) if sel == table.getn(t_select) then return end pc.give_item2(weapons[pc.job][sel]) set_state(level_10) end We are left with the interface part. For this we go to the questlib and look for this function: function say_item_vnum_inline(vnum,index,total) if index >= total then return end if total > 3 then return end raw_script("[INSERT_IMAGE image_type;item|idx;"..vnum.."|title;"..item_name(vnum).."|desc;".."".."|index;"..index.."|total;"..total.."]") end (I don't like how it's programmed but it works, we'll use it that way.) This function has the same goal as say_item, but instead of displaying a single item in the middle, this time up to 3 will be displayed on the same line. Just like in the original quest. Parameters: vnum: vnum of the item index: number of the position in the line. It starts with 0. total: maximum number of items that you are going to put in the row Example if I were a warrior: say_item_vnum_inline(21900, 0, 2) --the 2 is because we will put the sword and spear say_item_vnum_inline(21903, 1, 2) In this case, the level 1 sword and spear will be shown in a single line. Example if I were a ninja: say_item_vnum_inline(21900, 0, 3) say_item_vnum_inline(21901, 1, 3) say_item_vnum_inline(21902, 2, 3) Example if it were a surah: say_item_vnum_inline(21900, 0, 1) Do you understand the pattern? We're going to make it dynamic because for me it's the best way to do it, but if you don't understand it, go over it a bit because sometimes it's difficult the first time. local count_weapon = table.getn(weapons[pc.job]) for i = 0, count_weapon do local weapon_vnum = weapons[pc.job][i] say_item_vnum_inline(weapon_vnum, i, count_weapon) end Now we add it to our when, like this: when button or info begin say_title(locale(82)) say(locale(84)) wait() say(locale(85)) local weapons = { [0] = {21900, 21903} [1] = {21900, 21901, 21902} [2] = {21900} [3] = {21904, 21905} [4] = {21906} } local count_weapon = table.getn(weapons[pc.job]) for i = 0, count_weapon do local weapon_vnum = weapons[pc.job][i] say_item_vnum_inline(weapon_vnum, i, count_weapon) end local t_select = {} for i = 1, table.getn(weapons[pc.job]) donde local weapon_vnum = weapons[pc.job][i] table.insert(t_select, item_name(weapon_vnum)) end table.insert(t_select, "Close") local sel = select_table(t_select) if sel == table.getn(t_select) then return end pc.give_item2(weapons[pc.job][sel]) set_state(level_10) end Now just do the same with the other states. Remember to change the vnum of the weapons. Note: this code is a guide and is not tested. I have written it impromptu but I hope it will be useful.
  4. I ask you the favor that you don't sabotage my posts, just as I don't sabotage yours. At no time have I commented on your answers. Secondly, you talk about advantages of lua and you mentioned an advantage that does not have to do with performance, which is the topic we are talking about. If you're so professional, why don't you explain how to do it and that's it?
  5. This quest can cause problems (as it may not) because of the amount of processing it does per kill. Think about this, every monster you kill will do all those cycles. You can easily kill 40 at the same time. Another 50 people are also killing monsters. You can improve this by making the tables global and indexed. Difference between a local variable and a global variable in this case: If you put the local variable in the when, for each kill it will be declared again. If you put the global variable, it will only be reloaded once you start the server or reload q, this takes a lot of weight off the server. Difference between a table with indexed values and a table without indexed values. Think web. Imagine if web pages were not indexed. If you were to search for a web page within the page table, it would probably take a year to find the one you are looking for. For example: web_pages = { www.page1.com, --index 1 www.page2.com, --index 2 ... www.yourpage.com, --index (?) (possibly billions) ... } If, when searching in your browser, it goes through the cycle asking in each index if the page is the one you are looking for, there is a very big problem, you get desperate from waiting so long and you leave. See how web pages work if it were lua. web_pages = { ["www.page1.com"] = 0, ["www.page2.com"] = 0, ... ["www.yourpage.com"] = 0, ... } Note: You must assign any value that is different from nil, since by default all values in a table are nil. In this case, when searching for your page on the internet, it will appear immediately because it will not be necessary to execute a cycle since your page is in an index. Just do web_pages["www.yourpage.com"] and it will return 0 indicating that your page is there. Now you must use your creativity to make your drop table global and indexed. This is a recommendation from Camilo, but you can do what you want
  6. This happens only when there are many processes at the same time. In this case, a single drop where it cycles 3 objects is no problem. Even for every monster kill those cycles are executed, it's still not relevant. The server prioritizes quests over everything, and can slow down the entire game. For example, if you run a cycle of 10 thousand iterations doing a certain process, you can freeze the server for a few seconds while the quest ends. What if this happens? Well, the character that opens the quest will remain suspended in movie mode while it ends, while the other players will not be able to do anything (only move because they are clientside), for example, if they hit a monster, it does not respond. I am already talking about extreme cases of 10 thousand cycles. It's a thousand times better for the drop to be done by source code (C++) than by lua, since C++ is so much faster and slowdowns won't happen. If your drop is something very simple, go ahead. How to know if your server is going to slow down? Put the drops you want, and if it doesn't happen, don't worry, just keep going. Otherwise, move the drop to C++ (txt) Recommendations for common_drop_item.txt 1. If you have the possibility to change the structure to a more readable one, go ahead (there is a post about this) 2. About the old common_drop_item.txt, open with Excel. There is a column that is not used at all, in that you can put the value you want and it will not influence. Where it says PAWN and similar, it is the rank of the monsters, you can see it in mob_proto.
  7. game -> exchange.cpp Find this method: bool CExchange::AddItem(TItemPos item_pos, BYTE display_pos) { Find this code into: if (IS_SET(item->GetAntiFlag(), ITEM_ANTIFLAG_GIVE)) { m_pOwner->ChatPacket(CHAT_TYPE_INFO, "659"); return false; } Replace by: if (!m_pOwner->IsGM()) if (IS_SET(item->GetAntiFlag(), ITEM_ANTIFLAG_GIVE)) { m_pOwner->ChatPacket(CHAT_TYPE_INFO, "659"); return false; } No tested. My code is a guide, it may or may not work, you should know what you are doing.
  8. game -> battle.cpp Find: int CalcAttBonus(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, int iAtk) Make sure all the elements are together, like this (example): if (pkVictim->IsRaceFlag(RACE_FLAG_ATT_WIND)) iAtk += (iAtk * pkAttacker->GetPoint(POINT_ENCHANT_WIND)) / 100; Do the same with fire, elec, ice, earth, dark
  9. Add a new column in shop_item called "price_vnum", int 11 default 0 I guess that you need "price_count" too.
  10. 99.9% of developers don't know about bugs until they happen. 1. Be careful when logging in because if you change character, all the code will be executed again. Your code: when login or enter with death_hall.isInDungeon(DUNGEON_MAP_INDEX) begin ... d.setf("deathhall_stage", 1) end New code: when login or enter with death_hall.isInDungeon(DUNGEON_MAP_INDEX) and d.getf("deathhall_stage") == 0 begin ... d.setf("deathhall_stage", 1) end In this way you ensure that the code is executed only once. 2. Another very common mistake is to put generic names in the server_timers, this causes conflicts because if you do another quest, it is very likely that you put the same name of the server_timer and it will be overwritten. When you compile the quests, a directory tree is created where the parent is "object". The timers are stored there with the name. For example, the server_timer "dungeon_end" generates a folder object/dungeon_end/server_timer/death_hall.start If you build another quest where there is a server_timer with the same name, it will be replaced. (Ymir bugs) The solution is to put more detailed names, for example: death_hall_dungeon_end 3. This code: when 304.kill with d.getf("deathhall_stage") == 2 and death_hall.isInDungeon(DUNGEON_MAP_INDEX) begin local count_monster = d.getf("monster_count") + 1 d.setf("monster_count", count_monster) if count_monster > 0 then d.set_minimap_dungeoninfo_gauge(1, d.getf("monster_count"), 100) if count_monster == 100 then d.setf("monster_count", count_pillar) d.kill_all() d.set_minimap_dungeoninfo_stage(3,6) d.set_minimap_dungeoninfo_gauge(1,d.getf("stones_use_flag"),4) d.set_minimap_dungeoninfo_notice("Vernichte solange Monster bis du einen Schlüsselstein droppst!") d.regen_file("data/zylon_dungeon/zylon_regen_s.txt") d.regen_file("data/zylon_dungeon/zylon_regen02.txt") d.setf("deathhall_stage", 3) d.setf("pillar_stone_flag", 400) end end end when 304.kill with d.getf("deathhall_stage") == 3 and death_hall.isInDungeon(DUNGEON_MAP_INDEX) begin local count_pillar = d.getf("pillar_stone_flag") - 1 d.setf("pillar_stone_flag", count_pillar) if contains({0, 100, 200, 300}, count_pillar) then pc.give_item2(PILLAR_STONE, 1) d.set_minimap_dungeoninfo_notice("Benutze den Schlüsselstein mit rechtsklick") end end You must be careful because something similar happens here with the server_timers. If you have two similar whens, probably only one of the two will be saved. The solution is to put the blocks together. New code: when 304.kill with death_hall.isInDungeon(DUNGEON_MAP_INDEX) begin if d.getf("deathhall_stage") == 2 then local count_monster = d.getf("monster_count") + 1 d.setf("monster_count", count_monster) if count_monster > 0 then d.set_minimap_dungeoninfo_gauge(1, d.getf("monster_count"), 100) if count_monster == 100 then d.setf("monster_count", count_pillar) d.kill_all() d.set_minimap_dungeoninfo_stage(3,6) d.set_minimap_dungeoninfo_gauge(1,d.getf("stones_use_flag"),4) d.set_minimap_dungeoninfo_notice("Vernichte solange Monster bis du einen Schlüsselstein droppst!") d.regen_file("data/zylon_dungeon/zylon_regen_s.txt") d.regen_file("data/zylon_dungeon/zylon_regen02.txt") d.setf("deathhall_stage", 3) d.setf("pillar_stone_flag", 400) end end elseif d.getf("deathhall_stage") == 3 then local count_pillar = d.getf("pillar_stone_flag") - 1 d.setf("pillar_stone_flag", count_pillar) if contains({0, 100, 200, 300}, count_pillar) then pc.give_item2(PILLAR_STONE, 1) d.set_minimap_dungeoninfo_notice("Benutze den Schlüsselstein mit rechtsklick") end end end If your code works, that's fine, even if your code isn't the most optimal, however, it needs to be improved. Along the way you will improve it. I will complement with code that I would change. 1. Your code: function isInDungeon(idx) return pc.get_map_index() >= (idx * 10000) and pc.get_map_index() < ((idx+1) * 10000) end New code: function isInDungeon() return pc.get_map_index() >= (DUNGEON_MAP_INDEX * 10000) and pc.get_map_index() < (DUNGEON_MAP_INDEX+1) * 10000 end I do this because it is not necessary to send the map index, since the dungeon is specified by calling the function with death_hall.isInDungeon(). 2. Your code: when 8008.kill with d.getf("door_key_drop") == 1 begin local pct = number(1,10) if pct == 1 then pc.give_item2(DOOR_KEY, 1) d.set_minimap_dungeoninfo_notice("Du hast einen Torschlüssel erhalten. Ziehe ihn aufs Tor.") d.setf("door_key_flag", 2) clear_server_timer("time_out", get_server_timer_arg()) elseif pct != 1 then d.regen_file("data/zylon_dungeon/stone_regen.txt") end end New code: when 8008.kill with death_hall.isInDungeon() and d.getf("door_key_drop") == 1 begin if number(1, 10) == 1 then --10% pc.give_item2(DOOR_KEY) d.set_minimap_dungeoninfo_notice("Du hast einen Torschlüssel erhalten. Ziehe ihn aufs Tor.") d.setf("door_key_flag", 2) clear_server_timer("death_hall_time_out", d.get_map_index()) else d.regen_file("data/zylon_dungeon/stone_regen.txt") end end 3. I don't know if you know about the difference between item.remove and pc.remove_item. item.remove removes the item's stack. pc.remove_item removes the number of items from the vnum that you specify. I always use pc.remove_item Follow the advice of the experts and learn the codes of other quests (whether they are good or not so good), that will help you a lot. As you gain expertise, you will find yourself in need of creating libraries. I wish you the best of luck, that you do something great with your dungeon!
  11. Looks like someone's a little hot today. Maybe this post can help you. I recommend you save time by using ChatGPT. Me: "%.0f-%.0f" means? ChatGPT: "%.0f-%.0f" is a string format specifier in programming that is often used to format output values in a particular way. In this case, the format specifier is being used to represent a range of possible values for the "Attack Power" variable. The "%.0f" part of the specifier is used to format a floating-point number (a number with a decimal point) with zero decimal places, which means it will be rounded to the nearest integer. The "-" between the two format specifiers is simply a separator that indicates the range of values. So, if the values being formatted are 10.5 and 15.2, for example, the output would be "10-15", which represents the range of possible attack power values. Note that the specific meaning and usage of format specifiers may vary depending on the programming language and context.
  12. Before: def __DropSrcItemToDest... if srcItemSlotPos == ... return if app.ENABLE_SOULBIND_SYSTEM: ... After: def __DropSrcItemToDest... if srcItemSlotPos == ... return if app.ENABLE_SOULBIND_SYSTEM: ... Just remove one tab in 1510 and 1511
  13. define SOUL_STONE 40004 quest update_skill_g2p begin state start begin when SOUL_STONE.use begin MIN_LVL_SKILL = 30 MAX_LVL_SKILL = 40 if pc.get_skill_group() != 0 then local skill_list = special.active_skill_list[pc.job+1][pc.get_skill_group()] local ret_vnum_list, ret_name_list = {}, {} table.foreach(skill_list, function(i, skill_vnum) local skill_level = pc.get_skill_level(skill_vnum) if skill_level >= MIN_LVL_SKILL and skill_level < MAX_LVL_SKILL then table.insert(ret_vnum_list, skill_vnum) local name = locale.GM_SKILL_NAME_DICT[skill_vnum] if name == nil then name = skill_vnum end table.insert(ret_name_list, name) end end) if table.getn(ret_name_list) != 0 then table.insert(ret_name_list, "Close") say_title("Upgrade skill G to P") say() say("Disappear after use") local sel = select_table(ret_name_list) if sel != table.getn(ret_name_list) then pc.set_skill_level(ret_vnum_list[sel], MAX_LVL_SKILL) pc.remove_item(item.vnum) end end end end end end
  14. define SOURCE_VNUM_ITEM 30006 define SOURCE_COUNT_ITEM 10 define TARGET_VNUM_ITEM 30007 quest pick_item begin state start begin when SOURCE_ITEM.pick with pc.count_item(SOURCE_ITEM) >= SOURCE_COUNT_ITEM begin pc.remove_item(SOURCE_VNUM_ITEM, SOURCE_COUNT_ITEM) pc.give_item2(TARGET_VNUM_ITEM) end end end
  15. When edit Python files you should setup a comfortable environment. 1. Edit files with Notepad++ (do predeterminated) 2. Select language (if it is not selected) from Language->P->Python 3. Visible characters from View->Show symbol->Show spaces and tabulations Now edit your file. Notepad++ in Python set default 4 spaces as tab. Modify your file normally. When you end edit file, do it: Edit->Cleaning operations->Space to TAB (all) What means indentation? Indentation refers to the spaces at the beginning of a code line. Where in other programming languages the indentation in code is for readability only, the indentation in Python is very important. Python uses indentation to indicate a block of code. From w3schools Tab are very important in Python. Good example of indentation: def test(): import chat chat.AppendChat(chat.CHAT_TYPE_INFO, "Test 1"); 1° Bad example of indentation: def test(): import chat chat.AppendChat(chat.CHAT_TYPE_INFO, "Test 1"); 2° Bad example of indentation: def test(): import chat chat.AppendChat(chat.CHAT_TYPE_INFO, "Test 1"); 3° Bad example of indentation: def test(): import chat chat.AppendChat(chat.CHAT_TYPE_INFO, "Test 1"); Look difference between dot and tab.
  16. cmd.cpp Search: { "purge", do_purge, 0, POS_DEAD, GM_WIZARD }, Replace: { "purge", do_purge, 0, POS_DEAD, GM_LOW_WIZARD },
  17. Hi, look my solution from lua. --questlib.lua table_players_by_map = {} function getTableSize(t) local count = 0 for _, __ in pairs(t) do count = count + 1 end return count end players_by_map = function(map_index) if map_index == nil then map_index = pc.get_map_index() end local count = 0 if table_players_by_map[map_index] == nil or getTableSize(table_players_by_map[map_index]) == 0 then return count end return getTableSize(table_players_by_map[map_index]) end --quest quest register_player_map begin state start begin when login begin local my_idx = pc.get_map_index() if table_players_by_map[my_idx] == nil then table_players_by_map[my_idx] = {} end table_players_by_map[my_idx][pc.get_vid()] = 0 end when logout begin local my_idx = pc.get_map_index() if table_players_by_map[my_idx] != nil then table_players_by_map[my_idx][pc.get_vid()] = nil end end end end --usage quest test begin when 20095.chat."Test" begin say(string.format("On your map there are %s players.", players_by_map())) say(string.format("On map index 62 there are %s players.", players_by_map(62))) end when 20095.chat."Full table (experimental)" with pc.is_gm() begin setskin(0) chat("table_players_by_map = {") for map_index, t_vids in pairs(table_players_by_map) do chat(string.format("\t[%s] = {", map_index)) for vid, value in pairs(t_vids) do chat(string.format("\t\t[%s] = %s", vid, value)) end chat("\t},") end chat("}") end end Check "Full table (experimental)" option in order to understand it. /rel q is same than set table_player_by_map nil. Be ware. Sure that your player stay on same core.
  18. Quest flag are stored on table player.quest Functions like GetQuestFlag, get these values from db. No need packets.
  19. --save x and y global positions pc.setqf("saved_x", pc.get_x()) pc.setqf("saved_y", pc.get_y()) --teleport global coords pc.warp(pc.getqf("saved_x")*100, pc.getqf("saved_y")*100)
  20. local date = os.date("%d%m%y") Output: 301122
  21. Alternative (no C++, only lua) party.give_item2 = function(item_vnum, item_count) if party.is_party() then local pids = {party.get_member_pids()} for _, pid in next, pids, nil do q.begin_other_pc_block(pid) pc.give_item2(item_vnum, item_count) q.end_other_pc_block() end party.get_member_pids() else pc.give_item2(item_vnum, item_count) end end Advantage: You can to use in dungeon or not. Usage (example): party.give_item2(27001, 200) quest_functions: party.give_item2 ¡Saludos!
×
×
  • 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.