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;
}