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.
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