Jump to content

caanmasu

Banned
  • Posts

    90
  • Joined

  • Last visited

  • Feedback

    0%

Everything posted by caanmasu

  1. Hello With some forum colleagues we have created a command to display the flags of a dungeon for debugging purposes. Usage: /dgetf [map_index] If map_index is not specified, the index of the current map is taken. cmd.cpp Add: ACMD(do_dgetf); Add command in list (above of { "\n",): { "dgetf", do_dgetf, 0, POS_DEAD, GM_IMPLEMENTOR }, cmd_gm.cpp Add this block: ACMD(do_dgetf) { char arg1[256]; one_argument(argument, arg1, sizeof(arg1)); if(!ch) return; DWORD mapIndex = ch->GetMapIndex(); if (*arg1) str_to_number(mapIndex, arg1); LPDUNGEON pDungeon = CDungeonManager::instance().FindByMapIndex(mapIndex); if (pDungeon) { ch->ChatPacket(CHAT_TYPE_INFO, "Dungeon flag list for %d:", mapIndex); pDungeon->SendFlagList(ch); } else ch->ChatPacket(CHAT_TYPE_INFO, "No dungeon found for mapindex: %d", mapIndex); } dungeon.cpp Add this block: void CDungeon::SendFlagList(LPCHARACTER ch) { for (const auto& [key, val]: m_map_Flag) ch->ChatPacket(CHAT_TYPE_INFO, "%s: %d", key.c_str(),val); } dungeon.h Add below of "void SetWarpLocation (long map_index, int x, int y);": void SendFlagList(LPCHARACTER ch); Greetings.
  2. config.cpp Find: BYTE g_bItemCountLimit = 200; Replace for: WORD g_bItemCountLimit = 200;
  3. Important! Update fix Undo fix 1. Put flag CRUSH again. CRUSH is stun in player skill. Explication of bug: This is the code if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_CRUSH | SKILL_FLAG_CRUSH_LONG) && !IS_SET(pkChrVictim->GetAIFlag(), AIFLAG_NOMOVE)) { ... pkChrVictim->Sync(tx, ty); pkChrVictim->Goto(tx, ty); pkChrVictim->CalculateMoveDuration(); if (m_pkChr->IsPC() && m_pkChr->m_SkillUseInfo[m_pkSk->dwVnum].GetMainTargetVID() == (DWORD) pkChrVictim->GetVID()) { SkillAttackAffect(pkChrVictim, 1000, IMMUNE_STUN, m_pkSk->dwVnum, POINT_NONE, 0, AFF_STUN, 4, m_pkSk->szName); } else{ pkChrVictim->SyncPacket(); } You have to start from some facts. 1. A player skill with the CRUSH flag stuns and pushes at the same time if the victim does not have immunity to blackouts. 2. If a character is stunned and receives a stun flag, they should not be pushed and should not be turned off again. 3. If a monster has CRUSH in his skill, he will always push the enemy The bug is that the CRUSH skill will always do pushback, whether you have immunity to blackouts or not. If a character launches sword strike against another character who is immune to blackouts, he pushes him but does not update packets (the push will not be seen). You can check this by looking at the distance in the syslog. The distance changes, even though it was never pushed. The solution is to push only when one character stuns another. In case a boss attacks with CRUSH, always push. Fix: if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_CRUSH | SKILL_FLAG_CRUSH_LONG) && !IS_SET(pkChrVictim->GetAIFlag(), AIFLAG_NOMOVE)) { ... //We remove the move in all cases. //pkChrVictim->Sync(tx, ty); //pkChrVictim->Goto(tx, ty); //pkChrVictim->CalculateMoveDuration(); bool bIgnoreStun = pkChrVictim->IsAffectFlag(AFF_STUN); //If the character is already stunned we save in variable if (m_pkChr->IsPC() && m_pkChr->m_SkillUseInfo[m_pkSk->dwVnum].GetMainTargetVID() == (DWORD) pkChrVictim->GetVID()) { SkillAttackAffect(pkChrVictim, 1000, IMMUNE_STUN, m_pkSk->dwVnum, POINT_NONE, 0, AFF_STUN, 4, m_pkSk->szName); } //else{ // pkChrVictim->SyncPacket(); //} if ((!bIgnoreStun && !(pkChrVictim->GetPoint(POINT_IMMUNE_STUN))) || m_pkChr->IsNPC()) //If the character has no stun and has no immune, or the attack comes from a monster { //Make the push and update packets pkChrVictim->Sync(tx, ty); pkChrVictim->Goto(tx, ty); pkChrVictim->CalculateMoveDuration(); pkChrVictim->SyncPacket(); } This fix solve the next bug, too: (double slide) [Hidden Content]
  4. Fix: char_item.cpp Find: else if (!IS_SET(item->GetAntiFlag(), ITEM_ANTIFLAG_GIVE | ITEM_ANTIFLAG_DROP) && GetParty()) { NPartyPickupDistribute::FFindOwnership funcFindOwnership(item); GetParty()->ForEachOnlineMember(funcFindOwnership); LPCHARACTER owner = funcFindOwnership.owner; // @fixme115 if (!owner) return false; int iEmptyCell; Add below: if (owner) { if (item->IsStackable() && !IS_SET(item->GetAntiFlag(), ITEM_ANTIFLAG_STACK)) { uint16_t wCount = item->GetCount(); for (int i = 0; i < INVENTORY_MAX_NUM; ++i) { LPITEM item2 = owner->GetInventoryItem(i); if (!item2) continue; if (item2->GetVnum() == item->GetVnum()) { int j; for (j = 0; j < ITEM_SOCKET_MAX_NUM; ++j) { if (item2->GetSocket(j) != item->GetSocket(j)) break; } if (j != ITEM_SOCKET_MAX_NUM) continue; const uint16_t wCount2 = MIN(g_wItemCountLimit - item2->GetCount(), wCount); wCount -= wCount2; item2->SetCount(item2->GetCount() + wCount2); if (wCount == 0) { owner->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("아이템 획득: %s 님으로부터 %s"), GetName(), item->GetName()); ChatPacket(CHAT_TYPE_INFO, LC_TEXT("아이템 전달: %s 님에게 %s"), owner->GetName(), item->GetName()); M2_DESTROY_ITEM(item); if (item2->GetType() == ITEM_QUEST) quest::CQuestManager::Instance().PickupItem(owner->GetPlayerID(), item2); return true; } } } item->SetCount(wCount); } } Note: check count datatypes. In my server is wCount2 with uint16_t but in your server likely is bCount2 BYTE
  5. Hello I found a base bug and I have found its solution. I share it with you with tests. Bug 1: Many times when attacking with Sword Strike the damage fails and this damage behavior varies very strange. Fix: Remove CRUSH flag from skill_proto (index 20) Bug 2: The damage of Sword Strike is too low compared to the other skills of your group. Fix: Change eSkillType from MELEE to RANGE in skill_proto (index 20) Testing bug 1: [Hidden Content]
  6. A client recently asked me for something like this and I sold it to him. I usually help with quests but it wouldn't seem fair to do so out of respect for my client. We can make a deal. Config looks so: reward_time = 60, --game time that must be accumulated to earn the coins coin_type = "cash", --coin column name in account coin_amount = 3, --for every x time (reward_time), x (coin_amount) will be obtained I can edit coins for an item
  7. Oh I understand I guess I can't help you this time. Only support in the development of the server. It's okay, I hope you can make it.
  8. I assume you mean normal pets (not eggs). In any case, the egg ones work in a very similar way in terms of their spawn. I am going to give you a guide to resummon the pet after a warp. It is advisable to store values that persist, since if you save it in the server's cache (a variable on the server), every time you restart it, all the characters when they enter will have to summon their pet. The solution is to save it to the database and the easiest way is to take advantage of the pet_system.quest using flags. If you review the code, to invoke the pet you need an item to be selected (when use), that is, the seal, and then through the pet.summon function you send the vnum of the pet (mob_proto) as a parameter. when [...].use begin pc.setqf("pet_item_id", item.get_id()) --stores the id (it's not vnum, it's the id) of the seal. We will use it when we log in and be able to select the seal again ... --every time you summon the pet, save the vnum of the monster it was summoned with pet.summon(mobVnum, false) pc.setqf("pet_vnum", mobVnum) --here ... --every time you unsummon the pet, don't forget to clear the vnum... pet.unsummon(mobVnum) pc.setqf("pet_vnum", 0) --here end Now we will make that when the character logs in, it invokes the pet: when login with pc.getqf("pet_vnum") != 0 begin --this makes sure we had an active pet last time item.select(pc.getqf("pet_item_id")) --with the seal id flag saved we can select it, it's like executing when use pet.summon(pc.getqf("pet_vnum"), false) --summon the pet with the vnum of the last pet we had active end The quest side is ready Now we need to make sure on the server side that when the pet is uninvoked, the values are cleaned up. PetSystem.cpp In: bool CPetActor::Update(DWORD deltaTime) Find: this->Unsummon(); And adds: m_pkOwner->SetQuestFlag("pet_system.pet_vnum", 0); m_pkOwner->SetQuestFlag("pet_system.pet_item_id", 0);
  9. Src/game pvp.cpp Add: #include "questmanager.h" Find: bool CPVPManager::CanAttack(LPCHARACTER pkChr, LPCHARACTER pkVictim) { Add below: if (quest::CQuestManager::instance().GetEventFlag("pvp_block_"+std::to_string(pkChr->GetMapIndex())) && pkVictim->IsPC()) return false; Usage in game: Example in map c1 Activate: /e pvp_block_41 1 Deactivate: /e pvp_block_41 0 You can set any map index.
  10. 1. Cooldown time: item_proto -> value1. Put cooldown time in seconds. Example 5 minutes: 300. 2. Grotto 2 TP: Add your map name in locale.lua (map index 73), then add the variable in select: original: sub_set = select(town1[empire],town2[empire],locale.map_name[64], locale.map_name[63], locale.map_name[61],locale.map_name[62],gameforge.locale.cancel) if sub_set == 7 then -- ´Ý±â return end new: sub_set = select(town1[empire],town2[empire],locale.map_name[64], locale.map_name[63], locale.map_name[61],locale.map_name[62], locale.map_name[73], gameforge.locale.cancel) if sub_set == 8 then -- ´Ý±â --put 8 instead 7 return end Add: -- 73 metin2_map_skipia_dungeon_02 { { 153600, 1203200 }, { 153600, 1203200 }, { 153600, 1203200 }, }, Below of: -- 62 metin2_map_n_flame_01 { { 153600, 1203200 }, { 153600, 1203200 }, { 153600, 1203200 }, }, (no tested)
  11. Hi @Deiklo! The coding is designed to be customized. You can assign the dungeon map you want, including monsters, rewards, required level, among others. To change the map you must modify the infinity_dungeon_lib.data table. It is very easy to do it since the variables are very telling, and in case they are not, I left comments that detail it. map_index, map_index_out, base_cords, outside_pos and in each jump of each mission. Regarding the compliment, thank you friend, a pleasure to serve you.
  12. Hello everyone I bring you my second quest tutorial where you can improve your quest/lua skills (a bit of SQL too) until you become a real expert. Note: in the attached file is the first and second tutorial (Spanish and English language) This tutorial is about an infinity Dungeon. The dynamic of my tutorials is to make a quest where I show point by point how I would do it, and I explain it in detail. If you want to skip the tutorial and just use the quest, you are free to do so, I left a folder with the files to install. I must warn that I only provide code, not models. I use already existing models from the game, specifically, demon tower map, Reaper as boss, dogs as monsters to destroy, etc. You can use the dungeon with the models you want, this way you improve the user experience. The dungeon is customizable, you only have to change numbers. I must also warn that the dungeon has not been tested with several players, although I did good tests with several of my characters, the best tests are done with real players. In case you find bugs, report them to my Discord. Another important note, if you don't read the tutorial, there are additional data that you can't miss, they are really mandatory. You must go to the tutorial and at the end you will find a section called 'Additional data:'. Really, you can't miss it. The content of this tutorial is as follows: 1. Show a button on the NPC 2. Show the NPC button only when we are in the map outside the dungeon. 3. Create the first dungeon jump 4. Create group mode 5. Add some restrictions for entry 6. Create two room mechanics and run them so that they are chosen randomly. 7. Take the character out of the dungeon when player die. 8. Create the ranking log 9. Add dungeon rejoin 10. Display the ranking 11. Add an announcement when someone breaks a floor record. 12. Create spectator mode 13. Increase the difficulty for each floor 14. Add logs 15. Create rewards 16. Create 5 dungeon mechanics 17. Dynamize the base coordinates 18. Add remaining time on each floor 19. Facilitate entry into GMs characters 20. Convert texts to variables Some gifs: Spectator mode Dugeon ranking Rejoin Announcement when someone breaks a floor record Mission 1: killAllMonsters Mission 2: killBossWithMonsters Mission 3: killAllStones Mission 4: purgePillarsRightOrder Mission 5: findRightKey Number of lines of code: translate.lua: 48 questlib.lua: 76 quest_infinity_dungeon.sql: 9 infinity_dungeon.quest: 167 infinity_dungeon_lib.lua: 408 infinity_dungeon_mission_lib.lua: 287 infinity_dungeon_rewards.lua: 26 Total: 1021 lines If you liked the intention of this post you can help me with a reaction, so you can give more popularity to this post and make it more visited so that more people can access to the knowledge of free quest/lua of the highest quality ever seen. Once an acquaintance told me that there was not enough quest documentation for Metin2 and I agreed with him. At that time I knew a little about quest/lua but I said to myself 'knowledge should be free but someone should provide it, not for profit' and I didn't expect that I was going to do it. I didn't want to be left alone with what I knew, I had the need to share it for everyone. About my knowledge of quest/lua: When I first learned quest/lua, I already had base experience of programming in general. I learned quest/lua empirically by watching other people's quest, the LUA source code in C++ about functions and the binary source code related to RAW TEXT tokens, in that order. I could say that I have seen and reviewed thousands of quests written by German, Spanish, English, Romanian, Turkish and other nationalities. I have also seen all levels of quests written by others. I have created many quests for clients, of all kinds of nationalities for all kinds of servers. In total I have accumulated more than 10 thousand hours of quest/lua programming. I have dedicated a lot of time of my life to this. I am a person who does not stay with what he knows, but wants to extend himself more, so I researched for a long time how to improve the quality of my code and to this day I put it into practice and it is the most important pillar of all my programming: code quality. I learned many code structures from many quest until I created my own structure and also my own libraries. When quest/lua is limited to what I need, I have no difficulty in modifying or creating LUA functions in C++ as this is also part of quest. There is much more about this but I think it is enough in this post. The day I leave, there will be forever (or until Metin2 dies completely) this knowledge that will be useful to improve the experience of players in Metin2 private servers. If they are shit server or not, I don't care. If you need any help you can contact me. My Discord is 'caanmasu'. Greetings to all. Download: [Hidden Content] or M2DL
  13. Test video: [Hidden Content] upgrade_skills.quest quest upgrade_skills begin state start begin when 50513.use begin upgrade_skills.upgradeSkillDialog(1) end when 50512.use begin upgrade_skills.upgradeSkillDialog(40) end function upgradeSkillDialog(inc) if pc.get_skill_group() != 0 then local vnum_list, name_list = {}, {} for _, vnum in ipairs(special.active_skill_list[pc.job+1][pc.get_skill_group()]) do if pc.get_skill_level(vnum) < 40 then table.insert(vnum_list, vnum) table.insert(name_list, locale.GM_SKILL_NAME_DICT[vnum] or vnum) end end if table.getn(name_list) != 0 then table.insert(name_list, "Close") say_title("Upgrade skill") say_reward("[ENTER]Item disappear after use") local sel = select_table(name_list) if sel != table.getn(name_list) then pc.set_skill_level(vnum_list[sel], pc.get_skill_level(vnum_list[sel])+inc) pc.remove_item(item.vnum) end end end end end end
  14. @SlayerPro Change quest lvl_110 begin By: quest playerMission begin
  15. This is my Hyperlink [Hidden Content] I can't help you directly. It takes time to find the solution but it is very likely that the error is there. You must review the flow of the code very well and know the values of the variables. Patience and tests. I can't help you here, sorry.
  16. Check: root/uitooltip.py This method: def SetHyperlinkItem(self, tokens):
  17. Hello everyone I bring a contribution to the quest section that in my opinion will be very useful for all the levels that I have seen in the forums that I have known. I saw some people who are just learning and some who are very talented, and I'm sure they'll tap into their potential with this hands-on tutorial. It's totally worth it. I did the Spanish version and it was a success. Now the English version, let's see how it goes. The minimum knowledge that you must have to perform this tutorial is to know how to compile the quests and optionally the source code on the server side. My recommendation to read the tutorial is that you first read it superficially from the beginning to the end, and then start from the beginning to the end applying the steps. The tutorial is about a real case I had with one of my clients, and he gave me permission to share it. Although the way of explaining is not how I would really do it, I made it easier to understand with the aim of teaching. Test video: [Hidden Content] Any questions or requests you have about me or my work, I will gladly reply to the private message of this forum. How to Tanaka event or M2DL Love and peace for everyone.
  18. when 20406.chat."Absorção de bônus"comece -> when 20406.chat."Absorção de bônus" begin if confirm == 2 então -> if confirm == 2 then
  19. char_resist.cpp Find: EVENTFUNC(poison_event) { TPoisonEventInfo * info = dynamic_cast<TPoisonEventInfo *>( event->info ); if ( info == NULL ) { sys_err( "poison_event> <Factor> Null pointer" ); return 0; } LPCHARACTER ch = info->ch; if (ch == NULL) { // <Factor> return 0; } LPCHARACTER pkAttacker = CHARACTER_MANAGER::instance().FindByPID(info->attacker_pid); Change this line: LPCHARACTER pkAttacker = CHARACTER_MANAGER::instance().FindByPID(info->attacker_pid); For: LPCHARACTER pkAttacker = CHARACTER_MANAGER::Instance().Find(info->attacker_pid); if (!pkAttacker) return 0; Find: void CHARACTER::AttackedByPoison(LPCHARACTER pkAttacker) { if (m_pkPoisonEvent) return; if (m_bHasPoisoned && !IsPC()) return; #ifdef ENABLE_WOLFMAN_CHARACTER if (m_pkBleedingEvent) return; if (m_bHasBled && !IsPC()) return; #endif if (pkAttacker && pkAttacker->GetLevel() < GetLevel()) { int delta = GetLevel() - pkAttacker->GetLevel(); if (delta > 8) delta = 8; if (number(1, 100) > poison_level_adjust[delta]) return; } /*if (IsImmune(IMMUNE_POISON)) return;*/ m_bHasPoisoned = true; AddAffect(AFFECT_POISON, POINT_NONE, 0, AFF_POISON, POISON_LENGTH + 1, 0, true); TPoisonEventInfo* info = AllocEventInfo<TPoisonEventInfo>(); info->ch = this; info->count = 10; info->attacker_pid = pkAttacker ? pkAttacker->GetPlayerID() : 0; Change this line: info->attacker_pid = pkAttacker ? pkAttacker->GetPlayerID() : 0; For: info->attacker_pid = pkAttacker ? pkAttacker->GetVID() : 0;
  20. This occurred because of a recent Visual Studio update. Solution: Right click on the project UserInterface -> Properties -> C/C++ -> Precompiled Headers -> Precompiled Header: Do not use precompiled headers. Do the same with the other projects (ScriptLib, SpeedTreeLib, etc) Do the same in Release and Debug.
  21. Hi @mrtcnglrr Search: if (pAttacker->GetPoint(POINT_STEAL_HP)) { int pct = 1; if (number(1, 10) <= pct) { int iHP = MIN(dam, MAX(0, iCurHP)) * pAttacker->GetPoint(POINT_STEAL_HP) / 100; if (iHP > 0 && GetHP() >= iHP) { CreateFly(FLY_HP_SMALL, pAttacker); #ifdef YOUR_DEFINE if (!IsNPC()) { pAttacker->PointChange(POINT_HP, iHP); PointChange(POINT_HP, -iHP); } #else pAttacker->PointChange(POINT_HP, iHP); PointChange(POINT_HP, -iHP); #endif } } } What I did in this code was omit the HP absorb damage on the monsters. Works.
  22. I do not recommend using that function for many reasons. Install mysql_direct_query
  23. Don't bother using functions that don't exist in your source code. There is a function for that, it uses: item.set_value(index, apply_type, apply_value) Like this: item.set_value(0, 72, math.min(MAX_AVG_DMG, START_AVG_DMG + PROG_AVG_DMG*pc.getqf("times")));
  24. Hi @backtop Many times the structure of the data is complicated, and anyone would do this: --Weapons lvl 1 local weapons = { [0] = {21900, 21903}, [1] = {21900, 21901, 21902}, [2] = {21900}, [3] = {21904, 21905}, } --Weapons lvl 10 local weapons = { [0] = {21910, 21913}, [1] = {21910, 21911, 21912}, [2] = {21910}, [3] = {21914, 21915}, } --Weapons lvl 20 local weapons = { [0] = {21920, 21923}, [1] = {21920, 21921, 21922}, [2] = {21920}, [3] = {21924, 21925}, } --... To fix this problem, you need to find the pattern. You must somehow relate the vnum of the weapon to its "jump". The jump corresponds to the count of the times the reward has been collected. It will start at 0 by default and it suits us for being a qf. In my case I called it "step". 21900+step*10 I think it is understood. Regarding the type of weapon, I have "dynamized" it. On the line: local weapon_type = {{0, 3}, {0, 1, 2}, {0}, {4, 5}, {6}} The order remains the same, only when I call pc.job, I add 1 to it to match. The number that is inside each table, for example, {0, 3}, corresponds to the type of weapon, these numbers being the last digits of the weapon's vnum. In this way I have dynamized the code. There are good contributions in this thread, take the attributes of average damage and ability to complement your code. I've done a 31 line quest in case you want to review it. I must warn you that the code is not tested and most likely contains bugs. This code is a guide. But don't worry, a quest that has a lot of code or not, does not influence the server, and also nobody will be able to know it. The important thing is that things work. quest young_weapon_heroes begin state start begin when letter or levelup with pc.level >= pc.getqf("step")*10 and pc.getqf("step") <= 7 begin send_letter(locale(82)) end when button or info begin say_title(locale(82)) say(locale(84)) wait() say(locale(85)) local weapon_type = {{0, 3}, {0, 1, 2}, {0}, {4, 5}, {6}} local my_weapon_type = weapon_type[pc.job+1] local t_select = {} for i = 1, table.getn(my_weapon_type) do local weapon_vnum = 21900+pc.getqf("step")*10+my_weapon_type[i] say_item_vnum_inline(weapon_vnum, i-1, table.getn(my_weapon_type)) 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_select(21900+pc.getqf("step")*10+my_weapon_type[sel]) pc.setqf("step", pc.getqf("step")+1) --add mean skill clear_letter() end end end
×
×
  • 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.