View Single Post
  #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