Refactor + fix skill validation

main
Alex 1 day ago
parent 73fe6e48f6
commit 657ff9f35d

@ -115,6 +115,7 @@
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<AdditionalIncludeDirectories>$(SolutionDir)src;$(SolutionDir)3rdparty\rapidyaml\src;$(SolutionDir)3rdparty\rapidyaml\ext\c4core\src;$(SolutionDir)3rdparty\libconfig\;$(SolutionDir)src\login;$(SolutionDir)src\map;$(SolutionDir)src\char;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>$(SolutionDir)src;$(SolutionDir)3rdparty\rapidyaml\src;$(SolutionDir)3rdparty\rapidyaml\ext\c4core\src;$(SolutionDir)3rdparty\libconfig\;$(SolutionDir)src\login;$(SolutionDir)src\map;$(SolutionDir)src\char;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation> <MultiProcessorCompilation>true</MultiProcessorCompilation>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
@ -144,6 +145,7 @@
<ClCompile Include="brokk.cpp" /> <ClCompile Include="brokk.cpp" />
<ClCompile Include="charakters.cpp" /> <ClCompile Include="charakters.cpp" />
<ClCompile Include="char_creation_helpers.cpp" /> <ClCompile Include="char_creation_helpers.cpp" />
<ClCompile Include="helper_inventory.cpp" />
<ClCompile Include="helper_skills.cpp" /> <ClCompile Include="helper_skills.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -152,6 +154,7 @@
<ClInclude Include="brokk.hpp" /> <ClInclude Include="brokk.hpp" />
<ClInclude Include="charakters.hpp" /> <ClInclude Include="charakters.hpp" />
<ClInclude Include="char_creation_helpers.hpp" /> <ClInclude Include="char_creation_helpers.hpp" />
<ClInclude Include="helper_inventory.hpp" />
<ClInclude Include="helper_skills.hpp" /> <ClInclude Include="helper_skills.hpp" />
<ClInclude Include="json.hpp" /> <ClInclude Include="json.hpp" />
</ItemGroup> </ItemGroup>

@ -33,6 +33,9 @@
<ClCompile Include="helper_skills.cpp"> <ClCompile Include="helper_skills.cpp">
<Filter>Quelldateien</Filter> <Filter>Quelldateien</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="helper_inventory.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="brokk.hpp"> <ClInclude Include="brokk.hpp">
@ -56,5 +59,8 @@
<ClInclude Include="helper_skills.hpp"> <ClInclude Include="helper_skills.hpp">
<Filter>Headerdateien</Filter> <Filter>Headerdateien</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="helper_inventory.hpp">
<Filter>Headerdateien</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
</Project> </Project>

@ -41,6 +41,7 @@
#include "char_creation_helpers.hpp" #include "char_creation_helpers.hpp"
#include "helper_skills.hpp" #include "helper_skills.hpp"
#include "helper_inventory.hpp"
using namespace rathena; using namespace rathena;
using namespace rathena::server_brokk; using namespace rathena::server_brokk;
@ -54,11 +55,13 @@ AccountDB* login_accounts = nullptr;
BotAccountDB* bot_accounts = nullptr; BotAccountDB* bot_accounts = nullptr;
CharDB* charakters = nullptr; CharDB* charakters = nullptr;
SkillDB* skills = nullptr; SkillDB* skills = nullptr;
InventoryDB* inventory = nullptr;
// YAML DB Objects of all touched infrastucture // YAML DB Objects of all touched infrastucture
BotStatPointDatabase bot_statpoint_db; BotStatPointDatabase bot_statpoint_db;
BotJobDatabase bot_job_db; BotJobDatabase bot_job_db;
//BotSkillTreeDatabase bot_skill_tree_db; //BotSkillTreeDatabase bot_skill_tree_db;
//BotItemDatabase bot_item_db;
// Advanced subnet check [LuzZza] // Advanced subnet check [LuzZza]
struct s_subnet { struct s_subnet {
@ -166,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 // Check if the bot account already exists
if (bot_accounts->load_num(bot_accounts, &bot_acc, acc.account_id)) { if (bot_accounts->load_num(bot_accounts, &bot_acc, acc.account_id)) {
ShowNotice("Attempt to create an already existing bot account (account: %s, sex: %c)\n", acc.account_id, sex); ShowWarning("Attempt to create an already existing bot account (account: %s, sex: %c). Skipping...\n", acc.account_id, sex);
return 1; // Account exists return 1; // Account exists
} }
@ -185,7 +188,7 @@ int brokk_mmo_auth_new(const char* userid, const char* pass, const char sex, con
if (!bot_accounts->create(bot_accounts, &bot_acc)) if (!bot_accounts->create(bot_accounts, &bot_acc))
return 0; // Account creation failed return 0; // Account creation failed
ShowNotice("Bot account creation successful (account %s, id: %d, sex: %c)\n", acc.userid, acc.account_id, acc.sex); ShowStatus("Bot account creation successful (account %s, id: %d, sex: %c)\n", acc.userid, acc.account_id, acc.sex);
// Registration rate limit logic // Registration rate limit logic
if (DIFF_TICK(tick, new_reg_tick) > 0) { if (DIFF_TICK(tick, new_reg_tick) > 0) {
@ -248,8 +251,10 @@ int brokk_mmo_auth_delete_num(uint32 account_id) {
struct mmo_bot_account bot_acc; struct mmo_bot_account bot_acc;
struct mmo_charakter charakter; struct mmo_charakter charakter;
struct bot_skill skill; struct bot_skill skill;
struct bot_inv_item item;
std::vector<uint32> char_ids_to_delete; std::vector<uint32> char_ids_to_delete;
std::vector<bot_skill> skills_to_delete; std::vector<bot_skill> skills_to_delete;
std::vector<bot_inv_item> items_to_delete;
// Check if the account exists // Check if the account exists
if (!login_accounts->load_num(login_accounts, &acc, account_id)) { if (!login_accounts->load_num(login_accounts, &acc, account_id)) {
@ -289,17 +294,15 @@ int brokk_mmo_auth_delete_num(uint32 account_id) {
} }
} }
iter_chars->destroy(iter_chars); // Destroy the iterator after use iter_chars->destroy(iter_chars); // Destroy the iterator after use
ShowDebug("DelMarco\n");
// Second pass: Delete the collected charakters // Second pass: Delete the collected charakters
for (const auto& char_id : char_ids_to_delete) { for (const auto& char_id : char_ids_to_delete) {
ShowDebug("DelPolo1\n");
ShowInfo("Deleting charakter with ID %d.\n", char_id); ShowInfo("Deleting charakter with ID %d.\n", char_id);
if (!charakters->remove(charakters, char_id)) { if (!charakters->remove(charakters, char_id)) {
ShowError("Failed to delete charakter (char_id: %s)\n", char_id); ShowError("Failed to delete charakter (char_id: %s)\n", char_id);
continue; // Deletion failed continue; // Deletion failed
} }
ShowDebug("DelPolo2\n");
// Step 3B: Attempt to delete skills of the char (if any) // Step 3B: Attempt to delete skills of the char (if any)
// First pass: Collect skill_ids to delete based on config // First pass: Collect skill_ids to delete based on config
SkillDBIterator* iter_skills = skills->iterator(skills, char_id); // Create the iterator SkillDBIterator* iter_skills = skills->iterator(skills, char_id); // Create the iterator
@ -309,19 +312,48 @@ int brokk_mmo_auth_delete_num(uint32 account_id) {
skills_to_delete.push_back(skill); skills_to_delete.push_back(skill);
} }
iter_skills->destroy(iter_skills); // Destroy the iterator after use iter_skills->destroy(iter_skills); // Destroy the iterator after use
ShowDebug("DelPolo3\n");
// Second pass: Delete the collected skills // Second pass: Delete the collected skills
for (const auto& skill_entry : skills_to_delete) { int skill_count = 0;
for (const auto& skill_entry : skills_to_delete) {
//ShowInfo("Deleting skill with ID %d.\n", char_id); //ShowInfo("Deleting skill with ID %d.\n", char_id);
if (!skills->remove(skills, char_id, skill_entry.skill_id)) { if (!skills->remove(skills, char_id, skill_entry.skill_id)) {
ShowError("Failed to delete skill (skill_id: %s)\n", skill_entry.skill_id); ShowError("Failed to delete skill (skill_id: %s)\n", skill_entry.skill_id);
continue; // Deletion failed continue; // Deletion failed
} }
else { else {
ShowNotice("Deleting of skill %s from char_id %d was successful\n", convert_skill_id_to_str(skill_entry.skill_id), char_id); //ShowNotice("Deleting of skill %s from char_id %d was successful\n", convert_skill_id_to_str(skill_entry.skill_id), char_id);
skill_count++;
} }
} }
ShowDebug("DelPolo4\n");
ShowNotice("Skill cleanup for char_id %d completed. %d skills cleaned.\n", char_id, skill_count);
ShowDebug("Marco\n"); //TODO Prüfn warum hier iterator keine validen items wirft
// Step 3C: Attempt to delete inventory of the char (if any)
// First pass: Collect inv_item_ids to delete based on config
InventoryDBIterator* iter_inventory = inventory->iterator(inventory, char_id); // Create the iterator
ShowDebug("Polo1\n");
while (iter_inventory->next(iter_inventory, char_id, &item)) {
ShowDebug("Polo iter loop\n");
std::string id_str = convert_item_id_to_name(item.item_id);
ShowDebug("Item %s found. Schedule for deletion...\n", id_str.c_str());
items_to_delete.push_back(item);
}
iter_inventory->destroy(iter_inventory); // Destroy the iterator after use
ShowDebug("Polo2\n");
// Second pass: Delete the collected items
for (const auto& inv_item_entry : items_to_delete) {
ShowInfo("Deleting item with ID %d.\n", inv_item_entry.inventory_id);
if (!inventory->remove(inventory, char_id, inv_item_entry.inventory_id)) {
ShowError("Failed to delete item (item_id: %s)\n", inv_item_entry.item_id);
continue; // Deletion failed
}
else {
ShowNotice("Deleting of item %s from char_id %d was successful\n", convert_item_id_to_aegisname(inv_item_entry.item_id), char_id);
}
}
ShowDebug("Polo3\n");
} }
ShowNotice("Bot account deletion completed (account: %s, id: %d)\n\n", acc.userid, acc.account_id); ShowNotice("Bot account deletion completed (account: %s, id: %d)\n\n", acc.userid, acc.account_id);
@ -563,6 +595,18 @@ bool brokk_initialize_dbs()
} }
} }
// Inventory database init (inventory table)
if (inventory == nullptr) {
ShowFatalError("do_init: inventory engine not found.\n");
return false;
}
else {
if (!inventory->init(inventory)) {
ShowFatalError("do_init: Failed to initialize inventory engine.\n");
return false;
}
}
return true; return true;
} }
@ -636,7 +680,7 @@ bool brokk_create_character(mmo_charakter& charakter, int& char_id, const bool a
} }
// Log success // Log success
ShowNotice("Character creation successful (name: %s, char_id: %d, sex: %c)\n", charakter.name, charakter.char_id, charakter.sex); ShowStatus("Character creation successful (name: %s, char_id: %d, sex: %c)\n", charakter.name, charakter.char_id, charakter.sex);
// Update registration limit // Update registration limit
if (DIFF_TICK(tick, new_reg_tick) > 0) { if (DIFF_TICK(tick, new_reg_tick) > 0) {
@ -675,6 +719,13 @@ int brokk_teach_skills(bot_skill_bundle& skill_bundle, int& char_id) {
std::string char_name = existing_char.name; std::string char_name = existing_char.name;
// TODO Define logic to handle session (on runtinme) added skills
// Those are added into the loaded skill tree on runtime by this function "void clif_addskill(map_session_data *sd, int skill_id)"
// A) There either needs to be a way to dynamicly change/create new skill trees depending on the demand from profiles (could be problematic because client code would be involved
// or
// B) There needs to be a way for brokk to communicate to sindri later, to tell sindri to add skills dynamicly to the skill tree of the logged in char
// Bottom line: Post-Pone decition for first polish iteration
for (auto& skill : skill_bundle.skills) { for (auto& skill : skill_bundle.skills) {
// Create entry in the skill database // Create entry in the skill database
@ -684,7 +735,7 @@ int brokk_teach_skills(bot_skill_bundle& skill_bundle, int& char_id) {
} }
// Log success single skill // Log success single skill
ShowNotice("Skill %s added to character char_id: %d)\n", convert_skill_id_to_str(skill.skill_id), skill.char_id); //ShowNotice("Skill %s added to character char_id: %d)\n", convert_skill_id_to_str(skill.skill_id), skill.char_id);
skill_count++; skill_count++;
} }
@ -694,11 +745,50 @@ int brokk_teach_skills(bot_skill_bundle& skill_bundle, int& char_id) {
return -1; // Success return -1; // Success
} }
/**
* Add an item to a charakters bundle in the database.
* @param item: Pre-configured bot_item object with all necessary fields populated.
* @return :
* -1: success
* 1: unregistered id (invalid char_id)
* 2: Error while writing into inventory table
*/
int brokk_apply_item_loadout(bot_item_bundle& item_bundle, int& char_id) {
int item_count = 0;
// Check if the character exists
mmo_charakter existing_char;
if (!charakters->load_num(charakters, &existing_char, char_id)) {
ShowError("Attempt to add an item to non-existing character (char_id: %d)\n", char_id);
return 1; // Character does not exist
}
std::string char_name = existing_char.name;
for (auto& item : item_bundle.items) {
// Create entry in the inventory database
if (!inventory->create(inventory, &item)) {
ShowError("Failed to create inventory entry (char_id: %d, item_id: %d)\n", item.char_id, item.item_id);
return 2; // inventory entry creation failed
}
// Log success single item
std::string aegisname = convert_item_id_to_aegisname(item.item_id);
ShowNotice("Item %s added to character char_id: %d)\n", aegisname.c_str(), item.char_id);
item_count++;
}
// Log success
ShowStatus("Successfully added %d Items to characters inventory (name: %s, char_id: %d)\n", item_count, char_name.c_str(), char_id);
return -1; // Success
}
void BrokkServer::finalize() { void BrokkServer::finalize() {
AccountDB* db = login_accounts; AccountDB* db = login_accounts;
BotAccountDB* bot_db = bot_accounts; BotAccountDB* bot_db = bot_accounts;
CharDB* char_db = charakters; CharDB* char_db = charakters;
SkillDB* skill_db = skills; SkillDB* skill_db = skills;
InventoryDB* inventory_db = inventory;
//brokk_log(0, "brokk server", 100, "brokk server shutdown"); //brokk_log(0, "brokk server", 100, "brokk server shutdown");
ShowStatus("Terminating...\n"); ShowStatus("Terminating...\n");
@ -728,10 +818,16 @@ void BrokkServer::finalize() {
skill_db = nullptr; skill_db = nullptr;
} }
if (inventory_db) { // destroy inventory engine
inventory_db->destroy(inventory_db);
inventory_db = nullptr;
}
login_accounts = nullptr; // destroyed in account_engine login_accounts = nullptr; // destroyed in account_engine
bot_accounts = nullptr; // destroyed in bot account_engine bot_accounts = nullptr; // destroyed in bot account_engine
charakters = nullptr;// destroyed in charakter engine charakters = nullptr;// destroyed in charakter engine
skills = nullptr; //destroy in skills engine skills = nullptr; //destroy in skills engine
inventory = nullptr; //destroy in inventory engine
if (brokk_fd != -1) if (brokk_fd != -1)
{ {
@ -761,6 +857,7 @@ bool BrokkServer::initialize(int argc, char* argv[]) {
bot_accounts = bot_account_db_sql(); bot_accounts = bot_account_db_sql();
charakters = char_db_sql(); charakters = char_db_sql();
skills = skill_db_sql(); skills = skill_db_sql();
inventory = inventory_db_sql();
// Initialize Yaml-DBs // Initialize Yaml-DBs
bot_read_yaml_dbs(); bot_read_yaml_dbs();
@ -790,7 +887,6 @@ bool BrokkServer::initialize(int argc, char* argv[]) {
/* /*
//Demo-Skill tree //Demo-Skill tree
//TODO Eintragen in Job-Profil -> welche Skills gelernt werden sollen und Mechanik bauen für plausibles auto-skill erlernen
std::shared_ptr<bot_skill_tree> tree = bot_skill_tree_db.find(JOB_MAGE); std::shared_ptr<bot_skill_tree> tree = bot_skill_tree_db.find(JOB_MAGE);
for (const auto& skill : tree->skills){ for (const auto& skill : tree->skills){
ShowDebug("Skill Found in tree: %s \n",convert_skill_id_to_str(skill.first)); ShowDebug("Skill Found in tree: %s \n",convert_skill_id_to_str(skill.first));
@ -833,7 +929,7 @@ bool BrokkServer::initialize(int argc, char* argv[]) {
// Close file after done with it // Close file after done with it
bot_accounts_file.close(); bot_accounts_file.close();
ShowStatus("Starting to create bot accounts...\n"); ShowInfo("Starting to create bot accounts...\n");
// Iterate over each bot entry in the JSON file // Iterate over each bot entry in the JSON file
for (const auto& bot : json_bot_accounts) { for (const auto& bot : json_bot_accounts) {
@ -843,7 +939,7 @@ bool BrokkServer::initialize(int argc, char* argv[]) {
const char sex = bot["sex"].get<std::string>()[0]; // Extract single char from string const char sex = bot["sex"].get<std::string>()[0]; // Extract single char from string
// Debug: Print the entire bot entry // Debug: Print the entire bot entry
//ShowInfo("Processing bot: %s\n", bot.dump(4).c_str()); ShowInfo("Processing bot account: %s ...\n", userid_str.c_str());
// Debug: Iterate over the keys in the bot object and print them // Debug: Iterate over the keys in the bot object and print them
//for (auto it = bot.begin(); it != bot.end(); ++it) { //for (auto it = bot.begin(); it != bot.end(); ++it) {
@ -873,13 +969,13 @@ bool BrokkServer::initialize(int argc, char* argv[]) {
const auto& json_characters = bot["characters"]; const auto& json_characters = bot["characters"];
//ShowDebug("StartChar Parsing"); //ShowDebug("StartChar Parsing");
for (const auto& json_character : json_characters) { //Iterate every declared character and try to create it for (const auto& json_character : json_characters) { //Iterate every declared character and try to create it
//ShowDebug("char_json:\n%s", character.dump().c_str());
mmo_charakter new_char{}; mmo_charakter new_char{};
//Step1 Meta-Data about character (allways have to be present/not bound to profile) //Step1 Meta-Data about character (allways have to be present/not bound to profile)
const std::string char_name = json_character["name"]; const std::string char_name = json_character["name"];
safestrncpy(new_char.name, char_name.c_str(), NAME_LENGTH); safestrncpy(new_char.name, char_name.c_str(), NAME_LENGTH);
ShowInfo("Processing bot character %s ...\n", char_name.c_str());
std::string sex_str = json_character["sex"]; std::string sex_str = json_character["sex"];
new_char.sex = sex_str[0]; new_char.sex = sex_str[0];
@ -888,21 +984,25 @@ bool BrokkServer::initialize(int argc, char* argv[]) {
new_char.inventory_slots = 100; new_char.inventory_slots = 100;
// Decode Job Value // Decode Job Value
std::string job_id = json_character["start_job"]; std::string job_id_str = json_character["start_job"];
new_char.class_ = convert_jobid_to_number(job_id.c_str()); int job_id = convert_jobid_to_number(job_id_str.c_str());
new_char.class_ = job_id;
if (new_char.class_ == -1) { if (new_char.class_ == -1) {
job_id = "JOB_NOVICE"; job_id_str = "JOB_NOVICE";
new_char.class_ = JOB_NOVICE; new_char.class_ = JOB_NOVICE;
} }
// Step2: build data of character // Step2: build data of character
new_char.account_id = account_id; new_char.account_id = account_id;
new_char.char_id = -1; new_char.char_id = -1;
ShowInfo("Preparing build data for bot character %s ...\n", char_name.c_str());
prepare_character_build_data(new_char, json_character, bot_job_db); prepare_character_build_data(new_char, json_character, bot_job_db);
// Step3: appearance of character // Step3: appearance of character
ShowInfo("Preparing appearance of bot character %s ...\n", char_name.c_str());
prepare_character_appearance_data(new_char, json_character); prepare_character_appearance_data(new_char, json_character);
// Step4: location data of character // Step4: location data of character
ShowInfo("Preparing location data for bot character %s ...\n", char_name.c_str());
prepare_character_location_data(new_char, json_character); prepare_character_location_data(new_char, json_character);
// All char table related data is collected and can be saved to allow further steps (skills/items/equip) // All char table related data is collected and can be saved to allow further steps (skills/items/equip)
@ -924,14 +1024,14 @@ bool BrokkServer::initialize(int argc, char* argv[]) {
ShowError("Character creation for '%s 'failed: %s\n",new_char.name, e.what()); ShowError("Character creation for '%s 'failed: %s\n",new_char.name, e.what());
continue; continue;
} }
//TODO Ab hier debuggen warum bei EvaExample bei neuanlage crashed
// Step5: Skills // Step5: Skills
// Prepare skills inside a skill bundle // Prepare skills inside a skill bundle
ShowInfo("Preparing skill data of bot character %s ...\n", char_name.c_str());
bot_skill_bundle skill_bundle{}; bot_skill_bundle skill_bundle{};
prepare_skill_bundle(skill_bundle, new_char_id, json_character); prepare_skill_bundle(skill_bundle, new_char_id, json_character);
validate_skill_bundle(skill_bundle, job_id_str.c_str());
// All skill table related data is collected and can be saved // All skill table related data is collected and can be saved
try { try {
@ -945,18 +1045,34 @@ bool BrokkServer::initialize(int argc, char* argv[]) {
continue; continue;
} }
ShowNotice("Skills successfully teached to character (name: %s, char_id: %d, sex: %c)\n", new_char.name, new_char.char_id, new_char.sex); // ShowNotice("Skills successfully teached to character (name: %s, char_id: %d, sex: %c)\n", new_char.name, new_char.char_id, new_char.sex);
//Step4: Items
//TODO Testweise folgende ItemIDs einbauen für Debug:
// 1950, 2627 , 4040
//ShowNotice("Items successfully handed out to character (name: %s, char_id: %d, sex: %c)\n", new_char.name, new_char.char_id, new_char.sex);
//Step5: Equipment // Step6: Items
// Prepare items inside a item bundle
ShowInfo("Preparing inventory data for bot character %s ...\n", char_name.c_str());
//ShowNotice("Gear/Weapons successfully equiped (name: %s, char_id: %d, sex: %c)\n", new_char.name, new_char.char_id, new_char.sex); bot_item_bundle item_bundle{};
prepare_item_bundle(item_bundle, new_char_id, json_character);
// All inventory table related data is collected and can be saved
try {
/* Lock-out while in debug
if (!brokk_apply_item_loadout(item_bundle, new_char_id)) {
ShowError("Failed to create inventory entry for: %s\n", new_char.name);
continue;
}*/
}
catch (const std::exception& e) {
ShowError("skill creation for '%s 'failed: %s\n", new_char.name, e.what());
continue;
}
ShowStatus("Items successfully handed out to character (name: %s, char_id: %d, sex: %c)\n", 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", new_char.name, new_char.char_id, new_char.sex); //Step5: Equipment
//ShowInfo("Preparing equipment for bot character %s ...\n", char_name.c_str());
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);
/***** /*****
* Validating/Applying skill build * Validating/Applying skill build

@ -14,6 +14,7 @@
#include "charakters.hpp" #include "charakters.hpp"
#include "char_creation_helpers.hpp" #include "char_creation_helpers.hpp"
#include "helper_skills.hpp" #include "helper_skills.hpp"
#include "helper_inventory.hpp"
using rathena::server_core::Core; using rathena::server_core::Core;
using rathena::server_core::e_core_type; using rathena::server_core::e_core_type;
@ -70,7 +71,7 @@ void bot_account_cleanup();
bool brokk_initialize_dbs(); bool brokk_initialize_dbs();
bool brokk_create_character(mmo_charakter& charakter, int& char_id, const bool auto_generated); bool brokk_create_character(mmo_charakter& charakter, int& char_id, const bool auto_generated);
int brokk_teach_skills(bot_skill_bundle& skill_bundle, int& char_id); int brokk_teach_skills(bot_skill_bundle& skill_bundle, int& char_id);
//int brokk_add_item(container_struct& container); int brokk_apply_item_loadout(bot_item_bundle& item_bundle, int& char_id);
//int brokk_apply_equip(container_struct& container); //int brokk_apply_equip(container_struct& container);

@ -510,7 +510,7 @@ static bool bot_skill_db_tosql(SkillDB_SQL* db, const struct bot_skill* skill, c
#endif #endif
db->skill_db, skill->char_id, skill->skill_id) db->skill_db, skill->char_id, skill->skill_id)
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_INT, (void*)skill->level, sizeof(skill->level)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_INT, (void*)skill->level, sizeof(skill->level))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_INT, (void*)skill->flag, sizeof(skill->flag)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_SHORT, (void*)skill->flag, sizeof(skill->flag))
#ifdef VIP_ENABLE #ifdef VIP_ENABLE
#endif #endif
|| SQL_SUCCESS != SqlStmt_Execute(stmt) || SQL_SUCCESS != SqlStmt_Execute(stmt)
@ -774,60 +774,7 @@ uint64 BotSkillTreeDatabase::parseBodyNode(const ryml::NodeRef& node) {
return true; return true;
} }
/*
* Checks if the required skills to a skill are met
*/
bool check_skill_requirements(int job_id, int skill_id, bot_skill_bundle& skill_bundle) {
if (is_skill_validation_exception(job_id, skill_id)) {
// Skill is exception for this job -> is not validated
std::string skill_name = convert_skill_id_to_str(skill_id);
ShowNotice("Skill %s is marked as exception to this job (%d)\n",skill_name.c_str(), job_id);
return true;
}
std::shared_ptr<bot_skill_tree> tree = bot_skill_tree_db.find(job_id);
if (!tree) {
ShowError("Skill ID %d (%s) not found in bot_skill_tree_db\n", skill_id, convert_skill_id_to_str(skill_id));
return false;
}
// Check the size of skills before iterating
if (tree->skills.empty()) {
ShowError("Skill tree for ID %d is empty\n", skill_id);
return false;
}
for (const auto& skill : tree->skills) {
//ShowDebug("Skill Found in tree: %s \n", convert_skill_id_to_str(skill.first));
if (skill.first == skill_id) {
//ShowDebug("skill max lvl : %d \n", skill.second->max_lv);
bool requirements_met = true;
for (const auto needed_skill : skill.second->need) {
//ShowDebug("requires skill %s on level %d\n", convert_skill_id_to_str(needed_skill.first), needed_skill.second);
bool skill_found = false;
for (const auto bundled_skill : skill_bundle.skills) {
if (needed_skill.first == bundled_skill.skill_id && needed_skill.second <= bundled_skill.level) {
// Skill must be in bundle and at least smaller or same level as required
skill_found = true;
break;
}
}
if (!skill_found) {
requirements_met = false;
break;
}
}
if (requirements_met) {
ShowDebug("All skill requirements fullfilled");
return true; //no missing skills, requirements are met
}
}
}
ShowDebug("One or more skill requirements not fullfilled");
return false; //Skill_bundle misses at least a required skill
}
void init_bot_skill_tree_db() { void init_bot_skill_tree_db() {
bot_skill_tree_db.reload(); bot_skill_tree_db.reload();
@ -924,20 +871,83 @@ std::unordered_map<uint16, uint16> parse_skill_bundle(const nlohmann::json& skil
return parsed_skill_bundle; return parsed_skill_bundle;
} }
bool validate_skills(uint16 start_job, bot_skill_bundle& skill_bundle) { void dump_skill_bundle(const bot_skill_bundle& skill_bundle) {
ShowDebug("Dumping skill bundle contents...\n");
if (skill_bundle.skills.empty()) {
ShowDebug("Skill bundle is empty.\n");
return;
}
for (const auto& skill : skill_bundle.skills) {
ShowDebug("Character ID: %u\n", skill.char_id);
ShowDebug(" Skill ID: %d\n", skill.skill_id);
std::string skill_name = convert_skill_id_to_str(skill.skill_id);
ShowDebug(" Skill Name: %s\n", skill_name.c_str());
ShowDebug(" Skill Level: %u\n", skill.level);
ShowDebug(" Flag: %d\n", skill.flag);
}
ShowDebug("End of skill bundle dump.\n");
}
void validate_skill_bundle(bot_skill_bundle& skill_bundle, const char* job_id) {
ShowInfo("Validating skill bundle for job ID: %s...\n", job_id);
// Retrieve the skill tree for the specified job ID
std::shared_ptr<bot_skill_tree> tree = bot_skill_tree_db.find(convert_jobid_to_number(job_id));
if (!tree) {
ShowWarning("Job ID %s not found in the skill tree database. Skipping validation.\n", job_id);
return;
}
std::vector<bot_skill> valid_skills; // Temporary vector to store valid skills
for (const auto& skill : skill_bundle.skills) { for (const auto& skill : skill_bundle.skills) {
int skill_id = skill.skill_id; // Look up the skill in the job-specific skill tree
uint16 level = skill.level; auto it = tree->skills.find(skill.skill_id);
if (it == tree->skills.end()) {
// Check requirements 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);
if (!check_skill_requirements(start_job, skill_id, skill_bundle)) { continue;
std::string skill_name = convert_skill_id_to_str(skill_id); }
ShowError("Skill %s(%d) at level %d does not meet requirements\n", skill_name.c_str(),skill_id, level);
return false; // Requirements not met std::shared_ptr<bot_skill_tree_entry> skill_entry = it->second;
bool requirements_met = true; // Flag to track if requirements are met
// 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;
// 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) {
found = true;
break;
}
}
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);
requirements_met = false;
break;
}
}
if (requirements_met) {
//ShowDebug("Skill ID %s validated successfully.\n", convert_skill_id_to_str(skill.skill_id));
valid_skills.push_back(skill); // Add the valid skill to the temporary vector
}
else {
ShowWarning("Skill ID %s does not meet requirements and will be removed from the bundle.\n", convert_skill_id_to_str(skill.skill_id));
} }
} }
return true; // Update the skill bundle with valid skills
skill_bundle.skills = std::move(valid_skills);
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) {
@ -968,7 +978,7 @@ void prepare_skill_bundle(bot_skill_bundle& bundle, uint32 char_id, const nlohma
std::string job_str = character["start_job"]; std::string job_str = character["start_job"];
uint16 related_job = convert_jobid_to_number(job_str.c_str()); uint16 related_job = convert_jobid_to_number(job_str.c_str());
validate_skills(related_job, bundle); //validate_skills(related_job, bundle);
// Add the skill to the skill bundle // Add the skill to the skill bundle
bot_skill new_skill{}; bot_skill new_skill{};
@ -978,7 +988,7 @@ void prepare_skill_bundle(bot_skill_bundle& bundle, uint32 char_id, const nlohma
new_skill.flag = 0; // Default: 0 for permanent new_skill.flag = 0; // Default: 0 for permanent
bundle.skills.push_back(new_skill); bundle.skills.push_back(new_skill);
} }
//dump_skill_bundle(bundle);
} }
else { else {
// Load skill Profile // Load skill Profile
@ -990,7 +1000,7 @@ void prepare_skill_bundle(bot_skill_bundle& bundle, uint32 char_id, const nlohma
// Try to Open skill Profile // Try to Open skill Profile
std::ifstream skill_profile_file(job_profile_file_path); std::ifstream skill_profile_file(job_profile_file_path);
if (!skill_profile_file.is_open()) { if (!skill_profile_file.is_open()) {
ShowWarning("Failed to open skill_profile_file %s. Trying to load DEFAULT instead.", job_profile_file_path.c_str()); ShowWarning("Failed to open skill_profile_file %s. Trying to load DEFAULT instead.\n", job_profile_file_path.c_str());
job_profile_file_path = "conf/bot_profiles/skill_profiles/DEFAULT_skills_profile.json"; job_profile_file_path = "conf/bot_profiles/skill_profiles/DEFAULT_skills_profile.json";
skill_profile_file.open(job_profile_file_path); skill_profile_file.open(job_profile_file_path);
if (!skill_profile_file.is_open()) { if (!skill_profile_file.is_open()) {
@ -1031,7 +1041,7 @@ void prepare_skill_bundle(bot_skill_bundle& bundle, uint32 char_id, const nlohma
std::string job_str = character["start_job"]; std::string job_str = character["start_job"];
uint16 related_job = convert_jobid_to_number(job_str.c_str()); uint16 related_job = convert_jobid_to_number(job_str.c_str());
validate_skills(related_job, bundle); //validate_skills(related_job, bundle);
// Add the skill to the skill bundle // Add the skill to the skill bundle
bot_skill new_skill{}; bot_skill new_skill{};
@ -1040,37 +1050,12 @@ void prepare_skill_bundle(bot_skill_bundle& bundle, uint32 char_id, const nlohma
new_skill.level = static_cast<uint16>(skill_level); new_skill.level = static_cast<uint16>(skill_level);
new_skill.flag = 0; // Default: 0 for permanent new_skill.flag = 0; // Default: 0 for permanent
bundle.skills.push_back(new_skill); bundle.skills.push_back(new_skill);
}
}
}
bool is_skill_validation_exception(int job_id, int skill_id) {
// Usually there should not be any exceptions for validation but in case you want to setup bots that have permanent soullink skills active or other temporaö skills, they should be added here
// A Exception is only needed if a skill should be added that is not present in the base skill tree but would use a slot in the jobs skill window like custom/item skills or soullink skills
// Map job IDs to arrays of exception skill IDs
static const std::unordered_map<int, std::vector<int>> skill_map = {
{JOB_DANCER, {BA_WHISTLE, BA_ASSASSINCROSS, BA_POEMBRAGI, BA_APPLEIDUN}}, // Dancer exceptions
{JOB_KNIGHT, {KN_ONEHAND}}, // Knight exceptions
// Add more job-specific exceptions here
};
// Check if the job_id exists in the map
auto it = skill_map.find(job_id);
if (it != skill_map.end()) {
// Check if the skill_id exists in the exception list for the job_id
const auto& exception_skills = it->second;
if (std::find(exception_skills.begin(), exception_skills.end(), skill_id) != exception_skills.end()) {
return true;
} }
//dump_skill_bundle(bundle);
} }
// If no match is found
return false;
} }
/**********+ /**********+
* Below this point there are only large converter functions. * Below this point there are only large converter functions.
************/ ************/

@ -124,8 +124,7 @@ struct SkillDB {
int convert_skill_str_to_id(const char* skill_str); int convert_skill_str_to_id(const char* skill_str);
const char* convert_skill_id_to_str(int skill_id); const char* convert_skill_id_to_str(int skill_id);
bool check_skill_requirements(int job_id, int skill_id, bot_skill_bundle& skill_bundle); void dump_skill_bundle(const bot_skill_bundle& skill_bundle);
bool is_skill_validation_exception(int job_id, int skill_id);
struct bot_skill_tree_entry { struct bot_skill_tree_entry {
uint16 skill_id, max_lv; uint16 skill_id, max_lv;
@ -155,7 +154,7 @@ public:
extern BotSkillTreeDatabase bot_skill_tree_db; extern BotSkillTreeDatabase bot_skill_tree_db;
void init_bot_skill_tree_db(); void init_bot_skill_tree_db();
bool validate_skills(uint16 start_job, const bot_skill_bundle& skill_bundle); 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 prepare_skill_bundle(bot_skill_bundle& bundle, uint32 char_id, const nlohmann::json& character);
#endif /* HELPER_SKILLS_HPP */ #endif /* HELPER_SKILLS_HPP */

Loading…
Cancel
Save