I've come up with a "fix" for the problem of quest items sticking with an npc - it's a temp fix for now until I have more time to take a look at options to the "npc's can equip items" code. Basically what I did was add a new field to the npc_types index in the database called "questflag". I then added a function to check it and put it within the Client::FinishTrade function. It checks the questflag field in npc_types, and if 0 is returned, it allows the npc to have the item added to its loot table. If 1 is returned, the item is ignored(for loot table purposes - the npc still accepts the item through all tests I've done).
In client.cpp, replace Client::FinishTrade() with:
Code:
void Client::FinishTrade(NPC* with){
int32 items[4]={0};
int8 charges[4]={0};
for (sint16 i=3000; i<=3003; i++){
const ItemInst* inst = m_inv[i];
if (inst) {
items[i-3000]=inst->GetItem()->ItemNumber;
charges[i-3000]=inst->GetCharges();
DeleteItemInInventory(i);
}
}
char temp1[100];
memset(temp1,0x0,100);
char temp2[100];
memset(temp2,0x0,100);
for ( int z=0; z < 4; z++ ) {
sprintf(temp1,"item%d.%d", z+1,with->GetNPCTypeID());
sprintf(temp2,"%d",items[z]);
parse->AddVar(temp1,temp2);
memset(temp1,0x0,100);
memset(temp2,0x0,100);
}
sprintf(temp1,"copper.%d",with->GetNPCTypeID());
sprintf(temp2,"%i",trade->cp);
parse->AddVar(temp1,temp2);
memset(temp1,0x0,100);
memset(temp2,0x0,100);
sprintf(temp1,"silver.%d",with->GetNPCTypeID());
sprintf(temp2,"%i",trade->sp);
parse->AddVar(temp1,temp2);
memset(temp1,0x0,100);
memset(temp2,0x0,100);
sprintf(temp1,"gold.%d",with->GetNPCTypeID());
sprintf(temp2,"%i",trade->gp);
parse->AddVar(temp1,temp2);
memset(temp1,0x0,100);
memset(temp2,0x0,100);
sprintf(temp1,"platinum.%d",with->GetNPCTypeID());
sprintf(temp2,"%i",trade->pp);
parse->AddVar(temp1,temp2);
memset(temp1,0x0,100);
memset(temp2,0x0,100);
parse->Event(EVENT_ITEM, with->GetNPCTypeID(), 0, with, this->CastToMob());
LinkedListIterator<ServerLootItem_Struct*> iterator(*with->CastToNPC()->itemlist);
iterator.Reset();
int xy = 0;
while(iterator.MoreElements()) {
xy++;
iterator.Advance();
}
for(int y=0;y<4;y++){
if (xy <20){
xy++;
NPC* npc=with->CastToNPC();
const Item_Struct* item2 = database.GetItem(items[y]);
if (item2) { //no "no drop" items for j00!
ServerLootItem_Struct* item = new ServerLootItem_Struct;
item->item_id = item2->ItemNumber;
item->charges = charges[y];
char newid[20];
memset(newid, 0, sizeof(newid));
for(int i=0;i<7;i++){
if (!isalpha(item2->IDFile[i])){
strncpy(newid, &item2->IDFile[i],5);
i=8;
}
}
APPLAYER* outapp = new APPLAYER(OP_WearChange, sizeof(WearChange_Struct));
WearChange_Struct* wc = (WearChange_Struct*)outapp->pBuffer;
wc->spawn_id = npc->GetID();
wc->material=0;
if (((item2->EquipSlots==24576) || (item2->EquipSlots==8192)) && (npc->d_meele_texture1==0)) {
wc->wear_slot_id=7;
if (item2->Common.SpellId!=0)
npc->CastToMob()->AddProcToWeapon(item2->Common.SpellId,true);
npc->equipment[7]=item2->ItemNumber;
npc->d_meele_texture1=atoi(newid);
if (item2->Common.Material >0)
wc->material=item2->Common.Material;
else
wc->material=atoi(newid);
npc->AC+=item2->Common.AC;
npc->STR+=item2->Common.STR;
npc->INT+=item2->Common.INT;
}
else if (((item2->EquipSlots==24576) || (item2->EquipSlots==16384)) && (npc->d_meele_texture2 ==0) && ((npc->GetLevel()>=13) || (item2->Common.Damage==0)))
{
if (item2->Common.SpellId!=0)
npc->CastToMob()->AddProcToWeapon(item2->Common.SpellId,true);
npc->d_meele_texture2=atoi(newid);
npc->equipment[8]=item2->ItemNumber;
wc->wear_slot_id=8;
if (item2->Common.Material >0)
wc->material=item2->Common.Material;
else
wc->material=atoi(newid);
npc->AC+=item2->Common.AC;
npc->STR+=item2->Common.STR;
npc->INT+=item2->Common.INT;
}
else if ((item2->EquipSlots==4) && (npc->equipment[0]==0)){
npc->equipment[0]=atoi(newid);
if (item2->Common.Material >0)
wc->material=item2->Common.Material;
else
wc->material=atoi(newid);
wc->wear_slot_id=0;
npc->AC+=item2->Common.AC;
npc->STR+=item2->Common.STR;
npc->INT+=item2->Common.INT;
}
else if ((item2->EquipSlots==131072) && (npc->equipment[1]==0)){
npc->equipment[1]=atoi(newid);
if (item2->Common.Material >0)
wc->material=item2->Common.Material;
else
wc->material=atoi(newid);
wc->wear_slot_id=1;
npc->AC+=item2->Common.AC;
npc->STR+=item2->Common.STR;
npc->INT+=item2->Common.INT;
}
else if ((item2->EquipSlots==128) && (npc->equipment[2]==0)){
npc->equipment[2]=atoi(newid);
if (item2->Common.Material >0)
wc->material=item2->Common.Material;
else
wc->material=atoi(newid);
wc->wear_slot_id=2;
npc->AC+=item2->Common.AC;
npc->STR+=item2->Common.STR;
npc->INT+=item2->Common.INT;
}
else if ((item2->EquipSlots==1536) && (npc->equipment[3]==0)){
npc->equipment[3]=atoi(newid);
if (item2->Common.Material >0)
wc->material=item2->Common.Material;
else
wc->material=atoi(newid);
wc->wear_slot_id=3;
npc->AC+=item2->Common.AC;
npc->STR+=item2->Common.STR;
npc->INT+=item2->Common.INT;
}
else if ((item2->EquipSlots==4096) && (npc->equipment[4]==0)){
npc->equipment[4]=atoi(newid);
if (item2->Common.Material >0)
wc->material=item2->Common.Material;
else
wc->material=atoi(newid);
wc->wear_slot_id=4;
npc->AC+=item2->Common.AC;
npc->STR+=item2->Common.STR;
npc->INT+=item2->Common.INT;
}
else if ((item2->EquipSlots==262144) && (npc->equipment[5]==0)){
npc->equipment[5]=atoi(newid);
if (item2->Common.Material >0)
wc->material=item2->Common.Material;
else
wc->material=atoi(newid);
wc->wear_slot_id=5;
npc->AC+=item2->Common.AC;
npc->STR+=item2->Common.STR;
npc->INT+=item2->Common.INT;
}
else if ((item2->EquipSlots==524288) && (npc->equipment[6]==0)){
npc->equipment[6]=atoi(newid);
if (item2->Common.Material >0)
wc->material=item2->Common.Material;
else
wc->material=atoi(newid);
wc->wear_slot_id=6;
npc->AC+=item2->Common.AC;
npc->STR+=item2->Common.STR;
npc->INT+=item2->Common.INT;
}
if (((npc->GetRace()==127) && (npc->CastToMob()->GetOwnerID()!=0)) && (item2->EquipSlots==24576) || (item2->EquipSlots==8192) || (item2->EquipSlots==16384)){
npc->d_meele_texture2=atoi(newid);
wc->wear_slot_id=8;
if (item2->Common.Material >0)
wc->material=item2->Common.Material;
else
wc->material=atoi(newid);
npc->AC+=item2->Common.AC;
npc->STR+=item2->Common.STR;
npc->INT+=item2->Common.INT;
}
item->equipSlot = item2->EquipSlots;
if((item2->NoDrop != 0 && !parse->HasQuestFile(with->GetNPCTypeID())) || this->GetGM())
if(GetQuestNPCFlag(CastToClient()->GetTarget()->CastToNPC()->GetNPCTypeID()) == 0){//Killspree: QuestNPC check
(*npc->itemlist).Append(item);//npc isn't a quest npc, let it have the item
entity_list.QueueClients(this, outapp);
safe_delete(outapp);
}
else{
entity_list.QueueClients(this, outapp);
safe_delete(outapp);
//Killspree: This npc is a quest npc, letting it keep items can be bad for duping!
}
}
}
}
}
Now above/below that add:
Code:
int8 GetQuestNPCFlag(int32 npcid){
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT questflag FROM npc_types WHERE id=%i",npcid), errbuf, &result)) {
safe_delete_array(query);
if (mysql_num_rows(result) == 1)
{
row = mysql_fetch_row(result);
int8 questflag = atoi(row[0]);
mysql_free_result(result);
return questflag;
}
else
{
mysql_free_result(result);
return 0;
}
mysql_free_result(result);
}
else
{
cerr << "Error in GetQuestNPCFlag query '" << query << "' " << errbuf << endl;
safe_delete_array(query);
return false;
}
return 0;
}
There you have it. If you have any problems with the code, please let me know and I'll take a look at it. If you have any suggestions on where better to place it by all means speak up on that as well.
For the database portion, simply type the following:
Code:
ALTER TABLE `npc_types` ADD `questflag` TINYINT(4) DEFAULT "0" NOT NULL AFTER `AC`
There you go. You'll have to set the "questflag" field to 1 for any NPCs you don't want to keep items given to them, default is 0.