Go Back   EQEmulator Home > EQEmulator Forums > Development > Development::Development

Development::Development Forum for development topics and for those interested in EQEMu development. (Not a support forum)

Reply
 
Thread Tools Display Modes
  #1  
Old 06-06-2009, 05:46 PM
realityincarnate
Developer
 
Join Date: Dec 2007
Posts: 122
Default Help with evolving item structures

I've been working off and on with charm stats and evolving items for a little while, and it looks like I have most of the server side stuff working properly now. I've hit a hangup with the client interactions for evolving items, however.

I've looked over the ShowEQ and 13th Floor forums and gotten some idea where the extra information for evolving items goes in the item serialization, but their structures aren't quite the same as SoF (I don't have a clue where to start on Titanium) and I'm not making a lot of progress. I know a lot of you are much better at figuring this sort of thing out than I am, so I was hoping for some pointers. It seems like there has to be a better way to test this than modifying the structures, recompiling, and basically spending all my free time chain crashing the client with no idea if I'm getting closer or not.

Thanks for any help anyone can offer.
Reply With Quote
  #2  
Old 06-06-2009, 06:23 PM
trevius's Avatar
trevius
Developer
 
Join Date: Aug 2006
Location: USA
Posts: 5,946
Default

I would help, but I don't really know much about evolving items. I think there is a string toward the very end of the item struct that is related to evolving items. When I was messing around with the struct and trying to get augs to work, I saw an error about evolving something string that was bad. It is probably something similar to the charm string, though we don't have much info on how charms work either lol.

It seems like they could both work similar to having an augment inserted into the item. Maybe there are extra packets for charm and evolving info stuff.
__________________
Trevazar/Trevius Owner of: Storm Haven
Everquest Emulator FAQ (Frequently Asked Questions) - Read It!
Reply With Quote
  #3  
Old 06-06-2009, 07:21 PM
KLS
Administrator
 
Join Date: Sep 2006
Posts: 1,348
Default

Charm stuff is pretty easy. Evolving I have no idea, their implementation might of even changed from titanium -> sof.

How far are you on charms? I've been working on them today, I've got them working minus scripting at the moment.
Reply With Quote
  #4  
Old 06-07-2009, 10:35 AM
realityincarnate
Developer
 
Join Date: Dec 2007
Posts: 122
Default

I've got a scripting system in place for charms that seems to work (although my testing is a little limited since I never have more than three of us on my server at once). I added a new parse->event call that takes an ItemInst* as an argument and handled calculating the multiple via perl and a $questitem->SetMultiple() function. I made a derived class from ItemInst that stored a pointer to a scaled version of the item, so that scaling only needed to be done when the multiplier actually changed. So far, I have working scripts for the Charm of the Emerald Warrior, Charm of the Brotherhood, and Charm of Exotic Speech. I never did figure out how the server is supposed to send the scaling factor to the client, so I just had it send the scaled item instead of the full stats.

I meant to post that stuff a couple of weeks ago, but the new scripting and derived ItemInst led so naturally into intelligent item scripts that I started working on that, and the whole thing got complicated very quickly. If you want, I can try to seperate out the charm stuff and post what I have.
Reply With Quote
  #5  
Old 06-07-2009, 02:27 PM
realityincarnate
Developer
 
Join Date: Dec 2007
Posts: 122
Default

I figured that I should post what I have for the charm scaling, so we aren't doubling our efforts. This diff has most of the evolving code removed since it isn't done yet, but several of the data members and accessors are still in place.

The item scripts are loaded from the quests/items directory, and are named after the CharmFile associated with the item. I created a new exported class for perl, $questitem, which doesn't have many functions right now but that I plan to expand for the evolving/intelligent item stuff.

Here's the diff (sorry, it's kind of big):
Code:
Index: common/Item.cpp
===================================================================
--- common/Item.cpp	(revision 637)
+++ common/Item.cpp	(working copy)
@@ -367,8 +367,9 @@
 void ItemInst::PutAugment(SharedDatabase *db, uint8 slot, uint32 item_id)
 {
 	if (item_id != 0) {
-		const ItemInst aug(db, item_id);
-		PutAugment(slot,aug);
+		const ItemInst* aug = db->CreateItem(item_id);
+		PutAugment(slot,*aug);
+		safe_delete(aug);
 	}
 }
 
@@ -1593,3 +1594,210 @@
 
 	return true;
 }
+
+// Methods for EvoItemInst, the extended ItemInst for evolving/scaling items
+
+// Copy constructors
+EvoItemInst::EvoItemInst(const EvoItemInst &copy) {
+	m_use_type=copy.m_use_type;
+	m_item=copy.m_item;
+	m_charges=copy.m_charges;
+	m_price=copy.m_price;
+	m_color=copy.m_color;
+	m_merchantslot=copy.m_merchantslot;
+	m_currentslot=copy.m_currentslot;
+	m_instnodrop=copy.m_instnodrop;
+	m_merchantcount=copy.m_merchantcount;
+	// Copy container contents
+	iter_contents it;
+	for (it=copy.m_contents.begin(); it!=copy.m_contents.end(); it++) {
+		ItemInst* inst_old = it->second;
+		ItemInst* inst_new = NULL;
+		
+		if (inst_old) {
+			inst_new = inst_old->Clone();
+		}
+		
+		if (inst_new != NULL) {
+			m_contents[it->first] = inst_new;
+		}
+	}
+	m_SerialNumber = copy.m_SerialNumber;
+	m_exp = copy.m_exp;
+	m_evolveLvl = copy.m_evolveLvl;
+	m_activated = copy.m_activated;
+	m_evolveInfo = NULL;
+	if (copy.m_scaledItem)
+		m_scaledItem = new Item_Struct(*copy.m_scaledItem);
+	else
+		m_scaledItem = NULL;
+}
+
+EvoItemInst::EvoItemInst(const ItemInst &basecopy) {
+	EvoItemInst* copy = (EvoItemInst*)&basecopy;
+	
+	m_use_type=copy->m_use_type;
+	m_item=copy->m_item;
+	m_charges=copy->m_charges;
+	m_price=copy->m_price;
+	m_color=copy->m_color;
+	m_merchantslot=copy->m_merchantslot;
+	m_currentslot=copy->m_currentslot;
+	m_instnodrop=copy->m_instnodrop;
+	m_merchantcount=copy->m_merchantcount;
+	// Copy container contents
+	iter_contents it;
+	for (it=copy->m_contents.begin(); it!=copy->m_contents.end(); it++) {
+		ItemInst* inst_old = it->second;
+		ItemInst* inst_new = NULL;
+		
+		if (inst_old) {
+			inst_new = inst_old->Clone();
+		}
+		
+		if (inst_new != NULL) {
+			m_contents[it->first] = inst_new;
+		}
+	}
+	m_SerialNumber = copy->m_SerialNumber;
+	m_exp = 0;
+	m_evolveLvl = 0;
+	m_activated = false;
+	m_evolveInfo = NULL;
+	m_scaledItem = NULL;
+}
+
+EvoItemInst::EvoItemInst(const Item_Struct* item, sint16 charges) {
+	m_use_type = ItemUseNormal;
+	m_item = item;
+	m_charges = charges;
+	m_price = 0;
+	m_instnodrop = false;
+	m_merchantslot = 0;
+	if(m_item &&m_item->ItemClass == ItemClassCommon)
+		m_color = m_item->Color;
+	else
+		m_color = 0;
+	m_merchantcount = 1;
+	m_SerialNumber = GetNextItemInstSerialNumber();
+	m_exp = 0;
+	m_evolveLvl = 0;
+	m_activated = false;
+	m_evolveInfo = NULL;
+	m_scaledItem = NULL;
+}
+
+EvoItemInst::~EvoItemInst() {
+	safe_delete(m_scaledItem); 
+}
+
+EvoItemInst* EvoItemInst::Clone() const {
+	return new EvoItemInst(*this);
+}
+
+const Item_Struct* EvoItemInst::GetItem() const {
+	if(!m_scaledItem)
+		return m_item;
+	else
+		return m_scaledItem;
+}
+
+const Item_Struct* EvoItemInst::GetUnscaledItem() const {
+	return m_item;
+}
+
+void EvoItemInst::Initialize(SharedDatabase *db) {
+	// if there's no actual item, don't do anything
+	if(!m_item) return;
+
+	// initialize scaling items
+	if (m_item->CharmFileID != 0) {
+		m_evolveLvl = -1;
+		this->ScaleItem();
+	}
+
+	// initialize evolving items
+	else if ((db) && m_item->LoreGroup >= 1000 && m_item->LoreGroup != -1) {
+		// not complete yet
+	}
+}
+
+void EvoItemInst::ScaleItem() {
+	// free memory from any previously scaled item data
+	safe_delete(m_scaledItem);
+	
+	m_scaledItem = new Item_Struct(*m_item);
+	float Mult = (float)(GetExp())/10000;	// scaling is determined by exp, with 10,000 being full stats
+	
+	m_scaledItem->AAgi = m_item->AAgi*Mult;
+	m_scaledItem->AC = m_item->AC*Mult;
+	m_scaledItem->ACha = m_item->ACha*Mult;
+	m_scaledItem->ADex = m_item->ADex*Mult;
+	m_scaledItem->AInt = m_item->AInt*Mult;
+	m_scaledItem->ASta = m_item->AStr*Mult;
+	m_scaledItem->AWis = m_item->AWis*Mult;
+	m_scaledItem->CR = m_item->CR*Mult;
+	m_scaledItem->Damage = m_item->Damage*Mult;
+	m_scaledItem->DamageShield = m_item->DamageShield*Mult;
+	m_scaledItem->DotShielding = m_item->DotShielding*Mult;
+	m_scaledItem->DR = m_item->DR*Mult;
+	m_scaledItem->Endur = m_item->Endur*Mult;
+	m_scaledItem->EnduranceRegen = m_item->EnduranceRegen*Mult;
+	m_scaledItem->FR = m_item->FR*Mult;
+	m_scaledItem->Haste = m_item->Haste*Mult;
+	m_scaledItem->HP = m_item->HP*Mult;
+	m_scaledItem->Mana = m_item->Mana*Mult;
+	m_scaledItem->ManaRegen = m_item->ManaRegen*Mult;
+	m_scaledItem->MR = m_item->MR*Mult;
+	m_scaledItem->PR = m_item->PR*Mult;
+	m_scaledItem->Regen = m_item->Regen*Mult;
+	m_scaledItem->Shielding = m_item->Shielding*Mult;
+	m_scaledItem->SpellShield = m_item->SpellShield*Mult;
+	m_scaledItem->StrikeThrough = m_item->StrikeThrough*Mult;
+	m_scaledItem->StunResist = m_item->StunResist*Mult;
+	m_scaledItem->Avoidance = m_item->Avoidance*Mult;
+	m_scaledItem->CharmFileID = 0;	// this stops the client from trying to scale the item itself.
+}
+
+bool EvoItemInst::EvolveOnAllKills() const {
+	return (m_evolveInfo && m_evolveInfo->AllKills);
+}
+
+sint8 EvoItemInst::GetMaxEvolveLvl() const {
+	if(m_evolveInfo)
+		return m_evolveInfo->MaxLvl;
+	else 
+		return 0;
+}
+
+uint32 EvoItemInst::GetKillsNeeded(uint8 currentlevel) {
+	uint32 kills = -1;	// default to -1 (max uint32 value) because this value is usually divided by, so we don't want to ever return zero.
+	if (m_evolveInfo) 
+		if (currentlevel != m_evolveInfo->MaxLvl) 
+			kills = m_evolveInfo->LvlKills[currentlevel-1];
+
+	if (kills == 0)
+		kills = -1;
+
+	return kills;
+}
+
+EvolveInfo::EvolveInfo() {
+	// nothing here yet
+}
+
+EvolveInfo::EvolveInfo(uint32 first, uint8 max, bool allkills, uint32 L2, uint32 L3, uint32 L4, uint32 L5, uint32 L6, uint32 L7, uint32 L8, uint32 L9, uint32 L10) {
+	FirstItem = first;
+	MaxLvl = max;
+	AllKills = allkills;
+	LvlKills[0] = L2;
+	LvlKills[1] = L3;
+	LvlKills[2] = L4;
+	LvlKills[3] = L5;
+	LvlKills[4] = L6;
+	LvlKills[5] = L7;
+	LvlKills[6] = L8;
+	LvlKills[7] = L9;
+	LvlKills[8] = L10;
+}
+
Index: common/Item.h
===================================================================
--- common/Item.h	(revision 637)
+++ common/Item.h	(working copy)
@@ -27,6 +27,8 @@
 class ItemInstQueue;		// Queue of ItemInst objects (i.e., cursor)
 class Inventory;			// Character inventory
 class ItemParse;			// Parses item packets
+class EvoItemInst;			// Extended item inst, for use with scaling/evolving items
+class EvolveInfo;			// Stores information about an evolving item family
 
 #include <string>
 #include <vector>
@@ -306,7 +308,7 @@
 
 	// Accessors
 	const uint32 GetID() const { return m_item->ID; }
-	const Item_Struct* GetItem() const		{ return m_item; }
+	virtual const Item_Struct* GetItem() const		{ return m_item; }
 	void SetItem(const Item_Struct* item)	{ m_item = item; }
 	
 	sint16 GetCharges() const				{ return m_charges; }
@@ -349,6 +351,9 @@
 	inline sint32 GetSerialNumber() const { return m_SerialNumber; }
 	inline void SetSerialNumber(sint32 id) { m_SerialNumber = id; }
 
+	virtual bool IsScaling() const		{ return false; }
+	virtual bool IsEvolving() const		{ return false; }
+			
 protected:
 	//////////////////////////
 	// Protected Members
@@ -377,4 +382,55 @@
 	
 };
 
+class EvoItemInst: public ItemInst {
+public:
+	// constructor and destructor
+	EvoItemInst(const EvoItemInst& copy);
+	EvoItemInst(const ItemInst& copy);
+	EvoItemInst(const Item_Struct* item = NULL, sint16 charges = 0);
+	~EvoItemInst();
+	
+	// accessors... a lot of these are for evolving items (not complete yet)
+	bool IsScaling() const				{ return (m_evolveLvl == -1); }
+	bool IsEvolving() const				{ return (m_evolveLvl >= 1); }
+	uint32 GetExp() const				{ return m_exp; }
+	void SetExp(uint32 exp)				{ m_exp = exp; }
+	void AddExp(uint32 exp)				{ m_exp += exp; }
+	bool IsActivated()					{ return m_activated; }
+	void SetActivated(bool activated)	{ m_activated = activated; }
+	sint8 GetEvolveLvl() const			{ return m_evolveLvl; }
+		
+	EvoItemInst* Clone() const;
+	const Item_Struct* GetItem() const;
+	const Item_Struct* GetUnscaledItem() const;
+	void Initialize(SharedDatabase *db = NULL);
+	void ScaleItem();
+	bool EvolveOnAllKills() const;	
+	sint8 GetMaxEvolveLvl() const;
+	uint32 GetKillsNeeded(uint8 currentlevel);
+		
+
+private:
+	uint32				m_exp;
+	sint8				m_evolveLvl;
+	bool				m_activated;
+	Item_Struct*		m_scaledItem;
+	const EvolveInfo*	m_evolveInfo;
+};
+
+class EvolveInfo {
+public:
+	friend class EvoItemInst;
+	//temporary
+	uint16				LvlKills[9];
+	uint32				FirstItem;
+	uint8				MaxLvl;
+	bool				AllKills;
+		
+	EvolveInfo();
+	EvolveInfo(uint32 first, uint8 max, bool allkills, uint32 L2, uint32 L3, uint32 L4, uint32 L5, uint32 L6, uint32 L7, uint32 L8, uint32 L9, uint32 L10);
+	~EvolveInfo();
+};
+	
+
 #endif // #define __ITEM_H
Index: common/shareddb.cpp
===================================================================
--- common/shareddb.cpp	(revision 637)
+++ common/shareddb.cpp	(working copy)
@@ -394,10 +394,13 @@
 		myitem = GetItem(itemid);
 		if(!myitem)
 			continue;
-		ItemInst myinst(myitem, charges);
+		
+		ItemInst* myinst = CreateBaseItem(myitem, charges);		
+		
 		if(slot < 0)
 			slot = inv->FindFreeSlot(0,0);
-		inv->PutItem(slot, myinst);
+		inv->PutItem(slot, *myinst);
+		safe_delete(myinst);
 	}
 
 	if(result) mysql_free_result(result);
@@ -442,16 +445,19 @@
 			if (item) {
 				sint16 put_slot_id = SLOT_INVALID;
 				
-				ItemInst inst(item, charges);
+				ItemInst* inst = CreateBaseItem(item, charges);
+				
 				if (item->ItemClass == ItemClassCommon) {
 					for(int i=0;i<5;i++) {
 						if (aug[i]) {
-							inst.PutAugment(this, i, aug[i]);
+							inst->PutAugment(this, i, aug[i]);
 						}
 					}
 				}
-				put_slot_id = inv->PutItem(slot_id, inst);
 				
+				put_slot_id = inv->PutItem(slot_id, *inst);
+				safe_delete(inst);
+				
 				// Save ptr to item in inventory
 				if (put_slot_id == SLOT_INVALID) {
 					LogFile->write(EQEMuLog::Error,
@@ -511,28 +517,30 @@
 			if (item) {
 				sint16 put_slot_id = SLOT_INVALID;
 				
-				ItemInst inst(item, charges);
-				if (instnodrop || (slot_id >= 0 && slot_id <= 21 && inst.GetItem()->Attuneable))
-						inst.SetInstNoDrop(true);
+				ItemInst* inst = CreateBaseItem(item, charges);
+				
+				if (instnodrop || (slot_id >= 0 && slot_id <= 21 && inst->GetItem()->Attuneable))
+						inst->SetInstNoDrop(true);
 				if (color > 0)
-					inst.SetColor(color);
+					inst->SetColor(color);
 				if(charges==255)
-					inst.SetCharges(-1);
+					inst->SetCharges(-1);
 				else
-					inst.SetCharges(charges);
+					inst->SetCharges(charges);
 
 				if (item->ItemClass == ItemClassCommon) {
 					for(int i=0;i<5;i++) {
 						if (aug[i]) {
-							inst.PutAugment(this, i, aug[i]);
+							inst->PutAugment(this, i, aug[i]);
 						}
 					}
 				}
 
 				if (slot_id>=8000 && slot_id <= 8999)
-					put_slot_id = inv->PushCursor(inst);
+					put_slot_id = inv->PushCursor(*inst);
 				else 
-					put_slot_id = inv->PutItem(slot_id, inst);
+					put_slot_id = inv->PutItem(slot_id, *inst);
+				safe_delete(inst);
 				
 				// Save ptr to item in inventory
 				if (put_slot_id == SLOT_INVALID) {
@@ -590,23 +598,26 @@
 			if(!item)
 				continue;
 
-			ItemInst inst(item, charges);
-			inst.SetInstNoDrop(instnodrop);
+			ItemInst* inst = CreateBaseItem(item, charges);
+						
+			inst->SetInstNoDrop(instnodrop);
 			if (color > 0)
-				inst.SetColor(color);
-			inst.SetCharges(charges);
+				inst->SetColor(color);
+			inst->SetCharges(charges);
 
 			if (item->ItemClass == ItemClassCommon) {
 				for(int i=0;i<5;i++) {
 					if (aug[i]) {
-						inst.PutAugment(this, i, aug[i]);
+						inst->PutAugment(this, i, aug[i]);
 					}
 				}
 			}
+
 			if (slot_id>=8000 && slot_id <= 8999)
-				put_slot_id = inv->PushCursor(inst);
+				put_slot_id = inv->PushCursor(*inst);
 			else 
-				put_slot_id = inv->PutItem(slot_id, inst);
+				put_slot_id = inv->PutItem(slot_id, *inst);
+			safe_delete(inst);
 			
 			// Save ptr to item in inventory
 			if (put_slot_id == SLOT_INVALID) {
@@ -1241,7 +1252,7 @@
 	ItemInst* inst = NULL;
 	item = GetItem(item_id);
 	if (item) {
-		inst = new ItemInst(item, charges);
+		inst = CreateBaseItem(item, charges);
 		inst->PutAugment(this, 0, aug1);
 		inst->PutAugment(this, 1, aug2);
 		inst->PutAugment(this, 2, aug3);
@@ -1261,18 +1272,34 @@
 	if (item) {
 		if (charges == 0)
 			charges = item->MaxCharges;
-		inst = new ItemInst(item, charges);
+		inst = CreateBaseItem(item, charges);
 		inst->PutAugment(this, 0, aug1);
 		inst->PutAugment(this, 1, aug2);
 		inst->PutAugment(this, 2, aug3);
 		inst->PutAugment(this, 3, aug4);
 		inst->PutAugment(this, 4, aug5);
-		inst->SetCharges(charges);
+		//inst->SetCharges(charges);
 	}
 	
 	return inst;
 }
 
+ItemInst* SharedDatabase::CreateBaseItem(const Item_Struct* item, sint16 charges) {
+	ItemInst* inst = NULL;
+	if (item) {
+		if (charges == 0)
+			charges = item->MaxCharges;
+
+		if(item->CharmFileID != 0 || (item->LoreGroup >= 1000 && item->LoreGroup != -1)) {
+			inst = new EvoItemInst(item, charges);
+			((EvoItemInst*)inst)->Initialize(this);
+		}
+		else 
+			inst = new ItemInst(item, charges);		
+	}
+	return inst;
+}
+
 sint32 SharedDatabase::DeleteStalePlayerCorpses() {
 	char errbuf[MYSQL_ERRMSG_SIZE];
     char *query = 0;
@@ -1489,3 +1516,6 @@
 	}
 }
 
+const EvolveInfo* SharedDatabase::GetEvolveInfo(uint32 loregroup) {
+	return NULL;	// nothing here for now... database and/or sharemem pulls later
+}
\ No newline at end of file
Index: common/shareddb.h
===================================================================
--- common/shareddb.h	(revision 637)
+++ common/shareddb.h	(working copy)
@@ -6,6 +6,7 @@
 #include "database.h"
 #include "skills.h"
 #include "../zone/spdat.h"
+#include "item.h"
 
 #include <list>
 
@@ -62,6 +63,7 @@
 	 */
 	ItemInst* CreateItem(uint32 item_id, sint16 charges=0, uint32 aug1=0, uint32 aug2=0, uint32 aug3=0, uint32 aug4=0, uint32 aug5=0);
 	ItemInst* CreateItem(const Item_Struct* item, sint16 charges=0, uint32 aug1=0, uint32 aug2=0, uint32 aug3=0, uint32 aug4=0, uint32 aug5=0);
+	ItemInst* CreateBaseItem(const Item_Struct* item, sint16 charges=0);
 	
 	
 	/*
@@ -72,7 +74,8 @@
 	inline const int32	GetMaxLootDropID()		{ return lootdrop_max; }
 	inline const int32	GetMaxNPCType()			{ return max_npc_type; }
 	inline const int32  GetMaxNPCFactionList()	{ return npcfactionlist_max; }
-	const Item_Struct*		GetItem(uint32 id);
+	const Item_Struct*	GetItem(uint32 id);
+	const EvolveInfo*	GetEvolveInfo(uint32 loregroup);
 	const NPCFactionList*	GetNPCFactionEntry(uint32 id);
 	int16	GetSkillCap(int8 Class_, SkillType Skill, int8 Level);
 	int8	GetTrainLevel(int8 Class_, SkillType Skill, int8 Level);
Index: zone/bonuses.cpp
===================================================================
--- zone/bonuses.cpp	(revision 637)
+++ zone/bonuses.cpp	(working copy)
@@ -26,6 +26,7 @@
 #include "../common/bodytypes.h"
 #include "../common/classes.h"
 #include "../common/rulesys.h"
+#include "embparser.h"
 #include <math.h>
 #include <assert.h>
 #ifndef WIN32
@@ -147,6 +148,9 @@
 		const ItemInst* inst = m_inv[i];
 		if(inst == 0)
 			continue;
+		if(inst->IsScaling()) {
+			CalcScale(const_cast<ItemInst*>(inst), i);
+		}
 		AddItemBonuses(inst, newbon);
 	}
 	
@@ -1176,3 +1180,17 @@
 	
 	}
 }
+
+void Client::CalcScale(ItemInst* iteminst, sint16 slot_id) {
+	EvoItemInst* inst = (EvoItemInst*)iteminst;
+	uint16 oldexp = inst->GetExp();
+		
+	#ifdef EMBPERL
+		((PerlembParser *)parse)->Event(EVENT_SCALE_CALC, inst->GetID(), "", inst, this);
+	#endif
+
+	if (inst->GetExp() != oldexp) {	// if the scaling factor changed, rescale the item and update the client
+		inst->ScaleItem();
+		this->SendItemPacket(slot_id, inst, ItemPacketTrade);
+	}
+}
Index: zone/client.h
===================================================================
--- zone/client.h	(revision 637)
+++ zone/client.h	(working copy)
@@ -44,6 +44,7 @@
 #include "questmgr.h"
 #include <float.h>
 #include <set>
+#include "../common/item_struct.h"
 
 #define CLIENT_TIMEOUT		90000
 #define CLIENT_LD_TIMEOUT	30000 // length of time client stays in zone after LDing
@@ -439,6 +440,7 @@
 	FACTION_VALUE	GetReverseFactionCon(Mob* iOther);
     FACTION_VALUE   GetFactionLevel(int32 char_id, int32 npc_id, int32 p_race, int32 p_class, int32 p_deity, sint32 pFaction, Mob* tnpc);
 	sint32	GetCharacterFactionLevel(sint32 faction_id);
+	sint32  GetModCharacterFactionLevel(sint32 faction_id);
 
 	void	SetFactionLevel(int32 char_id, int32 npc_id, int8 char_class, int8 char_race, int8 char_deity);
 	void    SetFactionLevel2(int32 char_id, sint32 faction_id, int8 char_class, int8 char_race, int8 char_deity, sint32 value);
@@ -835,6 +837,8 @@
 	void HandleLDoNPickLock(NPC *target, int16 skill, int8 type);
 	int	LDoNChest_SkillCheck(NPC *target, int skill);
 
+	void CalcScale(ItemInst* inst, sint16 slot_id);	// determine the multiplication factor for scaled items
+		
 protected:
 	friend class Mob;
 	void CalcItemBonuses(StatBonuses* newbon);
Index: zone/embparser.cpp
===================================================================
--- zone/embparser.cpp	(revision 637)
+++ zone/embparser.cpp	(working copy)
@@ -65,7 +65,8 @@
 	"EVENT_PLAYER_PICKUP",
 	"EVENT_POPUPRESPONSE",
 	"EVENT_PROXIMITY_SAY",
-	"EVENT_CAST"
+	"EVENT_CAST",
+	"EVENT_SCALE_CALC"
 };
 
 PerlembParser::PerlembParser(void) : Parser()
@@ -187,13 +188,13 @@
 		EventRecord e = eventQueue.front();
 		eventQueue.pop();
 
-		Event(e.event, e.npcid, e.data.c_str(), e.npcmob, e.mob, e.extradata);
+		EventCommon(e.event, e.objid, e.data.c_str(), e.npcmob, e.iteminst, e.mob, e.extradata);
 	}
 
 	eventQueueProcessing = false;
 }
 
-void PerlembParser::Event(QuestEventID event, int32 npcid, const char * data, NPC* npcmob, Mob* mob, int32 extradata)
+void PerlembParser::EventCommon(QuestEventID event, int32 objid, const char * data, NPC* npcmob, ItemInst* iteminst, Mob* mob, int32 extradata)
 {
 	char errbuf[MYSQL_ERRMSG_SIZE];
 	char *query = 0;
@@ -210,10 +211,11 @@
 		//queue the event for later.
 		EventRecord e;
 		e.event = event;
-		e.npcid = npcid;
+		e.objid = objid;
 		if(data != NULL)
 			e.data = data;
 		e.npcmob = npcmob;
+		e.iteminst = iteminst;
 		e.mob = mob;
 		e.extradata = extradata;
 		eventQueue.push(e);
@@ -221,19 +223,41 @@
 	}
 
 	bool isPlayerQuest = false;
-	if(!npcmob && mob)
-		isPlayerQuest = true;
-
+	bool isItemQuest = false;
+	if(!npcmob && mob) {
+		if(!iteminst) 
+			isPlayerQuest = true;
+		else 
+			isItemQuest = true;
+	}
+	
 	string packagename;
 	
-	if(!isPlayerQuest){
-		packagename = GetPkgPrefix(npcid);
+	if(!isPlayerQuest && !isItemQuest){
+		packagename = GetPkgPrefix(objid);
 
 		if(!isloaded(packagename.c_str()))
 		{
-			LoadScript(npcid, zone->GetShortName());
+			LoadScript(objid, zone->GetShortName());
 		}
 	}
+	else if(isItemQuest) {
+		const Item_Struct* item = iteminst->GetItem();
+		if (!item) return;
+
+		if (event == EVENT_SCALE_CALC) {
+			packagename = item->CharmFile;
+			if(!isloaded(packagename.c_str())) {
+				LoadItemScript(iteminst, packagename, itemQuestScale);
+			}
+		}
+		else {
+			packagename = "item_";
+			packagename += itoa(objid);
+			if(!isloaded(packagename.c_str()))
+				LoadItemScript(iteminst, packagename, itemQuestID);
+		}
+	}
 	else {
 		packagename = "player";
 		packagename += "_"; 
@@ -262,7 +286,7 @@
 
 	ExportVar(packagename.c_str(), "charid", charid);
 
-	if(!isPlayerQuest){
+	if(!isPlayerQuest && !isItemQuest){
 		//only export globals if the npcmob has the qglobal flag
 		if(npcmob && npcmob->GetQglobal()){
 			// Delete expired global variables
@@ -337,7 +361,7 @@
 //		ExportVar(packagename.c_str(), "cumflag", mob->CastToClient()->flag[50]);
 	}
 
-	if(!isPlayerQuest){
+	if(!isPlayerQuest && !isItemQuest){
 		if (mob && npcmob && mob->IsClient() && npcmob->IsNPC()) {
 			Client* client = mob->CastToClient();
 			NPC* npc = npcmob->CastToNPC();
@@ -362,7 +386,7 @@
 		ExportVar(packagename.c_str(), "userid", mob->GetID());
 	}
 
-	if(!isPlayerQuest){
+	if(!isPlayerQuest && !isItemQuest){
 		if (npcmob)
 		{
 			ExportVar(packagename.c_str(), "mname", npcmob->GetName());
@@ -440,7 +464,7 @@
 	switch (event) {
 		case EVENT_SAY: {
 			npcmob->FaceTarget(mob);
-			ExportVar(packagename.c_str(), "data", npcid);
+			ExportVar(packagename.c_str(), "data", objid);
 			ExportVar(packagename.c_str(), "text", data);
 			ExportVar(packagename.c_str(), "langid", extradata);
 			break;
@@ -448,14 +472,14 @@
 		case EVENT_ITEM: {
 			npcmob->FaceTarget(mob);
 			//this is such a hack... why arnt these just set directly..
-			ExportVar(packagename.c_str(), "item1", GetVar("item1", npcid).c_str());
-			ExportVar(packagename.c_str(), "item2", GetVar("item2", npcid).c_str());
-			ExportVar(packagename.c_str(), "item3", GetVar("item3", npcid).c_str());
-			ExportVar(packagename.c_str(), "item4", GetVar("item4", npcid).c_str());
-			ExportVar(packagename.c_str(), "copper", GetVar("copper", npcid).c_str());
-			ExportVar(packagename.c_str(), "silver", GetVar("silver", npcid).c_str());
-			ExportVar(packagename.c_str(), "gold", GetVar("gold", npcid).c_str());
-			ExportVar(packagename.c_str(), "platinum", GetVar("platinum", npcid).c_str());
+			ExportVar(packagename.c_str(), "item1", GetVar("item1", objid).c_str());
+			ExportVar(packagename.c_str(), "item2", GetVar("item2", objid).c_str());
+			ExportVar(packagename.c_str(), "item3", GetVar("item3", objid).c_str());
+			ExportVar(packagename.c_str(), "item4", GetVar("item4", objid).c_str());
+			ExportVar(packagename.c_str(), "copper", GetVar("copper", objid).c_str());
+			ExportVar(packagename.c_str(), "silver", GetVar("silver", objid).c_str());
+			ExportVar(packagename.c_str(), "gold", GetVar("gold", objid).c_str());
+			ExportVar(packagename.c_str(), "platinum", GetVar("platinum", objid).c_str());
 			string hashname = packagename + std::string("::itemcount");
 			perl->eval(std::string("%").append(hashname).append(" = ();").c_str());
 			perl->eval(std::string("++$").append(hashname).append("{$").append(packagename).append("::item1};").c_str());
@@ -531,7 +555,7 @@
 		}
 
 		case EVENT_AGGRO_SAY: {
-			ExportVar(packagename.c_str(), "data", npcid);
+			ExportVar(packagename.c_str(), "data", objid);
 			ExportVar(packagename.c_str(), "text", data);
 			ExportVar(packagename.c_str(), "langid", extradata);
 			break;
@@ -541,11 +565,16 @@
 			break;
 		}
 		case EVENT_PROXIMITY_SAY: {
-			ExportVar(packagename.c_str(), "data", npcid);
+			ExportVar(packagename.c_str(), "data", objid);
 			ExportVar(packagename.c_str(), "text", data);
 			ExportVar(packagename.c_str(), "langid", extradata);
 			break;
 		}
+		case EVENT_SCALE_CALC: {
+			ExportVar(packagename.c_str(), "itemid", objid);
+			ExportVar(packagename.c_str(), "itemname", iteminst->GetItem()->Name);
+			break;
+		}
 		//nothing special about these events
 		case EVENT_DEATH:
 		case EVENT_SPAWN:
@@ -566,16 +595,27 @@
 	}
 
 	if(isPlayerQuest){
-		SendCommands(packagename.c_str(), sub_name, 0, mob, mob);
+		this->SendCommands(packagename.c_str(), sub_name, 0, mob, mob, NULL);
 	}
+	else if(isItemQuest) {
+		this->SendCommands(packagename.c_str(), sub_name, 0, mob, mob, iteminst);
+	}
 	else {
-		SendCommands(packagename.c_str(), sub_name, npcid, npcmob, mob);
+		this->SendCommands(packagename.c_str(), sub_name, objid, npcmob, mob, NULL);
 	}
 
 	//now handle any events that cropped up...
 	HandleQueue();
 }
 
+void PerlembParser::Event(QuestEventID event, int32 npcid, const char* data, NPC* npcmob, Mob* mob, int32 extradata) {
+	EventCommon(event, npcid, data, npcmob, (ItemInst*)NULL, mob, extradata);
+}
+
+void PerlembParser::Event(QuestEventID event, int32 itemid, const char* data, ItemInst* iteminst, Mob* mob, int32 extradata) {
+	EventCommon(event, itemid, data, (NPC*)NULL, iteminst, mob, extradata);
+}
+
 void PerlembParser::ReloadQuests() {
 
 	command_clear_perl();
@@ -604,6 +644,7 @@
 
 	hasQuests.clear();
 	playerQuestLoaded.clear();
+	itemQuestLoaded.clear();
 }
 
 int PerlembParser::LoadScript(int npcid, const char * zone, Mob* activater)
@@ -846,6 +887,42 @@
 	return 1;
 }
 
+int PerlembParser::LoadItemScript(ItemInst* iteminst, string packagename, itemQuestMode Qtype) {
+	if(!perl)
+		return 0;
+
+	// if we've already tried to load it, don't try again
+	if(itemQuestLoaded.count(packagename) == 1)
+		return 1;
+
+	string filename = "quests/items/";
+	if(Qtype == itemQuestScale)
+		filename += packagename;
+	else if(Qtype == itemQuestLore) {
+		filename += "lore_";
+		filename += itoa(iteminst->GetItem()->LoreGroup);
+	}
+	else
+		filename += itoa(iteminst->GetID());
+	filename += ".pl";
+	printf("Loading file %s\n",filename.c_str());
+
+	try {
+		perl->eval_file(packagename.c_str(), filename.c_str());
+	}
+	catch(const char* err) {
+		LogFile->write(EQEMuLog::Quest, "WARNING: error compiling quest file %s: %s", filename.c_str(), err);
+	}
+
+	if(!isloaded(packagename.c_str())) {
+		itemQuestLoaded[packagename] = Qtype;
+		return 0;
+	}
+
+	itemQuestLoaded[packagename] = itemQuestUnloaded;
+	return 1;
+}
+
 bool PerlembParser::isloaded(const char *packagename) const {
 	char buffer[120];
 	snprintf(buffer, 120, "$%s::isloaded", packagename);
@@ -926,7 +1003,7 @@
 	return(std::string(buf));
 }
 
-void PerlembParser::SendCommands(const char * pkgprefix, const char *event, int32 npcid, Mob* other, Mob* mob)
+void PerlembParser::SendCommands(const char * pkgprefix, const char *event, int32 npcid, Mob* other, Mob* mob, ItemInst* iteminst)
 {
 	if(!perl)
 		return;
Index: zone/embparser.h
===================================================================
--- zone/embparser.h	(revision 637)
+++ zone/embparser.h	(working copy)
@@ -31,11 +31,19 @@
 	pQuestEventCast	// player.pl loaded, has an EVENT_CAST sub
 } playerQuestMode;
 
+typedef enum {
+	itemQuestUnloaded = 1,
+	itemQuestScale,
+	itemQuestLore,
+	itemQuestID
+} itemQuestMode;
+
 struct EventRecord {
 	QuestEventID event;
-	int32 npcid;
+	int32 objid;
 	string data;
 	NPC* npcmob;
+	ItemInst* iteminst;
 	Mob* mob;
 	int32 extradata;
 };
@@ -49,12 +57,14 @@
 	//if they do not have a quest or the default.
 	map<int32, questMode> hasQuests;	//npcid -> questMode
 	map<std::string, playerQuestMode> playerQuestLoaded; //zone shortname -> playerQuestMode
+	map<std::string, itemQuestMode> itemQuestLoaded;		// package name - > itemQuestMode
 
 	queue<EventRecord> eventQueue;		//for events that happen when perl is in use.
 	bool eventQueueProcessing;
 	
 	void HandleQueue();
-	
+	void EventCommon(QuestEventID event, int32 objid, const char * data, NPC* npcmob, ItemInst* iteminst, Mob* mob, int32 extradata);
+
 	Embperl * perl;
 	//export a symbol table of sorts
 	virtual void map_funs();
@@ -66,8 +76,10 @@
 	bool isloaded(const char *packagename) const;
 //	bool isdefault(const char *packagename) const { return perl->geti(std::string("$").append(packagename).append("::isdefault").c_str()); }
 	void Event(QuestEventID event, int32 npcid, const char * data, NPC* npcmob, Mob* mob, int32 extradata = 0);
+	void Event(QuestEventID event, int32 itemid, const char * data, ItemInst* iteminst, Mob* mob, int32 extradata = 0);
 	int LoadScript(int npcid, const char * zone, Mob* activater=0);
 	int LoadPlayerScript(const char *zone);
+	int LoadItemScript(ItemInst* iteminst, string packagename, itemQuestMode Qtype);
 	
 	//expose a var to the script (probably parallels addvar))
 	//i.e. exportvar("qst1234", "name", "somemob"); 
@@ -85,7 +97,7 @@
 	std::string GetPkgPrefix(int32 npcid, bool defaultOK = true);
 	//call the appropriate perl handler. afterwards, parse and dispatch the command queue
 	//SendCommands("qst1234", "EVENT_SAY") would trigger sub EVENT_SAY() from the qst1234.pl file
-	virtual void SendCommands(const char * pkgprefix, const char *event, int32 npcid, Mob* other, Mob* mob);
+	virtual void SendCommands(const char * pkgprefix, const char *event, int32 npcid, Mob* other, Mob* mob, ItemInst* iteminst);
 	virtual void ReloadQuests();
 	
 	int	HasQuestFile(int32 npcid);
Index: zone/embperl.cpp
===================================================================
--- zone/embperl.cpp	(revision 637)
+++ zone/embperl.cpp	(working copy)
@@ -37,6 +37,7 @@
 EXTERN_C XS(boot_Group);
 EXTERN_C XS(boot_Raid);
 EXTERN_C XS(boot_PerlPacket);
+EXTERN_C XS(boot_QuestItem);
 /*XS(XS_Client_new);
 //XS(XS_Mob_new);
 XS(XS_NPC_new);
@@ -84,6 +85,7 @@
 	newXS(strcpy(buf, "PerlPacket::boot_PerlPacket"), boot_PerlPacket, file);
 	newXS(strcpy(buf, "Group::boot_Group"), boot_Group, file);
 	newXS(strcpy(buf, "Raid::boot_Raid"), boot_Raid, file);
+	newXS(strcpy(buf, "QuestItem::boot_QuestItem"), boot_QuestItem, file);
 #endif
 #endif
 #ifdef EMBPERL_COMMANDS
Index: zone/event_codes.h
===================================================================
--- zone/event_codes.h	(revision 637)
+++ zone/event_codes.h	(working copy)
@@ -31,6 +31,7 @@
 	EVENT_POPUPRESPONSE,
 	EVENT_PROXIMITY_SAY,
 	EVENT_CAST,
+	EVENT_SCALE_CALC,
 	
 	_LargestEventID
 } QuestEventID;
Index: zone/faction.cpp
===================================================================
--- zone/faction.cpp	(revision 637)
+++ zone/faction.cpp	(working copy)
@@ -633,6 +633,18 @@
 	return(res->second);
 }
 
+// returns the character's faction level, adjusted for racial, class, and deity modifiers
+sint32 Client::GetModCharacterFactionLevel(sint32 faction_id) {
+	sint32 Modded = GetCharacterFactionLevel(faction_id);
+	FactionMods fm;
+	if(database.GetFactionData(&fm,GetClass(),GetRace(),GetDeity(),faction_id)) 
+		Modded += fm.base + fm.class_mod + fm.race_mod + fm.deity_mod;
+	if (Modded > MAX_FACTION)
+		Modded = MAX_FACTION;
+
+	return Modded;
+}
+
 bool ZoneDatabase::GetFactionData(FactionMods* fm, uint32 class_mod, uint32 race_mod, uint32 deity_mod, sint32 faction_id) {
 	if (faction_id <= 0 || faction_id > (sint32) max_faction)
 		return false;	
Index: zone/makefile.common
===================================================================
--- zone/makefile.common	(revision 637)
+++ zone/makefile.common	(working copy)
@@ -15,7 +15,7 @@
    doors.o command.o beacon.o embxs.o AA.o trap.o client_packet.o \
    bonuses.o trading.o spdat.o  spell_effects.o aggro.o guild.o \
    inventory.o client_mods.o tradeskills.o waypoints.o pets.o zone_profile.o \
-   effects.o perl_client.o perl_entity.o perl_mob.o perl_npc.o \
+   effects.o perl_client.o perl_entity.o perl_mob.o perl_questitem.o perl_npc.o \
    perl_PlayerCorpse.o perl_groups.o perl_raids.o questmgr.o client_logs.o perlparser.o \
    ../common/rdtsc.o ../common/extprofile.o horse.o exp.o pathing.o \
    fearpath.o special_attacks.o ../common/timeoutmgr.o ../common/Condition.o \
Index: zone/perl_client.cpp
===================================================================
--- zone/perl_client.cpp	(revision 637)
+++ zone/perl_client.cpp	(working copy)
@@ -3394,6 +3394,33 @@
 	XSRETURN(1);
 }
 
+XS(XS_Client_GetModCharacterFactionLevel); /* prototype to pass -Wmissing-prototypes */
+XS(XS_Client_GetModCharacterFactionLevel)
+{
+	dXSARGS;
+	if (items != 2)
+		Perl_croak(aTHX_ "Usage: Client::GetModCharacterFactionLevel(THIS, faction_id)");
+	{
+		Client *	THIS;
+		sint32		RETVAL;
+		dXSTARG;
+		sint32		faction_id = (sint32)SvIV(ST(1));
+
+		if (sv_derived_from(ST(0), "Client")) {
+			IV tmp = SvIV((SV*)SvRV(ST(0)));
+			THIS = INT2PTR(Client *,tmp);
+		}
+		else
+			Perl_croak(aTHX_ "THIS is not of type Client");
+		if(THIS == NULL)
+			Perl_croak(aTHX_ "THIS is NULL, avoiding crash.");
+
+		RETVAL = THIS->GetModCharacterFactionLevel(faction_id);
+		XSprePUSH; PUSHi((IV)RETVAL);
+	}
+	XSRETURN(1);
+}
+
 XS(XS_Client_SetZoneFlag); /* prototype to pass -Wmissing-prototypes */
 XS(XS_Client_SetZoneFlag)
 {
@@ -3896,6 +3923,7 @@
 		newXSproto(strcpy(buf, "ResetTrade"), XS_Client_ResetTrade, file, "$");
 		newXSproto(strcpy(buf, "UseDiscipline"), XS_Client_UseDiscipline, file, "$$$");
 		newXSproto(strcpy(buf, "GetCharacterFactionLevel"), XS_Client_GetCharacterFactionLevel, file, "$$");
+		newXSproto(strcpy(buf, "GetModCharacterFactionLevel"), XS_Client_GetModCharacterFactionLevel, file, "$$");
 		newXSproto(strcpy(buf, "SetZoneFlag"), XS_Client_SetZoneFlag, file, "$$");
 		newXSproto(strcpy(buf, "ClearZoneFlag"), XS_Client_ClearZoneFlag, file, "$$");
 		newXSproto(strcpy(buf, "HasZoneFlag"), XS_Client_HasZoneFlag, file, "$$");
Index: zone/perl_groups.cpp
===================================================================
--- zone/perl_groups.cpp	(revision 637)
+++ zone/perl_groups.cpp	(working copy)
@@ -575,6 +575,42 @@
 	XSRETURN(1);
 }
 
+XS(XS_Group_GetMember);
+XS(XS_Group_GetMember) 
+{
+	dXSARGS;
+	if (items != 2)
+		Perl_croak(aTHX_ "Usage: Group::GetMember(THIS, index)");
+	{
+		Group * THIS;
+		Mob* member;
+		Client*	RETVAL = NULL;
+		dXSTARG;
+
+		if (sv_derived_from(ST(0), "Group")) {
+			IV tmp = SvIV((SV*)SvRV(ST(0)));
+			THIS = INT2PTR(Group *,tmp);
+		}
+		else
+			Perl_croak(aTHX_ "THIS is not of type Group");
+		if(THIS == NULL)
+			Perl_croak(aTHX_ "THIS is NULL, avoiding crash.");
+
+		int index = (int)SvUV(ST(1));
+		if (index < 0 || index > 5) 
+			RETVAL = NULL;
+		else {
+			member = THIS->members[index];
+			if (member != NULL)
+				RETVAL = member->CastToClient();
+		}
+
+		ST(0) = sv_newmortal();
+		sv_setref_pv(ST(0), "Client", (void*)RETVAL);
+	}
+	XSRETURN(1);
+}
+
 #ifdef __cplusplus
 extern "C"
 #endif
@@ -613,6 +649,7 @@
 		newXSproto(strcpy(buf, "GetHighestLevel"), XS_Group_GetHighestLevel, file, "$");
 		newXSproto(strcpy(buf, "TeleportGroup"), XS_Group_TeleportGroup, file, "$$$$$$$");
 		newXSproto(strcpy(buf, "GetID"), XS_Group_GetID, file, "$");
+		newXSproto(strcpy(buf, "GetMember"), XS_Group_GetMember, file, "$");
 	XSRETURN_YES;
 }
 
Index: zone/perl_questitem.cpp
===================================================================
--- zone/perl_questitem.cpp	(revision 0)
+++ zone/perl_questitem.cpp	(revision 0)
@@ -0,0 +1,145 @@
+/*  EQEMu:  Everquest Server Emulator
+	Copyright (C) 2001-2004  EQEMu Development Team (http://eqemulator.net)
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; version 2 of the License.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY except by those people which sell it, which
+		are required to give you total support for your newly bought product;
+		without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+		A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "features.h"
+#include "client.h"
+#ifdef EMBPERL_XS_CLASSES
+#include "../common/debug.h"
+#include "embperl.h"
+
+#include "../common/item.h"
+
+#ifdef THIS		/* this macro seems to leak out on some systems */
+#undef THIS
+#endif
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+
+XS(XS_QuestItem_GetName);
+XS(XS_QuestItem_GetName) {
+	dXSARGS;
+	if (items != 1)
+		Perl_croak(aTHX_ "Usage: QuestItem::GetLastName(THIS)");
+	{
+		ItemInst *			THIS;
+		Const_char *		RETVAL;
+		dXSTARG;
+
+		if (sv_derived_from(ST(0), "QuestItem")) {
+			IV tmp = SvIV((SV*)SvRV(ST(0)));
+			THIS = INT2PTR(ItemInst *,tmp);
+		}
+		else
+			Perl_croak(aTHX_ "THIS is not of type ItemInst");
+		if(THIS == NULL)
+			Perl_croak(aTHX_ "THIS is NULL, avoiding crash.");
+
+		RETVAL = THIS->GetItem()->Name;
+		sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG;
+	}
+	XSRETURN(1);
+}
+
+XS(XS_QuestItem_SetScale);
+XS(XS_QuestItem_SetScale) 
+{
+	dXSARGS;
+	if (items != 2)
+		Perl_croak(aTHX_ "Usage: QuestItem::GetID(THIS, scale factor)");
+	{
+		ItemInst *	THIS;
+		float		Mult;
+		
+		if (sv_derived_from(ST(0), "QuestItem")) {
+			IV tmp = SvIV((SV*)SvRV(ST(0)));
+			THIS = INT2PTR(ItemInst *,tmp);
+		}
+		else
+			Perl_croak(aTHX_ "THIS is not of type ItemInst");
+		if(THIS == NULL)
+			Perl_croak(aTHX_ "THIS is NULL, avoiding crash.");
+
+		Mult = (float)SvNV(ST(1));
+
+		if(THIS->IsScaling()) {
+			((EvoItemInst*)THIS)->SetExp((int)(Mult*10000+.5));
+		}
+	}
+	XSRETURN_EMPTY;
+}
+
+XS(XS_QuestItem_ItemSay);
+XS(XS_QuestItem_ItemSay) 
+{
+	dXSARGS;
+	if (items != 2 && items != 3)
+		Perl_croak(aTHX_ "Usage: QuestItem::ItemSay(THIS, text [, language])");
+	{
+		ItemInst*	THIS;
+		Const_char*	text;
+		int			lang = 0;
+
+		if (sv_derived_from(ST(0), "QuestItem")) {
+			IV tmp = SvIV((SV*)SvRV(ST(0)));
+			THIS = INT2PTR(ItemInst *,tmp);
+		}
+		else
+			Perl_croak(aTHX_ "THIS is not of type ItemInst");
+		if(THIS == NULL)
+			Perl_croak(aTHX_ "THIS is NULL, avoiding crash.");
+
+		text = SvPV_nolen(ST(1));
+		if(items == 3)
+			lang = (int)SvUV(ST(2));
+
+		quest_manager.GetInitiator()->ChannelMessageSend(THIS->GetItem()->Name, 0, 8, lang, 100, text);
+	}
+	XSRETURN_EMPTY;
+}
+
+
+
+
+
+XS(boot_QuestItem);
+XS(boot_QuestItem) 
+{
+	dXSARGS;
+	char file[256];
+	strncpy(file, __FILE__, 256);
+	file[255] = 0;
+
+	if(items != 1)
+		fprintf(stderr, "boot_quest does not take any arguments.");
+	char buf[128];
+
+	//add the strcpy stuff to get rid of const warnings....
+
+	XS_VERSION_BOOTCHECK ;
+
+		newXSproto(strcpy(buf, "GetName"), XS_QuestItem_GetName, file, "$");
+		newXSproto(strcpy(buf, "SetScale"), XS_QuestItem_SetScale, file, "$");
+		newXSproto(strcpy(buf, "ItemSay"), XS_QuestItem_ItemSay, file, "$");
+
+	XSRETURN_YES;
+}
+
+#endif //EMBPERL_XS_CLASSES
Index: zone/perlparser.cpp
===================================================================
--- zone/perlparser.cpp	(revision 637)
+++ zone/perlparser.cpp	(working copy)
@@ -41,7 +41,7 @@
 */
 
 PerlXSParser::PerlXSParser() : PerlembParser() {
-	//we cannot rely on PerlembParser to call the rigth map_funs because
+	//we cannot rely on PerlembParser to call the right map_funs because
 	//our virtual table is not set up until after we call them, so we need to move
 	//the call to ReloadQuests out of the constructor.
 }
@@ -83,22 +83,25 @@
 
 	"package Raid;"
 	"&boot_Raid;"		//load our Raid XS
+
+	"package QuestItem;"
+	"&boot_QuestItem;"	// load quest item XS
 #endif
 	"package main;"
 	"}"
 	);//eval
 }
 
-void PerlXSParser::SendCommands(const char * pkgprefix, const char *event, int32 npcid, Mob* other, Mob* mob)
+void PerlXSParser::SendCommands(const char * pkgprefix, const char *event, int32 npcid, Mob* other, Mob* mob, ItemInst* iteminst)
 {
 	if(!perl)
 		return;
 	_ZP(PerlXSParser_SendCommands);
 
 	if(mob && mob->IsClient())
-		quest_manager.StartQuest(other, mob->CastToClient());
+		quest_manager.StartQuest(other, mob->CastToClient(), iteminst);
 	else
-		quest_manager.StartQuest(other, NULL);
+		quest_manager.StartQuest(other, NULL, NULL);
 
 	try {
 
@@ -127,6 +130,14 @@
 			sv_setref_pv(npc, "NPC", curn);
 		}
 
+		//only export QuestItem if it's an item quest
+		if(iteminst) {
+			ItemInst* curi = quest_manager.GetQuestItem();
+			snprintf(namebuf, 64, "%s::questitem", pkgprefix);
+			SV *questitem = get_sv(namebuf, true);
+			sv_setref_pv(questitem, "QuestItem", curi);
+		}
+		
 		snprintf(namebuf, 64, "%s::entity_list", pkgprefix);
 		SV *el = get_sv(namebuf, true);
 		sv_setref_pv(el, "EntityList", &entity_list);
@@ -203,6 +214,24 @@
 	XSRETURN(1);
 }
 
+//Any creation of new quest items gets the current quest item
+XS(XS_QuestItem_new);
+XS(XS_QuestItem_new) 
+{
+	dXSARGS;
+	if (items != 1)
+		Perl_croak(aTHX_ "Usage: QuestItem::new()");
+
+	ItemInst* RETVAL;
+
+	RETVAL = quest_manager.GetQuestItem();
+	ST(0) = sv_newmortal();
+	if(RETVAL)
+		sv_setref_pv(ST(0), "QuestItem", (void*)RETVAL);
+
+	XSRETURN(1);
+}
+
 #endif //EMBPERL_XS_CLASSES
 
 
Index: zone/perlparser.h
===================================================================
--- zone/perlparser.h	(revision 637)
+++ zone/perlparser.h	(working copy)
@@ -32,7 +32,7 @@
 	PerlXSParser();
 //	~PerlXSParser();
 
-	virtual void SendCommands(const char * pkgprefix, const char *event, int32 npcid, Mob* other, Mob* mob);
+	virtual void SendCommands(const char * pkgprefix, const char *event, int32 npcid, Mob* other, Mob* mob, ItemInst* iteminst);
 	
 protected:
 	void map_funs();
Index: zone/questmgr.cpp
===================================================================
--- zone/questmgr.cpp	(revision 637)
+++ zone/questmgr.cpp	(working copy)
@@ -157,10 +157,11 @@
 	}
 }
 
-void QuestManager::StartQuest(Mob *_owner, Client *_initiator) {
+void QuestManager::StartQuest(Mob *_owner, Client *_initiator, ItemInst* _questitem) {
 	quest_mutex.lock();
 	owner = _owner;
 	initiator = _initiator;
+	questitem = _questitem;
 	depop_npc = false;
 }
 
Index: zone/questmgr.h
===================================================================
--- zone/questmgr.h	(revision 637)
+++ zone/questmgr.h	(working copy)
@@ -34,7 +34,7 @@
 	QuestManager();
 	virtual ~QuestManager();
 	
-	void StartQuest(Mob *_owner, Client *_initiator = NULL);
+	void StartQuest(Mob *_owner, Client *_initiator = NULL, ItemInst* _questitem = NULL);
 	void EndQuest();
 	
 	void Process();
@@ -215,11 +215,13 @@
 	inline Client *GetInitiator() const { return(initiator); }
 	inline NPC *GetNPC() const { return(owner->IsNPC()?owner->CastToNPC():NULL); }
 	inline Mob *GetOwner() const { return(owner); }
+	inline ItemInst *GetQuestItem() const {return questitem; }
 	inline bool ProximitySayInUse() { return HaveProximitySays; }
 
 protected:
 	Mob *owner;	//NPC is never NULL when functions are called.
 	Client *initiator;	//this can be null.
+	ItemInst* questitem;	// this is usually NULL.
 	
 	bool depop_npc;	//true if EndQuest should depop the NPC
 	
Index: zone/spells.cpp
===================================================================
--- zone/spells.cpp	(revision 637)
+++ zone/spells.cpp	(working copy)
@@ -1047,7 +1047,7 @@
 		if( ((PerlembParser*)parse)->PlayerHasQuestSub("EVENT_CAST") ) {
 			char temp[64];
             sprintf(temp, "%d", spell_id);
-			((PerlembParser*)parse)->Event(EVENT_CAST, 0, temp, NULL, this->CastToClient());
+			((PerlembParser*)parse)->Event(EVENT_CAST, 0, temp, (NPC*)NULL, this->CastToClient());
 		}
 	#endif
 	}
Index: zone/Zone.vcproj
===================================================================
--- zone/Zone.vcproj	(revision 637)
+++ zone/Zone.vcproj	(working copy)
@@ -575,6 +575,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\perl_questitem.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\perl_raids.cpp"
 				>
 			</File>
And here's some example item files. Two of them use new quest commands that are also in the diff above.

Charm of the Emerald Warrior (CHRMEmeraldWarrior.pl):
Code:
sub EVENT_SCALE_CALC {
  # Charm of the Emerald Warrior: scales with Emerald Warriors faction 
  # get faction level with the Emerald Warriors
  my $scale = $client->GetModCharacterFactionLevel(92);
    
  # set the scale based on where faction is in the range [0, 1500]
  if($scale < 0) {
    $scale = 0;
  }
  $questitem->SetScale($scale/1500);
}
Charm of Exotic Speech (CHRMRareLang.pl)
Code:
sub EVENT_SCALE_CALC {
  # Used for charms that scale with number of rare languages learned
  my $langmastered = 0;
 
  #check each rare language: Old Erudian through Elder Dragon
  for (my $i = 11; $i <= 22; $i++) {
    if($client->GetLanguageSkill($i) == 100) {
      $langmastered++;
    }
  }
  $questitem->SetScale($langmastered/12);
}
Charm of the Brotherhood (CHRMBrotherhood.pl)
Code:
sub EVENT_SCALE_CALC {
  #Charm of the Brotherhood: improves when group members are same race or class
 
  my $playerrace = $client->GetRace();
  my $playerclass = $client->GetClass();
  my $group = $client->GetGroup();
  my $member;
  my $matchcount = 1;
  
  # cycle through all group members
  if($group) {
    for(my $i = 1; $i < 6; $i++) {
      $member = $group->GetMember($i);
      if($member) {
        # look for race/class that matches the player
        if(($member->GetClass() == $playerclass) || ($member->GetRace() == $playerrace)) {
	  $matchcount++;
	}
      }
    }
  }
  $questitem->SetScale($matchcount);
}
Reply With Quote
  #6  
Old 06-07-2009, 03:37 PM
KLS
Administrator
 
Join Date: Sep 2006
Posts: 1,348
Default

Nice, I'll try to combine this with what I have a bit.
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 07:29 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