Jump to content

OtherChoice

Inactive Member
  • Posts

    91
  • Joined

  • Last visited

  • Days Won

    3
  • Feedback

    0%

Posts posted by OtherChoice

  1. Quote
    
    	row = 0
    			for materialRow in self.materialSlots:
    				j = 0
    				for material in materialRow:
    					material.SetOverInItemEvent(lambda trash = 0, rowIndex = row,  col = j: self.__OverInMaterialSlot(trash, rowIndex, col))
    					material.SetSelectItemSlotEvent(lambda trash = 0, rowIndex = row,  col = j: self.__OnSelectMaterialSlot(trash, rowIndex, col))
    					material.SetOverOutItemEvent(lambda : self.__OverOutMaterialSlot())
    					j = j + 1
    				row = row + 1
    
    			row = 0
    			for resultSlot in self.resultSlots:
    				resultSlot.SetOverInItemEvent(lambda trash = 0, rowIndex = row: self.__OverInCubeResultSlot(trash, rowIndex))
    				resultSlot.SetOverOutItemEvent(lambda : self.__OverOutMaterialSlot())
    				row = row + 1

    Is It possible to use a weakref to self not to unroll lambda expressions?
    for example like this

    from _weakref import ref
    for materialRow in self.materialSlots:
    				j = 0
    				for material in materialRow:
    					material.SetOverInItemEvent(lambda  i= ref(self), trash = 0, rowIndex = row,  col = j: i.__OverInMaterialSlot(trash, rowIndex, col))
    					

    Or would the garbage collector still find +1 references? 

  2. Nice shot. For better code management and reusability you could remove the delete from the if (pkOldGrid) check and put it into the override method CGrid::CGrid(CGrid * pkGrid, int w, int h) : m_iWidth(w), m_iHeight(h) like this:
     

    CGrid::CGrid(int w, int h) : m_iWidth(w), m_iHeight(h)					
    {
    	m_pGrid = new char[m_iWidth * m_iHeight];						
    	memset(m_pGrid, 0, sizeof(char) * m_iWidth * m_iHeight);
    }
    
    CGrid::CGrid(CGrid * pkGrid, int w, int h) : m_iWidth(w), m_iHeight(h)		//<- if you add delete pkGrid here you can call M2_NEW CGrid(CGrid*, width, height) and it will delete the old pointer of safebox function at the end of this method 
    {
    	m_pGrid = new char[m_iWidth * m_iHeight];
    	int iSize = std::MIN(w * h, pkGrid->m_iWidth * pkGrid->m_iHeight);
    	thecore_memcpy(m_pGrid, pkGrid->m_pGrid, sizeof(char) * iSize);
      	//here
    	delete pkGrid;
    }

     

    • Love 1
  3. @Gurgarath Hello, I'm trying to reproduce the bug but I'm working on a "worked-around" source, do you know how this work around was made? 

     

    EDIT: After a bit of testing, as i was expecting is a client fix made on skilltable.txt. Your solution is also not a real fix, this skill as the majority of them checks on client side for hit collision. So the real problem is in client data inconsistencies in skill range set in skilltable.txt and hit data from skill yongpa(_2,_3,_4).msa.
    The following are the "actual hitting" ranges, while range in skilltable.txt is the range your character walks up to before casting the skill, so basically those two values have to match.

     Group Event03
        {
            MotionEventType      4
            StartingTime         1.193431
            DuringTime           0.200000
            EnableHitProcess     1
            
            AttackType           0
            HittingType          1
            StiffenTime          0.000000
            InvisibleTime        0.500000
            ExternalForce        0.000000
            HitLimitCount        5
            CollisionType        4
            
            SphereDataCount      1
            Group SphereData00
            {
                Radius           200.000000
                Position         0.000000 -200.000000 150.000000
            }
        }
        Group Event04
        {
            MotionEventType      4
            StartingTime         1.445256
            DuringTime           0.200000
            EnableHitProcess     1
            
            AttackType           0
            HittingType          1
            StiffenTime          0.000000
            InvisibleTime        0.500000
            ExternalForce        0.000000
            HitLimitCount        5
            CollisionType        4
            
            SphereDataCount      1
            Group SphereData00
            {
                Radius           200.000000
                Position         0.000000 -600.000000 150.000000
            }
        }

    those hitting spheres are positioned in front of the character at radius distance from you and from the first sphere covering a total distance of 4*radius so 800, I suggest to use this value lowered by a 50 for latency and moving victims, you can fiddle with this value and get the desired result, for skilltable.txt (and skill_proto as well, DB is not used but if you want to add a check serverside for skillrange hacks and such you'd better have consistent data).
    So with current .msa file settings the correct value for skilltable.txt is 750; hitsphere, however, is noticeably smaller than the dragon head range. Another good solution would be to increase sphere radius to 250 (for each of the 4 msa) and skilltable.txt value to 950.
    This solution has been tested and works with no double damage computation( @Heathcliff™) and with the default m_me->OnMove(true) function (no need to change source). If you want any further explaination about OnMove usage here do not esitate to ask.

    • Love 2
  4. Might be also a good idea to change data size for communicating structures like header of packets and sequence magic table (of course also change the table itself) currently unisgned char, more bits == more work for attacker. Those things will make packet forging attempts much harder. There are also packs which can be loaded, if client code gets rewrote wiser, after client-server first communication (majority of them, the ones you want to protect actually). Just combine those two factors (don't want to get too deep into it not to exploit it) and you will have a decent looking server packet decryption key system.

    • Love 1
  5. Hello everyone, today i'm on my second day trying to expand dynamic shadows view range, i found many limiters here and there for example SHADOW_APPLY_MAX_DISTANCE, bShadow_cutoff but none of those seem to be really affecting shadow range, Dynamic Shadows are rendered inside a square (not a circle so radius like constants mentioned above or distance between mainInstance and targetInstance are not involved) around the current playing character. Do you guys know if there's a cube or a square for dynamic shadows to be rendered on, and if so where is it defined? Hope you can help me I can't really figure it out.

     

    SOLVED: matLightProj was set to 2400.0f x 2400.0f x 1 x 15000.0f, changing the height and width of projection matrix will project more shadows

    UPDATE: larger projection will upscale shadow texture so it results in a shadow quality reduction, it can be partially bypassed by increasing m_wShadowMapSize but only up to a limit of 8192 set by Direct3d8.

    • Love 1
  6. The only variable in the formula is "k" which is the skillpower vaiable and ranges from 0.05 at level 1 to 1.25 at P. You have to ways of accomplish what you want, edit the formula to reach the cap you want at the current rate of change which implies intermediate values will follow the function. For example:

    Spoiler

    actual att damage formula is 10+60*k which is equal to 85, if you change it like this 10+152*k you will get 200 attack bonus at P but lower values are calculated from this one and you have no control over them: at G1 with current formula you would get k = 0.82 and bonus = 134 you can find k values either in source or when you start your server on the terminal preceded by SKILL_POWER_BY_JOB

    or you can totaly remove the formula and manually enter the values from level 0 to 40 in a declared array accessible by the function

    short	m_PartyAttackBonus[41] = { 1,2,3,4,5,/*YOUR 41 values here*/... };
    and then change int Bonus = m_PartyAttackBous[ch->GetParty()->GetLeader()->GetSkillLevel(YOUR_LEADERSHIP_SKILL_VALUE)]
    it would be better if you add a check if the party leader exists( if (ch->GetParty()->GetLeader()) ), maybe it is not needed but you would want to test it before (having two chars in group with the leader logging off while the party member is attaccking monsters so he is constantly checking for those values)
     

     

    • Love 1
  7. You can also use windef macros and use way less CWindows and workarounds, I made my client borderless (full screen over the taskbar but not locked as top window like actual fullscreen) with this exact method. Those macros, in the right execution order are: SetWindowLongPtr, SetRect, AdjustWindowRectEx, MoveWindow, SetWindowPos. You can have a look at how they are used in PythonApplication.cpp bool CPythonApplication::Create(PyObject * poSelf, const char * c_szName, int width, int height, int Windowed) for game window, but they can be applied to any window handler.

  8. Quote

    Find the dwID of the item in your database, maybe is still there.

     

    -Error with inventory cells

    -Error on the item_proto (wronge size etc.)

    -Other...

    run this query in player db and report the results:
    SELECT * FROM player.item WHERE id = 10019575;

    according to bt thats the ID of the item that crashes core when you try to remove it.

    • Love 1
  9. I can't see anything wrong neither in this function, so probably you should check your db item_proto entries as suggested (you can also have a sys_err("deleting %d", item->GetVnum()) at the beginning of your DestroyItem() function, after /ip or /ipurge command load your character, with your latest item_proto entries (most likely there's nothing wrong with vanilla items) and then disconnect, if you get a core again you can check which item caused it in your sys_err since it will be last line before crash).

  10. The crash occured after a disconnect as you can see in the bt, the Disconnect() method and the ~CHARACTER destructor are called, for what i can see there's no problem with ClearItems() and DestroyItem(), post your CHARACTER::Destroy() method maybe there are more clues on what happened. It might be a double deletion of something since you get undefined behaviour if you try to delete an object through a pointer more that once. But the pointers in func args seem ok they don't look like bogus pointers.

    Or maybe @WeedHex is right and you got db incosistencies.

    • Love 1
  11. float CalcAttackRating(LPCHARACTER pkAttacker, LPCHARACTER pkVictim, bool bIgnoreTargetRating)

    This function calculates Attack rating, which is a hidden factor in damage calculation, calculated with two value(attackerAR-victimER) which range from 0.7 to 1 for attacker and from 0 to 0.3 for victim based on dex and level of attacker and victim. Basically this is a multiplier which can range from 0.4 (minattackerAR = 0.7 - maxvictimER = 0.3) to 1 (maxattackerAR=1 - minvictimER = 0) and it is applied to every damage you compute. If you change those values every damage you will compute (with or without costume buff) will do more damage so changing that function won't works as you expect: Yes you will do more damage with costume buff but you will also do more damage without it, you are changing the way the game calculates this multiplier every time, not just the % bonus damage given from a said source.

    • Love 1
  12. 3 hours ago, dumita123 said:

    Hello metin2dev.

     

    I have a problem, per 10% mobs bonus on item, you get like 300 damage in +.

    How can I change that damage per % added? I searched the entire forum for this and I couldn't find it. Could never be more happier if anybody could guide me into what I need to do. I searched the game src, db src, the db itself but with no success, I didn't find any bonuses related to the damage it gives per %.

    Example > 10% mobs on costume gives 300 damage +

    I want 10% mobs on costume to give 800 damage+

     

    Thanks a lot for reading. Hope you can help me!

    % damages are calculated based on your damage without that modifier, if you have 10% bonus on costume, you will deal 10% more damage.
    For example if my damage is 3000, 10% mob would give me around 300 more damage; if my damage is 8000 then that 10% would add 800 damage. You can't change how much 10% is without increasing the base number, you can't change how percentages work because your math will loose consistency and not a single compiler or machine can work with inconsistent information. What you can do is to edit the bonus percentage it gives:

    if your damage is around 3000 (since you have 300 bonus damage from 10%) you can increase %damage to something between 25-30% and you will have your 800-900 damages bonus from costume

    • Love 1
  13. The problem is the m_Victim, each LPCHARACTERS stores the target and, even if ChangeVictimByAggro(args) won't be executed since type = DAMAGE_TYPE_POISON, the monster will keep the target pointer to their last victim, you need to also remove the resurrected LPCHARACTER as victim of nearby monsters. To do so you can use some simple structs, I already posted this solution for Invisibility skill.

    in char.cpp find void CHARACTER::ReviveInvisible(int iDur) and replace whole function
    
    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->GetNewNearestVictim(pkChr, m_pkOldVictim);
    					pkChr->SetVictim(new_victim);
    				}
    			}
    		}
    	}
    	LPCHARACTER m_pkOldVictim;
    };
    void CHARACTER::ReviveInvisible(int iDur)
    {
    	if (GetSectree())
    	{
    		RemoveInvisibleVictim f(this);
    		GetSectree()->ForEachAround(f);
    	}
    	AddAffect(AFFECT_REVIVE_INVISIBLE, POINT_NONE, 0, AFF_REVIVE_INVISIBLE, iDur, 0, true);
    }
    in char_battle.cpp add (and define it in char.h: LPCHARACTER GetNewNearestVictim(LPCHARACTER pkChr, LPCHARACTER pkVictimOld); )
    LPCHARACTER CHARACTER::GetNewNearestVictim(LPCHARACTER pkChr, LPCHARACTER pkVictimOld)
    {
    	if (NULL == pkChr)
    		pkChr = this;
    
    	float fMinDist = 99999.0f;
    	LPCHARACTER pkVictim = NULL;
    
    	TDamageMap::iterator it = m_map_kDamage.begin();
    
    	while (it != m_map_kDamage.end())
    	{
    		const VID & c_VID = it->first;
    		++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() && pAttacker != pkVictimOld)
    		{
    			pkVictim = pAttacker;
    			fMinDist = fDist;
    		}
    	}
    
    	return pkVictim;
    }

    And then clean some miscoding:

    In char_affect.cpp at CHARACTER::AddAffect(args...) if bool bOverride (one of the function arguments) is true it will first reset a said flag calling 

    if (pkAff && bOverride)
    	{
    		ComputeAffect(pkAff, false); //
    
    		if (GetDesc())
    			SendAffectRemovePacket(GetDesc(), GetPlayerID(), pkAff->dwType, pkAff->bApplyOn);
    	}
    
    WE change it as follows
      if (pkAff && bOverride)
    	{
    		ComputeAffect(pkAff, false, true); // ComputeAffect(Affect, !bOvveride, bOverride); 
    
    		if (GetDesc())
    			SendAffectRemovePacket(GetDesc(), GetPlayerID(), pkAff->dwType, pkAff->bApplyOn);
    	}

    But we do not need to reset said flag early in the function if we are going to set it back few moments later so we can change CHARACTER::ComputeAffect(args..) as follows:

    find
    void CHARACTER::ComputeAffect(CAffect * pkAff, bool bAdd)
      	if (bAdd && pkAff->dwType >= GUILD_SKILL_START && pkAff->dwType <= GUILD_SKILL_END)	
    	{
    		if (!GetGuild())
    			return;
    
    		if (!GetGuild()->UnderAnyWar())
    			return;
    	}
    	if (pkAff->dwFlag)
    	{
    		if (!bAdd)
    			m_afAffectFlag.Reset(pkAff->dwFlag);
    		else
    			m_afAffectFlag.Set(pkAff->dwFlag);
    	}
    replace with
    void CHARACTER::ComputeAffect(CAffect * pkAff, bool bAdd, bool bTemp)
    {
    	if (bAdd && pkAff->dwType >= GUILD_SKILL_START && pkAff->dwType <= GUILD_SKILL_END)	
    	{
    		if (!GetGuild())
    			return;
    
    		if (!GetGuild()->UnderAnyWar())
    			return;
    	}
    	if (pkAff->dwFlag)
    	{
    		if (!bAdd && !bTemp)
    			m_afAffectFlag.Reset(pkAff->dwFlag);
    		else if (bAdd)
    			m_afAffectFlag.Set(pkAff->dwFlag);
    	}
    
    //HEADER char.h find
    		void			ComputeAffect(CAffect * pkAff, bool bAdd);
    replace with
    		void			ComputeAffect(CAffect * pkAff, bool bAdd, bool bTemp = false);

    tested and working.

    • Love 3
  14. Client sources are files which compile into your ".exe" application, they manage physics, graphics, incoming and outgoing data transfers, and use the resources contained in your client's pack folder, such as textures, 3d models (.gr2), python files and plain text files. Server sources are files which compile into your "game" and "db" (no extension since they're elf32) files hosted on the remote server. They manage incoming and outgoing data transfers, and use the resources contained in your database to evaluate formulas for pretty much anything. The only way to actually "use" them is compiling into the respective files, but you can change how they work on datas, or datas they work with.

    • Love 1
  15. @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
  16. 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
  17. 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
  18. 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.

     

×
×
  • 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.