EQEmulator Forums

EQEmulator Forums (https://www.eqemulator.org/forums/index.php)
-   Development::Development (https://www.eqemulator.org/forums/forumdisplay.php?f=590)
-   -   Fix: Tradeskills - recipe lookups and combines (https://www.eqemulator.org/forums/showthread.php?t=22815)

Darkonig 05-22-2007 05:41 AM

Fix: Tradeskills - recipe lookups and combines
 
Here are the diffs for correcting the operation of tradeskills so that they work like they do in live. I am reposting this as right after I posted the original fix there were several patches that affected some of the same functions. I integrated those patches into my fix and am reposting as diffs to make it easier to use. An explanation of the changes can be found in my earlier post http://www.eqemulator.net/forums/showthread.php?t=22490

Since they are too big to fit into a single post, They will appear in the next 2 replies.

Darkonig 05-22-2007 05:44 AM

Code:

cvs diff -- client.h client_packet.cpp tradeskills.cpp zonedb.h (in directory M:\cvswork\EQEmuCVS\Source\zone\)
Index: client.h
===================================================================
RCS file: /cvsroot/eqemulator/EQEmuCVS/Source/zone/client.h,v
retrieving revision 1.49
diff -r1.49 client.h
496c496
<        bool TradeskillExecute(DBTradeskillRecipe_Struct *spec, SkillType tradeskill);
---
>        bool TradeskillExecute(DBTradeskillRecipe_Struct *spec);
Index: client_packet.cpp
===================================================================
RCS file: /cvsroot/eqemulator/EQEmuCVS/Source/zone/client_packet.cpp,v
retrieving revision 1.46
diff -r1.46 client_packet.cpp
3979,3982c3979,3991
<        uint32 tskill = Object::TypeToSkill(tsf->object_type);
<        if(tskill == 0) {
<                LogFile->write(EQEMuLog::Error, "Unknown container type for OP_RecipesFavorite: %d\n", tsf->object_type);
<                return;
---
>        LogFile->write(EQEMuLog::Debug, "Requested Favorites for: %d - %d\n", tsf->object_type, tsf->some_id);
>
>        // results show that object_type is combiner type
>        //                  some_id = 0 if world combiner, item number otherwise
>
>        // make where clause segment for container(s)
>        char containers[30];
>        if (tsf->some_id == 0) {
>                // world combiner so no item number
>                snprintf(containers,29, "= %u", tsf->object_type);
>        } else {
>                // container in inventory
>                snprintf(containers,29, "in (%u,%u)", tsf->object_type, tsf->some_id);
4014,4015c4023,4026
<                " WHERE tr.id IN (%s) AND tradeskill=%lu "
<                " GROUP BY tr.id LIMIT 100 ", buf, tskill);
---
>                " WHERE tr.id IN (%s) "
>                " GROUP BY tr.id "
>                " HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 "
>                " LIMIT 100 ", buf, containers);
4035,4038c4046,4055
<        uint32 tskill = Object::TypeToSkill(rss->object_type);
<        if(tskill == 0) {
<                LogFile->write(EQEMuLog::Error, "Unknown container type for OP_RecipesSearch: %d\n", rss->object_type);
<                return;
---
>        LogFile->write(EQEMuLog::Debug, "Requested search recipes for: %d - %d\n", rss->object_type, rss->some_id);
>
>        // make where clause segment for container(s)
>        char containers[30];
>        if (rss->some_id == 0) {
>                // world combiner so no item number
>                snprintf(containers,29, "= %u", rss->object_type);
>        } else {
>                // container in inventory
>                snprintf(containers,29, "in (%u,%u)", rss->object_type, rss->some_id);
4059,4060c4076,4080
<                " WHERE %s tr.trivial >= %u AND tr.trivial <= %u AND tradeskill=%lu "
<                " GROUP BY tr.id LIMIT 200 ", searchclause, rss->mintrivial, rss->maxtrivial, tskill);
---
>                " WHERE %s tr.trivial >= %u AND tr.trivial <= %u "
>                " GROUP BY tr.id "
>                " HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 "
>                " LIMIT 200 "
>                , searchclause, rss->mintrivial, rss->maxtrivial, containers);
Index: zonedb.h
===================================================================
RCS file: /cvsroot/eqemulator/EQEmuCVS/Source/zone/zonedb.h,v
retrieving revision 1.7
diff -r1.7 zonedb.h
37a38
>        Skilltype tradeskill;
226,227c227,228
<        bool        GetTradeRecipe(const ItemInst* container, uint8 c_type, uint8 tradeskill, DBTradeskillRecipe_Struct *spec);
<        bool        GetTradeRecipe(uint32 recipe_id, uint8 c_type, uint8 tradeskill, DBTradeskillRecipe_Struct *spec);
---
>        bool        GetTradeRecipe(const ItemInst* container, uint8 c_type, uint32 some_id, DBTradeskillRecipe_Struct *spec);
>        bool        GetTradeRecipe(uint32 recipe_id, uint8 c_type, uint32 some_id, DBTradeskillRecipe_Struct *spec);


Darkonig 05-22-2007 05:49 AM

Code:

Index: tradeskills.cpp
===================================================================
RCS file: /cvsroot/eqemulator/EQEmuCVS/Source/zone/tradeskills.cpp,v
retrieving revision 1.17
diff -r1.17 tradeskills.cpp
108,109c108,109
<        int8 tstype = 0xE8;
<        uint8 passtype = 0;
---
>        uint8 c_type = 0xE8;
>        uint32 some_id = 0;
117a118
>                c_type = worldo->m_type;
125c126,127
<                                tstype = item->BagType;
---
>                                c_type = item->BagType;
>                                some_id = item->ID;
137,139c139,148
<        // Convert container type to tradeskill type
<        SkillType tradeskill = TradeskillUnknown;
<        switch (tstype)
---
>        DBTradeskillRecipe_Struct spec;
>        if (!database.GetTradeRecipe(container, c_type, some_id, &spec)) {
>                user->Message_StringID(4,TRADESKILL_NOCOMBINE);
>                EQApplicationPacket* outapp = new EQApplicationPacket(OP_TradeSkillCombine, 0);
>                user->QueuePacket(outapp);
>                safe_delete(outapp);
>                return;
>        }
>       
>        switch (spec.tradeskill)
141,177c150,151
<        case 16:
<                tradeskill = TAILORING;
<                break;
<        case 0xE8: //Generic World Container
<                if(!worldcontainer)        //just to garuntee that worldo is valid
<                        return;
<                passtype = worldo->m_type;
<               
<                if(worldo->m_type == OT_MEDICINEBAG) {
<                        if ((user_pp.class_ == SHAMAN) & (user_pp.level >= MIN_LEVEL_ALCHEMY))
<                                tradeskill = ALCHEMY;
<                        else if (user_pp.class_ != SHAMAN)
<                                user->Message(13, "This tradeskill can only be performed by a shaman.");
<                        else if (user_pp.level < MIN_LEVEL_ALCHEMY)
<                                user->Message(13, "You cannot perform alchemy until you reach level %i.", MIN_LEVEL_ALCHEMY);
<                        break;
<                } else {
<                        tradeskill = TypeToSkill(worldo->m_type);
<                }
<                break;
<        case 18:
<                tradeskill = FLETCHING;
<                break;
<        case 20:
<                tradeskill = JEWELRY_MAKING;
<                break;
<        case 30: //Pottery Still needs completion
<                tradeskill = POTTERY;
<                break;
<        case 14: // Baking
<        case 15:
<                tradeskill = BAKING;
<                break;
<        case 9: //Alchemy Still needs completion
<                if ((user_pp.class_ == SHAMAN) & (user_pp.level >= MIN_LEVEL_ALCHEMY))
<                        tradeskill = ALCHEMY;
<                else if (user_pp.class_ != SHAMAN)
---
>        case ALCHEMY:
>                if (user_pp.class_ != SHAMAN)
180a155
>                return;
182,185c157,158
<        case 10: //Tinkering Still needs completion
<                if (user_pp.race == GNOME)
<                        tradeskill = TINKERING;
<                else
---
>        case TINKERING:
>                if (user_pp.race != GNOME)
186a160
>                return;
188,200c162,163
<        case 24: //Research Still needs completion
<        case 25:
<        case 26:
<        case 27:
<                tradeskill = RESEARCH;
<                break;
<        case 28: // Another Quest Containers.. Cavedude asked for this
<                tradeskill = GENERIC_TRADESKILL;
<                break;
<        case 12:
<                if (user_pp.class_ == ROGUE)
<                        tradeskill = MAKE_POISON;
<                else
---
>        case MAKE_POISON:
>                if (user_pp.class_ != ROGUE)
202,223d164
<                break;
<        case 13: //Quest Containers
<                tradeskill = GENERIC_TRADESKILL;
<                break;
<        case 46: //Fishing Still needs completion
<                tradeskill = FISHING;
<                break;
<        default:
<                user->Message(13, "This tradeskill has not been implemented yet, if you get this message send a "
<                        "petition and let them know what tradeskill you were trying to use. and give them the following code: 0x%02X", tradeskill);
<        }
<       
<        if (tradeskill == TradeskillUnknown) {
<                return;
<        }
<       
<        DBTradeskillRecipe_Struct spec;
<        if (!database.GetTradeRecipe(container, passtype, tradeskill, &spec)) {
<                user->Message_StringID(4,TRADESKILL_NOCOMBINE);
<                EQApplicationPacket* outapp = new EQApplicationPacket(OP_TradeSkillCombine, 0);
<                user->QueuePacket(outapp);
<                safe_delete(outapp);
224a166
>                break;
228c170
<        bool success = user->TradeskillExecute(&spec, tradeskill);
---
>        bool success = user->TradeskillExecute(&spec);
272,279d213
<        SkillType tskill = Object::TypeToSkill(rac->object_type);
<        if(tskill == TradeskillUnknown) {
<                LogFile->write(EQEMuLog::Error, "Unknown container type for HandleAutoCombine: %d\n", rac->object_type);
<                user->QueuePacket(outapp);
<                safe_delete(outapp);
<                return;
<        }
<       
282c216
<        if (!database.GetTradeRecipe(rac->recipe_id, rac->object_type, tskill, &spec)) {
---
>        if (!database.GetTradeRecipe(rac->recipe_id, rac->object_type, rac->some_id, &spec)) {
395c329
<        bool success = user->TradeskillExecute(&spec, tskill);
---
>        bool success = user->TradeskillExecute(&spec);
653,654c587,588
< bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec, SkillType tradeskill) {
<        if(spec == NULL || tradeskill == 0)
---
> bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) {
>        if(spec == NULL)
657c591
<        int16 user_skill = GetSkill(tradeskill);
---
>        int16 user_skill = GetSkill(spec->tradeskill);
676c610
<        switch(tradeskill) {
---
>        switch(spec->tradeskill) {
698c632
<        if (tradeskill ==  FLETCHING || tradeskill == MAKE_POISON) {
---
>        if (spec->tradeskill ==  FLETCHING || spec->tradeskill == MAKE_POISON) {
701c635
<        } else if (tradeskill == BLACKSMITHING) {
---
>        } else if (spec->tradeskill == BLACKSMITHING) {
780c714
<        if (((tradeskill==75) || GetGM() || (chance > res)) || MakeRandomInt(0, 99) < AAChance){
---
>        if (((spec->tradeskill==75) || GetGM() || (chance > res)) || MakeRandomInt(0, 99) < AAChance){
784c718
<                        CheckIncreaseTradeskill(bonusstat, stat_modifier, skillup_modifier, success_modifier, tradeskill);
---
>                        CheckIncreaseTradeskill(bonusstat, stat_modifier, skillup_modifier, success_modifier, spec->tradeskill);
801c735
<                        CheckIncreaseTradeskill(bonusstat, stat_modifier, skillup_modifier, success_modifier, tradeskill);
---
>                        CheckIncreaseTradeskill(bonusstat, stat_modifier, skillup_modifier, success_modifier, spec->tradeskill);

continued next post, still too big

Darkonig 05-22-2007 05:49 AM

Code:

858c792
< bool ZoneDatabase::GetTradeRecipe(const ItemInst* container, uint8 c_type, uint8 tradeskill,
---
> bool ZoneDatabase::GetTradeRecipe(const ItemInst* container, uint8 c_type, uint32 some_id,
872,881c806,813
<        //use the world item type as type if we have a world item
<        //otherwise use the item's ID... this make the assumption that
<        //no tradeskill containers will have an item ID which is
<        //below the highest ID of objects, which is currently 0x30
<        uint32 type = c_type;
<       
<        //dunno why I have to cast this up to call GetItem
<        const Item_Struct *istruct = ((const ItemInst *) container)->GetItem();
<        if(c_type == 0 && istruct) {
<                type = istruct->ID;
---
>        // make where clause segment for container(s)
>        char containers[30];
>        if (some_id == 0) {
>                // world combiner so no item number
>                snprintf(containers,29, "= %u", c_type);
>        } else {
>                // container in inventory
>                snprintf(containers,29, "in (%u,%u)", c_type, some_id);
913,916d844
<        //add in the container.
<        count++;
<        sum += type;
<       
920,922c848,850
<        "  OR ( tre.item_id=%u AND tre.iscontainer=1 )"
<        " GROUP BY tre.recipe_id HAVING sum(tre.componentcount+tre.iscontainer) = %u"
<        "  AND sum(tre.item_id * (tre.componentcount+tre.iscontainer)) = %u", buf2, type, count, sum);
---
>        "  OR ( tre.item_id %s AND tre.iscontainer=1 )"
>        " GROUP BY tre.recipe_id HAVING sum(tre.componentcount) = %u"
>        "  AND sum(tre.item_id * tre.componentcount) = %u", buf2, containers, count, sum);
925c853
<                LogFile->write(EQEMuLog::Error, "Error in GetTradeRecept search, query: %s", query);
---
>                LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe search, query: %s", query);
927c855
<                LogFile->write(EQEMuLog::Error, "Error in GetTradeRecept search, error: %s", errbuf);
---
>                LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe search, error: %s", errbuf);
956,959c884,886
<                " WHERE tre.recipe_id IN (%s) "
<                "      AND (tre.iscontainer=0 OR tre.item_id=%u) "
<                " GROUP BY tre.recipe_id HAVING sum(tre.componentcount+tre.iscontainer) = %u"
<                " AND sum(tre.item_id * (tre.componentcount+tre.iscontainer)) = %u", buf2, type, count, sum);
---
>                " WHERE tre.recipe_id IN (%s)"
>                " GROUP BY tre.recipe_id HAVING sum(tre.componentcount) = %u"
>                "  AND sum(tre.item_id * tre.componentcount) = %u", buf2, count, sum);
962c889
<                        LogFile->write(EQEMuLog::Error, "Error in GetTradeRecept, re-query: %s", query);
---
>                        LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, re-query: %s", query);
964c891
<                        LogFile->write(EQEMuLog::Error, "Error in GetTradeRecept, error: %s", errbuf);
---
>                        LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", errbuf);
1018c945
<        return(GetTradeRecipe(recipe_id, c_type, tradeskill, spec));
---
>        return(GetTradeRecipe(recipe_id, c_type, some_id, spec));
1023c950
< bool ZoneDatabase::GetTradeRecipe(uint32 recipe_id, uint8 c_type, uint8 tradeskill,
---
> bool ZoneDatabase::GetTradeRecipe(uint32 recipe_id, uint8 c_type, uint32 some_id,
1034,1036c961,976
<        qlen = MakeAnyLenString(&query, "SELECT tr.skillneeded, tr.trivial, tr.nofail, tr.replace_container"
<        " FROM tradeskill_recipe AS tr"
<        " WHERE tr.id = %lu AND tr.tradeskill = %u", recipe_id, tradeskill);
---
>        // make where clause segment for container(s)
>        char containers[30];
>        if (some_id == 0) {
>                // world combiner so no item number
>                snprintf(containers,29, "= %u", c_type);
>        } else {
>                // container in inventory
>                snprintf(containers,29, "in (%u,%u)", c_type, some_id);
>        }
>       
>        qlen = MakeAnyLenString(&query, "SELECT tr.id, tr.tradeskill, tr.skillneeded,"
>        " tr.trivial, tr.nofail, tr.replace_container"
>        " FROM tradeskill_recipe AS tr inner join tradeskill_recipe_entries as tre"
>        " ON tr.id = tre.recipe_id"
>        " WHERE tr.id = %lu AND tre.item_id %s"
>        " GROUP BY tr.id", recipe_id, containers);
1039c979
<                LogFile->write(EQEMuLog::Error, "Error in GetTradeRecept, query: %s", query);
---
>                LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, query: %s", query);
1041c981
<                LogFile->write(EQEMuLog::Error, "Error in GetTradeRecept, error: %s", errbuf);
---
>                LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", errbuf);
1053,1056c993,997
<        spec->skill_needed                = (sint16)atoi(row[0]);
<        spec->trivial                        = (uint16)atoi(row[1]);
<        spec->nofail                        = atoi(row[2]) ? true : false;
<        spec->replace_container        = atoi(row[3]) ? true : false;
---
>        spec->tradeskill                        = (SkillType)atoi(row[1]);
>        spec->skill_needed                = (sint16)atoi(row[2]);
>        spec->trivial                        = (uint16)atoi(row[3]);
>        spec->nofail                        = atoi(row[4]) ? true : false;
>        spec->replace_container        = atoi(row[5]) ? true : false;


KLS 05-25-2007 08:38 AM

I was asked to take a look at this stuff. So I am, but SVN has been a little =x lately with all the server issues so might not get in right away of course.

KLS 05-25-2007 09:31 AM

Hey, could you post the changed switch statement in tradeskills.cpp?

I'm having trouble reading the diff at that part.

Darkonig 05-25-2007 10:55 AM

Not sure which switch statement you are referring to so will post each of the modified methods.

Code:

// Perform tradeskill combine
// complete tradeskill rewrite by father nitwit, 8/2004
void Object::HandleCombine(Client* user, const NewCombine_Struct* in_combine, Object *worldo)
{
        if (!user || !in_combine) {
                LogFile->write(EQEMuLog::Error, "Client or NewCombine_Struct not set in Object::HandleCombine");
                return;
        }
       
        Inventory& user_inv = user->GetInv();
        PlayerProfile_Struct& user_pp = user->GetPP();
        ItemInst* container = NULL;
        ItemInst* inst = NULL;
        uint8 c_type = 0xE8;
        uint32 some_id = 0;
        bool worldcontainer=false;
       
        if (in_combine->container_slot == SLOT_WORLDCONTAINER) {
                if(!worldo) {
                        user->Message(13, "Error: Server is not aware of the tradeskill container you are attempting to use");
                        return;
                }
                inst = worldo->m_inst;
                c_type = worldo->m_type;
                worldcontainer=true;
        }
        else {
                inst = user_inv.GetItem(in_combine->container_slot);
                if (inst) {
                        const Item_Struct* item = inst->GetItem();
                        if (item && inst->IsType(ItemClassContainer)) {
                                c_type = item->BagType;
                                some_id = item->ID;
                        }
                }
        }
       
        if (!inst || !inst->IsType(ItemClassContainer)) {
                user->Message(13, "Error: Server does not recognize specified tradeskill container");
                return;
        }
       
        container = inst;
       
        DBTradeskillRecipe_Struct spec;
        if (!database.GetTradeRecipe(container, c_type, some_id, &spec)) {
                user->Message_StringID(4,TRADESKILL_NOCOMBINE);
                EQApplicationPacket* outapp = new EQApplicationPacket(OP_TradeSkillCombine, 0);
                user->QueuePacket(outapp);
                safe_delete(outapp);
                return;
        }
       
        switch (spec.tradeskill)
        {
        case ALCHEMY:
                if (user_pp.class_ != SHAMAN)
                        user->Message(13, "This tradeskill can only be performed by a shaman.");
                else if (user_pp.level < MIN_LEVEL_ALCHEMY)
                        user->Message(13, "You cannot perform alchemy until you reach level %i.", MIN_LEVEL_ALCHEMY);
                return;
                break;
        case TINKERING:
                if (user_pp.race != GNOME)
                        user->Message(13, "Only gnomes can tinker.");
                return;
                break;
        case MAKE_POISON:
                if (user_pp.class_ != ROGUE)
                        user->Message(13, "Only rogues can mix poisons.");
                return;
                break;
        }
       
        //do the check and send results...
        bool success = user->TradeskillExecute(&spec);
       
        // Send acknowledgement packets to client
        EQApplicationPacket* outapp = new EQApplicationPacket(OP_TradeSkillCombine, 0);
        user->QueuePacket(outapp);
        safe_delete(outapp);
       
        //now clean out the containers.
        if(worldcontainer){
                container->Clear();
                outapp = new EQApplicationPacket(OP_ClearObject,0);
                user->QueuePacket(outapp);
                safe_delete(outapp);
                database.DeleteWorldContainer(worldo->m_id, zone->GetZoneID());
                if(success && spec.replace_container) {
                        //should report this error, but we dont have the recipe ID, so its not very useful
                        LogFile->write(EQEMuLog::Error, "Replace container combine executed in a world container.");
                }
        } else{
                for (uint8 i=0; i<10; i++){
                        const ItemInst* inst = container->GetItem(i);
                        if (inst) {
                                user->DeleteItemInInventory(Inventory::CalcSlotId(in_combine->container_slot,i),0,true);
                        }
                }
                container->Clear();
                if(success && spec.replace_container) {
                        user->DeleteItemInInventory(in_combine->container_slot, 0, true);
                }
        }
}

Code:

void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac) {
       
        //get our packet ready, gotta send one no matter what...
        EQApplicationPacket* outapp = new EQApplicationPacket(OP_RecipeAutoCombine, sizeof(RecipeAutoCombine_Struct));
        RecipeAutoCombine_Struct *outp = (RecipeAutoCombine_Struct *)outapp->pBuffer;
        outp->object_type = rac->object_type;
        outp->some_id = rac->some_id;
        outp->unknown1 = rac->unknown1;
        outp->recipe_id = rac->recipe_id;
        outp->reply_code = 0xFFFFFFF5;        //default fail.
       
       
        //ask the database for the recipe to make sure it exists...
        DBTradeskillRecipe_Struct spec;
        if (!database.GetTradeRecipe(rac->recipe_id, rac->object_type, rac->some_id, &spec)) {
                LogFile->write(EQEMuLog::Error, "Unknown recipe for HandleAutoCombine: %u\n", rac->recipe_id);
                user->QueuePacket(outapp);
                safe_delete(outapp);
                return;
        }
       
        char errbuf[MYSQL_ERRMSG_SIZE];
    MYSQL_RES *result;
    MYSQL_ROW row;
    char *query = 0;
       
        uint32 qlen = 0;
        uint8 qcount = 0;

        //pull the list of components
        qlen = MakeAnyLenString(&query, "SELECT tre.item_id,tre.componentcount "
        " FROM tradeskill_recipe_entries AS tre "
        " WHERE tre.componentcount > 0 AND tre.recipe_id=%u", rac->recipe_id);

        if (!database.RunQuery(query, qlen, errbuf, &result)) {
                LogFile->write(EQEMuLog::Error, "Error in HandleAutoCombine query '%s': %s", query, errbuf);
                safe_delete_array(query);
                user->QueuePacket(outapp);
                safe_delete(outapp);
                return;
        }
        safe_delete_array(query);
       
        qcount = mysql_num_rows(result);
        if(qcount < 1) {
                LogFile->write(EQEMuLog::Error, "Error in HandleAutoCombine: no components returned");
                user->QueuePacket(outapp);
                safe_delete(outapp);
                return;
        }
        if(qcount > 10) {
                LogFile->write(EQEMuLog::Error, "Error in HandleAutoCombine: too many components returned (%u)", qcount);
                user->QueuePacket(outapp);
                safe_delete(outapp);
                return;
        }
       
        uint32 items[10];
        memset(items, 0, sizeof(items));
        uint8 counts[10];
        memset(counts, 0, sizeof(counts));
       
       
        //search for all the crap in their inventory
        Inventory& user_inv = user->GetInv();
        uint8 count = 0;
        uint8 needcount = 0;
        uint8 r,k;
        for(r = 0; r < qcount; r++) {
                row = mysql_fetch_row(result);
                uint32 item = (uint32)atoi(row[0]);
                uint8 num = (uint8) atoi(row[1]);
               
                needcount += num;
               
                //because a HasItem on items with num > 1 only returns the
                //last-most slot... the results of this are useless to us
                //when we go to delete them because we cannot assume it is in a single stack.
                if(user_inv.HasItem(item, num, invWherePersonal) != SLOT_INVALID)
                        count += num;
                //dont start deleting anything until we have found it all.
                items[r] = item;
                counts[r] = num;
        }
        mysql_free_result(result);
       
        //make sure we found it all...
        if(count != needcount) {
                user->QueuePacket(outapp);
                safe_delete(outapp);
                return;
        }
       
        //now we know they have everything...
       
        //remove all the items from the players inventory, with updates...
        sint16 slot;
        for(r = 0; r < qcount; r++) {
                if(items[r] == 0 || counts[r] == 0)
                        continue;        //skip empties, could prolly break here
               
                //we have to loop here to delete 1 at a time in case its in multiple stacks.
                for(k = 0; k < counts[r]; k++) {
                        slot = user_inv.HasItem(items[r], 1, invWherePersonal);
                        if(slot == SLOT_INVALID) {
                                //WTF... I just checked this above, but just to be sure...
                                //we cant undo the previous deletes without a lot of work.
                                //so just call it quits, this shouldent ever happen anyways.
                                user->QueuePacket(outapp);
                                safe_delete(outapp);
                                return;
                        }
                       
                        user->DeleteItemInInventory(slot, 1, true);
                }
        }
       
        //otherwise, we found it all...
        outp->reply_code = 0x00000000;        //success for finding it...
       
        user->QueuePacket(outapp);
        //DumpPacket(outapp);
        safe_delete(outapp);
       
       
        //now actually try to make something...
       
        bool success = user->TradeskillExecute(&spec);
       
        //TODO: find in-pack containers in inventory, make sure they are really
        //there, and then use that slot to handle replace_container too.
        if(success && spec.replace_container) {
//                user->DeleteItemInInventory(in_combine->container_slot, 0, true);
        }
       
}


Darkonig 05-25-2007 10:57 AM

Code:

//returns true on success
bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) {
        if(spec == NULL)
                return(false);
       
        int16 user_skill = GetSkill(spec->tradeskill);
        float chance = 0;
        float skillup_modifier;
        sint16 thirdstat = 0;
        sint16 stat_modifier = 15;
        uint16 success_modifier;

        // Rework based on the info on eqtraders.com
        // http://mboards.eqtraders.com/eq/showthread.php?t=22246
        // 09/10/2006 v0.1 (eq4me)
        // 09/11/2006 v0.2 (eq4me)
        // Todo:
        //    Implementing AAs
        //    Success modifiers based on recipes
        //    Skillup modifiers based on the rarity of the ingredients

        // Some tradeskills are more eqal then others. ;-)
        // If you want to customize the stage1 success rate do it here.
    // Remember: skillup_modifier is (float). Lower is better
        switch(spec->tradeskill) {
        case FLETCHING:
        case ALCHEMY:
        case JEWELRY_MAKING:
        case POTTERY:
                skillup_modifier = 4;
                break;
        case BAKING:
        case BREWING:
                skillup_modifier = 3;
                break;
        case RESEARCH:
                skillup_modifier = 1;
                break;
        default:
                skillup_modifier = 2;
                break;
        }

        // Some tradeskills take the higher of one additional stat beside INT and WIS
        // to determine the skillup rate. Additionally these tradeskills do not have an
        // -15 modifier on their statbonus.
        switch(spec->tradeskill) {
        case FLETCHING:
        case MAKE_POISON:
                thirdstat = GetDEX();
                stat_modifier = 0;
                break;
        case BLACKSMITHING:
                thirdstat = GetSTR();
                stat_modifier = 0;
                break;
        default:
                break;
        }
       
        sint16 higher_from_int_wis = (GetINT() > GetWIS()) ? GetINT() : GetWIS();
        sint16 bonusstat = (higher_from_int_wis > thirdstat) ? higher_from_int_wis : thirdstat;
       
        vector< pair<uint32,uint8> >::iterator itr;


    //calculate the base success chance
        // For trivials over 68 the chance is (skill - 0.75*trivial) +51.5
    // For trivial up to 68 the chance is (skill - trivial) + 66
        if (spec->trivial >= 68) {
                chance = (user_skill - (0.75*spec->trivial)) + 51.5;
        } else {
                chance = (user_skill - spec->trivial) + 66;
        }
       
        sint16 over_trivial = (sint16)user_skill - (sint16)spec->trivial;

        //handle caps
        if(spec->nofail) {
                chance = 100;        //cannot fail.
                _log(TRADESKILLS__TRACE, "...This combine cannot fail.");
        } else if(over_trivial > 0) {
                // At reaching trivial the chance goes to 95% going up an additional
                // percent for every 40 skillpoints above the trivial.
                // The success rate is not modified through stats.
                // Mastery AAs are unaccounted for so far.
                // chance_AA = chance + ((100 - chance) * mastery_modifier)
                // But the 95% limit with an additional 1% for every 40 skill points
                // above critical still stands.
                // Mastery modifier is: 10%/25%/50% for rank one/two/three
                chance = 95.0f + (float(user_skill - spec->trivial) / 40.0f);
                Message_StringID(4, TRADESKILL_TRIVIAL);
        } else if(chance < 5) {
                // Minimum chance is always 5
                chance = 5;
        } else if(chance > 95) {
                //cap is 95, shouldent reach this before trivial, but just in case.
                chance = 95;
        }
       
        _log(TRADESKILLS__TRACE, "...Current skill: %d , Trivial: %d , Success chance: %f percent", user_skill , spec->trivial , chance);
        _log(TRADESKILLS__TRACE, "...Bonusstat: %d , INT: %d , WIS: %d , DEX: %d , STR: %d", bonusstat , GetINT() , GetWIS() , GetDEX() , GetSTR());
       
        float res = MakeRandomFloat(0, 99);
        if ((spec->tradeskill==75) || GetGM() || (chance > res)){
                success_modifier = 1;
               
                if(over_trivial < 0)
                        CheckIncreaseTradeskill(bonusstat, stat_modifier, skillup_modifier, success_modifier, spec->tradeskill);
               
                Message_StringID(4,TRADESKILL_SUCCEED);

                _log(TRADESKILLS__TRACE, "Tradeskill success");

                itr = spec->onsuccess.begin();
                while(itr != spec->onsuccess.end()) {
                        //should we check this crap?
                        SummonItem(itr->first, itr->second);
                        itr++;
                }
                return(true);
        } else {
                success_modifier = 2; // Halves the chance
               
                if(over_trivial < 0)
                        CheckIncreaseTradeskill(bonusstat, stat_modifier, skillup_modifier, success_modifier, spec->tradeskill);
               
                Message_StringID(4,TRADESKILL_FAILED);

                _log(TRADESKILLS__TRACE, "Tradeskill failed");
               
                itr = spec->onfail.begin();
                while(itr != spec->onfail.end()) {
                        //should we check these arguments?
                        SummonItem(itr->first, itr->second);
                        itr++;
                }
        }
        return(false);
}


Darkonig 05-25-2007 10:58 AM

Code:

bool ZoneDatabase::GetTradeRecipe(const ItemInst* container, uint8 c_type, uint32 some_id,
        DBTradeskillRecipe_Struct *spec)
{
        char errbuf[MYSQL_ERRMSG_SIZE];
    MYSQL_RES *result;
    MYSQL_ROW row;
    char *query = 0;
        char buf2[2048];
       
        uint32 sum = 0;
        uint32 count = 0;
        uint32 qcount = 0;
        uint32 qlen = 0;

        // make where clause segment for container(s)
        char containers[30];
        if (some_id == 0) {
                // world combiner so no item number
                snprintf(containers,29, "= %u", c_type);
        } else {
                // container in inventory
                snprintf(containers,29, "in (%u,%u)", c_type, some_id);
        }

        buf2[0] = '\0';
       
        //Could prolly watch for stacks in this loop and handle them properly...
        //just increment sum and count accordingly
        bool first = true;
        uint8 i;
        char *pos = buf2;
        for (i=0; i<10; i++) {
                const ItemInst* inst = container->GetItem(i);
                if (inst) {
                        const Item_Struct* item = GetItem(inst->GetItem()->ID);
                        if (item) {
                                if(first) {
                                        pos += snprintf(pos, 19, "%d", item->ID);
                                        first = false;
                                } else {
                                        pos += snprintf(pos, 19, ",%d", item->ID);
                                }
                                sum += item->ID;
                                count++;
                        }
                }
        }
        *pos = '\0';
       
        if(count < 1) {
                return(false);        //no items == no recipe
        }
       
        qlen = MakeAnyLenString(&query, "SELECT tre.recipe_id "
        " FROM tradeskill_recipe_entries AS tre"
        " WHERE ( tre.item_id IN(%s) AND tre.componentcount>0 )"
        "  OR ( tre.item_id %s AND tre.iscontainer=1 )"
        " GROUP BY tre.recipe_id HAVING sum(tre.componentcount) = %u"
        "  AND sum(tre.item_id * tre.componentcount) = %u", buf2, containers, count, sum);
       
        if (!RunQuery(query, qlen, errbuf, &result)) {
                LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe search, query: %s", query);
                safe_delete_array(query);
                LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe search, error: %s", errbuf);
                return(false);
        }
        safe_delete_array(query);
       
        qcount = mysql_num_rows(result);
        if(qcount > 1) {
                //multiple recipes, partial match... do an extra query to get it exact.
                //this happens when combining components for a smaller recipe
                //which is completely contained within another recipe
               
                first = true;
                pos = buf2;
                for (i = 0; i < qcount; i++) {
                        row = mysql_fetch_row(result);
                        uint32 recipeid = (uint32) atoi(row[0]);
                        if(first) {
                                pos += snprintf(pos, 19, "%u", recipeid);
                                first = false;
                        } else {
                                pos += snprintf(pos, 19, ",%u", recipeid);
                        }
                        //length sanity check on buf2
                        if(pos > (buf2 + 2020))
                                break;
                }
               
                qlen = MakeAnyLenString(&query, "SELECT tre.recipe_id"
                " FROM tradeskill_recipe_entries AS tre"
                " WHERE tre.recipe_id IN (%s)"
                " GROUP BY tre.recipe_id HAVING sum(tre.componentcount) = %u"
                "  AND sum(tre.item_id * tre.componentcount) = %u", buf2, count, sum);
               
                if (!RunQuery(query, qlen, errbuf, &result)) {
                        LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, re-query: %s", query);
                        safe_delete_array(query);
                        LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", errbuf);
                        return(false);
                }
                safe_delete_array(query);
       
                qcount = mysql_num_rows(result);
        }
        if (qcount != 1) {
                if(qcount > 1) {
                        LogFile->write(EQEMuLog::Error, "Combine error: Recipe is not unique!");
                }
                //else, just not found i guess..
                return(false);
        }
       
        row = mysql_fetch_row(result);
        uint32 recipe_id = (uint32)atoi(row[0]);
        mysql_free_result(result);
       
        return(GetTradeRecipe(recipe_id, c_type, some_id, spec));
}

Code:

bool ZoneDatabase::GetTradeRecipe(uint32 recipe_id, uint8 c_type, uint32 some_id,
        DBTradeskillRecipe_Struct *spec)
{       
        char errbuf[MYSQL_ERRMSG_SIZE];
    MYSQL_RES *result;
    MYSQL_ROW row;
    char *query = 0;
       
        uint32 qcount = 0;
        uint32 qlen;

        // make where clause segment for container(s)
        char containers[30];
        if (some_id == 0) {
                // world combiner so no item number
                snprintf(containers,29, "= %u", c_type);
        } else {
                // container in inventory
                snprintf(containers,29, "in (%u,%u)", c_type, some_id);
        }
       
        qlen = MakeAnyLenString(&query, "SELECT tr.id, tr.tradeskill, tr.skillneeded,"
        " tr.trivial, tr.nofail, tr.replace_container"
        " FROM tradeskill_recipe AS tr inner join tradeskill_recipe_entries as tre"
        " ON tr.id = tre.recipe_id"
        " WHERE tr.id = %lu AND tre.item_id %s"
        " GROUP BY tr.id", recipe_id, containers);
               
        if (!RunQuery(query, qlen, errbuf, &result)) {
                LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, query: %s", query);
                safe_delete_array(query);
                LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", errbuf);
                return(false);
        }
        safe_delete_array(query);
       
        qcount = mysql_num_rows(result);
        if(qcount != 1) {
                //just not found i guess..
                return(false);
        }
       
        row = mysql_fetch_row(result);
        spec->tradeskill                        = (SkillType)atoi(row[1]);
        spec->skill_needed                = (sint16)atoi(row[2]);
        spec->trivial                        = (uint16)atoi(row[3]);
        spec->nofail                        = atoi(row[4]) ? true : false;
        spec->replace_container        = atoi(row[5]) ? true : false;
        mysql_free_result(result);
       
        //Pull the on-success items...
        qlen = MakeAnyLenString(&query, "SELECT item_id,successcount FROM tradeskill_recipe_entries"
        " WHERE successcount>0 AND componentcount=0 AND recipe_id=%u", recipe_id);
       
        if (!RunQuery(query, qlen, errbuf, &result)) {
                LogFile->write(EQEMuLog::Error, "Error in GetTradeRecept success query '%s': %s", query, errbuf);
                safe_delete_array(query);
                return(false);
        }
        safe_delete_array(query);
       
        qcount = mysql_num_rows(result);
        if(qcount < 1) {
                LogFile->write(EQEMuLog::Error, "Error in GetTradeRecept success: no success items returned");
                return(false);
        }
        uint8 r;
        spec->onsuccess.clear();
        for(r = 0; r < qcount; r++) {
                row = mysql_fetch_row(result);
                /*if(r == 0) {
                        *product1_id        = (uint32)atoi(row[0]);
                        *productcount        = (uint32)atoi(row[1]);
                } else if(r == 1) {
                        *product2_id        = (uint32)atoi(row[0]);
                } else {
                        LogFile->write(EQEMuLog::Warning, "Warning: recipe returned more than 2 products, not yet supported.");
                }*/
                uint32 item = (uint32)atoi(row[0]);
                uint8 num = (uint8) atoi(row[1]);
                spec->onsuccess.push_back(pair<uint32,uint8>::pair(item, num));
        }
        mysql_free_result(result);
       
       
        //Pull the on-fail items...
        qlen = MakeAnyLenString(&query, "SELECT item_id,failcount FROM tradeskill_recipe_entries"
        " WHERE failcount>0 AND componentcount=0 AND recipe_id=%u", recipe_id);

        spec->onfail.clear();
        if (RunQuery(query, qlen, errbuf, &result)) {
               
                qcount = mysql_num_rows(result);
                uint8 r;
                for(r = 0; r < qcount; r++) {
                        row = mysql_fetch_row(result);
                        /*if(r == 0) {
                                *failproduct_id        = (uint32)atoi(row[0]);
                        } else {
                                LogFile->write(EQEMuLog::Warning, "Warning: recipe returned more than 1 fail product, not yet supported.");
                        }*/
                        uint32 item = (uint32)atoi(row[0]);
                        uint8 num = (uint8) atoi(row[1]);
                        spec->onfail.push_back(pair<uint32,uint8>::pair(item, num));
                }
                mysql_free_result(result);
        }
        safe_delete_array(query);
       
        return(true);
}


KLS 05-25-2007 11:19 AM

That's fine, thanks. Didn't have patch installed for diffs and was doing them by eye but without context diffs I got turned around. =p


All times are GMT -4. The time now is 02:54 PM.

Powered by vBulletin®, Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.