summaryrefslogtreecommitdiff
path: root/src/map/itemdb.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/map/itemdb.c')
-rw-r--r--src/map/itemdb.c2381
1 files changed, 1613 insertions, 768 deletions
diff --git a/src/map/itemdb.c b/src/map/itemdb.c
index 859b19aac..0d3146191 100644
--- a/src/map/itemdb.c
+++ b/src/map/itemdb.c
@@ -2,44 +2,49 @@
// See the LICENSE file
// Portions Copyright (c) Athena Dev Teams
-#include "../common/nullpo.h"
-#include "../common/malloc.h"
-#include "../common/random.h"
-#include "../common/showmsg.h"
-#include "../common/strlib.h"
-#include "../common/utils.h"
+#define HERCULES_CORE
+
+#include "../config/core.h" // DBPATH, RENEWAL
#include "itemdb.h"
-#include "map.h"
-#include "battle.h" // struct battle_config
-#include "script.h" // item script processing
-#include "pc.h" // W_MUSICAL, W_WHIP
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-static struct item_data* itemdb_array[MAX_ITEMDB];
-static DBMap* itemdb_other;// int nameid -> struct item_data*
+#include "battle.h" // struct battle_config
+#include "map.h"
+#include "mob.h" // MAX_MOB_DB
+#include "pc.h" // W_MUSICAL, W_WHIP
+#include "script.h" // item script processing
+#include "../common/conf.h"
+#include "../common/malloc.h"
+#include "../common/nullpo.h"
+#include "../common/random.h"
+#include "../common/showmsg.h"
+#include "../common/strlib.h"
+#include "../common/utils.h"
-struct item_data dummy_item; //This is the default dummy item used for non-existant items. [Skotlex]
+struct itemdb_interface itemdb_s;
/**
- * Search for item name
+ * Search for item name
* name = item alias, so we should find items aliases first. if not found then look for "jname" (full name)
* @see DBApply
*/
-static int itemdb_searchname_sub(DBKey key, DBData *data, va_list ap)
+int itemdb_searchname_sub(DBKey key, DBData *data, va_list ap)
{
struct item_data *item = DB->data2ptr(data), **dst, **dst2;
char *str;
str=va_arg(ap,char *);
dst=va_arg(ap,struct item_data **);
dst2=va_arg(ap,struct item_data **);
- if(item == &dummy_item) return 0;
+ if (item == &itemdb->dummy) return 0;
//Absolute priority to Aegis code name.
if (*dst != NULL) return 0;
- if( strcmpi(item->name,str)==0 )
+ if ( battle_config.case_sensitive_aegisnames && strcmp(item->name,str) == 0 )
+ *dst=item;
+ else if ( !battle_config.case_sensitive_aegisnames && strcasecmp(item->name,str) == 0 )
*dst=item;
//Second priority to Client displayed name.
@@ -50,22 +55,22 @@ static int itemdb_searchname_sub(DBKey key, DBData *data, va_list ap)
}
/*==========================================
- * Return item data from item name. (lookup)
+ * Return item data from item name. (lookup)
*------------------------------------------*/
-struct item_data* itemdb_searchname(const char *str)
-{
+struct item_data* itemdb_searchname(const char *str) {
struct item_data* item;
struct item_data* item2=NULL;
int i;
- for( i = 0; i < ARRAYLENGTH(itemdb_array); ++i )
- {
- item = itemdb_array[i];
+ for( i = 0; i < ARRAYLENGTH(itemdb->array); ++i ) {
+ item = itemdb->array[i];
if( item == NULL )
continue;
// Absolute priority to Aegis code name.
- if( strcasecmp(item->name,str) == 0 )
+ if ( battle_config.case_sensitive_aegisnames && strcmp(item->name,str) == 0 )
+ return item;
+ else if ( !battle_config.case_sensitive_aegisnames && strcasecmp(item->name,str) == 0 )
return item;
//Second priority to Client displayed name.
@@ -74,45 +79,63 @@ struct item_data* itemdb_searchname(const char *str)
}
item = NULL;
- itemdb_other->foreach(itemdb_other,itemdb_searchname_sub,str,&item,&item2);
+ itemdb->other->foreach(itemdb->other,itemdb->searchname_sub,str,&item,&item2);
return item?item:item2;
}
+/* name to item data */
+struct item_data* itemdb_name2id(const char *str) {
+ return strdb_get(itemdb->names,str);
+}
/**
* @see DBMatcher
*/
-static int itemdb_searchname_array_sub(DBKey key, DBData data, va_list ap)
+int itemdb_searchname_array_sub(DBKey key, DBData data, va_list ap)
{
struct item_data *item = DB->data2ptr(&data);
char *str;
str=va_arg(ap,char *);
- if (item == &dummy_item)
+ if (item == &itemdb->dummy)
return 1; //Invalid item.
if(stristr(item->jname,str))
return 0;
- if(stristr(item->name,str))
+ if(battle_config.case_sensitive_aegisnames && strstr(item->name,str))
+ return 0;
+ if(!battle_config.case_sensitive_aegisnames && stristr(item->name,str))
return 0;
return strcmpi(item->jname,str);
}
/*==========================================
* 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 itemdb_searchname_array(struct item_data** data, int size, const char *str, int flag) {
struct item_data* item;
int i;
int count=0;
// Search in the array
- for( i = 0; i < ARRAYLENGTH(itemdb_array); ++i )
+ for( i = 0; i < ARRAYLENGTH(itemdb->array); ++i )
{
- item = itemdb_array[i];
+ item = itemdb->array[i];
if( item == NULL )
continue;
- if( stristr(item->jname,str) || stristr(item->name,str) )
- {
+ 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;
@@ -125,45 +148,132 @@ int itemdb_searchname_array(struct item_data** data, int size, const char *str)
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, (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;
}
return count;
}
-
-
-/*==========================================
- * Return a random item id from group. (takes into account % chance giving/tot group)
- *------------------------------------------*/
-int itemdb_searchrandomid(int group)
-{
- if(group<1 || group>=MAX_ITEMGROUP) {
- ShowError("itemdb_searchrandomid: Invalid group id %d\n", group);
+/* [Ind/Hercules] */
+int itemdb_chain_item(unsigned short chain_id, int *rate) {
+ struct item_chain_entry *entry;
+
+ if( chain_id >= itemdb->chain_count ) {
+ ShowError("itemdb_chain_item: unknown chain id %d\n", chain_id);
return UNKNOWN_ITEM_ID;
}
- if (itemgroup_db[group].qty)
- return itemgroup_db[group].nameid[rnd()%itemgroup_db[group].qty];
- ShowError("itemdb_searchrandomid: No item entries for group id %d\n", group);
- return UNKNOWN_ITEM_ID;
+ entry = &itemdb->chains[chain_id].items[ rnd()%itemdb->chains[chain_id].qty ];
+
+ if( rnd()%10000 >= entry->rate )
+ return 0;
+
+ if( rate )
+ rate[0] = entry->rate;
+ return entry->id;
}
+/* [Ind/Hercules] */
+void itemdb_package_item(struct map_session_data *sd, struct item_package *package) {
+ int i = 0, get_count, j, flag;
+
+ for( i = 0; i < package->must_qty; i++ ) {
+ struct item it;
+ memset(&it, 0, sizeof(it));
+ it.nameid = package->must_items[i].id;
+ it.identify = 1;
+
+ if( package->must_items[i].hours ) {
+ it.expire_time = (unsigned int)(time(NULL) + ((package->must_items[i].hours*60)*60));
+ }
+
+ if( package->must_items[i].named ) {
+ it.card[0] = CARD0_FORGE;
+ it.card[1] = 0;
+ it.card[2] = GetWord(sd->status.char_id, 0);
+ it.card[3] = GetWord(sd->status.char_id, 1);
+ }
+
+ if( package->must_items[i].announce )
+ clif->package_announce(sd,package->must_items[i].id,package->id);
+
+ get_count = itemdb->isstackable(package->must_items[i].id) ? package->must_items[i].qty : 1;
+
+ it.amount = get_count == 1 ? 1 : get_count;
+
+ for( j = 0; j < package->must_items[i].qty; j += get_count ) {
+ if ( ( flag = pc->additem(sd, &it, get_count, LOG_TYPE_SCRIPT) ) )
+ clif->additem(sd, 0, 0, flag);
+ }
+ }
+
+ if( package->random_qty ) {
+ for( i = 0; i < package->random_qty; i++ ) {
+ struct item_package_rand_entry *entry;
+
+ entry = &package->random_groups[i].random_list[rnd()%package->random_groups[i].random_qty];
+
+ while( 1 ) {
+ if( rnd()%10000 >= entry->rate ) {
+ entry = entry->next;
+ continue;
+ } else {
+ struct item it;
+ memset(&it, 0, sizeof(it));
+
+ it.nameid = entry->id;
+ it.identify = 1;
+
+ if( entry->hours ) {
+ it.expire_time = (unsigned int)(time(NULL) + ((entry->hours*60)*60));
+ }
+
+ if( entry->named ) {
+ it.card[0] = CARD0_FORGE;
+ it.card[1] = 0;
+ it.card[2] = GetWord(sd->status.char_id, 0);
+ it.card[3] = GetWord(sd->status.char_id, 1);
+ }
+
+ if( entry->announce )
+ clif->package_announce(sd,entry->id,package->id);
+
+ get_count = itemdb->isstackable(entry->id) ? entry->qty : 1;
+
+ it.amount = get_count == 1 ? 1 : get_count;
+
+ for( j = 0; j < entry->qty; j += get_count ) {
+ if ( ( flag = pc->additem(sd, &it, get_count, LOG_TYPE_SCRIPT) ) )
+ clif->additem(sd, 0, 0, flag);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ return;
+}
/*==========================================
- * Calculates total item-group related bonuses for the given item
+ * Return a random item id from group. (takes into account % chance giving/tot group)
*------------------------------------------*/
-int itemdb_group_bonus(struct map_session_data* sd, int itemid)
-{
- int bonus = 0, i, j;
- for (i=0; i < MAX_ITEMGROUP; i++) {
- if (!sd->itemgrouphealrate[i])
- continue;
- ARR_FIND( 0, itemgroup_db[i].qty, j, itemgroup_db[i].nameid[j] == itemid );
- if( j < itemgroup_db[i].qty )
- bonus += sd->itemgrouphealrate[i];
- }
- return bonus;
+int itemdb_searchrandomid(struct item_group *group) {
+
+ if (group->qty)
+ return group->nameid[rnd()%group->qty];
+
+ 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) {
+ int i;
+
+ for( i = 0; i < group->qty; i++ )
+ if( group->nameid[i] == nameid )
+ return true;
+
+ return false;
}
/// Searches for the item_data.
@@ -172,10 +282,10 @@ struct item_data* itemdb_exists(int nameid)
{
struct item_data* item;
- if( nameid >= 0 && nameid < ARRAYLENGTH(itemdb_array) )
- return itemdb_array[nameid];
- item = (struct item_data*)idb_get(itemdb_other,nameid);
- if( item == &dummy_item )
+ if( nameid >= 0 && nameid < ARRAYLENGTH(itemdb->array) )
+ return itemdb->array[nameid];
+ item = (struct item_data*)idb_get(itemdb->other,nameid);
+ if( item == &itemdb->dummy )
return NULL;// dummy data, doesn't exist
return item;
}
@@ -202,10 +312,10 @@ const char* itemdb_typename(int type)
}
/*==========================================
- * Converts the jobid from the format in itemdb
+ * Converts the jobid from the format in itemdb
* to the format used by the map server. [Skotlex]
*------------------------------------------*/
-static void itemdb_jobid2mapid(unsigned int *bclass, unsigned int jobmask)
+void itemdb_jobid2mapid(unsigned int *bclass, unsigned int jobmask)
{
int i;
bclass[0]= bclass[1]= bclass[2]= 0;
@@ -257,7 +367,10 @@ static void itemdb_jobid2mapid(unsigned int *bclass, unsigned int jobmask)
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]
@@ -269,21 +382,23 @@ static void itemdb_jobid2mapid(unsigned int *bclass, unsigned int jobmask)
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;
}
-static void create_dummy_data(void)
+void create_dummy_data(void)
{
- memset(&dummy_item, 0, sizeof(struct item_data));
- dummy_item.nameid=500;
- dummy_item.weight=1;
- dummy_item.value_sell=1;
- dummy_item.type=IT_ETC; //Etc item
- safestrncpy(dummy_item.name,"UNKNOWN_ITEM",sizeof(dummy_item.name));
- safestrncpy(dummy_item.jname,"UNKNOWN_ITEM",sizeof(dummy_item.jname));
- dummy_item.view_id=UNKNOWN_ITEM_ID;
+ memset(&itemdb->dummy, 0, sizeof(struct item_data));
+ itemdb->dummy.nameid=500;
+ itemdb->dummy.weight=1;
+ itemdb->dummy.value_sell=1;
+ itemdb->dummy.type=IT_ETC; //Etc item
+ safestrncpy(itemdb->dummy.name,"UNKNOWN_ITEM",sizeof(itemdb->dummy.name));
+ safestrncpy(itemdb->dummy.jname,"UNKNOWN_ITEM",sizeof(itemdb->dummy.jname));
+ itemdb->dummy.view_id=UNKNOWN_ITEM_ID;
}
-static struct item_data* create_item_data(int nameid)
+struct item_data* create_item_data(int nameid)
{
struct item_data *id;
CREATE(id, struct item_data, 1);
@@ -299,19 +414,19 @@ static struct item_data* create_item_data(int nameid)
struct item_data* itemdb_load(int nameid) {
struct item_data *id;
- if( nameid >= 0 && nameid < ARRAYLENGTH(itemdb_array) )
+ if( nameid >= 0 && nameid < ARRAYLENGTH(itemdb->array) )
{
- id = itemdb_array[nameid];
- if( id == NULL || id == &dummy_item )
- id = itemdb_array[nameid] = create_item_data(nameid);
+ id = itemdb->array[nameid];
+ if( id == NULL || id == &itemdb->dummy )
+ id = itemdb->array[nameid] = itemdb->create_item_data(nameid);
return id;
}
- id = (struct item_data*)idb_get(itemdb_other, nameid);
- if( id == NULL || id == &dummy_item )
+ id = (struct item_data*)idb_get(itemdb->other, nameid);
+ if( id == NULL || id == &itemdb->dummy )
{
- id = create_item_data(nameid);
- idb_put(itemdb_other, nameid, id);
+ id = itemdb->create_item_data(nameid);
+ idb_put(itemdb->other, nameid, id);
}
return id;
}
@@ -322,16 +437,16 @@ struct item_data* itemdb_load(int nameid) {
struct item_data* itemdb_search(int nameid)
{
struct item_data* id;
- if( nameid >= 0 && nameid < ARRAYLENGTH(itemdb_array) )
- id = itemdb_array[nameid];
+ if( nameid >= 0 && nameid < ARRAYLENGTH(itemdb->array) )
+ id = itemdb->array[nameid];
else
- id = (struct item_data*)idb_get(itemdb_other, nameid);
+ id = (struct item_data*)idb_get(itemdb->other, nameid);
if( id == NULL )
{
ShowWarning("itemdb_search: Item ID %d does not exists in the item_db. Using dummy data.\n", nameid);
- id = &dummy_item;
- dummy_item.nameid = nameid;
+ id = &itemdb->dummy;
+ itemdb->dummy.nameid = nameid;
}
return id;
}
@@ -355,8 +470,7 @@ int itemdb_isequip(int nameid)
/*==========================================
* Alternate version of itemdb_isequip
*------------------------------------------*/
-int itemdb_isequip2(struct item_data *data)
-{
+int itemdb_isequip2(struct item_data *data) {
nullpo_ret(data);
switch(data->type) {
case IT_WEAPON:
@@ -407,44 +521,44 @@ int itemdb_isstackable2(struct item_data *data)
* Trade Restriction functions [Skotlex]
*------------------------------------------*/
int itemdb_isdropable_sub(struct item_data *item, int gmlv, int unused) {
- return (item && (!(item->flag.trade_restriction&1) || gmlv >= item->gm_lv_trade_override));
+ 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) {
- return (item && (!(item->flag.trade_restriction&2) || gmlv >= item->gm_lv_trade_override || gmlv2 >= item->gm_lv_trade_override));
+ 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) {
- return (item && (item->flag.trade_restriction&4 || gmlv >= item->gm_lv_trade_override || gmlv2 >= item->gm_lv_trade_override));
+ 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) {
- return (item && (!(item->flag.trade_restriction&8) || gmlv >= item->gm_lv_trade_override));
+ 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) {
- return (item && (!(item->flag.trade_restriction&16) || gmlv >= item->gm_lv_trade_override));
+ 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) {
- return (item && (!(item->flag.trade_restriction&32) || gmlv >= item->gm_lv_trade_override));
+ 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) {
- return (item && (!(item->flag.trade_restriction&64) || gmlv >= item->gm_lv_trade_override));
+ 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) {
- return (item && (!(item->flag.trade_restriction&128) || gmlv >= item->gm_lv_trade_override));
+ 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) {
- return (item && (!(item->flag.trade_restriction&256) || gmlv >= item->gm_lv_trade_override));
+ 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))
{
- struct item_data* item_data = itemdb_search(item->nameid);
+ struct item_data* item_data = itemdb->search(item->nameid);
int i;
if (!func(item_data, gmlv, gmlv2))
@@ -455,7 +569,7 @@ int itemdb_isrestricted(struct item* item, int gmlv, int gmlv2, int (*func)(stru
for(i = 0; i < item_data->slot; i++) {
if (!item->card[i]) continue;
- if (!func(itemdb_search(item->card[i]), gmlv, gmlv2))
+ if (!func(itemdb->search(item->card[i]), gmlv, gmlv2))
return 0;
}
return 1;
@@ -487,269 +601,589 @@ int itemdb_isidentified2(struct item_data *data) {
}
}
+void itemdb_read_groups(void) {
+ config_t item_group_conf;
+ 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
+ const char *itname;
+ int i = 0, count = 0, c;
+ unsigned int *gsize = NULL;
-/*==========================================
- * Search by name for the override flags available items
- * (Give item another sprite)
- *------------------------------------------*/
-static bool itemdb_read_itemavail(char* str[], int columns, int current)
-{// <nameid>,<sprite>
- int nameid, sprite;
- struct item_data *id;
-
- nameid = atoi(str[0]);
-
- if( ( id = itemdb_exists(nameid) ) == NULL )
- {
- ShowWarning("itemdb_read_itemavail: Invalid item id %d.\n", nameid);
- return false;
- }
-
- sprite = atoi(str[1]);
-
- if( sprite > 0 )
- {
- id->flag.available = 1;
- id->view_id = sprite;
- }
- else
- {
- id->flag.available = 0;
- }
-
- return true;
-}
-
-/*==========================================
- * read item group data
- *------------------------------------------*/
-static unsigned int itemdb_read_itemgroup_sub(const char* filename) {
- FILE *fp;
- char line[1024];
- int ln=0;
- unsigned int count = 0;
- int groupid,j,k,nameid;
- char *str[3],*p;
- char w1[1024], w2[1024];
-
- if( (fp=fopen(filename,"r"))==NULL ){
- ShowError("can't read %s\n", filename);
- return 0;
+ if (libconfig->read_file(&item_group_conf, config_filename)) {
+ ShowError("can't read %s\n", config_filename);
+ return;
}
-
- while(fgets(line, sizeof(line), fp))
- {
- ln++;
- if(line[0]=='/' && line[1]=='/')
- continue;
- if(strstr(line,"import")) {
- if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2 &&
- strcmpi(w1, "import") == 0) {
- count += itemdb_read_itemgroup_sub(w2);
- continue;
- }
- }
- memset(str,0,sizeof(str));
- for(j=0,p=line;j<3 && p;j++){
- str[j]=p;
- p=strchr(p,',');
- if(p) *p++=0;
- }
- if(str[0]==NULL)
- continue;
- if (j<3) {
- if (j>1) //Or else it barks on blank lines...
- ShowWarning("itemdb_read_itemgroup: Insufficient fields for entry at %s:%d\n", filename, ln);
- continue;
- }
- groupid = atoi(str[0]);
- if (groupid < 0 || groupid >= MAX_ITEMGROUP) {
- ShowWarning("itemdb_read_itemgroup: Invalid group %d in %s:%d\n", groupid, filename, ln);
+
+ gsize = aMalloc( libconfig->setting_length(item_group_conf.root) * sizeof(unsigned int) );
+
+ for(i = 0; i < libconfig->setting_length(item_group_conf.root); i++)
+ gsize[i] = 0;
+
+ i = 0;
+ while( (itg = libconfig->setting_get_elem(item_group_conf.root,i++)) ) {
+ const char *name = config_setting_name(itg);
+
+ if( !itemdb->name2id(name) ) {
+ ShowWarning("itemdb_read_groups: unknown group item '%s', skipping..\n",name);
+ config_setting_remove(item_group_conf.root, name);
+ --i;
continue;
}
- nameid = atoi(str[1]);
- if (!itemdb_exists(nameid)) {
- ShowWarning("itemdb_read_itemgroup: Non-existant item %d in %s:%d\n", nameid, filename, ln);
- continue;
+
+ c = 0;
+ while( (it = libconfig->setting_get_elem(itg,c++)) ) {
+ if( config_setting_is_list(it) )
+ gsize[ i - 1 ] += libconfig->setting_get_int_elem(it,1);
+ else
+ gsize[ i - 1 ] += 1;
}
- k = atoi(str[2]);
- if (itemgroup_db[groupid].qty+k >= MAX_RANDITEM) {
- ShowWarning("itemdb_read_itemgroup: Group %d is full (%d entries) in %s:%d\n", groupid, MAX_RANDITEM, filename, ln);
- continue;
+
+ }
+
+ i = 0;
+ CREATE(itemdb->groups, struct item_group, libconfig->setting_length(item_group_conf.root));
+ itemdb->group_count = (unsigned short)libconfig->setting_length(item_group_conf.root);
+
+ while( (itg = libconfig->setting_get_elem(item_group_conf.root,i++)) ) {
+ struct item_data *data = itemdb->name2id(config_setting_name(itg));
+ int ecount = 0;
+
+ data->group = &itemdb->groups[count];
+
+ itemdb->groups[count].id = data->nameid;
+ itemdb->groups[count].qty = gsize[ count ];
+
+ CREATE(itemdb->groups[count].nameid, unsigned short, gsize[ count ] + 1);
+ c = 0;
+ while( (it = libconfig->setting_get_elem(itg,c++)) ) {
+ int repeat = 1;
+ if( config_setting_is_list(it) ) {
+ itname = libconfig->setting_get_string_elem(it,0);
+ repeat = libconfig->setting_get_int_elem(it,1);
+ } else
+ itname = libconfig->setting_get_string_elem(itg,c - 1);
+
+ if( itname[0] == 'I' && itname[1] == 'D' && strlen(itname) < 8 ) {
+ 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) ) )
+ ShowWarning("itemdb_read_groups: unknown item '%s' in group '%s'!\n",itname,config_setting_name(itg));
+
+ itemdb->groups[count].nameid[ecount] = data ? data->nameid : 0;
+ if( repeat > 1 ) {
+ //memset would be better? I failed to get the following to work though hu
+ //memset(&itemdb->groups[count].nameid[ecount+1],itemdb->groups[count].nameid[ecount],sizeof(itemdb->groups[count].nameid[0])*repeat);
+ int z;
+ for( z = ecount+1; z < ecount+repeat; z++ )
+ itemdb->groups[count].nameid[z] = itemdb->groups[count].nameid[ecount];
+ }
+ ecount += repeat;
}
- for(j=0;j<k;j++)
- itemgroup_db[groupid].nameid[itemgroup_db[groupid].qty++] = nameid;
+
count++;
}
- fclose(fp);
- return count;
-}
-
-static void itemdb_read_itemgroup(void)
-{
- char path[256];
- unsigned int count;
- snprintf(path, 255, "%s/"DBPATH"item_group_db.txt", iMap->db_path);
- memset(&itemgroup_db, 0, sizeof(itemgroup_db));
- count = itemdb_read_itemgroup_sub(path);
- ShowStatus("Done reading '"CL_WHITE"%lu"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, "item_group_db.txt");
- return;
+
+ libconfig->destroy(&item_group_conf);
+ aFree(gsize);
+
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, config_filename);
}
-
-/*==========================================
- * Reads item trade restrictions [Skotlex]
- *------------------------------------------*/
-static bool itemdb_read_itemtrade(char* str[], int columns, int current)
-{// <nameid>,<mask>,<gm level>
- int nameid, flag, gmlv;
- struct item_data *id;
-
- nameid = atoi(str[0]);
-
- if( ( id = itemdb_exists(nameid) ) == NULL )
- {
- //ShowWarning("itemdb_read_itemtrade: Invalid item id %d.\n", nameid);
- //return false;
- // FIXME: item_trade.txt contains items, which are commented in item database.
- return true;
- }
-
- flag = atoi(str[1]);
- gmlv = atoi(str[2]);
-
- if( flag < 0 || flag > 511 ) {//Check range
- ShowWarning("itemdb_read_itemtrade: Invalid trading mask %d for item id %d.\n", flag, nameid);
- return false;
+/* [Ind/Hercules] - HCache for Packages */
+void itemdb_write_cached_packages(const char *config_filename) {
+ FILE *file;
+ unsigned short pcount = itemdb->package_count;
+ unsigned short i;
+
+ if( !(file = HCache->open(config_filename,"wb")) ) {
+ return;
}
- if( gmlv < 1 )
- {
- ShowWarning("itemdb_read_itemtrade: Invalid override GM level %d for item id %d.\n", gmlv, nameid);
- return false;
+
+ // first 2 bytes = package count
+ 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;
+ unsigned short c;
+ //into a package, first 2 bytes = id.
+ hwrite(&id,sizeof(id),1,file);
+ //next 2 bytes = must count
+ hwrite(&must_qty,sizeof(must_qty),1,file);
+ //next 2 bytes = random count
+ hwrite(&random_qty,sizeof(random_qty),1,file);
+ //now we loop into must
+ for(c = 0; c < must_qty; c++) {
+ struct item_package_must_entry *entry = &itemdb->packages[i].must_items[c];
+ unsigned char announce = entry->announce == 1 ? 1 : 0, named = entry->named == 1 ? 1 : 0;
+ //first 2 byte = item id
+ hwrite(&entry->id,sizeof(entry->id),1,file);
+ //next 2 byte = qty
+ hwrite(&entry->qty,sizeof(entry->qty),1,file);
+ //next 2 byte = hours
+ hwrite(&entry->hours,sizeof(entry->hours),1,file);
+ //next 1 byte = announce (1:0)
+ hwrite(&announce,sizeof(announce),1,file);
+ //next 1 byte = named (1:0)
+ hwrite(&named,sizeof(announce),1,file);
+ }
+ //now we loop into random groups
+ for(c = 0; c < random_qty; c++) {
+ struct item_package_rand_group *group = &itemdb->packages[i].random_groups[c];
+ unsigned short group_qty = group->random_qty, h;
+
+ //next 2 bytes = how many entries in this group
+ hwrite(&group_qty,sizeof(group_qty),1,file);
+ //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 char announce = entry->announce == 1 ? 1 : 0, named = entry->named == 1 ? 1 : 0;
+ //first 2 byte = item id
+ hwrite(&entry->id,sizeof(entry->id),1,file);
+ //next 2 byte = qty
+ hwrite(&entry->qty,sizeof(entry->qty),1,file);
+ //next 2 byte = rate
+ hwrite(&entry->rate,sizeof(entry->rate),1,file);
+ //next 2 byte = hours
+ hwrite(&entry->hours,sizeof(entry->hours),1,file);
+ //next 1 byte = announce (1:0)
+ hwrite(&announce,sizeof(announce),1,file);
+ //next 1 byte = named (1:0)
+ hwrite(&named,sizeof(announce),1,file);
+ }
+ }
}
-
- id->flag.trade_restriction = flag;
- id->gm_lv_trade_override = gmlv;
-
- return true;
+
+ fclose(file);
+
+ return;
}
+bool itemdb_read_cached_packages(const char *config_filename) {
+ FILE *file;
+ unsigned short pcount;
+ unsigned short i;
-/*==========================================
- * Reads item delay amounts [Paradox924X]
- *------------------------------------------*/
-static bool itemdb_read_itemdelay(char* str[], int columns, int current)
-{// <nameid>,<delay>
- int nameid, delay;
- struct item_data *id;
-
- nameid = atoi(str[0]);
-
- if( ( id = itemdb_exists(nameid) ) == NULL )
- {
- ShowWarning("itemdb_read_itemdelay: Invalid item id %d.\n", nameid);
+ if( !(file = HCache->open(config_filename,"rb")) ) {
return false;
}
+
+ // first 2 bytes = package count
+ hread(&pcount,sizeof(pcount),1,file);
- delay = atoi(str[1]);
+ CREATE(itemdb->packages, struct item_package, pcount);
+ itemdb->package_count = pcount;
- if( delay < 0 )
- {
- ShowWarning("itemdb_read_itemdelay: Invalid delay %d for item id %d.\n", id->delay, nameid);
- return false;
+ for( i = 0; i < pcount; i++ ) {
+ unsigned short id = 0, 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.
+ hread(&id,sizeof(id),1,file);
+ //next 2 bytes = must count
+ hread(&must_qty,sizeof(must_qty),1,file);
+ //next 2 bytes = random count
+ hread(&random_qty,sizeof(random_qty),1,file);
+
+ if( !(pdata = itemdb->exists(id)) )
+ ShowWarning("itemdb_read_cached_packages: unknown package item '%d', skipping..\n",id);
+ else
+ pdata->package = &itemdb->packages[i];
+
+ package->id = id;
+ package->random_qty = random_qty;
+ package->must_qty = must_qty;
+ package->must_items = NULL;
+ package->random_groups = NULL;
+
+ if( package->must_qty ) {
+ CREATE(package->must_items, struct item_package_must_entry, package->must_qty);
+ //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;
+ unsigned char announce = 0, named = 0;
+ struct item_data *data;
+ //first 2 byte = item id
+ hread(&mid,sizeof(mid),1,file);
+ //next 2 byte = qty
+ hread(&qty,sizeof(qty),1,file);
+ //next 2 byte = hours
+ hread(&hours,sizeof(hours),1,file);
+ //next 1 byte = announce (1:0)
+ hread(&announce,sizeof(announce),1,file);
+ //next 1 byte = named (1:0)
+ hread(&named,sizeof(announce),1,file);
+
+ if( !(data = itemdb->exists(mid)) )
+ ShowWarning("itemdb_read_cached_packages: unknown item '%d' in package '%s'!\n",mid,itemdb_name(package->id));
+
+ entry->id = data ? data->nameid : 0;
+ entry->hours = hours;
+ entry->qty = qty;
+ entry->announce = announce ? 1 : 0;
+ entry->named = named ? 1 : 0;
+ }
+ }
+ if( package->random_qty ) {
+ //now we loop into random groups
+ CREATE(package->random_groups, struct item_package_rand_group, package->random_qty);
+ for(c = 0; c < package->random_qty; c++) {
+ unsigned short group_qty = 0, h;
+ struct item_package_rand_entry *prev = NULL;
+
+ //next 2 bytes = how many entries in this group
+ hread(&group_qty,sizeof(group_qty),1,file);
+
+ package->random_groups[c].random_qty = group_qty;
+ CREATE(package->random_groups[c].random_list, struct item_package_rand_entry, package->random_groups[c].random_qty);
+
+ //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;
+ unsigned char announce = 0, named = 0;
+ struct item_data *data;
+
+ if( prev ) prev->next = entry;
+
+ //first 2 byte = item id
+ hread(&mid,sizeof(mid),1,file);
+ //next 2 byte = qty
+ hread(&qty,sizeof(qty),1,file);
+ //next 2 byte = rate
+ hread(&rate,sizeof(rate),1,file);
+ //next 2 byte = hours
+ hread(&hours,sizeof(hours),1,file);
+ //next 1 byte = announce (1:0)
+ hread(&announce,sizeof(announce),1,file);
+ //next 1 byte = named (1:0)
+ hread(&named,sizeof(announce),1,file);
+
+ if( !(data = itemdb->exists(mid)) )
+ ShowWarning("itemdb_read_cached_packages: unknown item '%d' in package '%s'!\n",mid,itemdb_name(package->id));
+
+ entry->id = data ? data->nameid : 0;
+ entry->rate = rate;
+ entry->hours = hours;
+ entry->qty = qty;
+ entry->announce = announce ? 1 : 0;
+ entry->named = named ? 1 : 0;
+
+ prev = entry;
+ }
+ if( prev )
+ prev->next = &itemdb->packages[i].random_groups[c].random_list[0];
+ }
+ }
}
-
- id->delay = delay;
+
+ fclose(file);
+
+ ShowStatus("Done reading '"CL_WHITE"%hu"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"' ("CL_GREEN"C"CL_RESET").\n", pcount, config_filename);
return true;
}
-
-/*==================================================================
- * Reads item stacking restrictions
- *----------------------------------------------------------------*/
-static bool itemdb_read_stack(char* fields[], int columns, int current)
-{// <item id>,<stack limit amount>,<type>
- unsigned short nameid, amount;
- unsigned int type;
- struct item_data* id;
-
- nameid = (unsigned short)strtoul(fields[0], NULL, 10);
-
- if( ( id = itemdb_exists(nameid) ) == NULL )
- {
- ShowWarning("itemdb_read_stack: Unknown item id '%hu'.\n", nameid);
- return false;
+void itemdb_read_packages(void) {
+ config_t item_packages_conf;
+ 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
+ const char *itname;
+ int i = 0, count = 0, c = 0, highest_gcount = 0;
+ unsigned int *must = NULL, *random = NULL, *rgroup = NULL, **rgroups = NULL;
+ struct item_package_rand_entry **prev = NULL;
+
+ if( HCache->check(config_filename) ) {
+ if( itemdb->read_cached_packages(config_filename) )
+ return;
}
-
- if( !itemdb_isstackable2(id) )
- {
- ShowWarning("itemdb_read_stack: Item id '%hu' is not stackable.\n", nameid);
- return false;
+
+ if (libconfig->read_file(&item_packages_conf, config_filename)) {
+ ShowError("can't read %s\n", 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) );
+ rgroup = aMalloc( libconfig->setting_length(item_packages_conf.root) * sizeof(unsigned int) );
+ rgroups = aMalloc( libconfig->setting_length(item_packages_conf.root) * sizeof(unsigned int *) );
- amount = (unsigned short)strtoul(fields[1], NULL, 10);
- type = strtoul(fields[2], NULL, 10);
-
- if( !amount )
- {// ignore
- return true;
+
+ for(i = 0; i < libconfig->setting_length(item_packages_conf.root); i++) {
+ must[i] = 0;
+ random[i] = 0;
+ rgroup[i] = 0;
+ rgroups[i] = NULL;
+ }
+
+ /* validate tree, drop poisonous fruits! */
+ i = 0;
+ while( (itg = libconfig->setting_get_elem(item_packages_conf.root,i++)) ) {
+ const char *name = config_setting_name(itg);
+
+ if( !itemdb->name2id(name) ) {
+ ShowWarning("itemdb_read_packages: unknown package item '%s', skipping..\n",name);
+ libconfig->setting_remove(item_packages_conf.root, name);
+ --i;
+ continue;
+ }
+
+ c = 0;
+ while( (it = libconfig->setting_get_elem(itg,c++)) ) {
+ int rval = 0;
+ if( !( t = libconfig->setting_get_member(it, "Random") ) || (rval = libconfig->setting_get_int(t)) < 0 ) {
+ ShowWarning("itemdb_read_packages: invalid 'Random' value (%d) for item '%s' in package '%s', defaulting to must!\n",rval,config_setting_name(it),name);
+ libconfig->setting_remove(it, config_setting_name(it));
+ --c;
+ continue;
+ }
+
+ if( rval == 0 )
+ must[ i - 1 ] += 1;
+ else {
+ random[ i - 1 ] += 1;
+ if( rval > rgroup[i - 1] )
+ rgroup[i - 1] = rval;
+ if( rval > highest_gcount )
+ highest_gcount = rval;
+ }
+ }
}
- id->stack.amount = amount;
- id->stack.inventory = (type&1)!=0;
- id->stack.cart = (type&2)!=0;
- id->stack.storage = (type&4)!=0;
- id->stack.guildstorage = (type&8)!=0;
-
- return true;
-}
-
+ CREATE(prev, struct item_package_rand_entry *, highest_gcount);
+ for(i = 0; i < highest_gcount; i++) {
+ prev[i] = NULL;
+ }
+
+ for(i = 0; i < libconfig->setting_length(item_packages_conf.root); i++ ) {
+ rgroups[i] = aMalloc( rgroup[i] * sizeof(unsigned int) );
+ for( c = 0; c < rgroup[i]; c++ ) {
+ rgroups[i][c] = 0;
+ }
+ }
-/// Reads items allowed to be sold in buying stores
-static bool itemdb_read_buyingstore(char* fields[], int columns, int current)
-{// <nameid>
- int nameid;
- struct item_data* id;
+ /* grab the known sizes */
+ i = 0;
+ while( (itg = libconfig->setting_get_elem(item_packages_conf.root,i++)) ) {
+ c = 0;
+ while( (it = libconfig->setting_get_elem(itg,c++)) ) {
+ int rval = 0;
+ if( ( t = libconfig->setting_get_member(it, "Random")) && ( rval = libconfig->setting_get_int(t) ) > 0 ) {
+ rgroups[i - 1][rval - 1] += 1;
+ }
+ }
+ }
+
+ CREATE(itemdb->packages, struct item_package, libconfig->setting_length(item_packages_conf.root));
+ itemdb->package_count = (unsigned short)libconfig->setting_length(item_packages_conf.root);
+
+ /* write */
+ i = 0;
+ while( (itg = libconfig->setting_get_elem(item_packages_conf.root,i++)) ) {
+ struct item_data *data = itemdb->name2id(config_setting_name(itg));
+ int r = 0, m = 0;
+
+ for(r = 0; r < highest_gcount; r++) {
+ prev[r] = NULL;
+ }
+
+ data->package = &itemdb->packages[count];
+
+ itemdb->packages[count].id = data->nameid;
+ itemdb->packages[count].random_groups = NULL;
+ itemdb->packages[count].must_items = NULL;
+ itemdb->packages[count].random_qty = rgroup[ i - 1 ];
+ itemdb->packages[count].must_qty = must[ i - 1 ];
+
+ if( itemdb->packages[count].random_qty ) {
+ CREATE(itemdb->packages[count].random_groups, struct item_package_rand_group, itemdb->packages[count].random_qty);
+ for( c = 0; c < itemdb->packages[count].random_qty; c++ ) {
+ if( !rgroups[ i - 1 ][c] )
+ ShowError("itemdb_read_packages: package '%s' missing 'Random' field %d! there must not be gaps!\n",config_setting_name(itg),c+1);
+ else
+ CREATE(itemdb->packages[count].random_groups[c].random_list, struct item_package_rand_entry, rgroups[ i - 1 ][c]);
+ itemdb->packages[count].random_groups[c].random_qty = 0;
+ }
+ }
+ if( itemdb->packages[count].must_qty )
+ CREATE(itemdb->packages[count].must_items, struct item_package_must_entry, itemdb->packages[count].must_qty);
+
+ c = 0;
+ while( (it = libconfig->setting_get_elem(itg,c++)) ) {
+ int icount = 1, expire = 0, rate = 10000, gid = 0;
+ bool announce = false, named = false;
+
+ itname = config_setting_name(it);
+
+ if( itname[0] == 'I' && itname[1] == 'D' && strlen(itname) < 8 ) {
+ 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) ) )
+ ShowWarning("itemdb_read_packages: unknown item '%s' in package '%s'!\n",itname,config_setting_name(itg));
+
+ if( ( t = libconfig->setting_get_member(it, "Count")) )
+ icount = libconfig->setting_get_int(t);
+
+ if( ( t = libconfig->setting_get_member(it, "Expire")) )
+ expire = libconfig->setting_get_int(t);
+
+ if( ( t = libconfig->setting_get_member(it, "Rate")) ) {
+ if( (rate = (unsigned short)libconfig->setting_get_int(t)) > 10000 ) {
+ ShowWarning("itemdb_read_packages: invalid rate (%d) for item '%s' in package '%s'!\n",rate,itname,config_setting_name(itg));
+ rate = 10000;
+ }
+ }
- nameid = atoi(fields[0]);
+ if( ( t = libconfig->setting_get_member(it, "Announce")) && libconfig->setting_get_bool(t) )
+ announce = true;
- if( ( id = itemdb_exists(nameid) ) == NULL )
- {
- ShowWarning("itemdb_read_buyingstore: Invalid item id %d.\n", nameid);
- return false;
+ if( ( t = libconfig->setting_get_member(it, "Named")) && libconfig->setting_get_bool(t) )
+ named = true;
+
+ if( !( t = libconfig->setting_get_member(it, "Random") ) ) {
+ ShowWarning("itemdb_read_packages: missing 'Random' field for item '%s' in package '%s', defaulting to must!\n",itname,config_setting_name(itg));
+ gid = 0;
+ } else
+ gid = libconfig->setting_get_int(t);
+
+ if( gid == 0 ) {
+ itemdb->packages[count].must_items[m].id = data ? data->nameid : 0;
+ itemdb->packages[count].must_items[m].qty = icount;
+ itemdb->packages[count].must_items[m].hours = expire;
+ itemdb->packages[count].must_items[m].announce = announce == true ? 1 : 0;
+ itemdb->packages[count].must_items[m].named = named == true ? 1 : 0;
+ m++;
+ } else {
+ int gidx = gid - 1;
+
+ r = itemdb->packages[count].random_groups[gidx].random_qty;
+
+ if( prev[gidx] )
+ prev[gidx]->next = &itemdb->packages[count].random_groups[gidx].random_list[r];
+
+ itemdb->packages[count].random_groups[gidx].random_list[r].id = data ? data->nameid : 0;
+ itemdb->packages[count].random_groups[gidx].random_list[r].qty = icount;
+ if( (itemdb->packages[count].random_groups[gidx].random_list[r].rate = rate) == 10000 ) {
+ ShowWarning("itemdb_read_packages: item '%s' in '%s' has 100%% drop rate!! set this item as 'Random: 0' or other items won't drop!!!\n",itname,config_setting_name(itg));
+ }
+ itemdb->packages[count].random_groups[gidx].random_list[r].hours = expire;
+ itemdb->packages[count].random_groups[gidx].random_list[r].announce = announce == true ? 1 : 0;
+ itemdb->packages[count].random_groups[gidx].random_list[r].named = named == true ? 1 : 0;
+ itemdb->packages[count].random_groups[gidx].random_qty += 1;
+
+ prev[gidx] = &itemdb->packages[count].random_groups[gidx].random_list[r];
+ }
+
+ }
+
+ for(r = 0; r < highest_gcount; r++) {
+ if( prev[r] )
+ prev[r]->next = &itemdb->packages[count].random_groups[r].random_list[0];
+ }
+
+ for( r = 0; r < itemdb->packages[count].random_qty; r++ ) {
+ if( itemdb->packages[count].random_groups[r].random_qty == 1 ) {
+ //item packages don't stop looping until something comes out of them, so if you have only one item in it the drop is guaranteed.
+ ShowWarning("itemdb_read_packages: in '%s' 'Random: %d' group has only 1 random option, drop rate will be 100%%!\n",
+ itemdb_name(itemdb->packages[count].id),r+1);
+ itemdb->packages[count].random_groups[r].random_list[0].rate = 10000;
+ }
+ }
+
+ count++;
}
-
- if( !itemdb_isstackable2(id) )
- {
- ShowWarning("itemdb_read_buyingstore: Non-stackable item id %d cannot be enabled for buying store.\n", nameid);
- return false;
+
+
+ aFree(must);
+ aFree(random);
+ for(i = 0; i < libconfig->setting_length(item_packages_conf.root); i++ ) {
+ aFree(rgroups[i]);
}
+ aFree(rgroups);
+ aFree(rgroup);
+ aFree(prev);
+
+ libconfig->destroy(&item_packages_conf);
- id->flag.buyingstore = true;
-
- return true;
+ if( HCache->enabled )
+ itemdb->write_cached_packages(config_filename);
+
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, config_filename);
}
-/*******************************************
-** Item usage restriction (item_nouse.txt)
-********************************************/
-static bool itemdb_read_nouse(char* fields[], int columns, int current)
-{// <nameid>,<flag>,<override>
- int nameid, flag, override;
- struct item_data* id;
+void itemdb_read_chains(void) {
+ config_t item_chain_conf;
+ config_setting_t *itc = NULL, *entry = 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
+ int i = 0, count = 0;
+
+ if (libconfig->read_file(&item_chain_conf, config_filename)) {
+ ShowError("can't read %s\n", config_filename);
+ return;
+ }
- nameid = atoi(fields[0]);
+ 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);
- if( ( id = itemdb_exists(nameid) ) == NULL ) {
- ShowWarning("itemdb_read_nouse: Invalid item id %d.\n", nameid);
- return false;
+#ifdef ENABLE_CASE_CHECK
+ script->parser_current_file = config_filename;
+#endif // ENABLE_CASE_CHECK
+ while( (itc = libconfig->setting_get_elem(item_chain_conf.root,i++)) ) {
+ struct item_data *data = NULL;
+ struct item_chain_entry *prev = NULL;
+ const char *name = config_setting_name(itc);
+ int c = 0;
+
+ script->set_constant2(name,i-1,0);
+ itemdb->chains[count].qty = (unsigned short)libconfig->setting_length(itc);
+
+ CREATE(itemdb->chains[count].items, struct item_chain_entry, libconfig->setting_length(itc));
+
+ 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( !( 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) ) )
+ ShowWarning("itemdb_read_chains: unknown item '%s' in chain '%s'!\n",itname,name);
+
+ if( prev )
+ prev->next = &itemdb->chains[count].items[c - 1];
+
+ itemdb->chains[count].items[c - 1].id = data ? data->nameid : 0;
+ itemdb->chains[count].items[c - 1].rate = data ? libconfig->setting_get_int(entry) : 0;
+
+ prev = &itemdb->chains[count].items[c - 1];
+ }
+
+ if( prev )
+ prev->next = &itemdb->chains[count].items[0];
+
+ count++;
}
+#ifdef ENABLE_CASE_CHECK
+ script->parser_current_file = NULL;
+#endif // ENABLE_CASE_CHECK
- flag = atoi(fields[1]);
- override = atoi(fields[2]);
-
- id->item_usage.flag = flag;
- id->item_usage.override = override;
-
- return true;
+ libconfig->destroy(&item_chain_conf);
+
+ if( !script->get_constant("ITMCHAIN_ORE",&i) )
+ ShowWarning("itemdb_read_chains: failed to find 'ITMCHAIN_ORE' chain to link to cache!\n");
+ 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);
}
/**
@@ -781,13 +1215,13 @@ void itemdb_read_combos() {
uint32 lines = 0, count = 0;
char line[1024];
- char path[256];
+ char filepath[256];
FILE* fp;
- sprintf(path, "%s/%s", iMap->db_path, DBPATH"item_combo_db.txt");
+ sprintf(filepath, "%s/%s", map->db_path, DBPATH"item_combo_db.txt");
- if ((fp = fopen(path, "r")) == NULL) {
- ShowError("itemdb_read_combos: File not found \"%s\".\n", path);
+ if ((fp = fopen(filepath, "r")) == NULL) {
+ ShowError("itemdb_read_combos: File not found \"%s\".\n", filepath);
return;
}
@@ -809,10 +1243,9 @@ void itemdb_read_combos() {
if (*p == '\0')
continue;// empty line
- if (!strchr(p,','))
- {
+ if (!strchr(p,',')) {
/* is there even a single column? */
- ShowError("itemdb_read_combos: Insufficient columns in line %d of \"%s\", skipping.\n", lines, path);
+ ShowError("itemdb_read_combos: Insufficient columns in line %d of \"%s\", skipping.\n", lines, filepath);
continue;
}
@@ -822,86 +1255,65 @@ void itemdb_read_combos() {
p++;
str[1] = p;
- p = strchr(p,',');
+ p = strchr(p,',');
p++;
if (str[1][0] != '{') {
- ShowError("itemdb_read_combos(#1): Invalid format (Script column) in line %d of \"%s\", skipping.\n", lines, path);
+ ShowError("itemdb_read_combos(#1): Invalid format (Script column) in line %d 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, path);
+ ShowError("itemdb_read_combos(#2): Invalid format (Script column) in line %d of \"%s\", skipping.\n", lines, filepath);
continue;
} else {
int items[MAX_ITEMS_PER_COMBO];
int v = 0, retcount = 0;
- struct item_data * id = NULL;
- int idx = 0;
+ 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, path);
+ 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);
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, path,items[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]);
break;
}
}
/* failed at some item */
if( v < retcount )
continue;
-
- id = itemdb_exists(items[0]);
- idx = id->combos_count;
-
- /* first entry, create */
- if( id->combos == NULL ) {
- CREATE(id->combos, struct item_combo*, 1);
- id->combos_count = 1;
- } else {
- RECREATE(id->combos, struct item_combo*, ++id->combos_count);
- }
+ RECREATE(itemdb->combos, struct item_combo*, ++itemdb->combo_count);
- CREATE(id->combos[idx],struct item_combo,1);
+ CREATE(combo, struct item_combo, 1);
- id->combos[idx]->nameid = aMalloc( retcount * sizeof(unsigned short) );
- id->combos[idx]->count = retcount;
- id->combos[idx]->script = parse_script(str[1], path, lines, 0);
- id->combos[idx]->id = count;
- id->combos[idx]->isRef = false;
+ 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++ ) {
- id->combos[idx]->nameid[v] = items[v];
+ combo->nameid[v] = items[v];
}
- /* populate the children to refer to this combo */
- for( v = 1; v < retcount; v++ ) {
+ itemdb->combos[itemdb->combo_count - 1] = combo;
+
+ /* populate the items to refer to this combo */
+ for( v = 0; v < retcount; v++ ) {
struct item_data * it;
int index;
- it = itemdb_exists(items[v]);
+ it = itemdb->exists(items[v]);
index = it->combos_count;
- if( it->combos == NULL ) {
- CREATE(it->combos, struct item_combo*, 1);
- it->combos_count = 1;
- } else {
- RECREATE(it->combos, struct item_combo*, ++it->combos_count);
- }
+ RECREATE(it->combos, struct item_combo*, ++it->combos_count);
- CREATE(it->combos[index],struct item_combo,1);
-
- /* we copy previously alloc'd pointers and just set it to reference */
- memcpy(it->combos[index],id->combos[idx],sizeof(struct item_combo));
- /* we flag this way to ensure we don't double-dealloc same data */
- it->combos[index]->isRef = true;
+ it->combos[index] = combo;
}
}
@@ -911,7 +1323,7 @@ void itemdb_read_combos() {
fclose(fp);
- ShowStatus("Done reading '"CL_WHITE"%lu"CL_RESET"' entries in '"CL_WHITE"item_combo_db"CL_RESET"'.\n", count);
+ ShowStatus("Done reading '"CL_WHITE"%"PRIu32""CL_RESET"' entries in '"CL_WHITE"item_combo_db"CL_RESET"'.\n", count);
return;
}
@@ -921,7 +1333,7 @@ void itemdb_read_combos() {
/*======================================
* Applies gender restrictions according to settings. [Skotlex]
*======================================*/
-static int itemdb_gendercheck(struct item_data *id)
+int itemdb_gendercheck(struct item_data *id)
{
if (id->nameid == WEDDING_RING_M) //Grom Ring
return 1;
@@ -934,409 +1346,708 @@ static int itemdb_gendercheck(struct item_data *id)
return (battle_config.ignore_items_gender) ? 2 : id->sex;
}
-/**
- * [RRInd]
- * For backwards compatibility, in Renewal mode, MATK from weapons comes from the atk slot
- * We use a ':' delimiter which, if not found, assumes the weapon does not provide any matk.
- **/
-void itemdb_re_split_atoi(char *str, int *atk, int *matk) {
- int i, val[2];
- for (i=0; i<2; i++) {
- if (!str) break;
- val[i] = atoi(str);
- str = strchr(str,':');
- if (str)
- *str++=0;
+/**
+ * Validates an item DB entry and inserts it into the database.
+ * This function is called after preparing the item entry data, and it takes
+ * care of inserting it and cleaning up any remainders of the previous one.
+ *
+ * @param *entry Pointer to the new item_data entry. Ownership is NOT taken,
+ * but the content is modified to reflect the validation.
+ * @param n Ordinal number of the entry, to be displayed in case of
+ * validation errors.
+ * @param *source Source of the entry (table or file name), to be displayed in
+ * case of validation errors.
+ * @return Nameid of the validated entry, or 0 in case of failure.
+ *
+ * Note: This is safe to call if the new entry is a copy of the old one (i.e.
+ * item_db2 inheritance), as it will make sure not to free any scripts still in
+ * use in the new entry.
+ */
+int itemdb_validate_entry(struct item_data *entry, int n, const char *source) {
+ struct item_data *item;
+
+ 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 (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( i == 0 ) {
- *atk = *matk = 0;
- return;//no data found
+
+ if( entry->type < 0 || entry->type == IT_UNKNOWN || entry->type == IT_UNKNOWN2
+ || (entry->type > IT_DELAYCONSUME && entry->type < IT_CASH ) || entry->type >= IT_MAX
+ ) {
+ // catch invalid item types
+ ShowWarning("itemdb_validate_entry: Invalid item type %d for item %d in '%s'. IT_ETC will be used.\n",
+ entry->type, entry->nameid, source);
+ entry->type = IT_ETC;
+ } else if( entry->type == IT_DELAYCONSUME ) {
+ //Items that are consumed only after target confirmation
+ entry->type = IT_USABLE;
+ entry->flag.delay_consume = 1;
}
- if( i == 1 ) {//Single Value, we assume it's the ATK
- *atk = val[0];
- *matk = 0;
- return;
+
+ //When a particular price is not given, we should base it off the other one
+ //(it is important to make a distinction between 'no price' and 0z)
+ if( entry->value_buy < 0 && entry->value_sell < 0 ) {
+ entry->value_buy = entry->value_sell = 0;
+ } else if( entry->value_buy < 0 ) {
+ entry->value_buy = entry->value_sell * 2;
+ } else if( entry->value_sell < 0 ) {
+ entry->value_sell = entry->value_buy / 2;
}
- //We assume we have 2 values.
- *atk = val[0];
- *matk = val[1];
- return;
-}
-/*==========================================
- * processes one itemdb entry
- *------------------------------------------*/
-int itemdb_parse_dbrow(char** str, const char* source, int line, int scriptopt) {
- /*
- +----+--------------+---------------+------+-----------+------------+--------+--------+---------+-------+-------+------------+-------------+---------------+-----------------+--------------+-------------+------------+------+--------+--------------+----------------+
- | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
- +----+--------------+---------------+------+-----------+------------+--------+--------+---------+-------+-------+------------+-------------+---------------+-----------------+--------------+-------------+------------+------+--------+--------------+----------------+
- | id | name_english | name_japanese | type | price_buy | price_sell | weight | attack | defence | range | slots | equip_jobs | equip_upper | equip_genders | equip_locations | weapon_level | equip_level | refineable | view | script | equip_script | unequip_script |
- +----+--------------+---------------+------+-----------+------------+--------+--------+---------+-------+-------+------------+-------------+---------------+-----------------+--------------+-------------+------------+------+--------+--------------+----------------+
- */
- int nameid;
- struct item_data* id;
- unsigned char offset = 0;
-
- nameid = atoi(str[0]);
- if( nameid <= 0 ) {
- ShowWarning("itemdb_parse_dbrow: Invalid id %d in line %d of \"%s\", skipping.\n", nameid, line, source);
- return 0;
+ if( entry->value_buy/124. < entry->value_sell/75. ) {
+ ShowWarning("itemdb_validate_entry: Buying/Selling [%d/%d] price of item %d (%s) in '%s' "
+ "allows Zeny making exploit through buying/selling at discounted/overcharged prices!\n",
+ entry->value_buy, entry->value_sell, entry->nameid, entry->jname, source);
}
- //ID,Name,Jname,Type,Price,Sell,Weight,ATK,DEF,Range,Slot,Job,Job Upper,Gender,Loc,wLV,eLV,refineable,View
- id = itemdb_load(nameid);
- safestrncpy(id->name, str[1], sizeof(id->name));
- safestrncpy(id->jname, str[2], sizeof(id->jname));
+ if( entry->slot > MAX_SLOTS ) {
+ ShowWarning("itemdb_validate_entry: Item %d (%s) in '%s' specifies %d slots, but the server only supports up to %d. Using %d slots.\n",
+ entry->nameid, entry->jname, source, entry->slot, MAX_SLOTS, MAX_SLOTS);
+ entry->slot = MAX_SLOTS;
+ }
- id->type = atoi(str[3]);
+ if (!entry->equip && itemdb->isequip2(entry)) {
+ ShowWarning("itemdb_validate_entry: Item %d (%s) in '%s' is an equipment with no equip-field! Making it an etc item.\n",
+ entry->nameid, entry->jname, source);
+ entry->type = IT_ETC;
+ }
- if( id->type < 0 || id->type == IT_UNKNOWN || id->type == IT_UNKNOWN2 || ( id->type > IT_DELAYCONSUME && id->type < IT_CASH ) || id->type >= IT_MAX )
- {// catch invalid item types
- ShowWarning("itemdb_parse_dbrow: Invalid item type %d for item %d. IT_ETC will be used.\n", id->type, nameid);
- id->type = IT_ETC;
+ if (entry->flag.trade_restriction < 0 || 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);
+ entry->flag.trade_restriction = ITR_NONE;
}
- if (id->type == IT_DELAYCONSUME)
- { //Items that are consumed only after target confirmation
- id->type = IT_USABLE;
- id->flag.delay_consume = 1;
- } else //In case of an itemdb reload and the item type changed.
- id->flag.delay_consume = 0;
+ if (entry->gm_lv_trade_override < 0 || entry->gm_lv_trade_override > 100) {
+ ShowWarning("itemdb_validate_entry: Invalid trade-override GM level %d for item %d (%s) in '%s', defaulting to none.\n",
+ entry->gm_lv_trade_override, entry->nameid, entry->jname, source);
+ entry->gm_lv_trade_override = 0;
+ }
+ if (entry->gm_lv_trade_override == 0) {
+ // Default value if none or an ivalid one was specified
+ entry->gm_lv_trade_override = 100;
+ }
- //When a particular price is not given, we should base it off the other one
- //(it is important to make a distinction between 'no price' and 0z)
- if ( str[4][0] )
- id->value_buy = atoi(str[4]);
- else
- id->value_buy = atoi(str[5]) * 2;
+ if (entry->item_usage.flag > INR_ALL) {
+ ShowWarning("itemdb_validate_entry: Invalid nouse flag 0x%x for item %d (%s) in '%s', defaulting to none.\n",
+ entry->item_usage.flag, entry->nameid, entry->jname, source);
+ entry->item_usage.flag = INR_NONE;
+ }
- if ( str[5][0] )
- id->value_sell = atoi(str[5]);
- else
- id->value_sell = id->value_buy / 2;
- /*
- if ( !str[4][0] && !str[5][0])
- {
- ShowWarning("itemdb_parse_dbrow: No buying/selling price defined for item %d (%s), using 20/10z\n", nameid, id->jname);
- id->value_buy = 20;
- id->value_sell = 10;
- } else
- */
- if (id->value_buy/124. < id->value_sell/75.)
- ShowWarning("itemdb_parse_dbrow: Buying/Selling [%d/%d] price of item %d (%s) allows Zeny making exploit through buying/selling at discounted/overcharged prices!\n",
- id->value_buy, id->value_sell, nameid, id->jname);
-
- id->weight = atoi(str[6]);
-#ifdef RENEWAL
- if( iMap->db_use_sqldbs ) {
- id->atk = atoi(str[7]);
- id->matk = atoi(str[8]);
- offset += 1;
- } else
- itemdb_re_split_atoi(str[7],&id->atk,&id->matk);
-#else
- id->atk = atoi(str[7]);
-#endif
- id->def = atoi(str[8+offset]);
- id->range = atoi(str[9+offset]);
- id->slot = atoi(str[10+offset]);
+ if (entry->item_usage.override > 100) {
+ ShowWarning("itemdb_validate_entry: Invalid nouse-override GM level %d for item %d (%s) in '%s', defaulting to none.\n",
+ entry->item_usage.override, entry->nameid, entry->jname, source);
+ entry->item_usage.override = 0;
+ }
+ if (entry->item_usage.override == 0) {
+ // Default value if none or an ivalid one was specified
+ entry->item_usage.override = 100;
+ }
- if (id->slot > MAX_SLOTS) {
- ShowWarning("itemdb_parse_dbrow: Item %d (%s) specifies %d slots, but the server only supports up to %d. Using %d slots.\n", nameid, id->jname, id->slot, MAX_SLOTS, MAX_SLOTS);
- id->slot = MAX_SLOTS;
+ if (entry->stack.amount > 0 && !itemdb->isstackable2(entry)) {
+ ShowWarning("itemdb_validate_entry: Item %d (%s) of type %d is not stackable, ignoring stack settings in '%s'.\n",
+ entry->nameid, entry->jname, entry->type, source);
+ memset(&entry->stack, '\0', sizeof(entry->stack));
}
- itemdb_jobid2mapid(id->class_base, (unsigned int)strtoul(str[11+offset],NULL,0));
- id->class_upper = atoi(str[12+offset]);
- id->sex = atoi(str[13+offset]);
- id->equip = atoi(str[14+offset]);
+ entry->wlv = cap_value(entry->wlv, REFINE_TYPE_ARMOR, REFINE_TYPE_MAX);
- if (!id->equip && itemdb_isequip2(id)) {
- ShowWarning("Item %d (%s) is an equipment with no equip-field! Making it an etc item.\n", nameid, id->jname);
- id->type = IT_ETC;
- }
+ if( !entry->elvmax )
+ entry->elvmax = MAX_LEVEL;
+ else if( entry->elvmax < entry->elv )
+ entry->elvmax = entry->elv;
- id->wlv = cap_value(atoi(str[15+offset]), REFINE_TYPE_ARMOR, REFINE_TYPE_MAX);
-#ifdef RENEWAL
- if( iMap->db_use_sqldbs ) {
- id->elv = atoi(str[16+offset]);
- id->elvmax = atoi(str[17+offset]);
- offset += 1;
- } else
- itemdb_re_split_atoi(str[16],&id->elv,&id->elvmax);
-#else
- id->elv = atoi(str[16]);
-#endif
- id->flag.no_refine = atoi(str[17+offset]) ? 0 : 1; //FIXME: verify this
- id->look = atoi(str[18+offset]);
+ if( entry->type != IT_ARMOR && entry->type != IT_WEAPON && !entry->flag.no_refine )
+ entry->flag.no_refine = 1;
- id->flag.available = 1;
- id->view_id = 0;
- id->sex = itemdb_gendercheck(id); //Apply gender filtering.
+ if (entry->flag.available != 1) {
+ entry->flag.available = 1;
+ entry->view_id = 0;
+ }
+
+ entry->sex = itemdb->gendercheck(entry); //Apply gender filtering.
- if (id->script) {
- script_free_code(id->script);
- id->script = NULL;
+ // Validated. Finally insert it
+ item = itemdb->load(entry->nameid);
+
+ if (item->script && item->script != entry->script) { // Don't free if it's inheriting the same script
+ script->free_code(item->script);
+ item->script = NULL;
}
- if (id->equip_script) {
- script_free_code(id->equip_script);
- id->equip_script = NULL;
+ if (item->equip_script && item->equip_script != entry->equip_script) { // Don't free if it's inheriting the same script
+ script->free_code(item->equip_script);
+ item->equip_script = NULL;
}
- if (id->unequip_script) {
- script_free_code(id->unequip_script);
- id->unequip_script = NULL;
+ if (item->unequip_script && item->unequip_script != entry->unequip_script) { // Don't free if it's inheriting the same script
+ script->free_code(item->unequip_script);
+ item->unequip_script = NULL;
}
- if (*str[19+offset])
- id->script = parse_script(str[19+offset], source, line, scriptopt);
- if (*str[20+offset])
- id->equip_script = parse_script(str[20+offset], source, line, scriptopt);
- if (*str[21+offset])
- id->unequip_script = parse_script(str[21+offset], source, line, scriptopt);
+ *item = *entry;
+ return item->nameid;
+}
+
+/**
+ * Processes one itemdb entry from the sql backend, loading and inserting it
+ * into the item database.
+ *
+ * @param *handle MySQL connection handle. It is expected to have data
+ * available (i.e. already queried) and it won't be freed (it
+ * is care of the caller to do so)
+ * @param n Ordinal number of the entry, to be displayed in case of
+ * validation errors.
+ * @param *source Source of the entry (table name), to be displayed in case of
+ * validation errors.
+ * @return Nameid of the validated entry, or 0 in case of failure.
+ */
+int itemdb_readdb_sql_sub(Sql *handle, int n, const char *source) {
+ struct item_data id = { 0 };
+ char *data = NULL;
+
+ /*
+ * `id` smallint(5) unsigned NOT NULL DEFAULT '0'
+ * `name_english` varchar(50) NOT NULL DEFAULT ''
+ * `name_japanese` varchar(50) NOT NULL DEFAULT ''
+ * `type` tinyint(2) unsigned NOT NULL DEFAULT '0'
+ * `price_buy` mediumint(10) DEFAULT NULL
+ * `price_sell` mediumint(10) DEFAULT NULL
+ * `weight` smallint(5) unsigned DEFAULT NULL
+ * `atk` smallint(5) unsigned DEFAULT NULL
+ * `matk` smallint(5) unsigned DEFAULT NULL
+ * `defence` smallint(5) unsigned DEFAULT NULL
+ * `range` tinyint(2) unsigned DEFAULT NULL
+ * `slots` tinyint(2) unsigned DEFAULT NULL
+ * `equip_jobs` int(12) unsigned DEFAULT NULL
+ * `equip_upper` tinyint(8) unsigned DEFAULT NULL
+ * `equip_genders` tinyint(2) unsigned DEFAULT NULL
+ * `equip_locations` smallint(4) unsigned DEFAULT NULL
+ * `weapon_level` tinyint(2) unsigned DEFAULT NULL
+ * `equip_level_min` smallint(5) unsigned DEFAULT NULL
+ * `equip_level_max` smallint(5) unsigned DEFAULT NULL
+ * `refineable` tinyint(1) unsigned DEFAULT NULL
+ * `view` smallint(3) unsigned DEFAULT NULL
+ * `bindonequip` tinyint(1) unsigned DEFAULT NULL
+ * `buyingstore` tinyint(1) NOT NULL DEFAULT NULL
+ * `delay` mediumint(9) NOT NULL DEFAULT NULL
+ * `trade_flag` smallint(4) NOT NULL DEFAULT NULL
+ * `trade_group` smallint(4) NOT NULL DEFAULT NULL
+ * `nouse_flag` smallint(4) NOT NULL DEFAULT NULL
+ * `nouse_group` smallint(4) NOT NULL DEFAULT NULL
+ * `stack_amount` mediumint(6) NOT NULL DEFAULT NULL
+ * `stack_flag` smallint(2) NOT NULL DEFAULT NULL
+ * `sprite` mediumint(6) NOT NULL DEFAULT NULL
+ * `script` text
+ * `equip_script` text
+ * `unequip_script` text
+ */
+ SQL->GetData(handle, 0, &data, NULL); id.nameid = (uint16)atoi(data);
+ SQL->GetData(handle, 1, &data, NULL); safestrncpy(id.name, data, sizeof(id.name));
+ SQL->GetData(handle, 2, &data, NULL); safestrncpy(id.jname, data, sizeof(id.jname));
+ SQL->GetData(handle, 3, &data, NULL); id.type = atoi(data);
+ SQL->GetData(handle, 4, &data, NULL); id.value_buy = data ? atoi(data) : -1; // Using invalid price -1 when missing, it'll be validated later
+ SQL->GetData(handle, 5, &data, NULL); id.value_sell = data ? atoi(data) : -1;
+ SQL->GetData(handle, 6, &data, NULL); id.weight = data ? atoi(data) : 0;
+ SQL->GetData(handle, 7, &data, NULL); id.atk = data ? atoi(data) : 0;
+ SQL->GetData(handle, 8, &data, NULL); id.matk = data ? atoi(data) : 0;
+ SQL->GetData(handle, 9, &data, NULL); id.def = data ? atoi(data) : 0;
+ SQL->GetData(handle, 10, &data, NULL); id.range = data ? atoi(data) : 0;
+ SQL->GetData(handle, 11, &data, NULL); id.slot = data ? atoi(data) : 0;
+ SQL->GetData(handle, 12, &data, NULL); itemdb->jobid2mapid(id.class_base, data ? (unsigned int)strtoul(data,NULL,0) : UINT_MAX);
+ SQL->GetData(handle, 13, &data, NULL); id.class_upper = data ? (unsigned int)atoi(data) : ITEMUPPER_ALL;
+ SQL->GetData(handle, 14, &data, NULL); id.sex = data ? atoi(data) : 2;
+ SQL->GetData(handle, 15, &data, NULL); id.equip = data ? atoi(data) : 0;
+ SQL->GetData(handle, 16, &data, NULL); id.wlv = data ? atoi(data) : 0;
+ SQL->GetData(handle, 17, &data, NULL); id.elv = data ? atoi(data) : 0;
+ SQL->GetData(handle, 18, &data, NULL); id.elvmax = data ? atoi(data) : 0;
+ SQL->GetData(handle, 19, &data, NULL); id.flag.no_refine = data && atoi(data) ? 0 : 1;
+ SQL->GetData(handle, 20, &data, NULL); id.look = data ? atoi(data) : 0;
+ SQL->GetData(handle, 21, &data, NULL); id.flag.bindonequip = data && atoi(data) ? 1 : 0;
+ SQL->GetData(handle, 22, &data, NULL); id.flag.buyingstore = data && atoi(data) ? 1 : 0;
+ SQL->GetData(handle, 23, &data, NULL); id.delay = data ? atoi(data) : 0;
+ SQL->GetData(handle, 24, &data, NULL); id.flag.trade_restriction = data ? atoi(data) : ITR_NONE;
+ SQL->GetData(handle, 25, &data, NULL); id.gm_lv_trade_override = data ? atoi(data) : 0;
+ SQL->GetData(handle, 26, &data, NULL); id.item_usage.flag = data ? atoi(data) : INR_NONE;
+ SQL->GetData(handle, 27, &data, NULL); id.item_usage.override = data ? atoi(data) : 0;
+ SQL->GetData(handle, 28, &data, NULL); id.stack.amount = data ? atoi(data) : 0;
+ SQL->GetData(handle, 29, &data, NULL);
+ if (data) {
+ int stack_flag = atoi(data);
+ id.stack.inventory = (stack_flag&1)!=0;
+ id.stack.cart = (stack_flag&2)!=0;
+ id.stack.storage = (stack_flag&4)!=0;
+ id.stack.guildstorage = (stack_flag&8)!=0;
+ }
+ SQL->GetData(handle, 30, &data, NULL);
+ if (data) {
+ id.view_id = atoi(data);
+ if (id.view_id)
+ id.flag.available = 1;
+ }
+ SQL->GetData(handle, 31, &data, NULL); id.script = data && *data ? script->parse(data, source, -id.nameid, SCRIPT_IGNORE_EXTERNAL_BRACKETS, NULL) : NULL;
+ SQL->GetData(handle, 32, &data, NULL); id.equip_script = data && *data ? script->parse(data, source, -id.nameid, SCRIPT_IGNORE_EXTERNAL_BRACKETS, NULL) : NULL;
+ SQL->GetData(handle, 33, &data, NULL); id.unequip_script = data && *data ? script->parse(data, source, -id.nameid, SCRIPT_IGNORE_EXTERNAL_BRACKETS, NULL) : NULL;
- return id->nameid;
+ return itemdb->validate_entry(&id, n, source);
}
-/*==========================================
- * Reading item from item db
- * item_db2 overwriting item_db
- *------------------------------------------*/
-static int itemdb_readdb(void)
-{
- const char* filename[] = {
- DBPATH"item_db.txt",
- "item_db2.txt" };
+/**
+ * Processes one itemdb entry from the sql backend, loading and inserting it
+ * into the item database.
+ *
+ * @param *it 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)
+ * @param n Ordinal number of the entry, to be displayed in case of
+ * validation errors.
+ * @param *source Source of the entry (file name), to be displayed in case of
+ * 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) {
+ struct item_data id = { 0 };
+ config_setting_t *t = NULL;
+ const char *str = NULL;
+ int i32 = 0;
+ bool inherit = false;
- int fi;
+ /*
+ * // Mandatory fields
+ * Id: ID
+ * AegisName: "Aegis_Name"
+ * Name: "Item Name"
+ * // Optional fields
+ * Type: Item Type
+ * Buy: Buy Price
+ * Sell: Sell Price
+ * Weight: Item Weight
+ * Atk: Attack
+ * Matk: Attack
+ * Def: Defense
+ * Range: Attack Range
+ * Slots: Slots
+ * Job: Job mask
+ * Upper: Upper mask
+ * Gender: Gender
+ * Loc: Equip location
+ * WeaponLv: Weapon Level
+ * EquipLv: Equip required level or [min, max]
+ * Refine: Refineable
+ * View: View ID
+ * BindOnEquip: (true or false)
+ * BuyingStore: (true or false)
+ * Delay: Delay to use item
+ * Trade: {
+ * override: Group to override
+ * nodrop: (true or false)
+ * notrade: (true or false)
+ * partneroverride: (true or false)
+ * noselltonpc: (true or false)
+ * nocart: (true or false)
+ * nostorage: (true or false)
+ * nogstorage: (true or false)
+ * nomail: (true or false)
+ * noauction: (true or false)
+ * }
+ * Nouse: {
+ * override: Group to override
+ * sitting: (true or false)
+ * }
+ * Stack: [Stackable Amount, Stack Type]
+ * Sprite: SpriteID
+ * Script: <"
+ * Script
+ * (it can be multi-line)
+ * ">
+ * OnEquipScript: <" OnEquip Script ">
+ * OnUnequipScript: <" OnUnequip Script ">
+ * Inherit: inherit or override
+ */
+ if( !libconfig->setting_lookup_int(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;
- for( fi = 0; fi < ARRAYLENGTH(filename); ++fi ) {
- uint32 lines = 0, count = 0;
- char line[1024];
+ if( (t = libconfig->setting_get_member(it, "Inherit")) && (inherit = libconfig->setting_get_bool(t)) ) {
+ if( !itemdb->exists(id.nameid) ) {
+ ShowWarning("itemdb_readdb_libconfig_sub: Trying to inherit nonexistent item %d, default values will be used instead.\n", id.nameid);
+ inherit = false;
+ } else {
+ // Use old entry as default
+ struct item_data *old_entry = itemdb->load(id.nameid);
+ memcpy(&id, old_entry, sizeof(struct item_data));
+ }
+ }
- char path[256];
- FILE* fp;
+ if( !libconfig->setting_lookup_string(it, "AegisName", &str) || !*str ) {
+ if( !inherit ) {
+ ShowWarning("itemdb_readdb_libconfig_sub: Missing AegisName in item %d of \"%s\", skipping.\n", id.nameid, source);
+ return 0;
+ }
+ } else {
+ safestrncpy(id.name, str, sizeof(id.name));
+ }
- sprintf(path, "%s/%s", iMap->db_path, filename[fi]);
- fp = fopen(path, "r");
- if( fp == NULL ) {
- ShowWarning("itemdb_readdb: File not found \"%s\", skipping.\n", path);
- continue;
+ if( !libconfig->setting_lookup_string(it, "Name", &str) || !*str ) {
+ if( !inherit ) {
+ ShowWarning("itemdb_readdb_libconfig_sub: Missing Name in item %d of \"%s\", skipping.\n", id.nameid, source);
+ return 0;
}
+ } else {
+ safestrncpy(id.jname, str, sizeof(id.jname));
+ }
- // process rows one by one
- while(fgets(line, sizeof(line), fp))
- {
- char *str[32], *p;
- int i;
- lines++;
- if(line[0] == '/' && line[1] == '/')
- continue;
- memset(str, 0, sizeof(str));
-
- p = line;
- while( ISSPACE(*p) )
- ++p;
- if( *p == '\0' )
- continue;// empty line
- for( i = 0; i < 19; ++i )
- {
- str[i] = p;
- p = strchr(p,',');
- if( p == NULL )
- break;// comma not found
- *p = '\0';
- ++p;
+ if( libconfig->setting_lookup_int(it, "Type", &i32) )
+ id.type = i32;
+ else if( !inherit )
+ id.type = IT_UNKNOWN;
+
+ if( libconfig->setting_lookup_int(it, "Buy", &i32) )
+ id.value_buy = i32;
+ else if( !inherit )
+ id.value_buy = -1;
+ if( libconfig->setting_lookup_int(it, "Sell", &i32) )
+ id.value_sell = i32;
+ else if( !inherit )
+ id.value_sell = -1;
+
+ if( libconfig->setting_lookup_int(it, "Weight", &i32) && i32 >= 0 )
+ id.weight = i32;
+
+ if( libconfig->setting_lookup_int(it, "Atk", &i32) && i32 >= 0 )
+ id.atk = i32;
+
+ if( libconfig->setting_lookup_int(it, "Matk", &i32) && i32 >= 0 )
+ id.matk = i32;
+
+ if( libconfig->setting_lookup_int(it, "Def", &i32) && i32 >= 0 )
+ id.def = i32;
+
+ if( libconfig->setting_lookup_int(it, "Range", &i32) && i32 >= 0 )
+ id.range = i32;
+
+ if( libconfig->setting_lookup_int(it, "Slots", &i32) && i32 >= 0 )
+ id.slot = i32;
+
+ if( libconfig->setting_lookup_int(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( libconfig->setting_lookup_int(it, "Upper", &i32) && i32 >= 0 )
+ id.class_upper = (unsigned int)i32;
+ else if( !inherit )
+ id.class_upper = ITEMUPPER_ALL;
+
+ if( libconfig->setting_lookup_int(it, "Gender", &i32) && i32 >= 0 )
+ id.sex = i32;
+ else if( !inherit )
+ id.sex = 2;
+
+ if( libconfig->setting_lookup_int(it, "Loc", &i32) && i32 >= 0 )
+ id.equip = i32;
+
+ if( libconfig->setting_lookup_int(it, "WeaponLv", &i32) && i32 >= 0 )
+ id.wlv = i32;
+
+ if( (t = libconfig->setting_get_member(it, "EquipLv")) ) {
+ if( config_setting_is_aggregate(t) ) {
+ if( libconfig->setting_length(t) >= 2 )
+ id.elvmax = libconfig->setting_get_int_elem(t, 1);
+ if( libconfig->setting_length(t) >= 1 )
+ id.elv = libconfig->setting_get_int_elem(t, 0);
+ } else {
+ id.elv = libconfig->setting_get_int(t);
+ }
+ }
+
+ if( (t = libconfig->setting_get_member(it, "Refine")) )
+ id.flag.no_refine = libconfig->setting_get_bool(t) ? 0 : 1;
+
+ if( libconfig->setting_lookup_int(it, "View", &i32) && i32 >= 0 )
+ id.look = i32;
+
+ if( (t = libconfig->setting_get_member(it, "BindOnEquip")) )
+ id.flag.bindonequip = libconfig->setting_get_bool(t) ? 1 : 0;
+
+ if ( (t = libconfig->setting_get_member(it, "BuyingStore")) )
+ id.flag.buyingstore = libconfig->setting_get_bool(t) ? 1 : 0;
+
+ if (libconfig->setting_lookup_int(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;
+
+ if ((tt = libconfig->setting_get_member(t, "override"))) {
+ id.gm_lv_trade_override = libconfig->setting_get_int(tt);
}
- if( p == NULL )
- {
- ShowError("itemdb_readdb: Insufficient columns in line %d of \"%s\" (item with id %d), skipping.\n", lines, path, atoi(str[0]));
- continue;
+ if ((tt = libconfig->setting_get_member(t, "nodrop"))) {
+ id.flag.trade_restriction &= ~ITR_NODROP;
+ if (libconfig->setting_get_bool(tt))
+ id.flag.trade_restriction |= ITR_NODROP;
}
- // Script
- if( *p != '{' )
- {
- ShowError("itemdb_readdb: Invalid format (Script column) in line %d of \"%s\" (item with id %d), skipping.\n", lines, path, atoi(str[0]));
- continue;
+ if ((tt = libconfig->setting_get_member(t, "notrade"))) {
+ id.flag.trade_restriction &= ~ITR_NOTRADE;
+ if (libconfig->setting_get_bool(tt))
+ id.flag.trade_restriction |= ITR_NOTRADE;
}
- str[19] = p;
- p = strstr(p+1,"},");
- if( p == NULL )
- {
- ShowError("itemdb_readdb: Invalid format (Script column) in line %d of \"%s\" (item with id %d), skipping.\n", lines, path, atoi(str[0]));
- continue;
+
+ if ((tt = libconfig->setting_get_member(t, "partneroverride"))) {
+ id.flag.trade_restriction &= ~ITR_PARTNEROVERRIDE;
+ if (libconfig->setting_get_bool(tt))
+ id.flag.trade_restriction |= ITR_PARTNEROVERRIDE;
}
- p[1] = '\0';
- p += 2;
- // OnEquip_Script
- if( *p != '{' )
- {
- ShowError("itemdb_readdb: Invalid format (OnEquip_Script column) in line %d of \"%s\" (item with id %d), skipping.\n", lines, path, atoi(str[0]));
- continue;
+ if ((tt = libconfig->setting_get_member(t, "noselltonpc"))) {
+ id.flag.trade_restriction &= ~ITR_NOSELLTONPC;
+ if (libconfig->setting_get_bool(tt))
+ id.flag.trade_restriction |= ITR_NOSELLTONPC;
}
- str[20] = p;
- p = strstr(p+1,"},");
- if( p == NULL )
- {
- ShowError("itemdb_readdb: Invalid format (OnEquip_Script column) in line %d of \"%s\" (item with id %d), skipping.\n", lines, path, atoi(str[0]));
- continue;
+
+ if ((tt = libconfig->setting_get_member(t, "nocart"))) {
+ id.flag.trade_restriction &= ~ITR_NOCART;
+ if (libconfig->setting_get_bool(tt))
+ id.flag.trade_restriction |= ITR_NOCART;
}
- p[1] = '\0';
- p += 2;
- // OnUnequip_Script (last column)
- if( *p != '{' )
- {
- ShowError("itemdb_readdb: Invalid format (OnUnequip_Script column) in line %d of \"%s\" (item with id %d), skipping.\n", lines, path, atoi(str[0]));
- continue;
+ if ((tt = libconfig->setting_get_member(t, "nostorage"))) {
+ id.flag.trade_restriction &= ~ITR_NOSTORAGE;
+ if (libconfig->setting_get_bool(tt))
+ id.flag.trade_restriction |= ITR_NOSTORAGE;
}
- str[21] = p;
-
- if ( str[21][strlen(str[21])-2] != '}' ) {
- /* lets count to ensure it's not something silly e.g. a extra space at line ending */
- int v, lcurly = 0, rcurly = 0;
-
- for( v = 0; v < strlen(str[21]); v++ ) {
- if( str[21][v] == '{' )
- lcurly++;
- else if ( str[21][v] == '}' )
- rcurly++;
- }
-
- if( lcurly != rcurly ) {
- ShowError("itemdb_readdb: Mismatching curly braces in line %d of \"%s\" (item with id %d), skipping.\n", lines, path, atoi(str[0]));
- continue;
- }
+
+ if ((tt = libconfig->setting_get_member(t, "nogstorage"))) {
+ id.flag.trade_restriction &= ~ITR_NOGSTORAGE;
+ if (libconfig->setting_get_bool(tt))
+ id.flag.trade_restriction |= ITR_NOGSTORAGE;
}
- if (!itemdb->parse_dbrow(str, path, lines, 0))
- continue;
-
- count++;
+ if ((tt = libconfig->setting_get_member(t, "nomail"))) {
+ id.flag.trade_restriction &= ~ITR_NOMAIL;
+ if (libconfig->setting_get_bool(tt))
+ id.flag.trade_restriction |= ITR_NOMAIL;
+ }
+
+ if ((tt = libconfig->setting_get_member(t, "noauction"))) {
+ id.flag.trade_restriction &= ~ITR_NOAUCTION;
+ if (libconfig->setting_get_bool(tt))
+ id.flag.trade_restriction |= ITR_NOAUCTION;
+ }
+ } else { // Fallback to int if it's not a group
+ id.flag.trade_restriction = libconfig->setting_get_int(t);
}
+ }
- fclose(fp);
+ if ((t = libconfig->setting_get_member(it, "Nouse"))) {
+ if (config_setting_is_group(t)) {
+ config_setting_t *nt = NULL;
+
+ if ((nt = libconfig->setting_get_member(t, "override"))) {
+ id.item_usage.override = libconfig->setting_get_int(nt);
+ }
+
+ if ((nt = libconfig->setting_get_member(t, "sitting"))) {
+ id.item_usage.flag &= ~INR_SITTING;
+ if (libconfig->setting_get_bool(nt))
+ id.item_usage.flag |= INR_SITTING;
+ }
- ShowStatus("Done reading '"CL_WHITE"%lu"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filename[fi]);
+ } else { // Fallback to int if it's not a group
+ id.item_usage.flag = libconfig->setting_get_int(t);
+ }
}
- return 0;
+ if ((t = libconfig->setting_get_member(it, "Stack"))) {
+ if (config_setting_is_aggregate(t) && libconfig->setting_length(t) >= 1) {
+ int stack_flag = libconfig->setting_get_int_elem(t, 1);
+ int stack_amount = libconfig->setting_get_int_elem(t, 0);
+ if (stack_amount >= 0) {
+ id.stack.amount = cap_value(stack_amount, 0, USHRT_MAX);
+ id.stack.inventory = (stack_flag&1)!=0;
+ id.stack.cart = (stack_flag&2)!=0;
+ id.stack.storage = (stack_flag&4)!=0;
+ id.stack.guildstorage = (stack_flag&8)!=0;
+ }
+ }
+ }
+
+ if (libconfig->setting_lookup_int(it, "Sprite", &i32) && i32 >= 0) {
+ id.flag.available = 1;
+ id.view_id = i32;
+ }
+
+ if( libconfig->setting_lookup_string(it, "Script", &str) )
+ id.script = *str ? script->parse(str, source, -id.nameid, SCRIPT_IGNORE_EXTERNAL_BRACKETS, NULL) : NULL;
+
+ if( libconfig->setting_lookup_string(it, "OnEquipScript", &str) )
+ id.equip_script = *str ? script->parse(str, source, -id.nameid, SCRIPT_IGNORE_EXTERNAL_BRACKETS, NULL) : NULL;
+
+ if( libconfig->setting_lookup_string(it, "OnUnequipScript", &str) )
+ id.unequip_script = *str ? script->parse(str, source, -id.nameid, SCRIPT_IGNORE_EXTERNAL_BRACKETS, NULL) : NULL;
+
+ return itemdb->validate_entry(&id, n, source);
}
-/*======================================
- * item_db table reading
- *======================================*/
-static int itemdb_read_sqldb(void) {
-
- const char* item_db_name[] = {
- #ifdef RENEWAL
- iMap->item_db_re_db,
- #else
- iMap->item_db_db,
- #endif
- iMap->item_db2_db };
- int fi;
-
- for( fi = 0; fi < ARRAYLENGTH(item_db_name); ++fi ) {
- uint32 count = 0;
-
- // retrieve all rows from the item database
- if( SQL_ERROR == SQL->Query(mmysql_handle, "SELECT * FROM `%s`", item_db_name[fi]) ) {
- Sql_ShowDebug(mmysql_handle);
+
+/**
+ * Reads from a libconfig-formatted itemdb file and inserts the found entries into the
+ * item database, overwriting duplicate ones (i.e. item_db2 overriding item_db.)
+ *
+ * @param *filename File name, relative to the database path.
+ * @return The number of found entries.
+ */
+int itemdb_readdb_libconfig(const char *filename) {
+ bool duplicate[MAX_ITEMDB];
+ config_t item_db_conf;
+ config_setting_t *itdb, *it;
+ char filepath[256];
+ int i = 0, count = 0;
+
+ 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")) ) {
+ ShowError("can't read %s\n", filepath);
+ return 0;
+ }
+
+ while( (it = libconfig->setting_get_elem(itdb,i++)) ) {
+ int nameid = itemdb->readdb_libconfig_sub(it, i-1, filename);
+
+ if( !nameid )
continue;
- }
- // process rows one by one
- while( SQL_SUCCESS == SQL->NextRow(mmysql_handle) ) {// wrap the result into a TXT-compatible format
- char* str[ITEMDB_SQL_COLUMNS];
- char* dummy = "";
- int i;
- for( i = 0; i < ITEMDB_SQL_COLUMNS; ++i ) {
- SQL->GetData(mmysql_handle, i, &str[i], NULL);
- if( str[i] == NULL )
- str[i] = dummy; // get rid of NULL columns
- }
+ count++;
- if (!itemdb->parse_dbrow(str, item_db_name[fi], -(atoi(str[0])), SCRIPT_IGNORE_EXTERNAL_BRACKETS))
- continue;
- ++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;
+ }
+ libconfig->destroy(&item_db_conf);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filename);
+
+ return count;
+}
- // free the query result
- SQL->FreeResult(mmysql_handle);
+/**
+ * Reads from a sql itemdb table and inserts the found entries into the item
+ * database, overwriting duplicate ones (i.e. item_db2 overriding item_db.)
+ *
+ * @param *tablename Table name to query.
+ * @return The number of found entries.
+ */
+int itemdb_readdb_sql(const char *tablename) {
+ int i = 0, count = 0;
+
+ // retrieve all rows from the item database
+ if( SQL_ERROR == SQL->Query(map->mysql_handle, "SELECT `id`, `name_english`, `name_japanese`, `type`,"
+ " `price_buy`, `price_sell`, `weight`, `atk`,"
+ " `matk`, `defence`, `range`, `slots`,"
+ " `equip_jobs`, `equip_upper`, `equip_genders`, `equip_locations`,"
+ " `weapon_level`, `equip_level_min`, `equip_level_max`, `refineable`,"
+ " `view`, `bindonequip`, `buyingstore`, `delay`,"
+ " `trade_flag`, `trade_group`, `nouse_flag`, `nouse_group`,"
+ " `stack_amount`, `stack_flag`, `sprite`, `script`,"
+ " `equip_script`, `unequip_script`"
+ "FROM `%s`", tablename) ) {
+ Sql_ShowDebug(map->mysql_handle);
+ return 0;
+ }
- ShowStatus("Done reading '"CL_WHITE"%lu"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, item_db_name[fi]);
+ // process rows one by one
+ while( SQL_SUCCESS == SQL->NextRow(map->mysql_handle) ) {
+ if( itemdb->readdb_sql_sub(map->mysql_handle, i++, tablename) )
+ count++;
}
- return 0;
+ // free the query result
+ SQL->FreeResult(map->mysql_handle);
+
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, tablename);
+
+ return count;
}
/*==========================================
* Unique item ID function
* Only one operation by once
-* Flag:
-* 0 return new id
-* 1 set new value, checked with current value
-* 2 set new value bypassing anything
-* 3/other return last value
*------------------------------------------*/
-uint64 itemdb_unique_id(int8 flag, int64 value) {
- static uint64 item_uid = 0;
-
- if(flag)
- {
- if(flag == 1)
- { if(item_uid < value)
- return (item_uid = value);
- }else if(flag == 2)
- return (item_uid = value);
-
- return item_uid;
- }
+uint64 itemdb_unique_id(struct map_session_data *sd) {
- return ++item_uid;
+ return ((uint64)sd->status.char_id << 32) | sd->status.uniqueitem_counter++;
}
-int itemdb_uid_load() {
-
- char * uid;
- if (SQL_ERROR == SQL->Query(mmysql_handle, "SELECT `value` FROM `%s` WHERE `varname`='unique_id'",iMap->interreg_db))
- Sql_ShowDebug(mmysql_handle);
- if( SQL_SUCCESS != SQL->NextRow(mmysql_handle) ) {
- ShowError("itemdb_uid_load: Unable to fetch unique_id data\n");
- SQL->FreeResult(mmysql_handle);
- return -1;
+/**
+ * Reads all item-related databases.
+ */
+void itemdb_read(bool minimal) {
+ int i;
+ DBData prev;
+
+ if (map->db_use_sql_item_db) {
+ const char* item_db_name[] = {
+#ifdef RENEWAL
+ map->item_db_re_db,
+#else // not RENEWAL
+ map->item_db_db,
+#endif // RENEWAL
+ map->item_db2_db
+ };
+ for(i = 0; i < ARRAYLENGTH(item_db_name); i++)
+ itemdb->readdb_sql(item_db_name[i]);
+ } else {
+ const char* filename[] = {
+ DBPATH"item_db.conf",
+ "item_db2.conf",
+ };
+
+ for(i = 0; i < ARRAYLENGTH(filename); i++)
+ itemdb->readdb_libconfig(filename[i]);
}
+
+ 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) ) {
+ struct item_data *data = DB->data2ptr(&prev);
+ ShowError("itemdb_read: duplicate AegisName '%s' in item ID %d and %d\n",itemdb->array[i]->name,itemdb->array[i]->nameid,data->nameid);
+ }
+ }
+ }
+
+ if (minimal)
+ return;
- SQL->GetData(mmysql_handle, 0, &uid, NULL);
- itemdb_unique_id(1, (uint64)strtoull(uid, NULL, 10));
- SQL->FreeResult(mmysql_handle);
+ itemdb->read_combos();
+ itemdb->read_groups();
+ itemdb->read_chains();
+ itemdb->read_packages();
- return 0;
}
-/*====================================
- * read all item-related databases
- *------------------------------------*/
-static void itemdb_read(void) {
-
- if (iMap->db_use_sqldbs)
- itemdb_read_sqldb();
- else
- itemdb_readdb();
-
- itemdb_read_combos();
- itemdb_read_itemgroup();
- sv->readdb(iMap->db_path, "item_avail.txt", ',', 2, 2, -1, &itemdb_read_itemavail);
- sv->readdb(iMap->db_path, DBPATH"item_trade.txt", ',', 3, 3, -1, &itemdb_read_itemtrade);
- sv->readdb(iMap->db_path, "item_delay.txt", ',', 2, 2, -1, &itemdb_read_itemdelay);
- sv->readdb(iMap->db_path, "item_stack.txt", ',', 3, 3, -1, &itemdb_read_stack);
- sv->readdb(iMap->db_path, DBPATH"item_buyingstore.txt", ',', 1, 1, -1, &itemdb_read_buyingstore);
- sv->readdb(iMap->db_path, "item_nouse.txt", ',', 3, 3, -1, &itemdb_read_nouse);
-
- itemdb_uid_load();
+/**
+ * retrieves item_combo data by combo id
+ **/
+struct item_combo * itemdb_id2combo( unsigned short id ) {
+ if( id > itemdb->combo_count )
+ return NULL;
+ return itemdb->combos[id];
}
/*==========================================
@@ -1344,28 +2055,19 @@ static void itemdb_read(void) {
*------------------------------------------*/
/// Destroys the item_data.
-static void destroy_item_data(struct item_data* self, int free_self)
+void destroy_item_data(struct item_data* self, int free_self)
{
if( self == NULL )
return;
// free scripts
if( self->script )
- script_free_code(self->script);
+ script->free_code(self->script);
if( self->equip_script )
- script_free_code(self->equip_script);
+ script->free_code(self->equip_script);
if( self->unequip_script )
- script_free_code(self->unequip_script);
- if( self->combos_count ) {
- int i;
- for( i = 0; i < self->combos_count; i++ ) {
- if( !self->combos[i]->isRef ) {
- aFree(self->combos[i]->nameid);
- script_free_code(self->combos[i]->script);
- }
- aFree(self->combos[i]);
- }
+ script->free_code(self->unequip_script);
+ if( self->combos )
aFree(self->combos);
- }
#if defined(DEBUG)
// trash item
memset(self, 0xDD, sizeof(struct item_data));
@@ -1378,34 +2080,92 @@ static void destroy_item_data(struct item_data* self, int free_self)
/**
* @see DBApply
*/
-static int itemdb_final_sub(DBKey key, DBData *data, va_list ap)
+int itemdb_final_sub(DBKey key, DBData *data, va_list ap)
{
struct item_data *id = DB->data2ptr(data);
- if( id != &dummy_item )
- destroy_item_data(id, 1);
+ if( id != &itemdb->dummy )
+ itemdb->destroy_item_data(id, 1);
return 0;
}
+void itemdb_clear(bool total) {
+ int i;
+ // clear the previous itemdb data
+ for( i = 0; i < ARRAYLENGTH(itemdb->array); ++i ) {
+ if( itemdb->array[i] )
+ itemdb->destroy_item_data(itemdb->array[i], 1);
+ }
+
+ for( i = 0; i < itemdb->group_count; i++ ) {
+ if( itemdb->groups[i].nameid )
+ aFree(itemdb->groups[i].nameid);
+ }
+
+ if( itemdb->groups )
+ aFree(itemdb->groups);
+
+ itemdb->groups = NULL;
+ itemdb->group_count = 0;
+
+ for( i = 0; i < itemdb->chain_count; i++ ) {
+ if( itemdb->chains[i].items )
+ aFree(itemdb->chains[i].items);
+ }
+
+ if( itemdb->chains )
+ aFree(itemdb->chains);
+
+ itemdb->chains = NULL;
+ itemdb->chain_count = 0;
+
+ for( i = 0; i < itemdb->package_count; i++ ) {
+ int c;
+ for( c = 0; c < itemdb->packages[i].random_qty; c++ )
+ aFree(itemdb->packages[i].random_groups[c].random_list);
+ if( itemdb->packages[i].random_groups )
+ aFree(itemdb->packages[i].random_groups);
+ if( itemdb->packages[i].must_items )
+ aFree(itemdb->packages[i].must_items);
+ }
+
+ if( itemdb->packages )
+ aFree(itemdb->packages);
+
+ itemdb->packages = NULL;
+ itemdb->package_count = 0;
+
+ for(i = 0; i < itemdb->combo_count; i++) {
+ if( itemdb->combos[i]->script ) // Check if script was loaded
+ script->free_code(itemdb->combos[i]->script);
+ aFree(itemdb->combos[i]);
+ }
+ if( itemdb->combos )
+ aFree(itemdb->combos);
+
+ itemdb->combos = NULL;
+ itemdb->combo_count = 0;
+
+ if( total )
+ return;
+
+ itemdb->other->clear(itemdb->other, itemdb->final_sub);
+
+ memset(itemdb->array, 0, sizeof(itemdb->array));
+
+ db_clear(itemdb->names);
-void itemdb_reload(void)
-{
+}
+void itemdb_reload(void) {
struct s_mapiterator* iter;
struct map_session_data* sd;
int i,d,k;
- // clear the previous itemdb data
- for( i = 0; i < ARRAYLENGTH(itemdb_array); ++i )
- if( itemdb_array[i] )
- destroy_item_data(itemdb_array[i], 1);
+ itemdb->clear(false);
- itemdb_other->clear(itemdb_other, itemdb_final_sub);
-
- memset(itemdb_array, 0, sizeof(itemdb_array));
-
// read new data
- itemdb_read();
+ itemdb->read(false);
//Epoque's awesome @reloaditemdb fix - thanks! [Ind]
//- Fixes the need of a @reloadmobdb after a @reloaditemdb to re-link monster drop data
@@ -1413,12 +2173,12 @@ void itemdb_reload(void)
struct mob_db *entry;
if( !((i < 1324 || i > 1363) && (i < 1938 || i > 1946)) )
continue;
- entry = mob_db(i);
+ entry = mob->db(i);
for(d = 0; d < MAX_MOB_DROP; d++) {
struct item_data *id;
if( !entry->dropitem[d].nameid )
continue;
- id = itemdb_search(entry->dropitem[d].nameid);
+ id = itemdb->search(entry->dropitem[d].nameid);
for (k = 0; k < MAX_SEARCH; k++) {
if (id->mob[k].chance <= entry->dropitem[d].p)
@@ -1440,48 +2200,133 @@ void itemdb_reload(void)
for( sd = (struct map_session_data*)mapit->first(iter); mapit->exists(iter); sd = (struct map_session_data*)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;
/* clear combo bonuses */
- if( sd->combos.count ) {
- aFree(sd->combos.bonus);
- aFree(sd->combos.id);
- sd->combos.bonus = NULL;
- sd->combos.id = NULL;
- sd->combos.count = 0;
+ if( sd->combo_count ) {
+ aFree(sd->combos);
+ sd->combos = NULL;
+ sd->combo_count = 0;
if( pc->load_combo(sd) > 0 )
- status_calc_pc(sd,0);
+ status_calc_pc(sd,SCO_FORCE);
}
-
+ pc->checkitem(sd);
}
mapit->free(iter);
}
+void itemdb_name_constants(void) {
+ DBIterator *iter = db_iterator(itemdb->names);
+ struct item_data *data;
+
+#ifdef ENABLE_CASE_CHECK
+ script->parser_current_file = "Item Database (Likely an invalid or conflicting AegisName)";
+#endif // ENABLE_CASE_CHECK
+ for( data = dbi_first(iter); dbi_exists(iter); data = dbi_next(iter) )
+ script->set_constant2(data->name,data->nameid,0);
+#ifdef ENABLE_CASE_CHECK
+ script->parser_current_file = NULL;
+#endif // ENABLE_CASE_CHECK
+
+ dbi_destroy(iter);
+}
+void do_final_itemdb(void) {
+ itemdb->clear(true);
+
+ itemdb->other->destroy(itemdb->other, itemdb->final_sub);
+ itemdb->destroy_item_data(&itemdb->dummy, 0);
+ db_destroy(itemdb->names);
+}
-void do_final_itemdb(void)
-{
- int i;
-
- for( i = 0; i < ARRAYLENGTH(itemdb_array); ++i )
- if( itemdb_array[i] )
- destroy_item_data(itemdb_array[i], 1);
+void do_init_itemdb(bool minimal) {
+ memset(itemdb->array, 0, sizeof(itemdb->array));
+ itemdb->other = idb_alloc(DB_OPT_BASE);
+ itemdb->names = strdb_alloc(DB_OPT_BASE,ITEM_NAME_LENGTH);
+ itemdb->create_dummy_data(); //Dummy data item.
+ itemdb->read(minimal);
- itemdb_other->destroy(itemdb_other, itemdb_final_sub);
- destroy_item_data(&dummy_item, 0);
-}
+ if (minimal)
+ return;
-int do_init_itemdb(void) {
- memset(itemdb_array, 0, sizeof(itemdb_array));
- itemdb_other = idb_alloc(DB_OPT_BASE);
- create_dummy_data(); //Dummy data item.
- itemdb_read();
clif->cashshop_load();
-
- return 0;
}
-/* incomplete */
void itemdb_defaults(void) {
itemdb = &itemdb_s;
- itemdb->reload = itemdb_reload;//incomplet=e
+ itemdb->init = do_init_itemdb;
+ itemdb->final = do_final_itemdb;
+ itemdb->reload = itemdb_reload;
+ itemdb->name_constants = itemdb_name_constants;
+ /* */
+ itemdb->groups = NULL;
+ itemdb->group_count = 0;
+ /* */
+ itemdb->chains = NULL;
+ itemdb->chain_count = 0;
+ /* */
+ itemdb->packages = NULL;
+ itemdb->package_count = 0;
+ /* */
+ itemdb->combos = NULL;
+ itemdb->combo_count = 0;
+ /* */
+ itemdb->names = NULL;
+ /* */
+ /* itemdb->array is cleared on itemdb->init() */
+ itemdb->other = NULL;
+ memset(&itemdb->dummy, 0, sizeof(struct item_data));
+ /* */
+ itemdb->read_groups = itemdb_read_groups;
+ itemdb->read_chains = itemdb_read_chains;
+ itemdb->read_packages = itemdb_read_packages;
+ /* */
+ itemdb->write_cached_packages = itemdb_write_cached_packages;
+ itemdb->read_cached_packages = itemdb_read_cached_packages;
/* */
- itemdb->parse_dbrow = itemdb_parse_dbrow;
- itemdb->exists = itemdb_exists;//incomplete
+ itemdb->name2id = itemdb_name2id;
+ itemdb->search_name = itemdb_searchname;
+ itemdb->search_name_array = itemdb_searchname_array;
+ itemdb->load = itemdb_load;
+ itemdb->search = itemdb_search;
+ itemdb->exists = itemdb_exists;
+ itemdb->in_group = itemdb_in_group;
+ itemdb->group_item = itemdb_searchrandomid;
+ itemdb->chain_item = itemdb_chain_item;
+ itemdb->package_item = itemdb_package_item;
+ itemdb->searchname_sub = itemdb_searchname_sub;
+ itemdb->searchname_array_sub = itemdb_searchname_array_sub;
+ itemdb->searchrandomid = itemdb_searchrandomid;
+ itemdb->typename = itemdb_typename;
+ itemdb->jobid2mapid = itemdb_jobid2mapid;
+ itemdb->create_dummy_data = create_dummy_data;
+ itemdb->create_item_data = create_item_data;
+ itemdb->isequip = itemdb_isequip;
+ itemdb->isequip2 = itemdb_isequip2;
+ itemdb->isstackable = itemdb_isstackable;
+ itemdb->isstackable2 = itemdb_isstackable2;
+ itemdb->isdropable_sub = itemdb_isdropable_sub;
+ itemdb->cantrade_sub = itemdb_cantrade_sub;
+ itemdb->canpartnertrade_sub = itemdb_canpartnertrade_sub;
+ itemdb->cansell_sub = itemdb_cansell_sub;
+ itemdb->cancartstore_sub = itemdb_cancartstore_sub;
+ itemdb->canstore_sub = itemdb_canstore_sub;
+ itemdb->canguildstore_sub = itemdb_canguildstore_sub;
+ itemdb->canmail_sub = itemdb_canmail_sub;
+ itemdb->canauction_sub = itemdb_canauction_sub;
+ 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->gendercheck = itemdb_gendercheck;
+ itemdb->validate_entry = itemdb_validate_entry;
+ itemdb->readdb_sql_sub = itemdb_readdb_sql_sub;
+ itemdb->readdb_libconfig_sub = itemdb_readdb_libconfig_sub;
+ itemdb->readdb_libconfig = itemdb_readdb_libconfig;
+ itemdb->readdb_sql = itemdb_readdb_sql;
+ itemdb->unique_id = itemdb_unique_id;
+ itemdb->read = itemdb_read;
+ itemdb->destroy_item_data = destroy_item_data;
+ itemdb->final_sub = itemdb_final_sub;
+ itemdb->clear = itemdb_clear;
+ itemdb->id2combo = itemdb_id2combo;
}