diff options
Diffstat (limited to 'src/map/itemdb.c')
-rw-r--r-- | src/map/itemdb.c | 610 |
1 files changed, 493 insertions, 117 deletions
diff --git a/src/map/itemdb.c b/src/map/itemdb.c index bd552dd16..9a43bae14 100644 --- a/src/map/itemdb.c +++ b/src/map/itemdb.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -49,7 +49,7 @@ struct itemdb_interface *itemdb; * name = item alias, so we should find items aliases first. if not found then look for "jname" (full name) * @see DBApply */ -int itemdb_searchname_sub(DBKey key, DBData *data, va_list ap) +int itemdb_searchname_sub(union DBKey key, struct DBData *data, va_list ap) { struct item_data *item = DB->data2ptr(data), **dst, **dst2; char *str; @@ -112,7 +112,7 @@ struct item_data* itemdb_name2id(const char *str) { /** * @see DBMatcher */ -int itemdb_searchname_array_sub(DBKey key, DBData data, va_list ap) +int itemdb_searchname_array_sub(union DBKey key, struct DBData data, va_list ap) { struct item_data *item = DB->data2ptr(&data); char *str; @@ -170,10 +170,10 @@ int itemdb_searchname_array(struct item_data** data, int size, const char *str, // search in the db if( count < size ) { - DBData *db_data[MAX_SEARCH]; + struct DBData *db_data[MAX_SEARCH]; int db_count = 0; size -= count; - db_count = itemdb->other->getall(itemdb->other, (DBData**)&db_data, size, itemdb->searchname_array_sub, str); + db_count = itemdb->other->getall(itemdb->other, (struct DBData**)&db_data, size, itemdb->searchname_array_sub, str); for (i = 0; i < db_count; i++) data[count++] = DB->data2ptr(db_data[i]); count += db_count; @@ -321,6 +321,16 @@ struct item_data* itemdb_exists(int nameid) return item; } +/** + * Searches for the item_option data. + * @param option_index as the index of the item option (client side). + * @return pointer to struct item_option data or NULL. + */ +struct item_option *itemdb_option_exists(int idx) +{ + return (struct item_option *)idb_get(itemdb->options, idx); +} + /// Returns human readable name for given item type. /// @param type Type id to retrieve name for ( IT_* ). const char* itemdb_typename(int type) @@ -342,81 +352,224 @@ const char* itemdb_typename(int type) return "Unknown Type"; } -/*========================================== - * Converts the jobid from the format in itemdb - * to the format used by the map server. [Skotlex] - *------------------------------------------*/ -void itemdb_jobid2mapid(unsigned int *bclass, unsigned int jobmask) +/** + * Converts the JobID to the format used by map-server to check item + * restriction as per job. + * + * @param bclass Pointer to the variable containing the new format + * @param job_id Variable containing JobID + * @param enable Boolean value which (un)set the restriction. + * + * @author Dastgir + */ +void itemdb_jobid2mapid(uint64 *bclass, int job_class, bool enable) { + uint64 mask[3] = { 0 }; int i; + nullpo_retv(bclass); - bclass[0]= bclass[1]= bclass[2]= 0; + + switch (job_class) { + // Base Classes + case JOB_NOVICE: + case JOB_SUPER_NOVICE: + mask[0] = 1ULL << MAPID_NOVICE; + mask[1] = 1ULL << MAPID_NOVICE; + break; + case JOB_SWORDMAN: + mask[0] = 1ULL << MAPID_SWORDMAN; + break; + case JOB_MAGE: + mask[0] = 1ULL << MAPID_MAGE; + break; + case JOB_ARCHER: + mask[0] = 1ULL << MAPID_ARCHER; + break; + case JOB_ACOLYTE: + mask[0] = 1ULL << MAPID_ACOLYTE; + break; + case JOB_MERCHANT: + mask[0] = 1ULL << MAPID_MERCHANT; + break; + case JOB_THIEF: + mask[0] = 1ULL << MAPID_THIEF; + break; + // 2-1 Classes + case JOB_KNIGHT: + mask[1] = 1ULL << MAPID_SWORDMAN; + break; + case JOB_PRIEST: + mask[1] = 1ULL << MAPID_ACOLYTE; + break; + case JOB_WIZARD: + mask[1] = 1ULL << MAPID_MAGE; + break; + case JOB_BLACKSMITH: + mask[1] = 1ULL << MAPID_MERCHANT; + break; + case JOB_HUNTER: + mask[1] = 1ULL << MAPID_ARCHER; + break; + case JOB_ASSASSIN: + mask[1] = 1ULL << MAPID_THIEF; + break; + // 2-2 Classes + case JOB_CRUSADER: + mask[2] = 1ULL << MAPID_SWORDMAN; + break; + case JOB_MONK: + mask[2] = 1ULL << MAPID_ACOLYTE; + break; + case JOB_SAGE: + mask[2] = 1ULL << MAPID_MAGE; + break; + case JOB_ALCHEMIST: + mask[2] = 1ULL << MAPID_MERCHANT; + break; + case JOB_BARD: + mask[2] = 1ULL << MAPID_ARCHER; + break; + case JOB_ROGUE: + mask[2] = 1ULL << MAPID_THIEF; + break; + // Extended Classes + case JOB_TAEKWON: + mask[0] = 1ULL << MAPID_TAEKWON; + break; + case JOB_STAR_GLADIATOR: + mask[1] = 1ULL << MAPID_TAEKWON; + break; + case JOB_SOUL_LINKER: + mask[2] = 1ULL << MAPID_TAEKWON; + break; + case JOB_GUNSLINGER: + mask[0] = 1ULL << MAPID_GUNSLINGER; + mask[1] = 1ULL << MAPID_GUNSLINGER; + break; + case JOB_NINJA: + mask[0] = 1ULL << MAPID_NINJA; + mask[1] = 1ULL << MAPID_NINJA; + break; + case JOB_KAGEROU: + case JOB_OBORO: + mask[1] = 1ULL << MAPID_NINJA; + break; + case JOB_REBELLION: + mask[1] = 1ULL << MAPID_GUNSLINGER; + break; + case JOB_SUMMONER: + mask[0] = 1ULL << MAPID_SUMMONER; + break; + // Other Classes + case JOB_GANGSI: //Bongun/Munak + mask[0] = 1ULL << MAPID_GANGSI; + break; + case JOB_DEATH_KNIGHT: + mask[1] = 1ULL << MAPID_GANGSI; + break; + case JOB_DARK_COLLECTOR: + mask[2] = 1ULL << MAPID_GANGSI; + break; + } + + for (i = 0; i < ARRAYLENGTH(mask); i++) { + if (mask[i] == 0) + continue; + if (enable) + bclass[i] |= mask[i]; + else + bclass[i] &= ~mask[i]; + } +} + +/** + * Converts the JobMask to the format used by map-server to check item + * restriction as per job. + * + * @param bclass Pointer to the variable containing the new format. + * @param jobmask Variable containing JobMask. + */ +void itemdb_jobmask2mapid(uint64 *bclass, uint64 jobmask) +{ + nullpo_retv(bclass); + bclass[0] = bclass[1] = bclass[2] = 0; //Base classes - if (jobmask & 1<<JOB_NOVICE) { + if (jobmask & 1ULL<<JOB_NOVICE) { //Both Novice/Super-Novice are counted with the same ID - bclass[0] |= 1<<MAPID_NOVICE; - bclass[1] |= 1<<MAPID_NOVICE; - } - for (i = JOB_NOVICE+1; i <= JOB_THIEF; i++) - { - if (jobmask & 1<<i) - bclass[0] |= 1<<(MAPID_NOVICE+i); - } + bclass[0] |= 1ULL<<MAPID_NOVICE; + bclass[1] |= 1ULL<<MAPID_NOVICE; + } + if (jobmask & 1ULL<<JOB_SWORDMAN) + bclass[0] |= 1ULL<<MAPID_SWORDMAN; + if (jobmask & 1ULL<<JOB_MAGE) + bclass[0] |= 1ULL<<MAPID_MAGE; + if (jobmask & 1ULL<<JOB_ARCHER) + bclass[0] |= 1ULL<<MAPID_ARCHER; + if (jobmask & 1ULL<<JOB_ACOLYTE) + bclass[0] |= 1ULL<<MAPID_ACOLYTE; + if (jobmask & 1ULL<<JOB_MERCHANT) + bclass[0] |= 1ULL<<MAPID_MERCHANT; + if (jobmask & 1ULL<<JOB_THIEF) + bclass[0] |= 1ULL<<MAPID_THIEF; //2-1 classes - if (jobmask & 1<<JOB_KNIGHT) - bclass[1] |= 1<<MAPID_SWORDMAN; - if (jobmask & 1<<JOB_PRIEST) - bclass[1] |= 1<<MAPID_ACOLYTE; - if (jobmask & 1<<JOB_WIZARD) - bclass[1] |= 1<<MAPID_MAGE; - if (jobmask & 1<<JOB_BLACKSMITH) - bclass[1] |= 1<<MAPID_MERCHANT; - if (jobmask & 1<<JOB_HUNTER) - bclass[1] |= 1<<MAPID_ARCHER; - if (jobmask & 1<<JOB_ASSASSIN) - bclass[1] |= 1<<MAPID_THIEF; + if (jobmask & 1ULL<<JOB_KNIGHT) + bclass[1] |= 1ULL<<MAPID_SWORDMAN; + if (jobmask & 1ULL<<JOB_PRIEST) + bclass[1] |= 1ULL<<MAPID_ACOLYTE; + if (jobmask & 1ULL<<JOB_WIZARD) + bclass[1] |= 1ULL<<MAPID_MAGE; + if (jobmask & 1ULL<<JOB_BLACKSMITH) + bclass[1] |= 1ULL<<MAPID_MERCHANT; + if (jobmask & 1ULL<<JOB_HUNTER) + bclass[1] |= 1ULL<<MAPID_ARCHER; + if (jobmask & 1ULL<<JOB_ASSASSIN) + bclass[1] |= 1ULL<<MAPID_THIEF; //2-2 classes - if (jobmask & 1<<JOB_CRUSADER) - bclass[2] |= 1<<MAPID_SWORDMAN; - if (jobmask & 1<<JOB_MONK) - bclass[2] |= 1<<MAPID_ACOLYTE; - if (jobmask & 1<<JOB_SAGE) - bclass[2] |= 1<<MAPID_MAGE; - if (jobmask & 1<<JOB_ALCHEMIST) - bclass[2] |= 1<<MAPID_MERCHANT; - if (jobmask & 1<<JOB_BARD) - bclass[2] |= 1<<MAPID_ARCHER; + if (jobmask & 1ULL<<JOB_CRUSADER) + bclass[2] |= 1ULL<<MAPID_SWORDMAN; + if (jobmask & 1ULL<<JOB_MONK) + bclass[2] |= 1ULL<<MAPID_ACOLYTE; + if (jobmask & 1ULL<<JOB_SAGE) + bclass[2] |= 1ULL<<MAPID_MAGE; + if (jobmask & 1ULL<<JOB_ALCHEMIST) + bclass[2] |= 1ULL<<MAPID_MERCHANT; + if (jobmask & 1ULL<<JOB_BARD) + bclass[2] |= 1ULL<<MAPID_ARCHER; #if 0 // Bard/Dancer share the same slot now. - if (jobmask & 1<<JOB_DANCER) - bclass[2] |= 1<<MAPID_ARCHER; + if (jobmask & 1ULL<<JOB_DANCER) + bclass[2] |= 1ULL<<MAPID_ARCHER; #endif // 0 - if (jobmask & 1<<JOB_ROGUE) - bclass[2] |= 1<<MAPID_THIEF; + if (jobmask & 1ULL<<JOB_ROGUE) + bclass[2] |= 1ULL<<MAPID_THIEF; //Special classes that don't fit above. - if (jobmask & 1<<21) //Taekwon boy - bclass[0] |= 1<<MAPID_TAEKWON; - if (jobmask & 1<<22) //Star Gladiator - bclass[1] |= 1<<MAPID_TAEKWON; - if (jobmask & 1<<23) //Soul Linker - bclass[2] |= 1<<MAPID_TAEKWON; - if (jobmask & 1<<JOB_GUNSLINGER) - {//Rebellion job can equip Gunslinger equips. [Rytech] - bclass[0] |= 1<<MAPID_GUNSLINGER; - bclass[1] |= 1<<MAPID_GUNSLINGER; - } - if (jobmask & 1<<JOB_NINJA) - {bclass[0] |= 1<<MAPID_NINJA; - bclass[1] |= 1<<MAPID_NINJA;}//Kagerou/Oboro jobs can equip Ninja equips. [Rytech] - if (jobmask & 1<<26) //Bongun/Munak - bclass[0] |= 1<<MAPID_GANGSI; - if (jobmask & 1<<27) //Death Knight - bclass[1] |= 1<<MAPID_GANGSI; - if (jobmask & 1<<28) //Dark Collector - bclass[2] |= 1<<MAPID_GANGSI; - if (jobmask & 1<<29) //Kagerou / Oboro - bclass[1] |= 1<<MAPID_NINJA; - if (jobmask & 1<<30) //Rebellion - bclass[1] |= 1<<MAPID_GUNSLINGER; + if (jobmask & 1ULL<<21) //Taekwon boy + bclass[0] |= 1ULL<<MAPID_TAEKWON; + if (jobmask & 1ULL<<22) //Star Gladiator + bclass[1] |= 1ULL<<MAPID_TAEKWON; + if (jobmask & 1ULL<<23) //Soul Linker + bclass[2] |= 1ULL<<MAPID_TAEKWON; + if (jobmask & 1ULL<<JOB_GUNSLINGER) { + //Rebellion job can equip Gunslinger equips. [Rytech] + bclass[0] |= 1ULL<<MAPID_GUNSLINGER; + bclass[1] |= 1ULL<<MAPID_GUNSLINGER; + } + if (jobmask & 1ULL<<JOB_NINJA) { + //Kagerou/Oboro jobs can equip Ninja equips. [Rytech] + bclass[0] |= 1ULL<<MAPID_NINJA; + bclass[1] |= 1ULL<<MAPID_NINJA; + } + if (jobmask & 1ULL<<26) //Bongun/Munak + bclass[0] |= 1ULL<<MAPID_GANGSI; + if (jobmask & 1ULL<<27) //Death Knight + bclass[1] |= 1ULL<<MAPID_GANGSI; + if (jobmask & 1ULL<<28) //Dark Collector + bclass[2] |= 1ULL<<MAPID_GANGSI; + if (jobmask & 1ULL<<29) //Kagerou / Oboro + bclass[1] |= 1ULL<<MAPID_NINJA; + if (jobmask & 1ULL<<30) //Rebellion + bclass[1] |= 1ULL<<MAPID_GUNSLINGER; + if (jobmask & 1ULL<<31) //Summoner + bclass[0] |= 1ULL<<MAPID_SUMMONER; } void create_dummy_data(void) @@ -637,8 +790,8 @@ int itemdb_isidentified2(struct item_data *data) { } void itemdb_read_groups(void) { - config_t item_group_conf; - config_setting_t *itg = NULL, *it = NULL; + struct config_t item_group_conf; + struct config_setting_t *itg = NULL, *it = NULL; #ifdef RENEWAL const char *config_filename = "db/re/item_group.conf"; // FIXME hardcoded name #else @@ -648,10 +801,8 @@ void itemdb_read_groups(void) { int i = 0, count = 0, c; unsigned int *gsize = NULL; - if (libconfig->read_file(&item_group_conf, config_filename)) { - ShowError("can't read %s\n", config_filename); + if (!libconfig->load_file(&item_group_conf, config_filename)) return; - } gsize = aMalloc( libconfig->setting_length(item_group_conf.root) * sizeof(unsigned int) ); @@ -929,8 +1080,8 @@ bool itemdb_read_cached_packages(const char *config_filename) { return true; } void itemdb_read_packages(void) { - config_t item_packages_conf; - config_setting_t *itg = NULL, *it = NULL, *t = NULL; + struct config_t item_packages_conf; + struct config_setting_t *itg = NULL, *it = NULL, *t = NULL; #ifdef RENEWAL const char *config_filename = "db/re/item_packages.conf"; // FIXME hardcoded name #else @@ -946,10 +1097,8 @@ void itemdb_read_packages(void) { return; } - if (libconfig->read_file(&item_packages_conf, config_filename)) { - ShowError("can't read %s\n", config_filename); + if (!libconfig->load_file(&item_packages_conf, config_filename)) return; - } must = aMalloc( libconfig->setting_length(item_packages_conf.root) * sizeof(unsigned int) ); random = aMalloc( libconfig->setting_length(item_packages_conf.root) * sizeof(unsigned int) ); @@ -1160,9 +1309,128 @@ void itemdb_read_packages(void) { ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, config_filename); } +/** + * Processes any (plugin-defined) additional fields for a itemdb_options entry. + * + * @param[in,out] entry The destination ito entry, already initialized + * (item_opt.index, status.mode are expected to be already set). + * @param[in] t The libconfig entry. + * @param[in] source Source of the entry (file name), to be displayed in + * case of validation errors. + */ +void itemdb_readdb_options_additional_fields(struct item_option *ito, struct config_setting_t *t, const char *source) +{ + // do nothing. plugins can do their own work +} + +/** + * Reads the Item Options configuration file. + */ +void itemdb_read_options(void) +{ + struct config_t item_options_db; + struct config_setting_t *ito = NULL, *conf = NULL; + int index = 0, count = 0; + const char *filepath = "db/item_options.conf"; + VECTOR_DECL(int) duplicate_id; + + if (!libconfig->load_file(&item_options_db, filepath)) + return; + +#ifdef ENABLE_CASE_CHECK + script->parser_current_file = filepath; +#endif // ENABLE_CASE_CHECK + + if ((ito=libconfig->setting_get_member(item_options_db.root, "item_options_db")) == NULL) { + ShowError("itemdb_read_options: '%s' could not be loaded.\n", filepath); + libconfig->destroy(&item_options_db); + return; + } + + VECTOR_INIT(duplicate_id); + + VECTOR_ENSURE(duplicate_id, libconfig->setting_length(ito), 1); + + while ((conf = libconfig->setting_get_elem(ito, index++))) { + struct item_option t_opt = { 0 }, *s_opt = NULL; + const char *str = NULL; + int i = 0; + + /* Id Lookup */ + if (!libconfig->setting_lookup_int16(conf, "Id", &t_opt.index) || t_opt.index <= 0) { + ShowError("itemdb_read_options: Invalid Option Id provided for entry %d in '%s', skipping...\n", t_opt.index, filepath); + continue; + } + + /* Checking for duplicate entries. */ + ARR_FIND(0, VECTOR_LENGTH(duplicate_id), i, VECTOR_INDEX(duplicate_id, i) == t_opt.index); + + if (i != VECTOR_LENGTH(duplicate_id)) { + ShowError("itemdb_read_options: Duplicate entry for Option Id %d in '%s', skipping...\n", t_opt.index, filepath); + continue; + } + + VECTOR_PUSH(duplicate_id, t_opt.index); + + /* Name Lookup */ + if (!libconfig->setting_lookup_string(conf, "Name", &str)) { + ShowError("itemdb_read_options: Invalid Option Name '%s' provided for Id %d in '%s', skipping...\n", str, t_opt.index, filepath); + continue; + } + + /* check for illegal characters in the constant. */ + { + const char *c = str; + + while (ISALNUM(*c) || *c == '_') + ++c; + + if (*c != '\0') { + ShowError("itemdb_read_options: Invalid characters in Option Name '%s' for Id %d in '%s', skipping...\n", str, t_opt.index, filepath); + continue; + } + } + + /* Set name as a script constant with index as value. */ + script->set_constant2(str, t_opt.index, false, false); + + /* Script Code Lookup */ + if (!libconfig->setting_lookup_string(conf, "Script", &str)) { + ShowError("itemdb_read_options: Script code not found for entry %s (Id: %d) in '%s', skipping...\n", str, t_opt.index, filepath); + continue; + } + + /* Set Script */ + t_opt.script = *str ? script->parse(str, filepath, t_opt.index, SCRIPT_IGNORE_EXTERNAL_BRACKETS, NULL) : NULL; + + /* Additional fields through plugins */ + itemdb->readdb_options_additional_fields(&t_opt, ito, filepath); + + /* Allocate memory and copy contents */ + CREATE(s_opt, struct item_option, 1); + + *s_opt = t_opt; + + /* Store ptr in the database */ + idb_put(itemdb->options, t_opt.index, s_opt); + + count++; + } + +#ifdef ENABLE_CASE_CHECK + script->parser_current_file = NULL; +#endif // ENABLE_CASE_CHECK + + libconfig->destroy(&item_options_db); + + VECTOR_CLEAR(duplicate_id); + + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filepath); +} + void itemdb_read_chains(void) { - config_t item_chain_conf; - config_setting_t *itc = NULL; + struct config_t item_chain_conf; + struct config_setting_t *itc = NULL; #ifdef RENEWAL const char *config_filename = "db/re/item_chain.conf"; // FIXME hardcoded name #else @@ -1170,10 +1438,8 @@ void itemdb_read_chains(void) { #endif int i = 0, count = 0; - if (libconfig->read_file(&item_chain_conf, config_filename)) { - ShowError("can't read %s\n", config_filename); + if (!libconfig->load_file(&item_chain_conf, config_filename)) return; - } CREATE(itemdb->chains, struct item_chain, libconfig->setting_length(item_chain_conf.root)); itemdb->chain_count = (unsigned short)libconfig->setting_length(item_chain_conf.root); @@ -1186,7 +1452,7 @@ void itemdb_read_chains(void) { struct item_chain_entry *prev = NULL; const char *name = config_setting_name(itc); int c = 0; - config_setting_t *entry = NULL; + struct config_setting_t *entry = NULL; script->set_constant2(name, i-1, false, false); itemdb->chains[count].qty = (unsigned short)libconfig->setting_length(itc); @@ -1261,7 +1527,7 @@ void itemdb_read_combos(void) char filepath[256]; FILE* fp; - sprintf(filepath, "%s/%s", map->db_path, DBPATH"item_combo_db.txt"); + snprintf(filepath, 256, "%s/%s", map->db_path, DBPATH"item_combo_db.txt"); if ((fp = fopen(filepath, "r")) == NULL) { ShowError("itemdb_read_combos: File not found \"%s\".\n", filepath); @@ -1286,7 +1552,7 @@ void itemdb_read_combos(void) if (!strchr(p,',')) { /* is there even a single column? */ - ShowError("itemdb_read_combos: Insufficient columns in line %d of \"%s\", skipping.\n", lines, filepath); + ShowError("itemdb_read_combos: Insufficient columns in line %u of \"%s\", skipping.\n", lines, filepath); continue; } @@ -1300,13 +1566,13 @@ void itemdb_read_combos(void) p++; if (str[1][0] != '{') { - ShowError("itemdb_read_combos(#1): Invalid format (Script column) in line %d of \"%s\", skipping.\n", lines, filepath); + ShowError("itemdb_read_combos(#1): Invalid format (Script column) in line %u of \"%s\", skipping.\n", lines, filepath); continue; } /* no ending key anywhere (missing \}\) */ if ( str[1][strlen(str[1])-1] != '}' ) { - ShowError("itemdb_read_combos(#2): Invalid format (Script column) in line %d of \"%s\", skipping.\n", lines, filepath); + ShowError("itemdb_read_combos(#2): Invalid format (Script column) in line %u of \"%s\", skipping.\n", lines, filepath); continue; } else { int items[MAX_ITEMS_PER_COMBO]; @@ -1314,14 +1580,14 @@ void itemdb_read_combos(void) struct item_combo *combo = NULL; if((retcount = itemdb->combo_split_atoi(str[0], items)) < 2) { - ShowError("itemdb_read_combos: line %d of \"%s\" doesn't have enough items to make for a combo (min:2), skipping.\n", lines, filepath); + ShowError("itemdb_read_combos: line %u of \"%s\" doesn't have enough items to make for a combo (min:2), skipping.\n", lines, filepath); continue; } /* validate */ for(v = 0; v < retcount; v++) { if( !itemdb->exists(items[v]) ) { - ShowError("itemdb_read_combos: line %d of \"%s\" contains unknown item ID %d, skipping.\n", lines, filepath,items[v]); + ShowError("itemdb_read_combos: line %u of \"%s\" contains unknown item ID %d, skipping.\n", lines, filepath, items[v]); break; } } @@ -1420,6 +1686,30 @@ int itemdb_validate_entry(struct item_data *entry, int n, const char *source) { return 0; } + { + const char *c = entry->name; + while (ISALNUM(*c) || *c == '_') + ++c; + + if (*c != '\0') { + ShowWarning("itemdb_validate_entry: Invalid characters in the AegisName '%s' for item %d in '%s'. Skipping.\n", + entry->name, entry->nameid, source); + if (entry->script) { + script->free_code(entry->script); + entry->script = NULL; + } + if (entry->equip_script) { + script->free_code(entry->equip_script); + entry->equip_script = NULL; + } + if (entry->unequip_script) { + script->free_code(entry->unequip_script); + entry->unequip_script = NULL; + } + return 0; + } + } + if( entry->type < 0 || entry->type == IT_UNKNOWN || entry->type == IT_UNKNOWN2 || (entry->type > IT_DELAYCONSUME && entry->type < IT_CASH ) || entry->type >= IT_MAX ) { @@ -1462,7 +1752,7 @@ int itemdb_validate_entry(struct item_data *entry, int n, const char *source) { if (entry->flag.trade_restriction > ITR_ALL) { ShowWarning("itemdb_validate_entry: Invalid trade restriction flag 0x%x for item %d (%s) in '%s', defaulting to none.\n", - entry->flag.trade_restriction, entry->nameid, entry->jname, source); + (unsigned int)entry->flag.trade_restriction, entry->nameid, entry->jname, source); entry->flag.trade_restriction = ITR_NONE; } @@ -1498,6 +1788,12 @@ int itemdb_validate_entry(struct item_data *entry, int n, const char *source) { memset(&entry->stack, '\0', sizeof(entry->stack)); } + if (entry->type == IT_WEAPON && (entry->look < 0 || entry->look >= MAX_SINGLE_WEAPON_TYPE)) { + ShowWarning("itemdb_validate_entry: Invalid View for weapon items. View value %d for item %d (%s) in '%s', defaulting to 1.\n", + entry->look, entry->nameid, entry->jname, source); + entry->look = 1; + } + entry->wlv = cap_value(entry->wlv, REFINE_TYPE_ARMOR, REFINE_TYPE_MAX); if( !entry->elvmax ) @@ -1507,7 +1803,10 @@ int itemdb_validate_entry(struct item_data *entry, int n, const char *source) { if( entry->type != IT_ARMOR && entry->type != IT_WEAPON && !entry->flag.no_refine ) entry->flag.no_refine = 1; - + + if (entry->type != IT_ARMOR && entry->type != IT_WEAPON && !entry->flag.no_options) + entry->flag.no_options = 1; + if (entry->flag.available != 1) { entry->flag.available = 1; entry->view_id = 0; @@ -1535,9 +1834,42 @@ int itemdb_validate_entry(struct item_data *entry, int n, const char *source) { return item->nameid; } -void itemdb_readdb_additional_fields(int itemid, config_setting_t *it, int n, const char *source) +void itemdb_readdb_additional_fields(int itemid, struct config_setting_t *it, int n, const char *source) { - // do nothing. plugins can do own work + // do nothing. plugins can do own work +} + +/** + * Processes job names and changes it into mapid format. + * + * @param id item_data entry. + * @param t Libconfig setting entry. It is expected to be valid and it won't + * be freed (it is care of the caller to do so if necessary). + */ +void itemdb_readdb_job_sub(struct item_data *id, struct config_setting_t *t) +{ + int idx = 0; + struct config_setting_t *it = NULL; + bool enable_all = false; + + id->class_base[0] = id->class_base[1] = id->class_base[2] = 0; + + if (libconfig->setting_lookup_bool_real(t, "All", &enable_all) && enable_all) { + itemdb->jobmask2mapid(id->class_base, UINT64_MAX); + } + while ((it = libconfig->setting_get_elem(t, idx++)) != NULL) { + const char *job_name = config_setting_name(it); + int job_id; + + if (strcmp(job_name, "All") == 0) + continue; + + if ((job_id = pc->check_job_name(job_name)) == -1) { + ShowWarning("itemdb_readdb_job_sub: unknown job name '%s'!\n", job_name); + } else { + itemdb->jobid2mapid(id->class_base, job_id, libconfig->setting_get_bool(it)); + } + } } /** @@ -1553,9 +1885,9 @@ void itemdb_readdb_additional_fields(int itemid, config_setting_t *it, int n, co * validation errors. * @return Nameid of the validated entry, or 0 in case of failure. */ -int itemdb_readdb_libconfig_sub(config_setting_t *it, int n, const char *source) { +int itemdb_readdb_libconfig_sub(struct config_setting_t *it, int n, const char *source) { struct item_data id = { 0 }; - config_setting_t *t = NULL; + struct config_setting_t *t = NULL; const char *str = NULL; int i32 = 0; bool inherit = false; @@ -1681,10 +2013,17 @@ int itemdb_readdb_libconfig_sub(config_setting_t *it, int n, const char *source) if( itemdb->lookup_const(it, "Slots", &i32) && i32 >= 0 ) id.slot = i32; - if( itemdb->lookup_const(it, "Job", &i32) ) // This is an unsigned value, do not check for >= 0 - itemdb->jobid2mapid(id.class_base, (unsigned int)i32); - else if( !inherit ) - itemdb->jobid2mapid(id.class_base, UINT_MAX); + if ((t = libconfig->setting_get_member(it, "Job")) != NULL) { + if (config_setting_is_group(t)) { + itemdb->readdb_job_sub(&id, t); + } else if (itemdb->lookup_const(it, "Job", &i32)) { // This is an unsigned value, do not check for >= 0 + itemdb->jobmask2mapid(id.class_base, (uint64)i32); + } else if (!inherit) { + itemdb->jobmask2mapid(id.class_base, UINT64_MAX); + } + } else if (!inherit) { + itemdb->jobmask2mapid(id.class_base, UINT64_MAX); + } if( itemdb->lookup_const(it, "Upper", &i32) && i32 >= 0 ) id.class_upper = (unsigned int)i32; @@ -1715,6 +2054,9 @@ int itemdb_readdb_libconfig_sub(config_setting_t *it, int n, const char *source) if( (t = libconfig->setting_get_member(it, "Refine")) ) id.flag.no_refine = libconfig->setting_get_bool(t) ? 0 : 1; + + if ((t = libconfig->setting_get_member(it, "DisableOptions"))) + id.flag.no_options = libconfig->setting_get_bool(t) ? 1 : 0; if( itemdb->lookup_const(it, "View", &i32) && i32 >= 0 ) id.look = i32; @@ -1731,12 +2073,15 @@ int itemdb_readdb_libconfig_sub(config_setting_t *it, int n, const char *source) if ((t = libconfig->setting_get_member(it, "KeepAfterUse"))) id.flag.keepafteruse = libconfig->setting_get_bool(t) ? 1 : 0; + if ((t = libconfig->setting_get_member(it, "DropAnnounce"))) + id.flag.drop_announce = libconfig->setting_get_bool(t) ? 1 : 0; + if (itemdb->lookup_const(it, "Delay", &i32) && i32 >= 0) id.delay = i32; if ( (t = libconfig->setting_get_member(it, "Trade")) ) { if (config_setting_is_group(t)) { - config_setting_t *tt = NULL; + struct config_setting_t *tt = NULL; if ((tt = libconfig->setting_get_member(t, "override"))) { id.gm_lv_trade_override = libconfig->setting_get_int(tt); @@ -1802,7 +2147,7 @@ int itemdb_readdb_libconfig_sub(config_setting_t *it, int n, const char *source) if ((t = libconfig->setting_get_member(it, "Nouse"))) { if (config_setting_is_group(t)) { - config_setting_t *nt = NULL; + struct config_setting_t *nt = NULL; if ((nt = libconfig->setting_get_member(t, "override"))) { id.item_usage.override = libconfig->setting_get_int(nt); @@ -1850,7 +2195,7 @@ int itemdb_readdb_libconfig_sub(config_setting_t *it, int n, const char *source) return itemdb->validate_entry(&id, n, source); } -bool itemdb_lookup_const(const config_setting_t *it, const char *name, int *value) +bool itemdb_lookup_const(const struct config_setting_t *it, const char *name, int *value) { nullpo_retr(false, name); nullpo_retr(false, value); @@ -1879,19 +2224,24 @@ bool itemdb_lookup_const(const config_setting_t *it, const char *name, int *valu */ int itemdb_readdb_libconfig(const char *filename) { bool duplicate[MAX_ITEMDB]; - config_t item_db_conf; - config_setting_t *itdb, *it; + struct config_t item_db_conf; + struct config_setting_t *itdb, *it; char filepath[256]; int i = 0, count = 0; nullpo_ret(filename); + sprintf(filepath, "%s/%s", map->db_path, filename); - memset(&duplicate,0,sizeof(duplicate)); - if( libconfig->read_file(&item_db_conf, filepath) || !(itdb = libconfig->setting_get_member(item_db_conf.root, "item_db")) ) { + if (!libconfig->load_file(&item_db_conf, filepath)) + return 0; + + if ((itdb = libconfig->setting_get_member(item_db_conf.root, "item_db")) == NULL) { ShowError("can't read %s\n", filepath); return 0; } + memset(&duplicate,0,sizeof(duplicate)); + while( (it = libconfig->setting_get_elem(itdb,i++)) ) { int nameid = itemdb->readdb_libconfig_sub(it, i-1, filename); @@ -1928,7 +2278,7 @@ uint64 itemdb_unique_id(struct map_session_data *sd) { */ void itemdb_read(bool minimal) { int i; - DBData prev; + struct DBData prev; const char *filename[] = { DBPATH"item_db.conf", @@ -1953,6 +2303,7 @@ void itemdb_read(bool minimal) { itemdb->read_groups(); itemdb->read_chains(); itemdb->read_packages(); + itemdb->read_options(); } @@ -2005,7 +2356,7 @@ void destroy_item_data(struct item_data* self, int free_self) /** * @see DBApply */ -int itemdb_final_sub(DBKey key, DBData *data, va_list ap) +int itemdb_final_sub(union DBKey key, struct DBData *data, va_list ap) { struct item_data *id = DB->data2ptr(data); @@ -2014,6 +2365,17 @@ int itemdb_final_sub(DBKey key, DBData *data, va_list ap) return 0; } + +int itemdb_options_final_sub(union DBKey key, struct DBData *data, va_list ap) +{ + struct item_option *ito = DB->data2ptr(data); + + if (ito->script != NULL) + script->free_code(ito->script); + + return 0; +} + void itemdb_clear(bool total) { int i; // clear the previous itemdb data @@ -2077,6 +2439,7 @@ void itemdb_clear(bool total) { return; itemdb->other->clear(itemdb->other, itemdb->final_sub); + itemdb->options->clear(itemdb->options, itemdb->options_final_sub); memset(itemdb->array, 0, sizeof(itemdb->array)); @@ -2127,22 +2490,27 @@ void itemdb_reload(void) { for (sd = BL_UCAST(BL_PC, mapit->first(iter)); mapit->exists(iter); sd = BL_UCAST(BL_PC, mapit->next(iter))) { memset(sd->item_delay, 0, sizeof(sd->item_delay)); // reset item delays pc->setinventorydata(sd); - if( battle_config.item_check ) - sd->state.itemcheck = 1; + + if (battle->bc->item_check != PCCHECKITEM_NONE) // Check and flag items for inspection. + sd->itemcheck = (enum pc_checkitem_types) battle->bc->item_check; + /* clear combo bonuses */ - if( sd->combo_count ) { + if (sd->combo_count) { aFree(sd->combos); sd->combos = NULL; sd->combo_count = 0; if( pc->load_combo(sd) > 0 ) status_calc_pc(sd,SCO_FORCE); } + + // Check for and delete unavailable/disabled items. pc->checkitem(sd); } mapit->free(iter); } -void itemdb_name_constants(void) { - DBIterator *iter = db_iterator(itemdb->names); +void itemdb_name_constants(void) +{ + struct DBIterator *iter = db_iterator(itemdb->names); struct item_data *data; #ifdef ENABLE_CASE_CHECK @@ -2160,6 +2528,7 @@ void do_final_itemdb(void) { itemdb->clear(true); itemdb->other->destroy(itemdb->other, itemdb->final_sub); + itemdb->options->destroy(itemdb->options, itemdb->options_final_sub); itemdb->destroy_item_data(&itemdb->dummy, 0); db_destroy(itemdb->names); } @@ -2167,6 +2536,7 @@ void do_final_itemdb(void) { void do_init_itemdb(bool minimal) { memset(itemdb->array, 0, sizeof(itemdb->array)); itemdb->other = idb_alloc(DB_OPT_BASE); + itemdb->options = idb_alloc(DB_OPT_RELEASE_DATA); itemdb->names = strdb_alloc(DB_OPT_BASE,ITEM_NAME_LENGTH); itemdb->create_dummy_data(); //Dummy data item. itemdb->read(minimal); @@ -2177,7 +2547,7 @@ void do_init_itemdb(bool minimal) { clif->cashshop_load(); /** it failed? we disable it **/ - if( !clif->parse_roulette_db() ) + if (battle_config.feature_roulette == 1 && !clif->parse_roulette_db()) battle_config.feature_roulette = 0; } void itemdb_defaults(void) { @@ -2209,6 +2579,7 @@ void itemdb_defaults(void) { itemdb->read_groups = itemdb_read_groups; itemdb->read_chains = itemdb_read_chains; itemdb->read_packages = itemdb_read_packages; + itemdb->read_options = itemdb_read_options; /* */ itemdb->write_cached_packages = itemdb_write_cached_packages; itemdb->read_cached_packages = itemdb_read_cached_packages; @@ -2219,6 +2590,7 @@ void itemdb_defaults(void) { itemdb->load = itemdb_load; itemdb->search = itemdb_search; itemdb->exists = itemdb_exists; + itemdb->option_exists = itemdb_option_exists; itemdb->in_group = itemdb_in_group; itemdb->group_item = itemdb_searchrandomid; itemdb->chain_item = itemdb_chain_item; @@ -2227,6 +2599,7 @@ void itemdb_defaults(void) { itemdb->searchname_array_sub = itemdb_searchname_array_sub; itemdb->searchrandomid = itemdb_searchrandomid; itemdb->typename = itemdb_typename; + itemdb->jobmask2mapid = itemdb_jobmask2mapid; itemdb->jobid2mapid = itemdb_jobid2mapid; itemdb->create_dummy_data = create_dummy_data; itemdb->create_item_data = create_item_data; @@ -2250,13 +2623,16 @@ void itemdb_defaults(void) { itemdb->read_combos = itemdb_read_combos; itemdb->gendercheck = itemdb_gendercheck; itemdb->validate_entry = itemdb_validate_entry; + itemdb->readdb_options_additional_fields = itemdb_readdb_options_additional_fields; itemdb->readdb_additional_fields = itemdb_readdb_additional_fields; + itemdb->readdb_job_sub = itemdb_readdb_job_sub; itemdb->readdb_libconfig_sub = itemdb_readdb_libconfig_sub; itemdb->readdb_libconfig = itemdb_readdb_libconfig; itemdb->unique_id = itemdb_unique_id; itemdb->read = itemdb_read; itemdb->destroy_item_data = destroy_item_data; itemdb->final_sub = itemdb_final_sub; + itemdb->options_final_sub = itemdb_options_final_sub; itemdb->clear = itemdb_clear; itemdb->id2combo = itemdb_id2combo; itemdb->is_item_usable = itemdb_is_item_usable; |