Jump to content

ForgetMyAttacker - Skill Eunhyung (Ninja)


Recommended Posts

Hey,

 

In different Games always Ninja's have the ability to hide example in  Wow when Rogue hide the Monsters losing their aggro, but in Metin2 when you hide with the Skill Eunhyung the Monster still have aggro on you, this seems little wrong, Hided and Monster still can reach you? Maybe because you can use Poison / Bleeding / Fire that make this little bit difficult, but i think i come up with a solution. I think the only part that missing is when you clean the Target from the Monster to move again back but is not really necessary.

 

* I Added also for GMs in /in command the function.

In the Tutorial bellow, you can find the Function ForgetMyAttacker with my improvements.

 

Spoiler

in service.h or CommonDefines.h: add:


#define __EUNHYUNG_FORGET_MY_ATTACKER__

char.h

 

// Search for:


void                ForgetMyAttacker();

// Replace with:
 


#ifdef __EUNHYUNG_FORGET_MY_ATTACKER__
        void                ForgetMyAttacker(bool bIsEunhyung = false);
#else
        void                ForgetMyAttacker();
#endif

char_battle.cpp

 

 

// Search for:


struct FuncForgetMyAttacker
{
    [...]
}

// Replace with:
struct FuncForgetMyAttacker


{
    LPCHARACTER m_ch;

#ifdef __EUNHYUNG_FORGET_MY_ATTACKER__
    bool m_bIsEunhyung;

    FuncForgetMyAttacker(LPCHARACTER ch, bool bIsEunhyung) { m_ch = ch, m_bIsEunhyung = bIsEunhyung; }
#else
    FuncForgetMyAttacker(LPCHARACTER ch) { m_ch = ch; }
#endif

    void operator()(LPENTITY ent)
    {
        if (ent->IsType(ENTITY_CHARACTER))
        {
            LPCHARACTER ch = (LPCHARACTER) ent;

            if (ch->IsPC())
                return;

            if (ch->m_kVIDVictim == m_ch->GetVID())
            {
#ifdef __EUNHYUNG_FORGET_MY_ATTACKER__
                if (m_bIsEunhyung)
                {
                    if (ch->IsAffectFlag(AFF_POISON))
                        ch->RemovePoison();

#ifdef ENABLE_WOLFMAN_CHARACTER
                    if (ch->IsAffectFlag(AFF_BLEEDING))
                        ch->RemoveBleeding();
#endif
                    if (ch->IsAffectFlag(AFF_FIRE))
                        ch->RemoveFire();
                }
#endif

                ch->SetVictim(NULL);
            }
        }
    }
};

// Search for:


void CHARACTER::ForgetMyAttacker()
{
    [...]
}

// Replace with:


#ifdef __EUNHYUNG_FORGET_MY_ATTACKER__
void CHARACTER::ForgetMyAttacker(bool bIsEunhyung)
#else
void CHARACTER::ForgetMyAttacker()
#endif
{
    LPSECTREE pSec = GetSectree();

    if (pSec)
    {
#ifdef __EUNHYUNG_FORGET_MY_ATTACKER__
        FuncForgetMyAttacker f(this, bIsEunhyung);
#else
        FuncForgetMyAttacker f(this);
#endif

        pSec->ForEachAround(f);
    }

#ifdef __EUNHYUNG_FORGET_MY_ATTACKER__
    if (!bIsEunhyung)
        ReviveInvisible(5);
#else
    ReviveInvisible(5);
#endif
}

char_skill.cpp
 

// Search for:
bool CHARACTER::UseSkill(DWORD dwVnum, LPCHARACTER pkVictim, bool bUseGrandMaster)


{
    [...]
}

// Paste the code in the function:


#ifdef __EUNHYUNG_FORGET_MY_ATTACKER__
    if (dwVnum == SKILL_EUNHYUNG)
        ForgetMyAttacker(true);
#endif// Result:


// Don't replace your code with this, hope you have brain.


bool CHARACTER::UseSkill(DWORD dwVnum, LPCHARACTER pkVictim, bool bUseGrandMaster)
{
    [...] // Rest of the code.

#ifdef __EUNHYUNG_FORGET_MY_ATTACKER__
    if (dwVnum == SKILL_EUNHYUNG)
        ForgetMyAttacker(true);
#endif

    m_dwLastSkillTime = get_dword_time();
    m_dwLastSkillVnum = dwVnum;

    return true;
}


cmd_gm.cpp

// Seach for:
 


ACMD(do_invisibility)
{
    [...]
}

// Replace with:


ACMD(do_invisibility)
{
    if (ch->IsAffectFlag(AFF_INVISIBILITY))
        ch->RemoveAffect(AFFECT_INVISIBILITY);
    else
    {
        ch->AddAffect(AFFECT_INVISIBILITY, POINT_NONE, 0, AFF_INVISIBILITY, INFINITE_AFFECT_DURATION, 0, true);

#ifdef __EUNHYUNG_FORGET_MY_ATTACKER__
        ch->ForgetMyAttacker(true);
#endif
    }
}

 

 

  • Dislove 1
  • Confused 1
  • Lmao 1
  • Good 1
  • Love 22
Link to comment
Share on other sites

Right now I'm not able to test it  but I think this is what you were looking for:

in char.h add

friend struct RemoveInvisibleVictim;

then in char_skill.cpp add this struct

struct RemoveInvisibleVictim{
	RemoveInvisibleVictim(LPCHARACTER pkOldVictim)
	{
		m_pkOldVictim = pkOldVictim;
	}
	void operator () (LPENTITY ent)
	{
		if (ent->IsType(ENTITY_CHARACTER))
		{
			LPCHARACTER pkChr = (LPCHARACTER) ent;
			if (pkChr && pkChr->IsMonster())
			{
				LPCHARACTER pkVictim = pkChr->GetVictim();
				if (pkVictim && pkVictim == m_pkOldVictim)
				{
					LPCHARACTER new_victim =  pkChr->GetNearestVictim(pkChr);
					if (new_victim != m_pkOldVictim)    			//NEED TO ADD CHECK HERE AFF_EUNHYUNG WILL BE SET LATER
						pkChr->SetVictim(new_victim);
					else
						pkChr->SetVictim(NULL);
				}
			}
		}
	}
	LPCHARACTER m_pkOldVictim;
};

then in function (char_skill.cpp) int CHARACTER::ComputeSkill(DWORD dwVnum, LPCHARACTER pkVictim, BYTE bSkillLevel) add

if (dwVnum == SKILL_EUNHYUNG && GetSectree())
{
	RemoveInvisibleVictim f(this);
	GetSectree()->ForEachAround(f);
}


For any problem please reply providing as much informations as possible.

 

Link to comment
Share on other sites

27 minutes ago, OtherChoice said:

Right now I'm not able to test it  but I think this is what you were looking for:

in char.h add

friend struct RemoveInvisibleVictim;

then in char_skill.cpp add this struct


struct RemoveInvisibleVictim{
	RemoveInvisibleVictim(LPCHARACTER pkOldVictim)
	{
		m_pkOldVictim = pkOldVictim;
	}
	void operator () (LPENTITY ent)
	{
		if (ent->IsType(ENTITY_CHARACTER))
		{
			LPCHARACTER pkChr = (LPCHARACTER) ent;
			if (pkChr && pkChr->IsMonster())
			{
				LPCHARACTER pkVictim = pkChr->GetVictim();
				if (pkVictim && pkVictim == m_pkOldVictim)
				{
					LPCHARACTER new_victim =  pkChr->GetNearestVictim(pkChr);
					pkChr->SetVictim(new_victim);
				}
			}
		}
	}

	LPCHARACTER m_pkOldVictim;

}

then in function (char_skill.cpp) int CHARACTER::ComputeSkill(DWORD dwVnum, LPCHARACTER pkVictim, BYTE bSkillLevel) add


if (dwVnum == SKILL_EUNHYUNG && GetSectree())
{
	RemoveInvisibleVictim f(this);
	GetSectree()->ForEachAround(f);
}


For any problem please reply providing as much informations as possible.

 

 

The function already exists in Source, i just improve it a little bit for Ninja's, Your function is not really necessary, also i think is wrong when the Monster losing their aggro to find the nearest victim cause if you are in a group and attacking Boss, if the Ninja has the aggro and after using the Eunhyung Skill, is gonna take a aggro from the nearest player, and maybe the random one couldn't tank the boss, so after when Boss losing their aggro is not Regen back his HP for some seconds as i saw, so someone else could take the aggro before that and continue basic to my idea func.

Link to comment
Share on other sites

2 minutes ago, HITRON said:

 

The function already exists in Source, i just improve it a little bit for Ninja's, Your function is not really necessary, also i think is wrong when the Monster losing their aggro to find the nearest victim cause if you are in a group and attacking Boss, if the Ninja was the aggro and after using the Eunhyung Skill, is gonna take a aggro from the nearest player, and maybe the random one couldn't tank the boss, so after when Boss losing their aggro is not losing Regen back the HP for some seconds as i saw, so someone else could take the aggro before that and continue.

The aggro taken is from a damage map iterator hence the only players found by the function are player attacking the monster, moreover you can just create a different method for selecting your next target just the way you select the nearest. The already existing functions checks for invisibility affect when selecting target and they have no knowledge about a potential change of the affect, that's why when you enter invisibility you need to check sectree to tell who and who's not attacking you and inform them about your new state.
 

example code for GetHighestDpsVictim(LPCHARACTER pkChr)
{
	if (NULL == pkChr)
	pkChr = this;

	float fMinDist = 99999.0f;
	float fMaxDamage = 0.0f;
	LPCHARACTER pkVictim = NULL;

	TDamageMap::iterator it = m_map_kDamage.begin();

	// 일단 주위에 없는 사람을 걸러 낸다.
	while (it != m_map_kDamage.end())
	{
		const VID & c_VID = it->first;
		float fDamage = it->second.iTotalDamage;
		++it;

		LPCHARACTER pAttacker = CHARACTER_MANAGER::instance().Find(c_VID);

		if (!pAttacker)
			continue;

		if (pAttacker->IsAffectFlag(AFF_EUNHYUNG) || 
				pAttacker->IsAffectFlag(AFF_INVISIBILITY) ||
				pAttacker->IsAffectFlag(AFF_REVIVE_INVISIBLE))
			continue;

		float fDist = DISTANCE_APPROX(pAttacker->GetX() - pkChr->GetX(), pAttacker->GetY() - pkChr->GetY());

		if (fDist < fMinDist && !pAttacker->IsDead() && fDamage > fMaxDamage)
		{
			pkVictim = pAttacker;
			fMaxDamage = fDamage;
		}
	}

	return pkVictim;
}

 

  • Love 1
Link to comment
Share on other sites

44 minutes ago, OtherChoice said:

The aggro taken is from a damage map iterator hence the only players found by the function are player attacking the monster, moreover you can just create a different method for selecting your next target just the way you select the nearest. The already existing functions checks for invisibility affect when selecting target and they have no knowledge about a potential change of the affect, that's why when you enter invisibility you need to check sectree to tell who and who's not attacking you and inform them about your new state.
 


example code for GetHighestDpsVictim(LPCHARACTER pkChr)
{
	if (NULL == pkChr)
	pkChr = this;

	float fMinDist = 99999.0f;
	float fMaxDamage = 0.0f;
	LPCHARACTER pkVictim = NULL;

	TDamageMap::iterator it = m_map_kDamage.begin();

	// 일단 주위에 없는 사람을 걸러 낸다.
	while (it != m_map_kDamage.end())
	{
		const VID & c_VID = it->first;
		float fDamage = it->second.iTotalDamage;
		++it;

		LPCHARACTER pAttacker = CHARACTER_MANAGER::instance().Find(c_VID);

		if (!pAttacker)
			continue;

		if (pAttacker->IsAffectFlag(AFF_EUNHYUNG) || 
				pAttacker->IsAffectFlag(AFF_INVISIBILITY) ||
				pAttacker->IsAffectFlag(AFF_REVIVE_INVISIBLE))
			continue;

		float fDist = DISTANCE_APPROX(pAttacker->GetX() - pkChr->GetX(), pAttacker->GetY() - pkChr->GetY());

		if (fDist < fMinDist && !pAttacker->IsDead() && fDamage > fMaxDamage)
		{
			pkVictim = pAttacker;
			fMaxDamage = fDamage;
		}
	}

	return pkVictim;
}

 

Base to your idea to find next victim, then yes, otherwise doesn't matter to check if anybody else is invisible cause Monsters can't attack invisible targets at the first place it happens only when you attack and hiding after, but you logic is good in your case.

 

@Syreldar I tested it and seems that is passing the arg correctly (ifdef).

Link to comment
Share on other sites

I think I did not explained my ideas correctly or clearly, I apologize. This is NOT a struct to check if anybody else is invisible, as you said. This struct checks who's attacking a said target, and you call it when that target enters invisibility. So basically when someone uses stealth skill, the server checks if any monster is attacking him, IF so it tells them to find someone else to attack. The check is on the monster attacking a target who's getting invisible.

  • Love 1
Link to comment
Share on other sites

@HITRON After a brief test i noticed that the flag AFF_EUNHYUNG is set after skill computation, so it needs a fix, i will edit the first post as well.
Full fix tested and working

in char.h add

friend struct RemoveInvisibleVictim;

then in char_skill.cpp add this struct

struct RemoveInvisibleVictim{
	RemoveInvisibleVictim(LPCHARACTER pkOldVictim)
	{
		m_pkOldVictim = pkOldVictim;
	}
	void operator () (LPENTITY ent)
	{
		if (ent->IsType(ENTITY_CHARACTER))
		{
			LPCHARACTER pkChr = (LPCHARACTER) ent;
			if (pkChr && pkChr->IsMonster())
			{
				LPCHARACTER pkVictim = pkChr->GetVictim();
				if (pkVictim && pkVictim == m_pkOldVictim)
				{
					LPCHARACTER new_victim =  pkChr->GetNearestVictim(pkChr); // or optional GetHighestDpsVictim(pkChr)
					if (new_victim != m_pkOldVictim)    			//NEED TO ADD CHECK HERE AFF_EUNHYUNG WILL BE SET LATER
						pkChr->SetVictim(new_victim);
					else
						pkChr->SetVictim(NULL);
				}
			}
		}
	}
	LPCHARACTER m_pkOldVictim;
};

 

then in function (char_skill.cpp) int CHARACTER::ComputeSkill(DWORD dwVnum, LPCHARACTER pkVictim, BYTE bSkillLevel) add

 

if (dwVnum == SKILL_EUNHYUNG && GetSectree())
{
	RemoveInvisibleVictim f(this);
	GetSectree()->ForEachAround(f);
}

OPTIONAL: GetHighestDpsVictim in char_battle.cpp

LPCHARACTER CHARACTER::GetHighestDpsVictim(LPCHARACTER pkChr)
{
	if (NULL == pkChr)
	pkChr = this;

	float fMinDist = 99999.0f;
	float fMaxDamage = 0.0f;
	LPCHARACTER pkVictim = NULL;

	TDamageMap::iterator it = m_map_kDamage.begin();

	// 일단 주위에 없는 사람을 걸러 낸다.
	while (it != m_map_kDamage.end())
	{
		const VID & c_VID = it->first;
		float fDamage = it->second.iTotalDamage;
		++it;

		LPCHARACTER pAttacker = CHARACTER_MANAGER::instance().Find(c_VID);

		if (!pAttacker)
			continue;

		if (pAttacker->IsAffectFlag(AFF_EUNHYUNG) || 
				pAttacker->IsAffectFlag(AFF_INVISIBILITY) ||
				pAttacker->IsAffectFlag(AFF_REVIVE_INVISIBLE))
			continue;

		float fDist = DISTANCE_APPROX(pAttacker->GetX() - pkChr->GetX(), pAttacker->GetY() - pkChr->GetY());

		if (fDist < fMinDist && !pAttacker->IsDead() && fDamage > fMaxDamage)
		{
			pkVictim = pAttacker;
			fMaxDamage = fDamage;
		}
	}

	return pkVictim;
}

 

  • Love 2
Link to comment
Share on other sites

  • 2 years later...

Announcements



  • Similar Content

  • Similar Content

  • Similar Content

  • Tags

  • Activity

    1. 5

      Effect weapons

    2. 3

      Crystal Metinstone

    3. 3

      Feeding game source to LLM

    4. 113

      Ulthar SF V2 (TMP4 Base)

    5. 3

      Feeding game source to LLM

    6. 0

      Target Information System

    7. 3

      Feeding game source to LLM

    8. 2

      anti exp explanation pls

  • Recently Browsing

    • No registered users viewing this page.
×
×
  • 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.