|
@@ -121,6 +121,271 @@ struct script_event_s{
|
|
|
// Holds pointers to the commonly executed scripts for speedup. [Skotlex]
|
|
|
std::map<enum npce_event, std::vector<struct script_event_s>> script_event;
|
|
|
|
|
|
+const std::string StylistDatabase::getDefaultLocation(){
|
|
|
+ return std::string(db_path) + "/stylist.yml";
|
|
|
+}
|
|
|
+
|
|
|
+bool StylistDatabase::parseCostNode( std::shared_ptr<s_stylist_entry> entry, bool doram, const YAML::Node& node ){
|
|
|
+ std::shared_ptr<s_stylist_costs> costs = doram ? entry->doram : entry->human;
|
|
|
+ bool costs_exists = costs != nullptr;
|
|
|
+
|
|
|
+ if( !costs_exists ){
|
|
|
+ costs = std::make_shared<s_stylist_costs>();
|
|
|
+ }
|
|
|
+
|
|
|
+ if( this->nodeExists( node, "Price" ) ){
|
|
|
+ uint32 price;
|
|
|
+
|
|
|
+ if( !this->asUInt32( node, "Price", price ) ){
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if( price > MAX_ZENY ){
|
|
|
+ this->invalidWarning( node["Price"], "stylist_parseCostNode: Price %u is too high, capping to MAX_ZENY...\n", price );
|
|
|
+ price = MAX_ZENY;
|
|
|
+ }
|
|
|
+
|
|
|
+ costs->price = price;
|
|
|
+ }else{
|
|
|
+ if( !costs_exists ){
|
|
|
+ costs->price = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if( this->nodeExists( node, "RequiredItem" ) ){
|
|
|
+ std::string item;
|
|
|
+
|
|
|
+ if( !this->asString( node, "RequiredItem", item ) ){
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ std::shared_ptr<item_data> id = item_db.search_aegisname( item.c_str() );
|
|
|
+
|
|
|
+ if( id == nullptr ){
|
|
|
+ this->invalidWarning( node["RequiredItem"], "stylist_parseCostNode: Unknown item \"%s\"...\n", item.c_str() );
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ costs->requiredItem = id->nameid;
|
|
|
+ }else{
|
|
|
+ if( !costs_exists ){
|
|
|
+ costs->requiredItem = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if( this->nodeExists( node, "RequiredItemBox" ) ){
|
|
|
+ std::string item;
|
|
|
+
|
|
|
+ if( !this->asString( node, "RequiredItemBox", item ) ){
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ std::shared_ptr<item_data> id = item_db.search_aegisname( item.c_str() );
|
|
|
+
|
|
|
+ if( id == nullptr ){
|
|
|
+ this->invalidWarning( node["RequiredItemBox"], "stylist_parseCostNode: Unknown item \"%s\"...\n", item.c_str() );
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ costs->requiredItemBox = id->nameid;
|
|
|
+ }else{
|
|
|
+ if( !costs_exists ){
|
|
|
+ costs->requiredItemBox = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if( !costs_exists ){
|
|
|
+ if( doram ){
|
|
|
+ entry->doram = costs;
|
|
|
+ }else{
|
|
|
+ entry->human = costs;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+uint64 StylistDatabase::parseBodyNode( const YAML::Node &node ){
|
|
|
+ if( !this->nodesExist( node, { "Look", "Options" } ) ){
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ std::string look_str;
|
|
|
+
|
|
|
+ if( !this->asString( node, "Look", look_str ) ){
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ int64 constant;
|
|
|
+
|
|
|
+ if( !script_get_constant( ( "LOOK_" + look_str ).c_str(), &constant ) ){
|
|
|
+ this->invalidWarning( node["Look"], "stylist_parseBodyNode: Invalid look %s.\n", look_str.c_str() );
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch( constant ){
|
|
|
+ case LOOK_HEAD_TOP:
|
|
|
+ case LOOK_HEAD_MID:
|
|
|
+ case LOOK_HEAD_BOTTOM:
|
|
|
+ case LOOK_HAIR:
|
|
|
+ case LOOK_HAIR_COLOR:
|
|
|
+ case LOOK_CLOTHES_COLOR:
|
|
|
+ case LOOK_BODY2:
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ this->invalidWarning( node["Look"], "stylist_parseBodyNode: Unsupported look value \"%s\"...\n", look_str.c_str() );
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ std::shared_ptr<s_stylist_list> list = this->find( (uint32)constant );
|
|
|
+ bool exists = list != nullptr;
|
|
|
+ uint64 count = 0;
|
|
|
+
|
|
|
+ if( !exists ){
|
|
|
+ list = std::make_shared<s_stylist_list>();
|
|
|
+ list->look = (uint16)constant;
|
|
|
+ }
|
|
|
+
|
|
|
+ for( const YAML::Node& optionNode : node["Options"] ){
|
|
|
+ int16 index;
|
|
|
+
|
|
|
+ if( !this->asInt16( optionNode, "Index", index ) ){
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if( index == 0 ){
|
|
|
+ this->invalidWarning( optionNode["Index"], "stylist_parseBodyNode: Unsupported index value \"%hd\"...\n", index );
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ std::shared_ptr<s_stylist_entry> entry = util::umap_find( list->entries, index );
|
|
|
+ bool entry_exists = entry != nullptr;
|
|
|
+
|
|
|
+ if( !entry_exists ){
|
|
|
+ entry = std::make_shared<s_stylist_entry>();
|
|
|
+ entry->look = list->look;
|
|
|
+ entry->index = index;
|
|
|
+
|
|
|
+ if( !this->nodesExist( optionNode, { "Value" } ) ){
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if( this->nodeExists( optionNode, "Value" ) ){
|
|
|
+ uint32 value;
|
|
|
+
|
|
|
+ switch( list->look ){
|
|
|
+ case LOOK_HEAD_TOP:
|
|
|
+ case LOOK_HEAD_MID:
|
|
|
+ case LOOK_HEAD_BOTTOM: {
|
|
|
+ std::string item;
|
|
|
+
|
|
|
+ if( !this->asString( optionNode, "Value", item ) ){
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ std::shared_ptr<item_data> id = item_db.search_aegisname( item.c_str() );
|
|
|
+
|
|
|
+ if( id == nullptr ){
|
|
|
+ this->invalidWarning( optionNode["Value"], "stylist_parseBodyNode: Unknown item \"%s\"...\n", item.c_str() );
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ value = id->nameid;
|
|
|
+ } break;
|
|
|
+ case LOOK_HAIR:
|
|
|
+ if( !this->asUInt32( optionNode, "Value", value ) ){
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if( value < MIN_HAIR_STYLE ){
|
|
|
+ this->invalidWarning( optionNode["Value"], "stylist_parseBodyNode: hair style \"%u\" is too low...\n", value );
|
|
|
+ return 0;
|
|
|
+ }else if( value > MAX_HAIR_STYLE ){
|
|
|
+ this->invalidWarning( optionNode["Value"], "stylist_parseBodyNode: hair style \"%u\" is too high...\n", value );
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case LOOK_HAIR_COLOR:
|
|
|
+ if( !this->asUInt32( optionNode, "Value", value ) ){
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if( value < MIN_HAIR_COLOR ){
|
|
|
+ this->invalidWarning( optionNode["Value"], "stylist_parseBodyNode: hair color \"%u\" is too low...\n", value );
|
|
|
+ return 0;
|
|
|
+ }else if( value > MAX_HAIR_COLOR ){
|
|
|
+ this->invalidWarning( optionNode["Value"], "stylist_parseBodyNode: hair color \"%u\" is too high...\n", value );
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case LOOK_CLOTHES_COLOR:
|
|
|
+ if( !this->asUInt32( optionNode, "Value", value ) ){
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if( value < MIN_CLOTH_COLOR ){
|
|
|
+ this->invalidWarning( optionNode["Value"], "stylist_parseBodyNode: cloth color \"%u\" is too low...\n", value );
|
|
|
+ return 0;
|
|
|
+ }else if( value > MAX_CLOTH_COLOR ){
|
|
|
+ this->invalidWarning( optionNode["Value"], "stylist_parseBodyNode: cloth color \"%u\" is too high...\n", value );
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case LOOK_BODY2:
|
|
|
+ if( !this->asUInt32( optionNode, "Value", value ) ){
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if( value < MIN_BODY_STYLE ){
|
|
|
+ this->invalidWarning( optionNode["Value"], "stylist_parseBodyNode: body style \"%u\" is too low...\n", value );
|
|
|
+ return 0;
|
|
|
+ }else if( value > MAX_BODY_STYLE ){
|
|
|
+ this->invalidWarning( optionNode["Value"], "stylist_parseBodyNode: body style \"%u\" is too high...\n", value );
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ entry->value = value;
|
|
|
+ }
|
|
|
+
|
|
|
+ if( this->nodeExists( optionNode, "CostsHuman" ) ) {
|
|
|
+ if( !this->parseCostNode( entry, false, optionNode["CostsHuman"] ) ){
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ if( !entry_exists ){
|
|
|
+ entry->human = nullptr;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if( this->nodeExists( optionNode, "CostsDoram" ) ) {
|
|
|
+ if( !this->parseCostNode( entry, true, optionNode["CostsDoram"] ) ){
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ if( !entry_exists ){
|
|
|
+ entry->doram = nullptr;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if( !entry_exists ){
|
|
|
+ list->entries[index] = entry;
|
|
|
+ }
|
|
|
+
|
|
|
+ count++;
|
|
|
+ }
|
|
|
+
|
|
|
+ if( !exists ){
|
|
|
+ this->put( (uint32)constant, list );
|
|
|
+ }
|
|
|
+
|
|
|
+ return count;
|
|
|
+}
|
|
|
+
|
|
|
+StylistDatabase stylist_db;
|
|
|
+
|
|
|
/**
|
|
|
* Returns the viewdata for normal NPC classes.
|
|
|
* @param class_: NPC class ID
|
|
@@ -4785,6 +5050,8 @@ int npc_reload(void) {
|
|
|
"\t-'" CL_WHITE "%d" CL_RESET "' Mobs Not Cached\n",
|
|
|
npc_id - npc_new_min, npc_warp, npc_shop, npc_script, npc_mob, npc_cache_mob, npc_delay_mob);
|
|
|
|
|
|
+ stylist_db.reload();
|
|
|
+
|
|
|
//Re-read the NPC Script Events cache.
|
|
|
npc_read_event_script();
|
|
|
|
|
@@ -4851,6 +5118,7 @@ void do_final_npc(void) {
|
|
|
#if PACKETVER >= 20131223
|
|
|
NPCMarketDB->destroy(NPCMarketDB, npc_market_free);
|
|
|
#endif
|
|
|
+ stylist_db.clear();
|
|
|
ers_destroy(timer_event_ers);
|
|
|
ers_destroy(npc_sc_display_ers);
|
|
|
npc_clearsrcfile();
|
|
@@ -4936,6 +5204,8 @@ void do_init_npc(void){
|
|
|
"\t-'" CL_WHITE "%d" CL_RESET "' Mobs Not Cached\n",
|
|
|
npc_id - START_NPC_NUM, npc_warp, npc_shop, npc_script, npc_mob, npc_cache_mob, npc_delay_mob);
|
|
|
|
|
|
+ stylist_db.load();
|
|
|
+
|
|
|
// set up the events cache
|
|
|
npc_read_event_script();
|
|
|
|