From 50de6b49043968d01b2cc7565376d5e06c29b490 Mon Sep 17 00:00:00 2001 From: Haru Date: Mon, 7 Dec 2015 02:58:02 +0100 Subject: Modified Mob DB loader to behave like the Item DB loader - Separated load and validation logic. - The load/validation functions now return the loaded mob ID Signed-off-by: Haru --- src/map/itemdb.c | 36 ++-- src/map/mob.c | 631 +++++++++++++++++++++++++++++++++---------------------- src/map/mob.h | 14 +- 3 files changed, 410 insertions(+), 271 deletions(-) (limited to 'src/map') 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..820376508 100644 --- a/src/map/mob.c +++ b/src/map/mob.c @@ -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,448 @@ 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" + * // 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(entry->name, str, sizeof(entry->name)); - safestrncpy(entry->jname, str, sizeof(entry->jname)); + safestrncpy(md.name, str, sizeof(md.name)); + 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); + double exp = (double)(i32) * (double)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); + double exp = (double)(i32) * (double)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] + double exp = (double)i32 * (double)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 + // Now that we know if it is an mvp or not, apply battle_config modifiers [Skotlex] + double maxhp = (double)md.status.max_hp; + if (md.mexp > 0) { //Mvp if (battle_config.mvp_hp_rate != 100) maxhp = maxhp * (double)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.; } - mstatus->max_hp = (unsigned int)cap_value(maxhp, 1, UINT_MAX); - } - if (maxspUpdated) { - if(mstatus->max_sp < 1) mstatus->max_sp = 1; + md.status.max_hp = (unsigned int)cap_value(maxhp, 1, UINT_MAX); } - //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 +4395,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 +4421,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 +5263,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); -- cgit v1.2.3-70-g09d2 From 8121e1d12dca0c85892ad45f0bb765e7eba770aa Mon Sep 17 00:00:00 2001 From: Haru Date: Sun, 20 Dec 2015 02:24:07 +0100 Subject: Replaced several floating-point operations with integer operations This fixes several rounding errors happening in various places (i.e. the base exp for HORONG being calculated as 819 instead of 820 when the server rates are set to 1x) Signed-off-by: Haru --- src/map/mob.c | 28 +++++++-------- src/map/npc.c | 30 ++++++++-------- src/map/pc.c | 101 +++++++++++++++++++++++++++++------------------------- src/map/script.c | 30 +++++++--------- src/map/vending.c | 12 +++---- 5 files changed, 101 insertions(+), 100 deletions(-) (limited to 'src/map') diff --git a/src/map/mob.c b/src/map/mob.c index 820376508..5da9a7162 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); } @@ -4173,12 +4173,12 @@ int mob_read_db_sub(config_setting_t *mobt, int n, const char *source) } if (mob->lookup_const(mobt, "Exp", &i32) && i32 >= 0) { - double exp = (double)(i32) * (double)battle_config.base_exp_rate / 100.; + 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) { - double exp = (double)(i32) * (double)battle_config.job_exp_rate / 100.; + int64 exp = apply_percentrate64(i32, battle_config.job_exp_rate, 100); md.job_exp = (unsigned int)cap_value(exp, 0, UINT_MAX); } @@ -4302,19 +4302,17 @@ int mob_read_db_sub(config_setting_t *mobt, int n, const char *source) // MVP EXP Bonus: MEXP if (mob->lookup_const(mobt, "MvpExp", &i32) && i32 >= 0) { // Some new MVP's MEXP multiple by high exp-rate cause overflow. [LuzZza] - double exp = (double)i32 * (double)battle_config.mvp_exp_rate / 100.; + 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] - double maxhp = (double)md.status.max_hp; + int64 maxhp = md.status.max_hp; if (md.mexp > 0) { //Mvp - if (battle_config.mvp_hp_rate != 100) - maxhp = maxhp * (double)battle_config.mvp_hp_rate / 100.; + 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); } md.status.max_hp = (unsigned int)cap_value(maxhp, 1, UINT_MAX); } diff --git a/src/map/npc.c b/src/map/npc.c index acecff6d0..c70964e5e 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 /// @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 ca53b6f6d..9e113a6f9 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -3851,7 +3851,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; @@ -3883,25 +3883,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; @@ -9710,20 +9706,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++ ) { -- cgit v1.2.3-70-g09d2 From 0426f8c42f9f6d94db8d6a23f248e876b79c7973 Mon Sep 17 00:00:00 2001 From: Haru Date: Sun, 20 Dec 2015 03:28:55 +0100 Subject: Re-added support for the JName field in the mob db Signed-off-by: Haru --- db/mob_db2.conf | 1 + db/pre-re/mob_db.conf | 1 + db/re/mob_db.conf | 1 + src/map/mob.c | 8 ++++++++ 4 files changed, 11 insertions(+) (limited to 'src/map') diff --git a/db/mob_db2.conf b/db/mob_db2.conf index dd9e65202..e1084cff2 100644 --- a/db/mob_db2.conf +++ b/db/mob_db2.conf @@ -37,6 +37,7 @@ mob_db: ( SpriteName: "SPRITE_NAME" (string) Name: "Mob name" (string) // ================ Optional fields =============================== + JName: "Mob name" (string) Lv: level (int, defaults to 1) Hp: health (int, defaults to 1) Sp: mana (int, defaults to 0) diff --git a/db/pre-re/mob_db.conf b/db/pre-re/mob_db.conf index 41787d2b1..33dabad68 100644 --- a/db/pre-re/mob_db.conf +++ b/db/pre-re/mob_db.conf @@ -37,6 +37,7 @@ mob_db: ( SpriteName: "SPRITE_NAME" (string) Name: "Mob name" (string) // ================ Optional fields =============================== + JName: "Mob name" (string) Lv: level (int, defaults to 1) Hp: health (int, defaults to 1) Sp: mana (int, defaults to 0) diff --git a/db/re/mob_db.conf b/db/re/mob_db.conf index 6b9ec165d..f6df598cf 100644 --- a/db/re/mob_db.conf +++ b/db/re/mob_db.conf @@ -37,6 +37,7 @@ mob_db: ( SpriteName: "SPRITE_NAME" (string) Name: "Mob name" (string) // ================ Optional fields =============================== + JName: "Mob name" (string) Lv: level (int, defaults to 1) Hp: health (int, defaults to 1) Sp: mana (int, defaults to 0) diff --git a/src/map/mob.c b/src/map/mob.c index 5da9a7162..bc78c6098 100644 --- a/src/map/mob.c +++ b/src/map/mob.c @@ -4060,6 +4060,7 @@ int mob_read_db_sub(config_setting_t *mobt, int n, const char *source) * Id: ID * SpriteName: "SPRITE_NAME" * Name: "Mob name" + * JName: "Mob name" * // Optional fields * Lv: level * Hp: health @@ -4149,6 +4150,13 @@ int mob_read_db_sub(config_setting_t *mobt, int n, const char *source) } } 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(md.jname, str, sizeof(md.jname)); } -- cgit v1.2.3-70-g09d2