Go Back   EQEmulator Home > EQEmulator Forums > Development > Development::Server Code Submissions

Reply
 
Thread Tools Display Modes
  #1  
Old 08-23-2012, 08:40 AM
image
Demi-God
 
Join Date: Jan 2002
Posts: 1,290
Default COMMITTED: Classic Multiquest Support

All of this code will redesign the sub function check_handin so that before we call EVENT_ITEM we keep all items turned in by players into the NPC's memory.

Then when check_handin is called we feed the item id and count back into C++ to see if the NPC has received that item before (or just now). If not we break out and return false because all items are not yet available to complete a quest.

questmgr.h (in public section above protected):
Code:
	// KaB - Red69 - Zek / Adding multiquest support
	bool TurnInItem(int32 itm, int charges);
	void CompleteHandIn();
	void ResetHandIn();
questmgr.cpp (end of file)

Code:
bool QuestManager::TurnInItem(int32 itm, int charges)
{
	if ( owner && owner->IsNPC() )
	{
		if ( owner->CastToNPC()->DoesQuestItemExist(itm, charges, true) )
			return true;
	}

	return false;
}

void QuestManager::CompleteHandIn()
{
	if ( owner && owner->IsNPC() )
	{
		owner->CastToNPC()->RemoveQuestDeleteItems();
	}
}

void QuestManager::ResetHandIn()
{
	if ( owner && owner->IsNPC() )
	{
		owner->CastToNPC()->ResetQuestDeleteList();
	}
}

perlparser.cpp (new quest:: commands to add)
Look for:
newXS(strcpy(buf, "voicetell"), XS__voicetell, file);
newXS(strcpy(buf, "LearnRecipe"), XS__LearnRecipe, file);
(this is just the end of EXTERN_C XS(boot_quest))
add:

Code:
		// KaB - Zek - Red69 / Addition for multiquest
        newXS(strcpy(buf, "handleturnin"), XS__handleturnin, file);
        newXS(strcpy(buf, "completehandin"), XS__completehandin, file);
        newXS(strcpy(buf, "resethandin"), XS__resethandin, file);
Add these above that function that loads all the command names:
Code:
XS(XS__handleturnin); // prototype to pass -Wmissing-prototypes
XS(XS__handleturnin) {
	dXSARGS;
	
	if (items != 2)
		Perl_croak(aTHX_ "Usage: handleturnin(itemid, itemcharges)");
	int	itemid = (int)SvIV(ST(0));
	int	charges = (int)SvIV(ST(1));

	bool returnVal = quest_manager.TurnInItem(itemid,charges);
	
	ST(0) = boolSV(returnVal);
	sv_2mortal(ST(0));
	XSRETURN(1);
}

XS(XS__completehandin); // prototype to pass -Wmissing-prototypes
XS(XS__completehandin) {
	dXSARGS;
	
	if (items != 0)
		Perl_croak(aTHX_ "Usage: completeturnin()");

	quest_manager.CompleteHandIn();
	
	XSRETURN_EMPTY;
}

XS(XS__resethandin); // prototype to pass -Wmissing-prototypes
XS(XS__resethandin) {
	dXSARGS;
	
	if (items != 0)
		Perl_croak(aTHX_ "Usage: resetturnin()");

	quest_manager.ResetHandIn();
	
	XSRETURN_EMPTY;
}
npc.h (protected area above private):
Code:
	LinkedList<ItemInst*> questItems;
	LinkedList<ItemInst*> questDeletionItems;
npc.h (public area, above protected):
Code:
	// KaB - Zek - Red69 / Multiquest additions
	void AddQuestItem(ItemInst* inst) { questItems.Insert(inst); }

	void ClearQuestLists()
	{
		ClearQuestItems(true);
		ClearQuestDeleteItems(true);
	}

	void ResetQuestDeleteList()
	{
		ClearQuestDeleteItems(true);
	}
	
	
	void ClearQuestItems(bool delete_=false)
	{
		LinkedListIterator<ItemInst*> iterator(questItems);
		iterator.Reset();
		while(iterator.MoreElements())
		{
			ItemInst* inst = iterator.GetData();
			iterator.RemoveCurrent(delete_);
		}

		questItems.Clear();
	}

	void ClearQuestDeleteItems(bool delete_=false)
	{
		LinkedListIterator<ItemInst*> iterator(questDeletionItems);
		iterator.Reset();
		while(iterator.MoreElements())
		{
			ItemInst* inst = iterator.GetData();
			iterator.RemoveCurrent(delete_);
		}

		questDeletionItems.Clear();
	}
	
	ItemInst* FindQuestItemByID(int32 itmID, int charges, bool flagItemForDeletion=false)
	{
		LinkedListIterator<ItemInst*> iterator(questItems);
		iterator.Reset();
		int totalCharges = 0;
		while(iterator.MoreElements())
		{
			if ( iterator.GetData()->GetItem()->ID == itmID )
			{
				totalCharges += 1;

				if ( flagItemForDeletion )
					questDeletionItems.Insert(iterator.GetData()->Clone());
				if ( charges > totalCharges )
				{
					iterator.Advance();
					continue;
				}

				return iterator.GetData();
			}
			iterator.Advance();
		}
		return NULL;
	}

	bool DoesQuestItemExist(int32 itmID, int charges, bool flagItemForDeletion=false) { 	
		ItemInst* inst = FindQuestItemByID(itmID,charges,flagItemForDeletion);
		if ( inst != NULL )
		{
			return true;
		}
		else
			return false;
	}

	void ClearQuestItem(ItemInst* inst, bool delete_=true)
	{
		LinkedListIterator<ItemInst*> iterator(questItems);
		iterator.Reset();

		while(iterator.MoreElements())
		{
			if ( iterator.GetData ()->GetItem()->ID == inst->GetItem()->ID )
			{
				iterator.RemoveCurrent(delete_);
				break;
			}
			iterator.Advance();
		}
	}

	void RemoveQuestDeleteItems()
	{
		LinkedListIterator<ItemInst*> iterator(questDeletionItems);
		iterator.Reset();
		while(iterator.MoreElements())
		{
			ClearQuestItem(iterator.GetData(),true);
			iterator.RemoveCurrent(true);
		}

		questDeletionItems.Clear();
	}

	void PrintOutQuestItems(Client* c);
npc.cpp:

Add a new function in the NPC deconstructor to clear out their multiquest items
Code:
NPC::~NPC()
{
	// KaB - Red69 - Zek / Multiquest addition
	ClearQuestLists();

Add a new function to npc.cpp that will allow us to peek into the items the npc has:

Code:
void NPC::PrintOutQuestItems(Client* c){
		c->Message(4,"Quest Items currently awaiting completion on %s",GetName());

		LinkedListIterator<ItemInst*> iterator(questItems);
		iterator.Reset();

		while(iterator.MoreElements())
		{
			c->Message(5,"ItemName: %s (%d) | Charges: %i",iterator.GetData()->GetItem()->Name,iterator.GetData()->GetItem()->ID,iterator.GetData()->GetCharges());
			iterator.Advance();
		}

		c->Message(4,"End of quest items list.");
}

client.h (public function)

Code:
	bool	StoreTurnInItems(Mob* with);
trading.cpp

Code:
// KaB - Red69 - Zek / Aug 22 2012.  Rework on trade/quest turn in of items
bool Client::StoreTurnInItems(Mob* tradingWith) {
	
	if ( !tradingWith || !tradingWith->IsNPC() )
		return false;

				for (sint16 i=3000; i<=3003; i++) {
					const ItemInst* inst = m_inv[i];
					if (inst) {
						database.logevents(AccountName(),AccountID(),admin,GetName(),tradingWith->GetName(),"Quest Turn In Attempt",inst->GetItem()->Name,22,GetX(),
						GetY(),GetZ(), (char*)database.GetZoneName(GetZoneID(), GetPP().zoneInstance, true),tradingWith->GetX(),tradingWith->GetY(),tradingWith->GetZ());

						tradingWith->CastToNPC()->AddQuestItem(inst->Clone());
					}
				}

	return true;
}
trading.cpp in Client::FinishTrade(Mob* tradingWith) add store call StoreTurnInItems when we confirm it is a quest npc.

Code:
#ifdef EMBPERL
		if(((PerlembParser *)parse)->HasQuestSub(tradingWith->GetNPCTypeID(), "EVENT_ITEM")) {
#else
		if(parse->HasQuestFile(tradingWith->GetNPCTypeID())) {
#endif
			// This is a quest NPC
			quest_npc = true;
			
			// KaB - Red69 - Zek / Aug 22 2012.  Rework on trade/quest turn in of items
			StoreTurnInItems(tradingWith);
		}

command.cpp (new print quest items command):

command_init

Code:
		command_add("printquestitems","Returns available quest items for multiquesting currently on the target npc.",200,command_printquestitems) ||
Code:
void command_printquestitems(Client *c, const Seperator *sep)
{
	if (c->GetTarget() != 0)
	{
		if ( c->GetTarget()->IsNPC() )
			c->GetTarget()->CastToNPC()->PrintOutQuestItems(c);
		else
			c->Message(13,"Pick a NPC target.");
	}
	else
			c->Message(13,"Pick a NPC target.");
}
command.h:

Code:
void command_printquestitems(Client *c, const Seperator *sep);
plugins/check_handin.pl
Code:
sub check_handin {
    my $hashref = shift;
    my %required = @_;
    	quest::resethandin();
    foreach my $req (keys %required) {
	$charges = $required{$req};
	if ( !quest::handleturnin($req,$charges) )
	{
		return(0);
	}
    }
	quest::completehandin();
     return 1;
}
__________________
www.eq2emu.com
EQ2Emu Developer
Former EQEMu Developer / GuildWars / Zek Seasons Servers
Member of the "I hate devn00b" club.
Reply With Quote
  #2  
Old 08-23-2012, 08:53 AM
image
Demi-God
 
Join Date: Jan 2002
Posts: 1,290
Default

I should add I have also disabled return_items (just removed the contents from the sub function) since it is exploitable. This multiquest code won't handle item returns but it never existed in classic anyway.
__________________
www.eq2emu.com
EQ2Emu Developer
Former EQEMu Developer / GuildWars / Zek Seasons Servers
Member of the "I hate devn00b" club.
Reply With Quote
  #3  
Old 08-23-2012, 12:42 PM
c0ncrete's Avatar
c0ncrete
Dragon
 
Join Date: Dec 2009
Posts: 719
Default

i'm trying to wrap my head around why this couldn't (and should not be) implemented entirely in perl. that way, it could easily be modified to do things like limit multi-questing to group members present in zone and things of that nature.
Reply With Quote
  #4  
Old 08-23-2012, 01:20 PM
image
Demi-God
 
Join Date: Jan 2002
Posts: 1,290
Default

I put this up because someone had requested me to. I don't have time to get the latest SVN, have a custom setup just to tailor to their source versus mine and from there do the diff.

It is quite amusing all the critique that exists but no action around here. If people want to continue working with a unreliable broken source be my guest.

As for why its not in perl im going to be adding more logging events so I can track the process of hand ins. Do it in perl if you want.
__________________
www.eq2emu.com
EQ2Emu Developer
Former EQEMu Developer / GuildWars / Zek Seasons Servers
Member of the "I hate devn00b" club.
Reply With Quote
  #5  
Old 08-23-2012, 09:44 PM
image
Demi-God
 
Join Date: Jan 2002
Posts: 1,290
Default

I got the latest SVN downloaded and made this diff. I don't have any time to actually compile, setup the db and test the latest SVN right now, at your own risk for now.

Don't forget despite the diff you still need to update your plugin/check_handin.pl

Perl check_handin.pl change
Code:
sub check_handin {
    my $hashref = shift;
    my %required = @_;
    	quest::resethandin();
    foreach my $req (keys %required) {
	$charges = $required{$req};
	if ( !quest::handleturnin($req,$charges) )
	{
		return(0);
	}
    }
	quest::completehandin();
     return 1;
}
Diff
Code:
Index: client.h
===================================================================
--- client.h	(revision 2194)
+++ client.h	(working copy)
@@ -1096,7 +1096,9 @@
 
 	char* GetRacePlural(Client* client);
 	char* GetClassPlural(Client* client);
-
+		
+	// image: multiquest additions for eqemu diff aug 23 2012
+	bool	StoreTurnInItems(Mob* with);
 protected:
 	friend class Mob;
 	void CalcItemBonuses(StatBonuses* newbon);
Index: command.cpp
===================================================================
--- command.cpp	(revision 2194)
+++ command.cpp	(working copy)
@@ -456,7 +456,9 @@
         command_add("sensetrap", "Analog for ldon sense trap for the newer clients since we still don't have it working.", 0, command_sensetrap) ||
         command_add("picklock", "Analog for ldon pick lock for the newer clients since we still don't have it working.", 0, command_picklock) ||
 		command_add("mysql", "Mysql CLI, see 'help' for options.", 250, command_mysql) ||
-		command_add("xtargets", "Show your targets Extended Targets and optionally set how many xtargets they can have.", 250, command_xtargets) 
+		command_add("xtargets", "Show your targets Extended Targets and optionally set how many xtargets they can have.", 250, command_xtargets) ||
+		// image: multiquest additions for eqemu diff aug 23 2012
+		command_add("printquestitems","Returns available quest items for multiquesting currently on the target npc.",200,command_printquestitems)
 		)
 	{
 		command_deinit();
@@ -11529,3 +11531,16 @@
 	else
 		t->ShowXTargets(c);
 }
+
+void command_printquestitems(Client *c, const Seperator *sep)
+{
+	if (c->GetTarget() != 0)
+	{
+		if ( c->GetTarget()->IsNPC() )
+			c->GetTarget()->CastToNPC()->PrintOutQuestItems(c);
+		else
+			c->Message(13,"Pick a NPC target.");
+	}
+	else
+			c->Message(13,"Pick a NPC target.");
+}
\ No newline at end of file
Index: command.h
===================================================================
--- command.h	(revision 2194)
+++ command.h	(working copy)
@@ -321,6 +321,10 @@
 void command_mysql(Client *c, const Seperator *sep);
 void command_xtargets(Client *c, const Seperator *sep);
 
+			
+// image: multiquest additions for eqemu diff aug 23 2012
+void command_printquestitems(Client *c, const Seperator *sep);
+
 #ifdef EMBPERL
 void command_embperl_plugin(Client *c, const Seperator *sep);
 void command_embperl_eval(Client *c, const Seperator *sep);
Index: npc.cpp
===================================================================
--- npc.cpp	(revision 2194)
+++ npc.cpp	(working copy)
@@ -351,6 +351,9 @@
 	  
 NPC::~NPC()
 {
+	// image: multiquest additions for eqemu diff aug 23 2012
+	ClearQuestLists();
+
 	entity_list.RemoveNPC(GetID());
 	AI_Stop();
 
@@ -2270,4 +2273,112 @@
 		return true;
 	
 	return false;
+}
+
+
+// image: multiquest additions for eqemu diff aug 23 2012
+void NPC::ClearQuestItems(bool delete_=false)
+{
+	LinkedListIterator<ItemInst*> iterator(questItems);
+	iterator.Reset();
+	while(iterator.MoreElements())
+	{
+		ItemInst* inst = iterator.GetData();
+		iterator.RemoveCurrent(delete_);
+	}
+
+	questItems.Clear();
+}
+
+void NPC::ClearQuestDeleteItems(bool delete_=false)
+{
+	LinkedListIterator<ItemInst*> iterator(questDeletionItems);
+	iterator.Reset();
+	while(iterator.MoreElements())
+	{
+		ItemInst* inst = iterator.GetData();
+		iterator.RemoveCurrent(delete_);
+	}
+
+	questDeletionItems.Clear();
+}
+
+ItemInst* NPC::FindQuestItemByID(int32 itmID, int charges, bool flagItemForDeletion=false)
+{
+	LinkedListIterator<ItemInst*> iterator(questItems);
+	iterator.Reset();
+	int totalCharges = 0;
+	while(iterator.MoreElements())
+	{
+		if ( iterator.GetData()->GetItem()->ID == itmID )
+		{
+			totalCharges += 1;
+
+			if ( flagItemForDeletion )
+				questDeletionItems.Insert(iterator.GetData()->Clone());
+			if ( charges > totalCharges )
+			{
+				iterator.Advance();
+				continue;
+			}
+
+			return iterator.GetData();
+		}
+		iterator.Advance();
+	}
+	return NULL;
+}
+
+bool NPC::DoesQuestItemExist(int32 itmID, int charges, bool flagItemForDeletion=false) { 	
+	ItemInst* inst = FindQuestItemByID(itmID,charges,flagItemForDeletion);
+	if ( inst != NULL )
+	{
+		return true;
+	}
+	else
+		return false;
+}
+
+void NPC::ClearQuestItem(ItemInst* inst, bool delete_=true)
+{
+	LinkedListIterator<ItemInst*> iterator(questItems);
+	iterator.Reset();
+
+	while(iterator.MoreElements())
+	{
+		if ( iterator.GetData ()->GetItem()->ID == inst->GetItem()->ID )
+		{
+			iterator.RemoveCurrent(delete_);
+			break;
+		}
+		iterator.Advance();
+	}
+}
+
+void NPC::RemoveQuestDeleteItems()
+{
+	LinkedListIterator<ItemInst*> iterator(questDeletionItems);
+	iterator.Reset();
+	while(iterator.MoreElements())
+	{
+		ClearQuestItem(iterator.GetData(),true);
+		iterator.RemoveCurrent(true);
+	}
+
+	questDeletionItems.Clear();
+}
+
+void NPC::PrintOutQuestItems(Client* c){
+	c->Message(4,"Quest Items currently awaiting completion on %s",GetName());
+
+	LinkedListIterator<ItemInst*> iterator(questItems);
+	iterator.Reset();
+
+	while(iterator.MoreElements())
+	{
+		c->Message(5,"ItemName: %s (%d) | Charges: %i",iterator.GetData()->GetItem()->Name,iterator.GetData()->GetItem()->ID,iterator.GetData()->GetCharges());
+		iterator.Advance();
+	}
+
+	c->Message(4,"End of quest items list.");
 }
\ No newline at end of file
Index: npc.h
===================================================================
--- npc.h	(revision 2194)
+++ npc.h	(working copy)
@@ -329,7 +329,29 @@
 	NPC_Emote_Struct* GetNPCEmote(int16 emoteid, int8 event_);
 	void DoNPCEmote(int8 event_, int16 emoteid);
 	bool CanTalk();
+	
+	// image: multiquest additions for eqemu diff aug 23 2012
+	void AddQuestItem(ItemInst* inst) { questItems.Insert(inst); }
 
+	void ClearQuestLists()
+	{
+		ClearQuestItems(true);
+		ClearQuestDeleteItems(true);
+	}
+
+	void ResetQuestDeleteList()
+	{
+		ClearQuestDeleteItems(true);
+	}
+
+	void ClearQuestItems(bool delete_=false);
+	void ClearQuestDeleteItems(bool delete_=false);
+	ItemInst* FindQuestItemByID(int32 itmID, int charges, bool flagItemForDeletion=false);
+	bool DoesQuestItemExist(int32 itmID, int charges, bool flagItemForDeletion=false);
+	void ClearQuestItem(ItemInst* inst, bool delete_=true);
+	void RemoveQuestDeleteItems();
+	void PrintOutQuestItems(Client* c);
+
 protected:
 	
 	const NPCType*	NPCTypedata;
@@ -418,7 +440,10 @@
 	bool ldon_trap_detected;
 	QGlobalCache *qGlobals;
 	uint32 adventure_template_id;
-
+		
+	// image: multiquest additions for eqemu diff aug 23 2012
+	LinkedList<ItemInst*> questItems;
+	LinkedList<ItemInst*> questDeletionItems;
 private:
 	int32	loottable_id;
 	bool	p_depop;
Index: perlparser.cpp
===================================================================
--- perlparser.cpp	(revision 2194)
+++ perlparser.cpp	(working copy)
@@ -3335,6 +3335,48 @@
 	XSRETURN_UV(seconds);
 }
 
+	
+// image: multiquest additions for eqemu diff aug 23 2012
+XS(XS__handleturnin); // prototype to pass -Wmissing-prototypes
+XS(XS__handleturnin) {
+	dXSARGS;
+	
+	if (items != 2)
+		Perl_croak(aTHX_ "Usage: handleturnin(itemid, itemcharges)");
+	int	itemid = (int)SvIV(ST(0));
+	int	charges = (int)SvIV(ST(1));
+
+	bool returnVal = quest_manager.TurnInItem(itemid,charges);
+	
+	ST(0) = boolSV(returnVal);
+	sv_2mortal(ST(0));
+	XSRETURN(1);
+}
+
+XS(XS__completehandin); // prototype to pass -Wmissing-prototypes
+XS(XS__completehandin) {
+	dXSARGS;
+	
+	if (items != 0)
+		Perl_croak(aTHX_ "Usage: completeturnin()");
+
+	quest_manager.CompleteHandIn();
+	
+	XSRETURN_EMPTY;
+}
+
+XS(XS__resethandin); // prototype to pass -Wmissing-prototypes
+XS(XS__resethandin) {
+	dXSARGS;
+	
+	if (items != 0)
+		Perl_croak(aTHX_ "Usage: resetturnin()");
+
+	quest_manager.ResetHandIn();
+	
+	XSRETURN_EMPTY;
+}
+
 /*
 This is the callback perl will look for to setup the
 quest package's XSUBs
@@ -3548,6 +3590,9 @@
         newXS(strcpy(buf, "GetZoneID"), XS__GetZoneID, file);
         newXS(strcpy(buf, "GetZoneLongName"), XS__GetZoneLongName, file);
         newXS(strcpy(buf, "GetTimeSeconds"), XS__GetTimeSeconds, file);
+		newXS(strcpy(buf, "handleturnin"), XS__handleturnin, file);
+        newXS(strcpy(buf, "completehandin"), XS__completehandin, file);
+        newXS(strcpy(buf, "resethandin"), XS__resethandin, file);
 	XSRETURN_YES;
 }
 
Index: questmgr.cpp
===================================================================
--- questmgr.cpp	(revision 2194)
+++ questmgr.cpp	(working copy)
@@ -2642,3 +2642,30 @@
     return ln.c_str();
 }
 
+// image: multiquest additions for eqemu diff aug 23 2012
+bool QuestManager::TurnInItem(int32 itm, int charges)
+{
+	if ( owner && owner->IsNPC() )
+	{
+		if ( owner->CastToNPC()->DoesQuestItemExist(itm, charges, true) )
+			return true;
+	}
+
+	return false;
+}
+
+void QuestManager::CompleteHandIn()
+{
+	if ( owner && owner->IsNPC() )
+	{
+		owner->CastToNPC()->RemoveQuestDeleteItems();
+	}
+}
+
+void QuestManager::ResetHandIn()
+{
+	if ( owner && owner->IsNPC() )
+	{
+		owner->CastToNPC()->ResetQuestDeleteList();
+	}
+}
\ No newline at end of file
Index: questmgr.h
===================================================================
--- questmgr.h	(revision 2194)
+++ questmgr.h	(working copy)
@@ -246,7 +246,11 @@
 	bool botquest();
 	bool createBot(const char *name, const char *lastname, uint8 level, uint16 race, uint8 botclass, uint8 gender);
 #endif
-
+		
+	// image: multiquest additions for eqemu diff aug 23 2012
+	bool TurnInItem(int32 itm, int charges);
+	void CompleteHandIn();
+	void ResetHandIn();
 protected:
 	Mob *owner;	//NPC is never NULL when functions are called.
 	Client *initiator;	//this can be null.
Index: trading.cpp
===================================================================
--- trading.cpp	(revision 2194)
+++ trading.cpp	(working copy)
@@ -376,6 +376,8 @@
 		if(parse->HasQuestSub(tradingWith->GetNPCTypeID(), "EVENT_ITEM")) {
 			// This is a quest NPC
 			quest_npc = true;
+			// image: multiquest additions for eqemu diff aug 23 2012
+			StoreTurnInItems(tradingWith);
 		}
 
 		int32 items[4]={0};
@@ -2536,3 +2538,21 @@
 	safe_delete(outapp);
 }
 
+// image: multiquest additions for eqemu diff aug 23 2012
+bool Client::StoreTurnInItems(Mob* tradingWith) {
+	
+	if ( !tradingWith || !tradingWith->IsNPC() )
+		return false;
+
+				for (sint16 i=3000; i<=3003; i++) {
+					const ItemInst* inst = m_inv[i];
+					if (inst) {
+						database.logevents(AccountName(),AccountID(),admin,GetName(),tradingWith->GetName(),"Quest Turn In Attempt",inst->GetItem()->Name,22,GetX(),
+						GetY(),GetZ(), (char*)database.GetZoneName(GetZoneID(), GetPP().zoneInstance, true),tradingWith->GetX(),tradingWith->GetY(),tradingWith->GetZ());
+
+						tradingWith->CastToNPC()->AddQuestItem(inst->Clone());
+					}
+				}
+
+	return true;
+}
\ No newline at end of file
__________________
www.eq2emu.com
EQ2Emu Developer
Former EQEMu Developer / GuildWars / Zek Seasons Servers
Member of the "I hate devn00b" club.
Reply With Quote
  #6  
Old 08-24-2012, 07:12 AM
trevius's Avatar
trevius
Developer
 
Join Date: Aug 2006
Location: USA
Posts: 5,946
Default

Quote:
Originally Posted by image View Post
I should add I have also disabled return_items (just removed the contents from the sub function) since it is exploitable. This multiquest code won't handle item returns but it never existed in classic anyway.
That is true about the old return_items plugin being exploitable, but I have a new one that removes any exploits related to it. A while back, I added the item instance fields for each turned in item (charges and attuned) to the source to allow this to function. Note that this does require some of the newer plugins that may not exist in all older servers, but they can be found here:

https://code.google.com/p/eqemuplugi...e/#svn%2Ftrunk

Code:
sub return_items {    
	my $hashref = plugin::var('$itemcount');
	my $client = plugin::val('$client');
	my $items_returned = 0;

	my %ItemHash = (
		0 => [ plugin::val('$item1'), plugin::val('$item1_charges'), plugin::val('$item1_attuned') ],
		1 => [ plugin::val('$item2'), plugin::val('$item2_charges'), plugin::val('$item2_attuned') ],
		2 => [ plugin::val('$item3'), plugin::val('$item3_charges'), plugin::val('$item3_attuned') ],
		3 => [ plugin::val('$item4'), plugin::val('$item4_charges'), plugin::val('$item4_attuned') ],
	);
	
	foreach my $k (keys(%{$hashref}))
	{
		next if($k == 0);
		my $rcount = $hashref->{$k};
		my $r;
		for ($r = 0; $r < 4; $r++)
		{
			if ($rcount > 0 && $ItemHash{$r}[0] && $ItemHash{$r}[0] == $k)
			{
				if ($client)
				{
					$client->SummonItem($k, $ItemHash{$r}[1], $ItemHash{$r}[2]);
					$items_returned = 1;
				}
				else
				{
					# This shouldn't be needed, but just in case
					quest::summonitem($k, 0);
					$items_returned = 1;
				}
				$rcount--;
			}
		}
		delete $hashref->{$k};
	}
	
	# Return true if items were returned
	return $items_returned;

}
This is not directly related to the purpose of this thread, but I figured it was worth mentioning since it was brought up.
__________________
Trevazar/Trevius Owner of: Storm Haven
Everquest Emulator FAQ (Frequently Asked Questions) - Read It!
Reply With Quote
  #7  
Old 08-24-2012, 08:10 AM
image
Demi-God
 
Join Date: Jan 2002
Posts: 1,290
Default

Neat I was not aware since the base seemed to still be using the old sub functions. I might end up using that at a later point, but I will need to have a determination about which items should be returned or not.

I was thinking of flagging all quest items for non-return and then sort out what is left to return to the player.
__________________
www.eq2emu.com
EQ2Emu Developer
Former EQEMu Developer / GuildWars / Zek Seasons Servers
Member of the "I hate devn00b" club.
Reply With Quote
  #8  
Old 08-28-2012, 09:15 AM
Gameross
Fire Beetle
 
Join Date: Jan 2004
Posts: 21
Default

Wow! It's about time someone got around to adding this! It's long overdue and amazingly small # of changes. Good job Image!


PS Live didn't return items as I recall either, so that's more like live too.
Reply With Quote
  #9  
Old 09-02-2012, 12:43 PM
cavedude's Avatar
cavedude
The PEQ Dude
 
Join Date: Apr 2003
Location: -
Posts: 1,988
Default

Thanks for sharing this image!

I've added it to SVN r2197. I included the required plugin in the EQEmu SVN, but it's also on PEQ quest SVN and I'll post here it so to be sure everybody sees it.

Basically, if you want to continue using our current system, you do nothing different. plugin::check_handin and return_items will both work as they always have, with the added addition of clearing the NPC's quest item list (no need to have non-MQ handins use up additional memory.)

If you wish to use MQ, there is a new plugin called check_mq_handin that will handle that. As image has said, do not use return_items with that.

check_handin.pl
Code:
# plugin::check_handin($item1 => #required_amount,...);
# autoreturns extra unused items on success
sub check_handin {
    my $hashref = shift;
    my %required = @_;
    foreach my $req (keys %required) {
	if ((!defined $hashref->{$req}) || ($hashref->{$req} != $required{$req})) {
            return(0);
	}
    }
     foreach my $req (keys %required) {
         if ($required{$req} < $hashref->{$req}) {
             $hashref->{$req} -= $required{$req};
         } else {
             delete $hashref->{$req};
         }
     }
     quest::clearhandin();
     return 1;
}

sub check_mq_handin {
    my $hashref = shift;
    my %required = @_;
    	quest::resethandin();
    foreach my $req (keys %required) {
	$charges = $required{$req};
	if ( !quest::handleturnin($req,$charges) )
	{
		return(0);
	}
    }
     quest::completehandin();
     return 1;
}

sub return_items {    
	my $hashref = plugin::var('$itemcount');
	my $client = plugin::val('$client');
	my $items_returned = 0;

	my %ItemHash = (
		0 => [ plugin::val('$item1'), plugin::val('$item1_charges'), plugin::val('$item1_attuned') ],
		1 => [ plugin::val('$item2'), plugin::val('$item2_charges'), plugin::val('$item2_attuned') ],
		2 => [ plugin::val('$item3'), plugin::val('$item3_charges'), plugin::val('$item3_attuned') ],
		3 => [ plugin::val('$item4'), plugin::val('$item4_charges'), plugin::val('$item4_attuned') ],
	);
	
	foreach my $k (keys(%{$hashref}))
	{
		next if($k == 0);
		my $rcount = $hashref->{$k};
		my $r;
		for ($r = 0; $r < 4; $r++)
		{
			if ($rcount > 0 && $ItemHash{$r}[0] && $ItemHash{$r}[0] == $k)
			{
				if ($client)
				{
					$client->SummonItem($k, $ItemHash{$r}[1], $ItemHash{$r}[2]);
					$items_returned = 1;
				}
				else
				{
					# This shouldn't be needed, but just in case
					quest::summonitem($k, 0);
					$items_returned = 1;
				}
				$rcount--;
			}
		}
		delete $hashref->{$k};
	}
	quest::clearhandin();
	# Return true if items were returned
	return $items_returned;

}
Reply With Quote
  #10  
Old 09-03-2012, 09:12 AM
image
Demi-God
 
Join Date: Jan 2002
Posts: 1,290
Default

hey cavedude thanks for handling that merge. Only additional suggestion I think I should make is you mentioned that non mq hand in's should not store in memory.

I think you could just have a rule that enables/disables MQ capabilities. The reason I mention this is right now it just checks if the NPC has a EVENT_ITEM sub function and if so then it loads the items into memory via StoreTurnInItems(tradingWith); in trading.cpp.

Obviously if you don't use the appropriate mq check handin it won't matter that the NPC's have that special item list.

I don't think people will have to worry about memory too much though. Right now the hand ins only last till the zone shuts down. I was thinking later this could be integrated into the zonestate dump and maybe some like 7 day expiration could exist for the items.
__________________
www.eq2emu.com
EQ2Emu Developer
Former EQEMu Developer / GuildWars / Zek Seasons Servers
Member of the "I hate devn00b" club.
Reply With Quote
  #11  
Old 09-04-2012, 11:36 AM
cavedude's Avatar
cavedude
The PEQ Dude
 
Join Date: Apr 2003
Location: -
Posts: 1,988
Default

Good suggestion, added in Rev2203.
Reply With Quote
Reply


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

   

All times are GMT -4. The time now is 01:20 PM.


 

Everquest is a registered trademark of Daybreak Game Company LLC.
EQEmulator is not associated or affiliated in any way with Daybreak Game Company LLC.
Except where otherwise noted, this site is licensed under a Creative Commons License.
       
Powered by vBulletin®, Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Template by Bluepearl Design and vBulletin Templates - Ver3.3