diff options
Diffstat (limited to 'src/map/itemdb.c')
-rw-r--r-- | src/map/itemdb.c | 1112 |
1 files changed, 817 insertions, 295 deletions
diff --git a/src/map/itemdb.c b/src/map/itemdb.c index c59f627cc..2b8200c06 100644 --- a/src/map/itemdb.c +++ b/src/map/itemdb.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,6 +27,7 @@ #include "map/map.h" #include "map/mob.h" // MAX_MOB_DB #include "map/pc.h" // W_MUSICAL, W_WHIP +#include "map/refine.h" #include "map/script.h" // item script processing #include "common/HPM.h" #include "common/conf.h" @@ -41,7 +42,7 @@ #include <stdlib.h> #include <string.h> -struct itemdb_interface itemdb_s; +static struct itemdb_interface itemdb_s; struct itemdb_interface *itemdb; /** @@ -49,7 +50,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(union DBKey key, struct DBData *data, va_list ap) +static int itemdb_searchname_sub(union DBKey key, struct DBData *data, va_list ap) { struct item_data *item = DB->data2ptr(data), **dst, **dst2; char *str; @@ -78,7 +79,8 @@ int itemdb_searchname_sub(union DBKey key, struct DBData *data, va_list ap) /*========================================== * Return item data from item name. (lookup) *------------------------------------------*/ -struct item_data* itemdb_searchname(const char *str) { +static struct item_data *itemdb_searchname(const char *str) +{ struct item_data* item; struct item_data* item2=NULL; int i; @@ -105,83 +107,118 @@ struct item_data* itemdb_searchname(const char *str) { return item?item:item2; } /* name to item data */ -struct item_data* itemdb_name2id(const char *str) { +static struct item_data *itemdb_name2id(const char *str) +{ return strdb_get(itemdb->names,str); } /** * @see DBMatcher */ -int itemdb_searchname_array_sub(union DBKey key, struct DBData data, va_list ap) +static int itemdb_searchname_array_sub(union DBKey key, struct DBData data, va_list ap) { - struct item_data *item = DB->data2ptr(&data); - char *str; - str=va_arg(ap,char *); + struct item_data *itd = DB->data2ptr(&data); + const char *str = va_arg(ap, const char *); + enum item_name_search_flag flag = va_arg(ap, enum item_name_search_flag); + nullpo_ret(str); - if (item == &itemdb->dummy) + + if (itd == &itemdb->dummy) return 1; //Invalid item. - if(stristr(item->jname,str)) - return 0; - if(battle_config.case_sensitive_aegisnames && strstr(item->name,str)) - return 0; - if(!battle_config.case_sensitive_aegisnames && stristr(item->name,str)) + + if ( + (flag == IT_SEARCH_NAME_PARTIAL + && (stristr(itd->jname, str) != NULL + || (battle_config.case_sensitive_aegisnames && strstr(itd->name, str)) + || (!battle_config.case_sensitive_aegisnames && stristr(itd->name, str)) + )) + || (flag == IT_SEARCH_NAME_EXACT + && (strcmp(itd->jname, str) == 0 + || (battle_config.case_sensitive_aegisnames && strcmp(itd->name, str) == 0) + || (!battle_config.case_sensitive_aegisnames && strcasecmp(itd->name, str) == 0) + )) + ) { + return 0; - return strcmpi(item->jname,str); + } else { + return 1; + } } -/*========================================== - * Founds up to N matches. Returns number of matches [Skotlex] - * search flag : - * 0 - approximate match - * 1 - exact match - *------------------------------------------*/ -int itemdb_searchname_array(struct item_data** data, int size, const char *str, int flag) { - struct item_data* item; - int i; - int count=0; - +/** + * Finds up to passed size matches + * @param data array of struct item_data for returning the results in + * @param size size of the array + * @param str string used in this search + * @param flag search mode refer to enum item_name_search_flag for possible values + * @return returns all found matches in the database which could be bigger than size + **/ +static int itemdb_searchname_array(struct item_data **data, const int size, const char *str, enum item_name_search_flag flag) +{ nullpo_ret(data); nullpo_ret(str); - // Search in the array - for( i = 0; i < ARRAYLENGTH(itemdb->array); ++i ) - { - item = itemdb->array[i]; - if( item == NULL ) + Assert_ret(flag >= IT_SEARCH_NAME_PARTIAL && flag < IT_SEARCH_NAME_MAX); + Assert_ret(size > 0); + + int + results_count = 0, + length = 0; + + // Search in array + for (int i = 0; i < ARRAYLENGTH(itemdb->array); ++i) { + struct item_data *itd = itemdb->array[i]; + + if (itd == NULL) continue; - if( - (!flag - && (stristr(item->jname,str) - || (battle_config.case_sensitive_aegisnames && strstr(item->name,str)) - || (!battle_config.case_sensitive_aegisnames && stristr(item->name,str)) - )) - || (flag - && (strcmp(item->jname,str) == 0 - || (battle_config.case_sensitive_aegisnames && strcmp(item->name,str) == 0) - || (!battle_config.case_sensitive_aegisnames && strcasecmp(item->name,str) == 0) - )) - ) { - if( count < size ) - data[count] = item; - ++count; + if ( + (flag == IT_SEARCH_NAME_PARTIAL + && (stristr(itd->jname, str) != NULL + || (battle_config.case_sensitive_aegisnames && strstr(itd->name, str)) + || (!battle_config.case_sensitive_aegisnames && stristr(itd->name, str)) + )) + || (flag == IT_SEARCH_NAME_EXACT + && (strcmp(itd->jname, str) == 0 + || (battle_config.case_sensitive_aegisnames && strcmp(itd->name, str) == 0) + || (!battle_config.case_sensitive_aegisnames && strcasecmp(itd->name, str) == 0) + )) + ) { + if (length < size) { + data[length] = itd; + ++length; + } + + ++results_count; } } - // search in the db - if( count < size ) - { - struct DBData *db_data[MAX_SEARCH]; - int db_count = 0; - size -= count; - 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; + // Search in dbmap + int dbmap_size = size - length; + if (dbmap_size > 0) { + struct DBData **dbmap_data = NULL; + int dbmap_count = 0; + CREATE(dbmap_data, struct DBData *, dbmap_size); + + dbmap_count = itemdb->other->getall(itemdb->other, dbmap_data, dbmap_size, itemdb->searchname_array_sub, str, flag); + dbmap_size = min(dbmap_count, dbmap_size); + + for (int i = 0; i < dbmap_size; ++i) { + data[length] = DB->data2ptr(dbmap_data[i]); + ++length; + } + + results_count += dbmap_count; + aFree(dbmap_data); + } else { // We got all matches we can return, so we only need to count now. + results_count += itemdb->other->getall(itemdb->other, NULL, 0, itemdb->searchname_array_sub, str, flag); } - return count; + + return results_count; } + /* [Ind/Hercules] */ -int itemdb_chain_item(unsigned short chain_id, int *rate) { +static int itemdb_chain_item(unsigned short chain_id, int *rate) +{ struct item_chain_entry *entry; if( chain_id >= itemdb->chain_count ) { @@ -199,7 +236,8 @@ int itemdb_chain_item(unsigned short chain_id, int *rate) { return entry->id; } /* [Ind/Hercules] */ -void itemdb_package_item(struct map_session_data *sd, struct item_package *package) { +static void itemdb_package_item(struct map_session_data *sd, struct item_package *package) +{ int i = 0, get_count, j, flag; nullpo_retv(sd); @@ -287,7 +325,8 @@ void itemdb_package_item(struct map_session_data *sd, struct item_package *packa /*========================================== * Return a random item id from group. (takes into account % chance giving/tot group) *------------------------------------------*/ -int itemdb_searchrandomid(struct item_group *group) { +static int itemdb_searchrandomid(struct item_group *group) +{ nullpo_retr(UNKNOWN_ITEM_ID, group); if (group->qty) @@ -296,7 +335,8 @@ int itemdb_searchrandomid(struct item_group *group) { ShowError("itemdb_searchrandomid: No item entries for group id %d\n", group->id); return UNKNOWN_ITEM_ID; } -bool itemdb_in_group(struct item_group *group, int nameid) { +static bool itemdb_in_group(struct item_group *group, int nameid) +{ int i; nullpo_retr(false, group); @@ -309,7 +349,7 @@ bool itemdb_in_group(struct item_group *group, int nameid) { /// Searches for the item_data. /// Returns the item_data or NULL if it does not exist. -struct item_data* itemdb_exists(int nameid) +static struct item_data *itemdb_exists(int nameid) { struct item_data* item; @@ -321,9 +361,19 @@ 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 itemdb_option data or NULL. + */ +static struct itemdb_option *itemdb_option_exists(int idx) +{ + return (struct itemdb_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) +static const char *itemdb_typename(int type) { switch(type) { @@ -342,7 +392,7 @@ const char* itemdb_typename(int type) return "Unknown Type"; } - /** +/** * Converts the JobID to the format used by map-server to check item * restriction as per job. * @@ -352,14 +402,14 @@ const char* itemdb_typename(int type) * * @author Dastgir */ -void itemdb_jobid2mapid(uint64 *bclass, int job_id, bool enable) +static void itemdb_jobid2mapid(uint64 *bclass, int job_class, bool enable) { uint64 mask[3] = { 0 }; int i; nullpo_retv(bclass); - switch(job_id) { + switch (job_class) { // Base Classes case JOB_NOVICE: case JOB_SUPER_NOVICE: @@ -367,12 +417,22 @@ void itemdb_jobid2mapid(uint64 *bclass, int job_id, bool enable) 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_NOVICE+job_id); + mask[0] = 1ULL << MAPID_THIEF; break; // 2-1 Classes case JOB_KNIGHT: @@ -437,6 +497,9 @@ void itemdb_jobid2mapid(uint64 *bclass, int job_id, bool enable) 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; @@ -466,9 +529,8 @@ void itemdb_jobid2mapid(uint64 *bclass, int job_id, bool enable) * @param bclass Pointer to the variable containing the new format. * @param jobmask Variable containing JobMask. */ -void itemdb_jobmask2mapid(uint64 *bclass, uint64 jobmask) +static void itemdb_jobmask2mapid(uint64 *bclass, uint64 jobmask) { - int i; nullpo_retv(bclass); bclass[0] = bclass[1] = bclass[2] = 0; //Base classes @@ -477,10 +539,18 @@ void itemdb_jobmask2mapid(uint64 *bclass, uint64 jobmask) bclass[0] |= 1ULL<<MAPID_NOVICE; bclass[1] |= 1ULL<<MAPID_NOVICE; } - for (i = JOB_NOVICE+1; i <= JOB_THIEF; i++) { - if (jobmask & 1ULL<<i) - bclass[0] |= 1ULL<<(MAPID_NOVICE+i); - } + 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 & 1ULL<<JOB_KNIGHT) bclass[1] |= 1ULL<<MAPID_SWORDMAN; @@ -538,9 +608,11 @@ void itemdb_jobmask2mapid(uint64 *bclass, uint64 jobmask) 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) +static void create_dummy_data(void) { memset(&itemdb->dummy, 0, sizeof(struct item_data)); itemdb->dummy.nameid=500; @@ -552,7 +624,7 @@ void create_dummy_data(void) itemdb->dummy.view_id=UNKNOWN_ITEM_ID; } -struct item_data* create_item_data(int nameid) +static struct item_data *create_item_data(int nameid) { struct item_data *id; CREATE(id, struct item_data, 1); @@ -565,7 +637,8 @@ struct item_data* create_item_data(int nameid) /*========================================== * Loads (and creates if not found) an item from the db. *------------------------------------------*/ -struct item_data* itemdb_load(int nameid) { +static struct item_data *itemdb_load(int nameid) +{ struct item_data *id; if( nameid >= 0 && nameid < ARRAYLENGTH(itemdb->array) ) @@ -588,7 +661,7 @@ struct item_data* itemdb_load(int nameid) { /*========================================== * Loads an item from the db. If not found, it will return the dummy item. *------------------------------------------*/ -struct item_data* itemdb_search(int nameid) +static struct item_data *itemdb_search(int nameid) { struct item_data* id; if( nameid >= 0 && nameid < ARRAYLENGTH(itemdb->array) ) @@ -608,7 +681,7 @@ struct item_data* itemdb_search(int nameid) /*========================================== * Returns if given item is a player-equippable piece. *------------------------------------------*/ -int itemdb_isequip(int nameid) +static int itemdb_isequip(int nameid) { int type=itemdb_type(nameid); switch (type) { @@ -624,7 +697,8 @@ int itemdb_isequip(int nameid) /*========================================== * Alternate version of itemdb_isequip *------------------------------------------*/ -int itemdb_isequip2(struct item_data *data) { +static int itemdb_isequip2(struct item_data *data) +{ nullpo_ret(data); switch(data->type) { case IT_WEAPON: @@ -639,7 +713,7 @@ int itemdb_isequip2(struct item_data *data) { /*========================================== * Returns if given item's type is stackable. *------------------------------------------*/ -int itemdb_isstackable(int nameid) +static int itemdb_isstackable(int nameid) { int type=itemdb_type(nameid); switch(type) { @@ -656,7 +730,7 @@ int itemdb_isstackable(int nameid) /*========================================== * Alternate version of itemdb_isstackable *------------------------------------------*/ -int itemdb_isstackable2(struct item_data *data) +static int itemdb_isstackable2(struct item_data *data) { nullpo_ret(data); switch(data->type) { @@ -673,43 +747,52 @@ int itemdb_isstackable2(struct item_data *data) /*========================================== * Trade Restriction functions [Skotlex] *------------------------------------------*/ -int itemdb_isdropable_sub(struct item_data *item, int gmlv, int unused) { +static int itemdb_isdropable_sub(struct item_data *item, int gmlv, int unused) +{ return (item && (!(item->flag.trade_restriction&ITR_NODROP) || gmlv >= item->gm_lv_trade_override)); } -int itemdb_cantrade_sub(struct item_data* item, int gmlv, int gmlv2) { +static int itemdb_cantrade_sub(struct item_data *item, int gmlv, int gmlv2) +{ return (item && (!(item->flag.trade_restriction&ITR_NOTRADE) || gmlv >= item->gm_lv_trade_override || gmlv2 >= item->gm_lv_trade_override)); } -int itemdb_canpartnertrade_sub(struct item_data* item, int gmlv, int gmlv2) { +static int itemdb_canpartnertrade_sub(struct item_data *item, int gmlv, int gmlv2) +{ return (item && (item->flag.trade_restriction&ITR_PARTNEROVERRIDE || gmlv >= item->gm_lv_trade_override || gmlv2 >= item->gm_lv_trade_override)); } -int itemdb_cansell_sub(struct item_data* item, int gmlv, int unused) { +static int itemdb_cansell_sub(struct item_data *item, int gmlv, int unused) +{ return (item && (!(item->flag.trade_restriction&ITR_NOSELLTONPC) || gmlv >= item->gm_lv_trade_override)); } -int itemdb_cancartstore_sub(struct item_data* item, int gmlv, int unused) { +static int itemdb_cancartstore_sub(struct item_data *item, int gmlv, int unused) +{ return (item && (!(item->flag.trade_restriction&ITR_NOCART) || gmlv >= item->gm_lv_trade_override)); } -int itemdb_canstore_sub(struct item_data* item, int gmlv, int unused) { +static int itemdb_canstore_sub(struct item_data *item, int gmlv, int unused) +{ return (item && (!(item->flag.trade_restriction&ITR_NOSTORAGE) || gmlv >= item->gm_lv_trade_override)); } -int itemdb_canguildstore_sub(struct item_data* item, int gmlv, int unused) { +static int itemdb_canguildstore_sub(struct item_data *item, int gmlv, int unused) +{ return (item && (!(item->flag.trade_restriction&ITR_NOGSTORAGE) || gmlv >= item->gm_lv_trade_override)); } -int itemdb_canmail_sub(struct item_data* item, int gmlv, int unused) { +static int itemdb_canmail_sub(struct item_data *item, int gmlv, int unused) +{ return (item && (!(item->flag.trade_restriction&ITR_NOMAIL) || gmlv >= item->gm_lv_trade_override)); } -int itemdb_canauction_sub(struct item_data* item, int gmlv, int unused) { +static int itemdb_canauction_sub(struct item_data *item, int gmlv, int unused) +{ return (item && (!(item->flag.trade_restriction&ITR_NOAUCTION) || gmlv >= item->gm_lv_trade_override)); } -int itemdb_isrestricted(struct item* item, int gmlv, int gmlv2, int (*func)(struct item_data*, int, int)) +static int itemdb_isrestricted(struct item *item, int gmlv, int gmlv2, int (*func)(struct item_data*, int, int)) { struct item_data* item_data; int i; @@ -733,7 +816,8 @@ int itemdb_isrestricted(struct item* item, int gmlv, int gmlv2, int (*func)(stru /*========================================== * Specifies if item-type should drop unidentified. *------------------------------------------*/ -int itemdb_isidentified(int nameid) { +static int itemdb_isidentified(int nameid) +{ int type=itemdb_type(nameid); switch (type) { case IT_WEAPON: @@ -745,7 +829,8 @@ int itemdb_isidentified(int nameid) { } } /* same as itemdb_isidentified but without a lookup */ -int itemdb_isidentified2(struct item_data *data) { +static int itemdb_isidentified2(struct item_data *data) +{ nullpo_ret(data); switch (data->type) { case IT_WEAPON: @@ -757,14 +842,12 @@ int itemdb_isidentified2(struct item_data *data) { } } -void itemdb_read_groups(void) { +static void itemdb_read_groups(void) +{ 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 - const char *config_filename = "db/pre-re/item_group.conf"; // FIXME hardcoded name -#endif + char config_filename[256]; + libconfig->format_db_path(DBPATH"item_group.conf", config_filename, sizeof(config_filename)); const char *itname; int i = 0, count = 0, c; unsigned int *gsize = NULL; @@ -810,7 +893,7 @@ void itemdb_read_groups(void) { itemdb->groups[count].id = data->nameid; itemdb->groups[count].qty = gsize[ count ]; - CREATE(itemdb->groups[count].nameid, unsigned short, gsize[ count ] + 1); + CREATE(itemdb->groups[count].nameid, int, gsize[count] + 1); c = 0; while( (it = libconfig->setting_get_elem(itg,c++)) ) { int repeat = 1; @@ -820,7 +903,7 @@ void itemdb_read_groups(void) { } else itname = libconfig->setting_get_string_elem(itg,c - 1); - if( itname[0] == 'I' && itname[1] == 'D' && strlen(itname) < 8 ) { + if (itname[0] == 'I' && itname[1] == 'D' && strlen(itname) <= 12) { if( !( data = itemdb->exists(atoi(itname+2)) ) ) ShowWarning("itemdb_read_groups: unknown item ID '%d' in group '%s'!\n",atoi(itname+2),config_setting_name(itg)); } else if( !( data = itemdb->name2id(itname) ) ) @@ -845,7 +928,8 @@ void itemdb_read_groups(void) { } /* [Ind/Hercules] - HCache for Packages */ -void itemdb_write_cached_packages(const char *config_filename) { +static void itemdb_write_cached_packages(const char *config_filename) +{ FILE *file; unsigned short pcount = itemdb->package_count; unsigned short i; @@ -859,7 +943,8 @@ void itemdb_write_cached_packages(const char *config_filename) { hwrite(&pcount,sizeof(pcount),1,file); for(i = 0; i < pcount; i++) { - unsigned short id = itemdb->packages[i].id, random_qty = itemdb->packages[i].random_qty, must_qty = itemdb->packages[i].must_qty; + int id = itemdb->packages[i].id; + unsigned short random_qty = itemdb->packages[i].random_qty, must_qty = itemdb->packages[i].must_qty; unsigned short c; //into a package, first 2 bytes = id. hwrite(&id,sizeof(id),1,file); @@ -916,7 +1001,9 @@ void itemdb_write_cached_packages(const char *config_filename) { return; } -bool itemdb_read_cached_packages(const char *config_filename) { + +static bool itemdb_read_cached_packages(const char *config_filename) +{ FILE *file; unsigned short pcount = 0; unsigned short i; @@ -933,12 +1020,13 @@ bool itemdb_read_cached_packages(const char *config_filename) { itemdb->package_count = pcount; for( i = 0; i < pcount; i++ ) { - unsigned short id = 0, random_qty = 0, must_qty = 0; + int id = 0; + unsigned short random_qty = 0, must_qty = 0; struct item_data *pdata; struct item_package *package = &itemdb->packages[i]; unsigned short c; - //into a package, first 2 bytes = id. + //into a package, first 4 bytes = id. hread(&id,sizeof(id),1,file); //next 2 bytes = must count hread(&must_qty,sizeof(must_qty),1,file); @@ -961,10 +1049,11 @@ bool itemdb_read_cached_packages(const char *config_filename) { //now we loop into must for(c = 0; c < package->must_qty; c++) { struct item_package_must_entry *entry = &itemdb->packages[i].must_items[c]; - unsigned short mid = 0, qty = 0, hours = 0; + int mid = 0; + unsigned short qty = 0, hours = 0; unsigned char announce = 0, named = 0, force_serial = 0; struct item_data *data; - //first 2 byte = item id + //first 4 byte = item id hread(&mid,sizeof(mid),1,file); //next 2 byte = qty hread(&qty,sizeof(qty),1,file); @@ -1004,7 +1093,8 @@ bool itemdb_read_cached_packages(const char *config_filename) { //now we loop into the group's list for(h = 0; h < group_qty; h++) { struct item_package_rand_entry *entry = &itemdb->packages[i].random_groups[c].random_list[h]; - unsigned short mid = 0, qty = 0, hours = 0, rate = 0; + int mid = 0; + unsigned short qty = 0, hours = 0, rate = 0; unsigned char announce = 0, named = 0, force_serial = 0; struct item_data *data; @@ -1047,14 +1137,12 @@ bool itemdb_read_cached_packages(const char *config_filename) { return true; } -void itemdb_read_packages(void) { +static void itemdb_read_packages(void) +{ 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 - const char *config_filename = "db/pre-re/item_packages.conf"; // FIXME hardcoded name -#endif + char config_filename[256]; + libconfig->format_db_path(DBPATH"item_packages.conf", config_filename, sizeof(config_filename)); const char *itname; int i = 0, count = 0, c = 0, highest_gcount = 0; unsigned int *must = NULL, *random = NULL, *rgroup = NULL, **rgroups = NULL; @@ -1179,7 +1267,7 @@ void itemdb_read_packages(void) { itname = config_setting_name(it); - if( itname[0] == 'I' && itname[1] == 'D' && strlen(itname) < 8 ) { + if (itname[0] == 'I' && itname[1] == 'D' && strlen(itname) <= 12) { if( !( data = itemdb->exists(atoi(itname+2)) ) ) ShowWarning("itemdb_read_packages: unknown item ID '%d' in package '%s'!\n",atoi(itname+2),config_setting_name(itg)); } else if( !( data = itemdb->name2id(itname) ) ) @@ -1277,14 +1365,132 @@ void itemdb_read_packages(void) { ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, config_filename); } -void itemdb_read_chains(void) { +/** + * 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. + */ +static void itemdb_readdb_options_additional_fields(struct itemdb_option *ito, struct config_setting_t *t, const char *source) +{ + // do nothing. plugins can do their own work +} + +/** + * Reads the Item Options configuration file. + */ +static void itemdb_read_options(void) +{ + struct config_t item_options_db; + struct config_setting_t *ito = NULL, *conf = NULL; + int index = 0, count = 0; + char filepath[256]; + libconfig->format_db_path("item_options.conf", filepath, sizeof(filepath)); + 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 itemdb_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 itemdb_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); +} + +static void itemdb_read_chains(void) +{ 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 - const char *config_filename = "db/pre-re/item_chain.conf"; // FIXME hardcoded name -#endif + char config_filename[256]; + libconfig->format_db_path(DBPATH"item_chain.conf", config_filename, sizeof(config_filename)); int i = 0, count = 0; if (!libconfig->load_file(&item_chain_conf, config_filename)) @@ -1310,7 +1516,7 @@ void itemdb_read_chains(void) { while( (entry = libconfig->setting_get_elem(itc,c++)) ) { const char *itname = config_setting_name(entry); - if( itname[0] == 'I' && itname[1] == 'D' && strlen(itname) < 8 ) { + if (itname[0] == 'I' && itname[1] == 'D' && strlen(itname) <= 12) { if( !( data = itemdb->exists(atoi(itname+2)) ) ) ShowWarning("itemdb_read_chains: unknown item ID '%d' in chain '%s'!\n",atoi(itname+2),name); } else if( !( data = itemdb->name2id(itname) ) ) @@ -1341,155 +1547,127 @@ void itemdb_read_chains(void) { else itemdb->chain_cache[ECC_ORE] = i; - ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, config_filename); -} - -/** - * @return: amount of retrieved entries. - **/ -int itemdb_combo_split_atoi (char *str, int *val) { - int i; - - nullpo_ret(val); - - for (i=0; i<MAX_ITEMS_PER_COMBO; i++) { - if (!str) break; - - val[i] = atoi(str); - str = strchr(str,':'); - if (str) - *str++=0; - } + if (!script->get_constant("ITMCHAIN_SIEGFRIED", &i)) + ShowWarning("itemdb_read_chains: failed to find 'ITMCHAIN_SIEGFRIED' chain to link to cache!\n"); + else + itemdb->chain_cache[ECC_SIEGFRIED] = i; - if( i == 0 ) //No data found. - return 0; + if (!script->get_constant("ITMCHAIN_NEO_INSURANCE", &i)) + ShowWarning("itemdb_read_chains: failed to find 'ITMCHAIN_NEO_INSURANCE' chain to link to cache!\n"); + else + itemdb->chain_cache[ECC_NEO_INSURANCE] = i; - return i; + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, config_filename); } -/** - * <combo{:combo{:combo:{..}}}>,<{ script }> - **/ -void itemdb_read_combos(void) + +static bool itemdb_read_combodb_libconfig(void) { - uint32 lines = 0, count = 0; - char line[1024]; + struct config_t combo_conf; char filepath[256]; - FILE* fp; - - sprintf(filepath, "%s/%s", map->db_path, DBPATH"item_combo_db.txt"); + safesnprintf(filepath, sizeof(filepath), "%s/%s/%s", map->db_path, DBPATH, "item_combo_db.conf"); - if ((fp = fopen(filepath, "r")) == NULL) { - ShowError("itemdb_read_combos: File not found \"%s\".\n", filepath); - return; + if (libconfig->load_file(&combo_conf, filepath) == CONFIG_FALSE) { + ShowError("itemdb_read_combodb_libconfig: can't read %s\n", filepath); + return false; } - // process rows one by one - while(fgets(line, sizeof(line), fp)) { - char *str[2], *p; - - lines++; - - if (line[0] == '/' && line[1] == '/') - continue; + struct config_setting_t *combo_db = NULL; + if ((combo_db = libconfig->setting_get_member(combo_conf.root, "combo_db")) == NULL) { + ShowError("itemdb_read_combodb_libconfig: can't read %s\n", filepath); + return false; + } - memset(str, 0, sizeof(str)); + int i = 0; + int count = 0; + struct config_setting_t *it = NULL; - p = line; - p = trim(p); - if (*p == '\0') - continue;// empty line + while ((it = libconfig->setting_get_elem(combo_db, i++)) != NULL) { + if (itemdb->read_combodb_libconfig_sub(it, i - 1, filepath)) + ++count; + } - if (!strchr(p,',')) { - /* is there even a single column? */ - ShowError("itemdb_read_combos: Insufficient columns in line %u of \"%s\", skipping.\n", lines, filepath); - continue; - } + libconfig->destroy(&combo_conf); + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filepath); + return true; +} - str[0] = p; - p = strchr(p,','); - *p = '\0'; - p++; +static bool itemdb_read_combodb_libconfig_sub(struct config_setting_t *it, int idx, const char *source) +{ + nullpo_retr(false, it); + nullpo_retr(false, source); - str[1] = p; - p = strchr(p,','); - p++; + struct config_setting_t *t = NULL; - if (str[1][0] != '{') { - ShowError("itemdb_read_combos(#1): Invalid format (Script column) in line %u of \"%s\", skipping.\n", lines, filepath); - continue; - } + if ((t = libconfig->setting_get_member(it, "Items")) == NULL) { + ShowWarning("itemdb_read_combodb_libconfig_sub: invalid item list for combo (%d), in (%s), skipping..\n", idx, source); + return false; + } - /* no ending key anywhere (missing \}\) */ - if ( str[1][strlen(str[1])-1] != '}' ) { - 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]; - int v = 0, retcount = 0; - struct item_combo *combo = NULL; + if (!config_setting_is_array(t)) { + ShowWarning("itemdb_read_combodb_libconfig_sub: the combo (%d) item list must be an array, in (%s), skipping..\n", idx, source); + return false; + } - if((retcount = itemdb->combo_split_atoi(str[0], items)) < 2) { - 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; - } + int len = libconfig->setting_length(t); + if (len > MAX_ITEMS_PER_COMBO) { + ShowWarning("itemdb_read_combodb_libconfig_sub: the size of combo (%d) item list is too big (%d, max = %d), in (%s), skipping..\n", idx, len, MAX_ITEMS_PER_COMBO, source); + return false; + } - /* validate */ - for(v = 0; v < retcount; v++) { - if( !itemdb->exists(items[v]) ) { - ShowError("itemdb_read_combos: line %u of \"%s\" contains unknown item ID %d, skipping.\n", lines, filepath, items[v]); - break; - } - } - /* failed at some item */ - if( v < retcount ) - continue; + struct item_combo *combo = NULL; + RECREATE(itemdb->combos, struct item_combo *, ++itemdb->combo_count); + CREATE(combo, struct item_combo, 1); - RECREATE(itemdb->combos, struct item_combo*, ++itemdb->combo_count); + combo->id = itemdb->combo_count - 1; + combo->count = len; - CREATE(combo, struct item_combo, 1); + for (int i = 0; i < len; i++) { + struct item_data *item = NULL; + const char *name = libconfig->setting_get_string_elem(t, i); - combo->count = retcount; - combo->script = script->parse(str[1], filepath, lines, 0, NULL); - combo->id = itemdb->combo_count - 1; - /* populate ->nameid field */ - for( v = 0; v < retcount; v++ ) { - combo->nameid[v] = items[v]; - } + if ((item = itemdb->name2id(name)) == NULL) { + ShowWarning("itemdb_read_combodb_libconfig_sub: unknown item '%s', in (%s), skipping..\n", name, source); + --itemdb->combo_count; + aFree(combo); + return false; + } + combo->nameid[i] = item->nameid; + } - itemdb->combos[itemdb->combo_count - 1] = combo; + const char *str = NULL; + if (libconfig->setting_lookup_string(it, "Script", &str) == CONFIG_TRUE) { + combo->script = *str ? script->parse(str, source, -idx, SCRIPT_IGNORE_EXTERNAL_BRACKETS, NULL) : NULL; + } else { + ShowWarning("itemdb_read_combodb_libconfig_sub: invalid script for combo (%d) in (%s), skipping..\n", idx, source); + --itemdb->combo_count; + aFree(combo); + return false; + } - /* populate the items to refer to this combo */ - for( v = 0; v < retcount; v++ ) { - struct item_data * it; - int index; + itemdb->combos[combo->id] = combo; - it = itemdb->exists(items[v]); - index = it->combos_count; - RECREATE(it->combos, struct item_combo*, ++it->combos_count); - it->combos[index] = combo; - } - } - count++; + /* populate the items to refer to this combo */ + for (int i = 0; i < len; i++) { + struct item_data *item = itemdb->exists(combo->nameid[i]); + RECREATE(item->combos, struct item_combo *, ++item->combos_count); + item->combos[item->combos_count - 1] = combo; } - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%"PRIu32""CL_RESET"' entries in '"CL_WHITE"item_combo_db"CL_RESET"'.\n", count); - - return; + return true; } /*====================================== * Applies gender restrictions according to settings. [Skotlex] *======================================*/ -int itemdb_gendercheck(struct item_data *id) +static int itemdb_gendercheck(struct item_data *id) { nullpo_ret(id); if (id->nameid == WEDDING_RING_M) //Grom Ring return 1; if (id->nameid == WEDDING_RING_F) //Bride Ring return 0; - if (id->look == W_MUSICAL && id->type == IT_WEAPON) //Musical instruments are always male-only + if (id->subtype == W_MUSICAL && id->type == IT_WEAPON) //Musical instruments are always male-only return 1; - if (id->look == W_WHIP && id->type == IT_WEAPON) //Whips are always female-only + if (id->subtype == W_WHIP && id->type == IT_WEAPON) //Whips are always female-only return 0; return (battle_config.ignore_items_gender) ? 2 : id->sex; @@ -1512,14 +1690,20 @@ int itemdb_gendercheck(struct item_data *id) * (i.e. item_db2 inheritance), as it will make sure not to free any scripts * still in use by the new entry. */ -int itemdb_validate_entry(struct item_data *entry, int n, const char *source) { +static int itemdb_validate_entry(struct item_data *entry, int n, const char *source) +{ struct item_data *item; nullpo_ret(entry); nullpo_ret(source); - if( entry->nameid <= 0 || entry->nameid >= MAX_ITEMDB ) { - ShowWarning("itemdb_validate_entry: Invalid item ID %d in entry %d of '%s', allowed values 0 < ID < %d (MAX_ITEMDB), skipping.\n", - entry->nameid, n, source, MAX_ITEMDB); +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 + if (entry->nameid <= 0 || entry->nameid > MAX_ITEM_ID) { +#else + if (entry->nameid <= 0) { +#endif + // item id wrong for any packet versions + ShowWarning("itemdb_validate_entry: Invalid item ID %d in entry %d of '%s', allowed values 0 < ID < %d (MAX_ITEM_ID), skipping.\n", + entry->nameid, n, source, MAX_ITEM_ID); if (entry->script) { script->free_code(entry->script); entry->script = NULL; @@ -1532,7 +1716,54 @@ int itemdb_validate_entry(struct item_data *entry, int n, const char *source) { script->free_code(entry->unequip_script); entry->unequip_script = NULL; } + if (entry->rental_start_script != NULL) { + script->free_code(entry->rental_start_script); + entry->rental_start_script = NULL; + } + if (entry->rental_end_script != NULL) { + script->free_code(entry->rental_end_script); + entry->rental_end_script = NULL; + } return 0; +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 + } +#else + } else if (entry->nameid > MAX_ITEM_ID) { + // item id too big for packet version before item id in 4 bytes + entry->view_id = UNKNOWN_ITEM_ID; + } +#endif + + { + 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; + } + if (entry->rental_start_script != NULL) { + script->free_code(entry->rental_start_script); + entry->rental_start_script = NULL; + } + if (entry->rental_end_script != NULL) { + script->free_code(entry->rental_end_script); + entry->rental_end_script = NULL; + } + return 0; + } } if( entry->type < 0 || entry->type == IT_UNKNOWN || entry->type == IT_UNKNOWN2 @@ -1613,10 +1844,14 @@ 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; + if (entry->type == IT_WEAPON && (entry->subtype <= 0 || entry->subtype >= MAX_SINGLE_WEAPON_TYPE)) { + ShowWarning("itemdb_validate_entry: Invalid View for weapon items. View value %d for item %d (%s) in '%s', defaulting to W_DAGGER.\n", + entry->subtype, entry->nameid, entry->jname, source); + entry->subtype = W_DAGGER; + } else if (entry->type == IT_AMMO && (entry->subtype <= 0 || entry->subtype >= MAX_AMMO_TYPE)) { + ShowWarning("itemdb_validate_entry: Invalid View for ammunition items. View value %d for item %d (%s) in '%s', defaulting to A_ARROW.\n", + entry->subtype, entry->nameid, entry->jname, source); + entry->subtype = A_ARROW; } entry->wlv = cap_value(entry->wlv, REFINE_TYPE_ARMOR, REFINE_TYPE_MAX); @@ -1629,6 +1864,9 @@ 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; @@ -1651,14 +1889,21 @@ int itemdb_validate_entry(struct item_data *entry, int n, const char *source) { script->free_code(item->unequip_script); item->unequip_script = NULL; } - + if (item->rental_start_script != NULL && item->rental_start_script != entry->rental_start_script) { // Don't free if it's inheriting the same script + script->free_code(item->rental_start_script); + item->rental_start_script = NULL; + } + if (item->rental_end_script != NULL && item->rental_end_script != entry->rental_end_script) { // Don't free if it's inheriting the same script + script->free_code(item->rental_end_script); + item->rental_end_script = NULL; + } *item = *entry; return item->nameid; } -void itemdb_readdb_additional_fields(int itemid, struct config_setting_t *it, int n, const char *source) +static 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 } /** @@ -1668,7 +1913,7 @@ void itemdb_readdb_additional_fields(int itemid, struct config_setting_t *it, in * @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) +static void itemdb_readdb_job_sub(struct item_data *id, struct config_setting_t *t) { int idx = 0; struct config_setting_t *it = NULL; @@ -1707,7 +1952,8 @@ void itemdb_readdb_job_sub(struct item_data *id, struct config_setting_t *t) * validation errors. * @return Nameid of the validated entry, or 0 in case of failure. */ -int itemdb_readdb_libconfig_sub(struct config_setting_t *it, int n, const char *source) { +static int itemdb_readdb_libconfig_sub(struct config_setting_t *it, int n, const char *source) +{ struct item_data id = { 0 }; struct config_setting_t *t = NULL; const char *str = NULL; @@ -1766,13 +2012,15 @@ int itemdb_readdb_libconfig_sub(struct config_setting_t *it, int n, const char * * "> * OnEquipScript: <" OnEquip Script "> * OnUnequipScript: <" OnUnequip Script "> + * OnRentalStartScript: <" on renting script "> + * OnRentalEndScript: <" on renting end script "> * Inherit: inherit or override */ if( !itemdb->lookup_const(it, "Id", &i32) ) { ShowWarning("itemdb_readdb_libconfig_sub: Invalid or missing id in \"%s\", entry #%d, skipping.\n", source, n); return 0; } - id.nameid = (uint16)i32; + id.nameid = i32; if( (t = libconfig->setting_get_member(it, "Inherit")) && (inherit = libconfig->setting_get_bool(t)) ) { if( !itemdb->exists(id.nameid) ) { @@ -1808,6 +2056,14 @@ int itemdb_readdb_libconfig_sub(struct config_setting_t *it, int n, const char * else if( !inherit ) id.type = IT_ETC; + if (itemdb->lookup_const(it, "Subtype", &i32) && i32 >= 0) { + if (id.type == IT_WEAPON || id.type == IT_AMMO) + id.subtype = i32; + else + ShowWarning("itemdb_readdb_libconfig_sub: Field 'Subtype' is only allowed for IT_WEAPON or IT_AMMO (Item #%d: %s). Ignoring.\n", + id.nameid, id.name); + } + if( itemdb->lookup_const(it, "Buy", &i32) ) id.value_buy = i32; else if( !inherit ) @@ -1847,7 +2103,7 @@ int itemdb_readdb_libconfig_sub(struct config_setting_t *it, int n, const char * itemdb->jobmask2mapid(id.class_base, UINT64_MAX); } - if( itemdb->lookup_const(it, "Upper", &i32) && i32 >= 0 ) + if (itemdb->lookup_const_mask(it, "Upper", &i32) && i32 >= 0) id.class_upper = (unsigned int)i32; else if( !inherit ) id.class_upper = ITEMUPPER_ALL; @@ -1857,7 +2113,7 @@ int itemdb_readdb_libconfig_sub(struct config_setting_t *it, int n, const char * else if( !inherit ) id.sex = 2; - if( itemdb->lookup_const(it, "Loc", &i32) && i32 >= 0 ) + if (itemdb->lookup_const_mask(it, "Loc", &i32) && i32 >= 0) id.equip = i32; if( itemdb->lookup_const(it, "WeaponLv", &i32) && i32 >= 0 ) @@ -1877,8 +2133,32 @@ int itemdb_readdb_libconfig_sub(struct config_setting_t *it, int n, const char * if( (t = libconfig->setting_get_member(it, "Refine")) ) id.flag.no_refine = libconfig->setting_get_bool(t) ? 0 : 1; - if( itemdb->lookup_const(it, "View", &i32) && i32 >= 0 ) - id.look = i32; + if ((t = libconfig->setting_get_member(it, "DisableOptions"))) + id.flag.no_options = libconfig->setting_get_bool(t) ? 1 : 0; + + if ((t = libconfig->setting_get_member(it, "ShowDropEffect"))) + id.flag.showdropeffect = libconfig->setting_get_bool(t) ? 1 : 0; + + if (itemdb->lookup_const(it, "DropEffectMode", &i32) && i32 >= 0) + id.dropeffectmode = i32; + + if (itemdb->lookup_const(it, "ViewSprite", &i32) && i32 >= 0) + id.view_sprite = i32; + + if (itemdb->lookup_const(it, "View", &i32) && i32 >= 0) { // TODO: Remove (Deprecated - 2016-09-04 [Haru]) + if ((id.type == IT_WEAPON || id.type == IT_AMMO) && id.subtype == 0) { + ShowWarning("itemdb_readdb_libconfig_sub: The 'View' field is deprecated. Please rename it to 'Subtype' (or 'ViewSprite'). (Item #%d: %s)\n", + id.nameid, id.name); + id.subtype = i32; + } else if ((id.type != IT_WEAPON && id.type != IT_AMMO) && id.view_sprite == 0) { + ShowWarning("itemdb_readdb_libconfig_sub: The 'View' field is deprecated. Please rename it to 'ViewSprite' (or 'Subtype'). (Item #%d: %s)\n", + id.nameid, id.name); + id.view_sprite = i32; + } else { + ShowWarning("itemdb_readdb_libconfig_sub: The 'View' field is deprecated. Please rename it to 'Subtype' or 'ViewSprite'. (Item #%d: %s)\n", + id.nameid, id.name); + } + } if( (t = libconfig->setting_get_member(it, "BindOnEquip")) ) id.flag.bindonequip = libconfig->setting_get_bool(t) ? 1 : 0; @@ -1892,6 +2172,9 @@ int itemdb_readdb_libconfig_sub(struct config_setting_t *it, int n, const char * 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; @@ -2008,26 +2291,87 @@ int itemdb_readdb_libconfig_sub(struct config_setting_t *it, int n, const char * if( libconfig->setting_lookup_string(it, "OnUnequipScript", &str) ) id.unequip_script = *str ? script->parse(str, source, -id.nameid, SCRIPT_IGNORE_EXTERNAL_BRACKETS, NULL) : NULL; + if (libconfig->setting_lookup_string(it, "OnRentalStartScript", &str) != CONFIG_FALSE) + id.rental_start_script = (*str != '\0') ? script->parse(str, source, -id.nameid, SCRIPT_IGNORE_EXTERNAL_BRACKETS, NULL) : NULL; + + if (libconfig->setting_lookup_string(it, "OnRentalEndScript", &str) != CONFIG_FALSE) + id.rental_end_script = (*str != '\0') ? script->parse(str, source, -id.nameid, SCRIPT_IGNORE_EXTERNAL_BRACKETS, NULL) : NULL; + return itemdb->validate_entry(&id, n, source); } -bool itemdb_lookup_const(const struct config_setting_t *it, const char *name, int *value) +static bool itemdb_lookup_const(const struct config_setting_t *it, const char *name, int *value) { + const char *str = NULL; + nullpo_retr(false, name); nullpo_retr(false, value); - if (libconfig->setting_lookup_int(it, name, value)) - { + + if (libconfig->setting_lookup_int(it, name, value)) { return true; } - else - { + + if (libconfig->setting_lookup_string(it, name, &str)) { + if (*str && script->get_constant(str, value)) + return true; + } + + return false; +} + +static bool itemdb_lookup_const_mask(const struct config_setting_t *it, const char *name, int *value) +{ + const struct config_setting_t *t = NULL; + + nullpo_retr(false, it); + nullpo_retr(false, name); + nullpo_retr(false, value); + + if ((t = libconfig->setting_get_member(it, name)) == NULL) { + return false; + } + + if (config_setting_is_scalar(t)) { const char *str = NULL; - if (libconfig->setting_lookup_string(it, name, &str)) - { - if (*str && script->get_constant(str, value)) + + if (config_setting_is_number(t)) { + *value = libconfig->setting_get_int(t); + return true; + } + + if ((str = libconfig->setting_get_string(t)) != NULL) { + int i32 = -1; + if (script->get_constant(str, &i32) && i32 >= 0) { + *value = i32; return true; + } + } + + return false; + } + + if (config_setting_is_aggregate(t) && libconfig->setting_length(t) >= 1) { + const struct config_setting_t *elem = NULL; + int i = 0; + + *value = 0; + + while ((elem = libconfig->setting_get_elem(t, i++)) != NULL) { + const char *str = libconfig->setting_get_string(elem); + int i32 = -1; + + if (str == NULL) + return false; + + if (!script->get_constant(str, &i32) || i32 < 0) + return false; + + *value |= i32; } + + return true; } + return false; } @@ -2038,8 +2382,10 @@ bool itemdb_lookup_const(const struct config_setting_t *it, const char *name, in * @param filename File name, relative to the database path. * @return The number of found entries. */ -int itemdb_readdb_libconfig(const char *filename) { +static int itemdb_readdb_libconfig(const char *filename) +{ bool duplicate[MAX_ITEMDB]; + struct DBMap *duplicate_db; struct config_t item_db_conf; struct config_setting_t *itdb, *it; char filepath[256]; @@ -2047,7 +2393,7 @@ int itemdb_readdb_libconfig(const char *filename) { nullpo_ret(filename); - sprintf(filepath, "%s/%s", map->db_path, filename); + safesnprintf(filepath, sizeof(filepath), "%s/%s", map->db_path, filename); if (!libconfig->load_file(&item_db_conf, filepath)) return 0; @@ -2056,43 +2402,149 @@ int itemdb_readdb_libconfig(const char *filename) { return 0; } + // TODO add duplicates check for itemdb->other memset(&duplicate,0,sizeof(duplicate)); + duplicate_db = idb_alloc(DB_OPT_BASE); while( (it = libconfig->setting_get_elem(itdb,i++)) ) { int nameid = itemdb->readdb_libconfig_sub(it, i-1, filename); - if (nameid <= 0 || nameid >= MAX_ITEMDB) + if (nameid <= 0 || nameid > MAX_ITEM_ID) continue; itemdb->readdb_additional_fields(nameid, it, i - 1, filename); count++; - if( duplicate[nameid] ) { - ShowWarning("itemdb_readdb:%s: duplicate entry of ID #%d (%s/%s)\n", - filename, nameid, itemdb_name(nameid), itemdb_jname(nameid)); - } else - duplicate[nameid] = true; + if (nameid < MAX_ITEMDB) { + if (duplicate[nameid]) { + ShowWarning("itemdb_readdb:%s: duplicate entry of ID #%d (%s/%s)\n", + filename, nameid, itemdb_name(nameid), itemdb_jname(nameid)); + } else { + duplicate[nameid] = true; + } + } else { + if (idb_exists(duplicate_db, nameid)) { + ShowWarning("itemdb_readdb:%s: duplicate entry of ID #%d (%s/%s)\n", + filename, nameid, itemdb_name(nameid), itemdb_jname(nameid)); + } else { + idb_iput(duplicate_db, nameid, true); + } + } } + db_destroy(duplicate_db); libconfig->destroy(&item_db_conf); - ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filename); - + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filepath); return count; } /*========================================== -* Unique item ID function -* Only one operation by once -*------------------------------------------*/ -uint64 itemdb_unique_id(struct map_session_data *sd) { + * Unique item ID function + * Only one operation by once + *------------------------------------------*/ +static uint64 itemdb_unique_id(struct map_session_data *sd) +{ nullpo_ret(sd); return ((uint64)sd->status.char_id << 32) | sd->status.uniqueitem_counter++; } +static bool itemdb_read_libconfig_lapineddukddak(void) +{ + struct config_t item_lapineddukddak; + struct config_setting_t *it = NULL; + char filepath[256]; + + int i = 0; + int count = 0; + + safesnprintf(filepath, sizeof(filepath), "%s/%s", map->db_path, DBPATH"item_lapineddukddak.conf"); + if (libconfig->load_file(&item_lapineddukddak, filepath) == CONFIG_FALSE) + return false; + + while ((it = libconfig->setting_get_elem(item_lapineddukddak.root, i++)) != NULL) { + if (itemdb->read_libconfig_lapineddukddak_sub(it, filepath)) + ++count; + } + + libconfig->destroy(&item_lapineddukddak); + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filepath); + return true; +} + +static bool itemdb_read_libconfig_lapineddukddak_sub(struct config_setting_t *it, const char *source) +{ + nullpo_retr(false, it); + nullpo_retr(false, source); + + struct item_data *data = NULL; + const char *name = config_setting_name(it); + const char *str = NULL; + int i32 = 0; + + if ((data = itemdb->name2id(name)) == NULL) { + ShowWarning("itemdb_read_libconfig_lapineddukddak_sub: unknown item '%s', skipping..\n", name); + return false; + } + + data->lapineddukddak = aCalloc(1, sizeof(struct item_lapineddukddak)); + if (libconfig->setting_lookup_int(it, "NeedCount", &i32) == CONFIG_TRUE) + data->lapineddukddak->NeedCount = (int16)i32; + + if (libconfig->setting_lookup_int(it, "NeedRefineMin", &i32) == CONFIG_TRUE) + data->lapineddukddak->NeedRefineMin = (int8)i32; + + if (libconfig->setting_lookup_int(it, "NeedRefineMax", &i32) == CONFIG_TRUE) + data->lapineddukddak->NeedRefineMax = (int8)i32; + + struct config_setting_t *sources = libconfig->setting_get_member(it, "SourceItems"); + itemdb->read_libconfig_lapineddukddak_sub_sources(sources, data); + + if (libconfig->setting_lookup_string(it, "Script", &str) == CONFIG_TRUE) + data->lapineddukddak->script = *str ? script->parse(str, source, -data->nameid, SCRIPT_IGNORE_EXTERNAL_BRACKETS, NULL) : NULL; + return true; +} + +static bool itemdb_read_libconfig_lapineddukddak_sub_sources(struct config_setting_t *sources, struct item_data *data) +{ + nullpo_retr(false, data); + nullpo_retr(false, data->lapineddukddak); + + int i = 0; + struct config_setting_t *entry = NULL; + + if (sources == NULL || !config_setting_is_group(sources)) + return false; + + VECTOR_INIT(data->lapineddukddak->SourceItems); + while ((entry = libconfig->setting_get_elem(sources, i++)) != NULL) { + struct item_data *edata = NULL; + struct itemlist_entry item = { 0 }; + const char *name = config_setting_name(entry); + int i32 = 0; + + if ((edata = itemdb->name2id(name)) == NULL) { + ShowWarning("itemdb_read_libconfig_lapineddukddak_sub: unknown item '%s', skipping..\n", name); + continue; + } + item.id = edata->nameid; + + if ((i32 = libconfig->setting_get_int(entry)) == CONFIG_TRUE && (i32 <= 0 || i32 > MAX_AMOUNT)) { + ShowWarning("itemdb_read_libconfig_lapineddukddak_sub: invalid amount (%d) for source item '%s', skipping..\n", i32, name); + continue; + } + item.amount = i32; + + VECTOR_ENSURE(data->lapineddukddak->SourceItems, 1, 1); + VECTOR_PUSH(data->lapineddukddak->SourceItems, item); + } + return true; +} + /** * Reads all item-related databases. */ -void itemdb_read(bool minimal) { +static void itemdb_read(bool minimal) +{ int i; struct DBData prev; @@ -2103,6 +2555,7 @@ void itemdb_read(bool minimal) { for (i = 0; i < ARRAYLENGTH(filename); i++) itemdb->readdb_libconfig(filename[i]); + // TODO check duplicate names also in itemdb->other for( i = 0; i < ARRAYLENGTH(itemdb->array); ++i ) { if( itemdb->array[i] ) { if( itemdb->names->put(itemdb->names,DB->str2key(itemdb->array[i]->name),DB->ptr2data(itemdb->array[i]),&prev) ) { @@ -2112,20 +2565,44 @@ void itemdb_read(bool minimal) { } } + itemdb->other->foreach(itemdb->other, itemdb->addname_sub); + + itemdb->read_options(); + if (minimal) return; - itemdb->read_combos(); + itemdb->name_constants(); + + itemdb->read_combodb_libconfig(); itemdb->read_groups(); itemdb->read_chains(); itemdb->read_packages(); + itemdb->read_libconfig_lapineddukddak(); +} + +/** + * Add item name with high id into map + * @see DBApply + */ +static int itemdb_addname_sub(union DBKey key, struct DBData *data, va_list ap) +{ + struct item_data *item = DB->data2ptr(data); + struct DBData prev; + if (itemdb->names->put(itemdb->names, DB->str2key(item->name), DB->ptr2data(item), &prev)) { + struct item_data *oldItem = DB->data2ptr(&prev); + ShowError("itemdb_read: duplicate AegisName '%s' in item ID %d and %d\n", item->name, item->nameid, oldItem->nameid); + } + + return 0; } /** * retrieves item_combo data by combo id **/ -struct item_combo * itemdb_id2combo( unsigned short id ) { +static struct item_combo *itemdb_id2combo(int id) +{ if( id > itemdb->combo_count ) return NULL; return itemdb->combos[id]; @@ -2134,7 +2611,7 @@ struct item_combo * itemdb_id2combo( unsigned short id ) { /** * check is item have usable type **/ -bool itemdb_is_item_usable(struct item_data *item) +static bool itemdb_is_item_usable(struct item_data *item) { nullpo_retr(false, item); return item->type == IT_HEALING || item->type == IT_USABLE || item->type == IT_CASH; @@ -2145,7 +2622,7 @@ bool itemdb_is_item_usable(struct item_data *item) *------------------------------------------*/ /// Destroys the item_data. -void destroy_item_data(struct item_data* self, int free_self) +static void destroy_item_data(struct item_data *self, int free_self) { if( self == NULL ) return; @@ -2156,8 +2633,18 @@ void destroy_item_data(struct item_data* self, int free_self) script->free_code(self->equip_script); if( self->unequip_script ) script->free_code(self->unequip_script); + if (self->rental_start_script != NULL) + script->free_code(self->rental_start_script); + if (self->rental_end_script != NULL) + script->free_code(self->rental_end_script); if( self->combos ) aFree(self->combos); + if (self->lapineddukddak != NULL) { + if (self->lapineddukddak->script != NULL) + script->free_code(self->lapineddukddak->script); + VECTOR_CLEAR(self->lapineddukddak->SourceItems); + aFree(self->lapineddukddak); + } HPM->data_store_destroy(&self->hdata); #if defined(DEBUG) // trash item @@ -2171,7 +2658,7 @@ void destroy_item_data(struct item_data* self, int free_self) /** * @see DBApply */ -int itemdb_final_sub(union DBKey key, struct DBData *data, va_list ap) +static int itemdb_final_sub(union DBKey key, struct DBData *data, va_list ap) { struct item_data *id = DB->data2ptr(data); @@ -2180,7 +2667,19 @@ int itemdb_final_sub(union DBKey key, struct DBData *data, va_list ap) return 0; } -void itemdb_clear(bool total) { + +static int itemdb_options_final_sub(union DBKey key, struct DBData *data, va_list ap) +{ + struct itemdb_option *ito = DB->data2ptr(data); + + if (ito->script != NULL) + script->free_code(ito->script); + + return 0; +} + +static void itemdb_clear(bool total) +{ int i; // clear the previous itemdb data for( i = 0; i < ARRAYLENGTH(itemdb->array); ++i ) { @@ -2243,13 +2742,15 @@ 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)); db_clear(itemdb->names); } -void itemdb_reload(void) { +static void itemdb_reload(void) +{ struct s_mapiterator* iter; struct map_session_data* sd; @@ -2293,21 +2794,25 @@ 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) +static void itemdb_name_constants(void) { struct DBIterator *iter = db_iterator(itemdb->names); struct item_data *data; @@ -2323,17 +2828,22 @@ void itemdb_name_constants(void) dbi_destroy(iter); } -void do_final_itemdb(void) { +static 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); + VECTOR_CLEAR(clif->attendance_data); } -void do_init_itemdb(bool minimal) { +static 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); @@ -2346,8 +2856,11 @@ void do_init_itemdb(bool minimal) { /** it failed? we disable it **/ if (battle_config.feature_roulette == 1 && !clif->parse_roulette_db()) battle_config.feature_roulette = 0; + VECTOR_INIT(clif->attendance_data); + clif->pAttendanceDB(); } -void itemdb_defaults(void) { +void itemdb_defaults(void) +{ itemdb = &itemdb_s; itemdb->init = do_init_itemdb; @@ -2376,6 +2889,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; @@ -2386,6 +2900,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; @@ -2414,10 +2929,11 @@ void itemdb_defaults(void) { itemdb->isrestricted = itemdb_isrestricted; itemdb->isidentified = itemdb_isidentified; itemdb->isidentified2 = itemdb_isidentified2; - itemdb->combo_split_atoi = itemdb_combo_split_atoi; - itemdb->read_combos = itemdb_read_combos; + itemdb->read_combodb_libconfig = itemdb_read_combodb_libconfig; + itemdb->read_combodb_libconfig_sub = itemdb_read_combodb_libconfig_sub; 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; @@ -2426,8 +2942,14 @@ void itemdb_defaults(void) { 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; itemdb->lookup_const = itemdb_lookup_const; + itemdb->lookup_const_mask = itemdb_lookup_const_mask; + itemdb->addname_sub = itemdb_addname_sub; + itemdb->read_libconfig_lapineddukddak = itemdb_read_libconfig_lapineddukddak; + itemdb->read_libconfig_lapineddukddak_sub = itemdb_read_libconfig_lapineddukddak_sub; + itemdb->read_libconfig_lapineddukddak_sub_sources = itemdb_read_libconfig_lapineddukddak_sub_sources; } |