mirror of
https://github.com/EQEmu/Server.git
synced 2026-03-25 08:32:25 +00:00
Separate tradeskill queries to not be cross-boundary, clean up logic
This commit is contained in:
parent
e9b361445f
commit
128cc458fd
@ -196,6 +196,8 @@ SET(common_headers
|
|||||||
queue.h
|
queue.h
|
||||||
races.h
|
races.h
|
||||||
random.h
|
random.h
|
||||||
|
repositories/character_recipe_list_repository.h
|
||||||
|
repositories/tradeskill_recipe_repository.h
|
||||||
rdtsc.h
|
rdtsc.h
|
||||||
rulesys.h
|
rulesys.h
|
||||||
ruletypes.h
|
ruletypes.h
|
||||||
|
|||||||
117
common/repositories/character_recipe_list_repository.h
Normal file
117
common/repositories/character_recipe_list_repository.h
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
/**
|
||||||
|
* EQEmulator: Everquest Server Emulator
|
||||||
|
* Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server)
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; version 2 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY except by those people which sell it, which
|
||||||
|
* are required to give you total support for your newly bought product;
|
||||||
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef EQEMU_CHARACTER_RECIPE_LIST_REPOSITORY_H
|
||||||
|
#define EQEMU_CHARACTER_RECIPE_LIST_REPOSITORY_H
|
||||||
|
|
||||||
|
#include "../database.h"
|
||||||
|
#include "../string_util.h"
|
||||||
|
|
||||||
|
class CharacterRecipeListRepository {
|
||||||
|
public:
|
||||||
|
struct CharacterRecipeList {
|
||||||
|
int character_id;
|
||||||
|
int recipe_id;
|
||||||
|
int made_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::vector<std::string> Columns()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
"char_id",
|
||||||
|
"recipe_id",
|
||||||
|
"madecount",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string ColumnsRaw()
|
||||||
|
{
|
||||||
|
return std::string(implode(", ", Columns()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string TableName()
|
||||||
|
{
|
||||||
|
return std::string("char_recipe_list");
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string BaseSelect()
|
||||||
|
{
|
||||||
|
return std::string(
|
||||||
|
fmt::format(
|
||||||
|
"SELECT {} FROM {}",
|
||||||
|
ColumnsRaw(),
|
||||||
|
TableName()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static CharacterRecipeList GetDefaults()
|
||||||
|
{
|
||||||
|
CharacterRecipeList character_recipe_list_entry;
|
||||||
|
|
||||||
|
character_recipe_list_entry.character_id = 0;
|
||||||
|
character_recipe_list_entry.made_count = 0;
|
||||||
|
character_recipe_list_entry.recipe_id = 0;
|
||||||
|
|
||||||
|
return character_recipe_list_entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<CharacterRecipeList> GetLearnedRecipeList(int character_id)
|
||||||
|
{
|
||||||
|
std::vector<CharacterRecipeList> character_recipe_list;
|
||||||
|
|
||||||
|
auto results = database.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{} WHERE char_id = {}",
|
||||||
|
BaseSelect(),
|
||||||
|
character_id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||||
|
CharacterRecipeList character_recipe_list_entry;
|
||||||
|
|
||||||
|
character_recipe_list_entry.character_id = character_id;
|
||||||
|
character_recipe_list_entry.recipe_id = atoi(row[1]);
|
||||||
|
character_recipe_list_entry.made_count = atoi(row[2]);
|
||||||
|
|
||||||
|
character_recipe_list.push_back(character_recipe_list_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return character_recipe_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
static CharacterRecipeList GetRecipe(
|
||||||
|
std::vector<CharacterRecipeList> character_recipe_list,
|
||||||
|
int recipe_id
|
||||||
|
)
|
||||||
|
{
|
||||||
|
for (auto &row : character_recipe_list) {
|
||||||
|
if (row.recipe_id == recipe_id) {
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetDefaults();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //EQEMU_CHARACTER_RECIPE_LIST_REPOSITORY_H
|
||||||
134
common/repositories/tradeskill_recipe_repository.h
Normal file
134
common/repositories/tradeskill_recipe_repository.h
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
/**
|
||||||
|
* EQEmulator: Everquest Server Emulator
|
||||||
|
* Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server)
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; version 2 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY except by those people which sell it, which
|
||||||
|
* are required to give you total support for your newly bought product;
|
||||||
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef EQEMU_TRADESKILL_RECIPE_REPOSITORY_H
|
||||||
|
#define EQEMU_TRADESKILL_RECIPE_REPOSITORY_H
|
||||||
|
|
||||||
|
#include "../database.h"
|
||||||
|
#include "../string_util.h"
|
||||||
|
|
||||||
|
class TradeskillRecipeRepository {
|
||||||
|
public:
|
||||||
|
struct TradeskillRecipe {
|
||||||
|
int id;
|
||||||
|
std::string name;
|
||||||
|
int tradeskill;
|
||||||
|
int skillneeded;
|
||||||
|
int trivial;
|
||||||
|
uint8 nofail;
|
||||||
|
int replace_container;
|
||||||
|
std::string notes;
|
||||||
|
uint8 must_learn;
|
||||||
|
uint8 quest;
|
||||||
|
uint8 enabled;
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::vector<std::string> Columns()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"tradeskill",
|
||||||
|
"skillneeded",
|
||||||
|
"trivial",
|
||||||
|
"nofail",
|
||||||
|
"replace_container",
|
||||||
|
"notes",
|
||||||
|
"must_learn",
|
||||||
|
"quest",
|
||||||
|
"enabled",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string ColumnsRaw()
|
||||||
|
{
|
||||||
|
return std::string(implode(", ", Columns()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string TableName()
|
||||||
|
{
|
||||||
|
return std::string("tradeskill_recipe");
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string BaseSelect()
|
||||||
|
{
|
||||||
|
return std::string(
|
||||||
|
fmt::format(
|
||||||
|
"SELECT {} FROM {}",
|
||||||
|
ColumnsRaw(),
|
||||||
|
TableName()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TradeskillRecipe New()
|
||||||
|
{
|
||||||
|
TradeskillRecipe entry;
|
||||||
|
|
||||||
|
entry.id = 0;
|
||||||
|
entry.name = "";
|
||||||
|
entry.tradeskill = 0;
|
||||||
|
entry.skillneeded = 0;
|
||||||
|
entry.trivial = 0;
|
||||||
|
entry.nofail = 0;
|
||||||
|
entry.replace_container = 0;
|
||||||
|
entry.notes = "";
|
||||||
|
entry.must_learn = 0;
|
||||||
|
entry.quest = 0;
|
||||||
|
entry.enabled = 0;
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
static TradeskillRecipe GetRecipe(int recipe_id)
|
||||||
|
{
|
||||||
|
auto results = content_db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{} WHERE id = {}",
|
||||||
|
BaseSelect(),
|
||||||
|
recipe_id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
TradeskillRecipe tradeskill_recipe = New();
|
||||||
|
|
||||||
|
auto row = results.begin();
|
||||||
|
if (results.RowCount() == 0) {
|
||||||
|
return tradeskill_recipe;
|
||||||
|
}
|
||||||
|
|
||||||
|
tradeskill_recipe.id = atoi(row[0]);
|
||||||
|
tradeskill_recipe.name = (row[1] ? row[1] : "");
|
||||||
|
tradeskill_recipe.tradeskill = atoi(row[2]);
|
||||||
|
tradeskill_recipe.skillneeded = atoi(row[3]);
|
||||||
|
tradeskill_recipe.trivial = atoi(row[4]);
|
||||||
|
tradeskill_recipe.nofail = atoi(row[5]);
|
||||||
|
tradeskill_recipe.replace_container = atoi(row[6]);
|
||||||
|
tradeskill_recipe.notes = (row[7] ? row[7] : "");
|
||||||
|
tradeskill_recipe.must_learn = atoi(row[8]);
|
||||||
|
tradeskill_recipe.quest = atoi(row[9]);
|
||||||
|
tradeskill_recipe.enabled = atoi(row[10]);
|
||||||
|
|
||||||
|
return tradeskill_recipe;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -289,7 +289,7 @@ public:
|
|||||||
const char *message9 = nullptr);
|
const char *message9 = nullptr);
|
||||||
void Tell_StringID(uint32 string_id, const char *who, const char *message);
|
void Tell_StringID(uint32 string_id, const char *who, const char *message);
|
||||||
void SendColoredText(uint32 color, std::string message);
|
void SendColoredText(uint32 color, std::string message);
|
||||||
void SendBazaarResults(uint32 trader_id,uint32 class_,uint32 race,uint32 stat,uint32 slot,uint32 type,char name[64],uint32 minprice,uint32 maxprice);
|
void SendBazaarResults(uint32 trader_id, uint32 in_class, uint32 in_race, uint32 item_stat, uint32 item_slot, uint32 item_type, char item_name[64], uint32 min_price, uint32 max_price);
|
||||||
void SendTraderItem(uint32 item_id,uint16 quantity);
|
void SendTraderItem(uint32 item_id,uint16 quantity);
|
||||||
uint16 FindTraderItem(int32 SerialNumber,uint16 Quantity);
|
uint16 FindTraderItem(int32 SerialNumber,uint16 Quantity);
|
||||||
uint32 FindTraderItemSerialNumber(int32 ItemID);
|
uint32 FindTraderItemSerialNumber(int32 ItemID);
|
||||||
@ -325,7 +325,7 @@ public:
|
|||||||
void FilteredMessage(Mob *sender, uint32 type, eqFilterType filter, const char* message, ...);
|
void FilteredMessage(Mob *sender, uint32 type, eqFilterType filter, const char* message, ...);
|
||||||
void VoiceMacroReceived(uint32 Type, char *Target, uint32 MacroNumber);
|
void VoiceMacroReceived(uint32 Type, char *Target, uint32 MacroNumber);
|
||||||
void SendSound();
|
void SendSound();
|
||||||
void LearnRecipe(uint32 recipeID);
|
void LearnRecipe(uint32 recipe_id);
|
||||||
bool CanIncreaseTradeskill(EQEmu::skills::SkillType tradeskill);
|
bool CanIncreaseTradeskill(EQEmu::skills::SkillType tradeskill);
|
||||||
|
|
||||||
EQApplicationPacket* ReturnItemPacket(int16 slot_id, const EQEmu::ItemInstance* inst, ItemPacketType packet_type);
|
EQApplicationPacket* ReturnItemPacket(int16 slot_id, const EQEmu::ItemInstance* inst, ItemPacketType packet_type);
|
||||||
@ -745,7 +745,7 @@ public:
|
|||||||
inline uint16 MaxSkill(EQEmu::skills::SkillType skillid) const { return MaxSkill(skillid, GetClass(), GetLevel()); }
|
inline uint16 MaxSkill(EQEmu::skills::SkillType skillid) const { return MaxSkill(skillid, GetClass(), GetLevel()); }
|
||||||
uint8 SkillTrainLevel(EQEmu::skills::SkillType skillid, uint16 class_);
|
uint8 SkillTrainLevel(EQEmu::skills::SkillType skillid, uint16 class_);
|
||||||
|
|
||||||
void TradeskillSearchResults(const std::string &query, unsigned long objtype, unsigned long someid);
|
void SendTradeskillSearchResults(const std::string &query, unsigned long objtype, unsigned long someid);
|
||||||
void SendTradeskillDetails(uint32 recipe_id);
|
void SendTradeskillDetails(uint32 recipe_id);
|
||||||
bool TradeskillExecute(DBTradeskillRecipe_Struct *spec);
|
bool TradeskillExecute(DBTradeskillRecipe_Struct *spec);
|
||||||
void CheckIncreaseTradeskill(int16 bonusstat, int16 stat_modifier, float skillup_modifier, uint16 success_modifier, EQEmu::skills::SkillType tradeskill);
|
void CheckIncreaseTradeskill(int16 bonusstat, int16 stat_modifier, float skillup_modifier, uint16 success_modifier, EQEmu::skills::SkillType tradeskill);
|
||||||
|
|||||||
@ -11828,90 +11828,166 @@ void Client::Handle_OP_RecipesFavorite(const EQApplicationPacket *app)
|
|||||||
if (first) //no favorites....
|
if (first) //no favorites....
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const std::string query = StringFormat("SELECT tr.id, tr.name, tr.trivial, "
|
const std::string query = StringFormat(
|
||||||
"SUM(tre.componentcount), crl.madecount,tr.tradeskill "
|
SQL (
|
||||||
"FROM tradeskill_recipe AS tr "
|
SELECT
|
||||||
"LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id=tre.recipe_id "
|
tr.id,
|
||||||
"LEFT JOIN (SELECT recipe_id, madecount "
|
tr.name,
|
||||||
"FROM char_recipe_list "
|
tr.trivial,
|
||||||
"WHERE char_id = %u) AS crl ON tr.id=crl.recipe_id "
|
SUM(tre.componentcount),
|
||||||
"WHERE tr.enabled <> 0 AND tr.id IN (%s) "
|
tr.tradeskill
|
||||||
"AND tr.must_learn & 0x20 <> 0x20 AND "
|
FROM
|
||||||
"((tr.must_learn & 0x3 <> 0 AND crl.madecount IS NOT NULL) "
|
tradeskill_recipe AS tr
|
||||||
"OR (tr.must_learn & 0x3 = 0)) "
|
LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id = tre.recipe_id
|
||||||
"GROUP BY tr.id "
|
WHERE
|
||||||
"HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 AND SUM(tre.componentcount) <= %u "
|
tr.enabled <> 0
|
||||||
"LIMIT 100 ", CharacterID(), favoriteIDs.c_str(), containers.c_str(), combineObjectSlots);
|
AND tr.id IN (%s)
|
||||||
|
AND tr.must_learn & 0x20 <> 0x20
|
||||||
|
AND (
|
||||||
|
(
|
||||||
|
tr.must_learn & 0x3 <> 0
|
||||||
|
)
|
||||||
|
OR (tr.must_learn & 0x3 = 0)
|
||||||
|
)
|
||||||
|
GROUP BY
|
||||||
|
tr.id
|
||||||
|
HAVING
|
||||||
|
sum(
|
||||||
|
if(
|
||||||
|
tre.item_id %s
|
||||||
|
AND tre.iscontainer > 0,
|
||||||
|
1,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
) > 0
|
||||||
|
AND SUM(tre.componentcount) <= %u
|
||||||
|
LIMIT
|
||||||
|
100
|
||||||
|
),
|
||||||
|
favoriteIDs.c_str(),
|
||||||
|
containers.c_str(),
|
||||||
|
combineObjectSlots
|
||||||
|
);
|
||||||
|
|
||||||
TradeskillSearchResults(query, tsf->object_type, tsf->some_id);
|
SendTradeskillSearchResults(query, tsf->object_type, tsf->some_id);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::Handle_OP_RecipesSearch(const EQApplicationPacket *app)
|
void Client::Handle_OP_RecipesSearch(const EQApplicationPacket *app)
|
||||||
{
|
{
|
||||||
if (app->size != sizeof(RecipesSearch_Struct)) {
|
if (app->size != sizeof(RecipesSearch_Struct)) {
|
||||||
LogError("Invalid size for RecipesSearch_Struct: Expected: [{}], Got: [{}]",
|
LogError(
|
||||||
sizeof(RecipesSearch_Struct), app->size);
|
"Invalid size for RecipesSearch_Struct: Expected: [{}], Got: [{}]",
|
||||||
|
sizeof(RecipesSearch_Struct),
|
||||||
|
app->size
|
||||||
|
);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
RecipesSearch_Struct* rss = (RecipesSearch_Struct*)app->pBuffer;
|
auto* p_recipes_search_struct = (RecipesSearch_Struct*)app->pBuffer;
|
||||||
rss->query[55] = '\0'; //just to be sure.
|
p_recipes_search_struct->query[55] = '\0'; //just to be sure.
|
||||||
|
|
||||||
|
LogTradeskills(
|
||||||
|
"[Handle_OP_RecipesSearch] Requested search recipes for object_type [{}] some_id [{}]",
|
||||||
|
p_recipes_search_struct->object_type,
|
||||||
|
p_recipes_search_struct->some_id
|
||||||
|
);
|
||||||
|
|
||||||
LogDebug("Requested search recipes for: [{}] - [{}]\n", rss->object_type, rss->some_id);
|
char containers_where_clause[30];
|
||||||
|
uint32 combine_object_slots;
|
||||||
// make where clause segment for container(s)
|
if (p_recipes_search_struct->some_id == 0) {
|
||||||
char containers[30];
|
|
||||||
uint32 combineObjectSlots;
|
|
||||||
if (rss->some_id == 0) {
|
|
||||||
// world combiner so no item number
|
// world combiner so no item number
|
||||||
snprintf(containers, 29, "= %u", rss->object_type);
|
snprintf(containers_where_clause, 29, "= %u", p_recipes_search_struct->object_type);
|
||||||
combineObjectSlots = 10;
|
combine_object_slots = 10;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// container in inventory
|
// container in inventory
|
||||||
snprintf(containers, 29, "in (%u,%u)", rss->object_type, rss->some_id);
|
snprintf(containers_where_clause, 29, "in (%u,%u)", p_recipes_search_struct->object_type, p_recipes_search_struct->some_id);
|
||||||
auto item = database.GetItem(rss->some_id);
|
auto item = database.GetItem(p_recipes_search_struct->some_id);
|
||||||
if (!item)
|
if (!item) {
|
||||||
{
|
LogError(
|
||||||
LogError("Invalid container ID: [{}]. GetItem returned null. Defaulting to BagSlots = 10.\n", rss->some_id);
|
"Invalid container ID: [{}]. GetItem returned null. Defaulting to BagSlots = 10.",
|
||||||
combineObjectSlots = 10;
|
p_recipes_search_struct->some_id
|
||||||
|
);
|
||||||
|
combine_object_slots = 10;
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
combine_object_slots = item->BagSlots;
|
||||||
combineObjectSlots = item->BagSlots;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string searchClause;
|
std::string search_clause;
|
||||||
|
if (p_recipes_search_struct->query[0] != 0) {
|
||||||
//omit the rlike clause if query is empty
|
|
||||||
if (rss->query[0] != 0) {
|
|
||||||
char buf[120]; //larger than 2X rss->query
|
char buf[120]; //larger than 2X rss->query
|
||||||
database.DoEscapeString(buf, rss->query, strlen(rss->query));
|
database.DoEscapeString(buf, p_recipes_search_struct->query, strlen(p_recipes_search_struct->query));
|
||||||
searchClause = StringFormat("name rlike '%s' AND", buf);
|
search_clause = StringFormat("name rlike '%s' AND", buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
//arbitrary limit of 200 recipes, makes sense to me.
|
//arbitrary limit of 200 recipes, makes sense to me.
|
||||||
const std::string query = StringFormat("SELECT tr.id, tr.name, tr.trivial, "
|
std::string query = fmt::format(
|
||||||
"SUM(tre.componentcount), crl.madecount,tr.tradeskill "
|
SQL(
|
||||||
"FROM tradeskill_recipe AS tr "
|
SELECT
|
||||||
"LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id = tre.recipe_id "
|
tr.id,
|
||||||
"LEFT JOIN (SELECT recipe_id, madecount "
|
tr.name,
|
||||||
"FROM char_recipe_list WHERE char_id = %u) AS crl ON tr.id=crl.recipe_id "
|
tr.trivial,
|
||||||
"WHERE %s tr.trivial >= %u AND tr.trivial <= %u AND tr.enabled <> 0 "
|
SUM(tre.componentcount),
|
||||||
"AND tr.must_learn & 0x20 <> 0x20 "
|
crl.madecount,
|
||||||
"AND ((tr.must_learn & 0x3 <> 0 "
|
tr.tradeskill
|
||||||
"AND crl.madecount IS NOT NULL) "
|
FROM
|
||||||
"OR (tr.must_learn & 0x3 = 0)) "
|
tradeskill_recipe
|
||||||
"GROUP BY tr.id "
|
AS tr
|
||||||
"HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 AND SUM(tre.componentcount) <= %u "
|
LEFT
|
||||||
"LIMIT 200 ",
|
JOIN
|
||||||
CharacterID(), searchClause.c_str(),
|
tradeskill_recipe_entries
|
||||||
rss->mintrivial, rss->maxtrivial, containers, combineObjectSlots);
|
AS
|
||||||
TradeskillSearchResults(query, rss->object_type, rss->some_id);
|
tre
|
||||||
return;
|
ON
|
||||||
|
tr.id = tre.recipe_id
|
||||||
|
LEFT JOIN(
|
||||||
|
SELECT
|
||||||
|
recipe_id,
|
||||||
|
madecount
|
||||||
|
FROM
|
||||||
|
char_recipe_list
|
||||||
|
WHERE
|
||||||
|
char_id = {}
|
||||||
|
) AS crl ON tr.id = crl.recipe_id
|
||||||
|
WHERE
|
||||||
|
{} tr.trivial >= {}
|
||||||
|
AND tr.trivial <= {}
|
||||||
|
AND tr.enabled <> 0
|
||||||
|
AND tr.must_learn & 0x20 <> 0x20
|
||||||
|
AND (
|
||||||
|
(
|
||||||
|
tr.must_learn & 0x3 <> 0
|
||||||
|
AND crl.madecount IS NOT NULL
|
||||||
|
)
|
||||||
|
OR (tr.must_learn & 0x3 = 0)
|
||||||
|
)
|
||||||
|
GROUP BY
|
||||||
|
tr.id
|
||||||
|
HAVING
|
||||||
|
sum(
|
||||||
|
if (
|
||||||
|
tre.item_id {}
|
||||||
|
AND tre.iscontainer > 0,
|
||||||
|
1,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
) > 0
|
||||||
|
AND SUM(tre.componentcount) <= {}
|
||||||
|
LIMIT
|
||||||
|
200
|
||||||
|
),
|
||||||
|
CharacterID(),
|
||||||
|
search_clause,
|
||||||
|
p_recipes_search_struct->mintrivial,
|
||||||
|
p_recipes_search_struct->maxtrivial,
|
||||||
|
containers_where_clause,
|
||||||
|
combine_object_slots
|
||||||
|
);
|
||||||
|
|
||||||
|
SendTradeskillSearchResults(query, p_recipes_search_struct->object_type, p_recipes_search_struct->some_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::Handle_OP_ReloadUI(const EQApplicationPacket *app)
|
void Client::Handle_OP_ReloadUI(const EQApplicationPacket *app)
|
||||||
|
|||||||
@ -33,6 +33,8 @@
|
|||||||
#include "string_ids.h"
|
#include "string_ids.h"
|
||||||
#include "titles.h"
|
#include "titles.h"
|
||||||
#include "zonedb.h"
|
#include "zonedb.h"
|
||||||
|
#include "../common/repositories/character_recipe_list_repository.h"
|
||||||
|
#include "../common/repositories/tradeskill_recipe_repository.h"
|
||||||
|
|
||||||
extern QueryServ* QServ;
|
extern QueryServ* QServ;
|
||||||
|
|
||||||
@ -467,7 +469,7 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac
|
|||||||
|
|
||||||
//ask the database for the recipe to make sure it exists...
|
//ask the database for the recipe to make sure it exists...
|
||||||
DBTradeskillRecipe_Struct spec;
|
DBTradeskillRecipe_Struct spec;
|
||||||
if (!database.GetTradeRecipe(rac->recipe_id, rac->object_type, rac->some_id, user->CharacterID(), &spec)) {
|
if (!content_db.GetTradeRecipe(rac->recipe_id, rac->object_type, rac->some_id, user->CharacterID(), &spec)) {
|
||||||
LogError("Unknown recipe for HandleAutoCombine: [{}]\n", rac->recipe_id);
|
LogError("Unknown recipe for HandleAutoCombine: [{}]\n", rac->recipe_id);
|
||||||
user->QueuePacket(outapp);
|
user->QueuePacket(outapp);
|
||||||
safe_delete(outapp);
|
safe_delete(outapp);
|
||||||
@ -704,56 +706,69 @@ EQEmu::skills::SkillType Object::TypeToSkill(uint32 type)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::TradeskillSearchResults(const std::string &query, unsigned long objtype, unsigned long someid) {
|
void Client::SendTradeskillSearchResults(
|
||||||
|
const std::string &query,
|
||||||
auto results = content_db.QueryDatabase(query);
|
unsigned long objtype,
|
||||||
|
unsigned long someid
|
||||||
|
)
|
||||||
|
{
|
||||||
|
auto results = content_db.QueryDatabase(query);
|
||||||
if (!results.Success()) {
|
if (!results.Success()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(results.RowCount() < 1)
|
if (results.RowCount() < 1) {
|
||||||
return; //search gave no results... not an error
|
|
||||||
|
|
||||||
if(results.ColumnCount() != 6) {
|
|
||||||
LogError("Error in TradeskillSearchResults query [{}]: Invalid column count in result", query.c_str());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto row = results.begin(); row != results.end(); ++row) {
|
auto character_learned_recipe_list = CharacterRecipeListRepository::GetLearnedRecipeList(CharacterID());
|
||||||
if(row == nullptr || row[0] == nullptr || row[1] == nullptr || row[2] == nullptr || row[3] == nullptr || row[5] == nullptr)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
uint32 recipe = (uint32)atoi(row[0]);
|
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||||
const char *name = row[1];
|
if (row == nullptr || row[0] == nullptr || row[1] == nullptr || row[2] == nullptr || row[3] == nullptr ||
|
||||||
uint32 trivial = (uint32) atoi(row[2]);
|
row[5] == nullptr) {
|
||||||
uint32 comp_count = (uint32) atoi(row[3]);
|
continue;
|
||||||
uint32 tradeskill = (uint16) atoi(row[5]);
|
}
|
||||||
|
|
||||||
|
uint32 recipe_id = (uint32) atoi(row[0]);
|
||||||
|
const char *name = row[1];
|
||||||
|
uint32 trivial = (uint32) atoi(row[2]);
|
||||||
|
uint32 comp_count = (uint32) atoi(row[3]);
|
||||||
|
uint32 tradeskill = (uint16) atoi(row[5]);
|
||||||
|
|
||||||
// Skip the recipes that exceed the threshold in skill difference
|
// Skip the recipes that exceed the threshold in skill difference
|
||||||
// Recipes that have either been made before or were
|
// Recipes that have either been made before or were
|
||||||
// explicitly learned are excempt from that limit
|
// explicitly learned are excempt from that limit
|
||||||
if (RuleB(Skills, UseLimitTradeskillSearchSkillDiff)
|
if (RuleB(Skills, UseLimitTradeskillSearchSkillDiff) &&
|
||||||
&& ((int32)trivial - (int32)GetSkill((EQEmu::skills::SkillType)tradeskill)) > RuleI(Skills, MaxTradeskillSearchSkillDiff)
|
((int32) trivial - (int32) GetSkill((EQEmu::skills::SkillType) tradeskill)) >
|
||||||
&& row[4] == nullptr)
|
RuleI(Skills, MaxTradeskillSearchSkillDiff)) {
|
||||||
continue;
|
|
||||||
|
|
||||||
auto outapp = new EQApplicationPacket(OP_RecipeReply, sizeof(RecipeReply_Struct));
|
LogTradeskills("Checking limit recipe_id [{}] name [{}]", recipe_id, name);
|
||||||
|
|
||||||
|
auto character_learned_recipe = CharacterRecipeListRepository::GetRecipe(
|
||||||
|
character_learned_recipe_list,
|
||||||
|
recipe_id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (character_learned_recipe.made_count == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto outapp = new EQApplicationPacket(OP_RecipeReply, sizeof(RecipeReply_Struct));
|
||||||
RecipeReply_Struct *reply = (RecipeReply_Struct *) outapp->pBuffer;
|
RecipeReply_Struct *reply = (RecipeReply_Struct *) outapp->pBuffer;
|
||||||
|
|
||||||
reply->object_type = objtype;
|
reply->object_type = objtype;
|
||||||
reply->some_id = someid;
|
reply->some_id = someid;
|
||||||
reply->component_count = comp_count;
|
reply->component_count = comp_count;
|
||||||
reply->recipe_id = recipe;
|
reply->recipe_id = recipe_id;
|
||||||
reply->trivial = trivial;
|
reply->trivial = trivial;
|
||||||
strn0cpy(reply->recipe_name, name, sizeof(reply->recipe_name));
|
strn0cpy(reply->recipe_name, name, sizeof(reply->recipe_name));
|
||||||
FastQueuePacket(&outapp);
|
FastQueuePacket(&outapp);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::SendTradeskillDetails(uint32 recipe_id) {
|
void Client::SendTradeskillDetails(uint32 recipe_id) {
|
||||||
|
|
||||||
//pull the list of components
|
|
||||||
std::string query = StringFormat("SELECT tre.item_id,tre.componentcount,i.icon,i.Name "
|
std::string query = StringFormat("SELECT tre.item_id,tre.componentcount,i.icon,i.Name "
|
||||||
"FROM tradeskill_recipe_entries AS tre "
|
"FROM tradeskill_recipe_entries AS tre "
|
||||||
"LEFT JOIN items AS i ON tre.item_id = i.id "
|
"LEFT JOIN items AS i ON tre.item_id = i.id "
|
||||||
@ -1124,9 +1139,9 @@ void Client::CheckIncreaseTradeskill(int16 bonusstat, int16 stat_modifier, float
|
|||||||
NotifyNewTitlesAvailable();
|
NotifyNewTitlesAvailable();
|
||||||
}
|
}
|
||||||
|
|
||||||
LogTradeskills("skillup_modifier: [{}] , success_modifier: [{}] , stat modifier: [{}]", skillup_modifier , success_modifier , stat_modifier);
|
LogTradeskills("[CheckIncreaseTradeskill] skillup_modifier: [{}] , success_modifier: [{}] , stat modifier: [{}]", skillup_modifier , success_modifier , stat_modifier);
|
||||||
LogTradeskills("Stage1 chance was: [{}] percent", chance_stage1);
|
LogTradeskills("[CheckIncreaseTradeskill] Stage1 chance was: [{}] percent", chance_stage1);
|
||||||
LogTradeskills("Stage2 chance was: [{}] percent. 0 percent means stage1 failed", chance_stage2);
|
LogTradeskills("[CheckIncreaseTradeskill] Stage2 chance was: [{}] percent. 0 percent means stage1 failed", chance_stage2);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ZoneDatabase::GetTradeRecipe(const EQEmu::ItemInstance* container, uint8 c_type, uint32 some_id,
|
bool ZoneDatabase::GetTradeRecipe(const EQEmu::ItemInstance* container, uint8 c_type, uint32 some_id,
|
||||||
@ -1305,29 +1320,51 @@ bool ZoneDatabase::GetTradeRecipe(const EQEmu::ItemInstance* container, uint8 c_
|
|||||||
return GetTradeRecipe(recipe_id, c_type, some_id, char_id, spec);
|
return GetTradeRecipe(recipe_id, c_type, some_id, char_id, spec);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ZoneDatabase::GetTradeRecipe(uint32 recipe_id, uint8 c_type, uint32 some_id,
|
bool ZoneDatabase::GetTradeRecipe(
|
||||||
uint32 char_id, DBTradeskillRecipe_Struct *spec)
|
uint32 recipe_id,
|
||||||
|
uint8 c_type,
|
||||||
|
uint32 some_id,
|
||||||
|
uint32 char_id,
|
||||||
|
DBTradeskillRecipe_Struct *spec
|
||||||
|
)
|
||||||
{
|
{
|
||||||
|
|
||||||
// make where clause segment for container(s)
|
std::string container_where_filter;
|
||||||
std::string containers;
|
if (some_id == 0) {
|
||||||
if (some_id == 0)
|
// world combiner so no item number
|
||||||
containers = StringFormat("= %u", c_type); // world combiner so no item number
|
container_where_filter = StringFormat("= %u", c_type);
|
||||||
else
|
}
|
||||||
containers = StringFormat("IN (%u,%u)", c_type, some_id); // container in inventory
|
else {
|
||||||
|
// container in inventory
|
||||||
|
container_where_filter = StringFormat("IN (%u,%u)", c_type, some_id);
|
||||||
|
}
|
||||||
|
|
||||||
std::string query = StringFormat("SELECT tr.id, tr.tradeskill, tr.skillneeded, "
|
std::string query = StringFormat(
|
||||||
"tr.trivial, tr.nofail, tr.replace_container, "
|
SQL (
|
||||||
"tr.name, tr.must_learn, tr.quest, crl.madecount "
|
SELECT
|
||||||
"FROM tradeskill_recipe AS tr "
|
tradeskill_recipe.id,
|
||||||
"INNER JOIN tradeskill_recipe_entries AS tre "
|
tradeskill_recipe.tradeskill,
|
||||||
"ON tr.id = tre.recipe_id "
|
tradeskill_recipe.skillneeded,
|
||||||
"LEFT JOIN (SELECT recipe_id, madecount "
|
tradeskill_recipe.trivial,
|
||||||
"FROM char_recipe_list WHERE char_id = %u) AS crl "
|
tradeskill_recipe.nofail,
|
||||||
"ON tr.id = crl.recipe_id "
|
tradeskill_recipe.replace_container,
|
||||||
"WHERE tr.id = %lu AND tre.item_id %s AND tr.enabled "
|
tradeskill_recipe.name,
|
||||||
"GROUP BY tr.id",
|
tradeskill_recipe.must_learn,
|
||||||
char_id, (unsigned long)recipe_id, containers.c_str());
|
tradeskill_recipe.quest
|
||||||
|
FROM
|
||||||
|
tradeskill_recipe
|
||||||
|
INNER JOIN tradeskill_recipe_entries ON tradeskill_recipe.id = tradeskill_recipe_entries.recipe_id
|
||||||
|
WHERE
|
||||||
|
tradeskill_recipe.id = %lu
|
||||||
|
AND tradeskill_recipe_entries.item_id %s
|
||||||
|
AND tradeskill_recipe.enabled
|
||||||
|
GROUP BY
|
||||||
|
tradeskill_recipe.id
|
||||||
|
)
|
||||||
|
,
|
||||||
|
(unsigned long) recipe_id,
|
||||||
|
container_where_filter.c_str()
|
||||||
|
);
|
||||||
auto results = QueryDatabase(query);
|
auto results = QueryDatabase(query);
|
||||||
if (!results.Success()) {
|
if (!results.Success()) {
|
||||||
LogError("Error in GetTradeRecipe, query: [{}]", query.c_str());
|
LogError("Error in GetTradeRecipe, query: [{}]", query.c_str());
|
||||||
@ -1335,27 +1372,36 @@ bool ZoneDatabase::GetTradeRecipe(uint32 recipe_id, uint8 c_type, uint32 some_id
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(results.RowCount() != 1)
|
if (results.RowCount() != 1) {
|
||||||
return false;//just not found i guess..
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
auto row = results.begin();
|
auto row = results.begin();
|
||||||
spec->tradeskill = (EQEmu::skills::SkillType)atoi(row[1]);
|
|
||||||
spec->skill_needed = (int16)atoi(row[2]);
|
|
||||||
spec->trivial = (uint16)atoi(row[3]);
|
|
||||||
spec->nofail = atoi(row[4]) ? true : false;
|
|
||||||
spec->replace_container = atoi(row[5]) ? true : false;
|
|
||||||
spec->name = row[6];
|
|
||||||
spec->must_learn = (uint8)atoi(row[7]);
|
|
||||||
spec->quest = atoi(row[8]) ? true : false;
|
|
||||||
|
|
||||||
if (row[9] == nullptr) {
|
spec->tradeskill = (EQEmu::skills::SkillType) atoi(row[1]);
|
||||||
spec->has_learnt = false;
|
spec->skill_needed = (int16) atoi(row[2]);
|
||||||
spec->madecount = 0;
|
spec->trivial = (uint16) atoi(row[3]);
|
||||||
} else {
|
spec->nofail = atoi(row[4]) ? true : false;
|
||||||
|
spec->replace_container = atoi(row[5]) ? true : false;
|
||||||
|
spec->name = row[6];
|
||||||
|
spec->must_learn = (uint8) atoi(row[7]);
|
||||||
|
spec->quest = atoi(row[8]) ? true : false;
|
||||||
|
spec->has_learnt = false;
|
||||||
|
spec->madecount = 0;
|
||||||
|
spec->recipe_id = recipe_id;
|
||||||
|
|
||||||
|
auto character_learned_recipe_list = CharacterRecipeListRepository::GetLearnedRecipeList(char_id);
|
||||||
|
auto character_learned_recipe = CharacterRecipeListRepository::GetRecipe(
|
||||||
|
character_learned_recipe_list,
|
||||||
|
recipe_id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (character_learned_recipe.made_count > 0) {
|
||||||
|
LogTradeskills("[GetTradeRecipe] made_count [{}]", character_learned_recipe.made_count);
|
||||||
|
|
||||||
spec->has_learnt = true;
|
spec->has_learnt = true;
|
||||||
spec->madecount = (uint32)atoul(row[9]);
|
spec->madecount = (uint32)character_learned_recipe.made_count;
|
||||||
}
|
}
|
||||||
spec->recipe_id = recipe_id;
|
|
||||||
|
|
||||||
//Pull the on-success items...
|
//Pull the on-success items...
|
||||||
query = StringFormat("SELECT item_id,successcount FROM tradeskill_recipe_entries "
|
query = StringFormat("SELECT item_id,successcount FROM tradeskill_recipe_entries "
|
||||||
@ -1379,33 +1425,41 @@ bool ZoneDatabase::GetTradeRecipe(uint32 recipe_id, uint8 c_type, uint32 some_id
|
|||||||
|
|
||||||
spec->onfail.clear();
|
spec->onfail.clear();
|
||||||
//Pull the on-fail items...
|
//Pull the on-fail items...
|
||||||
query = StringFormat("SELECT item_id, failcount FROM tradeskill_recipe_entries "
|
query = StringFormat(
|
||||||
"WHERE failcount > 0 AND recipe_id = %u", recipe_id);
|
"SELECT item_id, failcount FROM tradeskill_recipe_entries "
|
||||||
|
"WHERE failcount > 0 AND recipe_id = %u", recipe_id
|
||||||
|
);
|
||||||
results = QueryDatabase(query);
|
results = QueryDatabase(query);
|
||||||
if (results.Success())
|
if (results.Success()) {
|
||||||
for(auto row = results.begin(); row != results.end(); ++row) {
|
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||||
uint32 item = (uint32)atoi(row[0]);
|
uint32 item = (uint32) atoi(row[0]);
|
||||||
uint8 num = (uint8) atoi(row[1]);
|
uint8 num = (uint8) atoi(row[1]);
|
||||||
spec->onfail.push_back(std::pair<uint32,uint8>(item, num));
|
spec->onfail.push_back(std::pair<uint32, uint8>(item, num));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
spec->salvage.clear();
|
spec->salvage.clear();
|
||||||
|
|
||||||
// Don't bother with the query if TS is nofail
|
// Don't bother with the query if TS is nofail
|
||||||
if (spec->nofail)
|
if (spec->nofail) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Pull the salvage list
|
// Pull the salvage list
|
||||||
query = StringFormat("SELECT item_id, salvagecount "
|
query = StringFormat(
|
||||||
"FROM tradeskill_recipe_entries "
|
"SELECT item_id, salvagecount "
|
||||||
"WHERE salvagecount > 0 AND recipe_id = %u", recipe_id);
|
"FROM tradeskill_recipe_entries "
|
||||||
results = QueryDatabase(query);
|
"WHERE salvagecount > 0 AND recipe_id = %u", recipe_id
|
||||||
if (results.Success())
|
);
|
||||||
for(auto row = results.begin(); row != results.begin(); ++row) {
|
|
||||||
uint32 item = (uint32)atoi(row[0]);
|
results = QueryDatabase(query);
|
||||||
uint8 num = (uint8)atoi(row[1]);
|
if (results.Success()) {
|
||||||
spec->salvage.push_back(std::pair<uint32,uint8>(item, num));
|
for (auto row = results.begin(); row != results.begin(); ++row) {
|
||||||
|
uint32 item = (uint32) atoi(row[0]);
|
||||||
|
uint8 num = (uint8) atoi(row[1]);
|
||||||
|
spec->salvage.push_back(std::pair<uint32, uint8>(item, num));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1419,43 +1473,57 @@ void ZoneDatabase::UpdateRecipeMadecount(uint32 recipe_id, uint32 char_id, uint3
|
|||||||
QueryDatabase(query);
|
QueryDatabase(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::LearnRecipe(uint32 recipeID)
|
void Client::LearnRecipe(uint32 recipe_id)
|
||||||
{
|
{
|
||||||
std::string query = StringFormat("SELECT tr.name, crl.madecount "
|
std::string query = fmt::format(
|
||||||
"FROM tradeskill_recipe AS tr "
|
SQL(
|
||||||
"LEFT JOIN (SELECT recipe_id, madecount "
|
select
|
||||||
"FROM char_recipe_list WHERE char_id = %u) AS crl "
|
char_id,
|
||||||
"ON tr.id = crl.recipe_id "
|
recipe_id,
|
||||||
"WHERE tr.id = %u ;", CharacterID(), recipeID);
|
madecount
|
||||||
|
from
|
||||||
// TODO: BOUNDARY REWRITE
|
char_recipe_list
|
||||||
|
where
|
||||||
|
char_id = {}
|
||||||
|
and recipe_id = {}
|
||||||
|
LIMIT 1
|
||||||
|
),
|
||||||
|
CharacterID(),
|
||||||
|
recipe_id
|
||||||
|
);
|
||||||
|
|
||||||
auto results = database.QueryDatabase(query);
|
auto results = database.QueryDatabase(query);
|
||||||
if (!results.Success()) {
|
if (!results.Success()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (results.RowCount() != 1) {
|
auto tradeskill_recipe = TradeskillRecipeRepository::GetRecipe(recipe_id);
|
||||||
LogInfo("Client::LearnRecipe - RecipeID: [{}] had [{}] occurences", recipeID, results.RowCount());
|
if (tradeskill_recipe.id == 0) {
|
||||||
|
LogError("Invalid recipe [{}]", recipe_id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LogTradeskills(
|
||||||
|
"[LearnRecipe] recipe_id [{}] name [{}] learned [{}]",
|
||||||
|
recipe_id,
|
||||||
|
tradeskill_recipe.name,
|
||||||
|
results.RowCount()
|
||||||
|
);
|
||||||
|
|
||||||
auto row = results.begin();
|
auto row = results.begin();
|
||||||
|
if (results.RowCount() > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (row[0] == nullptr)
|
MessageString(Chat::LightBlue, TRADESKILL_LEARN_RECIPE, tradeskill_recipe.name.c_str());
|
||||||
return;
|
|
||||||
|
|
||||||
// Only give Learn message if character doesn't know the recipe
|
database.QueryDatabase(
|
||||||
if (row[1] != nullptr)
|
fmt::format(
|
||||||
return;
|
"REPLACE INTO char_recipe_list (recipe_id, char_id, madecount) VALUES ({}, {}, 0)",
|
||||||
|
recipe_id,
|
||||||
MessageString(Chat::LightBlue, TRADESKILL_LEARN_RECIPE, row[0]);
|
CharacterID()
|
||||||
// Actually learn the recipe now
|
)
|
||||||
query = StringFormat("INSERT INTO char_recipe_list "
|
);
|
||||||
"SET recipe_id = %u, char_id = %u, madecount = 0 "
|
|
||||||
"ON DUPLICATE KEY UPDATE madecount = madecount;",
|
|
||||||
recipeID, CharacterID());
|
|
||||||
results = database.QueryDatabase(query);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Client::CanIncreaseTradeskill(EQEmu::skills::SkillType tradeskill) {
|
bool Client::CanIncreaseTradeskill(EQEmu::skills::SkillType tradeskill) {
|
||||||
|
|||||||
501
zone/trading.cpp
501
zone/trading.cpp
@ -1755,286 +1755,315 @@ void Client::SendBazaarWelcome()
|
|||||||
"or use /buyer to set up your own Buy Lines.", atoi(row[0]));
|
"or use /buyer to set up your own Buy Lines.", atoi(row[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::SendBazaarResults(uint32 TraderID, uint32 Class_, uint32 Race, uint32 ItemStat, uint32 Slot, uint32 Type,
|
void Client::SendBazaarResults(
|
||||||
char Name[64], uint32 MinPrice, uint32 MaxPrice) {
|
uint32 trader_id,
|
||||||
|
uint32 in_class,
|
||||||
|
uint32 in_race,
|
||||||
|
uint32 item_stat,
|
||||||
|
uint32 item_slot,
|
||||||
|
uint32 item_type,
|
||||||
|
char item_name[64],
|
||||||
|
uint32 min_price,
|
||||||
|
uint32 max_price
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::string search_values = " COUNT(item_id), trader.*, items.name ";
|
||||||
|
std::string search_criteria = " WHERE trader.item_id = items.id ";
|
||||||
|
|
||||||
std::string searchValues = " COUNT(item_id), trader.*, items.name ";
|
if (trader_id > 0) {
|
||||||
std::string searchCriteria = " WHERE trader.item_id = items.id ";
|
Client *trader = entity_list.GetClientByID(trader_id);
|
||||||
|
|
||||||
if(TraderID > 0) {
|
if (trader) {
|
||||||
Client* trader = entity_list.GetClientByID(TraderID);
|
search_criteria.append(StringFormat(" AND trader.char_id = %i", trader->CharacterID()));
|
||||||
|
}
|
||||||
if(trader)
|
|
||||||
searchCriteria.append(StringFormat(" AND trader.char_id = %i", trader->CharacterID()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(MinPrice != 0)
|
if (min_price != 0) {
|
||||||
searchCriteria.append(StringFormat(" AND trader.item_cost >= %i", MinPrice));
|
search_criteria.append(StringFormat(" AND trader.item_cost >= %i", min_price));
|
||||||
|
}
|
||||||
|
|
||||||
if(MaxPrice != 0)
|
if (max_price != 0) {
|
||||||
searchCriteria.append(StringFormat(" AND trader.item_cost <= %i", MaxPrice));
|
search_criteria.append(StringFormat(" AND trader.item_cost <= %i", max_price));
|
||||||
|
}
|
||||||
|
|
||||||
if(strlen(Name) > 0) {
|
if (strlen(item_name) > 0) {
|
||||||
char *safeName = RemoveApostrophes(Name);
|
char *safeName = RemoveApostrophes(item_name);
|
||||||
searchCriteria.append(StringFormat(" AND items.name LIKE '%%%s%%'", safeName));
|
search_criteria.append(StringFormat(" AND items.name LIKE '%%%s%%'", safeName));
|
||||||
safe_delete_array(safeName);
|
safe_delete_array(safeName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Class_ != 0xFFFFFFFF)
|
if (in_class != 0xFFFFFFFF) {
|
||||||
searchCriteria.append(StringFormat(" AND MID(REVERSE(BIN(items.classes)), %i, 1) = 1", Class_));
|
search_criteria.append(StringFormat(" AND MID(REVERSE(BIN(items.classes)), %i, 1) = 1", in_class));
|
||||||
|
}
|
||||||
|
|
||||||
if(Race != 0xFFFFFFFF)
|
if (in_race != 0xFFFFFFFF) {
|
||||||
searchCriteria.append(StringFormat(" AND MID(REVERSE(BIN(items.races)), %i, 1) = 1", Race));
|
search_criteria.append(StringFormat(" AND MID(REVERSE(BIN(items.races)), %i, 1) = 1", in_race));
|
||||||
|
}
|
||||||
|
|
||||||
if(Slot != 0xFFFFFFFF)
|
if (item_slot != 0xFFFFFFFF) {
|
||||||
searchCriteria.append(StringFormat(" AND MID(REVERSE(BIN(items.slots)), %i, 1) = 1", Slot + 1));
|
search_criteria.append(StringFormat(" AND MID(REVERSE(BIN(items.slots)), %i, 1) = 1", item_slot + 1));
|
||||||
|
}
|
||||||
|
|
||||||
switch(Type){
|
switch (item_type) {
|
||||||
case 0xFFFFFFFF:
|
case 0xFFFFFFFF:
|
||||||
break;
|
|
||||||
case 0:
|
|
||||||
// 1H Slashing
|
|
||||||
searchCriteria.append(" AND items.itemtype = 0 AND damage > 0");
|
|
||||||
break;
|
|
||||||
case 31:
|
|
||||||
searchCriteria.append(" AND items.itemclass = 2");
|
|
||||||
break;
|
|
||||||
case 46:
|
|
||||||
searchCriteria.append(" AND items.spellid > 0 AND items.spellid < 65000");
|
|
||||||
break;
|
|
||||||
case 47:
|
|
||||||
searchCriteria.append(" AND items.spellid = 998");
|
|
||||||
break;
|
|
||||||
case 48:
|
|
||||||
searchCriteria.append(" AND items.spellid >= 1298 AND items.spellid <= 1307");
|
|
||||||
break;
|
|
||||||
case 49:
|
|
||||||
searchCriteria.append(" AND items.focuseffect > 0");
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
searchCriteria.append(StringFormat(" AND items.itemtype = %i", Type));
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(ItemStat) {
|
|
||||||
|
|
||||||
case STAT_AC:
|
|
||||||
searchCriteria.append(" AND items.ac > 0");
|
|
||||||
searchValues.append(", items.ac");
|
|
||||||
break;
|
break;
|
||||||
|
case 0:
|
||||||
case STAT_AGI:
|
// 1H Slashing
|
||||||
searchCriteria.append(" AND items.aagi > 0");
|
search_criteria.append(" AND items.itemtype = 0 AND damage > 0");
|
||||||
searchValues.append(", items.aagi");
|
|
||||||
break;
|
break;
|
||||||
|
case 31:
|
||||||
case STAT_CHA:
|
search_criteria.append(" AND items.itemclass = 2");
|
||||||
searchCriteria.append(" AND items.acha > 0");
|
|
||||||
searchValues.append(", items.acha");
|
|
||||||
break;
|
break;
|
||||||
|
case 46:
|
||||||
case STAT_DEX:
|
search_criteria.append(" AND items.spellid > 0 AND items.spellid < 65000");
|
||||||
searchCriteria.append(" AND items.adex > 0");
|
|
||||||
searchValues.append(", items.adex");
|
|
||||||
break;
|
break;
|
||||||
|
case 47:
|
||||||
case STAT_INT:
|
search_criteria.append(" AND items.spellid = 998");
|
||||||
searchCriteria.append(" AND items.aint > 0");
|
|
||||||
searchValues.append(", items.aint");
|
|
||||||
break;
|
break;
|
||||||
|
case 48:
|
||||||
case STAT_STA:
|
search_criteria.append(" AND items.spellid >= 1298 AND items.spellid <= 1307");
|
||||||
searchCriteria.append(" AND items.asta > 0");
|
|
||||||
searchValues.append(", items.asta");
|
|
||||||
break;
|
break;
|
||||||
|
case 49:
|
||||||
case STAT_STR:
|
search_criteria.append(" AND items.focuseffect > 0");
|
||||||
searchCriteria.append(" AND items.astr > 0");
|
|
||||||
searchValues.append(", items.astr");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_WIS:
|
|
||||||
searchCriteria.append(" AND items.awis > 0");
|
|
||||||
searchValues.append(", items.awis");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_COLD:
|
|
||||||
searchCriteria.append(" AND items.cr > 0");
|
|
||||||
searchValues.append(", items.cr");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_DISEASE:
|
|
||||||
searchCriteria.append(" AND items.dr > 0");
|
|
||||||
searchValues.append(", items.dr");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_FIRE:
|
|
||||||
searchCriteria.append(" AND items.fr > 0");
|
|
||||||
searchValues.append(", items.fr");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_MAGIC:
|
|
||||||
searchCriteria.append(" AND items.mr > 0");
|
|
||||||
searchValues.append(", items.mr");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_POISON:
|
|
||||||
searchCriteria.append(" AND items.pr > 0");
|
|
||||||
searchValues.append(", items.pr");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_HP:
|
|
||||||
searchCriteria.append(" AND items.hp > 0");
|
|
||||||
searchValues.append(", items.hp");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_MANA:
|
|
||||||
searchCriteria.append(" AND items.mana > 0");
|
|
||||||
searchValues.append(", items.mana");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_ENDURANCE:
|
|
||||||
searchCriteria.append(" AND items.endur > 0");
|
|
||||||
searchValues.append(", items.endur");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_ATTACK:
|
|
||||||
searchCriteria.append(" AND items.attack > 0");
|
|
||||||
searchValues.append(", items.attack");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_HP_REGEN:
|
|
||||||
searchCriteria.append(" AND items.regen > 0");
|
|
||||||
searchValues.append(", items.regen");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_MANA_REGEN:
|
|
||||||
searchCriteria.append(" AND items.manaregen > 0");
|
|
||||||
searchValues.append(", items.manaregen");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_HASTE:
|
|
||||||
searchCriteria.append(" AND items.haste > 0");
|
|
||||||
searchValues.append(", items.haste");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STAT_DAMAGE_SHIELD:
|
|
||||||
searchCriteria.append(" AND items.damageshield > 0");
|
|
||||||
searchValues.append(", items.damageshield");
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
searchValues.append(", 0");
|
search_criteria.append(StringFormat(" AND items.itemtype = %i", item_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (item_stat) {
|
||||||
|
|
||||||
|
case STAT_AC:
|
||||||
|
search_criteria.append(" AND items.ac > 0");
|
||||||
|
search_values.append(", items.ac");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STAT_AGI:
|
||||||
|
search_criteria.append(" AND items.aagi > 0");
|
||||||
|
search_values.append(", items.aagi");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STAT_CHA:
|
||||||
|
search_criteria.append(" AND items.acha > 0");
|
||||||
|
search_values.append(", items.acha");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STAT_DEX:
|
||||||
|
search_criteria.append(" AND items.adex > 0");
|
||||||
|
search_values.append(", items.adex");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STAT_INT:
|
||||||
|
search_criteria.append(" AND items.aint > 0");
|
||||||
|
search_values.append(", items.aint");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STAT_STA:
|
||||||
|
search_criteria.append(" AND items.asta > 0");
|
||||||
|
search_values.append(", items.asta");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STAT_STR:
|
||||||
|
search_criteria.append(" AND items.astr > 0");
|
||||||
|
search_values.append(", items.astr");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STAT_WIS:
|
||||||
|
search_criteria.append(" AND items.awis > 0");
|
||||||
|
search_values.append(", items.awis");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STAT_COLD:
|
||||||
|
search_criteria.append(" AND items.cr > 0");
|
||||||
|
search_values.append(", items.cr");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STAT_DISEASE:
|
||||||
|
search_criteria.append(" AND items.dr > 0");
|
||||||
|
search_values.append(", items.dr");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STAT_FIRE:
|
||||||
|
search_criteria.append(" AND items.fr > 0");
|
||||||
|
search_values.append(", items.fr");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STAT_MAGIC:
|
||||||
|
search_criteria.append(" AND items.mr > 0");
|
||||||
|
search_values.append(", items.mr");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STAT_POISON:
|
||||||
|
search_criteria.append(" AND items.pr > 0");
|
||||||
|
search_values.append(", items.pr");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STAT_HP:
|
||||||
|
search_criteria.append(" AND items.hp > 0");
|
||||||
|
search_values.append(", items.hp");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STAT_MANA:
|
||||||
|
search_criteria.append(" AND items.mana > 0");
|
||||||
|
search_values.append(", items.mana");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STAT_ENDURANCE:
|
||||||
|
search_criteria.append(" AND items.endur > 0");
|
||||||
|
search_values.append(", items.endur");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STAT_ATTACK:
|
||||||
|
search_criteria.append(" AND items.attack > 0");
|
||||||
|
search_values.append(", items.attack");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STAT_HP_REGEN:
|
||||||
|
search_criteria.append(" AND items.regen > 0");
|
||||||
|
search_values.append(", items.regen");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STAT_MANA_REGEN:
|
||||||
|
search_criteria.append(" AND items.manaregen > 0");
|
||||||
|
search_values.append(", items.manaregen");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STAT_HASTE:
|
||||||
|
search_criteria.append(" AND items.haste > 0");
|
||||||
|
search_values.append(", items.haste");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STAT_DAMAGE_SHIELD:
|
||||||
|
search_criteria.append(" AND items.damageshield > 0");
|
||||||
|
search_values.append(", items.damageshield");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
search_values.append(", 0");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string query = StringFormat("SELECT %s, SUM(charges), items.stackable "
|
std::string query = StringFormat(
|
||||||
"FROM trader, items %s GROUP BY items.id, charges, char_id LIMIT %i",
|
"SELECT %s, SUM(charges), items.stackable "
|
||||||
searchValues.c_str(), searchCriteria.c_str(), RuleI(Bazaar, MaxSearchResults));
|
"FROM trader, items %s GROUP BY items.id, charges, char_id LIMIT %i",
|
||||||
auto results = database.QueryDatabase(query);
|
search_values.c_str(),
|
||||||
if (!results.Success()) {
|
search_criteria.c_str(),
|
||||||
|
RuleI(Bazaar, MaxSearchResults)
|
||||||
|
);
|
||||||
|
|
||||||
|
auto results = database.QueryDatabase(query);
|
||||||
|
|
||||||
|
if (!results.Success()) {
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
LogTrading("SRCH: [{}]", query.c_str());
|
|
||||||
|
|
||||||
int Size = 0;
|
|
||||||
uint32 ID = 0;
|
|
||||||
|
|
||||||
if (results.RowCount() == static_cast<unsigned long>(RuleI(Bazaar, MaxSearchResults)))
|
|
||||||
Message(Chat::Yellow, "Your search reached the limit of %i results. Please narrow your search down by selecting more options.",
|
|
||||||
RuleI(Bazaar, MaxSearchResults));
|
|
||||||
|
|
||||||
if(results.RowCount() == 0) {
|
|
||||||
auto outapp2 = new EQApplicationPacket(OP_BazaarSearch, sizeof(BazaarReturnDone_Struct));
|
|
||||||
BazaarReturnDone_Struct *brds = (BazaarReturnDone_Struct *)outapp2->pBuffer;
|
|
||||||
brds->TraderID = ID;
|
|
||||||
brds->Type = BazaarSearchDone;
|
|
||||||
brds->Unknown008 = 0xFFFFFFFF;
|
|
||||||
brds->Unknown012 = 0xFFFFFFFF;
|
|
||||||
brds->Unknown016 = 0xFFFFFFFF;
|
|
||||||
this->QueuePacket(outapp2);
|
|
||||||
safe_delete(outapp2);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Size = results.RowCount() * sizeof(BazaarSearchResults_Struct);
|
LogTrading("SRCH: [{}]", query.c_str());
|
||||||
auto buffer = new uchar[Size];
|
|
||||||
uchar *bufptr = buffer;
|
|
||||||
memset(buffer, 0, Size);
|
|
||||||
|
|
||||||
int Action = BazaarSearchResults;
|
int Size = 0;
|
||||||
uint32 Cost = 0;
|
uint32 ID = 0;
|
||||||
int32 SerialNumber = 0;
|
|
||||||
char temp_buffer[64] = {0};
|
|
||||||
int Count = 0;
|
|
||||||
uint32 StatValue = 0;
|
|
||||||
|
|
||||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
if (results.RowCount() == static_cast<unsigned long>(RuleI(Bazaar, MaxSearchResults))) {
|
||||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, Action);
|
Message(
|
||||||
Count = atoi(row[0]);
|
Chat::Yellow,
|
||||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, Count);
|
"Your search reached the limit of %i results. Please narrow your search down by selecting more options.",
|
||||||
SerialNumber = atoi(row[3]);
|
RuleI(Bazaar, MaxSearchResults));
|
||||||
VARSTRUCT_ENCODE_TYPE(int32, bufptr, SerialNumber);
|
}
|
||||||
Client *Trader2 = entity_list.GetClientByCharID(atoi(row[1]));
|
|
||||||
if (Trader2) {
|
|
||||||
ID = Trader2->GetID();
|
|
||||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, ID);
|
|
||||||
} else {
|
|
||||||
LogTrading("Unable to find trader: [{}]\n", atoi(row[1]));
|
|
||||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, 0);
|
|
||||||
}
|
|
||||||
Cost = atoi(row[5]);
|
|
||||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, Cost);
|
|
||||||
StatValue = atoi(row[8]);
|
|
||||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, StatValue);
|
|
||||||
bool Stackable = atoi(row[10]);
|
|
||||||
if (Stackable) {
|
|
||||||
int Charges = atoi(row[9]);
|
|
||||||
sprintf(temp_buffer, "%s(%i)", row[7], Charges);
|
|
||||||
} else
|
|
||||||
sprintf(temp_buffer, "%s(%i)", row[7], Count);
|
|
||||||
|
|
||||||
memcpy(bufptr, &temp_buffer, strlen(temp_buffer));
|
if (results.RowCount() == 0) {
|
||||||
|
auto outapp2 = new EQApplicationPacket(OP_BazaarSearch, sizeof(BazaarReturnDone_Struct));
|
||||||
|
BazaarReturnDone_Struct *brds = (BazaarReturnDone_Struct *) outapp2->pBuffer;
|
||||||
|
brds->TraderID = ID;
|
||||||
|
brds->Type = BazaarSearchDone;
|
||||||
|
brds->Unknown008 = 0xFFFFFFFF;
|
||||||
|
brds->Unknown012 = 0xFFFFFFFF;
|
||||||
|
brds->Unknown016 = 0xFFFFFFFF;
|
||||||
|
this->QueuePacket(outapp2);
|
||||||
|
safe_delete(outapp2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
bufptr += 64;
|
Size = results.RowCount() * sizeof(BazaarSearchResults_Struct);
|
||||||
|
auto buffer = new uchar[Size];
|
||||||
|
uchar *bufptr = buffer;
|
||||||
|
memset(buffer, 0, Size);
|
||||||
|
|
||||||
// Extra fields for SoD+
|
int Action = BazaarSearchResults;
|
||||||
//
|
uint32 Cost = 0;
|
||||||
if (Trader2)
|
int32 SerialNumber = 0;
|
||||||
sprintf(temp_buffer, "%s", Trader2->GetName());
|
char temp_buffer[64] = {0};
|
||||||
else
|
int Count = 0;
|
||||||
sprintf(temp_buffer, "Unknown");
|
uint32 StatValue = 0;
|
||||||
|
|
||||||
memcpy(bufptr, &temp_buffer, strlen(temp_buffer));
|
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, Action);
|
||||||
|
Count = atoi(row[0]);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, Count);
|
||||||
|
SerialNumber = atoi(row[3]);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(int32, bufptr, SerialNumber);
|
||||||
|
Client *Trader2 = entity_list.GetClientByCharID(atoi(row[1]));
|
||||||
|
if (Trader2) {
|
||||||
|
ID = Trader2->GetID();
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, ID);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LogTrading("Unable to find trader: [{}]\n", atoi(row[1]));
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, 0);
|
||||||
|
}
|
||||||
|
Cost = atoi(row[5]);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, Cost);
|
||||||
|
StatValue = atoi(row[8]);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, StatValue);
|
||||||
|
bool Stackable = atoi(row[10]);
|
||||||
|
if (Stackable) {
|
||||||
|
int Charges = atoi(row[9]);
|
||||||
|
sprintf(temp_buffer, "%s(%i)", row[7], Charges);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sprintf(temp_buffer, "%s(%i)", row[7], Count);
|
||||||
|
}
|
||||||
|
|
||||||
bufptr += 64;
|
memcpy(bufptr, &temp_buffer, strlen(temp_buffer));
|
||||||
|
|
||||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, atoi(row[1])); // ItemID
|
bufptr += 64;
|
||||||
}
|
|
||||||
|
|
||||||
auto outapp = new EQApplicationPacket(OP_BazaarSearch, Size);
|
// Extra fields for SoD+
|
||||||
|
//
|
||||||
|
if (Trader2) {
|
||||||
|
sprintf(temp_buffer, "%s", Trader2->GetName());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sprintf(temp_buffer, "Unknown");
|
||||||
|
}
|
||||||
|
|
||||||
memcpy(outapp->pBuffer, buffer, Size);
|
memcpy(bufptr, &temp_buffer, strlen(temp_buffer));
|
||||||
|
|
||||||
this->QueuePacket(outapp);
|
bufptr += 64;
|
||||||
|
|
||||||
safe_delete(outapp);
|
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, atoi(row[1])); // ItemID
|
||||||
safe_delete_array(buffer);
|
}
|
||||||
|
|
||||||
auto outapp2 = new EQApplicationPacket(OP_BazaarSearch, sizeof(BazaarReturnDone_Struct));
|
auto outapp = new EQApplicationPacket(OP_BazaarSearch, Size);
|
||||||
BazaarReturnDone_Struct *brds = (BazaarReturnDone_Struct *)outapp2->pBuffer;
|
|
||||||
|
|
||||||
brds->TraderID = ID;
|
memcpy(outapp->pBuffer, buffer, Size);
|
||||||
brds->Type = BazaarSearchDone;
|
|
||||||
|
|
||||||
brds->Unknown008 = 0xFFFFFFFF;
|
this->QueuePacket(outapp);
|
||||||
brds->Unknown012 = 0xFFFFFFFF;
|
|
||||||
brds->Unknown016 = 0xFFFFFFFF;
|
|
||||||
|
|
||||||
this->QueuePacket(outapp2);
|
safe_delete(outapp);
|
||||||
|
safe_delete_array(buffer);
|
||||||
|
|
||||||
safe_delete(outapp2);
|
auto outapp2 = new EQApplicationPacket(OP_BazaarSearch, sizeof(BazaarReturnDone_Struct));
|
||||||
|
BazaarReturnDone_Struct *brds = (BazaarReturnDone_Struct *) outapp2->pBuffer;
|
||||||
|
|
||||||
|
brds->TraderID = ID;
|
||||||
|
brds->Type = BazaarSearchDone;
|
||||||
|
|
||||||
|
brds->Unknown008 = 0xFFFFFFFF;
|
||||||
|
brds->Unknown012 = 0xFFFFFFFF;
|
||||||
|
brds->Unknown016 = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
this->QueuePacket(outapp2);
|
||||||
|
|
||||||
|
safe_delete(outapp2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void UpdateTraderCustomerItemsAdded(uint32 CustomerID, TraderCharges_Struct* gis, uint32 ItemID) {
|
static void UpdateTraderCustomerItemsAdded(uint32 CustomerID, TraderCharges_Struct* gis, uint32 ItemID) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user