Jump to content

Owsap

Honorable Member
  • Posts

    264
  • Joined

  • Last visited

  • Days Won

    29
  • Feedback

    91%

Posts posted by Owsap

  1. I recently came across this issue when updating some things on the party (group) system and I realized that this problem was present on all the branches of the source. Although I've seen certain topics with some solutions, they don't provide much information and, in my opinion, a good solution. So, I'll share mine with you and you can use it if you're happy with the results.

    The Bug

    When you set a role to a member as a leader, it will give the member a certain bonus according to the role, this bonus is given if the leadership skill is high. Now, if you decide to remove the members role, in theory the bonus should be removed. However, when this happens, it will only compute the battle points of the player which will make some bonuses of certain roles the same until the character computes all his points again. For example, if you set a member with the tanker role, which grants a bonus of additional health points and then remove the role from the member, the bonus will not be removed until the character computes his points again. It doesn't remove the bonus because the function by default only computes the battle points, this relates to roles like, attacker and defender.

    Realization

    So far, we realized a possible solution, which is replacing the ComputeBattlePoints with ComputePoints, well sure, and I've seen this solution on this forum in some help request topics but this comes to a major cost in performance since the role computation function CParty::ComputeRolePoint is frequently called.

    The Solution

    What we really want to do is check if the member has any bonus assigned to a role, remove the bonus and finally compute the character's points appropriately, avoiding constant updates to the ComputePoints function. If the member doesn't have any bonus given from a role, there will be no need to calculate the points.

    Spoiler
    /// @ server/game/src/party.cpp
    // Search @ void CParty::ComputeRolePoint
    	if (!bAdd)
    	{
    		ch->PointChange(POINT_PARTY_ATTACKER_BONUS, -ch->GetPoint(POINT_PARTY_ATTACKER_BONUS));
    		ch->PointChange(POINT_PARTY_TANKER_BONUS, -ch->GetPoint(POINT_PARTY_TANKER_BONUS));
    		ch->PointChange(POINT_PARTY_BUFFER_BONUS, -ch->GetPoint(POINT_PARTY_BUFFER_BONUS));
    		ch->PointChange(POINT_PARTY_SKILL_MASTER_BONUS, -ch->GetPoint(POINT_PARTY_SKILL_MASTER_BONUS));
    		ch->PointChange(POINT_PARTY_DEFENDER_BONUS, -ch->GetPoint(POINT_PARTY_DEFENDER_BONUS));
    		ch->PointChange(POINT_PARTY_HASTE_BONUS, -ch->GetPoint(POINT_PARTY_HASTE_BONUS));
    		ch->ComputeBattlePoints();
    		return;
    	}
    
    // Replace with (you might need to include <array> in your file)
    	if (!bAdd)
    	{
    		const BYTE bMaxRoleNum = PARTY_ROLE_MAX_NUM - PARTY_ROLE_ATTACKER;
    		std::array<long, bMaxRoleNum> alPartyPoints = {
    			ch->GetPoint(POINT_PARTY_ATTACKER_BONUS),
    			ch->GetPoint(POINT_PARTY_TANKER_BONUS),
    			ch->GetPoint(POINT_PARTY_BUFFER_BONUS),
    			ch->GetPoint(POINT_PARTY_SKILL_MASTER_BONUS),
    			ch->GetPoint(POINT_PARTY_HASTE_BONUS),
    			ch->GetPoint(POINT_PARTY_DEFENDER_BONUS)
    		};
    
    		bool bComputePoints = false;
    		for (BYTE bIndex = 0; bIndex < bMaxRoleNum; ++bIndex)
    		{
    			if (alPartyPoints[bIndex] != 0)
    			{
    				BYTE bRole = PARTY_ROLE_ATTACKER + bIndex;
    				WORD wPointType = POINT_NONE;
    				switch (bRole)
    				{
    					case PARTY_ROLE_ATTACKER: wPointType = POINT_PARTY_ATTACKER_BONUS; break;
    					case PARTY_ROLE_TANKER: wPointType = POINT_PARTY_TANKER_BONUS; break;
    					case PARTY_ROLE_BUFFER: wPointType = POINT_PARTY_BUFFER_BONUS; break;
    					case PARTY_ROLE_SKILL_MASTER: wPointType = POINT_PARTY_SKILL_MASTER_BONUS; break;
    					case PARTY_ROLE_HASTE: wPointType = POINT_PARTY_HASTE_BONUS; break;
    					case PARTY_ROLE_DEFENDER: wPointType = POINT_PARTY_DEFENDER_BONUS; break;
    					default: continue;
    				}
    				ch->PointChange(wPointType, -alPartyPoints[bIndex]);
    				bComputePoints = true;
    			}
    		}
    
    		if (bComputePoints)
    			ch->ComputePoints();
    
    		return;
    	}
    
    // If flexibility and readability are more important for you, than you can use a map.
    // However, it may have a slight performance overhead compared to array access.
    	if (!bAdd)
    	{
    		std::map<WORD, long> mPartyPoints;
    		mPartyPoints[POINT_PARTY_ATTACKER_BONUS] = ch->GetPoint(POINT_PARTY_ATTACKER_BONUS);
    		mPartyPoints[POINT_PARTY_TANKER_BONUS] = ch->GetPoint(POINT_PARTY_TANKER_BONUS);
    		mPartyPoints[POINT_PARTY_BUFFER_BONUS] = ch->GetPoint(POINT_PARTY_BUFFER_BONUS);
    		mPartyPoints[POINT_PARTY_SKILL_MASTER_BONUS] = ch->GetPoint(POINT_PARTY_SKILL_MASTER_BONUS);
    		mPartyPoints[POINT_PARTY_HASTE_BONUS] = ch->GetPoint(POINT_PARTY_HASTE_BONUS);
    		mPartyPoints[POINT_PARTY_DEFENDER_BONUS] = ch->GetPoint(POINT_PARTY_DEFENDER_BONUS);
    
    		bool bComputePoints = false;
    		for (const auto& it : mPartyPoints)
    		{
    			if (it.second != 0)
    			{
    				ch->PointChange(it.first, -it.second);
    				bComputePoints = true;
    			}
    		}
    
    		if (bComputePoints)
    			ch->ComputePoints();
    
    		return;
    	}

     

     

    • Metin2 Dev 5
    • muscle 1
    • Love 7
  2. Repository Updated

    Changed QuestionDialog to QuestionDialog2.

    This change is due to the multi-line token which is not supported by all clients.
    To improve compatibility, the dialog message has been split into two tokens.
    The issue with the player's distance while the dialogue is open has also been fixed.

    Thanks for testing and reporting back.

    I recommend taking a look at all the code in the repository as I amended some issues on the first commit.

    • Metin2 Dev 9
    • Love 6
  3. As the title indicates, the following changes will extend the affect flags limit from 64 to 96.

    Server

    common/service.h

    Spoiler
    /// 1.
    // Add
    #define __EXTENDED_AFFECT_FLAG__

     

    game/src/affect_flag.h

    Spoiler
    /// 1.
    // Search
    	DWORD bits[2];
    
    	inline TAffectFlag() { bits[0] = 0; bits[1] = 0; }
    	inline TAffectFlag(DWORD v1, DWORD v2 = 0) { bits[0] = v1; bits[1] = v2; }
    
    // Replace with
    #if defined(__EXTENDED_AFFECT_FLAG__)
    	DWORD bits[3];
    
    	inline TAffectFlag() { bits[0] = 0; bits[1] = 0; bits[2] = 0; }
    	inline TAffectFlag(DWORD v1, DWORD v2 = 0, DWORD v3 = 0) { bits[0] = v1; bits[1] = v2; bits[2] = v3; }
    #else
    	DWORD bits[2];
    
    	inline TAffectFlag() { bits[0] = 0; bits[1] = 0; }
    	inline TAffectFlag(DWORD v1, DWORD v2 = 0) { bits[0] = v1; bits[1] = v2; }
    #endif
    
    /// 2.
    // Search
    		bits[0] = rhs.bits[0];
    		bits[1] = rhs.bits[1];
    
    // Add below
    #if defined(__EXTENDED_AFFECT_FLAG__)
    		bits[2] = rhs.bits[2];
    #endif
    
    /// 3.
    // Search
    	return lhs.bits[0] == rhs.bits[0] && lhs.bits[1] == rhs.bits[1];
    
    // Replace with
    #if defined(__EXTENDED_AFFECT_FLAG__)
    	return lhs.bits[0] == rhs.bits[0] && lhs.bits[1] == rhs.bits[1] && lhs.bits[2] == rhs.bits[2];
    #else
    	return lhs.bits[0] == rhs.bits[0] && lhs.bits[1] == rhs.bits[1];
    #endif

     

    game/src/building.cpp

    Spoiler
    /// 1.
    // Search @ CObject::EncodeInsertPacket
    	pack.dwAffectFlag[0] = unsigned(m_data.xRot);
    	pack.dwAffectFlag[1] = unsigned(m_data.yRot);
    
    // Add below
    #if defined(__EXTENDED_AFFECT_FLAG__)
    	pack.dwAffectFlag[2] = unsigned(m_data.zRot);
    #endif

     

    game/src/char.cpp

    Spoiler
    /// 1.
    // Search @ CHARACTER::EncodeInsertPacket
    	pack.dwAffectFlag[0] = m_afAffectFlag.bits[0];
    	pack.dwAffectFlag[1] = m_afAffectFlag.bits[1];
    
    // Add below
    #if defined(__EXTENDED_AFFECT_FLAG__)
    	pack.dwAffectFlag[2] = m_afAffectFlag.bits[2];
    #endif
    
    /// 2.
    // Search @ CHARACTER::UpdatePacket
    	pack.dwAffectFlag[0] = m_afAffectFlag.bits[0];
    	pack.dwAffectFlag[1] = m_afAffectFlag.bits[1];
    
    // Add below
    #if defined(__EXTENDED_AFFECT_FLAG__)
    	pack.dwAffectFlag[2] = m_afAffectFlag.bits[2];
    #endif

     

    game/src/packet.h

    Spoiler
    /// 1.
    // Search @ struct packet_add_char
    	DWORD dwAffectFlag[2];
    
    // Replace with
    #if defined(__EXTENDED_AFFECT_FLAG__)
    	DWORD dwAffectFlag[3];
    #else
    	DWORD dwAffectFlag[2];
    #endif
    
    /// 2.
    // Search @ struct packet_update_char
    	DWORD dwAffectFlag[2];
    
    // Replace with
    #if defined(__EXTENDED_AFFECT_FLAG__)
    	DWORD dwAffectFlag[3];
    #else
    	DWORD dwAffectFlag[2];
    #endif

     

    Client

    UserInterface/Locale_inc.h

    Spoiler
    /// 1.
    // Add
    #define ENABLE_EXTENDED_AFFECT_FLAG

     

    UserInterface/AffectFlagContainer.h

    Spoiler
    /// 1.
    // Search
    		BIT_SIZE = 64,
    
    // Replace with
    #if defined(ENABLE_EXTENDED_AFFECT_FLAG)
    		BIT_SIZE = 96,
    #else
    		BIT_SIZE = 64,
    #endif

     

    UserInterface/Packet.h

    Spoiler
    /// 1.
    // Search @ struct packet_add_char
    	DWORD dwAffectFlag[2];
    
    // Replace with
    #if defined(ENABLE_EXTENDED_AFFECT_FLAG)
    	DWORD dwAffectFlag[3];
    #else
    	DWORD dwAffectFlag[2];
    #endif
    
    /// 2.
    // Search @ struct packet_add_char2
    	DWORD dwAffectFlag[2];
    
    // Replace with
    #if defined(ENABLE_EXTENDED_AFFECT_FLAG)
    	DWORD dwAffectFlag[3];
    #else
    	DWORD dwAffectFlag[2];
    #endif
    
    /// 3.
    // Search @ struct packet_update_char
    	DWORD dwAffectFlag[2];
    
    // Replace with
    #if defined(ENABLE_EXTENDED_AFFECT_FLAG)
    	DWORD dwAffectFlag[3];
    #else
    	DWORD dwAffectFlag[2];
    #endif
    
    /// 4.
    // Search @ struct packet_update_char2
    	DWORD dwAffectFlag[2];
    
    // Replace with
    #if defined(ENABLE_EXTENDED_AFFECT_FLAG)
    	DWORD dwAffectFlag[3];
    #else
    	DWORD dwAffectFlag[2];
    #endif

     

    UserInterface/PythonNetworkStreamPhaseGameActor.cpp

    Spoiler
    /// 1.
    // Search @ CPythonNetworkStream::RecvCharacterAppendPacket
    	kNetActorData.m_kAffectFlags.CopyData(32, sizeof(chrAddPacket.dwAffectFlag[1]), &chrAddPacket.dwAffectFlag[1]);
    
    // Add below
    #if defined(ENABLE_EXTENDED_AFFECT_FLAG)
    	kNetActorData.m_kAffectFlags.CopyData(64, sizeof(chrAddPacket.dwAffectFlag[2]), &chrAddPacket.dwAffectFlag[2]);
    #endif
    
    /// 2.
    // Search @ CPythonNetworkStream::RecvCharacterAppendPacketNew
    	kNetActorData.m_kAffectFlags.CopyData(32, sizeof(chrAddPacket.dwAffectFlag[1]), &chrAddPacket.dwAffectFlag[1]);
    
    // Add below
    #if defined(ENABLE_EXTENDED_AFFECT_FLAG)
    	kNetActorData.m_kAffectFlags.CopyData(64, sizeof(chrAddPacket.dwAffectFlag[2]), &chrAddPacket.dwAffectFlag[2]);
    #endif
    
    /// 3.
    // Search @ CPythonNetworkStream::RecvCharacterUpdatePacket
    	kNetUpdateActorData.m_kAffectFlags.CopyData(32, sizeof(chrUpdatePacket.dwAffectFlag[1]), &chrUpdatePacket.dwAffectFlag[1]);
    
    // Add below
    #if defined(ENABLE_EXTENDED_AFFECT_FLAG)
    	kNetUpdateActorData.m_kAffectFlags.CopyData(64, sizeof(chrUpdatePacket.dwAffectFlag[2]), &chrUpdatePacket.dwAffectFlag[2]);
    #endif
    
    /// 4.
    // Search @ CPythonNetworkStream::RecvCharacterUpdatePacketNew
    	kNetUpdateActorData.m_kAffectFlags.CopyData(32, sizeof(chrUpdatePacket.dwAffectFlag[1]), &chrUpdatePacket.dwAffectFlag[1]);
    
    // Add below
    #if defined(ENABLE_EXTENDED_AFFECT_FLAG)
    	kNetUpdateActorData.m_kAffectFlags.CopyData(64, sizeof(chrUpdatePacket.dwAffectFlag[2]), &chrUpdatePacket.dwAffectFlag[2]);
    #endif

     

    Curiosity

    Spoiler
    	enum EAffectBits
    	{
    		AFFECT_YMIR,
    		AFFECT_INVISIBILITY,
    		AFFECT_SPAWN,
    
    		AFFECT_POISON,
    		AFFECT_SLOW,
    		AFFECT_STUN,
    
    		AFFECT_DUNGEON_READY, // ´øÀü¿¡¼­ Áغñ »óÅÂ
    		AFFECT_SHOW_ALWAYS, // AFFECT_DUNGEON_UNIQUE ¿¡¼­ º¯°æ(Ŭ¶óÀ̾ðÆ®¿¡¼­ ÄøµµÇÁö¾ÊÀ½)
    
    		AFFECT_BUILDING_CONSTRUCTION_SMALL,
    		AFFECT_BUILDING_CONSTRUCTION_LARGE,
    		AFFECT_BUILDING_UPGRADE,
    
    		AFFECT_MOV_SPEED_POTION,
    		AFFECT_ATT_SPEED_POTION,
    
    		AFFECT_FISH_MIND,
    
    		AFFECT_JEONGWI, // Àü±ÍÈ¥
    		AFFECT_GEOMGYEONG, // °Ë°æ
    		AFFECT_CHEONGEUN, // õ±ÙÃß
    		AFFECT_GYEONGGONG, // °æ°ø¼ú
    		AFFECT_EUNHYEONG, // ÀºÇü¹ý
    		AFFECT_GWIGEOM, // ±Í°Ë
    		AFFECT_GONGPO, // °øÆ÷
    		AFFECT_JUMAGAP, // ÁÖ¸¶°©
    		AFFECT_HOSIN, // È£½Å
    		AFFECT_BOHO, // º¸È£
    		AFFECT_KWAESOK, // Äè¼Ó
    		AFFECT_HEUKSIN, // Èæ½Å¼öÈ£
    		AFFECT_MUYEONG, // ¹«¿µÁø
    		AFFECT_REVIVE_INVISIBILITY, // ºÎÈ° ¹«Àû
    		AFFECT_FIRE, // Áö¼Ó ºÒ
    		AFFECT_GICHEON, // ±âõ ´ë°ø
    		AFFECT_JEUNGRYEOK, // Áõ·Â¼ú 
    		AFFECT_DASH, // ´ë½¬
    		AFFECT_PABEOP, // ÆĹý¼ú
    		AFFECT_FALLEN_CHEONGEUN, // ´Ù¿î ±×·¹À̵å õ±ÙÃß
    
    		AFFECT_POLYMORPH, // Æú¸®¸ðÇÁ
    
    		AFFECT_WAR_FLAG1,
    		AFFECT_WAR_FLAG2,
    		AFFECT_WAR_FLAG3,
    
    		AFFECT_CHINA_FIREWORK,
    		AFFECT_CANNOT_ATTACK,
    		AFFECT_CANNOT_USE_SKILL,
    		AFFECT_DS,
    
    		AFFECT_BLEEDING,
    		AFFECT_RED_POSSESSION,
    		AFFECT_BLUE_POSSESSION, 
    
    		AFFECT_UNK45, // ?
    		AFFECT_UNK46, // ?
    		AFFECT_UNBEATABLE,
    
    		AFFECT_BATTLE_FIELD_RANK1,
    		AFFECT_BATTLE_FIELD_RANK2,
    		AFFECT_BATTLE_FIELD_RANK3,
    		AFFECT_TARGET_VICTIM,
    
    		AFFECT_UNK52, // ?
    
    		AFFECT_ELECTRIC_SHOCK,
    		AFFECT_CONFUSION,
    
    		AFFECT_SOUL_RED,
    		AFFECT_SOUL_BLUE,
    		AFFECT_SOUL_MIX,
    
    		AFFECT_UNK58, // ?
    		AFFECT_BATTLE_ROYALE_SLOW_1,
    		AFFECT_BATTLE_ROYALE_SLOW_2,
    		AFFECT_BATTLE_ROYALE_SLOW_3,
    
    		AFFECT_UNK62, // ?
    		AFFECT_PASWAE,
    		AFFECT_UNK64, // ?
    
    		AFFECT_FLAG_ELEMENT_BUFF_CRACK_NONE,
    		AFFECT_FLAG_ELEMENT_BUFF_CRACK_FIRE,
    		AFFECT_FLAG_ELEMENT_BUFF_CRACK_ICE,
    		AFFECT_FLAG_ELEMENT_BUFF_CRACK_ELECT,
    		AFFECT_FLAG_ELEMENT_BUFF_CRACK_WIND,
    		AFFECT_FLAG_ELEMENT_BUFF_CRACK_EARTH,
    		AFFECT_FLAG_ELEMENT_BUFF_CRACK_DARK,
    
    		AFFECT_FLAG_UNK72, // guild_battle (blue)
    		AFFECT_FLAG_UNK73, // guild_battle (yellow)
    		AFFECT_FLAG_UNK74, // guild_battle (green)
    		AFFECT_FLAG_UNK75, // guild_battle (red)
    
    		AFFECT_FLAG_UNK76, // ?
    		AFFECT_FLAG_UNK77, // ?
    		AFFECT_FLAG_UNK78, // ?
    		AFFECT_FLAG_UNK79, // ?
    
    		AFFECT_CHEONUN_INVINCIBILITY,
    		AFFECT_CHEONUN_NORMAL,
    		AFFECT_CHEONUN_MASTER,
    		AFFECT_CHEONUN_GRAND_MASTER,
    		AFFECT_CHEONUN_PERFECT_MASTER,
    
    		AFFECT_NUM,
    
    		AFFECT_HWAYEOM = AFFECT_GEOMGYEONG,
    	};

     

    .

    • Metin2 Dev 21
    • Good 5
    • Love 1
    • Love 16
  4. 6 hours ago, WeedHex said:

    It's different from official 

    I know they updated the quest part and removed the quest items because they are no longer in the proto. However, they rarely enable this event on the server so testing it fully is limited and that's why I made based on the old version but if you are sure about something please describe what is missing.

    • Love 1
  5. 1 hour ago, xGalardo said:

    0614 21:23:26078 :: NameError
    0614 21:23:26078 :: : 
    0614 21:23:26078 :: global name 'questbutton_max' is not defined
    0614 21:23:26078 :: 

    If dont have questbutton_max, just change to self.QUEST_BUTTON_MAX_NUM

    Both 2015 & 2018 root files have the mentioned lines from the guide but if that works for you, sure...

    On 6/13/2023 at 2:43 PM, Gurgarath said:

    Thank you for your release! It is really cool! Also, this is one of the new addition that I feel the most weird about, it looks like a bug more than a feature somehow

    I agree, when I first saw it on the live server it looked weird because the text didn't match the window length but at least for the buttons it's not that bad since it has space for bigger texts. I wonder how long it will take them until they adjust all the "[ENTER]" tokens in the locale_quest.txt or completely remove them to make the text match the width of the window and break line correctly.

    • Love 1
  6. Because why not...
    Only a few changes are necessary, and the input width has remained unchanged.

     

    Preview of the Quest Width Expansion
     

    Guide

    Client

    Spoiler

    Client
    root/uiQuest.py

    Spoiler
    ''' 1. '''
    # Search @ def MakeNextButton
    		b.SetSize(100, 26)
    		b.SetPosition(self.sx + self.board.GetWidth() / 2 - 50, self.sy + yPos)
    
    # Replace with
    		if app.ENABLE_QUEST_WIDTH_EXPANSION:
    			b.SetSize(280, 26)
    			b.SetPosition(self.sx + self.board.GetWidth() / 2 - 140, self.sy + yPos)
    		else:
    			b.SetSize(100, 26)
    			b.SetPosition(self.sx + self.board.GetWidth() / 2 - 50, self.sy + yPos)
    
    ''' 2. '''
    # Search @ def MakeQuestion
    		self.prevbutton.SetPosition(self.sx + self.board.GetWidth() / 2 - 164, self.sy + (event.GetLineCount(self.descIndex) + questbutton_max - 1) * 16 + 20 + 5)
    
    # Replace with
    		if app.ENABLE_QUEST_WIDTH_EXPANSION:
    			self.prevbutton.SetPosition(self.sx + self.board.GetWidth() / 2 - 340, self.sy + (event.GetLineCount(self.descIndex) + questbutton_max - 1) * 16 + 20 + 5)
    		else:
    			self.prevbutton.SetPosition(self.sx + self.board.GetWidth() / 2 - 164, self.sy + (event.GetLineCount(self.descIndex) + questbutton_max - 1) * 16 + 20 + 5)
    
    ''' 3. '''
    # Search @ def MakeQuestion
    		self.nextbutton.SetPosition(self.sx + self.board.GetWidth() / 2 + 112, self.sy + (event.GetLineCount(self.descIndex) + questbutton_max - 1) * 16 + 20 + 5)
    
    # Replace with
    		if app.ENABLE_QUEST_WIDTH_EXPANSION:
    			self.nextbutton.SetPosition(self.sx + self.board.GetWidth() / 2 + 288, self.sy + (event.GetLineCount(self.descIndex) + questbutton_max - 1) * 16 + 20 + 5)
    		else:
    			self.nextbutton.SetPosition(self.sx + self.board.GetWidth() / 2 + 112, self.sy + (event.GetLineCount(self.descIndex) + questbutton_max - 1) * 16 + 20 + 5)
    
    ''' 4. '''
    # Search @ def MakeEachButton
    			button.SetSize(200, 26)
    			button.SetPosition(self.sx + self.board.GetWidth() / 2 - 100, self.sy + (event.GetLineCount(self.descIndex) + i * 2) * 16 + 20 + 5)
    
    # Replace with
    			if app.ENABLE_QUEST_WIDTH_EXPANSION:
    				button.SetSize(560, 26)
    				button.SetPosition(self.sx + self.board.GetWidth() / 2 - 280, self.sy + (event.GetLineCount(self.descIndex) + i * 2) * 16 + 20 + 5)
    			else:
    				button.SetSize(200, 26)
    				button.SetPosition(self.sx + self.board.GetWidth() / 2 - 100, self.sy + (event.GetLineCount(self.descIndex) + i * 2) * 16 + 20 + 5)

     

    Client
    UIScript/QuestDialog.py

    Spoiler
    import app
    
    ROOT = "d:/ymir work/ui/public/"
    
    if app.ENABLE_QUEST_WIDTH_EXPANSION:
    	QUEST_BOARD_WIDTH_ADJUSTMENT_VALUE = 2 ## 퀘스트 보드 폭 조정 값
    
    window = {
    	"name" : "QuestDialog",
    	"style" : ("float",),
    
    	"x" : 0,
    	"y" : 0,
    
    	"width" : 800,
    	"height" : 450,
    
    	"children" :
    	(
    		{
    			"name" : "board",
    			"type" : "thinboard",
    			"style" : ("attach", "ignore_size",),
    
    			"x" : 0,
    			"y" : 0,
    
    			"horizontal_align" : "center",
    			"vertical_align" : "center",
    
    			"width" : 350,
    			"height" : 300,
    		},
    	),
    }
    
    if app.ENABLE_QUEST_WIDTH_EXPANSION:
    	window["children"][0]["width"] = window["children"][0]["width"] * QUEST_BOARD_WIDTH_ADJUSTMENT_VALUE

     

     

    Srcs

    Spoiler

    Client
    UserInterface/Locale_inc.h

    /// 1.
    // Add anywhere
    #define ENABLE_QUEST_WIDTH_EXPANSION

    Client
    UserInterface/PythonApplicationModule.cpp

    /// Search
    	PyModule_AddIntConstant(poModule, "CAMERA_STOP", CPythonApplication::CAMERA_STOP);
    
    // Add below
    #if defined(ENABLE_QUEST_WIDTH_EXPANSION)
    	PyModule_AddIntConstant(poModule, "ENABLE_QUEST_WIDTH_EXPANSION", 1);
    #else
    	PyModule_AddIntConstant(poModule, "ENABLE_QUEST_WIDTH_EXPANSION", 0);
    #endif

     

    • Metin2 Dev 43
    • kekw 1
    • Good 9
    • muscle 1
    • Love 1
    • Love 15
  7. I put together an AIO (all in one) package for those who might need.

    • Packs 23.1.4
    • Protos 23.1.2
    • Root Metadata & Dumped Binary 23.1.2

    I also took the liberty to add the `data` folder for monsters and pc motions for the server.
    And, if you are interested in finding guild marks, I dumped them in the `mark/` folder.

    Folder Structure

    Spoiler
    ├─── Client/
    │   ├─── bgm/
    │   │   └─── (Background Music)
    │   ├─── lib/
    │   │   └─── (Python Library)
    │   ├─── mark/
    │   │   └─── (Cropped Guild Marks)
    │   ├─── pack/
    │   │   ├─── (Packs 23.1.4)
    │   │   ├─── ...
    │   │   └─── root/
    │   │       └─── (Metadata 23.1.2)
    │   ├─── upload/
    │   │   └─── (Default Guild Marks)
    │   ├─── logo1.avi
    │   ├─── logo2.avi
    │   ├─── metin2.ico
    │   ├─── metin2client.dat
    │   └─── metin2client_v23.1.2_x86.exe
    └─── Server/
        └─── share/
            ├─── data/
            │   └─── (Motions)
            └─── package/
                └─── (Hybridcrypt Keys 23.1.4)
    

     

    External Link

    MEGA

    This is the hidden content, please

    M2DL

    This is the hidden content, please


    Special thanks to those people who helped in making this archive.

    • Metin2 Dev 187
    • Eyes 2
    • Lmao 1
    • Good 52
    • Love 6
    • Love 93
  8. @nicohare711

    Spoiler
    4 minutes ago, nicohare711 said:

    ¿Cómo puedo arreglar esto? @ Owsap

    char_item.cpp: en la función miembro 'bool CHARACTER::UseItemEx(LPITEM, TItemPos)':
    char_item.cpp:6409:74: error: el uso de 'auto' en la declaración de parámetros lambda solo está disponible con -std=c++14 o -std=gnu++14
           auto it = std::find_if(soul_map.begin(), soul_map.end(), [&](const auto& kv_pair) { ^~~~
                                                                              char_item.cpp
    : En la función lambda:
    char_item. cpp:6410:23: error: solicitud de miembro 'segundo' en 'kv_pair', que no es de tipo de clase 'const int'
            return kv_pair.segundo.punto_tipo == afecto.bApplyOn;
                           ^~~~~~
    En archivo incluido desde /usr/local/lib/gcc8/include/c++/bits/stl_algobase.h:71,
                     de /usr/local/lib/gcc8/include/c++/bits/char_traits.h:39,
                     de /usr/local/lib/gcc8/include/c++/string:40,
                     de ../../../ Externo/incluir/msl/utils.h:18,
                     desde ../../common/utils.h:1,
                     desde stdafx.h:15,
                     desde char_item.cpp:1:
    /usr/local/lib/gcc8/ include/c++/bits/predefined_ops.h: En instanciación de 'bool __gnu_cxx::__ops::_Iter_pred<_Predicate>::operator()(_Iterator) [with _Iterator = std::__detail::_Node_iterator<std::pair< const carácter sin firmar, CARÁCTER::UseItemEx(LPITEM, TItemPos)::SoulData>, false, false>; _Predicado = CARÁCTER::UseItemEx(LPITEM, TItemPos)::<lambda(const int&)>]':
    /usr/local/lib/gcc8/include/c++/bits/stl_algo.h:104:42: requerido de '_InputIterator std::__find_if(_InputIterator, _InputIterator, _Predicate, std::input_iterator_tag) [con _InputIterator = std:: __detail::_Node_iterator<std::pair<const unsigned char, CHARACTER::UseItemEx(LPITEM, TItemPos)::SoulData>, false, false>; _Predicado = __gnu_cxx::__ops::_Iter_pred<CHARACTER::UseItemEx(LPITEM, TItemPos)::<lambda(const int&)> >]'
    /usr/local/lib/gcc8/include/c++/bits/stl_algo.h: 161:23: requerido de '_Iterator std::__find_if(_Iterator, _Iterator, _Predicate) [with _Iterator = std::__detail::_Node_iterator<std::pair<const unsigned char, CHARACTER::UseItemEx(LPITEM, TItemPos): :SoulData>, falso, falso>; _Predicado = __gnu_cxx::__ops::_Iter_pred<CHARACTER::UseItemEx(LPITEM, TItemPos)::
    /usr/local/lib/gcc8/include/c++/bits/stl_algo.h:3930:28: requerido de '_IIter std::find_if(_IIter, _IIter, _Predicate) [with _IIter = std::__detail::_Node_iterator< std::pair<const unsigned char, CHARACTER::UseItemEx(LPITEM, TItemPos)::SoulData>, false, false>; _Predicate = CHARACTER::UseItemEx(LPITEM, TItemPos)::<lambda(const int&)>]'
    char_item.cpp:6411:9: requerido desde aquí
    /usr/local/lib/gcc8/include/c++/bits/predefined_ops. h:283:11: error: no hay coincidencia para llamar a '(CHARACTER::UseItemEx(LPITEM, TItemPos)::<lambda(const int&)>) (std::pair<const unsigned char, CHARACTER::UseItemEx(LPITEM , TItemPos)::SoulData>&)'
      { return bool(_M_pred(*__it)); }
               ^~~~~~~~~~~~~~~~~~~~
    char_item.cpp:6409:87: nota: candidato: 'CHARACTER::UseItemEx(LPITEM, TItemPos)::<lambda(const int&)>' auto it =
           std::find_if(soul_map.begin(), soul_map.end( ), [&](const auto& kv_pair) {
                                                                                           ^
    char_item.cpp:6409:87: nota: ninguna conversión conocida para el argumento 1 de 'std::pair<const unsigned char, CHARACTER::UseItemEx(LPITEM, TItemPos):: SoulData>' a 'const int&'
    gmake: *** [Makefile:185: .obj/char_item.o] Error 1
    root@jailsrc:/usr/src/Server/game/src #

     

    To fix this, you can either update the compiler flag to use C++14 or later by adding std=c++14 in your Makefile, or you can specify the data type of the lambda parameter explicitly instead of using auto.

    For example, instead of:

    auto it = std::find_if(soul_map.begin(), soul_map.end(), [&](const auto& kv_pair) {
        return kv_pair.second.point_type == affect.bApplyOn;
    });

    You can try:

    auto it = std::find_if(soul_map.begin(), soul_map.end(), [&](const std::pair<const BYTE, SoulData>& kv_pair) {
        return kv_pair.second.point_type == affect.bApplyOn;
    });

     

    I will not reply to every single error you could encounter due to C++ version warnings. Please consider upgrading your version to at least C++14.

     

  9. As the title indicates, I'm sharing with everyone my test script for creating UI's, systems, etc...
    The advantage of this tool is that you don't need to restart your client every time you make a change which time is crucial.

    This tool will certainly help you tremendously if you know how to use it correctly.
    I will not go in much detail on how to use it because it's very basic and understandable.

    As long as you respect the primary class name and contain the default methods in the class, you're good.
    Otherwise, if you know what you're doing then you can modify the script to your liking.
     

    uiTestScript.py

    #
    # File : uiTestScript.py
    # Author : Owsap
    # Copyright (C) 2023 Owsap Development
    #
    
    import app
    import ui
    import wndMgr
    import uiToolTip
    import localeInfo
    
    """
    # Example:
    # uiTest.py
    class TestWindow(ui.ScriptWindow):
    	def __init__(self):
    		ui.ScriptWindow.__init__(self)
    
    	def __del__(self):
    		ui.ScriptWindow.__del__(self)
    
    	def Show(self):
    		ui.ScriptWindow.Show(self)
    
    	def Hide(self):
    		ui.ScriptWindow.Hide(self)
    """
    
    def LoadModule(name):
    	import sys, imp
    
    	if name in sys.modules: del sys.modules[name]
    	if name in locals(): del locals()[name]
    	if name in globals(): del globals()[name]
    
    	module_info = imp.find_module(name)
    	module = imp.load_module(name, *module_info)
    
    	if not module in locals():
    		locals()[name] = module
    
    	if not module in globals():
    		globals()[name] = module
    
    	return module
    
    class TestScript(ui.BoardWithTitleBar):
    
    	BOARD_WIDTH = 230
    	BOARD_HEIGHT = 80
    
    	SLOT_WIDTH = 150
    
    	def __init__(self):
    		ui.BoardWithTitleBar.__init__(self)
    
    		self.wndScript = None
    
    		## Item ToolTip
    		self.tooltipItem = uiToolTip.ItemToolTip()
    		self.tooltipItem.Hide()
    
    		## Board
    		self.SetSize(self.BOARD_WIDTH, self.BOARD_HEIGHT)
    		self.SetPosition(0, wndMgr.GetScreenHeight() - self.BOARD_HEIGHT)
    
    		self.AddFlag("movable")
    		self.SetTitleName("Test Script")
    		self.SetCloseEvent(self.Hide)
    		self.Show()
    
    		## Board > Slot Bar
    		slotBar = ui.SlotBar()
    		slotBar.SetParent(self)
    		slotBar.SetSize(self.SLOT_WIDTH, 18)
    		slotBar.SetPosition(13, 32)
    		slotBar.Show()
    		self.slotBar = slotBar
    
    		## Board > Slot Bar > EditLine
    		editLine = ui.EditLine()
    		editLine.SetParent(self.slotBar)
    		editLine.SetSize(self.SLOT_WIDTH, 18)
    		editLine.SetPosition(4, 3)
    		editLine.SetMax(30)
    		editLine.SetText("uiTest")
    		editLine.Show()
    		self.editLine = editLine
    
    		## Board > Load Button
    		loadButton = ui.Button()
    		loadButton.SetParent(self)
    		loadButton.SetPosition(self.SLOT_WIDTH + 23, 32)
    		loadButton.SetUpVisual("d:/ymir work/ui/public/small_button_01.sub")
    		loadButton.SetOverVisual("d:/ymir work/ui/public/small_button_02.sub")
    		loadButton.SetDownVisual("d:/ymir work/ui/public/small_button_03.sub")
    		loadButton.SetEvent(self.__OnClickLoadButton)
    		loadButton.SetText("Load")
    		loadButton.Show()
    		self.loadButton = loadButton
    
    		## Board > TextLine (Print Mouse Position)
    		self.textLine = ui.TextLine()
    		self.textLine.SetParent(self)
    		self.textLine.SetFontName(localeInfo.UI_DEF_FONT)
    		self.textLine.SetWindowHorizontalAlignCenter()
    		self.textLine.SetHorizontalAlignCenter()
    		self.textLine.SetWindowVerticalAlignBottom()
    		self.textLine.SetVerticalAlignBottom()
    		self.textLine.SetPosition(0, 13)
    		self.textLine.SetText("Tip: Press F10 to load / reload.")
    		self.textLine.Show()
    
    	def __del__(self):
    		ui.BoardWithTitleBar.__del__(self)
    
    		del self.tooltipItem
    		del self.tooltipSkill
    
    		self.slotBar = None
    		self.editLine = None
    		self.loadButton = None
    		self.textLine = None
    
    	def Show(self):
    		ui.BoardWithTitleBar.Show(self)
    
    	def Hide(self):
    		ui.BoardWithTitleBar.Hide(self)
    
    	def OnKeyDown(self, key):
    		if key == app.DIK_F10:
    			self.__OnClickLoadButton()
    
    	def __OnClickLoadButton(self):
    		module_name = self.editLine.GetText()
    		if not module_name:
    			print("Empty module name")
    			return
    
    		if self.wndScript:
    			self.wndScript.Hide()
    			del self.wndScript
    
    		self.wndScript = None
    
    		try:
    			module = LoadModule(module_name)
    		except Exception as error:
    			print("Error loading module: %s" % str(error))
    			return
    
    		self.wndScript = module.TestWindow()
    		#self.wndScript.SetItemToolTip(self.tooltipItem)
    		self.wndScript.Show()
    
    wndTestScript = TestScript()
    wndTestScript.Show()

     

    Requirements

    In order to use this tool, you must make sure it executes the module when the client is started, if you're familiar with loginInfo.py, it's practically the same thing.
    Make sure you only enable this feature on a debug build for testing purposes.
    Simply add the script above on your main client directory and you're ready.

    """ 1. @ introLogin.py """
    # Search
    		if musicInfo.loginMusic != "":
    			snd.SetMusicVolume(systemSetting.GetMusicVolume())
    			snd.FadeInMusic("BGM/"+musicInfo.loginMusic)
    
    # Add above
    		self.__LoadTestFile("uiTestScript.py")
    
    """ 2. @ introLogin.py """
    # Search
    	def PopupDisplayMessage(self, msg):
    
    # Add above
    	def __LoadTestFile(self, fileName):
    		try:
    			testScriptDict = {}
    			execfile(fileName, testScriptDict)
    		except:
    			pass

    Here is an example of the uiTest.py file that will be loaded but you can load any other module that you add in your client main directory.

    Spoiler
    #
    # File : uiTest.py
    #
    
    import ui
    
    class TestWindow(ui.ScriptWindow):
    	def __init__(self):
    		ui.ScriptWindow.__init__(self)
    		self.__LoadWindow()
    
    	def __del__(self):
    		ui.ScriptWindow.__del__(self)
    
    	def __LoadWindow(self):
    		try:
    			self.__LoadScript("UIScript/SelectItemWindow.py")
    		except:
    			import exception
    			exception.Abort("TestWindow.LoadWindow.LoadObject")
    
    	def __LoadScript(self, fileName):
    		pyScrLoader = ui.PythonScriptLoader()
    		pyScrLoader.LoadScriptFile(self, fileName)
    
    	def Show(self):
    		ui.ScriptWindow.Show(self)
    
    	def Hide(self):
    		ui.ScriptWindow.Hide(self)

     

    Enjoy.

    • Metin2 Dev 14
    • Good 3
    • Love 1
    • Love 10
×
×
  • 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.