Add item/equip creation/application and management to brokk

main
Alex 3 weeks ago
parent 1eb2fb17b0
commit 602f3edd67

@ -169,7 +169,7 @@ int brokk_mmo_auth_new(const char* userid, const char* pass, const char sex, con
// Check if the bot account already exists
if (bot_accounts->load_num(bot_accounts, &bot_acc, acc.account_id)) {
ShowWarning("Attempt to create an already existing bot account (account: %s, sex: %c). Skipping...\n", acc.account_id, sex);
ShowWarning("Attempt to create an already existing bot account (account: %u, sex: %c). Skipping...\n", acc.account_id, sex);
return 1; // Account exists
}
@ -530,7 +530,7 @@ void brokk_set_defaults() {
void bot_account_cleanup() {
ShowStatus("Start bot deletion according to settings...\n");
ShowStatus("Start bot deletion according to settings...\n\n");
struct mmo_bot_account bot_acc;
BotAccountDBIterator* iter_bots = bot_accounts->iterator(bot_accounts);
@ -944,7 +944,7 @@ bool BrokkServer::initialize(int argc, char* argv[]) {
// Close file after done with it
bot_accounts_file.close();
ShowInfo("Starting to create bot accounts...\n");
ShowInfo("Starting to create bot accounts...\n\n");
// Iterate over each bot entry in the JSON file
for (const auto& bot : json_bot_accounts) {
@ -1070,7 +1070,6 @@ bool BrokkServer::initialize(int argc, char* argv[]) {
// Prepare items inside a item bundle
ShowInfo("Preparing inventory data for bot character %s ...\n", char_name.c_str());
bot_item_bundle item_bundle{};
prepare_item_bundle(item_bundle, new_char_id, json_character);
@ -1089,7 +1088,26 @@ bool BrokkServer::initialize(int argc, char* argv[]) {
ShowStatus("%d Items successfully handed out to character (name: %s, char_id: %d, sex: %c)\n", item_bundle.items.size(), new_char.name, new_char.char_id, new_char.sex);
//Step5: Equipment
//ShowInfo("Preparing equipment for bot character %s ...\n", char_name.c_str());
ShowInfo("Preparing equipment for bot character %s ...\n", char_name.c_str());
bot_item_bundle equip_bundle{};
prepare_equipment_bundle(equip_bundle, new_char_id, json_character);
// All equip related data is collected and can now be saved
try {
if (!brokk_apply_item_loadout(equip_bundle, new_char_id)) {
ShowError("Failed to create inventory entry for: %s\n", new_char.name);
continue;
}
}
catch (const std::exception& e) {
ShowError("item creation for '%s 'failed: %s\n", new_char.name, e.what());
continue;
}
ShowStatus("%d Gear-items successfully equiped to character (name: %s, char_id: %d, sex: %c)\n", equip_bundle.items.size(), new_char.name, new_char.char_id, new_char.sex);
ShowStatus("Bot-Character creation and administration completed (name: %s, char_id: %d, sex: %c)\n\n", new_char.name, new_char.char_id, new_char.sex);

@ -230,304 +230,6 @@ void BotStatPointDatabase::loadingFinished() {
* Job Data Functions
*/
/*====================================================
* This function return the name of the job (by [Yor])
*----------------------------------------------------
const char* job_name(int class_)
{
switch (class_) {
case JOB_NOVICE:
case JOB_SWORDMAN:
case JOB_MAGE:
case JOB_ARCHER:
case JOB_ACOLYTE:
case JOB_MERCHANT:
case JOB_THIEF:
return msg_txt(nullptr, 550 - JOB_NOVICE + class_);
case JOB_KNIGHT:
case JOB_PRIEST:
case JOB_WIZARD:
case JOB_BLACKSMITH:
case JOB_HUNTER:
case JOB_ASSASSIN:
return msg_txt(nullptr, 557 - JOB_KNIGHT + class_);
case JOB_KNIGHT2:
return msg_txt(nullptr, 557);
case JOB_CRUSADER:
case JOB_MONK:
case JOB_SAGE:
case JOB_ROGUE:
case JOB_ALCHEMIST:
case JOB_BARD:
case JOB_DANCER:
return msg_txt(nullptr, 563 - JOB_CRUSADER + class_);
case JOB_CRUSADER2:
return msg_txt(nullptr, 563);
case JOB_WEDDING:
case JOB_SUPER_NOVICE:
case JOB_GUNSLINGER:
case JOB_NINJA:
case JOB_XMAS:
return msg_txt(nullptr, 570 - JOB_WEDDING + class_);
case JOB_SUMMER:
case JOB_SUMMER2:
return msg_txt(nullptr, 621);
case JOB_HANBOK:
return msg_txt(nullptr, 694);
case JOB_OKTOBERFEST:
return msg_txt(nullptr, 696);
case JOB_NOVICE_HIGH:
case JOB_SWORDMAN_HIGH:
case JOB_MAGE_HIGH:
case JOB_ARCHER_HIGH:
case JOB_ACOLYTE_HIGH:
case JOB_MERCHANT_HIGH:
case JOB_THIEF_HIGH:
return msg_txt(nullptr, 575 - JOB_NOVICE_HIGH + class_);
case JOB_LORD_KNIGHT:
case JOB_HIGH_PRIEST:
case JOB_HIGH_WIZARD:
case JOB_WHITESMITH:
case JOB_SNIPER:
case JOB_ASSASSIN_CROSS:
return msg_txt(nullptr, 582 - JOB_LORD_KNIGHT + class_);
case JOB_LORD_KNIGHT2:
return msg_txt(nullptr, 582);
case JOB_PALADIN:
case JOB_CHAMPION:
case JOB_PROFESSOR:
case JOB_STALKER:
case JOB_CREATOR:
case JOB_CLOWN:
case JOB_GYPSY:
return msg_txt(nullptr, 588 - JOB_PALADIN + class_);
case JOB_PALADIN2:
return msg_txt(nullptr, 588);
case JOB_BABY:
case JOB_BABY_SWORDMAN:
case JOB_BABY_MAGE:
case JOB_BABY_ARCHER:
case JOB_BABY_ACOLYTE:
case JOB_BABY_MERCHANT:
case JOB_BABY_THIEF:
return msg_txt(nullptr, 595 - JOB_BABY + class_);
case JOB_BABY_KNIGHT:
case JOB_BABY_PRIEST:
case JOB_BABY_WIZARD:
case JOB_BABY_BLACKSMITH:
case JOB_BABY_HUNTER:
case JOB_BABY_ASSASSIN:
return msg_txt(nullptr, 602 - JOB_BABY_KNIGHT + class_);
case JOB_BABY_KNIGHT2:
return msg_txt(nullptr, 602);
case JOB_BABY_CRUSADER:
case JOB_BABY_MONK:
case JOB_BABY_SAGE:
case JOB_BABY_ROGUE:
case JOB_BABY_ALCHEMIST:
case JOB_BABY_BARD:
case JOB_BABY_DANCER:
return msg_txt(nullptr, 608 - JOB_BABY_CRUSADER + class_);
case JOB_BABY_CRUSADER2:
return msg_txt(nullptr, 608);
case JOB_SUPER_BABY:
return msg_txt(nullptr, 615);
case JOB_TAEKWON:
return msg_txt(nullptr, 616);
case JOB_STAR_GLADIATOR:
case JOB_STAR_GLADIATOR2:
return msg_txt(nullptr, 617);
case JOB_SOUL_LINKER:
return msg_txt(nullptr, 618);
case JOB_GANGSI:
case JOB_DEATH_KNIGHT:
case JOB_DARK_COLLECTOR:
return msg_txt(nullptr, 622 - JOB_GANGSI + class_);
case JOB_RUNE_KNIGHT:
case JOB_WARLOCK:
case JOB_RANGER:
case JOB_ARCH_BISHOP:
case JOB_MECHANIC:
case JOB_GUILLOTINE_CROSS:
return msg_txt(nullptr, 625 - JOB_RUNE_KNIGHT + class_);
case JOB_RUNE_KNIGHT_T:
case JOB_WARLOCK_T:
case JOB_RANGER_T:
case JOB_ARCH_BISHOP_T:
case JOB_MECHANIC_T:
case JOB_GUILLOTINE_CROSS_T:
return msg_txt(nullptr, 681 - JOB_RUNE_KNIGHT_T + class_);
case JOB_ROYAL_GUARD:
case JOB_SORCERER:
case JOB_MINSTREL:
case JOB_WANDERER:
case JOB_SURA:
case JOB_GENETIC:
case JOB_SHADOW_CHASER:
return msg_txt(nullptr, 631 - JOB_ROYAL_GUARD + class_);
case JOB_ROYAL_GUARD_T:
case JOB_SORCERER_T:
case JOB_MINSTREL_T:
case JOB_WANDERER_T:
case JOB_SURA_T:
case JOB_GENETIC_T:
case JOB_SHADOW_CHASER_T:
return msg_txt(nullptr, 687 - JOB_ROYAL_GUARD_T + class_);
case JOB_RUNE_KNIGHT2:
case JOB_RUNE_KNIGHT_T2:
return msg_txt(nullptr, 625);
case JOB_ROYAL_GUARD2:
case JOB_ROYAL_GUARD_T2:
return msg_txt(nullptr, 631);
case JOB_RANGER2:
case JOB_RANGER_T2:
return msg_txt(nullptr, 627);
case JOB_MECHANIC2:
case JOB_MECHANIC_T2:
return msg_txt(nullptr, 629);
case JOB_BABY_RUNE_KNIGHT:
case JOB_BABY_WARLOCK:
case JOB_BABY_RANGER:
case JOB_BABY_ARCH_BISHOP:
case JOB_BABY_MECHANIC:
case JOB_BABY_GUILLOTINE_CROSS:
case JOB_BABY_ROYAL_GUARD:
case JOB_BABY_SORCERER:
case JOB_BABY_MINSTREL:
case JOB_BABY_WANDERER:
case JOB_BABY_SURA:
case JOB_BABY_GENETIC:
case JOB_BABY_SHADOW_CHASER:
return msg_txt(nullptr, 638 - JOB_BABY_RUNE_KNIGHT + class_);
case JOB_BABY_RUNE_KNIGHT2:
return msg_txt(nullptr, 638);
case JOB_BABY_ROYAL_GUARD2:
return msg_txt(nullptr, 644);
case JOB_BABY_RANGER2:
return msg_txt(nullptr, 640);
case JOB_BABY_MECHANIC2:
return msg_txt(nullptr, 642);
case JOB_SUPER_NOVICE_E:
case JOB_SUPER_BABY_E:
return msg_txt(nullptr, 651 - JOB_SUPER_NOVICE_E + class_);
case JOB_KAGEROU:
case JOB_OBORO:
return msg_txt(nullptr, 653 - JOB_KAGEROU + class_);
case JOB_REBELLION:
return msg_txt(nullptr, 695);
case JOB_SUMMONER:
return msg_txt(nullptr, 697);
case JOB_BABY_SUMMONER:
return msg_txt(nullptr, 698);
case JOB_BABY_NINJA:
return msg_txt(nullptr, 699);
case JOB_BABY_KAGEROU:
case JOB_BABY_OBORO:
case JOB_BABY_TAEKWON:
case JOB_BABY_STAR_GLADIATOR:
case JOB_BABY_SOUL_LINKER:
case JOB_BABY_GUNSLINGER:
case JOB_BABY_REBELLION:
return msg_txt(nullptr, 753 - JOB_BABY_KAGEROU + class_);
case JOB_BABY_STAR_GLADIATOR2:
return msg_txt(nullptr, 756);
case JOB_STAR_EMPEROR:
case JOB_SOUL_REAPER:
case JOB_BABY_STAR_EMPEROR:
case JOB_BABY_SOUL_REAPER:
return msg_txt(nullptr, 782 - JOB_STAR_EMPEROR + class_);
case JOB_STAR_EMPEROR2:
return msg_txt(nullptr, 782);
case JOB_BABY_STAR_EMPEROR2:
return msg_txt(nullptr, 784);
case JOB_DRAGON_KNIGHT:
case JOB_MEISTER:
case JOB_SHADOW_CROSS:
case JOB_ARCH_MAGE:
case JOB_CARDINAL:
case JOB_WINDHAWK:
case JOB_IMPERIAL_GUARD:
case JOB_BIOLO:
case JOB_ABYSS_CHASER:
case JOB_ELEMENTAL_MASTER:
case JOB_INQUISITOR:
case JOB_TROUBADOUR:
case JOB_TROUVERE:
return msg_txt(nullptr, 800 - JOB_DRAGON_KNIGHT + class_);
case JOB_WINDHAWK2:
return msg_txt(nullptr, 805);
case JOB_MEISTER2:
return msg_txt(nullptr, 801);
case JOB_DRAGON_KNIGHT2:
return msg_txt(nullptr, 800);
case JOB_IMPERIAL_GUARD2:
return msg_txt(nullptr, 806);
case JOB_SKY_EMPEROR:
case JOB_SOUL_ASCETIC:
case JOB_SHINKIRO:
case JOB_SHIRANUI:
case JOB_NIGHT_WATCH:
case JOB_HYPER_NOVICE:
case JOB_SPIRIT_HANDLER:
return msg_txt(nullptr, 813 - JOB_SKY_EMPEROR + class_);
case JOB_SKY_EMPEROR2:
return msg_txt(nullptr, 813);
default:
return msg_txt(nullptr, 655);
}
}*/
/**
* Returns max base level for this character's class.
* @param job_id: Player's class
@ -677,11 +379,11 @@ void prepare_character_build_data(mmo_charakter& new_char, const nlohmann::json&
uint64 job_exp = 0;
if (new_char.base_level > 1) {
base_exp = bot_job_db.get_baseExp(job_id, new_char.base_level - 1);
base_exp = bot_job_db.get_baseExp(job_id, new_char.base_level);
}
if (new_char.job_level > 1) {
job_exp = bot_job_db.get_jobExp(job_id, new_char.job_level - 1);
job_exp = bot_job_db.get_jobExp(job_id, new_char.job_level);
}
new_char.base_exp = base_exp;
@ -689,11 +391,10 @@ void prepare_character_build_data(mmo_charakter& new_char, const nlohmann::json&
new_char.inventory_slots = 100; //Default
//TODO auslagern in calc fn
new_char.max_hp = 40;
new_char.hp = 40;
new_char.max_sp = 10;
new_char.sp = 10;
new_char.max_hp = pc_calc_basehp(new_char.base_level, new_char.class_);
new_char.hp = new_char.max_hp;
new_char.max_sp = pc_calc_basesp(new_char.base_level, new_char.class_);
new_char.sp = new_char.max_sp;
new_char.max_ap = 0;
new_char.ap = 0;
@ -742,7 +443,6 @@ void prepare_character_build_data(mmo_charakter& new_char, const nlohmann::json&
// If build profile is not found, default to INIT
build_profile = "INIT";
}
const auto& profile = json_build_profiles[build_profile];
new_char.base_level = profile["base_level"];
@ -761,6 +461,13 @@ void prepare_character_build_data(mmo_charakter& new_char, const nlohmann::json&
new_char.base_exp = base_exp;
new_char.job_exp = job_exp;
new_char.max_hp = pc_calc_basehp(new_char.base_level, new_char.class_);
new_char.hp = new_char.max_hp;
new_char.max_sp = pc_calc_basesp(new_char.base_level, new_char.class_);
new_char.sp = new_char.max_sp;
new_char.max_ap = 0;
new_char.ap = 0;
//Set stats
new_char.str = profile["str"];
new_char.agi = profile["agi"];
@ -775,6 +482,7 @@ void prepare_character_build_data(mmo_charakter& new_char, const nlohmann::json&
new_char.spl = profile["spl"];
new_char.con = profile["con"];
new_char.crt = profile["crt"];
}
//ShowDebug("Character: \n%s",dump_mmo_charakter(new_char));
}
@ -1164,8 +872,18 @@ const std::string BotJobDatabase::getDefaultLocation() {
*/
t_exp BotJobDatabase::get_baseExp(uint16 job_id, uint32 level) {
std::shared_ptr<bot_job_info> job = bot_job_db.find(job_id);
if (job) {
if (level > 1) {
return job->base_exp[level - 1];
}
else {
return 0;
}
return job ? job->base_exp[level - 1] : 0;
}
else {
return 0;
}
}
/**
@ -1176,8 +894,18 @@ t_exp BotJobDatabase::get_baseExp(uint16 job_id, uint32 level) {
*/
t_exp BotJobDatabase::get_jobExp(uint16 job_id, uint32 level) {
std::shared_ptr<bot_job_info> job = bot_job_db.find(job_id);
if (job) {
if (level > 1) {
return job->job_exp[level - 1];
}
else {
return 0;
}
return job ? job->job_exp[level - 1] : 0;
}
else {
return 0;
}
}
/**
@ -1287,25 +1015,25 @@ uint64 BotJobDatabase::parseBodyNode(const ryml::NodeRef& node) {
for (const auto& aspdit : aspdNode) {
std::string weapon;
c4::from_chars(aspdit.key(), &weapon);
std::string weapon_constant = "W_" + weapon;
int64 constant = convert_weaponid_to_number(weapon_constant.c_str());
//std::string weapon_constant = "W_" + weapon;
//int64 constant = convert_weaponid_to_number(weapon_constant.c_str());
//if (!script_get_constant(weapon_constant.c_str(), &constant)) {
// this->invalidWarning(aspdNode["BaseASPD"], "Unknown weapon type %s specified for %s, skipping.\n", weapon.c_str(), job_name.c_str());
// continue;
//}
if (constant < BOT_W_FIST || constant >= max) {
this->invalidWarning(aspdNode["BaseASPD"], "Invalid weapon type %s specified for %s, skipping.\n", weapon.c_str(), job_name.c_str());
continue;
}
//if (constant < BOT_W_FIST || constant >= max) {
// this->invalidWarning(aspdNode["BaseASPD"], "Invalid weapon type %s specified for %s, skipping.\n", weapon.c_str(), job_name.c_str());
// continue;
//}
int16 aspd;
//int16 aspd;
if (!this->asInt16(aspdNode, weapon.c_str(), aspd))
return 0;
//if (!this->asInt16(aspdNode, weapon.c_str(), aspd))
// return 0;
job->aspd_base[static_cast<int16>(constant)] = aspd;
//job->aspd_base[static_cast<int16>(constant)] = aspd;
}
}
else {

@ -1004,102 +1004,3 @@ int convert_jobid_to_number(const char* job_id) {
return -1; // Return an error value if the job ID is unknown
}
}
/*
* Weapon Types
*/
enum weapon_type : uint8 {
W_FIST, //Bare hands
W_DAGGER, //1
W_1HSWORD, //2
W_2HSWORD, //3
W_1HSPEAR, //4
W_2HSPEAR, //5
W_1HAXE, //6
W_2HAXE, //7
W_MACE, //8
W_2HMACE, //9 (unused)
W_STAFF, //10
W_BOW, //11
W_KNUCKLE, //12
W_MUSICAL, //13
W_WHIP, //14
W_BOOK, //15
W_KATAR, //16
W_REVOLVER, //17
W_RIFLE, //18
W_GATLING, //19
W_SHOTGUN, //20
W_GRENADE, //21
W_HUUMA, //22
W_2HSTAFF, //23
MAX_WEAPON_TYPE,
// dual-wield constants
W_DOUBLE_DD, // 2 daggers
W_DOUBLE_SS, // 2 swords
W_DOUBLE_AA, // 2 axes
W_DOUBLE_DS, // dagger + sword
W_DOUBLE_DA, // dagger + axe
W_DOUBLE_SA, // sword + axe
MAX_WEAPON_TYPE_ALL,
W_SHIELD = MAX_WEAPON_TYPE,
};
#define WEAPON_TYPE_ALL ((1<<MAX_WEAPON_TYPE)-1)
// Function to convert job_id string to job number
int convert_weaponid_to_number(const char* weapon_id) {
// Create a map for the job ID strings to the corresponding e_job enum values
static const std::unordered_map<std::string, int> weapon_map = {
{"W_FIST", W_FIST}, //Bare hands
{"W_DAGGER", W_DAGGER}, //1
{"W_1HSWORD", W_1HSWORD}, //2
{"W_2HSWORD", W_2HSWORD}, //3
{"W_1HSPEAR", W_1HSPEAR}, //4
{"W_2HSPEAR", W_2HSPEAR}, //5
{"W_1HAXE", W_1HAXE}, //6
{"W_2HAXE", W_2HAXE}, //7
{"W_MACE", W_MACE}, //8
{"W_2HMACE", W_2HMACE}, //9 (unused)
{"W_STAFF", W_STAFF}, //10
{"W_BOW", W_BOW}, //11
{"W_KNUCKLE", W_KNUCKLE}, //12
{"W_MUSICAL", W_MUSICAL}, //13
{"W_WHIP", W_WHIP}, //14
{"W_BOOK", W_BOOK}, //15
{"W_KATAR", W_KATAR}, //16
{"W_REVOLVER", W_REVOLVER}, //17
{"W_RIFLE", W_RIFLE}, //18
{"W_GATLING", W_GATLING}, //19
{"W_SHOTGUN", W_SHOTGUN}, //20
{"W_GRENADE", W_GRENADE}, //21
{"W_HUUMA", W_HUUMA}, //22
{"W_2HSTAFF", W_2HSTAFF}, //23
{"MAX_WEAPON_TYPE", MAX_WEAPON_TYPE},
// dual-wield constants
{"W_DOUBLE_DD", W_DOUBLE_DD}, // 2 daggers
{"W_DOUBLE_SS", W_DOUBLE_SS}, // 2 swords
{"W_DOUBLE_AA", W_DOUBLE_AA}, // 2 axes
{"W_DOUBLE_DS", W_DOUBLE_DS}, // dagger + sword
{"W_DOUBLE_DA", W_DOUBLE_DA}, // dagger + axe
{"W_DOUBLE_SA", W_DOUBLE_SA}, // sword + axe
{"MAX_WEAPON_TYPE_ALL", MAX_WEAPON_TYPE_ALL},
{"W_SHIELD", W_SHIELD},
// Add other job mappings here as needed
};
// Look up the job_id in the map
std::string weapon_str = weapon_id;
// Convert to upper case
for (int index = 0; index < weapon_str.size(); index++) {
weapon_str.at(index) = toupper(weapon_str.at(index));
}
auto it = weapon_map.find(weapon_str);
if (it != weapon_map.end()) {
return it->second; // Return the corresponding enum value
}
else {
ShowError("Unknown weapon ID: %s\n", weapon_id);
return -1; // Return an error value if the weapon ID is unknown
}
}

@ -211,6 +211,5 @@ struct CharDB {
//void mmo_save_global_accreg(BotAccountDB* self, int fd, uint32 account_id, uint32 char_id);
int convert_jobid_to_number(const char* job_id);
int convert_weaponid_to_number(const char* weapon_id);
#endif /* CHARAKTERS_HPP */

@ -34,6 +34,11 @@ using namespace rathena;
using json = nlohmann::json;
/// global defines
// Global cache for item AegisName-to-ID mapping
static json cached_itemdb_aegisname_to_id;
static json cached_itemdb_id_to_aegisname;
static json cached_itemdb_id_to_name;
static bool itemdbs_loaded = false; // Flag to track if data is loaded
/// internal structure
typedef struct InventoryDB_SQL {
@ -286,23 +291,7 @@ static bool bot_inv_item_db_sql_create(InventoryDB* self, struct bot_inv_item* i
* @param char_id: id of char, the item belongs to
* @param inventory_id: if of item to delete
* @return true if successful, false if something has failed
static bool bot_inv_item_db_sql_remove(InventoryDB* self, const uint32 char_id, const uint32 inventory_id) {
InventoryDB_SQL* db = (InventoryDB_SQL*)self;
Sql* sql_handle = db->bot_inventory;
bool result = false;
if( SQL_SUCCESS != Sql_QueryStr(sql_handle, "START TRANSACTION")
|| SQL_SUCCESS != Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `char_id` = %d AND `id` = %d", db->inventory_db, char_id, inventory_id)
)
Sql_ShowDebug(sql_handle);
else
result = true;
result &= ( SQL_SUCCESS == Sql_QueryStr(sql_handle, (result == true) ? "COMMIT" : "ROLLBACK") );
return result;
}*/
*/
static bool bot_inv_item_db_sql_remove(InventoryDB* self, const uint32 char_id, const uint32 inventory_id) {
InventoryDB_SQL* db = (InventoryDB_SQL*)self;
Sql* sql_handle = db->bot_inventory;
@ -395,27 +384,49 @@ void dump_item_bundle(const bot_item_bundle& item_bundle) {
ShowDebug("End of item bundle dump.\n");
}
/*
* Weapon Types
*/
enum weapon_type : uint8 {
W_FIST, //Bare hands
W_DAGGER, //1
W_1HSWORD, //2
W_2HSWORD, //3
W_1HSPEAR, //4
W_2HSPEAR, //5
W_1HAXE, //6
W_2HAXE, //7
W_MACE, //8
W_2HMACE, //9 (unused)
W_STAFF, //10
W_BOW, //11
W_KNUCKLE, //12
W_MUSICAL, //13
W_WHIP, //14
W_BOOK, //15
W_KATAR, //16
W_REVOLVER, //17
W_RIFLE, //18
W_GATLING, //19
W_SHOTGUN, //20
W_GRENADE, //21
W_HUUMA, //22
W_2HSTAFF, //23
MAX_WEAPON_TYPE,
// dual-wield constants
W_DOUBLE_DD, // 2 daggers
W_DOUBLE_SS, // 2 swords
W_DOUBLE_AA, // 2 axes
W_DOUBLE_DS, // dagger + sword
W_DOUBLE_DA, // dagger + axe
W_DOUBLE_SA, // sword + axe
MAX_WEAPON_TYPE_ALL,
W_SHIELD = MAX_WEAPON_TYPE,
};
#define WEAPON_TYPE_ALL ((1<<MAX_WEAPON_TYPE)-1)
/**
* Create a new forward iterator.
* @param self: pointer to db iterator
* @return a new db iterator
static InventoryDBIterator* bot_inv_item_db_sql_iterator(InventoryDB* self, const uint32 char_id) {
InventoryDB_SQL* db = (InventoryDB_SQL*)self;
InventoryDBIterator_SQL* iter = (InventoryDBIterator_SQL*)aCalloc(1, sizeof(InventoryDBIterator_SQL));
// set up the vtable
iter->vtable.destroy = &bot_inv_item_db_sql_iter_destroy;
iter->vtable.next = &bot_inv_item_db_sql_iter_next;
// fill data
iter->db = db;
iter->char_id = char_id;
iter->last_inv_item_id = -1;
return &iter->vtable;
} */
static InventoryDBIterator* bot_inv_item_db_sql_iterator(InventoryDB* self, const uint32 char_id) {
InventoryDB_SQL* db = (InventoryDB_SQL*)self;
@ -458,43 +469,6 @@ static void bot_inv_item_db_sql_iter_destroy(InventoryDBIterator* self) {
aFree(iter);
}
/**
* Fetches the next inv item entry in the database.
* @param self: pointer to db iterator
* @param inventory_id: id of the inventory table the item belongs to
* @return true if next inventory entry found and filled, false if something has failed
static bool bot_inv_item_db_sql_iter_next(InventoryDBIterator* self, struct bot_inv_item* item) {
InventoryDBIterator_SQL* iter = (InventoryDBIterator_SQL*)self;
InventoryDB_SQL* db = iter->db;
Sql* sql_handle = db->bot_inventory;
char* data;
// get next inventory ID
if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `id` FROM `%s` WHERE `char_id` = %d AND `id` > '%d' ORDER BY `id` ASC LIMIT 1",
db->inventory_db, iter->char_id, iter->last_inv_item_id) )
{
Sql_ShowDebug(sql_handle);
return false;
}
if( SQL_SUCCESS == Sql_NextRow(sql_handle) &&
SQL_SUCCESS == Sql_GetData(sql_handle, 0, &data, nullptr) &&
data != nullptr )
{// get item data
uint32 inventory_id = atoi(data);
item->inventory_id = inventory_id;
if( bot_inv_item_db_fromsql(db, item) )
{
iter->last_inv_item_id = inventory_id;
Sql_FreeResult(sql_handle);
return true;
}
}
Sql_FreeResult(sql_handle);
return false;
} */
static bool bot_inv_item_db_sql_iter_next(InventoryDBIterator* self, struct bot_inv_item* item) {
InventoryDBIterator_SQL* iter = (InventoryDBIterator_SQL*)self;
InventoryDB_SQL* db = iter->db;
@ -549,80 +523,6 @@ static bool bot_inv_item_db_sql_iter_next(InventoryDBIterator* self, struct bot_
return false;
}
/**
* Fetch a struct bot_inv_item from sql.
* @param db: pointer to db
* @param item: pointer of bot_inv_item to fill
* @param char_id: id of character, item belong to
* @param item_id: id of item to take data from
* @return true if successful, false if something has failed
static bool bot_inv_item_db_fromsql(InventoryDB_SQL* db, struct bot_inv_item* item) {
Sql* sql_handle = db->bot_inventory;
char* data;
// retrieve login entry for the specified account
if( SQL_ERROR == Sql_Query(sql_handle,
#ifdef VIP_ENABLE
"SELECT `id`, `char_id`, `nameid`, `amount`, `equip`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `enchantgrade` FROM `%s` WHERE `id` = %d",
#else
"SELECT `id`, `char_id`, `nameid`, `amount`, `equip`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `enchantgrade` FROM `%s` WHERE `id` = %d",
#endif
db->inventory_db, item->inventory_id )
) {
Sql_ShowDebug(sql_handle);
return false;
}
if( SQL_SUCCESS != Sql_NextRow(sql_handle) )
{// no such entry
Sql_FreeResult(sql_handle);
return false;
}
Sql_GetData(sql_handle, 0, &data, nullptr); item->inventory_id = atoi(data);
Sql_GetData(sql_handle, 1, &data, nullptr); item->char_id = atoi(data);
Sql_GetData(sql_handle, 2, &data, nullptr); item->item_id = atoi(data);
Sql_GetData(sql_handle, 3, &data, nullptr); item->amount = atoi(data);
Sql_GetData(sql_handle, 4, &data, nullptr); item->equip = atoi(data);
Sql_GetData(sql_handle, 5, &data, nullptr); item->refine = atoi(data);
Sql_GetData(sql_handle, 6, &data, nullptr); item->attribute = atoi(data);
Sql_GetData(sql_handle, 7, &data, nullptr); item->card0 = atoi(data);
Sql_GetData(sql_handle, 8, &data, nullptr); item->card1 = atoi(data);
Sql_GetData(sql_handle, 9, &data, nullptr); item->card2 = atoi(data);
Sql_GetData(sql_handle, 10, &data, nullptr); item->card3 = atoi(data);
Sql_GetData(sql_handle, 11, &data, nullptr); item->enchantgrade = atoi(data);
//endif
Sql_FreeResult(sql_handle);
//Other fields are more or less irrelevant for bot purposes -> therefor they are pre-filled with default values
item->identify = 1; //identify = 1 means is successfully identified
item->expire_time = 0; //Dont expire
item->favorite = 0;
item->bound = 0;
item->unique_id = 0;
item->equip_switch = 0;
item->option_id0 = 0;
item->option_parm0 = 0;
item->option_val0 = 0;
item->option_id1 = 0;
item->option_parm1 = 0;
item->option_val1 = 0;
item->option_id2 = 0;
item->option_parm2 = 0;
item->option_val2 = 0;
item->option_id3 = 0;
item->option_parm3 = 0;
item->option_val3 = 0;
item->option_id4 = 0;
item->option_parm4 = 0;
item->option_val4 = 0;
return true;
} */
static bool bot_inv_item_db_fromsql(InventoryDB_SQL* db, struct bot_inv_item* item) {
Sql* sql_handle = db->bot_inventory;
char* data;
@ -826,125 +726,80 @@ void set_bot_inv_item_defaults(bot_inv_item& item) {
item.enchantgrade = 0;
}
uint32 convert_item_aegisname_to_id(const char* aegisname)
{
// Open the mapping JSON file
std::ifstream bot_itemdb_aegisname_to_id_file("conf/bot_helper_jsons/itemdb_converter_aegisname_to_id.json");
if (!bot_itemdb_aegisname_to_id_file.is_open()) {
ShowError("Failed to open itemdb_converter_aegisname_to_id.json\n");
return 0;
}
// Parse the JSON file
json json_itemdb_aegisname_to_id;
try {
bot_itemdb_aegisname_to_id_file >> json_itemdb_aegisname_to_id;
}
catch (json::parse_error& e) {
ShowError("Error parsing bot_accounts.json: %s\n", e.what());
return false;
}
// Close file after done with it
bot_itemdb_aegisname_to_id_file.close();
//ShowStatus("Mapping aegisname to id...\n");
if (!json_itemdb_aegisname_to_id.contains(aegisname)) {
// Error: aegisname is not inside json file
ShowWarning("Aegisname \"%s\" was not found inside the itemdb. Skipped...", aegisname);
return 0;
}
const uint32 item_id = json_itemdb_aegisname_to_id[aegisname];
//ShowStatus("aegisname found for Item_id %d\n",item_id);
return item_id;
}
std::string convert_item_id_to_name(uint32 item_id)
void set_bot_equip_item_defaults(bot_inv_item& item)
{
// Open the mapping JSON file
std::ifstream bot_itemdb_item_id_to_name_file("conf/bot_helper_jsons/itemdb_converter_id_to_name.json");
if (!bot_itemdb_item_id_to_name_file.is_open()) {
ShowError("Failed to open itemdb_converter_id_to_name.json\n");
return "UNKNOWN";
}
item.inventory_id = -1;
item.char_id = 0;
item.item_id = 512; // Default apple
item.amount = 1;
item.equip = 0; // is Overriden by value
// Parse the JSON file
json json_itemdb_id_to_name;
item.identify = 1; // Default indentified
item.refine = 0;
item.attribute = 0;
try {
//bot_itemdb_item_id_to_name_file >> json_itemdb_id_to_name;
json test_json = json::parse(bot_itemdb_item_id_to_name_file);
//ShowDebug("Test parse successful: %s\n", test_json.dump().c_str());
}
catch (const json::parse_error& e) {
ShowError("JSON parse error: %s\n", e.what());
}
catch (const std::exception& e) {
ShowError("General exception: %s\n", e.what());
}
catch (...) {
ShowError("Unknown error occurred during JSON parsing.\n");
}
// Close file after done with it
item.card0 = 0;
item.card1 = 0;
item.card2 = 0;
item.card3 = 0;
bot_itemdb_item_id_to_name_file.close();
item.option_id0 = 0;
item.option_val0 = 0;
item.option_parm0 = 0;
//ShowStatus("Mapping item_id to item_name...\n");
std::string id_str = std::to_string(item_id);
item.option_id1 = 0;
item.option_val1 = 0;
item.option_parm1 = 0;
//ShowStatus("id=%s\n", id_str.c_str());
if (!json_itemdb_id_to_name.contains(id_str)) {
// Error: item_id is not inside json file
ShowWarning("Item_id \"%d\" was not found inside the itemdb. Skipped...\n", item_id);
return "UNKNOWN";
}
item.option_id2 = 0;
item.option_val2 = 0;
item.option_parm2 = 0;
const std::string item_name = json_itemdb_id_to_name[id_str];
//ShowStatus("Entry found. Item Name: %s\n", item_name.c_str());
item.option_id3 = 0;
item.option_val3 = 0;
item.option_parm3 = 0;
return item_name;
item.option_id4 = 0;
item.option_val4 = 0;
item.option_parm4 = 0;
item.expire_time = 0;
item.favorite = 0;
item.bound = 0;
item.unique_id = 0;
item.equip_switch = 0;
item.enchantgrade = 0;
}
std::string convert_item_id_to_aegisname(uint32 item_id) {
static json json_itemdb_id_to_aegisname;
static bool id_to_aegisname_is_loaded = false;
// Load the JSON file only once
if (!id_to_aegisname_is_loaded) {
std::ifstream bot_itemdb_item_id_to_aegisname_file("conf/bot_helper_jsons/itemdb_converter_id_to_aegisname.json");
if (!bot_itemdb_item_id_to_aegisname_file.is_open()) {
ShowError("Failed to open itemdb_converter_id_to_aegisname.json\n");
return "UNKNOWN";
}
try {
bot_itemdb_item_id_to_aegisname_file >> json_itemdb_id_to_aegisname;
id_to_aegisname_is_loaded = true;
void generate_crafted_item_attributes(bot_inv_item& item, uint32 creator_char_id, int element, int cnt_star_crumbs) {
// Ensure valid input
if (element < 1 || element > 4) {
ShowError("Invalid element type for crafted item. Valid range is 1-4.\n");
return;
}
catch (json::parse_error& e) {
ShowError("Error parsing itemdb_converter_id_to_aegisname.json: %s\n", e.what());
return "UNKNOWN";
if (cnt_star_crumbs < 0) {
ShowError("Invalid number of Star Crumbs. Must be non-negative.\n");
return;
}
bot_itemdb_item_id_to_aegisname_file.close();
}
// Assign crafted item flag to card1
item.card0 = 255; // Indicates inscribed equipment
// Lookup the item ID in the JSON data
std::string id_str = std::to_string(item_id);
if (!json_itemdb_id_to_aegisname.contains(id_str)) {
ShowWarning("Item_id \"%u\" was not found inside the itemdb. Skipped...", item_id);
return "UNKNOWN";
}
// Compute card2 using the formula
item.card1 = element + ((cnt_star_crumbs * 5) << 8);
// Split inscriber ID into card3 and card4
item.card2 = creator_char_id & 65535; // Lower 16 bits of inscriber_id
item.card3 = creator_char_id >> 16; // Upper 16 bits of inscriber_id
return json_itemdb_id_to_aegisname[id_str];
ShowDebug("Crafted item attributes generated:\n");
ShowDebug(" - Element: %d\n", element);
ShowDebug(" - Star Crumbs: %d\n", cnt_star_crumbs);
ShowDebug(" - Inscriber ID: %u (card2: %u, card3: %u)\n", creator_char_id, item.card2, item.card3);
}
/*
* Tool Functions for item name parsing
* Tool Functions for item preparation
*/
void prepare_item_bundle(bot_item_bundle& bundle, uint32 char_id, const nlohmann::json& character) {
@ -1041,5 +896,519 @@ void prepare_item_bundle(bot_item_bundle& bundle, uint32 char_id, const nlohmann
bundle.items.push_back(new_item);
}
}
}
void prepare_equipment_bundle(bot_item_bundle& bundle, uint32 char_id, const nlohmann::json& character) {
//Select for the used profile
std::string equipment_profile = character["char_equip_profile"];
if (equipment_profile == "CUSTOM") {
if (!character.contains("equip")) {
throw std::runtime_error("equip data is missing for CUSTOM equip profile.");
}
const auto& equip_items = character["equip"];
//Initialize the items bundle
bundle.items.clear();
// Iterate over the items in the JSON
for (const auto& equip_item : equip_items.items()) {
std::string equip_pos_str = equip_item.key();
const auto& equip_item_data = equip_item.value();
std::string equip_item_name;
//Einbauen: Check für crafted item
//generate_crafted_item_attributes(item, char_id, int element, int cnt_star_crumbs
uint32 equip_item_id = convert_item_aegisname_to_id(equip_item_name.c_str());
// Convert item name to item ID
uint32 equip_pos_id = convert_equipslot_id_to_number(equip_pos_str.c_str());
if (equip_pos_id <= 0) { // Invalid Equip slot ID
ShowWarning("Equip slot ID %s is Invalid. Skipping...\n", equip_pos_str.c_str());
continue; // Skip invalid item
}
const std::string& equip_slot = equip_item.key();
bot_inv_item new_item{};
set_bot_inv_item_defaults(new_item);
// Convert item aegisname to item ID
// Read item information
if (equip_item_data.contains("AEGISNAME")) {
std::string aegisname = equip_item_data["AEGISNAME"];
new_item.item_id = convert_item_aegisname_to_id(aegisname.c_str());
if (new_item.item_id <= 0) {
ShowWarning("Invalid AEGISNAME %s for slot %s. Skipping...\n", aegisname.c_str(), equip_slot.c_str());
continue;
}
}
else {
ShowWarning("Missing AEGISNAME for slot %s. Skipping...\n", equip_slot.c_str());
continue;
}
new_item.char_id = char_id;
new_item.equip = equip_pos_id;
// Read refine level
if (equip_item_data.contains("REFINELVL")) {
new_item.refine = equip_item_data["REFINELVL"];
}
else {
new_item.refine = 0; // Default refine level
}
// Read card fields
if (equip_item_data.contains("CARD0")) {
new_item.card0 = convert_item_aegisname_to_id(equip_item_data["CARD0"].get<std::string>().c_str());
}
if (equip_item_data.contains("CARD1")) {
new_item.card1 = convert_item_aegisname_to_id(equip_item_data["CARD1"].get<std::string>().c_str());
}
if (equip_item_data.contains("CARD2")) {
new_item.card2 = convert_item_aegisname_to_id(equip_item_data["CARD2"].get<std::string>().c_str());
}
if (equip_item_data.contains("CARD3")) {
new_item.card3 = convert_item_aegisname_to_id(equip_item_data["CARD3"].get<std::string>().c_str());
}
// Handle crafted items
if (equip_item_data.contains("CRAFTED")) {
const nlohmann::json& crafted_data = equip_item_data["CRAFTED"];
new_item.card0 = 255; // Crafted equipment flag
if (crafted_data.contains("CREATOR")) {
std::string creator = crafted_data["CREATOR"];
//uint32 inscriber_id = (creator == "SELF") ? char_id : get_char_id_by_name(creator.c_str());
uint32 inscriber_id = (creator == "SELF") ? char_id : 0; // If creator is not self by default creator is "Nameless"
new_item.card2 = inscriber_id & 65535;
new_item.card3 = inscriber_id >> 16;
}
if (crafted_data.contains("STAR_CRUMBS") && crafted_data.contains("ELEMENT")) {
int star_crumbs = crafted_data["STAR_CRUMBS"];
std::string element = crafted_data["ELEMENT"];
int element_id = convert_element_id_to_number(element);
new_item.card1 = element_id + ((star_crumbs * 5) << 8);
}
}
bundle.items.push_back(new_item);
}
}
else {
// Load item loadout Profile
std::string job_str = character["start_job"];
int job_id = convert_jobid_to_number(job_str.c_str());
std::string job_profile_file_path = "conf/bot_profiles/equip_profiles/" + job_str + "_equip_profile.json";
nlohmann::json json_equip_profiles;
// Try to Open iten Profile
std::ifstream equip_profile_file(job_profile_file_path);
if (!equip_profile_file.is_open()) {
ShowWarning("Failed to open equip_profile_file %s. Trying to load DEFAULT instead.\n", job_profile_file_path.c_str());
job_profile_file_path = "conf/bot_profiles/equip_profiles/DEFAULT_equip_profile.json";
equip_profile_file.open(job_profile_file_path);
if (!equip_profile_file.is_open()) {
throw std::runtime_error("Failed to open both job and default profiles.");
}
}
// Parse Profile Data
try {
equip_profile_file >> json_equip_profiles;
}
catch (const nlohmann::json::parse_error& e) {
throw std::runtime_error("Error parsing equip profile JSON: " + std::string(e.what()));
}
if (!json_equip_profiles.contains(equipment_profile)) {
// If equipment profile is not found, default to INIT
equipment_profile = "INIT";
}
const auto& equip_profile = json_equip_profiles[equipment_profile];
//Initialize the item bundle
bundle.items.clear();
// Iterate through each equipment slot
for (auto& equip_entry : equip_profile.items()) {
std::string equip_slot = equip_entry.key();
uint32 equip_pos_id = convert_equipslot_id_to_number(equip_slot.c_str());
const auto& equip_item_data = equip_entry.value();
bot_inv_item new_item{};
set_bot_inv_item_defaults(new_item);
// Convert item aegisname to item ID
// Read item information
if (equip_item_data.contains("AEGISNAME")) {
std::string aegisname = equip_item_data["AEGISNAME"];
new_item.item_id = convert_item_aegisname_to_id(aegisname.c_str());
if (new_item.item_id <= 0) {
ShowWarning("Invalid AEGISNAME %s for slot %s. Skipping...\n", aegisname.c_str(), equip_slot.c_str());
continue;
}
}
else {
ShowWarning("Missing AEGISNAME for slot %s. Skipping...\n", equip_slot.c_str());
continue;
}
new_item.char_id = char_id;
new_item.equip = equip_pos_id;
// Read refine level
if (equip_item_data.contains("REFINELVL")) {
new_item.refine = equip_item_data["REFINELVL"];
}
else {
new_item.refine = 0; // Default refine level
}
// Read card fields
if (equip_item_data.contains("CARD0")) {
new_item.card0 = convert_item_aegisname_to_id(equip_item_data["CARD0"].get<std::string>().c_str());
}
if (equip_item_data.contains("CARD1")) {
new_item.card1 = convert_item_aegisname_to_id(equip_item_data["CARD1"].get<std::string>().c_str());
}
if (equip_item_data.contains("CARD2")) {
new_item.card2 = convert_item_aegisname_to_id(equip_item_data["CARD2"].get<std::string>().c_str());
}
if (equip_item_data.contains("CARD3")) {
new_item.card3 = convert_item_aegisname_to_id(equip_item_data["CARD3"].get<std::string>().c_str());
}
// Handle crafted items
if (equip_item_data.contains("CRAFTED")) {
const nlohmann::json& crafted_data = equip_item_data["CRAFTED"];
new_item.card0 = 255; // Crafted equipment flag
if (crafted_data.contains("CREATOR")) {
std::string creator = crafted_data["CREATOR"];
//uint32 inscriber_id = (creator == "SELF") ? char_id : get_char_id_by_name(creator.c_str());
uint32 inscriber_id = (creator == "SELF") ? char_id : 0; // If creator is not self by default creator is "Nameless"
new_item.card2 = inscriber_id & 65535;
new_item.card3 = inscriber_id >> 16;
}
if (crafted_data.contains("STAR_CRUMBS") && crafted_data.contains("ELEMENT")) {
int star_crumbs = crafted_data["STAR_CRUMBS"];
std::string element = crafted_data["ELEMENT"];
int element_id = convert_element_id_to_number(element);
new_item.card1 = element_id + ((star_crumbs * 5) << 8);
}
}
bundle.items.push_back(new_item);
}
}
}
/*
* Converter Functions for item parsing
*/
uint32 convert_item_aegisname_to_id(const char* aegisname) {
if (!itemdbs_loaded) {
load_itemdbs();
}
if (!cached_itemdb_aegisname_to_id.contains(aegisname)) {
ShowWarning("Aegisname \"%s\" was not found inside the itemdb. Skipped...\n", aegisname);
return 0;
}
const uint32 item_id = cached_itemdb_aegisname_to_id[aegisname];
//ShowStatus("aegisname found for Item_id %d\n",item_id);
return item_id;
}
void load_itemdb_aegisname_to_id() {
if (itemdbs_loaded) return; // Already loaded, skip
std::ifstream bot_itemdb_aegisname_to_id_file("conf/bot_helper_jsons/itemdb_converter_aegisname_to_id.json");
if (!bot_itemdb_aegisname_to_id_file.is_open()) {
ShowError("Failed to open itemdb_converter_aegisname_to_id.json\n");
return;
}
try {
bot_itemdb_aegisname_to_id_file >> cached_itemdb_aegisname_to_id;
}
catch (json::parse_error& e) {
ShowError("Error parsing itemdb_converter_aegisname_to_id.json: %s\n", e.what());
throw e;
}
bot_itemdb_aegisname_to_id_file.close();
}
std::string convert_item_id_to_name(uint32 item_id)
{
if (!itemdbs_loaded) {
load_itemdbs();
}
//ShowStatus("Mapping item_id to item_name...\n");
std::string id_str = std::to_string(item_id);
if (!cached_itemdb_id_to_name.contains(id_str)) {
ShowWarning("id_str \"%s\" was not found inside the itemdb. Skipped...\n", id_str.c_str());
return 0;
}
std::string item_name = cached_itemdb_id_to_name[id_str];
//ShowStatus("item id found %d\n",item_name.c_str());
return item_name;
}
void load_itemdb_id_to_name() {
if (itemdbs_loaded) return; // Already loaded, skip
std::ifstream bot_itemdb_id_to_name_file("conf/bot_helper_jsons/itemdb_converter_id_to_name.json");
if (!bot_itemdb_id_to_name_file.is_open()) {
ShowError("Failed to open itemdb_converter_id_to_name.json\n");
return;
}
try {
bot_itemdb_id_to_name_file >> cached_itemdb_id_to_name;
}
catch (json::parse_error& e) {
ShowError("Error parsing itemdb_converter_id_to_name.json: %s\n", e.what());
throw e;
}
bot_itemdb_id_to_name_file.close();
}
std::string convert_item_id_to_aegisname(uint32 item_id) {
if (!itemdbs_loaded) {
load_itemdbs();
}
//ShowStatus("Mapping item_id to item_name...\n");
std::string id_str = std::to_string(item_id);
if (!cached_itemdb_id_to_aegisname.contains(id_str)) {
ShowWarning("id_str \"%s\" was not found inside the itemdb. Skipped...\n", id_str.c_str());
return 0;
}
std::string item_aegisname = cached_itemdb_id_to_aegisname[id_str];
//ShowStatus("item id found %d\n",item_name.c_str());
return item_aegisname;
}
void load_itemdb_id_to_aegisname() {
if (itemdbs_loaded) return; // Already loaded, skip
std::ifstream bot_itemdb_id_to_aegisname_file("conf/bot_helper_jsons/itemdb_converter_id_to_aegisname.json");
if (!bot_itemdb_id_to_aegisname_file.is_open()) {
ShowError("Failed to open itemdb_converter_id_to_aegisname.json\n");
return;
}
try {
bot_itemdb_id_to_aegisname_file >> cached_itemdb_id_to_aegisname;
}
catch (json::parse_error& e) {
ShowError("Error parsing itemdb_converter_id_to_aegisname.json: %s\n", e.what());
throw e;
}
bot_itemdb_id_to_aegisname_file.close();
}
uint32 convert_equipslot_id_to_number(const char* equipslot_id)
{
static const std::unordered_map<std::string, uint32> equipslot_map = {
{"EQP_HEAD_LOW" ,EQP_HEAD_LOW },
{"EQP_HEAD_MID" ,EQP_HEAD_MID },
{"EQP_HEAD_TOP" ,EQP_HEAD_TOP },
{"EQP_HEAD_LOWMID" ,EQP_HEAD_LOW + EQP_HEAD_MID },
{"EQP_HEAD_LOWTOP" ,EQP_HEAD_LOW + EQP_HEAD_TOP },
{"EQP_HEAD_MIDTOP" ,EQP_HEAD_MID + EQP_HEAD_TOP },
{"EQP_HEAD_ALL" ,EQP_HEAD_LOW + EQP_HEAD_MID + EQP_HEAD_TOP },
{"EQP_HAND_R" ,EQP_HAND_R },
{"EQP_HAND_L" ,EQP_HAND_L },
{"EQP_HAND_BOTH" ,EQP_HAND_R + EQP_HAND_L },
{"EQP_ARMOR" ,EQP_ARMOR },
{"EQP_SHOES" ,EQP_SHOES },
{"EQP_GARMENT" ,EQP_GARMENT },
{"EQP_ACC_R" ,EQP_ACC_R },
{"EQP_ACC_L" ,EQP_ACC_L },
{"EQP_COSTUME_HEAD_TOP" ,EQP_COSTUME_HEAD_TOP },
{"EQP_COSTUME_HEAD_MID" ,EQP_COSTUME_HEAD_MID },
{"EQP_COSTUME_HEAD_LOW" ,EQP_COSTUME_HEAD_LOW },
{"EQP_COSTUME_HEAD_LOWMID" ,EQP_COSTUME_HEAD_LOW + EQP_COSTUME_HEAD_MID },
{"EQP_COSTUME_HEAD_LOWTOP" ,EQP_COSTUME_HEAD_LOW + EQP_COSTUME_HEAD_TOP },
{"EQP_COSTUME_HEAD_MIDTOP" ,EQP_COSTUME_HEAD_MID + EQP_COSTUME_HEAD_TOP },
{"EQP_COSTUME_HEAD_ALL" ,EQP_COSTUME_HEAD_LOW + EQP_COSTUME_HEAD_MID + EQP_COSTUME_HEAD_TOP },
{"EQP_COSTUME_GARMENT" ,EQP_COSTUME_GARMENT },
{"EQP_AMMO" ,EQP_AMMO },
{"EQP_SHADOW_ARMOR" ,EQP_SHADOW_ARMOR },
{"EQP_SHADOW_WEAPON" ,EQP_SHADOW_WEAPON },
{"EQP_SHADOW_SHIELD" ,EQP_SHADOW_SHIELD },
{"EQP_SHADOW_SHOES" ,EQP_SHADOW_SHOES },
{"EQP_SHADOW_ACC_R" ,EQP_SHADOW_ACC_R },
{"EQP_SHADOW_ACC_L" ,EQP_SHADOW_ACC_L }
};
// Look up the skill_id in the map
std::string equipslot = equipslot_id;
auto it = equipslot_map.find(equipslot);
if (it != equipslot_map.end()) {
return it->second; // Return the corresponding str value
}
else {
ShowError("Unknown equipslot ID: %s\n", equipslot_id);
return 0; // Return an error value if the equipslot ID is unknown
}
}
const char* convert_equipslot_id_to_str(uint32 equipslot_id)
{
static const std::unordered_map<uint32, std::string> equipslot_map = {
{EQP_HEAD_LOW ,"EQP_HEAD_LOW" },
{EQP_HEAD_MID ,"EQP_HEAD_MID" },
{EQP_HEAD_TOP ,"EQP_HEAD_TOP" },
{EQP_HEAD_LOW + EQP_HEAD_MID ,"EQP_HEAD_LOWMID" },
{EQP_HEAD_LOW + EQP_HEAD_TOP ,"EQP_HEAD_LOWTOP" },
{EQP_HEAD_MID + EQP_HEAD_TOP ,"EQP_HEAD_MIDTOP" },
{EQP_HEAD_LOW + EQP_HEAD_MID + EQP_HEAD_TOP ,"EQP_HEAD_ALL" },
{EQP_HAND_R ,"EQP_HAND_R" },
{EQP_HAND_L ,"EQP_HAND_L" },
{EQP_HAND_R + EQP_HAND_L ,"EQP_HAND_BOTH" },
{EQP_ARMOR ,"EQP_ARMOR" },
{EQP_SHOES ,"EQP_SHOES" },
{EQP_GARMENT ,"EQP_GARMENT" },
{EQP_ACC_R ,"EQP_ACC_R" },
{EQP_ACC_L ,"EQP_ACC_L" },
{EQP_COSTUME_HEAD_TOP ,"EQP_COSTUME_HEAD_TOP" },
{EQP_COSTUME_HEAD_MID ,"EQP_COSTUME_HEAD_MID" },
{EQP_COSTUME_HEAD_LOW ,"EQP_COSTUME_HEAD_LOW" },
{EQP_COSTUME_HEAD_LOW + EQP_COSTUME_HEAD_MID,"EQP_COSTUME_HEAD_LOWMID" },
{EQP_COSTUME_HEAD_LOW + EQP_COSTUME_HEAD_TOP,"EQP_COSTUME_HEAD_LOWTOP" },
{EQP_COSTUME_HEAD_MID + EQP_COSTUME_HEAD_TOP,"EQP_COSTUME_HEAD_MIDTOP" },
{EQP_COSTUME_HEAD_LOW + EQP_COSTUME_HEAD_MID + EQP_COSTUME_HEAD_TOP ,"EQP_COSTUME_HEAD_ALL" },
{EQP_COSTUME_GARMENT ,"EQP_COSTUME_GARMENT" },
{EQP_AMMO ,"EQP_AMMO" },
{EQP_SHADOW_ARMOR ,"EQP_SHADOW_ARMOR" },
{EQP_SHADOW_WEAPON ,"EQP_SHADOW_WEAPON" },
{EQP_SHADOW_SHIELD ,"EQP_SHADOW_SHIELD" },
{EQP_SHADOW_SHOES ,"EQP_SHADOW_SHOES" },
{EQP_SHADOW_ACC_R ,"EQP_SHADOW_ACC_R" },
{EQP_SHADOW_ACC_L ,"EQP_SHADOW_ACC_L" }
};
// Look up the equip_id in the map
uint32 equip_item = (uint32)equipslot_id;
auto it = equipslot_map.find(equipslot_id);
if (it != equipslot_map.end()) {
return it->second.c_str(); // Return the corresponding str value
}
else {
ShowError("Unknown equipslot_id ID: %s\n", equipslot_id);
return "UNKNOWN"; // Return an error value if the equip ID is unknown
}
}
int convert_element_id_to_number(const std::string& element_name) {
static const std::unordered_map<std::string, int> element_map = {
{"ICE", 1},
{"EARTH", 2},
{"FIRE", 3},
{"WIND", 4},
};
auto it = element_map.find(element_name);
return (it != element_map.end()) ? it->second : 0;
}
const char* convert_element_id_to_str(uint8 element_id) {
static const std::unordered_map<uint8, std::string> element_map = {
{ 1 , "ICE"},
{ 2 , "EARTH"},
{ 3 , "FIRE"},
{ 4 , "WIND"},
};
auto it = element_map.find(element_id);
if (it != element_map.end()) {
return it->second.c_str(); // Return the corresponding str value
}
else {
ShowError("Unknown element_id ID: %s\n", element_id);
return "UNKNOWN"; // Return an error value if the element ID is unknown
}
}
// Function to convert job_id string to job number
int convert_weaponid_to_number(const char* weapon_id) {
// Create a map for the job ID strings to the corresponding e_job enum values
static const std::unordered_map<std::string, int> weapon_map = {
{"W_FIST", W_FIST}, //Bare hands
{"W_DAGGER", W_DAGGER}, //1
{"W_1HSWORD", W_1HSWORD}, //2
{"W_2HSWORD", W_2HSWORD}, //3
{"W_1HSPEAR", W_1HSPEAR}, //4
{"W_2HSPEAR", W_2HSPEAR}, //5
{"W_1HAXE", W_1HAXE}, //6
{"W_2HAXE", W_2HAXE}, //7
{"W_MACE", W_MACE}, //8
{"W_2HMACE", W_2HMACE}, //9 (unused)
{"W_STAFF", W_STAFF}, //10
{"W_BOW", W_BOW}, //11
{"W_KNUCKLE", W_KNUCKLE}, //12
{"W_MUSICAL", W_MUSICAL}, //13
{"W_WHIP", W_WHIP}, //14
{"W_BOOK", W_BOOK}, //15
{"W_KATAR", W_KATAR}, //16
{"W_REVOLVER", W_REVOLVER}, //17
{"W_RIFLE", W_RIFLE}, //18
{"W_GATLING", W_GATLING}, //19
{"W_SHOTGUN", W_SHOTGUN}, //20
{"W_GRENADE", W_GRENADE}, //21
{"W_HUUMA", W_HUUMA}, //22
{"W_2HSTAFF", W_2HSTAFF}, //23
{"MAX_WEAPON_TYPE", MAX_WEAPON_TYPE},
// dual-wield constants
{"W_DOUBLE_DD", W_DOUBLE_DD}, // 2 daggers
{"W_DOUBLE_SS", W_DOUBLE_SS}, // 2 swords
{"W_DOUBLE_AA", W_DOUBLE_AA}, // 2 axes
{"W_DOUBLE_DS", W_DOUBLE_DS}, // dagger + sword
{"W_DOUBLE_DA", W_DOUBLE_DA}, // dagger + axe
{"W_DOUBLE_SA", W_DOUBLE_SA}, // sword + axe
{"MAX_WEAPON_TYPE_ALL", MAX_WEAPON_TYPE_ALL},
{"W_SHIELD", W_SHIELD},
// Add other job mappings here as needed
};
// Look up the job_id in the map
std::string weapon_str = weapon_id;
// Convert to upper case
for (int index = 0; index < weapon_str.size(); index++) {
weapon_str.at(index) = toupper(weapon_str.at(index));
}
auto it = weapon_map.find(weapon_str);
if (it != weapon_map.end()) {
return it->second; // Return the corresponding enum value
}
else {
ShowError("Unknown weapon ID: %s\n", weapon_id);
return -1; // Return an error value if the weapon ID is unknown
}
}
void load_itemdbs()
{
try {
load_itemdb_id_to_aegisname();
load_itemdb_id_to_name();
load_itemdb_aegisname_to_id();
itemdbs_loaded = true;
ShowInfo("ItemDbs loaded successfully into cache...\n");
}
catch (json::parse_error& e) {
ShowError("Error parsing itemdbs: %s\n", e.what());
itemdbs_loaded = false;
}
}

@ -52,12 +52,19 @@ struct bot_inv_item {
// EQP_SHADOW_SHOES = 0x080000, // 524288
// EQP_SHADOW_ACC_R = 0x100000, // 1048576
// EQP_SHADOW_ACC_L = 0x200000, // 2097152
// NOTE: If Equip is on multiple slots their value is added together
// For example: 2 Hand Weapons have an equip value of 34 = Lefthand (32) + Righthand (2)
int identify; // state of identification | 1 = identified
uint8 refine; // state of refinement
uint8 refine; // state of refinement //Note: Shows the level of refinement (current max is +19)
uint8 attribute; // elemental attribute
// card fields represent inserted cards or other modifiers to an item (like very strong and link to creator,etc)
// Side note: When related to a crafted item, the following rules aply:
// card0 = 255 to indicate a crafted item
// card1 = element + ((star_crumbs * 5) << 8)
// card2 = char_id of creator (lower 16 bit of uint32) for example "char_id & 65535"
// card3 = char_id of creator (upper 16 bit of uint32) for example "char_id >> 16"
uint32 card0; // cardid of inserted card || special values for different modifiers possible
uint32 card1;
uint32 card2;
@ -180,9 +187,8 @@ struct InventoryDB {
};
void set_bot_inv_item_defaults(bot_inv_item& item);
uint32 convert_item_aegisname_to_id(const char* aegisname);
std::string convert_item_id_to_name(uint32 item_id);
std::string convert_item_id_to_aegisname(uint32 item_id);
void set_bot_equip_item_defaults(bot_inv_item& item);
void generate_crafted_item_attributes(bot_inv_item& item, uint32 inscriber_id, int element, int star_crumbs);
struct bot_item {
uint32 item_id;
@ -196,5 +202,17 @@ struct bot_item_bundle {
};
void prepare_item_bundle(bot_item_bundle& bundle, uint32 char_id, const nlohmann::json& character);
void prepare_equipment_bundle(bot_item_bundle& bundle, uint32 char_id, const nlohmann::json& character);
void dump_item_bundle(const bot_item_bundle& item_bundle);
uint32 convert_equipslot_id_to_number(const char* equipslot_id);
const char* convert_equipslot_id_to_str(const uint32 equipslot_id);
int convert_weaponid_to_number(const char* weapon_id);
int convert_element_id_to_number(const std::string& element_name);
const char* convert_element_id_to_str(uint8 element_id);
uint32 convert_item_aegisname_to_id(const char* aegisname);
std::string convert_item_id_to_name(uint32 item_id);
std::string convert_item_id_to_aegisname(uint32 item_id);
void load_itemdbs();
#endif /* HELPER_INVENTORY_HPP */

@ -872,7 +872,7 @@ void dump_skill_bundle(const bot_skill_bundle& skill_bundle) {
ShowDebug("End of skill bundle dump.\n");
}
void validate_skill_bundle(bot_skill_bundle& skill_bundle, const char* job_id) {
void validate_skill_bundle(bot_skill_bundle& skill_bundle, const char* job_id, bool debug_mode) {
ShowInfo("Validating skill bundle for job ID: %s...\n", job_id);
// Retrieve the skill tree for the specified job ID
@ -883,6 +883,8 @@ void validate_skill_bundle(bot_skill_bundle& skill_bundle, const char* job_id) {
return;
}
if (debug_mode) { ShowDebug("Polo1\n"); }
std::vector<bot_skill> valid_skills; // Temporary vector to store valid skills
for (const auto& skill : skill_bundle.skills) {
@ -892,16 +894,18 @@ void validate_skill_bundle(bot_skill_bundle& skill_bundle, const char* job_id) {
ShowWarning("Skill %s not found in the skill tree for job ID %s. Skipping.\n",convert_skill_id_to_str(skill.skill_id), job_id);
continue;
}
if (debug_mode) { ShowDebug("Polo2\n"); }
std::shared_ptr<bot_skill_tree_entry> skill_entry = it->second;
bool requirements_met = true; // Flag to track if requirements are met
if (debug_mode) { ShowDebug("Polo3\n"); }
// Check required skills
for (const auto& required_skill : skill_entry->need) {
uint16 required_skill_id = required_skill.first;
uint16 required_skill_level = required_skill.second;
bool found = false;
if (debug_mode) { ShowDebug("Polo4\n"); }
// Search for the required skill in the bundle
for (const auto& bundled_skill : skill_bundle.skills) {
if (bundled_skill.skill_id == required_skill_id && bundled_skill.level >= required_skill_level) {
@ -911,12 +915,12 @@ void validate_skill_bundle(bot_skill_bundle& skill_bundle, const char* job_id) {
}
if (!found) {
//ShowDebug("Skill ID %s requires skill ID %d with minimum level %d, but it is not found in the bundle.\n",
// convert_skill_id_to_str(skill.skill_id), required_skill_id, required_skill_level);
if (debug_mode) { ShowDebug("Skill ID %s requires skill ID %d with minimum level %d, but it is not found in the bundle.\n", convert_skill_id_to_str(skill.skill_id), required_skill_id, required_skill_level); }
requirements_met = false;
break;
}
}
if (debug_mode) { ShowDebug("Polo5\n"); }
if (requirements_met) {
//ShowDebug("Skill ID %s validated successfully.\n", convert_skill_id_to_str(skill.skill_id));
@ -926,6 +930,7 @@ void validate_skill_bundle(bot_skill_bundle& skill_bundle, const char* job_id) {
ShowWarning("Skill ID %s does not meet requirements and will be removed from the bundle.\n", convert_skill_id_to_str(skill.skill_id));
}
}
if (debug_mode) { ShowDebug("Polo6\n"); }
// Update the skill bundle with valid skills
skill_bundle.skills = std::move(valid_skills);
@ -933,10 +938,12 @@ void validate_skill_bundle(bot_skill_bundle& skill_bundle, const char* job_id) {
//ShowStatus("Skill bundle validation completed for job ID: %s.\n", job_id);
}
void prepare_skill_bundle(bot_skill_bundle& bundle, uint32 char_id, const nlohmann::json& character) {
void prepare_skill_bundle(bot_skill_bundle& bundle, uint32 char_id, const nlohmann::json& character, bool debug_mode) {
//Select for the used profile
std::string skill_profile = character["char_skills_profile"];
//if (debug_mode) { ShowDebug("Marco\n"); }
if (skill_profile == "CUSTOM") {
if (!character.contains("skills")) {
throw std::runtime_error("skills data is missing for CUSTOM skills profile.");
@ -980,6 +987,8 @@ void prepare_skill_bundle(bot_skill_bundle& bundle, uint32 char_id, const nlohma
std::string job_profile_file_path = "conf/bot_profiles/skill_profiles/" + job_str + "_skills_profile.json";
nlohmann::json json_skill_profiles;
//if (debug_mode) { ShowDebug("Polo1\n"); }
// Try to Open skill Profile
std::ifstream skill_profile_file(job_profile_file_path);
if (!skill_profile_file.is_open()) {
@ -990,7 +999,7 @@ void prepare_skill_bundle(bot_skill_bundle& bundle, uint32 char_id, const nlohma
throw std::runtime_error("Failed to open both job and default profiles.");
}
}
//if (debug_mode) { ShowDebug("Polo2\n"); }
// Parse Profile Data
try {
skill_profile_file >> json_skill_profiles;
@ -999,7 +1008,7 @@ void prepare_skill_bundle(bot_skill_bundle& bundle, uint32 char_id, const nlohma
throw std::runtime_error("Error parsing build profile JSON: " + std::string(e.what()));
}
//if (debug_mode) { ShowDebug("Polo3\n"); }
if (!json_skill_profiles.contains(skill_profile)) {
// If build profile is not found, default to INIT
skill_profile = "INIT";
@ -1009,7 +1018,7 @@ void prepare_skill_bundle(bot_skill_bundle& bundle, uint32 char_id, const nlohma
//Initialize the skill bundle
bundle.skills.clear();
//if (debug_mode) { ShowDebug("Polo4\n"); }
// Iterate over the skills in the JSON
for (const auto& skill : profile.items()) {
std::string skill_name = skill.key();
@ -1033,8 +1042,10 @@ void prepare_skill_bundle(bot_skill_bundle& bundle, uint32 char_id, const nlohma
new_skill.level = static_cast<uint16>(skill_level);
new_skill.flag = 0; // Default: 0 for permanent
bundle.skills.push_back(new_skill);
//if (debug_mode) { ShowDebug("Skill %s added to bundle.\n", skill_name.c_str()); }
}
//dump_skill_bundle(bundle);
if (debug_mode) { dump_skill_bundle(bundle);}
}
}
@ -1580,7 +1591,7 @@ const char* convert_skill_id_to_str(int skill_id) {
{SL_SKE ,"SL_SKE" },
{SL_SKA ,"SL_SKA" },
//Seams like quest skills for 3rd classes
//Seams like quest skills for 3rd classes (Trans-3rd Skills)
{SM_SELFPROVOKE ,"SM_SELFPROVOKE" },
{ NPC_EMOTION_ON ,"NPC_EMOTION_ON" },
{ ST_PRESERVE ,"ST_PRESERVE" },

@ -154,7 +154,7 @@ public:
extern BotSkillTreeDatabase bot_skill_tree_db;
void init_bot_skill_tree_db();
void validate_skill_bundle(bot_skill_bundle& skill_bundle, const char* job_id);
void prepare_skill_bundle(bot_skill_bundle& bundle, uint32 char_id, const nlohmann::json& character);
void validate_skill_bundle(bot_skill_bundle& skill_bundle, const char* job_id, bool debug_mode=false);
void prepare_skill_bundle(bot_skill_bundle& bundle, uint32 char_id, const nlohmann::json& character, bool debug_mode=false);
#endif /* HELPER_SKILLS_HPP */

Loading…
Cancel
Save