Jump to content

Quest Function - Party Check Item - Check if all Party Members have the Specified Item


Recommended Posts

  • Honorable Member

M2 Download Center

This is the hidden content, please
( Internal )

Hey guyz,

 

Today I made a small release. It's a new LUA function to check if all the members of the party have the specified item (and then it removes it).

Let's start to make it work!

Open party.h and search for: GetMemberMinLevel();

After add this:

bool		CheckItem(int targyID);

Save and close the file.

Open party.cpp and and the code below the function GetMemberMaxLevel:

bool CParty::CheckItem(int targyID)
{
	int dbszam = 0;
	itertype(m_memberMap) it = m_memberMap.begin();
	
	while (it!=m_memberMap.end())
	{
		if (!it->second.pCharacter)
		{
			++it;
			continue;
		}
		if(it->second.pCharacter->CountSpecifyItem(targyID) > 0)
			dbszam++;
		
		++it;
	}
	
	if(GetNearMemberCount() <= dbszam)
	{
		itertype(m_memberMap) it2 = m_memberMap.begin();
	
		while (it2!=m_memberMap.end())
		{
			if (!it2->second.pCharacter)
			{
				++it2;
				continue;
			}
			if(it2->second.pCharacter->CountSpecifyItem(targyID) > 0)
				it2->second.pCharacter->RemoveSpecifyItem(targyID);
		
			++it2;
		}
	}else{return false;}
	
	return true;
}

Save the file and close it.

Open questlua_party.cpp and add this function:

int party_check_item(lua_State* L)
	{
		DWORD item_vnum;
		if (lua_isnumber(L,1))
		{
			item_vnum = (DWORD)lua_tonumber(L, 1);
		}
		else
		{
			lua_pushboolean(L, 0);
			return 1;
		}
		LPCHARACTER ch = CQuestManager::instance().GetCurrentCharacterPtr();

		if (ch->GetParty())
			lua_pushboolean(L,ch->GetParty()->CheckItem(item_vnum));
		else
			lua_pushboolean(L, 0);

		return 1;
	}

 

Add the line below after { "is_in_dungeon",   party_is_in_dungeon   },

{ "check_item",	party_check_item	},

Compile the game and we're done.

In quests it will return a boolean value.

You can use it like:

if party.check_item(72702) then
d.join(207)
end

In this case it checks if all the party members have the item with the ID of 72702 and then it removes from them and warps them to the dungeon.

 

 

Don't copy it or at least mention my name!

  • Metin2 Dev 5
  • Love 3

WRnRW3H.gif

Link to comment
Share on other sites

25 minutes ago, Distraught said:
Spoiler


int party_check_item(lua_State* L)
	{
		DWORD item_vnum;
		if (lua_isnumber(L,1))
		{
			item_vnum = (DWORD)lua_tonumber(L, 1);
		}
		else
		{
			lua_pushboolean(L, 0);
			return 1;
		}
		LPCHARACTER ch = CQuestManager::instance().GetCurrentCharacterPtr();

		if (ch->GetParty())
			lua_pushboolean(L,ch->GetParty()->CheckItem(item_vnum));
		else
			lua_pushboolean(L, 0);

		return 1;
	}

 

 

Spoiler


bool CParty::CheckItem(int targyID)
{
	int dbszam = 0;
	itertype(m_memberMap) it = m_memberMap.begin();
	
	while (it!=m_memberMap.end())
	{
		if (!it->second.pCharacter)
		{
			++it;
			continue;
		}
		if(it->second.pCharacter->CountSpecifyItem(targyID) > 0)
			dbszam++;
		
		++it;
	}
	
	if(GetNearMemberCount() <= dbszam)
	{
		itertype(m_memberMap) it2 = m_memberMap.begin();
	
		while (it2!=m_memberMap.end())
		{
			if (!it2->second.pCharacter)
			{
				++it2;
				continue;
			}
			if(it2->second.pCharacter->CountSpecifyItem(targyID) > 0)
				it2->second.pCharacter->RemoveSpecifyItem(targyID);
		
			++it2;
		}
	}else{return false;}
	
	return true;
}

 

Thanks for release, but the code style is very bad, should look like this.

int party_check_item(lua_State* L)
{
	if (!lua_isnumber(L, 1))
	{
		lua_pushboolean(L, false);
		return 1;
	}
		
	LPCHARACTER ch = CQuestManager::instance().GetCurrentCharacterPtr();
	lua_pushboolean(L, (ch && ch->GetParty()) ? ch->GetParty()->CheckItem(lua_tonumber(L, 1)) : false));
	return 1;
}

bool CParty::CheckItem(int itemVnum)
{
	int iCount = 0;
	itertype(m_memberMap) it = m_memberMap.begin();

	for (; it != m_memberMap.end(); it++)
	{
		if (it->second.pCharacter && it->second.pCharacter->CountSpecifyItem(itemVnum))
			iCount++;
	}

	if (GetNearMemberCount() <= iCount)
	{
		for (; it != m_memberMap.end(); it++)
		{
			if (it->second.pCharacter && it->second.pCharacter->CountSpecifyItem(itemVnum))
				it->second.pCharacter->RemoveSpecifyItem(itemVnum);
		}
		
		return true;
	}

	return false;
}

 

  • Love 2
Link to comment
Share on other sites

3 minutes ago, Tasho said:

 

Thanks for release, but the code style is very bad, should look like this.


int party_check_item(lua_State* L)
{
	if (!lua_isnumber(L, 1))
	{
		lua_pushboolean(L, false);
		return 1;
	}
		
	LPCHARACTER ch = CQuestManager::instance().GetCurrentCharacterPtr();
	lua_pushboolean(L, (ch && ch->GetParty()) ? ch->GetParty()->CheckItem(lua_tonumber(L, 1)) : false));
	return 1;
}

bool CParty::CheckItem(int itemVnum)
{
	int iCount = 0;
	itertype(m_memberMap) it = m_memberMap.begin();

	for (; it != m_memberMap.end(); it++)
	{
		if (it->second.pCharacter && it->second.pCharacter->CountSpecifyItem(itemVnum))
			iCount++;
	}

	if (GetNearMemberCount() <= iCount)
	{
		for (; it != m_memberMap.end(); it++)
		{
			if (it->second.pCharacter && it->second.pCharacter->CountSpecifyItem(itemVnum))
				it->second.pCharacter->RemoveSpecifyItem(itemVnum);
		}
		
		return true;
	}

	return false;
}

 

364829827469148160.png364829827469148160.png364829827469148160.png

Edited by Metin2 Dev
Core X - External 2 Internal
  • Sad 2
  • Love 1
Link to comment
Share on other sites

Quote

bool CParty::CheckItem(int itemVnum)
{
	int iCount = 0;
	itertype(m_memberMap) it = m_memberMap.begin();

	for (; it != m_memberMap.end(); it++)
	{
		if (it->second.pCharacter && it->second.pCharacter->CountSpecifyItem(itemVnum))
			iCount++;
	}

	if (GetNearMemberCount() <= iCount)
	{
		for (; it != m_memberMap.end(); it++)
		{
			if (it->second.pCharacter && it->second.pCharacter->CountSpecifyItem(itemVnum))
				it->second.pCharacter->RemoveSpecifyItem(itemVnum);
		}
		
		return true;
	}

	return false;
}

@Tasho

His code style is perfectly fine.your addition actually made it for the worse.

 it is actually recommended to use ++it instead of it++ for 2 reasons

first, iterator is a container class and when you use a postfix ++ the compiler use the overloaded operator ++ method

look at the code example below:

Foo& Foo::operator++()   // called for ++i
{
    this->data += 1;
    return *this;
}

Foo Foo::operator++(int ignored_dummy_value)   // called for i++
{
    Foo tmp(*this);   // variable "tmp" cannot be optimized away by the compiler
    ++(*this);
    return tmp;
}

 

you can clearly see why prefix++ is better for perfomance for class objects.

the postfix++ actually creates a temp object which obviously result in less performance.

and second: in most of the code you'll see the iterators style is with prefix ++ operator. so it would be better to use it to match the original source style.

 

and for next time, please, unless you have a deep knowledge of c++ and design patterns, do not call others "bad style" because right now his code performance is better than yours.

  • Love 1
Link to comment
Share on other sites

43 minutes ago, metin2-factory said:

and for next time, please, unless you have a deep knowledge of c++ and design patterns, do not call others "bad style" because right now his code performance is better than yours.

Just stop to speak shit, you did that reply for make +1 for without sense.
That you called "better performance" ? 

Check the code of him and recheck the code what i put and go to sleep, don't search entourage, you are look shit_aim 2.
Have a nice night, don't reply me again, i don't care.

		DWORD item_vnum;
		if (lua_isnumber(L,1))
		{
			item_vnum = (DWORD)lua_tonumber(L, 1);
		}
		else
		{
			lua_pushboolean(L, 0);
			return 1;
		}
	while (it!=m_memberMap.end())
	{
		if (!it->second.pCharacter)
		{
			++it;
			continue;
		}
		if(it->second.pCharacter->CountSpecifyItem(targyID) > 0)
			dbszam++;
		
		++it;
	}
	
	if(GetNearMemberCount() <= dbszam)
	{
		itertype(m_memberMap) it2 = m_memberMap.begin();
	
		while (it2!=m_memberMap.end())
		{
			if (!it2->second.pCharacter)
			{
				++it2;
				continue;
			}
			if(it2->second.pCharacter->CountSpecifyItem(targyID) > 0)
				it2->second.pCharacter->RemoveSpecifyItem(targyID);
		
			++it2;
		}
	}else{return false;}
	
	return true;
}

 

Link to comment
Share on other sites

6 minutes ago, metin2-factory said:

???

your post does not make any sense. you should take my advices above as constructive criticism i am not trying to make fun of you.

it's just common that people here replying to other posts without really understanding the code behind it, how it works under the hood.

Who say that? The guy who buyed all stuff from lennt and ken and now is 'programmer'  :huh:?

Don't let me to make public conversations between you and others people from some time ago with proofs,  just stop to look 'smart', you are not in a position to give me 'advices'.

Just stop with that, i really don't care about you and m2 shit, you took it too seriously that, stop to make off-topic.

Link to comment
Share on other sites

11 minutes ago, Tasho said:

Who say that? Who buyed all stuff from lennt and ken and now is 'programmer' ?

Don't let me to make public conversations between you and others people from some time ago with proofs,  just stop to look 'smart', you are not in a position to give me 'advices'.

Just stop with that, i really don't care about you and m2 shit, you took it too seriously that, stop to make off-topic.

Well, unlike you and most of the other "Devs" here i actually have a degree in computer science and working in a real world programming job, i develop metin2 systems for fun.

so unless you're a certified developer or have a real world programming experience you have no right to judge other code. especially because you barely understand how it works

under the hood.

Lennet was one of a few devs here that actually deserve credit because he understood how stuff works unlike you.

Link to comment
Share on other sites

10 minutes ago, metin2-factory said:

Well, unlike you and most of the other "Devs" here i actually have a degree in computer science and working in a real world programming job, i develop metin2 systems for fun.

certified developer 

With that level of english you have study 'Computer science and working on programming job' ?

If you was had enough knowledge you didn't was stay here and have a 'service on metin2' 

 

A lot a years of programming etc and you was had a shit server with systems buyed from another peoples, same story was with aim shit too, a big programmer better then all, 20 years of programming, read a lot a books, a monster.. and he was just another s*** as you, in private he didn't was know to do nothing just to request something.

A nice quote: "Is not important to be a professional programmer on that life, you just need to look like one."

Good night, stop to reply already is enough, i don't like childs act but your force me to do that.

tom-delonge-wtf1.gif

Edited by Metin2 Dev
Core X - External 2 Internal
Link to comment
Share on other sites

  • Honorable Member

Acutally you both are right.

@metin2-factory says the ++i is running faster than the i++. It's true because i++ stores the old value and returns that and after adds 1 to the value of i. ++i only adds 1 and doesn't store the old value.

BUT

@Tasho is also right. I checked the runtime of both of our code (ran it 1.000.000 times and then calculated an average value). His code runs about 2x times faster than mine because of the optimalizations he did.

WRnRW3H.gif

Link to comment
Share on other sites

@Distraught the reason it ran faster is most likely due to this part of his code:

if (GetNearMemberCount() <= iCount)
	{
		for (; it != m_memberMap.end(); it++)
		{
			if (it->second.pCharacter && it->second.pCharacter->CountSpecifyItem(itemVnum))
				it->second.pCharacter->RemoveSpecifyItem(itemVnum);
		}
		
		return true;
	}

he didn't assign a starting object to iterate over in this part of the code. and therefore it doesn't execute the loop part of the code. and as consequence you get supposedly a better performance.

this for loop condition will always fail.. take a look.

  • Love 1
Link to comment
Share on other sites

  • Honorable Member
2 hours ago, metin2-factory said:

@Distraught the reason it ran faster is most likely due to this part of his code:


if (GetNearMemberCount() <= iCount)
	{
		for (; it != m_memberMap.end(); it++)
		{
			if (it->second.pCharacter && it->second.pCharacter->CountSpecifyItem(itemVnum))
				it->second.pCharacter->RemoveSpecifyItem(itemVnum);
		}
		
		return true;
	}

he didn't assign a starting object to iterate over in this part of the code. and therefore it doesn't execute the loop part of the code. and as consequence you get supposedly a better performance.

this for loop condition will always fail.. take a look.

oh really, that's right and he also missed this check too

so that's why the big difference in runtime

if (!it->second.pCharacter)
		{
			++it;
			continue;
		}

 

WRnRW3H.gif

Link to comment
Share on other sites

Guys.. pls..

There is no performance difference between it++ and ++it.
i++ could potentially be slower than ++i, since the old value of i might need to be saved for later use, but in practice all modern compilers will optimize this away.

I can demonstrate this by looking at the code for this function, both with ++i and i++.

$ cat i++.c
extern void g(int i);
void f()
{
    int i;

    for (i = 0; i < 100; i++)
        g(i);

}

 

The files are the same, except for ++i and i++:

$ diff i++.c ++i.c
6c6
<     for (i = 0; i < 100; i++)
---
>     for (i = 0; i < 100; ++i)

I 'll compile them, and also get the generated assembler:

Zitat

$ gcc -c i++.c ++i.c
$ gcc -S i++.c ++i.c

 

So after this we can see that both the generated object and assembler files are the same.

$ md5 i++.s ++i.s
MD5 (i++.s) = 90f620dda862cd0205cd5db1f2c8c06e
MD5 (++i.s) = 90f620dda862cd0205cd5db1f2c8c06e

$ md5 *.o
MD5 (++i.o) = dd3ef1408d3a9e4287facccec53f7d22
MD5 (i++.o) = dd3ef1408d3a9e4287facccec53f7d22

 This means, that after compiling both codes are the same.
Both have the same runtime.

Well, I hope that helps.

 

Best regards , ProfDrBielefeld

  • Love 1
Link to comment
Share on other sites

vor 27 Minuten schrieb metin2-factory:

@ProfDrBielefeld

We're talking here about iterators increment not integers.

for iterators, ++it will always have a better performance than it++.

Here's a good read:

https://stackoverflow.com/questions/1303899/performance-difference-between-iterator-and-iterator

I tested it...

 

Zitat

:~/test$ md5sum *.o
1b1f3a8f16ab59273c26db6b31a63eaf  perinc.o
a35c7ad5f28cec354845a5b043435fce  postinc.o

nvm then :D

 

 

btw.. for a every member you can easy use this function

template <class Func> void CParty::ForEachMemberPtr(Func & f)
{
	TMemberMap::iterator it;

	for (it = m_memberMap.begin(); it != m_memberMap.end(); ++it)
		f(it->second.pCharacter);
}

 

Link to comment
Share on other sites

As a quest function this release is not really necessary. You can do this using the quest functions provided by the core by default.

function checkItem(index, count)
	local partyMemberPids = table.pack(party.get_member_pids())
	
	local invalid = false
	for _, pid in ipairs(partyMemberPids) do
		q.begin_other_pc_block(pid)
		
		if pc.count_item(index) < count then
			invalid = true
		end
		
		q.end_other_pc_block()
		
		if invalid then
			break
		end
	end
	
	return not invalid
end

function removeItem(index, count)
	local partyMemberPids = table.pack(party.get_member_pids())
	
	for _, pid in ipairs(partyMemberPids) do
		q.begin_other_pc_block(pid)
		
		pc.remove_item(index, count)
		
		q.end_other_pc_block()
	end
end

 

 

By the way:

vor 39 Minuten schrieb metin2-factory:

@ProfDrBielefeld

We're talking here about iterators increment not integers.

for iterators, ++it will always have a better performance than it++.

Here's a good read:

https://stackoverflow.com/questions/1303899/performance-difference-between-iterator-and-iterator

The code optimization during compile time should know wether it's needed that the value returned by the operator is needed or not. If not the optimization should be aware of that and optimize the code in a way that both ways behave the same and therefore have no performance difference. Otherwise if it's needed, you're right of course and the pre-increment operator is better talking of performance but if you need it you won't have another way anyway and this does not matter.

 

Regards

  • Love 2
Link to comment
Share on other sites

	struct FPartyHasItem
	{
		DWORD itemid;
		bool allhaveitem;
		
		void operator () (LPCHARACTER ch)
		{
			if(!ch)
			{
				return;
			}
			
			if(!(ch->CountSpecifyItem(itemid) > 0))
			{
				allhaveitem = false;
			}
		}

	};

	int party_check_item(lua_State* L)
	{
		
		LPCHARACTER ch = CQuestManager::instance().GetCurrentCharacterPtr();
		FPartyHasItem f;
		if (!lua_isnumber(L, 1))
		{
			lua_pushboolean (L, false);
			return 1;
		}
		else
		{
			f.itemid = lua_tonumber(L, 1);
		}
		f.allhaveitem = true;
		ch->GetParty()->ForEachMemberPtr(f);
		lua_pushboolean (L, f.allhaveitem);
		return 1;
	}

 

This is my function for checking all member.

Link to comment
Share on other sites

2 minutes ago, ProfDrBielefeld said:

		void operator () (LPCHARACTER ch)
		{
			if(!ch)
			{
				return;
			}
			
			if(!(ch->CountSpecifyItem(itemid) > 0))
			{
				allhaveitem = false;
			}
		}

 

That is really bad style too..i really don't understand why you do check like this?

  • > 0 == 1+ == TRUE
  • 0 == FALSE
  • CountSpecifyItem return count value finded on inventory by a specify item, so if he don't find return 0, if he find it will return 1+ count. That means you can check it simple as a boolean if (CountSpecifyItem(x)) do something, exist item on inventory, if (!CountSpecifyItem(x)) not exist item, return count zero.
		void operator () (LPCHARACTER ch)
		{
			if (ch && !ch->CountSpecifyItem(itemid))
				allhaveitem = false;
		}

This one too, you use else when already you return something  and stop the func from if condition if not exist argument, what the sense?

2 minutes ago, ProfDrBielefeld said:

	int party_check_item(lua_State* L)
	{
		
		LPCHARACTER ch = CQuestManager::instance().GetCurrentCharacterPtr();
		FPartyHasItem f;
		if (!lua_isnumber(L, 1))
		{
			lua_pushboolean (L, false);
			return 1;
		}
		else
		{
			f.itemid = lua_tonumber(L, 1);
		}
		f.allhaveitem = true;
		ch->GetParty()->ForEachMemberPtr(f);
		lua_pushboolean (L, f.allhaveitem);
		return 1;
	}

 

Should look like that.

	int party_check_item(lua_State* L)
	{
		if (!lua_isnumber(L, 1))
		{
			lua_pushboolean(L, false);
			return 1;
		}

		FPartyHasItem f;
		f.itemid = lua_tonumber(L, 1);
		f.allhaveitem = true;

		CQuestManager::instance().GetCurrentCharacterPtr()->GetParty()->ForEachMemberPtr(f);
		lua_pushboolean (L, f.allhaveitem);
		return 1;
	}

I hope you get the points, don't take that as a insult or something.

  • Love 1
Link to comment
Share on other sites

  • Premium

.gif

 

I don't see why are you guys fighting, there is no doubt ++i will always be faster than i++ for obvious reasons, but the performance loss is so miserable even bothering about talking about it only means you're looking for a pointless fight to prove useless points, I think someone should just calm the fuck down here.

Besides, losing ~100 ms is TOTALLY worth if you're writing a clean, simple, and readable code, which anyway is totally not his case.

 

There is NO REASON at all to shit over all what you write to give it a TOTALLY UNNOTICEABLE speed boost, no one is ever going to give you an award for that, while they would respect you and even take you as example for themselves if you're making it an opensource project on GitGudHub, cause yeah, that's what makes a programmer what it is: their ability to keep the code understandable to everyone and as simple and sober as possible.

 

TL;DR: You should only look to improve the performance of your code if it doesn't totally fuck it up in the first place, fighting over <100ms speed is a childish act and whoever does that should just feel bad and realize how unworthy they are inside this environment.

  • Love 2

 

"Nothing's free in this life.

Ignorant people have an obligation to make up for their ignorance by paying those who help them.

Either you got the brains or cash, if you lack both you're useless."

Syreldar

Link to comment
Share on other sites

@Syerldar I totally agree with you regarding the importance of code being readable, maintainable and etc. 

Yet, i do feel an urge to be a little pedant on such performance issues. because personally when developing systems you should always taking scalability factor into account.

Right now it indeed has no impact but what if you had to iterate over 1000 or 1.000.000 or even 100.000.000 objects? metin2 is a real time action game

and you always should aim for players to have the best performance possible.

This case ofcours isn't included because we're iterating over a list of party members but i wrote it just as an example of why it is important to know how stuff works

under the hood.

and @Yiv i agree with what you said but again in my opinion using prefix incerement would always be preferable over postfix. because you always should expect for the worse.

If you're using an old/crappy compiler it is not certain it will do the required optimizations. and, the best case scenerio for postfix incrementor would be to even out with prefix performance.

 

There is a reason why the developers used prefix incerement for iterators and were very persistent and consistent about it in their entire code base.

Link to comment
Share on other sites

  • Active+ Member
  • New functions for something already there and talking about performance.
  • Code c++98 and talk about performance.
  • Systems writed with DirectQuery in loops and talk about performance.

Metin2 Source is a big source and already has shits that slow down so don't talk about performance.

Performance is not in one iterator used just when you enter in doungeon.

  • Love 9
Link to comment
Share on other sites

Announcements



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