summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrei Karas <akaras@inbox.ru>2016-02-05 00:19:58 +0300
committerAndrei Karas <akaras@inbox.ru>2016-02-05 00:19:58 +0300
commit88a6bbe81cdac8392af63d9732b582fa02b04261 (patch)
treeb3ff5d684c24990a1540bee398b59656c066089c /src
parenta59d2cf735c961d86050ae51d345a5a81cfeb77c (diff)
parentdcc9ee43a4b33ace1501864415f5a856b18f8201 (diff)
downloadhercules-88a6bbe81cdac8392af63d9732b582fa02b04261.tar.gz
hercules-88a6bbe81cdac8392af63d9732b582fa02b04261.tar.bz2
hercules-88a6bbe81cdac8392af63d9732b582fa02b04261.tar.xz
hercules-88a6bbe81cdac8392af63d9732b582fa02b04261.zip
Merge pull request #993 from HerculesWS/mobdb2sql
Mob DB support for the db2sql plugin
Diffstat (limited to 'src')
-rw-r--r--src/common/utils.c43
-rw-r--r--src/common/utils.h3
-rw-r--r--src/map/itemdb.c36
-rw-r--r--src/map/mob.c659
-rw-r--r--src/map/mob.h14
-rw-r--r--src/map/npc.c30
-rw-r--r--src/map/pc.c101
-rw-r--r--src/map/script.c30
-rw-r--r--src/map/vending.c12
-rw-r--r--src/plugins/db2sql.c557
10 files changed, 1024 insertions, 461 deletions
diff --git a/src/common/utils.c b/src/common/utils.c
index dcf0a749a..73df3aae1 100644
--- a/src/common/utils.c
+++ b/src/common/utils.c
@@ -25,6 +25,7 @@
#include "common/cbasetypes.h"
#include "common/core.h"
#include "common/mmo.h"
+#include "common/nullpo.h"
#include "common/showmsg.h"
#include "common/socket.h"
#include "common/strlib.h"
@@ -352,6 +353,48 @@ unsigned int get_percentage(const unsigned int A, const unsigned int B)
return (unsigned int)floor(result);
}
+/**
+ * Applies a percentual rate modifier.
+ *
+ * @param value The base value.
+ * @param rate The rate modifier to apply.
+ * @param stdrate The rate modifier's divider (rate == stdrate => 100%).
+ * @return The modified value.
+ */
+int64 apply_percentrate64(int64 value, int rate, int stdrate)
+{
+ Assert_ret(stdrate > 0);
+ Assert_ret(rate >= 0);
+ if (rate == stdrate)
+ return value;
+ if (rate == 0)
+ return 0;
+ if (INT64_MAX / rate < value) {
+ // Give up some precision to prevent overflows
+ return value / stdrate * rate;
+ }
+ return value * rate / stdrate;
+}
+
+/**
+ * Applies a percentual rate modifier.
+ *
+ * @param value The base value.
+ * @param rate The rate modifier to apply. Must be <= maxrate.
+ * @param maxrate The rate modifier's divider (maxrate = 100%).
+ * @return The modified value.
+ */
+int apply_percentrate(int value, int rate, int maxrate)
+{
+ Assert_ret(maxrate > 0);
+ Assert_ret(rate >= 0);
+ if (rate == maxrate)
+ return value;
+ if (rate == 0)
+ return 0;
+ return (int)(value * (int64)rate / maxrate);
+}
+
//-----------------------------------------------------
// custom timestamp formatting (from eApp)
//-----------------------------------------------------
diff --git a/src/common/utils.h b/src/common/utils.h
index da2a29317..3f181ef12 100644
--- a/src/common/utils.h
+++ b/src/common/utils.h
@@ -42,6 +42,9 @@ bool exists(const char* filename);
/// calculates the value of A / B, in percent (rounded down)
unsigned int get_percentage(const unsigned int A, const unsigned int B);
+int64 apply_percentrate64(int64 value, int rate, int maxrate);
+int apply_percentrate(int value, int rate, int maxrate);
+
const char* timestamp2string(char* str, size_t size, time_t timestamp, const char* format);
//////////////////////////////////////////////////////////////////////////
diff --git a/src/map/itemdb.c b/src/map/itemdb.c
index 6428bade5..bd552dd16 100644
--- a/src/map/itemdb.c
+++ b/src/map/itemdb.c
@@ -1385,17 +1385,17 @@ int itemdb_gendercheck(struct item_data *id)
* 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.
+ * @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 (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.
+ * Note: This is safe to call if the new entry is a shallow copy of the old one
+ * (i.e. item_db2 inheritance), as it will make sure not to free any scripts
+ * still in use by the new entry.
*/
int itemdb_validate_entry(struct item_data *entry, int n, const char *source) {
struct item_data *item;
@@ -1544,13 +1544,13 @@ void itemdb_readdb_additional_fields(int itemid, config_setting_t *it, int n, co
* Processes one itemdb entry from the libconfig 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.
+ * @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) {
@@ -1627,7 +1627,7 @@ int itemdb_readdb_libconfig_sub(config_setting_t *it, int n, const char *source)
} else {
// Use old entry as default
struct item_data *old_entry = itemdb->load(id.nameid);
- memcpy(&id, old_entry, sizeof(struct item_data));
+ memcpy(&id, old_entry, sizeof(id));
}
}
@@ -1874,7 +1874,7 @@ bool itemdb_lookup_const(const config_setting_t *it, const char *name, int *valu
* 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.
+ * @param filename File name, relative to the database path.
* @return The number of found entries.
*/
int itemdb_readdb_libconfig(const char *filename) {
diff --git a/src/map/mob.c b/src/map/mob.c
index f93a7fef3..bc78c6098 100644
--- a/src/map/mob.c
+++ b/src/map/mob.c
@@ -2482,15 +2482,15 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) {
if(mvp_sd && md->db->mexp > 0 && md->special_state.ai == AI_NONE) {
int log_mvp[2] = {0};
unsigned int mexp;
- double exp;
+ int64 exp;
//mapflag: noexp check [Lorky]
- if (map->list[m].flag.nobaseexp || type&2)
- exp =1;
- else {
+ if (map->list[m].flag.nobaseexp || type&2) {
+ exp = 1;
+ } else {
exp = md->db->mexp;
if (count > 1)
- exp += exp*(battle_config.exp_bonus_attacker*(count-1))/100.; //[Gengar]
+ exp += apply_percentrate64(exp, battle_config.exp_bonus_attacker * (count-1), 100); //[Gengar]
}
mexp = (unsigned int)cap_value(exp, 1, UINT_MAX);
@@ -3632,7 +3632,7 @@ int mob_makedummymobdb(int class_)
//Adjusts the drop rate of item according to the criteria given. [Skotlex]
unsigned int mob_drop_adjust(int baserate, int rate_adjust, unsigned short rate_min, unsigned short rate_max)
{
- double rate = baserate;
+ int64 rate = baserate;
if (battle_config.logarithmic_drops && rate_adjust > 0 && rate_adjust != 100 && baserate > 0) //Logarithmic drops equation by Ishizu-Chan
//Equation: Droprate(x,y) = x * (5 - log(x)) ^ (ln(y) / ln(5))
@@ -3640,7 +3640,7 @@ unsigned int mob_drop_adjust(int baserate, int rate_adjust, unsigned short rate_
rate = rate * pow((5.0 - log10(rate)), (log(rate_adjust/100.) / log(5.0))) + 0.5;
else
//Classical linear rate adjustment.
- rate = rate * rate_adjust/100;
+ rate = apply_percentrate64(rate, rate_adjust, 100);
return (unsigned int)cap_value(rate,rate_min,rate_max);
}
@@ -3678,30 +3678,45 @@ static inline int mob_parse_dbrow_cap_value(int class_, int min, int max, int va
return value;
}
-void mob_read_db_stats_sub(struct mob_db *entry, struct status_data *mstatus, int class_, config_setting_t *t)
+/**
+ * Processes the stats for a mob database entry.
+ *
+ * @param[in,out] entry The destination mob_db entry, already initialized
+ * (mob_id is expected to be already set).
+ * @param[in] t The libconfig entry.
+ */
+void mob_read_db_stats_sub(struct mob_db *entry, config_setting_t *t)
{
int i32;
if (mob->lookup_const(t, "Str", &i32) && i32 >= 0) {
- mstatus->str = mob_parse_dbrow_cap_value(class_, UINT16_MIN, UINT16_MAX, i32);
+ entry->status.str = mob_parse_dbrow_cap_value(entry->mob_id, UINT16_MIN, UINT16_MAX, i32);
}
if (mob->lookup_const(t, "Agi", &i32) && i32 >= 0) {
- mstatus->agi = mob_parse_dbrow_cap_value(class_, UINT16_MIN, UINT16_MAX, i32);
+ entry->status.agi = mob_parse_dbrow_cap_value(entry->mob_id, UINT16_MIN, UINT16_MAX, i32);
}
if (mob->lookup_const(t, "Vit", &i32) && i32 >= 0) {
- mstatus->vit = mob_parse_dbrow_cap_value(class_, UINT16_MIN, UINT16_MAX, i32);
+ entry->status.vit = mob_parse_dbrow_cap_value(entry->mob_id, UINT16_MIN, UINT16_MAX, i32);
}
if (mob->lookup_const(t, "Int", &i32) && i32 >= 0) {
- mstatus->int_ = mob_parse_dbrow_cap_value(class_, UINT16_MIN, UINT16_MAX, i32);
+ entry->status.int_ = mob_parse_dbrow_cap_value(entry->mob_id, UINT16_MIN, UINT16_MAX, i32);
}
if (mob->lookup_const(t, "Dex", &i32) && i32 >= 0) {
- mstatus->dex = mob_parse_dbrow_cap_value(class_, UINT16_MIN, UINT16_MAX, i32);
+ entry->status.dex = mob_parse_dbrow_cap_value(entry->mob_id, UINT16_MIN, UINT16_MAX, i32);
}
if (mob->lookup_const(t, "Luk", &i32) && i32 >= 0) {
- mstatus->luk = mob_parse_dbrow_cap_value(class_, UINT16_MIN, UINT16_MAX, i32);
+ entry->status.luk = mob_parse_dbrow_cap_value(entry->mob_id, UINT16_MIN, UINT16_MAX, i32);
}
}
-int mob_read_db_mode_sub(struct mob_db *entry, struct status_data *mstatus, int class_, config_setting_t *t)
+/**
+ * Processes the mode for a mob_db entry.
+ *
+ * @param[in] entry The destination mob_db entry, already initialized.
+ * @param[in] t The libconfig entry.
+ *
+ * @return The parsed mode.
+ */
+int mob_read_db_mode_sub(struct mob_db *entry, config_setting_t *t)
{
int mode = 0;
config_setting_t *t2;
@@ -3740,7 +3755,14 @@ int mob_read_db_mode_sub(struct mob_db *entry, struct status_data *mstatus, int
return mode;
}
-void mob_read_db_mvpdrops_sub(struct mob_db *entry, struct status_data *mstatus, int class_, config_setting_t *t)
+/**
+ * Processes the MVP drops for a mob_db entry.
+ *
+ * @param[in,out] entry The destination mob_db entry, already initialized
+ * (mob_id is expected to be already set).
+ * @param[in] t The libconfig entry.
+ */
+void mob_read_db_mvpdrops_sub(struct mob_db *entry, config_setting_t *t)
{
config_setting_t *drop;
int i = 0;
@@ -3752,28 +3774,26 @@ void mob_read_db_mvpdrops_sub(struct mob_db *entry, struct status_data *mstatus,
int rate_adjust = battle_config.item_rate_mvp;
struct item_data* id = itemdb->search_name(name);
int value = 0;
- if (!id)
- {
- ShowWarning("mob_read_db: mvp drop item %s not found in monster %d\n", name, class_);
- i ++;
+ if (!id) {
+ ShowWarning("mob_read_db: mvp drop item %s not found in monster %d\n", name, entry->mob_id);
+ i++;
continue;
}
if (mob->get_const(drop, &i32) && i32 >= 0) {
value = i32;
}
- if (value <= 0)
- {
- ShowWarning("mob_read_db: wrong drop chance %d for mvp drop item %s in monster %d\n", value, name, class_);
- i ++;
+ if (value <= 0) {
+ ShowWarning("mob_read_db: wrong drop chance %d for mvp drop item %s in monster %d\n", value, name, entry->mob_id);
+ i++;
continue;
}
entry->mvpitem[idx].nameid = id->nameid;
if (!entry->mvpitem[idx].nameid) {
entry->mvpitem[idx].p = 0; //No item....
- i ++;
+ i++;
continue;
}
- mob->item_dropratio_adjust(entry->mvpitem[idx].nameid, class_, &rate_adjust);
+ mob->item_dropratio_adjust(entry->mvpitem[idx].nameid, entry->mob_id, &rate_adjust);
entry->mvpitem[idx].p = mob->drop_adjust(value, rate_adjust, battle_config.item_drop_mvp_min, battle_config.item_drop_mvp_max);
//calculate and store Max available drop chance of the MVP item
@@ -3787,11 +3807,18 @@ void mob_read_db_mvpdrops_sub(struct mob_db *entry, struct status_data *mstatus,
idx++;
}
if (idx == MAX_MVP_DROP && libconfig->setting_get_elem(t, i)) {
- ShowWarning("mob_read_db: Too many mvp drops in mob %d\n", class_);
+ ShowWarning("mob_read_db: Too many mvp drops in mob %d\n", entry->mob_id);
}
}
-void mob_read_db_drops_sub(struct mob_db *entry, struct status_data *mstatus, int class_, config_setting_t *t)
+/**
+ * Processes the drops for a mob_db entry.
+ *
+ * @param[in,out] entry The destination mob_db entry, already initialized
+ * (mob_id, status.mode are expected to be already set).
+ * @param[in] t The libconfig entry.
+ */
+void mob_read_db_drops_sub(struct mob_db *entry, config_setting_t *t)
{
config_setting_t *drop;
int i = 0;
@@ -3805,73 +3832,72 @@ void mob_read_db_drops_sub(struct mob_db *entry, struct status_data *mstatus, in
unsigned short ratemin, ratemax;
struct item_data* id = itemdb->search_name(name);
int value = 0;
- if (!id)
- {
- ShowWarning("mob_read_db: drop item %s not found in monster %d\n", name, class_);
- i ++;
+ if (!id) {
+ ShowWarning("mob_read_db: drop item %s not found in monster %d\n", name, entry->mob_id);
+ i++;
continue;
}
if (mob->get_const(drop, &i32) && i32 >= 0) {
value = i32;
}
- if (value <= 0)
- {
- ShowWarning("mob_read_db: wrong drop chance %d for drop item %s in monster %d\n", value, name, class_);
- i ++;
+ if (value <= 0) {
+ ShowWarning("mob_read_db: wrong drop chance %d for drop item %s in monster %d\n", value, name, entry->mob_id);
+ i++;
continue;
}
entry->dropitem[idx].nameid = id->nameid;
if (!entry->dropitem[idx].nameid) {
entry->dropitem[idx].p = 0; //No drop.
- i ++;
+ i++;
continue;
}
type = id->type;
- if ((class_ >= MOBID_TREASURE_BOX1 && class_ <= MOBID_TREASURE_BOX40) || (class_ >= MOBID_TREASURE_BOX41 && class_ <= MOBID_TREASURE_BOX49)) {
+ if ((entry->mob_id >= MOBID_TREASURE_BOX1 && entry->mob_id <= MOBID_TREASURE_BOX40)
+ || (entry->mob_id >= MOBID_TREASURE_BOX41 && entry->mob_id <= MOBID_TREASURE_BOX49)) {
//Treasure box drop rates [Skotlex]
rate_adjust = battle_config.item_rate_treasure;
ratemin = battle_config.item_drop_treasure_min;
ratemax = battle_config.item_drop_treasure_max;
- }
- else switch (type)
- { // Added support to restrict normal drops of MVP's [Reddozen]
+ } else {
+ switch (type) { // Added support to restrict normal drops of MVP's [Reddozen]
case IT_HEALING:
- rate_adjust = (mstatus->mode&MD_BOSS) ? battle_config.item_rate_heal_boss : battle_config.item_rate_heal;
+ rate_adjust = (entry->status.mode&MD_BOSS) ? battle_config.item_rate_heal_boss : battle_config.item_rate_heal;
ratemin = battle_config.item_drop_heal_min;
ratemax = battle_config.item_drop_heal_max;
break;
case IT_USABLE:
case IT_CASH:
- rate_adjust = (mstatus->mode&MD_BOSS) ? battle_config.item_rate_use_boss : battle_config.item_rate_use;
+ rate_adjust = (entry->status.mode&MD_BOSS) ? battle_config.item_rate_use_boss : battle_config.item_rate_use;
ratemin = battle_config.item_drop_use_min;
ratemax = battle_config.item_drop_use_max;
break;
case IT_WEAPON:
case IT_ARMOR:
case IT_PETARMOR:
- rate_adjust = (mstatus->mode&MD_BOSS) ? battle_config.item_rate_equip_boss : battle_config.item_rate_equip;
+ rate_adjust = (entry->status.mode&MD_BOSS) ? battle_config.item_rate_equip_boss : battle_config.item_rate_equip;
ratemin = battle_config.item_drop_equip_min;
ratemax = battle_config.item_drop_equip_max;
break;
case IT_CARD:
- rate_adjust = (mstatus->mode&MD_BOSS) ? battle_config.item_rate_card_boss : battle_config.item_rate_card;
+ rate_adjust = (entry->status.mode&MD_BOSS) ? battle_config.item_rate_card_boss : battle_config.item_rate_card;
ratemin = battle_config.item_drop_card_min;
ratemax = battle_config.item_drop_card_max;
break;
default:
- rate_adjust = (mstatus->mode&MD_BOSS) ? battle_config.item_rate_common_boss : battle_config.item_rate_common;
+ rate_adjust = (entry->status.mode&MD_BOSS) ? battle_config.item_rate_common_boss : battle_config.item_rate_common;
ratemin = battle_config.item_drop_common_min;
ratemax = battle_config.item_drop_common_max;
break;
+ }
}
- mob->item_dropratio_adjust(id->nameid, class_, &rate_adjust);
+ mob->item_dropratio_adjust(id->nameid, entry->mob_id, &rate_adjust);
entry->dropitem[idx].p = mob->drop_adjust(value, rate_adjust, ratemin, ratemax);
//calculate and store Max available drop chance of the item
if (entry->dropitem[idx].p
- && (class_ < MOBID_TREASURE_BOX1 || class_ > MOBID_TREASURE_BOX40)
- && (class_ < MOBID_TREASURE_BOX41 || class_ > MOBID_TREASURE_BOX49)) {
+ && (entry->mob_id < MOBID_TREASURE_BOX1 || entry->mob_id > MOBID_TREASURE_BOX40)
+ && (entry->mob_id < MOBID_TREASURE_BOX41 || entry->mob_id > MOBID_TREASURE_BOX49)) {
//Skip treasure chests.
if (id->maxchance == -1 || (id->maxchance < entry->dropitem[idx].p) ) {
id->maxchance = entry->dropitem[idx].p; //item has bigger drop chance or sold in shops
@@ -3880,361 +3906,454 @@ void mob_read_db_drops_sub(struct mob_db *entry, struct status_data *mstatus, in
if (id->mob[k].chance <= entry->dropitem[idx].p)
break;
}
- if (k == MAX_SEARCH)
- {
+ if (k == MAX_SEARCH) {
i++;
idx++;
continue;
}
- if (id->mob[k].id != class_ && k != MAX_SEARCH - 1)
+ if (id->mob[k].id != entry->mob_id && k != MAX_SEARCH - 1)
memmove(&id->mob[k+1], &id->mob[k], (MAX_SEARCH-k-1)*sizeof(id->mob[0]));
id->mob[k].chance = entry->dropitem[idx].p;
- id->mob[k].id = class_;
+ id->mob[k].id = entry->mob_id;
}
i++;
idx++;
}
if (idx == MAX_MOB_DROP && libconfig->setting_get_elem(t, i)) {
- ShowWarning("mob_read_db: Too many drops in mob %d\n", class_);
+ ShowWarning("mob_read_db: Too many drops in mob %d\n", entry->mob_id);
}
}
-/*==========================================
- * processes one mobdb entry
- *------------------------------------------*/
-bool mob_read_db_sub(config_setting_t *mobt, int id, const char *source)
+/**
+ * Validates a mob DB entry and inserts it into the database.
+ * This function is called after preparing the mob entry data, and it takes
+ * care of inserting it and cleaning up any remainders of the previous one (in
+ * case it is overwriting an existing entry).
+ *
+ * @param entry Pointer to the new mob_db 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 (file name), to be displayed in case of
+ * validation errors.
+ * @return Mob ID of the validated entry, or 0 in case of failure.
+ *
+ * Note: This is safe to call if the new entry is a shallow copy of the old one
+ * (i.e. mob_db2 inheritance), as it will make sure not to free any data still
+ * in use by the new entry.
+ */
+int mob_db_validate_entry(struct mob_db *entry, int n, const char *source)
{
- struct mob_db *entry = NULL, tmpEntry;
- config_setting_t *t = NULL;
- int i32 = 0, value = 0, class_ = 0;
- struct status_data *mstatus;
struct mob_data data;
- const char *str = NULL;
- double maxhp;
- double exp;
- bool inherit = false;
- bool range2Updated = false;
- bool range3Updated = false;
- bool dmotionUpdated = false;
- bool maxhpUpdated = false;
- bool maxspUpdated = false;
- entry = &tmpEntry;
- if (!libconfig->setting_lookup_int(mobt, "Id", &class_)) {
- ShowWarning("mob_read_db_sub: Missing id in \"%s\", entry #%d, skipping.\n", source, class_);
- return false;
+ if (entry->mob_id <= 1000 || entry->mob_id > MAX_MOB_DB) {
+ ShowError("mob_db_validate_entry: Invalid monster ID %d, must be in range %d-%d.\n", entry->mob_id, 1000, MAX_MOB_DB);
+ return 0;
+ }
+ if (pc->db_checkid(entry->mob_id)) {
+ ShowError("mob_read_db_sub: Invalid monster ID %d, reserved for player classes.\n", entry->mob_id);
+ return 0;
+ }
+ if (entry->mob_id >= MOB_CLONE_START && entry->mob_id < MOB_CLONE_END) {
+ ShowError("mob_read_db_sub: Invalid monster ID %d. Range %d-%d is reserved for player clones. Please increase MAX_MOB_DB (%d).\n",
+ entry->mob_id, MOB_CLONE_START, MOB_CLONE_END-1, MAX_MOB_DB);
+ return 0;
}
- if (class_ <= 1000 || class_ > MAX_MOB_DB) {
- ShowError("mob_read_db_sub: Invalid monster ID %d, must be in range %d-%d.\n", class_, 1000, MAX_MOB_DB);
- return false;
+ entry->lv = cap_value(entry->lv, 1, USHRT_MAX);
+
+ if (entry->status.max_sp < 1)
+ entry->status.max_sp = 1;
+ //Since mobs always respawn with full life...
+ entry->status.hp = entry->status.max_hp;
+ entry->status.sp = entry->status.max_sp;
+
+ /*
+ * Disabled for renewal since difference of 0 and 1 still has an impact in the formulas
+ * Just in case there is a mishandled division by zero please let us know. [malufett]
+ */
+#ifndef RENEWAL
+ //All status should be min 1 to prevent divisions by zero from some skills. [Skotlex]
+ if (entry->status.str < 1) entry->status.str = 1;
+ if (entry->status.agi < 1) entry->status.agi = 1;
+ if (entry->status.vit < 1) entry->status.vit = 1;
+ if (entry->status.int_< 1) entry->status.int_= 1;
+ if (entry->status.dex < 1) entry->status.dex = 1;
+ if (entry->status.luk < 1) entry->status.luk = 1;
+#endif
+
+ if (entry->range2 < 1)
+ entry->range2 = 1;
+
+#if 0 // This code was (accidentally) never enabled. It'll stay commented out until it's proven to be needed.
+ //Tests showed that chase range is effectively 2 cells larger than expected [Playtester]
+ if (entry->range3 > 0)
+ entry->range3 += 2;
+#endif // 0
+
+ if (entry->range3 < entry->range2)
+ entry->range3 = entry->range2;
+
+ entry->status.size = cap_value(entry->status.size, 0, 2);
+
+ entry->status.race = cap_value(entry->status.race, 0, RC_MAX - 1);
+
+ if (entry->status.def_ele >= ELE_MAX) {
+ ShowWarning("mob_read_db_sub: Invalid element type %d for monster ID %d (max=%d).\n", entry->status.def_ele, entry->mob_id, ELE_MAX-1);
+ entry->status.def_ele = ELE_NEUTRAL;
+ entry->status.ele_lv = 1;
}
- if (pc->db_checkid(class_)) {
- ShowError("mob_read_db_sub: Invalid monster ID %d, reserved for player classes.\n", class_);
- return false;
+ if (entry->status.ele_lv < 1 || entry->status.ele_lv > 4) {
+ ShowWarning("mob_read_db_sub: Invalid element level %d for monster ID %d, must be in range 1-4.\n", entry->status.ele_lv, entry->mob_id);
+ entry->status.ele_lv = 1;
}
- if (class_ >= MOB_CLONE_START && class_ < MOB_CLONE_END) {
- ShowError("mob_read_db_sub: Invalid monster ID %d. Range %d-%d is reserved for player clones. Please increase MAX_MOB_DB (%d).\n", class_, MOB_CLONE_START, MOB_CLONE_END-1, MAX_MOB_DB);
- return false;
+ // If the attack animation is longer than the delay, the client crops the attack animation!
+ // On aegis there is no real visible effect of having a recharge-time less than amotion anyway.
+ if (entry->status.adelay < entry->status.amotion)
+ entry->status.adelay = entry->status.amotion;
+
+ // Fill in remaining status data by using a dummy monster.
+ data.bl.type = BL_MOB;
+ data.level = entry->lv;
+ memcpy(&data.status, &entry->status, sizeof(struct status_data));
+ status->calc_misc(&data.bl, &entry->status, entry->lv);
+
+ // Finally insert monster's data into the database.
+ if (mob->db_data[entry->mob_id] == NULL) {
+ mob->db_data[entry->mob_id] = (struct mob_db*)aMalloc(sizeof(struct mob_db));
+ } else {
+ //Copy over spawn data
+ memcpy(&entry->spawn, mob->db_data[entry->mob_id]->spawn, sizeof(entry->spawn));
}
+ memcpy(mob->db_data[entry->mob_id], entry, sizeof(struct mob_db));
+
+ return entry->mob_id;
+}
+
+/**
+ * Processes one mobdb entry from the libconfig file, loading and inserting it
+ * into the mob database.
+ *
+ * @param mobt 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 Mob ID of the validated entry, or 0 in case of failure.
+ */
+int mob_read_db_sub(config_setting_t *mobt, int n, const char *source)
+{
+ struct mob_db md = { 0 };
+ config_setting_t *t = NULL;
+ const char *str = NULL;
+ int i32 = 0;
+ bool inherit = false;
+ bool maxhpUpdated = false;
+
+ nullpo_ret(mobt);
+ /*
+ * // Mandatory fields
+ * Id: ID
+ * SpriteName: "SPRITE_NAME"
+ * Name: "Mob name"
+ * JName: "Mob name"
+ * // Optional fields
+ * Lv: level
+ * Hp: health
+ * Sp: mana
+ * Exp: basic experience
+ * JExp: job experience
+ * AttackRange: attack range
+ * Attack: [attack1, attack2]
+ * Def: defence
+ * Mdef: magic defence
+ * Stats: {
+ * Str: strength
+ * Agi: agility
+ * Vit: vitality
+ * Int: intelligence
+ * Dex: dexterity
+ * Luk: luck
+ * }
+ * ViewRange: view range
+ * ChaseRange: chase range
+ * Size: size
+ * Race: race
+ * Element: (type, level)
+ * Mode: {
+ * CanMove: true/false
+ * Looter: true/false
+ * Aggressive: true/false
+ * Assist: true/false
+ * CastSensorIdle:true/false
+ * Boss: true/false
+ * Plant: true/false
+ * CanAttack: true/false
+ * Detector: true/false
+ * CastSensorChase: true/false
+ * ChangeChase: true/false
+ * Angry: true/false
+ * ChangeTargetMelee: true/false
+ * ChangeTargetChase: true/false
+ * TargetWeak: true/false
+ * }
+ * MoveSpeed: move speed
+ * AttackDelay: attack delay
+ * AttackMotion: attack motion
+ * DamageMotion: damage motion
+ * MvpExp: mvp experience
+ * MvpDrops: {
+ * AegisName: chance
+ * ...
+ * }
+ * Drops: {
+ * AegisName: chance
+ * ...
+ * }
+ */
+
+ if (!libconfig->setting_lookup_int(mobt, "Id", &i32)) {
+ ShowWarning("mob_read_db_sub: Missing id in \"%s\", entry #%d, skipping.\n", source, n);
+ return 0;
+ }
+ md.mob_id = i32;
+ md.vd.class_ = md.mob_id;
if ((t = libconfig->setting_get_member(mobt, "Inherit")) && (inherit = libconfig->setting_get_bool(t))) {
- if (!mob->db_data[class_]) {
- ShowWarning("mob_read_db_sub: Trying to inherit nonexistent mob %d, default values will be used instead.\n", class_);
+ if (!mob->db_data[md.mob_id]) {
+ ShowWarning("mob_read_db_sub: Trying to inherit nonexistent mob %d, default values will be used instead.\n", md.mob_id);
inherit = false;
} else {
// Use old entry as default
- struct mob_db *old_entry = mob->db_data[class_];
- memcpy(entry, old_entry, sizeof(struct mob_db));
- inherit = true;
+ struct mob_db *old_entry = mob->db_data[md.mob_id];
+ memcpy(&md, old_entry, sizeof(md));
}
}
- if (!inherit) {
- memset(&tmpEntry, 0, sizeof(tmpEntry));
- }
-
- mstatus = &entry->status;
-
- entry->vd.class_ = class_;
if (!libconfig->setting_lookup_string(mobt, "SpriteName", &str) || !*str ) {
if (!inherit) {
- ShowWarning("mob_read_db_sub: Missing SpriteName in mob %d of \"%s\", skipping.\n", class_, source);
- return false;
+ ShowWarning("mob_read_db_sub: Missing SpriteName in mob %d of \"%s\", skipping.\n", md.mob_id, source);
+ return 0;
}
} else {
- safestrncpy(entry->sprite, str, sizeof(entry->sprite));
+ safestrncpy(md.sprite, str, sizeof(md.sprite));
}
if (!libconfig->setting_lookup_string(mobt, "Name", &str) || !*str ) {
if (!inherit) {
- ShowWarning("mob_read_db_sub: Missing Name in mob %d of \"%s\", skipping.\n", class_, source);
- return false;
+ ShowWarning("mob_read_db_sub: Missing Name in mob %d of \"%s\", skipping.\n", md.mob_id, source);
+ return 0;
+ }
+ } else {
+ safestrncpy(md.name, str, sizeof(md.name));
+ }
+
+ if (!libconfig->setting_lookup_string(mobt, "JName", &str) || !*str ) {
+ if (!inherit) {
+ safestrncpy(md.jname, md.name, sizeof(md.jname));
}
} else {
- safestrncpy(entry->name, str, sizeof(entry->name));
- safestrncpy(entry->jname, str, sizeof(entry->jname));
+ safestrncpy(md.jname, str, sizeof(md.jname));
}
if (mob->lookup_const(mobt, "Lv", &i32) && i32 >= 0) {
- entry->lv = i32;
- entry->lv = cap_value(entry->lv, 1, USHRT_MAX);
+ md.lv = i32;
} else if (!inherit) {
- entry->lv = 1;
+ md.lv = 1;
}
if (mob->lookup_const(mobt, "Hp", &i32) && i32 >= 0) {
- mstatus->max_hp = i32;
- maxhpUpdated = true;
+ md.status.max_hp = i32;
+ maxhpUpdated = true; // battle_config modifiers to max_hp are applied below
} else if (!inherit) {
- mstatus->max_hp = 1;
- maxhpUpdated = true;
+ md.status.max_hp = 1;
+ maxhpUpdated = true; // battle_config modifiers to max_hp are applied below
}
if (mob->lookup_const(mobt, "Sp", &i32) && i32 >= 0) {
- mstatus->max_sp = i32;
- maxspUpdated = true;
+ md.status.max_sp = i32;
} else if (!inherit) {
- maxspUpdated = true;
+ md.status.max_sp = 1;
}
if (mob->lookup_const(mobt, "Exp", &i32) && i32 >= 0) {
- exp = (double)(i32) * (double)battle_config.base_exp_rate / 100.;
- entry->base_exp = (unsigned int)cap_value(exp, 0, UINT_MAX);
+ int64 exp = apply_percentrate64(i32, battle_config.base_exp_rate, 100);
+ md.base_exp = (unsigned int)cap_value(exp, 0, UINT_MAX);
}
if (mob->lookup_const(mobt, "JExp", &i32) && i32 >= 0) {
- exp = (double)(i32) * (double)battle_config.job_exp_rate / 100.;
- entry->job_exp = (unsigned int)cap_value(exp, 0, UINT_MAX);
+ int64 exp = apply_percentrate64(i32, battle_config.job_exp_rate, 100);
+ md.job_exp = (unsigned int)cap_value(exp, 0, UINT_MAX);
}
if (mob->lookup_const(mobt, "AttackRange", &i32) && i32 >= 0) {
- mstatus->rhw.range = i32;
+ md.status.rhw.range = i32;
} else {
- mstatus->rhw.range = 1;
+ md.status.rhw.range = 1;
}
if ((t = libconfig->setting_get_member(mobt, "Attack"))) {
if (config_setting_is_aggregate(t)) {
if (libconfig->setting_length(t) >= 2)
- mstatus->rhw.atk2 = libconfig->setting_get_int_elem(t, 1);
+ md.status.rhw.atk2 = libconfig->setting_get_int_elem(t, 1);
if (libconfig->setting_length(t) >= 1)
- mstatus->rhw.atk = libconfig->setting_get_int_elem(t, 0);
+ md.status.rhw.atk = libconfig->setting_get_int_elem(t, 0);
} else if (mob->lookup_const(mobt, "Attack", &i32) && i32 >= 0) {
- mstatus->rhw.atk = i32;
- mstatus->rhw.atk2 = i32;
+ md.status.rhw.atk = i32;
+ md.status.rhw.atk2 = i32;
}
}
if (mob->lookup_const(mobt, "Def", &i32) && i32 >= 0) {
- mstatus->def = mob_parse_dbrow_cap_value(class_, DEFTYPE_MIN, DEFTYPE_MAX, i32);
+ md.status.def = mob_parse_dbrow_cap_value(md.mob_id, DEFTYPE_MIN, DEFTYPE_MAX, i32);
}
if (mob->lookup_const(mobt, "Mdef", &i32) && i32 >= 0) {
- mstatus->mdef = mob_parse_dbrow_cap_value(class_, DEFTYPE_MIN, DEFTYPE_MAX, i32);
+ md.status.mdef = mob_parse_dbrow_cap_value(md.mob_id, DEFTYPE_MIN, DEFTYPE_MAX, i32);
}
if ((t = libconfig->setting_get_member(mobt, "Stats"))) {
if (config_setting_is_group(t)) {
- mob->read_db_stats_sub(entry, mstatus, class_, t);
+ mob->read_db_stats_sub(&md, t);
} else if (mob->lookup_const(mobt, "Stats", &i32) && i32 >= 0) {
- mstatus->str = mstatus->agi = mstatus->vit = mstatus->int_ = mstatus->dex = mstatus->luk =
- mob_parse_dbrow_cap_value(class_, UINT16_MIN, UINT16_MAX, i32);
+ md.status.str = md.status.agi = md.status.vit = md.status.int_ = md.status.dex = md.status.luk =
+ mob_parse_dbrow_cap_value(md.mob_id, UINT16_MIN, UINT16_MAX, i32);
}
}
- /*
- * Disabled for renewal since difference of 0 and 1 still has an impact in the formulas
- * Just in case there is a mishandled division by zero please let us know. [malufett]
- */
-#ifndef RENEWAL
- //All status should be min 1 to prevent divisions by zero from some skills. [Skotlex]
- if (mstatus->str < 1) mstatus->str = 1;
- if (mstatus->agi < 1) mstatus->agi = 1;
- if (mstatus->vit < 1) mstatus->vit = 1;
- if (mstatus->int_< 1) mstatus->int_= 1;
- if (mstatus->dex < 1) mstatus->dex = 1;
- if (mstatus->luk < 1) mstatus->luk = 1;
-#endif
-
- //Tests showed that chase range is effectively 2 cells larger than expected [Playtester]
- if (entry->range3 > 0)
- entry->range3 += 2;
-
if (mob->lookup_const(mobt, "ViewRange", &i32) && i32 >= 0) {
- entry->range2 = i32;
- range2Updated = true;
+ if (battle_config.view_range_rate != 100) {
+ md.range2 = i32 * battle_config.view_range_rate / 100;
+ } else {
+ md.range2 = i32;
+ }
} else if (!inherit) {
- entry->range2 = 1;
- range2Updated = true;
+ md.range2 = 1;
}
if (mob->lookup_const(mobt, "ChaseRange", &i32) && i32 >= 0) {
- entry->range3 = i32;
- range3Updated = true;
- } else if (!inherit) {
- entry->range3 = 1;
- range3Updated = true;
- }
- if (range2Updated) {
- if (battle_config.view_range_rate != 100) {
- entry->range2 = entry->range2 * battle_config.view_range_rate / 100;
- if (entry->range2 < 1)
- entry->range2 = 1;
- }
- }
- if (range3Updated) {
if (battle_config.chase_range_rate != 100) {
- entry->range3 = entry->range3 * battle_config.chase_range_rate / 100;
- if (entry->range3 < entry->range2)
- entry->range3 = entry->range2;
+ md.range3 = i32 * battle_config.chase_range_rate / 100;
+ } else {
+ md.range3 = i32;
}
+ } else if (!inherit) {
+ md.range3 = 1;
}
if (mob->lookup_const(mobt, "Size", &i32) && i32 >= 0) {
- mstatus->size = i32;
- mstatus->size = cap_value(mstatus->size, 0, 2);
+ md.status.size = i32;
} else if (!inherit) {
- mstatus->size = 0;
+ md.status.size = 0;
}
if (mob->lookup_const(mobt, "Race", &i32) && i32 >= 0) {
- mstatus->race = i32;
- mstatus->race = cap_value(mstatus->race, 0, RC_MAX - 1);
+ md.status.race = i32;
} else if (!inherit) {
- mstatus->race = 0;
+ md.status.race = 0;
}
if ((t = libconfig->setting_get_member(mobt, "Element")) && config_setting_is_list(t)) {
+ int value = 0;
if (mob->get_const(libconfig->setting_get_elem(t, 0), &i32) && mob->get_const(libconfig->setting_get_elem(t, 1), &value)) {
- mstatus->def_ele = i32;
- mstatus->ele_lv = value;
- }
- } else {
- if (!inherit) {
- ShowError("mob_read_db_sub: Missing element for monster ID %d.\n", class_);
- return false;
- }
- }
-
- if (mstatus->def_ele >= ELE_MAX) {
- if (!inherit) {
- ShowError("mob_read_db_sub: Invalid element type %d for monster ID %d (max=%d).\n", mstatus->def_ele, class_, ELE_MAX-1);
- return false;
- }
- }
- if (mstatus->ele_lv < 1 || mstatus->ele_lv > 4) {
- if (!inherit) {
- ShowError("mob_read_db_sub: Invalid element level %d for monster ID %d, must be in range 1-4.\n", mstatus->ele_lv, class_);
- return false;
+ md.status.def_ele = i32;
+ md.status.ele_lv = value;
+ } else if (!inherit) {
+ ShowWarning("mob_read_db_sub: Missing element for monster ID %d.\n", md.mob_id);
+ md.status.def_ele = ELE_NEUTRAL;
+ md.status.ele_lv = 1;
}
+ } else if (!inherit) {
+ ShowWarning("mob_read_db_sub: Missing element for monster ID %d.\n", md.mob_id);
+ md.status.def_ele = ELE_NEUTRAL;
+ md.status.ele_lv = 1;
}
if ((t = libconfig->setting_get_member(mobt, "Mode"))) {
if (config_setting_is_group(t)) {
- mstatus->mode = mob->read_db_mode_sub(entry, mstatus, class_, t);
+ md.status.mode = mob->read_db_mode_sub(&md, t);
} else if (mob->lookup_const(mobt, "Mode", &i32) && i32 >= 0) {
- mstatus->mode = i32;
+ md.status.mode = i32;
}
}
-
if (!battle_config.monster_active_enable)
- mstatus->mode &= ~MD_AGGRESSIVE;
+ md.status.mode &= ~MD_AGGRESSIVE;
if (mob->lookup_const(mobt, "MoveSpeed", &i32) && i32 >= 0) {
- mstatus->speed = i32;
+ md.status.speed = i32;
}
- mstatus->aspd_rate = 1000;
+ md.status.aspd_rate = 1000;
if (mob->lookup_const(mobt, "AttackDelay", &i32) && i32 >= 0) {
- mstatus->adelay = cap_value(i32, battle_config.monster_max_aspd*2, 4000);
+ md.status.adelay = cap_value(i32, battle_config.monster_max_aspd*2, 4000);
} else if (!inherit) {
- mstatus->adelay = 4000;
+ md.status.adelay = 4000;
}
if (mob->lookup_const(mobt, "AttackMotion", &i32) && i32 >= 0) {
- mstatus->amotion = cap_value(i32, battle_config.monster_max_aspd, 2000);
+ md.status.amotion = cap_value(i32, battle_config.monster_max_aspd, 2000);
} else if (!inherit) {
- mstatus->amotion = 2000;
+ md.status.amotion = 2000;
}
- //If the attack animation is longer than the delay, the client crops the attack animation!
- //On aegis there is no real visible effect of having a recharge-time less than amotion anyway.
- if (mstatus->adelay < mstatus->amotion)
- mstatus->adelay = mstatus->amotion;
-
if (mob->lookup_const(mobt, "DamageMotion", &i32) && i32 >= 0) {
- mstatus->dmotion = i32;
- dmotionUpdated = true;
- } else if (!inherit) {
- dmotionUpdated = true;
+ if (battle_config.monster_damage_delay_rate != 100)
+ md.status.dmotion = i32 * battle_config.monster_damage_delay_rate / 100;
+ else
+ md.status.dmotion = i32;
}
- if (dmotionUpdated && battle_config.monster_damage_delay_rate != 100)
- mstatus->dmotion = mstatus->dmotion * battle_config.monster_damage_delay_rate / 100;
-
- // Fill in remaining status data by using a dummy monster.
- data.bl.type = BL_MOB;
- data.level = entry->lv;
- memcpy(&data.status, mstatus, sizeof(struct status_data));
- status->calc_misc(&data.bl, mstatus, entry->lv);
-
// MVP EXP Bonus: MEXP
- // Some new MVP's MEXP multiple by high exp-rate cause overflow. [LuzZza]
if (mob->lookup_const(mobt, "MvpExp", &i32) && i32 >= 0) {
- exp = (double)i32 * (double)battle_config.mvp_exp_rate / 100.;
- entry->mexp = (unsigned int)cap_value(exp, 0, UINT_MAX);
- } else if (!inherit) {
- exp = 0;
+ // Some new MVP's MEXP multiple by high exp-rate cause overflow. [LuzZza]
+ int64 exp = apply_percentrate64(i32, battle_config.mvp_exp_rate, 100);
+ md.mexp = (unsigned int)cap_value(exp, 0, UINT_MAX);
}
if (maxhpUpdated) {
- //Now that we know if it is an mvp or not, apply battle_config modifiers [Skotlex]
- maxhp = (double)mstatus->max_hp;
- if (entry->mexp > 0) { //Mvp
- if (battle_config.mvp_hp_rate != 100)
- maxhp = maxhp * (double)battle_config.mvp_hp_rate / 100.;
+ // Now that we know if it is an mvp or not, apply battle_config modifiers [Skotlex]
+ int64 maxhp = md.status.max_hp;
+ if (md.mexp > 0) { //Mvp
+ maxhp = apply_percentrate64(maxhp, battle_config.mvp_hp_rate, 100);
} else { //Normal mob
- if (battle_config.monster_hp_rate != 100)
- maxhp = maxhp * (double)battle_config.monster_hp_rate / 100.;
+ maxhp = apply_percentrate64(maxhp, battle_config.monster_hp_rate, 100);
}
- mstatus->max_hp = (unsigned int)cap_value(maxhp, 1, UINT_MAX);
+ md.status.max_hp = (unsigned int)cap_value(maxhp, 1, UINT_MAX);
}
- if (maxspUpdated) {
- if(mstatus->max_sp < 1) mstatus->max_sp = 1;
- }
-
- //Since mobs always respawn with full life...
- mstatus->hp = mstatus->max_hp;
- mstatus->sp = mstatus->max_sp;
if ((t = libconfig->setting_get_member(mobt, "MvpDrops"))) {
if (config_setting_is_group(t)) {
- mob->read_db_mvpdrops_sub(entry, mstatus, class_, t);
+ mob->read_db_mvpdrops_sub(&md, t);
}
}
if ((t = libconfig->setting_get_member(mobt, "Drops"))) {
if (config_setting_is_group(t)) {
- mob->read_db_drops_sub(entry, mstatus, class_, t);
+ mob->read_db_drops_sub(&md, t);
}
}
- mob->read_db_additional_fields(entry, class_, mobt, id, source);
- // Finally insert monster's data into the database.
- if (mob->db_data[class_] == NULL)
- mob->db_data[class_] = (struct mob_db*)aMalloc(sizeof(struct mob_db));
- else
- //Copy over spawn data
- memcpy(&entry->spawn, mob->db_data[class_]->spawn, sizeof(entry->spawn));
+ mob->read_db_additional_fields(&md, mobt, n, source);
- memcpy(mob->db_data[class_], entry, sizeof(struct mob_db));
- return true;
+ return mob->db_validate_entry(&md, n, source);
}
-void mob_read_db_additional_fields(struct mob_db *entry, int class_, config_setting_t *it, int n, const char *source)
+/**
+ * Processes any (plugin-defined) additional fields for a mob_db entry.
+ *
+ * @param[in,out] entry The destination mob_db entry, already initialized
+ * (mob_id, status.mode are expected to be already set).
+ * @param[in] t The libconfig entry.
+ * @param[in] n Ordinal number of the entry, to be displayed in case
+ * of validation errors.
+ * @param[in] source Source of the entry (file name), to be displayed in
+ * case of validation errors.
+ */
+void mob_read_db_additional_fields(struct mob_db *entry, config_setting_t *t, int n, const char *source)
{
// do nothing. plugins can do own work
}
@@ -4282,13 +4401,23 @@ void mob_readdb(void) {
mob->name_constants();
}
+/**
+ * Reads from a libconfig-formatted mobdb file and inserts the found entries
+ * into the mob database, overwriting duplicate ones (i.e. mob_db2 overriding
+ * mob_db.)
+ *
+ * @param filename File name, relative to the database path.
+ * @param ignore_missing Whether to ignore errors caused by a missing db file.
+ * @return the number of found entries.
+ */
int mob_read_libconfig(const char *filename, bool ignore_missing)
{
+ bool duplicate[MAX_MOB_DB] = { 0 };
config_t mob_db_conf;
char filepath[256];
config_setting_t *mdb;
config_setting_t *t;
- int i = 0;
+ int i = 0, count = 0;
nullpo_ret(filename);
sprintf(filepath, "%s/%s", map->db_path, filename);
@@ -4298,15 +4427,28 @@ int mob_read_libconfig(const char *filename, bool ignore_missing)
if (libconfig->read_file(&mob_db_conf, filepath) || !(mdb = libconfig->setting_get_member(mob_db_conf.root, "mob_db"))) {
ShowError("can't read %s\n", filepath);
- return -1;
+ return 0;
}
while ((t = libconfig->setting_get_elem(mdb, i++))) {
- mob->read_db_sub(t, i - 1, filepath);
+ int mob_id = mob->read_db_sub(t, i - 1, filename);
+
+ if (mob_id <= 0 || mob_id >= MAX_MOB_DB)
+ continue;
+
+ count++;
+
+ if (duplicate[mob_id]) {
+ ShowWarning("mob_read_libconfig:%s: duplicate entry of ID #%d (%s/%s)\n",
+ filename, mob_id, mob->db_data[mob_id]->sprite, mob->db_data[mob_id]->jname);
+ } else {
+ duplicate[mob_id] = true;
+ }
}
libconfig->destroy(&mob_db_conf);
- ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", i, filepath);
- return 0;
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filename);
+
+ return count;
}
void mob_name_constants(void) {
@@ -5127,6 +5269,7 @@ void mob_defaults(void) {
mob->item_dropratio_adjust = item_dropratio_adjust;
mob->lookup_const = mob_lookup_const;
mob->get_const = mob_get_const;
+ mob->db_validate_entry = mob_db_validate_entry;
mob->readdb = mob_readdb;
mob->read_libconfig = mob_read_libconfig;
mob->read_db_additional_fields = mob_read_db_additional_fields;
diff --git a/src/map/mob.h b/src/map/mob.h
index 77218bf4a..9a5239b11 100644
--- a/src/map/mob.h
+++ b/src/map/mob.h
@@ -141,6 +141,7 @@ struct spawn_info {
};
struct mob_db {
+ int mob_id;
char sprite[NAME_LENGTH],name[NAME_LENGTH],jname[NAME_LENGTH];
unsigned int base_exp,job_exp;
unsigned int mexp;
@@ -511,13 +512,14 @@ struct mob_interface {
void (*readdb) (void);
bool (*lookup_const) (const config_setting_t *it, const char *name, int *value);
bool (*get_const) (const config_setting_t *it, int *value);
+ int (*db_validate_entry) (struct mob_db *entry, int n, const char *source);
int (*read_libconfig) (const char *filename, bool ignore_missing);
- void (*read_db_additional_fields) (struct mob_db *entry, int class_, config_setting_t *it, int n, const char *source);
- bool (*read_db_sub) (config_setting_t *mobt, int id, const char *source);
- void (*read_db_drops_sub) (struct mob_db *entry, struct status_data *mstatus, int class_, config_setting_t *t);
- void (*read_db_mvpdrops_sub) (struct mob_db *entry, struct status_data *mstatus, int class_, config_setting_t *t);
- int (*read_db_mode_sub) (struct mob_db *entry, struct status_data *mstatus, int class_, config_setting_t *t);
- void (*read_db_stats_sub) (struct mob_db *entry, struct status_data *mstatus, int class_, config_setting_t *t);
+ void (*read_db_additional_fields) (struct mob_db *entry, config_setting_t *it, int n, const char *source);
+ int (*read_db_sub) (config_setting_t *mobt, int id, const char *source);
+ void (*read_db_drops_sub) (struct mob_db *entry, config_setting_t *t);
+ void (*read_db_mvpdrops_sub) (struct mob_db *entry, config_setting_t *t);
+ int (*read_db_mode_sub) (struct mob_db *entry, config_setting_t *t);
+ void (*read_db_stats_sub) (struct mob_db *entry, config_setting_t *t);
void (*name_constants) (void);
bool (*readdb_mobavail) (char *str[], int columns, int current);
int (*read_randommonster) (void);
diff --git a/src/map/npc.c b/src/map/npc.c
index ae3cf56d0..23b0b9555 100644
--- a/src/map/npc.c
+++ b/src/map/npc.c
@@ -1769,7 +1769,7 @@ int npc_cashshop_buy(struct map_session_data *sd, int nameid, int amount, int po
if( w + sd->weight > sd->max_weight )
return ERROR_TYPE_INVENTORY_WEIGHT;
- if( (double)shop[i].value * amount > INT_MAX ) {
+ if ((int64)shop[i].value * amount > INT_MAX) {
ShowWarning("npc_cashshop_buy: Item '%s' (%d) price overflow attempt!\n", item->name, nameid);
ShowDebug("(NPC:'%s' (%s,%d,%d), player:'%s' (%d/%d), value:%d, amount:%d)\n",
nd->exname, map->list[nd->bl.m].name, nd->bl.x, nd->bl.y,
@@ -1812,7 +1812,7 @@ int npc_cashshop_buy(struct map_session_data *sd, int nameid, int amount, int po
int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list) {
struct npc_data* nd;
struct npc_item_list *shop = NULL;
- double z;
+ int64 z;
int i,j,w,skill_t,new_, idx = skill->get_index(MC_DISCOUNT);
unsigned short shop_size = 0;
@@ -1883,21 +1883,21 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list) {
value = pc->modifybuyvalue(sd,value);
- z += (double)value * amount;
+ z += (int64)value * amount;
w += itemdb_weight(nameid) * amount;
}
if( nd->master_nd != NULL ) //Script-based shops.
return npc->buylist_sub(sd,n,item_list,nd->master_nd);
- if( z > (double)sd->status.zeny )
+ if (z > sd->status.zeny)
return 1; // Not enough Zeny
if( w + sd->weight > sd->max_weight )
return 2; // Too heavy
if( pc->inventoryblank(sd) < new_ )
return 3; // Not enough space to store items
- pc->payzeny(sd,(int)z,LOG_TYPE_NPC, NULL);
+ pc->payzeny(sd, (int)z, LOG_TYPE_NPC, NULL);
for( i = 0; i < n; ++i ) {
int nameid = item_list[i*2+1];
@@ -1921,10 +1921,10 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list) {
skill_t = sd->status.skill[idx].flag - SKILL_FLAG_REPLACED_LV_0;
if( skill_t > 0 ) {
- z = z * (double)skill_t * (double)battle_config.shop_exp/10000.;
- if( z < 1 )
+ z = apply_percentrate64(z, skill_t * battle_config.shop_exp, 10000);
+ if (z < 1)
z = 1;
- pc->gainexp(sd,NULL,0,(int)z, false);
+ pc->gainexp(sd, NULL, 0, (int)z, false);
}
}
@@ -1937,7 +1937,7 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list) {
int npc_market_buylist(struct map_session_data* sd, unsigned short list_size, struct packet_npc_market_purchase *p) {
struct npc_data* nd;
struct npc_item_list *shop = NULL;
- double z;
+ int64 z;
int i,j,w,new_;
unsigned short shop_size = 0;
@@ -1997,11 +1997,11 @@ int npc_market_buylist(struct map_session_data* sd, unsigned short list_size, st
return 1;
}
- z += (double)value * amount;
+ z += (int64)value * amount;
w += itemdb_weight(nameid) * amount;
}
- if( z > (double)sd->status.zeny ) /* TODO find official response for this */
+ if (z > sd->status.zeny) /* TODO find official response for this */
return 1; // Not enough Zeny
if( w + sd->weight > sd->max_weight ) /* TODO find official response for this */
@@ -2098,7 +2098,7 @@ int npc_selllist_sub(struct map_session_data* sd, int n, unsigned short* item_li
/// @param item_list 'n' pairs <index,amount>
/// @return result code for clif->parse_NpcSellListSend
int npc_selllist(struct map_session_data* sd, int n, unsigned short* item_list) {
- double z;
+ int64 z;
int i,skill_t, skill_idx = skill->get_index(MC_OVERCHARGE);
struct npc_data *nd;
bool duplicates[MAX_INVENTORY] = { 0 };
@@ -2147,7 +2147,7 @@ int npc_selllist(struct map_session_data* sd, int n, unsigned short* item_list)
value = pc->modifysellvalue(sd, sd->inventory_data[idx]->value_sell);
- z += (double)value*amount;
+ z += (int64)value * amount;
}
if( nd->master_nd ) { // Script-controlled shops
@@ -2181,8 +2181,8 @@ int npc_selllist(struct map_session_data* sd, int n, unsigned short* item_list)
skill_t = sd->status.skill[skill_idx].flag - SKILL_FLAG_REPLACED_LV_0;
if( skill_t > 0 ) {
- z = z * (double)skill_t * (double)battle_config.shop_exp/10000.;
- if( z < 1 )
+ z = apply_percentrate64(z, skill_t * battle_config.shop_exp, 10000);
+ if (z < 1)
z = 1;
pc->gainexp(sd, NULL, 0, (int)z, false);
}
diff --git a/src/map/pc.c b/src/map/pc.c
index 491584385..8d1df71a9 100644
--- a/src/map/pc.c
+++ b/src/map/pc.c
@@ -4156,34 +4156,39 @@ int pc_insert_card(struct map_session_data* sd, int idx_card, int idx_equip)
/*==========================================
* Update buying value by skills
*------------------------------------------*/
-int pc_modifybuyvalue(struct map_session_data *sd,int orig_value) {
- int skill_lv,val = orig_value,rate1 = 0,rate2 = 0;
- if((skill_lv=pc->checkskill(sd,MC_DISCOUNT))>0) // merchant discount
+int pc_modifybuyvalue(struct map_session_data *sd, int orig_value)
+{
+ int skill_lv, rate1 = 0, rate2 = 0;
+ if (orig_value <= 0)
+ return 0;
+ if ((skill_lv=pc->checkskill(sd,MC_DISCOUNT)) > 0) // merchant discount
rate1 = 5+skill_lv*2-((skill_lv==10)? 1:0);
- if((skill_lv=pc->checkskill(sd,RG_COMPULSION))>0) // rogue discount
+ if ((skill_lv=pc->checkskill(sd,RG_COMPULSION)) > 0) // rogue discount
rate2 = 5+skill_lv*4;
- if(rate1 < rate2) rate1 = rate2;
- if(rate1)
- val = (int)((double)orig_value*(double)(100-rate1)/100.);
- if(val < 0) val = 0;
- if(orig_value > 0 && val < 1) val = 1;
-
- return val;
+ if (rate1 < rate2)
+ rate1 = rate2;
+ if (rate1 != 0)
+ orig_value = apply_percentrate(orig_value, 100-rate1, 100);
+ if (orig_value < 1)
+ orig_value = 1;
+ return orig_value;
}
/*==========================================
* Update selling value by skills
*------------------------------------------*/
-int pc_modifysellvalue(struct map_session_data *sd,int orig_value) {
- int skill_lv,val = orig_value,rate = 0;
- if((skill_lv=pc->checkskill(sd,MC_OVERCHARGE))>0) //OverCharge
+int pc_modifysellvalue(struct map_session_data *sd, int orig_value)
+{
+ int skill_lv, rate = 0;
+ if (orig_value <= 0)
+ return 0;
+ if ((skill_lv=pc->checkskill(sd,MC_OVERCHARGE)) > 0) //OverCharge
rate = 5+skill_lv*2-((skill_lv==10)? 1:0);
- if(rate)
- val = (int)((double)orig_value*(double)(100+rate)/100.);
- if(val < 0) val = 0;
- if(orig_value > 0 && val < 1) val = 1;
-
- return val;
+ if (rate != 0)
+ orig_value = apply_percentrate(orig_value, 100+rate, 100);
+ if (orig_value < 1)
+ orig_value = 1;
+ return orig_value;
}
/*==========================================
@@ -5259,7 +5264,7 @@ int pc_show_steal(struct block_list *bl,va_list ap)
int pc_steal_item(struct map_session_data *sd,struct block_list *bl, uint16 skill_lv)
{
int i,itemid,flag;
- double rate;
+ int rate;
struct status_data *sd_status, *md_status;
struct mob_data *md = BL_CAST(BL_MOB, bl);
struct item tmp_item;
@@ -5284,18 +5289,22 @@ int pc_steal_item(struct map_session_data *sd,struct block_list *bl, uint16 skil
}
// base skill success chance (percentual)
- rate = (sd_status->dex - md_status->dex)/2 + skill_lv*6 + 4;
- rate += sd->bonus.add_steal_rate;
+ rate = (sd_status->dex - md_status->dex)/2 + skill_lv*6 + 4 + sd->bonus.add_steal_rate;
if( rate < 1 )
return 0;
// Try dropping one item, in the order from first to last possible slot.
// Droprate is affected by the skill success rate.
- for( i = 0; i < MAX_STEAL_DROP; i++ )
- if (md->db->dropitem[i].nameid > 0 && (data = itemdb->exists(md->db->dropitem[i].nameid)) != NULL && rnd() % 10000 < md->db->dropitem[i].p * rate/100.)
+ for (i = 0; i < MAX_STEAL_DROP; i++) {
+ if (md->db->dropitem[i].nameid == 0)
+ continue;
+ if ((data = itemdb->exists(md->db->dropitem[i].nameid)) == NULL)
+ continue;
+ if (rnd() % 10000 < apply_percentrate(md->db->dropitem[i].p, rate, 100))
break;
- if( i == MAX_STEAL_DROP )
+ }
+ if (i == MAX_STEAL_DROP)
return 0;
itemid = md->db->dropitem[i].nameid;
@@ -6612,16 +6621,16 @@ void pc_calcexp(struct map_session_data *sd, unsigned int *base_exp, unsigned in
if (sd->sc.data[SC_OVERLAPEXPUP])
bonus += sd->sc.data[SC_OVERLAPEXPUP]->val1;
- *base_exp = (unsigned int) cap_value(*base_exp + (double)*base_exp * bonus/100., 1, UINT_MAX);
+ *base_exp = (unsigned int) cap_value(*base_exp + apply_percentrate64(*base_exp, bonus, 100), 1, UINT_MAX);
if (sd->sc.data[SC_CASH_PLUSONLYJOBEXP])
bonus += sd->sc.data[SC_CASH_PLUSONLYJOBEXP]->val1;
- *job_exp = (unsigned int) cap_value(*job_exp + (double)*job_exp * bonus/100., 1, UINT_MAX);
+ *job_exp = (unsigned int) cap_value(*job_exp + apply_percentrate64(*job_exp, bonus, 100), 1, UINT_MAX);
- if( sd->status.mod_exp != 100 ) {
- *base_exp = (unsigned int) cap_value((double)*base_exp * sd->status.mod_exp/100., 1, UINT_MAX);
- *job_exp = (unsigned int) cap_value((double)*job_exp * sd->status.mod_exp/100., 1, UINT_MAX);
+ if (sd->status.mod_exp != 100) {
+ *base_exp = (unsigned int) cap_value(apply_percentrate64(*base_exp, sd->status.mod_exp, 100), 1, UINT_MAX);
+ *job_exp = (unsigned int) cap_value(apply_percentrate64(*job_exp, sd->status.mod_exp, 100), 1, UINT_MAX);
}
}
@@ -7713,18 +7722,18 @@ int pc_dead(struct map_session_data *sd,struct block_list *src) {
&& !map->list[sd->bl.m].flag.noexppenalty && !map_flag_gvg2(sd->bl.m)
&& !sd->sc.data[SC_BABY] && !sd->sc.data[SC_CASH_DEATHPENALTY]
) {
- unsigned int base_penalty = 0;
if (battle_config.death_penalty_base > 0) {
+ unsigned int base_penalty = 0;
switch (battle_config.death_penalty_type) {
case 1:
- base_penalty = (unsigned int) ((double)pc->nextbaseexp(sd) * (double)battle_config.death_penalty_base/10000);
+ base_penalty = (unsigned int) apply_percentrate64(pc->nextbaseexp(sd), battle_config.death_penalty_base, 10000);
break;
case 2:
- base_penalty = (unsigned int) ((double)sd->status.base_exp * (double)battle_config.death_penalty_base/10000);
+ base_penalty = (unsigned int) apply_percentrate64(sd->status.base_exp, battle_config.death_penalty_base, 10000);
break;
}
- if(base_penalty) {
+ if (base_penalty != 0) {
if (battle_config.pk_mode && src && src->type==BL_PC)
base_penalty*=2;
if( sd->status.mod_death != 100 )
@@ -7735,31 +7744,31 @@ int pc_dead(struct map_session_data *sd,struct block_list *src) {
}
if(battle_config.death_penalty_job > 0) {
- base_penalty = 0;
+ unsigned int job_penalty = 0;
switch (battle_config.death_penalty_type) {
case 1:
- base_penalty = (unsigned int) ((double)pc->nextjobexp(sd) * (double)battle_config.death_penalty_job/10000);
+ job_penalty = (unsigned int) apply_percentrate64(pc->nextjobexp(sd), battle_config.death_penalty_job, 10000);
break;
case 2:
- base_penalty = (unsigned int) ((double)sd->status.job_exp * (double)battle_config.death_penalty_job/10000);
+ job_penalty = (unsigned int) apply_percentrate64(sd->status.job_exp, battle_config.death_penalty_job, 10000);
break;
}
- if(base_penalty) {
+ if (job_penalty != 0) {
if (battle_config.pk_mode && src && src->type==BL_PC)
- base_penalty*=2;
+ job_penalty*=2;
if( sd->status.mod_death != 100 )
- base_penalty = base_penalty * sd->status.mod_death / 100;
- sd->status.job_exp -= min(sd->status.job_exp, base_penalty);
+ job_penalty = job_penalty * sd->status.mod_death / 100;
+ sd->status.job_exp -= min(sd->status.job_exp, job_penalty);
clif->updatestatus(sd,SP_JOBEXP);
}
}
- if(battle_config.zeny_penalty > 0 && !map->list[sd->bl.m].flag.nozenypenalty) {
- base_penalty = (unsigned int)((double)sd->status.zeny * (double)battle_config.zeny_penalty / 10000.);
- if(base_penalty)
- pc->payzeny(sd, base_penalty, LOG_TYPE_PICKDROP_PLAYER, NULL);
+ if (battle_config.zeny_penalty > 0 && !map->list[sd->bl.m].flag.nozenypenalty) {
+ int zeny_penalty = apply_percentrate(sd->status.zeny, battle_config.zeny_penalty, 10000);
+ if (zeny_penalty != 0)
+ pc->payzeny(sd, zeny_penalty, LOG_TYPE_PICKDROP_PLAYER, NULL);
}
}
diff --git a/src/map/script.c b/src/map/script.c
index 64bdba592..f3c839555 100644
--- a/src/map/script.c
+++ b/src/map/script.c
@@ -3845,7 +3845,7 @@ void op_2str(struct script_state* st, int op, const char* s1, const char* s2)
void op_2num(struct script_state* st, int op, int i1, int i2)
{
int ret;
- double ret_double;
+ int64 ret64;
switch( op ) {
case C_AND: ret = i1 & i2; break;
@@ -3877,25 +3877,21 @@ void op_2num(struct script_state* st, int op, int i1, int i2)
ret = i1 % i2;
break;
default:
- switch( op )
- {// operators that can overflow/underflow
- case C_ADD: ret = i1 + i2; ret_double = (double)i1 + (double)i2; break;
- case C_SUB: ret = i1 - i2; ret_double = (double)i1 - (double)i2; break;
- case C_MUL: ret = i1 * i2; ret_double = (double)i1 * (double)i2; break;
+ switch (op) { // operators that can overflow/underflow
+ case C_ADD: ret = i1 + i2; ret64 = (int64)i1 + i2; break;
+ case C_SUB: ret = i1 - i2; ret64 = (int64)i1 - i2; break;
+ case C_MUL: ret = i1 * i2; ret64 = (int64)i1 * i2; break;
default:
ShowError("script:op_2num: unexpected number operator %s i1=%d i2=%d\n", script->op2name(op), i1, i2);
script->reportsrc(st);
script_pushnil(st);
return;
}
- if( ret_double < (double)INT_MIN )
- {
+ if (ret64 < INT_MIN) {
ShowWarning("script:op_2num: underflow detected op=%s i1=%d i2=%d\n", script->op2name(op), i1, i2);
script->reportsrc(st);
ret = INT_MIN;
- }
- else if( ret_double > (double)INT_MAX )
- {
+ } else if (ret64 > INT_MAX) {
ShowWarning("script:op_2num: overflow detected op=%s i1=%d i2=%d\n", script->op2name(op), i1, i2);
script->reportsrc(st);
ret = INT_MAX;
@@ -9702,20 +9698,18 @@ BUILDIN(makepet)
BUILDIN(getexp)
{
int base=0,job=0;
- double bonus;
struct map_session_data *sd = script->rid2sd(st);
if (sd == NULL)
return true;
- base=script_getnum(st,2);
- job =script_getnum(st,3);
- if(base<0 || job<0)
+ base = script_getnum(st,2);
+ job = script_getnum(st,3);
+ if (base < 0 || job < 0)
return true;
// bonus for npc-given exp
- bonus = battle_config.quest_exp_rate / 100.;
- base = (int) cap_value(base * bonus, 0, INT_MAX);
- job = (int) cap_value(job * bonus, 0, INT_MAX);
+ base = cap_value(apply_percentrate(base, battle_config.quest_exp_rate, 100), 0, INT_MAX);
+ job = cap_value(apply_percentrate(job, battle_config.quest_exp_rate, 100), 0, INT_MAX);
pc->gainexp(sd, &sd->bl, base, job, true);
diff --git a/src/map/vending.c b/src/map/vending.c
index 810e6b07a..6e74e6c3e 100644
--- a/src/map/vending.c
+++ b/src/map/vending.c
@@ -89,7 +89,7 @@ void vending_vendinglistreq(struct map_session_data* sd, unsigned int id) {
*------------------------------------------*/
void vending_purchasereq(struct map_session_data* sd, int aid, unsigned int uid, const uint8* data, int count) {
int i, j, cursor, w, new_ = 0, blank, vend_list[MAX_VENDING];
- double z;
+ int64 z;
struct s_vending vend[MAX_VENDING]; // against duplicate packets
struct map_session_data* vsd = map->id2sd(aid);
@@ -116,7 +116,7 @@ void vending_purchasereq(struct map_session_data* sd, int aid, unsigned int uid,
memcpy(&vend, &vsd->vending, sizeof(vsd->vending)); // copy vending list
// some checks
- z = 0.; // zeny counter
+ z = 0; // zeny counter
w = 0; // weight counter
for( i = 0; i < count; i++ ) {
short amount = *(uint16*)(data + 4*i + 0);
@@ -136,12 +136,12 @@ void vending_purchasereq(struct map_session_data* sd, int aid, unsigned int uid,
else
vend_list[i] = j;
- z += ((double)vsd->vending[j].value * (double)amount);
- if( z > (double)sd->status.zeny || z < 0. || z > (double)MAX_ZENY ) {
+ z += (int64)vsd->vending[j].value * amount;
+ if (z > sd->status.zeny || z < 0 || z > MAX_ZENY) {
clif->buyvending(sd, idx, amount, 1); // you don't have enough zeny
return;
}
- if( z + (double)vsd->status.zeny > (double)MAX_ZENY && !battle_config.vending_over_max ) {
+ if (z > MAX_ZENY - vsd->status.zeny && !battle_config.vending_over_max) {
clif->buyvending(sd, idx, vsd->vending[j].amount, 4); // too much zeny = overflow
return;
@@ -181,7 +181,7 @@ void vending_purchasereq(struct map_session_data* sd, int aid, unsigned int uid,
pc->payzeny(sd, (int)z, LOG_TYPE_VENDING, vsd);
if( battle_config.vending_tax )
- z -= z * (battle_config.vending_tax/10000.);
+ z -= apply_percentrate64(z, battle_config.vending_tax, 10000);
pc->getzeny(vsd, (int)z, LOG_TYPE_VENDING, sd);
for( i = 0; i < count; i++ ) {
diff --git a/src/plugins/db2sql.c b/src/plugins/db2sql.c
index 5b5d79bac..44e2bac3c 100644
--- a/src/plugins/db2sql.c
+++ b/src/plugins/db2sql.c
@@ -24,12 +24,12 @@
#include "common/conf.h"
#include "common/memmgr.h"
#include "common/mmo.h"
+#include "common/nullpo.h"
#include "common/strlib.h"
-#include "common/timer.h"
-#include "map/clif.h"
+#include "map/battle.h"
#include "map/itemdb.h"
+#include "map/mob.h"
#include "map/map.h"
-#include "map/pc.h"
#include "common/HPMDataCheck.h"
@@ -43,38 +43,102 @@ HPExport struct hplugin_info pinfo = {
HPM_VERSION, // HPM Version (don't change, macro is automatically updated)
};
+#ifdef RENEWAL
+#define DBSUFFIX "_re"
+#else // not RENEWAL
+#define DBSUFFIX ""
+#endif
+
+/// Conversion state tracking.
struct {
- FILE *fp;
+ FILE *fp; ///< Currently open file pointer
struct {
- char *p;
- size_t len;
- } buf[4];
- char *db_name;
+ char *p; ///< Buffer pointer
+ size_t len; ///< Buffer length
+ } buf[4]; ///< Output buffer
+ const char *db_name; ///< Database table name
} tosql;
-bool torun = false;
+/// Whether the item_db converter will automatically run.
+bool itemdb2sql_torun = false;
+/// Whether the mob_db converter will automatically run.
+bool mobdb2sql_torun = false;
+
+/// Backup of the original item_db parser function pointer.
int (*itemdb_readdb_libconfig_sub) (config_setting_t *it, int n, const char *source);
+/// Backup of the original mob_db parser function pointer.
+int (*mob_read_db_sub) (config_setting_t *it, int n, const char *source);
-void hstr(const char *str) {
- if( strlen(str) > tosql.buf[3].len ) {
+/**
+ * Normalizes and appends a string to the output buffer.
+ *
+ * @param str The string to append.
+ */
+void hstr(const char *str)
+{
+ if (strlen(str) > tosql.buf[3].len) {
tosql.buf[3].len = tosql.buf[3].len + strlen(str) + 1000;
RECREATE(tosql.buf[3].p,char,tosql.buf[3].len);
}
safestrncpy(tosql.buf[3].p,str,strlen(str));
normalize_name(tosql.buf[3].p,"\t\n ");
}
-int db2sql(config_setting_t *entry, int n, const char *source) {
+
+/**
+ * Prints a SQL file header for the current item_db file.
+ */
+void db2sql_fileheader(void)
+{
+ time_t t = time(NULL);
+ struct tm *lt = localtime(&t);
+ int year = lt->tm_year+1900;
+
+ fprintf(tosql.fp,
+ "-- This file is part of Hercules.\n"
+ "-- http://herc.ws - http://github.com/HerculesWS/Hercules\n"
+ "--\n"
+ "-- Copyright (C) 2013-%d Hercules Dev Team\n"
+ "--\n"
+ "-- Hercules is free software: you can redistribute it and/or modify\n"
+ "-- it under the terms of the GNU General Public License as published by\n"
+ "-- the Free Software Foundation, either version 3 of the License, or\n"
+ "-- (at your option) any later version.\n"
+ "--\n"
+ "-- This program is distributed in the hope that it will be useful,\n"
+ "-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+ "-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
+ "-- GNU General Public License for more details.\n"
+ "--\n"
+ "-- You should have received a copy of the GNU General Public License\n"
+ "-- along with this program. If not, see <http://www.gnu.org/licenses/>.\n\n"
+
+ "-- NOTE: This file was auto-generated and should never be manually edited,\n"
+ "-- as it will get overwritten. If you need to modify this file,\n"
+ "-- please consider modifying the corresponding .conf file inside\n"
+ "-- the db folder, and then re-run the db2sql plugin.\n"
+ "\n", year);
+}
+
+/**
+ * Converts an Item DB entry to SQL.
+ *
+ * @see itemdb_readdb_libconfig_sub.
+ */
+int itemdb2sql_sub(config_setting_t *entry, int n, const char *source)
+{
struct item_data *it = NULL;
- if( (it = itemdb->exists(itemdb_readdb_libconfig_sub(entry,n,source))) ) {
+ if ((it = itemdb->exists(itemdb_readdb_libconfig_sub(entry,n,source)))) {
char e_name[ITEM_NAME_LENGTH*2+1];
const char *bonus = NULL;
char *str;
int i32;
- unsigned int ui32;
+ uint32 ui32;
config_setting_t *t = NULL;
StringBuf buf;
+ nullpo_ret(entry);
+
StrBuf->Init(&buf);
// id
@@ -92,10 +156,10 @@ int db2sql(config_setting_t *entry, int n, const char *source) {
StrBuf->Printf(&buf, "'%u',", it->flag.delay_consume?IT_DELAYCONSUME:it->type);
// price_buy
- StrBuf->Printf(&buf, "'%u',", it->value_buy);
+ StrBuf->Printf(&buf, "'%d',", it->value_buy);
// price_sell
- StrBuf->Printf(&buf, "'%u',", it->value_sell);
+ StrBuf->Printf(&buf, "'%d',", it->value_sell);
// weight
StrBuf->Printf(&buf, "'%u',", it->weight);
@@ -116,15 +180,15 @@ int db2sql(config_setting_t *entry, int n, const char *source) {
StrBuf->Printf(&buf, "'%u',", it->slot);
// equip_jobs
- if( libconfig->setting_lookup_int(entry, "Job", &i32) ) // This is an unsigned value, do not check for >= 0
- ui32 = (unsigned int)i32;
+ if (libconfig->setting_lookup_int(entry, "Job", &i32)) // This is an unsigned value, do not check for >= 0
+ ui32 = (uint32)i32;
else
ui32 = UINT_MAX;
StrBuf->Printf(&buf, "'%u',", ui32);
// equip_upper
- if( libconfig->setting_lookup_int(entry, "Upper", &i32) && i32 >= 0 )
- ui32 = (unsigned int)i32;
+ if (libconfig->setting_lookup_int(entry, "Upper", &i32) && i32 >= 0)
+ ui32 = (uint32)i32;
else
ui32 = ITEMUPPER_ALL;
StrBuf->Printf(&buf, "'%u',", ui32);
@@ -142,7 +206,7 @@ int db2sql(config_setting_t *entry, int n, const char *source) {
StrBuf->Printf(&buf, "'%u',", it->elv);
// equip_level_max
- if( (t = libconfig->setting_get_member(entry, "EquipLv")) && config_setting_is_aggregate(t) && libconfig->setting_length(t) >= 2 )
+ if ((t = libconfig->setting_get_member(entry, "EquipLv")) && config_setting_is_aggregate(t) && libconfig->setting_length(t) >= 2)
StrBuf->Printf(&buf, "'%u',", it->elvmax);
else
StrBuf->AppendStr(&buf, "NULL,");
@@ -253,36 +317,15 @@ int db2sql(config_setting_t *entry, int n, const char *source) {
return it?it->nameid:0;
}
-void totable(void) {
- time_t t;
- struct tm *lt;
- t = time(NULL);
- lt = localtime(&t);
- int year = lt->tm_year+1900;
- fprintf(tosql.fp,
- "-- This file is part of Hercules.\n"
- "-- http://herc.ws - http://github.com/HerculesWS/Hercules\n"
- "--\n"
- "-- Copyright (C) 2013-%d Hercules Dev Team\n"
- "--\n"
- "-- Hercules is free software: you can redistribute it and/or modify\n"
- "-- it under the terms of the GNU General Public License as published by\n"
- "-- the Free Software Foundation, either version 3 of the License, or\n"
- "-- (at your option) any later version.\n"
- "--\n"
- "-- This program is distributed in the hope that it will be useful,\n"
- "-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
- "-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
- "-- GNU General Public License for more details.\n"
- "--\n"
- "-- You should have received a copy of the GNU General Public License\n"
- "-- along with this program. If not, see <http://www.gnu.org/licenses/>.\n\n"
- "-- NOTE: This file was auto-generated and should never be manually edited,\n"
- "-- as it will get overwritten. If you need to modify this file,\n"
- "-- please consider modifying the corresponding .conf file inside\n"
- "-- the db folder, and then re-run the db2sql plugin.\n"
- "\n"
+/**
+ * Prints a SQL table header for the current item_db table.
+ */
+void itemdb2sql_tableheader(void)
+{
+ db2sql_fileheader();
+
+ fprintf(tosql.fp,
"--\n"
"-- Table structure for table `%s`\n"
"--\n"
@@ -326,79 +369,405 @@ void totable(void) {
" `unequip_script` text,\n"
" PRIMARY KEY (`id`)\n"
") ENGINE=MyISAM;\n"
- "\n", year, tosql.db_name,tosql.db_name,tosql.db_name);
+ "\n", tosql.db_name,tosql.db_name,tosql.db_name);
}
-void do_db2sql(void) {
+
+/**
+ * Item DB Conversion.
+ *
+ * Converts Item DB and Item DB2 to SQL scripts.
+ */
+void do_itemdb2sql(void)
+{
+ int i;
+ struct convert_db_files {
+ const char *name;
+ const char *source;
+ const char *destination;
+ } files[] = {
+ {"item_db", DBPATH"item_db.conf", "sql-files/item_db" DBSUFFIX ".sql"},
+ {"item_db2", "item_db2.conf", "sql-files/item_db2.sql"},
+ };
+
/* link */
itemdb_readdb_libconfig_sub = itemdb->readdb_libconfig_sub;
- itemdb->readdb_libconfig_sub = db2sql;
- /* */
+ itemdb->readdb_libconfig_sub = itemdb2sql_sub;
+
+ memset(&tosql.buf, 0, sizeof(tosql.buf));
+ itemdb->clear(false);
+
+ for (i = 0; i < ARRAYLENGTH(files); i++) {
+ if ((tosql.fp = fopen(files[i].destination, "wt+")) == NULL) {
+ ShowError("itemdb_tosql: File not found \"%s\".\n", files[i].destination);
+ return;
+ }
+
+ tosql.db_name = files[i].name;
+ itemdb2sql_tableheader();
+
+ itemdb->readdb_libconfig(files[i].source);
- if ((tosql.fp = fopen("sql-files/item_db_re.sql", "wt+")) == NULL) {
- ShowError("itemdb_tosql: File not found \"%s\".\n", "sql-files/item_db_re.sql");
- return;
+ fclose(tosql.fp);
}
- tosql.db_name = "item_db";
- totable();
+ /* unlink */
+ itemdb->readdb_libconfig_sub = itemdb_readdb_libconfig_sub;
- memset(&tosql.buf, 0, sizeof(tosql.buf) );
+ for (i = 0; i < ARRAYLENGTH(tosql.buf); i++) {
+ if (tosql.buf[i].p)
+ aFree(tosql.buf[i].p);
+ }
+}
- itemdb->clear(false);
- itemdb->readdb_libconfig("re/item_db.conf");
+/**
+ * Converts a Mob DB entry to SQL.
+ *
+ * @see mobdb_readdb_libconfig_sub.
+ */
+int mobdb2sql_sub(config_setting_t *mobt, int n, const char *source)
+{
+ struct mob_db *md = NULL;
+ nullpo_ret(mobt);
+
+ if ((md = mob->db(mob_read_db_sub(mobt, n, source))) != mob->dummy) {
+ char e_name[NAME_LENGTH*2+1];
+ StringBuf buf;
+ int card_idx = 9, i;
+
+ StrBuf->Init(&buf);
+
+ // id
+ StrBuf->Printf(&buf, "%u,", md->mob_id);
- fclose(tosql.fp);
+ // Sprite
+ SQL->EscapeString(NULL, e_name, md->sprite);
+ StrBuf->Printf(&buf, "'%s',", e_name);
+
+ // kName
+ SQL->EscapeString(NULL, e_name, md->name);
+ StrBuf->Printf(&buf, "'%s',", e_name);
- if ((tosql.fp = fopen("sql-files/item_db.sql", "wt+")) == NULL) {
- ShowError("itemdb_tosql: File not found \"%s\".\n", "sql-files/item_db.sql");
- return;
+ // iName
+ SQL->EscapeString(NULL, e_name, md->jname);
+ StrBuf->Printf(&buf, "'%s',", e_name);
+
+ // LV
+ StrBuf->Printf(&buf, "%u,", md->lv);
+
+ // HP
+ StrBuf->Printf(&buf, "%u,", md->status.max_hp);
+
+ // SP
+ StrBuf->Printf(&buf, "%u,", md->status.max_sp);
+
+ // EXP
+ StrBuf->Printf(&buf, "%u,", md->base_exp);
+
+ // JEXP
+ StrBuf->Printf(&buf, "%u,", md->job_exp);
+
+ // Range1
+ StrBuf->Printf(&buf, "%u,", md->status.rhw.range);
+
+ // ATK1
+ StrBuf->Printf(&buf, "%u,", md->status.rhw.atk);
+
+ // ATK2
+ StrBuf->Printf(&buf, "%u,", md->status.rhw.atk2);
+
+ // DEF
+ StrBuf->Printf(&buf, "%u,", md->status.def);
+
+ // MDEF
+ StrBuf->Printf(&buf, "%u,", md->status.mdef);
+
+ // STR
+ StrBuf->Printf(&buf, "%u,", md->status.str);
+
+ // AGI
+ StrBuf->Printf(&buf, "%u,", md->status.agi);
+
+ // VIT
+ StrBuf->Printf(&buf, "%u,", md->status.vit);
+
+ // INT
+ StrBuf->Printf(&buf, "%u,", md->status.int_);
+
+ // DEX
+ StrBuf->Printf(&buf, "%u,", md->status.dex);
+
+ // LUK
+ StrBuf->Printf(&buf, "%u,", md->status.luk);
+
+ // Range2
+ StrBuf->Printf(&buf, "%u,", md->range2);
+
+ // Range3
+ StrBuf->Printf(&buf, "%u,", md->range3);
+
+ // Scale
+ StrBuf->Printf(&buf, "%u,", md->status.size);
+
+ // Race
+ StrBuf->Printf(&buf, "%u,", md->status.race);
+
+ // Element
+ StrBuf->Printf(&buf, "%u,", md->status.def_ele + 20 * md->status.ele_lv);
+
+ // Mode
+ StrBuf->Printf(&buf, "0x%X,", md->status.mode);
+
+ // Speed
+ StrBuf->Printf(&buf, "%u,", md->status.speed);
+
+ // aDelay
+ StrBuf->Printf(&buf, "%u,", md->status.adelay);
+
+ // aMotion
+ StrBuf->Printf(&buf, "%u,", md->status.amotion);
+
+ // dMotion
+ StrBuf->Printf(&buf, "%u,", md->status.dmotion);
+
+ // MEXP
+ StrBuf->Printf(&buf, "%u,", md->mexp);
+
+ for (i = 0; i < 3; i++) {
+ // MVP{i}id
+ StrBuf->Printf(&buf, "%u,", md->mvpitem[i].nameid);
+ // MVP{i}per
+ StrBuf->Printf(&buf, "%u,", md->mvpitem[i].p);
+ }
+
+ // Scan for cards
+ for (i = 0; i < 10; i++) {
+ struct item_data *it = NULL;
+ if (md->dropitem[i].nameid != 0 && (it = itemdb->exists(md->dropitem[i].nameid)) != NULL && it->type == IT_CARD)
+ card_idx = i;
+ }
+
+ for (i = 0; i < 10; i++) {
+ if (card_idx == i)
+ continue;
+ // Drop{i}id
+ StrBuf->Printf(&buf, "%u,", md->dropitem[i].nameid);
+ // Drop{i}per
+ StrBuf->Printf(&buf, "%u,", md->dropitem[i].p);
+ }
+
+ // DropCardid
+ StrBuf->Printf(&buf, "%u,", md->dropitem[card_idx].nameid);
+ // DropCardper
+ StrBuf->Printf(&buf, "%u", md->dropitem[card_idx].p);
+
+ fprintf(tosql.fp, "REPLACE INTO `%s` VALUES (%s);\n", tosql.db_name, StrBuf->Value(&buf));
+
+ StrBuf->Destroy(&buf);
}
- tosql.db_name = "item_db";
- totable();
+ return md ? md->mob_id : 0;
+}
- itemdb->clear(false);
- itemdb->readdb_libconfig("pre-re/item_db.conf");
+/**
+ * Prints a SQL table header for the current mob_db table.
+ */
+void mobdb2sql_tableheader(void)
+{
+ db2sql_fileheader();
+
+ fprintf(tosql.fp,
+ "--\n"
+ "-- Table structure for table `%s`\n"
+ "--\n"
+ "\n"
+ "DROP TABLE IF EXISTS `%s`;\n"
+ "CREATE TABLE `%s` (\n"
+ " `ID` MEDIUMINT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `Sprite` TEXT NOT NULL,\n"
+ " `kName` TEXT NOT NULL,\n"
+ " `iName` TEXT NOT NULL,\n"
+ " `LV` TINYINT(6) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `HP` INT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `SP` MEDIUMINT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `EXP` MEDIUMINT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `JEXP` MEDIUMINT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `Range1` TINYINT(4) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `ATK1` SMALLINT(6) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `ATK2` SMALLINT(6) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `DEF` SMALLINT(6) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `MDEF` SMALLINT(6) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `STR` SMALLINT(6) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `AGI` SMALLINT(6) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `VIT` SMALLINT(6) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `INT` SMALLINT(6) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `DEX` SMALLINT(6) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `LUK` SMALLINT(6) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `Range2` TINYINT(4) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `Range3` TINYINT(4) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `Scale` TINYINT(4) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `Race` TINYINT(4) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `Element` TINYINT(4) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `Mode` SMALLINT(6) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `Speed` SMALLINT(6) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `aDelay` SMALLINT(6) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `aMotion` SMALLINT(6) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `dMotion` SMALLINT(6) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `MEXP` MEDIUMINT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `MVP1id` SMALLINT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `MVP1per` SMALLINT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `MVP2id` SMALLINT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `MVP2per` SMALLINT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `MVP3id` SMALLINT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `MVP3per` SMALLINT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `Drop1id` SMALLINT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `Drop1per` SMALLINT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `Drop2id` SMALLINT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `Drop2per` SMALLINT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `Drop3id` SMALLINT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `Drop3per` SMALLINT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `Drop4id` SMALLINT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `Drop4per` SMALLINT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `Drop5id` SMALLINT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `Drop5per` SMALLINT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `Drop6id` SMALLINT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `Drop6per` SMALLINT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `Drop7id` SMALLINT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `Drop7per` SMALLINT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `Drop8id` SMALLINT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `Drop8per` SMALLINT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `Drop9id` SMALLINT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `Drop9per` SMALLINT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `DropCardid` SMALLINT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " `DropCardper` SMALLINT(9) UNSIGNED NOT NULL DEFAULT '0',\n"
+ " PRIMARY KEY (`ID`)\n"
+ ") ENGINE=MyISAM;\n"
+ "\n", tosql.db_name, tosql.db_name, tosql.db_name);
+}
- fclose(tosql.fp);
+/**
+ * Mob DB Conversion.
+ *
+ * Converts Mob DB and Mob DB2 to SQL scripts.
+ */
+void do_mobdb2sql(void)
+{
+ int i;
+ struct convert_db_files {
+ const char *name;
+ const char *source;
+ const char *destination;
+ } files[] = {
+ {"mob_db", DBPATH"mob_db.conf", "sql-files/mob_db" DBSUFFIX ".sql"},
+ {"mob_db2", "mob_db2.conf", "sql-files/mob_db2.sql"},
+ };
+
+ /* link */
+ mob_read_db_sub = mob->read_db_sub;
+ mob->read_db_sub = mobdb2sql_sub;
- if ((tosql.fp = fopen("sql-files/item_db2.sql", "wt+")) == NULL) {
- ShowError("itemdb_tosql: File not found \"%s\".\n", "sql-files/item_db2.sql");
- return;
+ if (map->minimal) {
+ // Set up modifiers
+ battle->config_set_defaults();
}
- tosql.db_name = "item_db2";
- totable();
+ memset(&tosql.buf, 0, sizeof(tosql.buf));
+ for (i = 0; i < ARRAYLENGTH(files); i++) {
+ if ((tosql.fp = fopen(files[i].destination, "wt+")) == NULL) {
+ ShowError("mobdb_tosql: File not found \"%s\".\n", files[i].destination);
+ return;
+ }
- itemdb->clear(false);
- itemdb->readdb_libconfig("item_db2.conf");
+ tosql.db_name = files[i].name;
+ mobdb2sql_tableheader();
- fclose(tosql.fp);
+ mob->read_libconfig(files[i].source, false);
+
+ fclose(tosql.fp);
+ }
/* unlink */
- itemdb->readdb_libconfig_sub = itemdb_readdb_libconfig_sub;
+ mob->read_db_sub = mob_read_db_sub;
+
+ for (i = 0; i < ARRAYLENGTH(tosql.buf); i++) {
+ if (tosql.buf[i].p)
+ aFree(tosql.buf[i].p);
+ }
+}
+
+/**
+ * Console command db2sql.
+ */
+CPCMD(db2sql)
+{
+ do_itemdb2sql();
+ do_mobdb2sql();
+}
- if( tosql.buf[0].p ) aFree(tosql.buf[0].p);
- if( tosql.buf[1].p ) aFree(tosql.buf[1].p);
- if( tosql.buf[2].p ) aFree(tosql.buf[2].p);
- if( tosql.buf[3].p ) aFree(tosql.buf[3].p);
+/**
+ * Console command itemdb2sql.
+ */
+CPCMD(itemdb2sql)
+{
+ do_itemdb2sql();
}
-CPCMD(db2sql) {
- do_db2sql();
+
+/**
+ * Console command mobdb2sql.
+ */
+CPCMD(mobdb2sql)
+{
+ do_mobdb2sql();
}
+
+/**
+ * Command line argument handler for --db2sql
+ */
CMDLINEARG(db2sql)
{
- map->minimal = torun = true;
+ map->minimal = true;
+ itemdb2sql_torun = true;
+ mobdb2sql_torun = true;
return true;
}
-HPExport void server_preinit(void) {
- addArg("--db2sql",false,db2sql,NULL);
+/**
+ * Command line argument handler for --itemdb2sql
+ */
+CMDLINEARG(itemdb2sql)
+{
+ map->minimal = true;
+ itemdb2sql_torun = true;
+ return true;
}
-HPExport void plugin_init(void) {
- addCPCommand("server:tools:db2sql",db2sql);
+
+/**
+ * Command line argument handler for --mobdb2sql
+ */
+CMDLINEARG(mobdb2sql)
+{
+ map->minimal = true;
+ mobdb2sql_torun = true;
+ return true;
+}
+
+HPExport void server_preinit(void)
+{
+ addArg("--db2sql", false, db2sql, NULL);
+ addArg("--itemdb2sql", false, itemdb2sql, NULL);
+ addArg("--mobdb2sql", false, mobdb2sql, NULL);
+}
+
+HPExport void plugin_init(void)
+{
+ addCPCommand("server:tools:db2sql", db2sql);
+ addCPCommand("server:tools:itemdb2sql", itemdb2sql);
+ addCPCommand("server:tools:mobdb2sql", mobdb2sql);
}
-HPExport void server_online(void) {
- if( torun )
- do_db2sql();
+
+HPExport void server_online(void)
+{
+ if (itemdb2sql_torun)
+ do_itemdb2sql();
+ if (mobdb2sql_torun)
+ do_mobdb2sql();
}