static bool mob_parse_dbrow(char** str)
{
struct mob_db *db, entry;
struct status_data *status;
int class_, i, k;
double exp, maxhp;
struct mob_data data;
class_ = atoi(str[0]);
if (class_ <= 1000 || class_ > MAX_MOB_DB) {
ShowError("mob_parse_dbrow: Invalid monster ID %d, must be in range %d-%d.\n", class_, 1000, MAX_MOB_DB);
return false;
}
if (pcdb_checkid(class_)) {
ShowError("mob_parse_dbrow: Invalid monster ID %d, reserved for player classes.\n", class_);
return false;
}
if (class_ >= MOB_CLONE_START && class_ < MOB_CLONE_END) {
ShowError("mob_parse_dbrow: 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;
}
memset(&entry, 0, sizeof(entry));
db = &entry;
status = &db->status;
db->vd.class_ = class_;
safestrncpy(db->sprite, str[1], sizeof(db->sprite));
safestrncpy(db->jname, str[2], sizeof(db->jname));
safestrncpy(db->name, str[3], sizeof(db->name));
db->lv = atoi(str[4]);
db->lv = cap_value(db->lv, 1, USHRT_MAX);
status->max_hp = atoi(str[5]);
status->max_sp = atoi(str[6]);
exp = (double)atoi(str[7]) * (double)battle_config.base_exp_rate / 100.;
db->base_exp = (unsigned int)cap_value(exp, 0, UINT_MAX);
exp = (double)atoi(str[8]) * (double)battle_config.job_exp_rate / 100.;
db->job_exp = (unsigned int)cap_value(exp, 0, UINT_MAX);
status->rhw.range = atoi(str[9]);
status->rhw.atk = atoi(str[10]);
status->rhw.atk2 = atoi(str[11]);
status->def = atoi(str[12]);
status->mdef = atoi(str[13]);
status->str = atoi(str[14]);
status->agi = atoi(str[15]);
status->vit = atoi(str[16]);
status->int_ = atoi(str[17]);
status->dex = atoi(str[18]);
status->luk = atoi(str[19]);
//All status should be min 1 to prevent divisions by zero from some skills. [Skotlex]
if (status->str < 1) status->str = 1;
if (status->agi < 1) status->agi = 1;
if (status->vit < 1) status->vit = 1;
if (status->int_< 1) status->int_= 1;
if (status->dex < 1) status->dex = 1;
if (status->luk < 1) status->luk = 1;
db->range2 = atoi(str[20]);
db->range3 = atoi(str[21]);
if (battle_config.view_range_rate != 100) {
db->range2 = db->range2 * battle_config.view_range_rate / 100;
if (db->range2 < 1)
db->range2 = 1;
}
if (battle_config.chase_range_rate != 100) {
db->range3 = db->range3 * battle_config.chase_range_rate / 100;
if (db->range3 < db->range2)
db->range3 = db->range2;
}
status->size = atoi(str[22]);
status->race = atoi(str[23]);
i = atoi(str[24]); //Element
status->def_ele = i%10;
status->ele_lv = i/20;
if (status->def_ele >= ELE_MAX) {
ShowError("mob_parse_dbrow: Invalid element type %d for monster ID %d (max=%d).\n", status->def_ele, class_, ELE_MAX-1);
return false;
}
if (status->ele_lv < 1 || status->ele_lv > 4) {
ShowError("mob_parse_dbrow: Invalid element level %d for monster ID %d, must be in range 1-4.\n", status->ele_lv, class_);
return false;
}
status->mode = (int)strtol(str[25], NULL, 0);
if (!battle_config.monster_active_enable)
status->mode &= ~MD_AGGRESSIVE;
status->speed = atoi(str[26]);
status->aspd_rate = 1000;
i = atoi(str[27]);
status->adelay = cap_value(i, battle_config.monster_max_aspd*2, 4000);
i = atoi(str[28]);
status->amotion = cap_value(i, battle_config.monster_max_aspd, 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 (status->adelay < status->amotion)
status->adelay = status->amotion;
status->dmotion = atoi(str[29]);
if(battle_config.monster_damage_delay_rate != 100)
status->dmotion = status->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 = db->lv;
memcpy(&data.status, status, sizeof(struct status_data));
iStatus->calc_misc(&data.bl, status, db->lv);
// MVP EXP Bonus: MEXP
// Some new MVP's MEXP multipled by high exp-rate cause overflow. [LuzZza]
exp = (double)atoi(str[30]) * (double)battle_config.mvp_exp_rate / 100.;
db->mexp = (unsigned int)cap_value(exp, 0, UINT_MAX);
//Now that we know if it is an mvp or not, apply battle_config modifiers [Skotlex]
maxhp = (double)status->max_hp;
if (db->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.;
status->max_hp = (unsigned int)cap_value(maxhp, 1, UINT_MAX);
if(status->max_sp < 1) status->max_sp = 1;
//Since mobs always respawn with full life...
status->hp = status->max_hp;
status->sp = status->max_sp;
// MVP Drops: MVP1id,MVP1per,MVP2id,MVP2per,MVP3id,MVP3per
for(i = 0; i < MAX_MVP_DROP; i++) {
int rate_adjust = battle_config.item_rate_mvp;;
db->mvpitem[i].nameid = atoi(str[31+i*2]);
if (!db->mvpitem[i].nameid) {
db->mvpitem[i].p = 0; //No item....
continue;
}
item_dropratio_adjust(db->mvpitem[i].nameid, class_, &rate_adjust);
db->mvpitem[i].p = mob_drop_adjust(atoi(str[32+i*2]), 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
if (db->mvpitem[i].p) {
struct item_data *id;
id = itemdb->search(db->mvpitem[i].nameid);
if (id->maxchance == -1 || (id->maxchance < db->mvpitem[i].p/10 + 1) ) {
//item has bigger drop chance or sold in shops
id->maxchance = db->mvpitem[i].p/10 + 1; //reduce MVP drop info to not spoil common drop rate
}
}
}
for(i = 0; i < MAX_MOB_DROP; i++) {
int rate = 0, rate_adjust, type;
unsigned short ratemin, ratemax;
struct item_data *id;
k = 31 + MAX_MVP_DROP*2 + i*2;
db->dropitem[i].nameid = atoi(str[k]);
if (!db->dropitem[i].nameid) {
db->dropitem[i].p = 0; //No drop.
continue;
}
id = itemdb->search(db->dropitem[i].nameid);
type = id->type;
rate = atoi(str[k+1]);
if( (class_ >= 1324 && class_ <= 1363) || (class_ >= 1938 && class_ <= 1946) )
{ //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 suport to restrict normal drops of MVP's [Reddozen]
case IT_HEALING:
rate_adjust = (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 = (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 = (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 = (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 = (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;
}
item_dropratio_adjust(id->nameid, class_, &rate_adjust);
db->dropitem[i].p = mob_drop_adjust(rate, rate_adjust, ratemin, ratemax);
//calculate and store Max available drop chance of the item
if( db->dropitem[i].p && (class_ < 1324 || class_ > 1363) && (class_ < 1938 || class_ > 1946) )
{ //Skip treasure chests.
if (id->maxchance == -1 || (id->maxchance < db->dropitem[i].p) ) {
id->maxchance = db->dropitem[i].p; //item has bigger drop chance or sold in shops
}
for (k = 0; k< MAX_SEARCH; k++) {
if (id->mob[k].chance <= db->dropitem[i].p)
break;
}
if (k == MAX_SEARCH)
continue;
if (id->mob[k].id != class_)
memmove(&id->mob[k+1], &id->mob[k], (MAX_SEARCH-k-1)*sizeof(id->mob[0]));
id->mob[k].chance = db->dropitem[i].p;
id->mob[k].id = class_;
}
}
// 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(&db->spawn, mob_db_data[class_]->spawn, sizeof(db->spawn));
memcpy(mob_db_data[class_], db, sizeof(struct mob_db));
return true;
}