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

Reply
 
Thread Tools Display Modes
  #1  
Old 12-13-2010, 12:56 AM
l0stmancd
Fire Beetle
 
Join Date: Apr 2005
Posts: 23
Default COMMITTED: Implemented Augmention Sealer from Inventory

Hey folks.

As per the thread http://www.eqemulator.org/forums/showthread.php?t=25547 and the fix http://www.eqemulator.org/forums/showthread.php?t=25547, there had previously been an easy to reproduce problem with users accidentally closing world containers and finding their nodrop items deleted. Wheeljack fixed this for the normal case but it still left the uncommon issue of items in world containers becoming lost if the zone crashes or shuts down in an unanticipated fashion. This can easily be tested if you open up a world container and then crash the zone by closing the server app. Upon relogging in, you will find your items missing.

Discussed my comments on the thread with Trevius in IRC and found that the proposed change to have these lost items find their owner on reuse of the container is a high risk change (duplication, no drop trade exploits) and we are not likely to fix this in the forseeable future.

Instead, handling this another way for augmentations as this is the area where users are more likely to be severely impacted. A new server rule to allow server owners to specify another on-the-character tradeskill container that can be used for augmentatin. This handles augmenting and safe-removing of augmentations. This explicitly does not allow for deletion of augments as this really requires the user to choose an augmentation slot to delete in order to be safe for the user. The rule defaults to being turned off and the augmentation pool will behave the same as it has if this is the case. If the flag is set to the item id of a tradeskill container (certain restrictions apply) it will allow auging/deauging in here without impacting the augmentation pool (for those that still wish to use that).

NOTE - merged the code paths for the augmentation pool and the tradeskill container augmentation to a common function to make sure that these do not take on separate lifes as the project continues.

Testing:
1. Validated that I could still augment slots without issue in aug pool.
2. Validated that I could still remove augments without issue in aug pool.
3. Validated that I could still delete augments without issue in aug pool..
4. Validated that if the rule is set to 0, combines in tradeskill containers are not allowed.
5. Validated that if the rule is set to a given tradeskill container (Spit, for example) that this would not allow automatic aug combination in other non specified tradeskill containers.
6. Validated that if the rule is set to a given tradeskill container you could augment with this.
7. Validated that if the rule is set to a given tradeskill container, you could remove augments with this and the appropriate de-aug juice.
8. Validated that if the rule is set to a given tradeskill container, you would -not- be allowed to delete an augment (using a non-safe-removal solvent) using the tradeskill container.
9. Validated that if the rule is set to a given tradeskill container, you would -not- be allowed to put augments onto armor that would result in an unusable piece of armor based on the slot.
10. Validated that if the rule is set to a given tradeskill container, you would -not- be allowed to allowed to put augments onto armor that you would not be able to wear as your given class / race.
11. Validated that if the rule is set to a given tradeskill container, you would -not- be allowed to cause an item lore exception by putting multiple lore items onto the same armor by accident. Honestly, my guard code here may be superfluous as I was unable to recreate this defect even with the guard code present. Leaving in place and a dev can remove if they believe this is unnecessary and confusing.
12. Verified that only the specified augmentation removal juice would be able to remove a given augment. IE: An augment that requires a XVII solvent could will only be allowed to be removed by an XVII augment.
13. Validated that if the rule is set to a given tradeskill container, you would -not- be allowed to run the augmentation logic (application, removal, and cleanup) if you had more in the container than the specified item and augment.
14. Verified in all of the above cases, that the items in either the tradeskill container or the aug pool world container are deleted if the augmentation succeeded in some fashion (whether deaug'ing or augmenting).
15. Verified that if the rule is set to a given tradeskill container, you would still be able to perform normal tradeskill / recipe combines in it if it did not detect that you were attempting to augment an item. IE: If you combine an item + augment in a spit, you have created a new auged item. If you then combine batwings + frosting in a spit, you have created batwing crunchies.

Overall, I believe I have covered all of the testing cases.

Please let me know if you have any comments or questions.

Please see the below for the patches:

---------------------

Required SQL:
Code:
INSERT INTO RULE_VALUES
SELECT 1, 'Skills:ContainerIdToAllowAugCombines', 0, 'Allows for augmentation combines (normally in a world container augmentation sealer) to also be created in a user held item.  This allows for less chance of a lost item on a vulgar zone restart.'
common/ruletypes.h
Code:
Index: ruletypes.h
===================================================================
--- ruletypes.h	(revision 1766)
+++ ruletypes.h	(working copy)
@@ -92,6 +92,7 @@
 RULE_INT ( Skills, MaxTrainTradeskills, 21 )
 RULE_BOOL ( Skills, UseLimitTradeskillSearchSkillDiff, true )
 RULE_INT ( Skills, MaxTradeskillSearchSkillDiff, 50 )
+RULE_INT ( Skills, ContainerIdToAllowAugCombines, 0 )
 RULE_CATEGORY_END()
 
 RULE_CATEGORY( Pets )
common/Item.h
Code:
Index: Item.h
===================================================================
--- Item.h	(revision 1766)
+++ Item.h	(working copy)
@@ -266,6 +266,7 @@
 	virtual bool IsStackable() const;
 
 	// Can item be equipped by/at?
+	bool SharesACommonSlot(ItemInst* itemToCompare) const;
 	virtual bool IsEquipable(int16 race, int16 class_) const;
 	virtual bool IsEquipable(sint16 slot_id) const;
 	
@@ -275,6 +276,8 @@
 	inline bool IsAugmentable() const { return m_item->AugSlotType[0]!=0; }
 	bool AvailableWearSlot(uint32 aug_wear_slots) const;
 	sint8 AvailableAugmentSlot(sint32 augtype) const;
+	inline bool IsAugment() const { return m_item->AugType!=0; }
+	bool WillAugmentCauseLoreCollision(ItemInst* newAugment);
 	inline sint32 GetAugmentType() const { return m_item->AugType; }
 
 	inline bool IsExpendable() const { return ((m_item->Click.Type == ET_Expendable ) || (m_item->ItemType == ItemTypePotion)); }
@@ -283,6 +286,7 @@
 	// Contents
 	//
 	ItemInst* GetItem(uint8 slot) const;
+	uint8 GetAvailableItemCount();
 	virtual uint32 GetItemID(uint8 slot) const;
 	inline const ItemInst* operator[](uint8 slot) const { return GetItem(slot); }
 	void PutItem(uint8 slot, const ItemInst& inst);
common/Item.cpp
Code:
Index: Item.cpp
===================================================================
--- Item.cpp	(revision 1766)
+++ Item.cpp	(working copy)
@@ -219,6 +219,52 @@
 	return m_item->IsEquipable(race, class_);
 }
 
+bool ItemInst::WillAugmentCauseLoreCollision(ItemInst* newAugment)
+{
+	bool returnValue = false;
+
+	// If the new augment is an augment -and- it is lore (in some fashion)
+	//	perform further checks.
+	if (newAugment->IsAugment() && newAugment->m_item->LoreGroup != 0)
+	{
+		// Check all augments and see if any share the same loregroup status.
+		for(uint8 ctr = 0; ctr < 10; ctr++) {
+			
+			ItemInst* itemAugment = this->GetAugment(ctr);
+			if (itemAugment) {
+				// Lore Group Specific check... 
+				if (itemAugment->m_item->LoreGroup != -1 && itemAugment->m_item->LoreGroup == newAugment->m_item->LoreGroup) {
+					returnValue = true;
+					break;
+				} 
+				// Or they have the same id and they are lore...
+				else if (-1 == newAugment->m_item->LoreGroup && itemAugment->GetID() == newAugment->GetID()) {
+					returnValue = true;
+					break;
+				}
+			}
+		}
+	}
+
+	return returnValue;
+}
+
+bool ItemInst::SharesACommonSlot(ItemInst* itemToCompare) const
+{
+	bool returnValue = false;
+
+	// Go over all possible slots - each slot that is available
+	//	in this item, check to see if it is an available slot in the passed
+	//	item.  If so, exit out.
+	for(int i = 0; i <= 22 && !returnValue; i++) {
+		if (m_item->Slots & ( 1 << i )) {
+			returnValue = itemToCompare->IsEquipable( (i == 22) ? 9999 : i );
+		}
+	}
+
+	return returnValue;
+}
+
 // Can equip at this slot?
 bool ItemInst::IsEquipable(sint16 slot_id) const
 {
@@ -363,6 +409,20 @@
 	}
 }
 
+uint8 ItemInst::GetAvailableItemCount()
+{
+	uint8 returnValue = 0;
+
+	for(uint8 i = 0; i < 10; i++) {
+		const ItemInst* inst = this->GetItem(i);
+		if (inst) {
+			returnValue++;
+		}
+	}
+
+	return returnValue;
+}
+
 // Retrieve item inside container
 ItemInst* ItemInst::GetItem(uint8 index) const
 {
zone/object.h
Code:
Index: object.h
===================================================================
--- object.h	(revision 1766)
+++ object.h	(working copy)
@@ -148,7 +148,9 @@
 	static void HandleCombine(Client* user, const NewCombine_Struct* in_combine, Object *worldo);
 	static void HandleAugmentation(Client* user, const AugmentItem_Struct* in_augment, Object *worldo);
 	static void HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac);
-	
+	static bool Object::Internal_HandleAugmentation(Client* user, ItemInst* tobe_auged, ItemInst* auged_with, Object *worldo, ItemInst* heldContainer, sint32 augmentationSlotToRemoveFrom, sint32 heldContainer_ContainerSlot);
+	static bool Object::HandleTradeskillAugmentationCombine(Client* user, const NewCombine_Struct* in_combine, ItemInst* container);
+		
 	static SkillType TypeToSkill(uint32 type);
 	
 	// Packet functions
zone/zonedb.h
Code:
Index: zonedb.h
===================================================================
--- zonedb.h	(revision 1766)
+++ zonedb.h	(working copy)
@@ -277,6 +277,7 @@
 	int32   GetZoneForage(int32 ZoneID, int8 skill);    /* for foraging - BoB */
 	int32   GetZoneFishing(int32 ZoneID, int8 skill, uint32 &npc_id, uint8 &npc_chance);
 	void	UpdateRecipeMadecount(uint32 recipe_id, uint32 char_id, uint32 madecount);
+	bool	GetIsAllowedAugmentRemoval(uint32 augmentId, uint32 dissolverToTest);
 	
 	/*
 	 * Tribute
zone/tradeskills.cpp
Code:
Index: tradeskills.cpp
===================================================================
--- tradeskills.cpp	(revision 1766)
+++ tradeskills.cpp	(working copy)
@@ -53,7 +53,6 @@
 	}
 	
 	ItemInst *tobe_auged, *auged_with = NULL;
-	sint8 slot=-1;
 	ItemInst* container = worldo->m_inst;
 
 	if (!container) {
@@ -78,40 +77,187 @@
 		}
 	}
 
+	Internal_HandleAugmentation(user,tobe_auged,auged_with,worldo,NULL,in_augment->augment_slot, in_augment->augment_slot);
+}
+
+bool Object::Internal_HandleAugmentation(Client* user, ItemInst* tobe_auged, ItemInst* auged_with, Object *worldo, ItemInst* heldContainer, sint32 augmentationSlotToRemoveFrom, sint32 heldContainer_ContainerSlot)
+{
+	// Flag denoting whether an action was performed for augmentation.
+	// If an aug action was performed successfully, we need to wipe out the users "ingredients"
+	bool wipeContainer = false;
+
 	// Adding augment
-	if (in_augment->augment_slot == -1) {
+	if (augmentationSlotToRemoveFrom == -1) {
+		sint8 slot=-1;
 		if (((slot=tobe_auged->AvailableAugmentSlot(auged_with->GetAugmentType()))!=-1) && (tobe_auged->AvailableWearSlot(auged_with->GetItem()->Slots))) {
+			wipeContainer = true;
+
 			tobe_auged->PutAugment(slot,*auged_with);
 			user->PushItemOnCursor(*tobe_auged,true);
-			container->Clear();
+		} else {
+			// Only give warning / error message if this is an official augmentation sealer (world object).
+			if (worldo != NULL) {
+				user->Message(13, "Error: No available slot for augment");
+			}
+		}
+	} else {
+		
+		ItemInst *old_aug=NULL;
+		const uint32 id=auged_with->GetID();
+		
+		if (id==40408 || id==40409 || id==40410) {
+			tobe_auged->DeleteAugment(augmentationSlotToRemoveFrom);
+		}
+		else {
+			old_aug=tobe_auged->RemoveAugment(augmentationSlotToRemoveFrom);
+		}
+
+		user->PushItemOnCursor(*tobe_auged,true);
+
+		if (old_aug)
+			user->PushItemOnCursor(*old_aug,true);
+
+		wipeContainer = true;		
+	}
+	
+	if (wipeContainer) {
+		if (worldo != NULL) {
+			// Clear the container
+			worldo->m_inst->Clear();
+			
+			// Sent out container clear packer.
 			EQApplicationPacket* outapp = new EQApplicationPacket(OP_ClearObject, sizeof(ClearObject_Struct));
 			ClearObject_Struct *cos = (ClearObject_Struct *)outapp->pBuffer;
 			cos->Clear = 1;
 			user->QueuePacket(outapp);
 			safe_delete(outapp);
+			
+			// Delete world container contents explicitly.
 			database.DeleteWorldContainer(worldo->m_id, zone->GetZoneID());
 		} else {
-			user->Message(13, "Error: No available slot for augment");
+			// Delete items in our inventory container... 
+			for (uint8 i=0; i<10; i++){
+				const ItemInst* inst = heldContainer->GetItem(i);
+				if (inst) {
+					user->DeleteItemInInventory(Inventory::CalcSlotId(heldContainer_ContainerSlot,i),0,true);
+				}
+			}
+			// Explicitly mark container as cleared.
+			heldContainer->Clear();
 		}
-	} else {
-		ItemInst *old_aug=NULL;
-		const uint32 id=auged_with->GetID();
-		if (id==40408 || id==40409 || id==40410)
-			tobe_auged->DeleteAugment(in_augment->augment_slot);
-		else
-			old_aug=tobe_auged->RemoveAugment(in_augment->augment_slot);
+	}
 
-		user->PushItemOnCursor(*tobe_auged,true);
-		if (old_aug)
-			user->PushItemOnCursor(*old_aug,true);
-		container->Clear();
-		EQApplicationPacket* outapp = new EQApplicationPacket(OP_ClearObject, sizeof(ClearObject_Struct));
-		ClearObject_Struct *cos = (ClearObject_Struct *)outapp->pBuffer;
-		cos->Clear = 1;
+	return wipeContainer;
+}
+
+bool Object::HandleTradeskillAugmentationCombine(Client* user, const NewCombine_Struct* in_combine, ItemInst* container) 
+{
+	// Early out - if there are more than two items we are combining, we will not consider an aug combine.
+	if (2 != container->GetAvailableItemCount()){
+		return false;
+	}
+
+	bool augmentationActionHandled = false;
+
+	// For a successful augmentation, we need to have an augmentable item and either an augment or a distiller.
+	ItemInst* toBeAuged = NULL; // If set, this -will- be the item that will be modified.
+	ItemInst* augment = NULL; // If set, this will be the augment we attempt to apply.
+	ItemInst* otherItem = NULL; // If this is set, this -may- be an augment solvent (or similar) that we need to remove.
+
+	// Find the augment and the item to aug - or, the item to aug and the "other item" that may be an augment solvent.
+	int itemsFoundCount = 0;
+	for(uint8 ctr = 0; ctr < 10 && itemsFoundCount < 2; ctr++) {
+		
+		ItemInst* itemFound = container->GetItem(ctr);
+		if (itemFound) {
+			itemsFoundCount++;
+			if (itemFound->IsAugmentable()) {
+				toBeAuged = itemFound;
+			} else if (itemFound->IsAugment()) {
+				augment = itemFound;
+			} else {
+				otherItem = itemFound;
+			}
+		}
+	}
+
+	// Now that we know the two items we have to work with, lets see what we can do.
+	// We should have an item to be augmented and either an "other item" (solvent) or an augment.
+	if (toBeAuged) {
+		
+		// If we are passed an augment and:
+		//	1.  augment is usable by my race/class
+		//	2.  shares a common slot with the item to be augmented.
+		//  3.  will not cause an augment-lore collision on the item
+		//  then let us send the augmentation command to the internal augment function.
+		if (augment) {
+			if (augment->IsEquipable(user->GetPP().race, user->GetPP().class_) &&
+				augment->SharesACommonSlot(toBeAuged) &&
+				!toBeAuged->WillAugmentCauseLoreCollision(augment)
+				) {
+				// Attempt to augment.
+				augmentationActionHandled = Internal_HandleAugmentation(user,toBeAuged,augment,NULL,container,-1,in_combine->container_slot);	
+			}
+		}
+		// If we are not passed an augment and our item-to-augment is not augmented, there is no work to do.
+		else if (toBeAuged->IsAugmented()) {
+			// REMOVE AN AUGMENT.
+
+			// Important thing here - we cannot let users choose which item to remove so let us limit this, 
+			//	explicitly, to exclude the "delete augment" distillers.  Better to make this a safe remove 
+			//	only rather than to allow functionality that would destroy an augment on accident.
+			// 
+
+			const uint32 idOfAssumedSolvent = otherItem->GetID();
+			if(idOfAssumedSolvent==40408 || idOfAssumedSolvent==40409 || idOfAssumedSolvent==40410) {
+				user->Message(13, "Container based augmentation is not allowed for augment deletion.  Use a safe-removal solvent or an augmentation world container.");
+				
+				// If we are explicitly refusing to handle a case, then there should be no tradeskill combine that is allowed here.  Out!
+				augmentationActionHandled = true;
+
+			} else {
+
+				// Time to determine which augment (if any) can be removed by the proposed solvent.
+				// This will hit the database.  It -may- be worthwhile to consider pulling this data into the database
+				//	all at one time as opposed to performing checks whenever needed.  That said, the lookup is a lightweight
+				//	one and no performance issues were found when testing.  Lets not prematurely optimize this.
+				
+				// We will find the first augment that can be removed with this solvent and remove it.  If the user wishes
+				//	to control de-augmentation in a finer way, the world container is still an option.
+				
+				sint32 slotToRemove = -1;
+				
+				for(uint8 ctr = 0; ctr < 10; ctr++) {
+
+					ItemInst* augToRemove = toBeAuged->GetAugment(ctr);
+					if (augToRemove) {
+						
+						// Check if this item can be removed... 
+						if (database.GetIsAllowedAugmentRemoval(augToRemove->GetID(), idOfAssumedSolvent)) {
+							slotToRemove = ctr;
+							break;
+						}						
+					}
+				}
+				
+				// If we found something to remove, lets do it.
+				if (slotToRemove > -1) {
+					augmentationActionHandled = Internal_HandleAugmentation(user,toBeAuged,otherItem,NULL,container,slotToRemove,in_combine->container_slot);	
+				}
+			}
+		}
+	}
+
+	// If we actually -did- something that will stop tradeskills from continuing, let us
+	//	go ahead and send the tradeskill combine acknowledgement packet so that the client is not
+	//	paused and waiting for us.
+	if (augmentationActionHandled) {
+		EQApplicationPacket* outapp = new EQApplicationPacket(OP_TradeSkillCombine, 0);
 		user->QueuePacket(outapp);
 		safe_delete(outapp);
-		database.DeleteWorldContainer(worldo->m_id, zone->GetZoneID());
 	}
+	
+	return augmentationActionHandled;
 }
 
 // Perform tradeskill combine
@@ -157,6 +303,17 @@
 	}
 	
 	container = inst;
+
+	// Do we allow augmentation combines from this container?
+	if (some_id > 0 && RuleI(Skills, ContainerIdToAllowAugCombines)==some_id) {
+		
+		// Attempt to handle a possible aug combination.
+		if (HandleTradeskillAugmentationCombine(user, in_combine, container)) {
+			
+			// Do no further work as the combine is already handled.
+			return;
+		}
+	}
 	
  	DBTradeskillRecipe_Struct spec;
  	if (!database.GetTradeRecipe(container, c_type, some_id, user->CharacterID(), &spec)) {
@@ -1016,7 +1173,36 @@
 	_log(TRADESKILLS__TRACE, "...Stage2 chance was: %f percent. 0 percent means stage1 failed",  chance_stage2);
 }
 
+/// Just a quick check to see if an augment dissolver is allowed to be used against a particular augment.
+bool ZoneDatabase::GetIsAllowedAugmentRemoval(uint32 augmentId, uint32 dissolverToTest)
+{
+	bool returnValue = false;
 
+	char errbuf[MYSQL_ERRMSG_SIZE];
+    MYSQL_RES *result;
+    char *query = 0;
+	uint32 qlen = 0;
+
+	qlen = MakeAnyLenString(&query, "SELECT 1 FROM items WHERE id = %u AND augdistiller = %u AND id <> 0 AND augdistiller <> 0", augmentId, dissolverToTest);
+
+	if (!RunQuery(query, qlen, errbuf, &result)) {
+		LogFile->write(EQEMuLog::Error, "Error in GetIsAllowedAugmentRemoval search, query: %s", query);
+		safe_delete_array(query);
+		LogFile->write(EQEMuLog::Error, "Error in GetIsAllowedAugmentRemoval search, error: %s", errbuf);
+		return(false);
+	}
+
+	safe_delete_array(query);
+	
+	// If even a single row is returned, then augmentation removal is allowed.
+	returnValue = mysql_num_rows(result) > 0;
+
+	// Clean up string allocation.
+	safe_delete_array(query);
+
+	return(returnValue);
+}
+
 bool ZoneDatabase::GetTradeRecipe(const ItemInst* container, uint8 c_type, uint32 some_id, 
 	uint32 char_id, DBTradeskillRecipe_Struct *spec)
 {
Reply With Quote
  #2  
Old 12-14-2010, 10:27 AM
l0stmancd
Fire Beetle
 
Join Date: Apr 2005
Posts: 23
Default All patches in a single code section...

Hey folks,

I realized that it may be easiest to just apply a patch from a single patch file as opposed to the patch files spread around.

My apologies.

See below for a single code section you can apply as a patch file to the eqemu trunk.

NOTE: This does not include the require sql rule insert as specified in the post above.

Code:
Index: common/Item.cpp
===================================================================
--- common/Item.cpp	(revision 1766)
+++ common/Item.cpp	(working copy)
@@ -219,6 +219,52 @@
 	return m_item->IsEquipable(race, class_);
 }
 
+bool ItemInst::WillAugmentCauseLoreCollision(ItemInst* newAugment)
+{
+	bool returnValue = false;
+
+	// If the new augment is an augment -and- it is lore (in some fashion)
+	//	perform further checks.
+	if (newAugment->IsAugment() && newAugment->m_item->LoreGroup != 0)
+	{
+		// Check all augments and see if any share the same loregroup status.
+		for(uint8 ctr = 0; ctr < 10; ctr++) {
+			
+			ItemInst* itemAugment = this->GetAugment(ctr);
+			if (itemAugment) {
+				// Lore Group Specific check... 
+				if (itemAugment->m_item->LoreGroup != -1 && itemAugment->m_item->LoreGroup == newAugment->m_item->LoreGroup) {
+					returnValue = true;
+					break;
+				} 
+				// Or they have the same id and they are lore...
+				else if (-1 == newAugment->m_item->LoreGroup && itemAugment->GetID() == newAugment->GetID()) {
+					returnValue = true;
+					break;
+				}
+			}
+		}
+	}
+
+	return returnValue;
+}
+
+bool ItemInst::SharesACommonSlot(ItemInst* itemToCompare) const
+{
+	bool returnValue = false;
+
+	// Go over all possible slots - each slot that is available
+	//	in this item, check to see if it is an available slot in the passed
+	//	item.  If so, exit out.
+	for(int i = 0; i <= 22 && !returnValue; i++) {
+		if (m_item->Slots & ( 1 << i )) {
+			returnValue = itemToCompare->IsEquipable( (i == 22) ? 9999 : i );
+		}
+	}
+
+	return returnValue;
+}
+
 // Can equip at this slot?
 bool ItemInst::IsEquipable(sint16 slot_id) const
 {
@@ -363,6 +409,20 @@
 	}
 }
 
+uint8 ItemInst::GetAvailableItemCount()
+{
+	uint8 returnValue = 0;
+
+	for(uint8 i = 0; i < 10; i++) {
+		const ItemInst* inst = this->GetItem(i);
+		if (inst) {
+			returnValue++;
+		}
+	}
+
+	return returnValue;
+}
+
 // Retrieve item inside container
 ItemInst* ItemInst::GetItem(uint8 index) const
 {
Index: common/Item.h
===================================================================
--- common/Item.h	(revision 1766)
+++ common/Item.h	(working copy)
@@ -266,6 +266,7 @@
 	virtual bool IsStackable() const;
 
 	// Can item be equipped by/at?
+	bool SharesACommonSlot(ItemInst* itemToCompare) const;
 	virtual bool IsEquipable(int16 race, int16 class_) const;
 	virtual bool IsEquipable(sint16 slot_id) const;
 	
@@ -275,6 +276,8 @@
 	inline bool IsAugmentable() const { return m_item->AugSlotType[0]!=0; }
 	bool AvailableWearSlot(uint32 aug_wear_slots) const;
 	sint8 AvailableAugmentSlot(sint32 augtype) const;
+	inline bool IsAugment() const { return m_item->AugType!=0; }
+	bool WillAugmentCauseLoreCollision(ItemInst* newAugment);
 	inline sint32 GetAugmentType() const { return m_item->AugType; }
 
 	inline bool IsExpendable() const { return ((m_item->Click.Type == ET_Expendable ) || (m_item->ItemType == ItemTypePotion)); }
@@ -283,6 +286,7 @@
 	// Contents
 	//
 	ItemInst* GetItem(uint8 slot) const;
+	uint8 GetAvailableItemCount();
 	virtual uint32 GetItemID(uint8 slot) const;
 	inline const ItemInst* operator[](uint8 slot) const { return GetItem(slot); }
 	void PutItem(uint8 slot, const ItemInst& inst);
Index: common/ruletypes.h
===================================================================
--- common/ruletypes.h	(revision 1766)
+++ common/ruletypes.h	(working copy)
@@ -92,6 +92,7 @@
 RULE_INT ( Skills, MaxTrainTradeskills, 21 )
 RULE_BOOL ( Skills, UseLimitTradeskillSearchSkillDiff, true )
 RULE_INT ( Skills, MaxTradeskillSearchSkillDiff, 50 )
+RULE_INT ( Skills, ContainerIdToAllowAugCombines, 0 )
 RULE_CATEGORY_END()
 
 RULE_CATEGORY( Pets )
Index: zone/object.h
===================================================================
--- zone/object.h	(revision 1766)
+++ zone/object.h	(working copy)
@@ -148,7 +148,9 @@
 	static void HandleCombine(Client* user, const NewCombine_Struct* in_combine, Object *worldo);
 	static void HandleAugmentation(Client* user, const AugmentItem_Struct* in_augment, Object *worldo);
 	static void HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac);
-	
+	static bool Object::Internal_HandleAugmentation(Client* user, ItemInst* tobe_auged, ItemInst* auged_with, Object *worldo, ItemInst* heldContainer, sint32 augmentationSlotToRemoveFrom, sint32 heldContainer_ContainerSlot);
+	static bool Object::HandleTradeskillAugmentationCombine(Client* user, const NewCombine_Struct* in_combine, ItemInst* container);
+		
 	static SkillType TypeToSkill(uint32 type);
 	
 	// Packet functions
Index: zone/tradeskills.cpp
===================================================================
--- zone/tradeskills.cpp	(revision 1766)
+++ zone/tradeskills.cpp	(working copy)
@@ -53,7 +53,6 @@
 	}
 	
 	ItemInst *tobe_auged, *auged_with = NULL;
-	sint8 slot=-1;
 	ItemInst* container = worldo->m_inst;
 
 	if (!container) {
@@ -78,40 +77,187 @@
 		}
 	}
 
+	Internal_HandleAugmentation(user,tobe_auged,auged_with,worldo,NULL,in_augment->augment_slot, in_augment->augment_slot);
+}
+
+bool Object::Internal_HandleAugmentation(Client* user, ItemInst* tobe_auged, ItemInst* auged_with, Object *worldo, ItemInst* heldContainer, sint32 augmentationSlotToRemoveFrom, sint32 heldContainer_ContainerSlot)
+{
+	// Flag denoting whether an action was performed for augmentation.
+	// If an aug action was performed successfully, we need to wipe out the users "ingredients"
+	bool wipeContainer = false;
+
 	// Adding augment
-	if (in_augment->augment_slot == -1) {
+	if (augmentationSlotToRemoveFrom == -1) {
+		sint8 slot=-1;
 		if (((slot=tobe_auged->AvailableAugmentSlot(auged_with->GetAugmentType()))!=-1) && (tobe_auged->AvailableWearSlot(auged_with->GetItem()->Slots))) {
+			wipeContainer = true;
+
 			tobe_auged->PutAugment(slot,*auged_with);
 			user->PushItemOnCursor(*tobe_auged,true);
-			container->Clear();
+		} else {
+			// Only give warning / error message if this is an official augmentation sealer (world object).
+			if (worldo != NULL) {
+				user->Message(13, "Error: No available slot for augment");
+			}
+		}
+	} else {
+		
+		ItemInst *old_aug=NULL;
+		const uint32 id=auged_with->GetID();
+		
+		if (id==40408 || id==40409 || id==40410) {
+			tobe_auged->DeleteAugment(augmentationSlotToRemoveFrom);
+		}
+		else {
+			old_aug=tobe_auged->RemoveAugment(augmentationSlotToRemoveFrom);
+		}
+
+		user->PushItemOnCursor(*tobe_auged,true);
+
+		if (old_aug)
+			user->PushItemOnCursor(*old_aug,true);
+
+		wipeContainer = true;		
+	}
+	
+	if (wipeContainer) {
+		if (worldo != NULL) {
+			// Clear the container
+			worldo->m_inst->Clear();
+			
+			// Sent out container clear packer.
 			EQApplicationPacket* outapp = new EQApplicationPacket(OP_ClearObject, sizeof(ClearObject_Struct));
 			ClearObject_Struct *cos = (ClearObject_Struct *)outapp->pBuffer;
 			cos->Clear = 1;
 			user->QueuePacket(outapp);
 			safe_delete(outapp);
+			
+			// Delete world container contents explicitly.
 			database.DeleteWorldContainer(worldo->m_id, zone->GetZoneID());
 		} else {
-			user->Message(13, "Error: No available slot for augment");
+			// Delete items in our inventory container... 
+			for (uint8 i=0; i<10; i++){
+				const ItemInst* inst = heldContainer->GetItem(i);
+				if (inst) {
+					user->DeleteItemInInventory(Inventory::CalcSlotId(heldContainer_ContainerSlot,i),0,true);
+				}
+			}
+			// Explicitly mark container as cleared.
+			heldContainer->Clear();
 		}
-	} else {
-		ItemInst *old_aug=NULL;
-		const uint32 id=auged_with->GetID();
-		if (id==40408 || id==40409 || id==40410)
-			tobe_auged->DeleteAugment(in_augment->augment_slot);
-		else
-			old_aug=tobe_auged->RemoveAugment(in_augment->augment_slot);
+	}
 
-		user->PushItemOnCursor(*tobe_auged,true);
-		if (old_aug)
-			user->PushItemOnCursor(*old_aug,true);
-		container->Clear();
-		EQApplicationPacket* outapp = new EQApplicationPacket(OP_ClearObject, sizeof(ClearObject_Struct));
-		ClearObject_Struct *cos = (ClearObject_Struct *)outapp->pBuffer;
-		cos->Clear = 1;
+	return wipeContainer;
+}
+
+bool Object::HandleTradeskillAugmentationCombine(Client* user, const NewCombine_Struct* in_combine, ItemInst* container) 
+{
+	// Early out - if there are more than two items we are combining, we will not consider an aug combine.
+	if (2 != container->GetAvailableItemCount()){
+		return false;
+	}
+
+	bool augmentationActionHandled = false;
+
+	// For a successful augmentation, we need to have an augmentable item and either an augment or a distiller.
+	ItemInst* toBeAuged = NULL; // If set, this -will- be the item that will be modified.
+	ItemInst* augment = NULL; // If set, this will be the augment we attempt to apply.
+	ItemInst* otherItem = NULL; // If this is set, this -may- be an augment solvent (or similar) that we need to remove.
+
+	// Find the augment and the item to aug - or, the item to aug and the "other item" that may be an augment solvent.
+	int itemsFoundCount = 0;
+	for(uint8 ctr = 0; ctr < 10 && itemsFoundCount < 2; ctr++) {
+		
+		ItemInst* itemFound = container->GetItem(ctr);
+		if (itemFound) {
+			itemsFoundCount++;
+			if (itemFound->IsAugmentable()) {
+				toBeAuged = itemFound;
+			} else if (itemFound->IsAugment()) {
+				augment = itemFound;
+			} else {
+				otherItem = itemFound;
+			}
+		}
+	}
+
+	// Now that we know the two items we have to work with, lets see what we can do.
+	// We should have an item to be augmented and either an "other item" (solvent) or an augment.
+	if (toBeAuged) {
+		
+		// If we are passed an augment and:
+		//	1.  augment is usable by my race/class
+		//	2.  shares a common slot with the item to be augmented.
+		//  3.  will not cause an augment-lore collision on the item
+		//  then let us send the augmentation command to the internal augment function.
+		if (augment) {
+			if (augment->IsEquipable(user->GetPP().race, user->GetPP().class_) &&
+				augment->SharesACommonSlot(toBeAuged) &&
+				!toBeAuged->WillAugmentCauseLoreCollision(augment)
+				) {
+				// Attempt to augment.
+				augmentationActionHandled = Internal_HandleAugmentation(user,toBeAuged,augment,NULL,container,-1,in_combine->container_slot);	
+			}
+		}
+		// If we are not passed an augment and our item-to-augment is not augmented, there is no work to do.
+		else if (toBeAuged->IsAugmented()) {
+			// REMOVE AN AUGMENT.
+
+			// Important thing here - we cannot let users choose which item to remove so let us limit this, 
+			//	explicitly, to exclude the "delete augment" distillers.  Better to make this a safe remove 
+			//	only rather than to allow functionality that would destroy an augment on accident.
+			// 
+
+			const uint32 idOfAssumedSolvent = otherItem->GetID();
+			if(idOfAssumedSolvent==40408 || idOfAssumedSolvent==40409 || idOfAssumedSolvent==40410) {
+				user->Message(13, "Container based augmentation is not allowed for augment deletion.  Use a safe-removal solvent or an augmentation world container.");
+				
+				// If we are explicitly refusing to handle a case, then there should be no tradeskill combine that is allowed here.  Out!
+				augmentationActionHandled = true;
+
+			} else {
+
+				// Time to determine which augment (if any) can be removed by the proposed solvent.
+				// This will hit the database.  It -may- be worthwhile to consider pulling this data into the database
+				//	all at one time as opposed to performing checks whenever needed.  That said, the lookup is a lightweight
+				//	one and no performance issues were found when testing.  Lets not prematurely optimize this.
+				
+				// We will find the first augment that can be removed with this solvent and remove it.  If the user wishes
+				//	to control de-augmentation in a finer way, the world container is still an option.
+				
+				sint32 slotToRemove = -1;
+				
+				for(uint8 ctr = 0; ctr < 10; ctr++) {
+
+					ItemInst* augToRemove = toBeAuged->GetAugment(ctr);
+					if (augToRemove) {
+						
+						// Check if this item can be removed... 
+						if (database.GetIsAllowedAugmentRemoval(augToRemove->GetID(), idOfAssumedSolvent)) {
+							slotToRemove = ctr;
+							break;
+						}						
+					}
+				}
+				
+				// If we found something to remove, lets do it.
+				if (slotToRemove > -1) {
+					augmentationActionHandled = Internal_HandleAugmentation(user,toBeAuged,otherItem,NULL,container,slotToRemove,in_combine->container_slot);	
+				}
+			}
+		}
+	}
+
+	// If we actually -did- something that will stop tradeskills from continuing, let us
+	//	go ahead and send the tradeskill combine acknowledgement packet so that the client is not
+	//	paused and waiting for us.
+	if (augmentationActionHandled) {
+		EQApplicationPacket* outapp = new EQApplicationPacket(OP_TradeSkillCombine, 0);
 		user->QueuePacket(outapp);
 		safe_delete(outapp);
-		database.DeleteWorldContainer(worldo->m_id, zone->GetZoneID());
 	}
+	
+	return augmentationActionHandled;
 }
 
 // Perform tradeskill combine
@@ -157,6 +303,17 @@
 	}
 	
 	container = inst;
+
+	// Do we allow augmentation combines from this container?
+	if (some_id > 0 && RuleI(Skills, ContainerIdToAllowAugCombines)==some_id) {
+		
+		// Attempt to handle a possible aug combination.
+		if (HandleTradeskillAugmentationCombine(user, in_combine, container)) {
+			
+			// Do no further work as the combine is already handled.
+			return;
+		}
+	}
 	
  	DBTradeskillRecipe_Struct spec;
  	if (!database.GetTradeRecipe(container, c_type, some_id, user->CharacterID(), &spec)) {
@@ -1016,7 +1173,36 @@
 	_log(TRADESKILLS__TRACE, "...Stage2 chance was: %f percent. 0 percent means stage1 failed",  chance_stage2);
 }
 
+/// Just a quick check to see if an augment dissolver is allowed to be used against a particular augment.
+bool ZoneDatabase::GetIsAllowedAugmentRemoval(uint32 augmentId, uint32 dissolverToTest)
+{
+	bool returnValue = false;
 
+	char errbuf[MYSQL_ERRMSG_SIZE];
+    MYSQL_RES *result;
+    char *query = 0;
+	uint32 qlen = 0;
+
+	qlen = MakeAnyLenString(&query, "SELECT 1 FROM items WHERE id = %u AND augdistiller = %u AND id <> 0 AND augdistiller <> 0", augmentId, dissolverToTest);
+
+	if (!RunQuery(query, qlen, errbuf, &result)) {
+		LogFile->write(EQEMuLog::Error, "Error in GetIsAllowedAugmentRemoval search, query: %s", query);
+		safe_delete_array(query);
+		LogFile->write(EQEMuLog::Error, "Error in GetIsAllowedAugmentRemoval search, error: %s", errbuf);
+		return(false);
+	}
+
+	safe_delete_array(query);
+	
+	// If even a single row is returned, then augmentation removal is allowed.
+	returnValue = mysql_num_rows(result) > 0;
+
+	// Clean up string allocation.
+	safe_delete_array(query);
+
+	return(returnValue);
+}
+
 bool ZoneDatabase::GetTradeRecipe(const ItemInst* container, uint8 c_type, uint32 some_id, 
 	uint32 char_id, DBTradeskillRecipe_Struct *spec)
 {
Index: zone/zonedb.h
===================================================================
--- zone/zonedb.h	(revision 1766)
+++ zone/zonedb.h	(working copy)
@@ -277,6 +277,7 @@
 	int32   GetZoneForage(int32 ZoneID, int8 skill);    /* for foraging - BoB */
 	int32   GetZoneFishing(int32 ZoneID, int8 skill, uint32 &npc_id, uint8 &npc_chance);
 	void	UpdateRecipeMadecount(uint32 recipe_id, uint32 char_id, uint32 madecount);
+	bool	GetIsAllowedAugmentRemoval(uint32 augmentId, uint32 dissolverToTest);
 	
 	/*
 	 * Tribute
Reply With Quote
  #3  
Old 12-19-2010, 02:31 PM
l0stmancd
Fire Beetle
 
Join Date: Apr 2005
Posts: 23
Default Updating patch to work with Revision 1771

Howdy folks,

There has been a few changes lately to the trunk source - updated my local to the latest revision (1771), validated that all is as it should be, and produced a new patch file.

This is a single patch that can be applied to the trunk and has everything needed -except- the mandatory sql mentioned in the first post.

I will keep updating this to work with the latest revision but it would be nice to get this into the source proper so that this is not needed.

EQEmu_Patch_AugmentationContainer_rev1771.patch
Code:
Index: common/Item.cpp
===================================================================
--- common/Item.cpp	(revision 1771)
+++ common/Item.cpp	(working copy)
@@ -219,6 +219,52 @@
 	return m_item->IsEquipable(race, class_);
 }
 
+bool ItemInst::WillAugmentCauseLoreCollision(ItemInst* newAugment)
+{
+	bool returnValue = false;
+
+	// If the new augment is an augment -and- it is lore (in some fashion)
+	//	perform further checks.
+	if (newAugment->IsAugment() && newAugment->m_item->LoreGroup != 0)
+	{
+		// Check all augments and see if any share the same loregroup status.
+		for(uint8 ctr = 0; ctr < 10; ctr++) {
+			
+			ItemInst* itemAugment = this->GetAugment(ctr);
+			if (itemAugment) {
+				// Lore Group Specific check... 
+				if (itemAugment->m_item->LoreGroup != -1 && itemAugment->m_item->LoreGroup == newAugment->m_item->LoreGroup) {
+					returnValue = true;
+					break;
+				} 
+				// Or they have the same id and they are lore...
+				else if (-1 == newAugment->m_item->LoreGroup && itemAugment->GetID() == newAugment->GetID()) {
+					returnValue = true;
+					break;
+				}
+			}
+		}
+	}
+
+	return returnValue;
+}
+
+bool ItemInst::SharesACommonSlot(ItemInst* itemToCompare) const
+{
+	bool returnValue = false;
+
+	// Go over all possible slots - each slot that is available
+	//	in this item, check to see if it is an available slot in the passed
+	//	item.  If so, exit out.
+	for(int i = 0; i <= 22 && !returnValue; i++) {
+		if (m_item->Slots & ( 1 << i )) {
+			returnValue = itemToCompare->IsEquipable( (i == 22) ? 9999 : i );
+		}
+	}
+
+	return returnValue;
+}
+
 // Can equip at this slot?
 bool ItemInst::IsEquipable(sint16 slot_id) const
 {
@@ -363,6 +409,20 @@
 	}
 }
 
+uint8 ItemInst::GetAvailableItemCount()
+{
+	uint8 returnValue = 0;
+
+	for(uint8 i = 0; i < 10; i++) {
+		const ItemInst* inst = this->GetItem(i);
+		if (inst) {
+			returnValue++;
+		}
+	}
+
+	return returnValue;
+}
+
 // Retrieve item inside container
 ItemInst* ItemInst::GetItem(uint8 index) const
 {
Index: common/Item.h
===================================================================
--- common/Item.h	(revision 1771)
+++ common/Item.h	(working copy)
@@ -266,6 +266,7 @@
 	virtual bool IsStackable() const;
 
 	// Can item be equipped by/at?
+	bool SharesACommonSlot(ItemInst* itemToCompare) const;
 	virtual bool IsEquipable(int16 race, int16 class_) const;
 	virtual bool IsEquipable(sint16 slot_id) const;
 	
@@ -275,6 +276,8 @@
 	inline bool IsAugmentable() const { return m_item->AugSlotType[0]!=0; }
 	bool AvailableWearSlot(uint32 aug_wear_slots) const;
 	sint8 AvailableAugmentSlot(sint32 augtype) const;
+	inline bool IsAugment() const { return m_item->AugType!=0; }
+	bool WillAugmentCauseLoreCollision(ItemInst* newAugment);
 	inline sint32 GetAugmentType() const { return m_item->AugType; }
 
 	inline bool IsExpendable() const { return ((m_item->Click.Type == ET_Expendable ) || (m_item->ItemType == ItemTypePotion)); }
@@ -283,6 +286,7 @@
 	// Contents
 	//
 	ItemInst* GetItem(uint8 slot) const;
+	uint8 GetAvailableItemCount();
 	virtual uint32 GetItemID(uint8 slot) const;
 	inline const ItemInst* operator[](uint8 slot) const { return GetItem(slot); }
 	void PutItem(uint8 slot, const ItemInst& inst);
Index: common/ruletypes.h
===================================================================
--- common/ruletypes.h	(revision 1771)
+++ common/ruletypes.h	(working copy)
@@ -92,6 +92,7 @@
 RULE_INT ( Skills, MaxTrainTradeskills, 21 )
 RULE_BOOL ( Skills, UseLimitTradeskillSearchSkillDiff, true )
 RULE_INT ( Skills, MaxTradeskillSearchSkillDiff, 50 )
+RULE_INT ( Skills, ContainerIdToAllowAugCombines, 0 )
 RULE_CATEGORY_END()
 
 RULE_CATEGORY( Pets )
Index: zone/object.h
===================================================================
--- zone/object.h	(revision 1771)
+++ zone/object.h	(working copy)
@@ -148,7 +148,9 @@
 	static void HandleCombine(Client* user, const NewCombine_Struct* in_combine, Object *worldo);
 	static void HandleAugmentation(Client* user, const AugmentItem_Struct* in_augment, Object *worldo);
 	static void HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac);
-	
+	static bool Object::Internal_HandleAugmentation(Client* user, ItemInst* tobe_auged, ItemInst* auged_with, Object *worldo, ItemInst* heldContainer, sint32 augmentationSlotToRemoveFrom, sint32 heldContainer_ContainerSlot);
+	static bool Object::HandleTradeskillAugmentationCombine(Client* user, const NewCombine_Struct* in_combine, ItemInst* container);
+		
 	static SkillType TypeToSkill(uint32 type);
 	
 	// Packet functions
Index: zone/tradeskills.cpp
===================================================================
--- zone/tradeskills.cpp	(revision 1771)
+++ zone/tradeskills.cpp	(working copy)
@@ -53,7 +53,6 @@
 	}
 	
 	ItemInst *tobe_auged, *auged_with = NULL;
-	sint8 slot=-1;
 	ItemInst* container = worldo->m_inst;
 
 	if (!container) {
@@ -78,40 +77,187 @@
 		}
 	}
 
+	Internal_HandleAugmentation(user,tobe_auged,auged_with,worldo,NULL,in_augment->augment_slot, in_augment->augment_slot);
+}
+
+bool Object::Internal_HandleAugmentation(Client* user, ItemInst* tobe_auged, ItemInst* auged_with, Object *worldo, ItemInst* heldContainer, sint32 augmentationSlotToRemoveFrom, sint32 heldContainer_ContainerSlot)
+{
+	// Flag denoting whether an action was performed for augmentation.
+	// If an aug action was performed successfully, we need to wipe out the users "ingredients"
+	bool wipeContainer = false;
+
 	// Adding augment
-	if (in_augment->augment_slot == -1) {
+	if (augmentationSlotToRemoveFrom == -1) {
+		sint8 slot=-1;
 		if (((slot=tobe_auged->AvailableAugmentSlot(auged_with->GetAugmentType()))!=-1) && (tobe_auged->AvailableWearSlot(auged_with->GetItem()->Slots))) {
+			wipeContainer = true;
+
 			tobe_auged->PutAugment(slot,*auged_with);
 			user->PushItemOnCursor(*tobe_auged,true);
-			container->Clear();
+		} else {
+			// Only give warning / error message if this is an official augmentation sealer (world object).
+			if (worldo != NULL) {
+				user->Message(13, "Error: No available slot for augment");
+			}
+		}
+	} else {
+		
+		ItemInst *old_aug=NULL;
+		const uint32 id=auged_with->GetID();
+		
+		if (id==40408 || id==40409 || id==40410) {
+			tobe_auged->DeleteAugment(augmentationSlotToRemoveFrom);
+		}
+		else {
+			old_aug=tobe_auged->RemoveAugment(augmentationSlotToRemoveFrom);
+		}
+
+		user->PushItemOnCursor(*tobe_auged,true);
+
+		if (old_aug)
+			user->PushItemOnCursor(*old_aug,true);
+
+		wipeContainer = true;		
+	}
+	
+	if (wipeContainer) {
+		if (worldo != NULL) {
+			// Clear the container
+			worldo->m_inst->Clear();
+			
+			// Sent out container clear packer.
 			EQApplicationPacket* outapp = new EQApplicationPacket(OP_ClearObject, sizeof(ClearObject_Struct));
 			ClearObject_Struct *cos = (ClearObject_Struct *)outapp->pBuffer;
 			cos->Clear = 1;
 			user->QueuePacket(outapp);
 			safe_delete(outapp);
+			
+			// Delete world container contents explicitly.
 			database.DeleteWorldContainer(worldo->m_id, zone->GetZoneID());
 		} else {
-			user->Message(13, "Error: No available slot for augment");
+			// Delete items in our inventory container... 
+			for (uint8 i=0; i<10; i++){
+				const ItemInst* inst = heldContainer->GetItem(i);
+				if (inst) {
+					user->DeleteItemInInventory(Inventory::CalcSlotId(heldContainer_ContainerSlot,i),0,true);
+				}
+			}
+			// Explicitly mark container as cleared.
+			heldContainer->Clear();
 		}
-	} else {
-		ItemInst *old_aug=NULL;
-		const uint32 id=auged_with->GetID();
-		if (id==40408 || id==40409 || id==40410)
-			tobe_auged->DeleteAugment(in_augment->augment_slot);
-		else
-			old_aug=tobe_auged->RemoveAugment(in_augment->augment_slot);
+	}
 
-		user->PushItemOnCursor(*tobe_auged,true);
-		if (old_aug)
-			user->PushItemOnCursor(*old_aug,true);
-		container->Clear();
-		EQApplicationPacket* outapp = new EQApplicationPacket(OP_ClearObject, sizeof(ClearObject_Struct));
-		ClearObject_Struct *cos = (ClearObject_Struct *)outapp->pBuffer;
-		cos->Clear = 1;
+	return wipeContainer;
+}
+
+bool Object::HandleTradeskillAugmentationCombine(Client* user, const NewCombine_Struct* in_combine, ItemInst* container) 
+{
+	// Early out - if there are more than two items we are combining, we will not consider an aug combine.
+	if (2 != container->GetAvailableItemCount()){
+		return false;
+	}
+
+	bool augmentationActionHandled = false;
+
+	// For a successful augmentation, we need to have an augmentable item and either an augment or a distiller.
+	ItemInst* toBeAuged = NULL; // If set, this -will- be the item that will be modified.
+	ItemInst* augment = NULL; // If set, this will be the augment we attempt to apply.
+	ItemInst* otherItem = NULL; // If this is set, this -may- be an augment solvent (or similar) that we need to remove.
+
+	// Find the augment and the item to aug - or, the item to aug and the "other item" that may be an augment solvent.
+	int itemsFoundCount = 0;
+	for(uint8 ctr = 0; ctr < 10 && itemsFoundCount < 2; ctr++) {
+		
+		ItemInst* itemFound = container->GetItem(ctr);
+		if (itemFound) {
+			itemsFoundCount++;
+			if (itemFound->IsAugmentable()) {
+				toBeAuged = itemFound;
+			} else if (itemFound->IsAugment()) {
+				augment = itemFound;
+			} else {
+				otherItem = itemFound;
+			}
+		}
+	}
+
+	// Now that we know the two items we have to work with, lets see what we can do.
+	// We should have an item to be augmented and either an "other item" (solvent) or an augment.
+	if (toBeAuged) {
+		
+		// If we are passed an augment and:
+		//	1.  augment is usable by my race/class
+		//	2.  shares a common slot with the item to be augmented.
+		//  3.  will not cause an augment-lore collision on the item
+		//  then let us send the augmentation command to the internal augment function.
+		if (augment) {
+			if (augment->IsEquipable(user->GetPP().race, user->GetPP().class_) &&
+				augment->SharesACommonSlot(toBeAuged) &&
+				!toBeAuged->WillAugmentCauseLoreCollision(augment)
+				) {
+				// Attempt to augment.
+				augmentationActionHandled = Internal_HandleAugmentation(user,toBeAuged,augment,NULL,container,-1,in_combine->container_slot);	
+			}
+		}
+		// If we are not passed an augment and our item-to-augment is not augmented, there is no work to do.
+		else if (toBeAuged->IsAugmented()) {
+			// REMOVE AN AUGMENT.
+
+			// Important thing here - we cannot let users choose which item to remove so let us limit this, 
+			//	explicitly, to exclude the "delete augment" distillers.  Better to make this a safe remove 
+			//	only rather than to allow functionality that would destroy an augment on accident.
+			// 
+
+			const uint32 idOfAssumedSolvent = otherItem->GetID();
+			if(idOfAssumedSolvent==40408 || idOfAssumedSolvent==40409 || idOfAssumedSolvent==40410) {
+				user->Message(13, "Container based augmentation is not allowed for augment deletion.  Use a safe-removal solvent or an augmentation world container.");
+				
+				// If we are explicitly refusing to handle a case, then there should be no tradeskill combine that is allowed here.  Out!
+				augmentationActionHandled = true;
+
+			} else {
+
+				// Time to determine which augment (if any) can be removed by the proposed solvent.
+				// This will hit the database.  It -may- be worthwhile to consider pulling this data into the database
+				//	all at one time as opposed to performing checks whenever needed.  That said, the lookup is a lightweight
+				//	one and no performance issues were found when testing.  Lets not prematurely optimize this.
+				
+				// We will find the first augment that can be removed with this solvent and remove it.  If the user wishes
+				//	to control de-augmentation in a finer way, the world container is still an option.
+				
+				sint32 slotToRemove = -1;
+				
+				for(uint8 ctr = 0; ctr < 10; ctr++) {
+
+					ItemInst* augToRemove = toBeAuged->GetAugment(ctr);
+					if (augToRemove) {
+						
+						// Check if this item can be removed... 
+						if (database.GetIsAllowedAugmentRemoval(augToRemove->GetID(), idOfAssumedSolvent)) {
+							slotToRemove = ctr;
+							break;
+						}						
+					}
+				}
+				
+				// If we found something to remove, lets do it.
+				if (slotToRemove > -1) {
+					augmentationActionHandled = Internal_HandleAugmentation(user,toBeAuged,otherItem,NULL,container,slotToRemove,in_combine->container_slot);	
+				}
+			}
+		}
+	}
+
+	// If we actually -did- something that will stop tradeskills from continuing, let us
+	//	go ahead and send the tradeskill combine acknowledgement packet so that the client is not
+	//	paused and waiting for us.
+	if (augmentationActionHandled) {
+		EQApplicationPacket* outapp = new EQApplicationPacket(OP_TradeSkillCombine, 0);
 		user->QueuePacket(outapp);
 		safe_delete(outapp);
-		database.DeleteWorldContainer(worldo->m_id, zone->GetZoneID());
 	}
+	
+	return augmentationActionHandled;
 }
 
 // Perform tradeskill combine
@@ -157,6 +303,17 @@
 	}
 	
 	container = inst;
+
+	// Do we allow augmentation combines from this container?
+	if (some_id > 0 && RuleI(Skills, ContainerIdToAllowAugCombines)==some_id) {
+		
+		// Attempt to handle a possible aug combination.
+		if (HandleTradeskillAugmentationCombine(user, in_combine, container)) {
+			
+			// Do no further work as the combine is already handled.
+			return;
+		}
+	}
 	
  	DBTradeskillRecipe_Struct spec;
  	if (!database.GetTradeRecipe(container, c_type, some_id, user->CharacterID(), &spec)) {
@@ -1016,7 +1173,36 @@
 	_log(TRADESKILLS__TRACE, "...Stage2 chance was: %f percent. 0 percent means stage1 failed",  chance_stage2);
 }
 
+/// Just a quick check to see if an augment dissolver is allowed to be used against a particular augment.
+bool ZoneDatabase::GetIsAllowedAugmentRemoval(uint32 augmentId, uint32 dissolverToTest)
+{
+	bool returnValue = false;
 
+	char errbuf[MYSQL_ERRMSG_SIZE];
+    MYSQL_RES *result;
+    char *query = 0;
+	uint32 qlen = 0;
+
+	qlen = MakeAnyLenString(&query, "SELECT 1 FROM items WHERE id = %u AND augdistiller = %u AND id <> 0 AND augdistiller <> 0", augmentId, dissolverToTest);
+
+	if (!RunQuery(query, qlen, errbuf, &result)) {
+		LogFile->write(EQEMuLog::Error, "Error in GetIsAllowedAugmentRemoval search, query: %s", query);
+		safe_delete_array(query);
+		LogFile->write(EQEMuLog::Error, "Error in GetIsAllowedAugmentRemoval search, error: %s", errbuf);
+		return(false);
+	}
+
+	safe_delete_array(query);
+	
+	// If even a single row is returned, then augmentation removal is allowed.
+	returnValue = mysql_num_rows(result) > 0;
+
+	// Clean up string allocation.
+	safe_delete_array(query);
+
+	return(returnValue);
+}
+
 bool ZoneDatabase::GetTradeRecipe(const ItemInst* container, uint8 c_type, uint32 some_id, 
 	uint32 char_id, DBTradeskillRecipe_Struct *spec)
 {
Index: zone/zonedb.h
===================================================================
--- zone/zonedb.h	(revision 1771)
+++ zone/zonedb.h	(working copy)
@@ -277,6 +277,7 @@
 	int32   GetZoneForage(int32 ZoneID, int8 skill);    /* for foraging - BoB */
 	int32   GetZoneFishing(int32 ZoneID, int8 skill, uint32 &npc_id, uint8 &npc_chance);
 	void	UpdateRecipeMadecount(uint32 recipe_id, uint32 char_id, uint32 madecount);
+	bool	GetIsAllowedAugmentRemoval(uint32 augmentId, uint32 dissolverToTest);
 	
 	/*
 	 * Tribute
Reply With Quote
  #4  
Old 12-24-2010, 06:41 AM
thepoetwarrior
Discordant
 
Join Date: Aug 2007
Posts: 307
Thumbs up

I would really like to see this patch get applied to the main source code since losing items in the Aug Pool is common.

See picture for examples from what the users have emailed me.




Reply With Quote
  #5  
Old 12-24-2010, 11:09 AM
trevius's Avatar
trevius
Developer
 
Join Date: Aug 2006
Location: USA
Posts: 5,946
Default

Those are swapitem errors, Hunter. I don't think those are related to the patch in this thread at all. I think many of the swapitem errors that cause items to poof have been resolved, though there definitely are still a couple scenarios that cause it. The problem happens when the client and the server get out of sync on exactly where all items are on a character. If the client tells the server to move an item from a slot that the server doesn't think exists in that slot, the server deletes it.

I do like the idea of this patch, but just haven't really had time to review it yet.

EDIT: Oops, said Lillu when I meant Hunter. Corrected that lol.
__________________
Trevazar/Trevius Owner of: Storm Haven
Everquest Emulator FAQ (Frequently Asked Questions) - Read It!

Last edited by trevius; 12-24-2010 at 02:17 PM..
Reply With Quote
  #6  
Old 12-24-2010, 11:44 AM
l0stmancd
Fire Beetle
 
Join Date: Apr 2005
Posts: 23
Default

Hey folks,

I took a look at the errors that thepoetwarrior mentioned and as Trevius mentioned, I do not believe those would be resolved from this patch.

Specifically, this was targeting the errors surrounding a zone crashing causing world container items to be lost.

I spoke to a few of the guys from the EZ Server and they mentioned they see this only when they are removing an augment using the SoD client that they put on using the Titanium client. I tested this out with the container approach and did not reproduce this issue. This means nothing, though, because I tested this out using the augmentation pool approach and did not reproduce this issue either.

I see the area in the code where this deletion is occurring but I do not know what would be causing it beyond what Trevius mentioned. We cannot remove that part of the code as it was added to deal with an exploit in.. shared bank I believe?...

Will research that issue, if it is still happening, in another thread post.

---

Update - spoke to thepoetwarrior and it seems that this (the item deletion message) happens on his server about once a week to some player. I believe this is all centered around the augmentation pool. I do not know if the tradeskill container would have an effect on this.

---

Update 2 - In consideration of this "Deleting Item!" issue, while it may not be directly worldcontainer related it may still have something to do with this in an indirect fashion. I assume this issue comes from moving an item from your character to a world container and back. With this patch, you have the option of saying that all aug items can be combined on your local container ___. This means, unless this item deletion is -directly- related to the augmentation procedures, this should move the chance to hit the "Deleting Item!" defect to what you would normally get when you re-arrange your packs or move equipment from one slot to another. IE: It may not fix the underlying issue, but it should make it as common as you get when you take off your chest slot or put your chest slot back on.
Reply With Quote
  #7  
Old 12-28-2010, 09:48 AM
trevius's Avatar
trevius
Developer
 
Join Date: Aug 2006
Location: USA
Posts: 5,946
Default

Finally had a chance to review this code a bit. I think it is a good idea, but not sure I follow the code enough to commit it myself.

I am not really sure why you check 10 slots in this function (and a few other spots):

Code:
bool ItemInst::WillAugmentCauseLoreCollision(ItemInst* newAugment)
Max aug slots on an item is currently 5 slots and if anything, should be coded to use the defined value for max aug slots, "MAX_AUGMENT_SLOTS". The only thing I can think of that would run through 10 slots would be bags. I don't really know the goal of this function. I would think you could just use CheckLoreConflict(), but maybe I am missing something here. Aug Lore checks should be done against the entire inventory, not just per item. I haven't looked too in-depth into it, though.

Also, I don't think the DB query you have is required. You should be able to pull that info directly from shared memory, I think.

There were a few other things I am not quite clear on yet, but I don't really want to go into each one.

As it is currently, I think this could probably be done in a different and maybe more user-friendly way. With the limitations of how this is coded (such as you can't select which aug slot to remove), I think it might be a bit too much of a hack to make it into the SVN. Even though it would be another hack, I think this could be done by tricking the client into thinking it is interacting with a real aug pool. It would probably take more work and innovation to make it work as I am thinking, but I think it may be possible to trick the client into using a bag as a real aug pool. I can imagine how to do part of it (may only work for SoF+ clients though), but the part I am not sure of offhand is how it would actually be saved in the inventory in the case of a crash.

Sorry, I don't mean to shoot down your submission, but if something like this was added, I just think it would have to be done a bit differently.
__________________
Trevazar/Trevius Owner of: Storm Haven
Everquest Emulator FAQ (Frequently Asked Questions) - Read It!

Last edited by trevius; 12-28-2010 at 09:59 AM..
Reply With Quote
  #8  
Old 12-28-2010, 11:57 AM
l0stmancd
Fire Beetle
 
Join Date: Apr 2005
Posts: 23
Default

Hi Trevius,

Thanks for taking the time to review this.

The lore conflict may simply be confusion on my part. I was under the impression that a lore augment was simply an augment that could not be in an item twice. The client itself and other server checks have already determined if a user can hold two of an augment (in hand, etc) by the time the user has them. This check was simply to guarantee there would not be another of the same lore item (or lore group) in the item they are augmenting. If the game should not allow two of the same lore augment -anywhere, in any augmentation slot, on any item - then we should definitely remove the check lore augment code and move to the CheckLoreConflict code. Mind you - this may be unnecessary since by the time the user has two of these same lore augments, the CheckLoreConflict should already have been called (which is why it does not exist in the augmentation procedures as they are in trunk).

Regarding the database query? You are absolutely correct. Can remove this function and switch to the following code snippet.

Code:
				for(uint8 ctr = 0; ctr < MAX_AUGMENT_SLOTS; ctr++) {
					ItemInst* augToRemove = toBeAuged->GetAugment(ctr);
					
					if (augToRemove) {
						if (idOfAssumedSolvent == augToRemove->AugDistiller) {
							slotToRemove = ctr;	
							break;
						}
					}
				}
Holding off on fixing those and resubmitting until I understand your last note.

If it is possible to change this to work as a hand held augmentation pool (with item select, etc) this would be a good fix as long as it would handle zone server crashing. I am afraid, though, I am not familiar with how you would do that. If you do not have time to look into this, would you mind giving a two sentence explanation that I can take and dig into?

I agree that this fix is not perfect in so far as you could not select which augment to remove (just the first one it finds of a given type that has been inserted) but it would fix an issue that is causing problems now. That said, given that this would be an optional rule and does not seem to pollute the code surrounding tradeskills / augmentation I do not believe this would be hard to revert out if there was a better solution in the future. I will gladly spend the time to fix any issues you see / think can be improved here if you would still consider the concept for submission. If it would be easier, I can create a set of much smaller diffs that show the code progression more obviously.

Let me know - Took off work until Tuesday of next week and will gladly spend that time working on this.
Reply With Quote
  #9  
Old 01-02-2011, 05:55 AM
trevius's Avatar
trevius
Developer
 
Join Date: Aug 2006
Location: USA
Posts: 5,946
Default

Well, I had another chance to look into this a bit further to find a way to do this that I think would be good enough to make it into the SVN and I think I found it!

I had a feeling that there may be some existing way to let aug pool windows work from inventory and was about to start experimenting with some ideas. First, I figured I would look into what it is that causes the different existing containers like Collapsible Spit to bring up a tradeskill window. The answer is simple; bagtype. So, I checked the wiki page here:

http://www.eqemulator.net/wiki/wikka...uDBSchemaitems

Under Bagtype there, it lists each type. One of them happens to be:

Quote:
53 = Augmentation Sealer (for applying augments)
So, searching the DB for an item with that bagtype yielded this:

66180 - Augmentation Sealer

Checking that item out looks like it should be fairly simple to implement a fully functional aug pool that works from inventory.

I don't really have the time to work on the code for it, but I think implementing support for bagtype 53 to act as an aug pool in the inventory would be perfectly acceptable.

You won't need a rule for setting item ID of the item, just need to have it only work from bagtype 53 items.

Also, I don't think you really need to worry about doing things that aren't already done in the current aug pool code. Stuff like the extra lore conflict checking shouldn't be necessary. Though, since the existing aug pool code probably isn't perfect, I don't think some adjustments here and there would be bad if they make sense to do.

I am still a fairly novice to intermediate coder myself. I don't know how best to put it, but if I am finding issues with your code like the previous stuff, it concerns me a bit. So, if you do make another rewrite and submit it here, I will have to make sure I fully understand everything in it 100% before I will consider committing it. Otherwise, I may have to defer to one of the more experienced devs to commit it if it passes their inspection.
__________________
Trevazar/Trevius Owner of: Storm Haven
Everquest Emulator FAQ (Frequently Asked Questions) - Read It!
Reply With Quote
  #10  
Old 01-02-2011, 06:16 PM
l0stmancd
Fire Beetle
 
Join Date: Apr 2005
Posts: 23
Default

Hi Trevius,

I see what you mean by the bagtype change. Just started working on it here. Changing the bagtype to 53 has given a lot of things by default but there still needs to be a code change. Looking into this now.

Thanks!
Reply With Quote
  #11  
Old 01-02-2011, 08:20 PM
l0stmancd
Fire Beetle
 
Join Date: Apr 2005
Posts: 23
Default

This is actually a pretty clean change. Still working through it but I think the only really odd part is in the slot numbering. The client is sending slots for the "container" that appear to be shifted. IE: Sending slot 30 for container slot 8 in inventory (29). Consistent in SoD inventory, havent tested bank yet...

Will see if we are handling this in a consistent way in the code already...
Reply With Quote
  #12  
Old 01-03-2011, 03:38 AM
trevius's Avatar
trevius
Developer
 
Join Date: Aug 2006
Location: USA
Posts: 5,946
Default

It is normal for all SoF+ clients to send shifted slot IDs for all slots over slot 21, since Power Source replaced the Titanium Ammo slot and bumped them all up by 1. This is all handled directly in the <patch>.cpp files near the top such as in this file for SoD:

http://code.google.com/p/projecteqem...atches/SoD.cpp

I made slot converting functions that adjusts Titanium slots to later clients slots and visa versa.

So, even though you see it sending slot 30 (cursor slot in Titanium), when it is in slot 29, it is actually converted back to slot 29 before it is actually handled in any way.

If something is making it into the handling without being converted, it is most likely due to us missing an encode/decode in that .cpp file for that particular packet type. It is pretty easy to add encodes/decodes if needed, we just need to know which packet it is that isn't in there already.
__________________
Trevazar/Trevius Owner of: Storm Haven
Everquest Emulator FAQ (Frequently Asked Questions) - Read It!
Reply With Quote
  #13  
Old 01-03-2011, 04:06 AM
l0stmancd
Fire Beetle
 
Join Date: Apr 2005
Posts: 23
Default

Hey folks,

Rewrote the code submission based on Trevius's recommendation. Turns out it was a fairly simple change with a -lot- less code than before.

The only "interesting" part of it was that I had to add decode functions to the expansion patch files to allow us to shift the slot numbers to the titanium slot numbers. This is done following the same logic as the tradeskill decode functions for the same reason. As I mentioned above, the slot numbers were incorrect between Titanium and SoD due to them using different slightly different slot numbers.

Tested this with ItemID 66180 and also a custom container I created for it - works fine.

Tests ran:
1. Validated that I could still augment slots without issue in aug pool.
2. Validated that I could still remove augments without issue in aug pool.
3. Validated that I could still delete augments without issue in aug pool..
4. Validated that I could agument slots with bags of type 53.
5. Validated that I could remove augments with bags of type 53 (and select which one to remove).
6. Validate that I could delete augments with bags of type 53 (and select which one to remove).
7. Validated that after a successful augmentation, deaugmentation, or augment deletion that the items in your tradeskill container are wiped and the new items are still pushed onto your cursor.
8. Validated all of the above on both Titanium and SoD clients.
9. Validated that items are not lost on zone crash.

You can use the following item ids to test this: 69312, 60332, 47013, 66180

No new logic was added - for the most part this is using the exact same logic that the augmentation pool is using.

Hah - just noticed you responded to my statement - yeah, was testing other things before posting it. Thanks man...

Code:
Index: common/patches/HoT.cpp
===================================================================
--- common/patches/HoT.cpp	(revision 1798)
+++ common/patches/HoT.cpp	(working copy)
@@ -3105,6 +3105,16 @@
 	FINISH_DIRECT_DECODE();
 }
 
+DECODE(OP_AugmentItem) {
+	DECODE_LENGTH_EXACT(structs::AugmentItem_Struct);
+	SETUP_DIRECT_DECODE(AugmentItem_Struct, structs::AugmentItem_Struct);
+
+	emu->container_slot = HoTToTitaniumSlot(eq->container_slot);
+	emu->augment_slot = eq->augment_slot;
+
+	FINISH_DIRECT_DECODE();
+}
+
 DECODE(OP_TradeSkillCombine) {
 	DECODE_LENGTH_EXACT(structs::NewCombine_Struct);
 	SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct);
Index: common/patches/HoT_ops.h
===================================================================
--- common/patches/HoT_ops.h	(revision 1798)
+++ common/patches/HoT_ops.h	(working copy)
@@ -114,5 +114,6 @@
 D(OP_ChannelMessage)
 D(OP_DeleteItem)
 D(OP_ZoneChange)
+D(OP_AugmentItem)
 #undef E
 #undef D
Index: common/patches/SoD.cpp
===================================================================
--- common/patches/SoD.cpp	(revision 1798)
+++ common/patches/SoD.cpp	(working copy)
@@ -2771,6 +2771,16 @@
 	FINISH_DIRECT_DECODE();
 }
 
+DECODE(OP_AugmentItem) {
+	DECODE_LENGTH_EXACT(structs::AugmentItem_Struct);
+	SETUP_DIRECT_DECODE(AugmentItem_Struct, structs::AugmentItem_Struct);
+
+	emu->container_slot = SoDToTitaniumSlot(eq->container_slot);
+	emu->augment_slot = eq->augment_slot;
+
+	FINISH_DIRECT_DECODE();
+}
+
 DECODE(OP_TradeSkillCombine) {
 	DECODE_LENGTH_EXACT(structs::NewCombine_Struct);
 	SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct);
Index: common/patches/SoD_ops.h
===================================================================
--- common/patches/SoD_ops.h	(revision 1798)
+++ common/patches/SoD_ops.h	(working copy)
@@ -104,5 +104,6 @@
 D(OP_LoadSpellSet)
 D(OP_ApplyPoison)
 D(OP_DeleteItem)
+D(OP_AugmentItem)
 #undef E
 #undef D
Index: common/patches/SoF.cpp
===================================================================
--- common/patches/SoF.cpp	(revision 1798)
+++ common/patches/SoF.cpp	(working copy)
@@ -2269,6 +2269,16 @@
 	FINISH_DIRECT_DECODE();
 }
 
+DECODE(OP_AugmentItem) {
+	DECODE_LENGTH_EXACT(structs::AugmentItem_Struct);
+	SETUP_DIRECT_DECODE(AugmentItem_Struct, structs::AugmentItem_Struct);
+
+	emu->container_slot = SoFToTitaniumSlot(eq->container_slot);
+	emu->augment_slot = eq->augment_slot;
+
+	FINISH_DIRECT_DECODE();
+}
+
 DECODE(OP_TradeSkillCombine) {
 	DECODE_LENGTH_EXACT(structs::NewCombine_Struct);
 	SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct);
Index: common/patches/SoF_ops.h
===================================================================
--- common/patches/SoF_ops.h	(revision 1798)
+++ common/patches/SoF_ops.h	(working copy)
@@ -88,5 +88,6 @@
 D(OP_InspectAnswer)
 D(OP_ApplyPoison)
 D(OP_DeleteItem)
+D(OP_AugmentItem)
 #undef E
 #undef D
Index: common/patches/Underfoot.cpp
===================================================================
--- common/patches/Underfoot.cpp	(revision 1798)
+++ common/patches/Underfoot.cpp	(working copy)
@@ -3000,6 +3000,16 @@
 	FINISH_DIRECT_DECODE();
 }
 
+DECODE(OP_AugmentItem) {
+	DECODE_LENGTH_EXACT(structs::AugmentItem_Struct);
+	SETUP_DIRECT_DECODE(AugmentItem_Struct, structs::AugmentItem_Struct);
+
+	emu->container_slot = UnderfootToTitaniumSlot(eq->container_slot);
+	emu->augment_slot = eq->augment_slot;
+
+	FINISH_DIRECT_DECODE();
+}
+
 DECODE(OP_TradeSkillCombine) {
 	DECODE_LENGTH_EXACT(structs::NewCombine_Struct);
 	SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct);
Index: common/patches/Underfoot_ops.h
===================================================================
--- common/patches/Underfoot_ops.h	(revision 1798)
+++ common/patches/Underfoot_ops.h	(working copy)
@@ -113,5 +113,6 @@
 D(OP_ChannelMessage)
 D(OP_DeleteItem)
 D(OP_PetCommands)
+D(OP_AugmentItem)
 #undef E
 #undef D
Index: zone/tradeskills.cpp
===================================================================
--- zone/tradeskills.cpp	(revision 1798)
+++ zone/tradeskills.cpp	(working copy)
@@ -45,16 +45,48 @@
 		LogFile->write(EQEMuLog::Error, "Client or AugmentItem_Struct not set in Object::HandleAugmentation");
 		return;
 	}
+	
+	ItemInst* container = NULL;
 
-	if(!worldo)
-	{
+	if (worldo) {
+		container = worldo->m_inst;
+	} 
+	else {
+		// Check to see if they have an inventory container type 53 that is used for this.
+		Inventory& user_inv = user->GetInv();
+		ItemInst* inst = NULL;
+		
+		inst = user_inv.GetItem(in_augment->container_slot);
+		if (inst) {
+			const Item_Struct* item = inst->GetItem();
+			if (item && inst->IsType(ItemClassContainer) && item->BagType == 53) {
+				// We have found an appropriate inventory augmentation sealer
+				container = inst;
+
+				// Verify that no more than two items are in container to guarantee no inadvertant wipes.
+				uint8 itemsFound = 0;
+				for (uint8 i=0; i<10; i++){
+					const ItemInst* inst = container->GetItem(i);
+					if (inst) {
+						itemsFound++;
+					}
+				}
+
+				if (itemsFound != 2) {
+					user->Message(13, "Error:  Too many/few items in augmentation container.");
+					return;
+				}
+			}
+		}
+	}
+
+	if(!container) {
 		LogFile->write(EQEMuLog::Error, "Player tried to augment an item without a world object set.");
 		return;
 	}
 	
 	ItemInst *tobe_auged, *auged_with = NULL;
 	sint8 slot=-1;
-	ItemInst* container = worldo->m_inst;
 
 	if (!container) {
 		user->Message(13, "Error: This item is not a container!");
@@ -78,18 +110,14 @@
 		}
 	}
 
+	bool deleteItems = false;
+
 	// Adding augment
 	if (in_augment->augment_slot == -1) {
 		if (((slot=tobe_auged->AvailableAugmentSlot(auged_with->GetAugmentType()))!=-1) && (tobe_auged->AvailableWearSlot(auged_with->GetItem()->Slots))) {
 			tobe_auged->PutAugment(slot,*auged_with);
 			user->PushItemOnCursor(*tobe_auged,true);
-			container->Clear();
-			EQApplicationPacket* outapp = new EQApplicationPacket(OP_ClearObject, sizeof(ClearObject_Struct));
-			ClearObject_Struct *cos = (ClearObject_Struct *)outapp->pBuffer;
-			cos->Clear = 1;
-			user->QueuePacket(outapp);
-			safe_delete(outapp);
-			database.DeleteWorldContainer(worldo->m_id, zone->GetZoneID());
+			deleteItems = true;
 		} else {
 			user->Message(13, "Error: No available slot for augment");
 		}
@@ -104,14 +132,31 @@
 		user->PushItemOnCursor(*tobe_auged,true);
 		if (old_aug)
 			user->PushItemOnCursor(*old_aug,true);
-		container->Clear();
-		EQApplicationPacket* outapp = new EQApplicationPacket(OP_ClearObject, sizeof(ClearObject_Struct));
-		ClearObject_Struct *cos = (ClearObject_Struct *)outapp->pBuffer;
-		cos->Clear = 1;
-		user->QueuePacket(outapp);
-		safe_delete(outapp);
-		database.DeleteWorldContainer(worldo->m_id, zone->GetZoneID());
+		deleteItems = true;
 	}
+
+	if (deleteItems) {
+		if (worldo) {
+			container->Clear();
+			EQApplicationPacket* outapp = new EQApplicationPacket(OP_ClearObject, sizeof(ClearObject_Struct));
+			ClearObject_Struct *cos = (ClearObject_Struct *)outapp->pBuffer;
+			cos->Clear = 1;
+			user->QueuePacket(outapp);
+			safe_delete(outapp);
+			database.DeleteWorldContainer(worldo->m_id, zone->GetZoneID());
+		}
+		else {
+			// Delete items in our inventory container... 
+			for (uint8 i=0; i<10; i++){
+				const ItemInst* inst = container->GetItem(i);
+				if (inst) {
+					user->DeleteItemInInventory(Inventory::CalcSlotId(in_augment->container_slot,i),0,true);
+				}
+			}
+			// Explicitly mark container as cleared.
+			container->Clear();
+		}
+	}
 }
 
 // Perform tradeskill combine
Reply With Quote
  #14  
Old 01-03-2011, 05:01 AM
trevius's Avatar
trevius
Developer
 
Join Date: Aug 2006
Location: USA
Posts: 5,946
Default

Excellent work! That looks much better to me

Unless anyone sees issues with this last submission, I will get it committed as soon as I have a chance.

I will be making use of it right away as well, so it should get some good testing fairly quickly. Currently, I have a custom collapsible aug pool item that I have cast a spell and spawn an aug pool on the spot. It works well enough, but is a bit of a pain since it requires code in all zones player.pl to make sure the aug pool gets removed when the zone is emptied. It also causes aug pools to become permanent in zones that crash or if the server is shut down with one in a zone. Your new aug container will be a MUCH better permanent solution. Not only does it mean portable augmenting, but means 100% (or close) safe aug combines without item loss.

Sorry I had to give you so much hassle with the original code submission, but I think it resulted in a great solution in the end
__________________
Trevazar/Trevius Owner of: Storm Haven
Everquest Emulator FAQ (Frequently Asked Questions) - Read It!
Reply With Quote
  #15  
Old 01-03-2011, 05:36 AM
lerxst2112
Demi-God
 
Join Date: Aug 2010
Posts: 1,743
Default

This is definitely much cleaner. I didn't think the first patch was necessarily a problem, but it was complex enough to make me worried about using it without a ton of testing.

I'm excited to see this in action.
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:27 AM.


 

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