diff options
Diffstat (limited to 'src/map/mob.c')
-rw-r--r-- | src/map/mob.c | 1034 |
1 files changed, 632 insertions, 402 deletions
diff --git a/src/map/mob.c b/src/map/mob.c index 8a8e96508..2b519462d 100644 --- a/src/map/mob.c +++ b/src/map/mob.c @@ -4,50 +4,51 @@ #define HERCULES_CORE -#include "../config/core.h" // AUTOLOOT_DISTANCE, DBPATH, DEFTYPE_MAX, DEFTYPE_MIN, RENEWAL_DROP, RENEWAL_EXP +#include "config/core.h" // AUTOLOOT_DISTANCE, DBPATH, DEFTYPE_MAX, DEFTYPE_MIN, RENEWAL_DROP, RENEWAL_EXP #include "mob.h" +#include "map/atcommand.h" +#include "map/battle.h" +#include "map/clif.h" +#include "map/date.h" +#include "map/elemental.h" +#include "map/guild.h" +#include "map/homunculus.h" +#include "map/intif.h" +#include "map/itemdb.h" +#include "map/log.h" +#include "map/map.h" +#include "map/mercenary.h" +#include "map/npc.h" +#include "map/party.h" +#include "map/path.h" +#include "map/pc.h" +#include "map/pet.h" +#include "map/quest.h" +#include "map/script.h" +#include "map/skill.h" +#include "map/status.h" +#include "common/HPM.h" +#include "common/cbasetypes.h" +#include "common/db.h" +#include "common/ers.h" +#include "common/memmgr.h" +#include "common/nullpo.h" +#include "common/random.h" +#include "common/showmsg.h" +#include "common/socket.h" +#include "common/strlib.h" +#include "common/timer.h" +#include "common/utils.h" + #include <math.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include "atcommand.h" -#include "battle.h" -#include "clif.h" -#include "date.h" -#include "elemental.h" -#include "guild.h" -#include "homunculus.h" -#include "intif.h" -#include "itemdb.h" -#include "log.h" -#include "map.h" -#include "mercenary.h" -#include "npc.h" -#include "party.h" -#include "path.h" -#include "pc.h" -#include "pet.h" -#include "quest.h" -#include "script.h" -#include "skill.h" -#include "status.h" -#include "../common/HPM.h" -#include "../common/cbasetypes.h" -#include "../common/db.h" -#include "../common/ers.h" -#include "../common/malloc.h" -#include "../common/nullpo.h" -#include "../common/random.h" -#include "../common/showmsg.h" -#include "../common/socket.h" -#include "../common/strlib.h" -#include "../common/timer.h" -#include "../common/utils.h" - struct mob_interface mob_s; +struct mob_interface *mob; #define ACTIVE_AI_RANGE 2 //Distance added on top of 'AREA_SIZE' at which mobs enter active AI mode. @@ -290,7 +291,7 @@ struct mob_data* mob_spawn_dataset(struct spawn_data *data) { status->set_viewdata(&md->bl, md->class_); status->change_init(&md->bl); unit->dataset(&md->bl); - + map->addiddb(&md->bl); return md; } @@ -378,14 +379,14 @@ bool mob_ksprotected(struct block_list *src, struct block_list *target) { break; // No KS Protected if( sd->bl.id == sce->val1 || // Same Owner - (sce->val2 == 2 && sd->status.party_id && sd->status.party_id == sce->val3) || // Party KS allowed - (sce->val2 == 3 && sd->status.guild_id && sd->status.guild_id == sce->val4) ) // Guild KS allowed + (sce->val2 == KSPROTECT_PARTY && sd->status.party_id && sd->status.party_id == sce->val3) || // Party KS allowed + (sce->val2 == KSPROTECT_GUILD && sd->status.guild_id && sd->status.guild_id == sce->val4) ) // Guild KS allowed break; if( t_sd && ( - (sce->val2 == 1 && sce->val1 != t_sd->bl.id) || - (sce->val2 == 2 && sce->val3 && sce->val3 != t_sd->status.party_id) || - (sce->val2 == 3 && sce->val4 && sce->val4 != t_sd->status.guild_id)) ) + (sce->val2 == KSPROTECT_SELF && sce->val1 != t_sd->bl.id) || + (sce->val2 == KSPROTECT_PARTY && sce->val3 && sce->val3 != t_sd->status.party_id) || + (sce->val2 == KSPROTECT_GUILD && sce->val4 && sce->val4 != t_sd->status.guild_id)) ) break; if( (pl_sd = map->id2sd(sce->val1)) == NULL || pl_sd->bl.m != md->bl.m ) @@ -467,7 +468,7 @@ int mob_once_spawn(struct map_session_data* sd, int16 m, int16 x, int16 y, const struct mob_data* md = NULL; int count, lv; bool no_guardian_data = false; - + if( ai && ai&0x200 ) { no_guardian_data = true; ai &=~ 0x200; @@ -544,7 +545,7 @@ int mob_once_spawn_area(struct map_session_data* sd, int16 m, int16 x0, int16 y0 x = rnd()%(x1-x0+1)+x0; y = rnd()%(y1-y0+1)+y0; j++; - } while (map->getcell(m,x,y,CELL_CHKNOPASS) && j < max); + } while (map->getcell(m, NULL, x, y, CELL_CHKNOPASS) && j < max); if (j == max) {// attempt to find an available cell failed @@ -1078,7 +1079,7 @@ int mob_ai_sub_hard_activesearch(struct block_list *bl,va_list ap) ) { //Pick closest target? #ifdef ACTIVEPATHSEARCH struct walkpath_data wpd; - if (!path->search(&wpd, md->bl.m, md->bl.x, md->bl.y, bl->x, bl->y, 0, CELL_CHKNOPASS)) // Count walk path cells + if (!path->search(&wpd, &md->bl, md->bl.m, md->bl.x, md->bl.y, bl->x, bl->y, 0, CELL_CHKNOPASS)) // Count walk path cells return 0; //Standing monsters use range2, walking monsters use range3 if ((md->ud.walktimer == INVALID_TIMER && wpd.path_len > md->db->range2) @@ -1298,7 +1299,7 @@ int mob_unlocktarget(struct mob_data *md, int64 tick) { break; default: mob_stop_attack(md); - mob_stop_walking(md,1); //Stop chasing. + mob_stop_walking(md, STOPWALKING_FLAG_FIXPOS); //Stop chasing. md->state.skillstate = MSS_IDLE; if(battle_config.mob_ai&0x8) //Walk instantly after dropping target md->next_walktime = tick+rnd()%1000; @@ -1343,7 +1344,7 @@ int mob_randomwalk(struct mob_data *md, int64 tick) { x+=md->bl.x; y+=md->bl.y; - if(((x != md->bl.x) || (y != md->bl.y)) && map->getcell(md->bl.m,x,y,CELL_CHKPASS) && unit->walktoxy(&md->bl,x,y,8)){ + if (((x != md->bl.x) || (y != md->bl.y)) && map->getcell(md->bl.m, &md->bl, x, y, CELL_CHKPASS) && unit->walktoxy(&md->bl, x, y, 8)) { break; } } @@ -1381,7 +1382,7 @@ int mob_warpchase(struct mob_data *md, struct block_list *target) return 0; //No need to do a warp chase. if (md->ud.walktimer != INVALID_TIMER && - map->getcell(md->bl.m,md->ud.to_x,md->ud.to_y,CELL_CHKNPC)) + map->getcell(md->bl.m, &md->bl, md->ud.to_x, md->ud.to_y, CELL_CHKNPC)) return 1; //Already walking to a warp. //Search for warps within mob's viewing range. @@ -1818,7 +1819,7 @@ int mob_delay_item_drop(int tid, int64 tick, int id, intptr_t data) { ditem = list->item; while (ditem) { struct item_drop *ditem_prev; - map->addflooritem(&ditem->item_data,ditem->item_data.amount, + map->addflooritem(NULL, &ditem->item_data,ditem->item_data.amount, list->m,list->x,list->y, list->first_charid,list->second_charid,list->third_charid,0); ditem_prev = ditem; @@ -1975,7 +1976,7 @@ void mob_log_damage(struct mob_data *md, struct block_list *src, int damage) case BL_MOB: { struct mob_data* md2 = (TBL_MOB*)src; - if( md2->special_state.ai && md2->master_id ) { + if (md2->special_state.ai != AI_NONE && md2->master_id) { struct map_session_data* msd = map->id2sd(md2->master_id); if( msd ) char_id = msd->status.char_id; @@ -2063,13 +2064,13 @@ void mob_damage(struct mob_data *md, struct block_list *src, int damage) { return; #if PACKETVER >= 20120404 - if( !(md->status.mode&MD_BOSS) ){ + if (battle_config.show_monster_hp_bar && !(md->status.mode&MD_BOSS)) { int i; for(i = 0; i < DAMAGELOG_SIZE; i++){ // must show hp bar to all char who already hit the mob. - if( md->dmglog[i].id ) { + if (md->dmglog[i].id) { struct map_session_data *sd = map->charid2sd(md->dmglog[i].id); - if( sd && check_distance_bl(&md->bl, &sd->bl, AREA_SIZE) ) // check if in range - clif->monster_hp_bar(md,sd); + if (sd && check_distance_bl(&md->bl, &sd->bl, AREA_SIZE)) // check if in range + clif->monster_hp_bar(md, sd); } } } @@ -2170,7 +2171,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) { if( !(type&2) //No exp && (!map->list[m].flag.pvp || battle_config.pvp_exp) //Pvp no exp rule [MouseJstr] - && (!md->master_id || !md->special_state.ai) //Only player-summoned mobs do not give exp. [Skotlex] + && (!md->master_id || md->special_state.ai == AI_NONE) //Only player-summoned mobs do not give exp. [Skotlex] && (!map->list[m].flag.nobaseexp || !map->list[m].flag.nojobexp) //Gives Exp ) { //Experience calculation. int bonus = 100; //Bonus on top of your share (common to all attackers). @@ -2183,7 +2184,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) { else ARR_FIND(0, MAX_PC_FEELHATE, i, temp == sd->hate_mob[i] && (battle_config.allow_skill_without_day || pc->sg_info[i].day_func())); - if(i<MAX_PC_FEELHATE && (temp=pc->checkskill(sd,pc->sg_info[i].bless_id))) + if(i<MAX_PC_FEELHATE && (temp=pc->checkskill(sd,pc->sg_info[i].bless_id)) > 0) bonus += (i==2?20:10)*temp; } if(battle_config.mobs_level_up && md->level > md->db->lv) // [Valaris] @@ -2298,9 +2299,9 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) { } //End EXP giving. if( !(type&1) && !map->list[m].flag.nomobloot && !md->state.rebirth && ( - !md->special_state.ai || //Non special mob + md->special_state.ai == AI_NONE || //Non special mob battle_config.alchemist_summon_reward == 2 || //All summoned give drops - (md->special_state.ai==2 && battle_config.alchemist_summon_reward == 1) //Marine Sphere Drops items. + (md->special_state.ai == AI_SPHERE && battle_config.alchemist_summon_reward == 1) //Marine Sphere Drops items. ) ) { // Item Drop struct item_drop_list *dlist = ers_alloc(item_drop_list_ers, struct item_drop_list); @@ -2370,7 +2371,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) { if( drop_rate < 1 ) drop_rate = 1; } - + // attempt to drop the item if (rnd() % 10000 >= drop_rate) continue; @@ -2389,14 +2390,14 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) { //MSG: "'%s' won %s's %s (chance: %0.02f%%)" intif->broadcast(message, strlen(message)+1, BC_DEFAULT); } - + /* heres the thing we got the feature set up however we're still discussing how to best define the ids, * so while we discuss, for a small period of time, the list is hardcoded (yes officially only those 2 use it, * thus why we're unsure on how to best place the setting) */ /* temp, will not be hardcoded for long thudu. */ if( it->nameid == 7782 || it->nameid == 7783 ) /* for when not hardcoded: add a check on mvp bonus drop as well */ clif->item_drop_announce(mvp_sd, it->nameid, md->name); - + // Announce first, or else ditem will be freed. [Lance] // By popular demand, use base drop rate for autoloot code. [Skotlex] mob->item_drop(md, dlist, ditem, 0, md->db->dropitem[i].p, homkillonly); @@ -2473,7 +2474,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) { timer->add(tick + (!battle_config.delay_battle_damage?500:0), mob->delay_item_drop, 0, (intptr_t)dlist); } - if(mvp_sd && md->db->mexp > 0 && !md->special_state.ai) { + if(mvp_sd && md->db->mexp > 0 && md->special_state.ai == AI_NONE) { int log_mvp[2] = {0}; unsigned int mexp; double exp; @@ -2542,7 +2543,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) { if((temp = pc->additem(mvp_sd,&item,1,LOG_TYPE_PICKDROP_PLAYER)) != 0) { clif->additem(mvp_sd,0,0,temp); - map->addflooritem(&item,1,mvp_sd->bl.m,mvp_sd->bl.x,mvp_sd->bl.y,mvp_sd->status.char_id,(second_sd?second_sd->status.char_id:0),(third_sd?third_sd->status.char_id:0),1); + map->addflooritem(&md->bl, &item, 1, mvp_sd->bl.m, mvp_sd->bl.x, mvp_sd->bl.y, mvp_sd->status.char_id, (second_sd?second_sd->status.char_id : 0), (third_sd ? third_sd->status.char_id : 0), 1); } //Logs items, MVP prizes [Lupus] @@ -2573,7 +2574,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) { if( sd ) { if( sd->mission_mobid == md->class_) { //TK_MISSION [Skotlex] - if( ++sd->mission_count >= 100 && (temp = mob->get_random_id(0, 0xE, sd->status.base_level)) ) { + if (++sd->mission_count >= 100 && (temp = mob->get_random_id(0, 0xE, sd->status.base_level)) != 0) { pc->addfame(sd, 1); sd->mission_mobid = temp; pc_setglobalreg(sd,script->add_str("TK_MISSION_ID"), temp); @@ -2746,7 +2747,7 @@ int mob_class_change (struct mob_data *md, int class_) if( mob_is_treasure(md) ) return 0; //Treasure Boxes - if( md->special_state.ai > 1 ) + if( md->special_state.ai > AI_ATTACK ) return 0; //Marine Spheres and Floras. if( mob->is_clone(md->class_) ) @@ -2764,7 +2765,7 @@ int mob_class_change (struct mob_data *md, int class_) memcpy(md->name,md->db->jname,NAME_LENGTH); mob_stop_attack(md); - mob_stop_walking(md, 0); + mob_stop_walking(md, STOPWALKING_FLAG_NONE); unit->skillcastcancel(&md->bl, 0); status->set_viewdata(&md->bl, class_); clif->class_change(&md->bl, md->vd->class_, 1); @@ -2797,19 +2798,19 @@ int mob_class_change (struct mob_data *md, int class_) /*========================================== * mob heal, update display hp info of mob for players *------------------------------------------*/ -void mob_heal(struct mob_data *md,unsigned int heal) +void mob_heal(struct mob_data *md, unsigned int heal) { if (battle_config.show_mob_info&3) clif->charnameack (0, &md->bl); - + #if PACKETVER >= 20120404 - if( !(md->status.mode&MD_BOSS) ){ + if (battle_config.show_monster_hp_bar && !(md->status.mode&MD_BOSS)) { int i; for(i = 0; i < DAMAGELOG_SIZE; i++){ // must show hp bar to all char who already hit the mob. - if( md->dmglog[i].id ) { + if (md->dmglog[i].id) { struct map_session_data *sd = map->charid2sd(md->dmglog[i].id); - if( sd && check_distance_bl(&md->bl, &sd->bl, AREA_SIZE) ) // check if in range - clif->monster_hp_bar(md,sd); + if (sd && check_distance_bl(&md->bl, &sd->bl, AREA_SIZE)) // check if in range + clif->monster_hp_bar(md, sd); } } } @@ -3016,7 +3017,7 @@ struct block_list *mob_getfriendhprate(struct mob_data *md,int min_rate,int max_ nullpo_retr(NULL, md); - if (md->special_state.ai) //Summoned creatures. [Skotlex] + if (md->special_state.ai != AI_NONE) //Summoned creatures. [Skotlex] type = BL_PC; map->foreachinrange(mob->getfriendhprate_sub, &md->bl, 8, type,md,min_rate,max_rate,&fr); @@ -3174,7 +3175,8 @@ int mobskill_use(struct mob_data *md, int64 tick, int event) { case MSC_MASTERHPLTMAXRATE: flag = ((fbl = mob->getmasterhpltmaxrate(md, ms[i].cond2)) != NULL); break; case MSC_MASTERATTACKED: - flag = (md->master_id > 0 && (fbl=map->id2bl(md->master_id)) && unit->counttargeted(fbl) > 0); break; + flag = (md->master_id > 0 && (fbl=map->id2bl(md->master_id)) != NULL && unit->counttargeted(fbl) > 0); + break; case MSC_ALCHEMIST: flag = (md->state.alchemist); break; @@ -3302,11 +3304,11 @@ int mobskill_event(struct mob_data *md, struct block_list *src, int64 tick, int if(md->bl.prev == NULL || md->status.hp <= 0) return 0; - if( md->special_state.ai == 2 ) {//LOne WOlf explained that ANYONE can trigger the marine countdown skill. [Skotlex] + if (md->special_state.ai == AI_SPHERE) {//LOne WOlf explained that ANYONE can trigger the marine countdown skill. [Skotlex] md->state.alchemist = 1; return mob->skill_use(md, timer->gettick(), MSC_ALCHEMIST); } - + target_id = md->target_id; if (!target_id || battle_config.mob_changetarget_byskill) md->target_id = src->id; @@ -3402,7 +3404,7 @@ int mob_clone_spawn(struct map_session_data *sd, int16 m, int16 x, int16 y, cons int idx = pc->skill_tree[pc->class2idx(sd->status.class_)][j].idx; int skill_id = pc->skill_tree[pc->class2idx(sd->status.class_)][j].id; if (!skill_id || sd->status.skill[idx].lv < 1 || - (skill->db[idx].inf2&(INF2_WEDDING_SKILL|INF2_GUILD_SKILL)) + (skill->dbs->db[idx].inf2&(INF2_WEDDING_SKILL|INF2_GUILD_SKILL)) ) continue; for(h = 0; h < map->list[sd->bl.m].zone->disabled_skills_count; h++) { @@ -3435,7 +3437,7 @@ int mob_clone_spawn(struct map_session_data *sd, int16 m, int16 x, int16 y, cons ms[i].casttime = skill->cast_fix(&sd->bl,skill_id, ms[i].skill_lv); ms[i].delay = 5000+skill->delay_fix(&sd->bl,skill_id, ms[i].skill_lv); - inf = skill->db[idx].inf; + inf = skill->dbs->db[idx].inf; if (inf&INF_ATTACK_SKILL) { ms[i].target = MST_TARGET; ms[i].cond1 = MSC_ALWAYS; @@ -3644,77 +3646,382 @@ void item_dropratio_adjust(int nameid, int mob_id, int *rate_adjust) *rate_adjust = item_drop_ratio_db[nameid]->drop_ratio; } } + /* (mob_parse_dbrow)_cap_value */ static inline int mob_parse_dbrow_cap_value(int class_, int min, int max, int value) { if( value > max ) { - ShowError("mob_parse_dbrow: for class '%d', field value '%d' is higher than the maximum '%d'! capping...\n", class_, value, max); + ShowError("mob_parse_dbrow_cap_value: for class '%d', field value '%d' is higher than the maximum '%d'! capping...\n", class_, value, max); return max; } else if ( value < min ) { - ShowError("mob_parse_dbrow: for class '%d', field value '%d' is lower than the minimum '%d'! capping...\n", class_, value, min); + ShowError("mob_parse_dbrow_cap_value: for class '%d', field value '%d' is lower than the minimum '%d'! capping...\n", class_, value, min); return min; } return value; } + +void mob_read_db_stats_sub(struct mob_db *entry, struct status_data *mstatus, int class_, 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); + } + if (mob->lookup_const(t, "Agi", &i32) && i32 >= 0) { + mstatus->agi = mob_parse_dbrow_cap_value(class_, 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); + } + if (mob->lookup_const(t, "Int", &i32) && i32 >= 0) { + mstatus->int_ = mob_parse_dbrow_cap_value(class_, 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); + } + if (mob->lookup_const(t, "Luk", &i32) && i32 >= 0) { + mstatus->luk = mob_parse_dbrow_cap_value(class_, 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) +{ + int mode = 0; + config_setting_t *t2; + + if ((t2 = libconfig->setting_get_member(t, "CanMove"))) + mode |= libconfig->setting_get_bool(t2) ? MD_CANMOVE : 0; + if ((t2 = libconfig->setting_get_member(t, "Looter"))) + mode |= libconfig->setting_get_bool(t2) ? MD_LOOTER : 0; + if ((t2 = libconfig->setting_get_member(t, "Aggressive"))) + mode |= libconfig->setting_get_bool(t2) ? MD_AGGRESSIVE : 0; + if ((t2 = libconfig->setting_get_member(t, "Assist"))) + mode |= libconfig->setting_get_bool(t2) ? MD_ASSIST : 0; + if ((t2 = libconfig->setting_get_member(t, "CastSensorIdle"))) + mode |= libconfig->setting_get_bool(t2) ? MD_CASTSENSOR_IDLE : 0; + if ((t2 = libconfig->setting_get_member(t, "Boss"))) + mode |= libconfig->setting_get_bool(t2) ? MD_BOSS : 0; + if ((t2 = libconfig->setting_get_member(t, "Plant"))) + mode |= libconfig->setting_get_bool(t2) ? MD_PLANT : 0; + if ((t2 = libconfig->setting_get_member(t, "CanAttack"))) + mode |= libconfig->setting_get_bool(t2) ? MD_CANATTACK : 0; + if ((t2 = libconfig->setting_get_member(t, "Detector"))) + mode |= libconfig->setting_get_bool(t2) ? MD_DETECTOR : 0; + if ((t2 = libconfig->setting_get_member(t, "CastSensorChase"))) + mode |= libconfig->setting_get_bool(t2) ? MD_CASTSENSOR_CHASE : 0; + if ((t2 = libconfig->setting_get_member(t, "ChangeChase"))) + mode |= libconfig->setting_get_bool(t2) ? MD_CHANGECHASE : 0; + if ((t2 = libconfig->setting_get_member(t, "Angry"))) + mode |= libconfig->setting_get_bool(t2) ? MD_ANGRY : 0; + if ((t2 = libconfig->setting_get_member(t, "ChangeTargetMelee"))) + mode |= libconfig->setting_get_bool(t2) ? MD_CHANGETARGET_MELEE : 0; + if ((t2 = libconfig->setting_get_member(t, "ChangeTargetChase"))) + mode |= libconfig->setting_get_bool(t2) ? MD_CHANGETARGET_CHASE : 0; + if ((t2 = libconfig->setting_get_member(t, "TargetWeak"))) + mode |= libconfig->setting_get_bool(t2) ? MD_TARGETWEAK : 0; + + return mode; +} + +void mob_read_db_mvpdrops_sub(struct mob_db *entry, struct status_data *mstatus, int class_, config_setting_t *t) +{ + config_setting_t *drop; + int i = 0; + int idx = 0; + int i32; + + while (idx < MAX_MVP_DROP && (drop = libconfig->setting_get_elem(t, i))) { + const char *name = config_setting_name(drop); + 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 ++; + 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 ++; + continue; + } + entry->mvpitem[idx].nameid = id->nameid; + if (!entry->mvpitem[idx].nameid) { + entry->mvpitem[idx].p = 0; //No item.... + i ++; + continue; + } + mob->item_dropratio_adjust(entry->mvpitem[idx].nameid, class_, &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 + if (entry->mvpitem[idx].p) { + if (id->maxchance == -1 || (id->maxchance < entry->mvpitem[idx].p/10 + 1) ) { + //item has bigger drop chance or sold in shops + id->maxchance = entry->mvpitem[idx].p/10 + 1; //reduce MVP drop info to not spoil common drop rate + } + } + i++; + 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_); + } +} + +void mob_read_db_drops_sub(struct mob_db *entry, struct status_data *mstatus, int class_, config_setting_t *t) +{ + config_setting_t *drop; + int i = 0; + int idx = 0; + int i32; + int k; + + while (idx < MAX_MOB_DROP && (drop = libconfig->setting_get_elem(t, i))) { + const char *name = config_setting_name(drop); + int rate_adjust, type; + 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 ++; + 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 ++; + continue; + } + + entry->dropitem[idx].nameid = id->nameid; + if (!entry->dropitem[idx].nameid) { + entry->dropitem[idx].p = 0; //No drop. + i ++; + continue; + } + type = id->type; + 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 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; + 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; + 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; + 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; + 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; + ratemin = battle_config.item_drop_common_min; + ratemax = battle_config.item_drop_common_max; + break; + } + mob->item_dropratio_adjust(id->nameid, class_, &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_ < 1324 || class_ > 1363) && (class_ < 1938 || class_ > 1946)) + { //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 + } + for (k = 0; k< MAX_SEARCH; k++) { + if (id->mob[k].chance <= entry->dropitem[idx].p) + break; + } + if (k == MAX_SEARCH) + { + i++; + idx++; + continue; + } + + if (id->mob[k].id != class_ && 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_; + } + 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_); + } +} + /*========================================== * processes one mobdb entry *------------------------------------------*/ -bool mob_parse_dbrow(char** str) { - struct mob_db *db, entry; +bool mob_read_db_sub(config_setting_t *mobt, int id, 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; - int class_, i; - double exp, maxhp; struct mob_data data; - - class_ = atoi(str[0]); + 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 (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); + ShowError("mob_read_db_sub: Invalid monster ID %d, must be in range %d-%d.\n", class_, 1000, MAX_MOB_DB); return false; } if (pc->db_checkid(class_)) { - ShowError("mob_parse_dbrow: Invalid monster ID %d, reserved for player classes.\n", class_); + ShowError("mob_read_db_sub: 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); + 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; } - memset(&entry, 0, sizeof(entry)); + 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_); + 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; + } + } + if (!inherit) { + memset(&tmpEntry, 0, sizeof(tmpEntry)); + } - db = &entry; - mstatus = &db->status; + mstatus = &entry->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); - mstatus->max_hp = atoi(str[5]); - mstatus->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); - - mstatus->rhw.range = atoi(str[9]); - - mstatus->rhw.atk = mob_parse_dbrow_cap_value(class_,UINT16_MIN,UINT16_MAX,atoi(str[10])); - mstatus->rhw.atk2 = mob_parse_dbrow_cap_value(class_,UINT16_MIN,UINT16_MAX,atoi(str[11])); - - mstatus->def = mob_parse_dbrow_cap_value(class_,DEFTYPE_MIN,DEFTYPE_MAX,atoi(str[12])); - mstatus->mdef = mob_parse_dbrow_cap_value(class_,DEFTYPE_MIN,DEFTYPE_MAX,atoi(str[13])); - - mstatus->str = mob_parse_dbrow_cap_value(class_,UINT16_MIN,UINT16_MAX,atoi(str[14])); - mstatus->agi = mob_parse_dbrow_cap_value(class_,UINT16_MIN,UINT16_MAX,atoi(str[15])); - mstatus->vit = mob_parse_dbrow_cap_value(class_,UINT16_MIN,UINT16_MAX,atoi(str[16])); - mstatus->int_ = mob_parse_dbrow_cap_value(class_,UINT16_MIN,UINT16_MAX,atoi(str[17])); - mstatus->dex = mob_parse_dbrow_cap_value(class_,UINT16_MIN,UINT16_MAX,atoi(str[18])); - mstatus->luk = mob_parse_dbrow_cap_value(class_,UINT16_MIN,UINT16_MAX,atoi(str[19])); + 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; + } + } else { + safestrncpy(entry->sprite, str, sizeof(entry->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; + } + } else { + safestrncpy(entry->name, str, sizeof(entry->name)); + safestrncpy(entry->jname, str, sizeof(entry->jname)); + } + + if (mob->lookup_const(mobt, "Lv", &i32) && i32 >= 0) { + entry->lv = i32; + entry->lv = cap_value(entry->lv, 1, USHRT_MAX); + } else if (!inherit) { + entry->lv = 1; + } + + if (mob->lookup_const(mobt, "Hp", &i32) && i32 >= 0) { + mstatus->max_hp = i32; + maxhpUpdated = true; + } else if (!inherit) { + mstatus->max_hp = 1; + maxhpUpdated = true; + } + + if (mob->lookup_const(mobt, "Sp", &i32) && i32 >= 0) { + mstatus->max_sp = i32; + maxspUpdated = true; + } else if (!inherit) { + maxspUpdated = true; + } + + 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); + } + + 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); + } + + if (mob->lookup_const(mobt, "AttackRange", &i32) && i32 >= 0) { + mstatus->rhw.range = i32; + } else { + mstatus->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); + if (libconfig->setting_length(t) >= 1) + mstatus->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; + } + } + + if (mob->lookup_const(mobt, "Def", &i32) && i32 >= 0) { + mstatus->def = mob_parse_dbrow_cap_value(class_, 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); + } + + if ((t = libconfig->setting_get_member(mobt, "Stats"))) { + if (config_setting_is_group(t)) { + mob->read_db_stats_sub(entry, mstatus, class_, 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); + } + } /* * Disabled for renewal since difference of 0 and 1 still has an impact in the formulas @@ -3731,265 +4038,253 @@ bool mob_parse_dbrow(char** str) { #endif //Tests showed that chase range is effectively 2 cells larger than expected [Playtester] - if (db->range3 > 0) - db->range3 += 2; + if (entry->range3 > 0) + entry->range3 += 2; + + if (mob->lookup_const(mobt, "ViewRange", &i32) && i32 >= 0) { + entry->range2 = i32; + range2Updated = true; + } else if (!inherit) { + entry->range2 = 1; + range2Updated = true; + } + + 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; + } + } - 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 (mob->lookup_const(mobt, "Size", &i32) && i32 >= 0) { + mstatus->size = i32; + mstatus->size = cap_value(mstatus->size, 0, 2); + } else if (!inherit) { + mstatus->size = 0; } - 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; + + if (mob->lookup_const(mobt, "Race", &i32) && i32 >= 0) { + mstatus->race = i32; + mstatus->race = cap_value(mstatus->race, 0, RC_MAX - 1); + } else if (!inherit) { + mstatus->race = 0; } - mstatus->size = atoi(str[22]); - mstatus->race = atoi(str[23]); + if ((t = libconfig->setting_get_member(mobt, "Element")) && config_setting_is_list(t)) { + 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; + } + } - i = atoi(str[24]); //Element - mstatus->def_ele = i%10; - mstatus->ele_lv = i/20; if (mstatus->def_ele >= ELE_MAX) { - ShowError("mob_parse_dbrow: Invalid element type %d for monster ID %d (max=%d).\n", mstatus->def_ele, class_, ELE_MAX-1); - return false; + 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) { - ShowError("mob_parse_dbrow: Invalid element level %d for monster ID %d, must be in range 1-4.\n", mstatus->ele_lv, class_); - return false; + 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; + } + } + + 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); + } else if (mob->lookup_const(mobt, "Mode", &i32) && i32 >= 0) { + mstatus->mode = i32; + } } - mstatus->mode = (int)strtol(str[25], NULL, 0); if (!battle_config.monster_active_enable) mstatus->mode &= ~MD_AGGRESSIVE; - mstatus->speed = atoi(str[26]); + if (mob->lookup_const(mobt, "MoveSpeed", &i32) && i32 >= 0) { + mstatus->speed = i32; + } + mstatus->aspd_rate = 1000; - i = atoi(str[27]); - mstatus->adelay = cap_value(i, battle_config.monster_max_aspd*2, 4000); - i = atoi(str[28]); - mstatus->amotion = cap_value(i, battle_config.monster_max_aspd, 2000); + + if (mob->lookup_const(mobt, "AttackDelay", &i32) && i32 >= 0) { + mstatus->adelay = cap_value(i32, battle_config.monster_max_aspd*2, 4000); + } else if (!inherit) { + mstatus->adelay = 4000; + } + + if (mob->lookup_const(mobt, "AttackMotion", &i32) && i32 >= 0) { + mstatus->amotion = cap_value(i32, battle_config.monster_max_aspd, 2000); + } else if (!inherit) { + mstatus->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; - mstatus->dmotion = atoi(str[29]); - if(battle_config.monster_damage_delay_rate != 100) + + if (mob->lookup_const(mobt, "DamageMotion", &i32) && i32 >= 0) { + mstatus->dmotion = i32; + dmotionUpdated = true; + } else if (!inherit) { + dmotionUpdated = true; + } + + 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 = db->lv; + data.level = entry->lv; memcpy(&data.status, mstatus, sizeof(struct status_data)); - status->calc_misc(&data.bl, mstatus, db->lv); + 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] - 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)mstatus->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.; - - mstatus->max_hp = (unsigned int)cap_value(maxhp, 1, UINT_MAX); - if(mstatus->max_sp < 1) mstatus->max_sp = 1; + 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; + } + + 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.; + } 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; + } //Since mobs always respawn with full life... mstatus->hp = mstatus->max_hp; mstatus->sp = mstatus->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; - } - mob->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 - } + if ((t = libconfig->setting_get_member(mobt, "MvpDrops"))) { + if (config_setting_is_group(t)) { + mob->read_db_mvpdrops_sub(entry, mstatus, class_, t); } } - for(i = 0; i < MAX_MOB_DROP; i++) { - int rate = 0, rate_adjust, type; - unsigned short ratemin, ratemax; - struct item_data *id; - int 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 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; - 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; - 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; - 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; - 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; - ratemin = battle_config.item_drop_common_min; - ratemax = battle_config.item_drop_common_max; - break; - } - mob->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_ && k != MAX_SEARCH - 1) - 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_; + 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_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(&db->spawn, mob->db_data[class_]->spawn, sizeof(db->spawn)); + memcpy(&entry->spawn, mob->db_data[class_]->spawn, sizeof(entry->spawn)); - memcpy(mob->db_data[class_], db, sizeof(struct mob_db)); + memcpy(mob->db_data[class_], entry, sizeof(struct mob_db)); return true; } -/*========================================== - * mob_db.txt reading - *------------------------------------------*/ -bool mob_readdb_sub(char* fields[], int columns, int current) { - return mob->parse_dbrow(fields); +void mob_read_db_additional_fields(struct mob_db *entry, int class_, config_setting_t *it, int n, const char *source) +{ + // do nothing. plugins can do own work } -void mob_readdb(void) { - const char* filename[] = { - DBPATH"mob_db.txt", - "mob_db2.txt" }; - int fi; - - for( fi = 0; fi < ARRAYLENGTH(filename); ++fi ) { - if(fi > 0) { - char filepath[256]; - sprintf(filepath, "%s/%s", map->db_path, filename[fi]); - if(!exists(filepath)) { - continue; - } +bool mob_lookup_const(const config_setting_t *it, const char *name, int *value) +{ + if (libconfig->setting_lookup_int(it, name, value)) + { + return true; + } + else + { + const char *str = NULL; + if (libconfig->setting_lookup_string(it, name, &str)) + { + if (*str && script->get_constant(str, value)) + return true; } - - sv->readdb(map->db_path, filename[fi], ',', 31+2*MAX_MVP_DROP+2*MAX_MOB_DROP, 31+2*MAX_MVP_DROP+2*MAX_MOB_DROP, -1, mob->readdb_sub); } - mob->name_constants(); + return false; } -/*========================================== - * mob_db table reading - *------------------------------------------*/ -int mob_read_sqldb(void) { - const char* mob_db_name[] = { - map->mob_db_db, - map->mob_db2_db - }; - int fi; - - for( fi = 0; fi < ARRAYLENGTH(mob_db_name); ++fi ) { - uint32 lines = 0, count = 0; +bool mob_get_const(const config_setting_t *it, int *value) +{ + const char *str = config_setting_get_string(it); + if (str && *str && script->get_constant(str, value)) + return true; - // retrieve all rows from the mob database - if( SQL_ERROR == SQL->Query(map->mysql_handle, "SELECT * FROM `%s`", mob_db_name[fi]) ) { - Sql_ShowDebug(map->mysql_handle); - continue; - } + *value = libconfig->setting_get_int(it); + return true; +} - // process rows one by one - while( SQL_SUCCESS == SQL->NextRow(map->mysql_handle) ) { - // wrap the result into a TXT-compatible format - char line[1024]; - char* str[31+2*MAX_MVP_DROP+2*MAX_MOB_DROP]; - char* p; - int i; +/*========================================== + * mob_db.txt reading + *------------------------------------------*/ +void mob_readdb(void) { + const char* filename[] = { + DBPATH"mob_db.conf", + "mob_db2.conf" }; + int i; - lines++; - for(i = 0, p = line; i < 31+2*MAX_MVP_DROP+2*MAX_MOB_DROP; i++) - { - char* data; - size_t len; - SQL->GetData(map->mysql_handle, i, &data, &len); + for (i = 0; i < ARRAYLENGTH(filename); ++i) { + mob->read_libconfig(filename[i], i > 0 ? true : false); + } + mob->name_constants(); +} - strcpy(p, data); - str[i] = p; - p+= len + 1; - } +int mob_read_libconfig(const char *filename, bool ignore_missing) +{ + config_t mob_db_conf; + char filepath[256]; + config_setting_t *mdb; + config_setting_t *t; + int i = 0; - if (!mob->parse_dbrow(str)) - continue; + nullpo_ret(filename); + sprintf(filepath, "%s/%s", map->db_path, filename); - count++; - } + if (ignore_missing && !exists(filepath)) + return 0; - // free the query result - SQL->FreeResult(map->mysql_handle); + 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; + } - ShowStatus("Done reading '"CL_WHITE"%"PRIu32""CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, mob_db_name[fi]); + while ((t = libconfig->setting_get_elem(mdb, i++))) { + mob->read_db_sub(t, i - 1, filepath); } - mob->name_constants(); + 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; } @@ -4399,13 +4694,13 @@ bool mob_parse_row_mobskilldb(char** str, int columns, int current) if ( skill->get_casttype2(sidx) == CAST_GROUND) {//Ground skill. if (ms->target > MST_AROUND) { ShowWarning("mob_parse_row_mobskilldb: Wrong mob skill target for ground skill %d (%s) for %s.\n", - ms->skill_id, skill->db[sidx].name, + ms->skill_id, skill->dbs->db[sidx].name, mob_id < 0?"all mobs":mob->db_data[mob_id]->sprite); ms->target = MST_TARGET; } } else if (ms->target > MST_MASTER) { ShowWarning("mob_parse_row_mobskilldb: Wrong mob skill target 'around' for non-ground skill %d (%s) for %s.\n", - ms->skill_id, skill->db[sidx].name, + ms->skill_id, skill->dbs->db[sidx].name, mob_id < 0?"all mobs":mob->db_data[mob_id]->sprite); ms->target = MST_TARGET; } @@ -4513,60 +4808,6 @@ void mob_readskilldb(void) { } } -/** - * mob_skill_db table reading [CalciumKid] - * not overly sure if this is all correct - * seems to work though... - */ -int mob_read_sqlskilldb(void) { - const char* mob_skill_db_name[] = { - map->mob_skill_db_db, - map->mob_skill_db2_db - }; - int fi; - - if( battle_config.mob_skill_rate == 0 ) { - ShowStatus("Mob skill use disabled. Not reading mob skills.\n"); - return 0; - } - - - for( fi = 0; fi < ARRAYLENGTH(mob_skill_db_name); ++fi ) { - uint32 lines = 0, count = 0; - - // retrieve all rows from the mob skill database - if( SQL_ERROR == SQL->Query(map->mysql_handle, "SELECT * FROM `%s`", mob_skill_db_name[fi]) ) { - Sql_ShowDebug(map->mysql_handle); - continue; - } - - // process rows one by one - while( SQL_SUCCESS == SQL->NextRow(map->mysql_handle) ) { - // wrap the result into a TXT-compatible format - char* str[19]; - char* dummy = ""; - int i; - ++lines; - for( i = 0; i < 19; ++i ) - { - SQL->GetData(map->mysql_handle, i, &str[i], NULL); - if( str[i] == NULL ) str[i] = dummy; // get rid of NULL columns - } - - if (!mob->parse_row_mobskilldb(str, 19, count)) - continue; - - count++; - } - - // free the query result - SQL->FreeResult(map->mysql_handle); - - ShowStatus("Done reading '"CL_WHITE"%"PRIu32""CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, mob_skill_db_name[fi]); - } - return 0; -} - /*========================================== * mob_race2_db.txt reading *------------------------------------------*/ @@ -4629,15 +4870,8 @@ void mob_load(bool minimal) { } sv->readdb(map->db_path, "mob_item_ratio.txt", ',', 2, 2+MAX_ITEMRATIO_MOBS, -1, mob->readdb_itemratio); // must be read before mobdb mob->readchatdb(); - if (map->db_use_sql_mob_db) { - mob->read_sqldb(); - } - if (map->db_use_sql_mob_skill_db) { - mob->read_sqlskilldb(); - } else { - mob->readdb(); - mob->readskilldb(); - } + mob->readdb(); + mob->readskilldb(); sv->readdb(map->db_path, "mob_avail.txt", ',', 2, 12, -1, mob->readdb_mobavail); mob->read_randommonster(); sv->readdb(map->db_path, DBPATH"mob_race2_db.txt", ',', 2, 20, -1, mob->readdb_race2); @@ -4645,7 +4879,7 @@ void mob_load(bool minimal) { void mob_reload(void) { int i; - + //Mob skills need to be cleared before re-reading them. [Skotlex] for (i = 0; i < MAX_MOB_DB; i++) if (mob->db_data[i] && !mob->is_clone(i)) { @@ -4707,16 +4941,7 @@ int do_init_mob(bool minimal) { void mob_destroy_mob_db(int index) { struct mob_db *data = mob->db_data[index]; - if (data->hdata) { - int i; - for (i = 0; i < data->hdatac; i++) { - if (data->hdata[i]->flag.free ) { - aFree(data->hdata[i]->data); - } - aFree(data->hdata[i]); - } - aFree(data->hdata); - } + HPM->data_store_destroy(&data->hdata); aFree(data); mob->db_data[index] = NULL; } @@ -4764,13 +4989,13 @@ void mob_defaults(void) { //Defines the Manuk/Splendide mob groups for the status reductions [Epoque] const int mob_manuk[8] = { 1986, 1987, 1988, 1989, 1990, 1997, 1998, 1999 }; const int mob_splendide[5] = { 1991, 1992, 1993, 1994, 1995 }; - + mob = &mob_s; - + memset(mob->db_data, 0, sizeof(mob->db_data)); mob->dummy = NULL; memset(mob->chat_db, 0, sizeof(mob->chat_db)); - + memcpy(mob->manuk, mob_manuk, sizeof(mob->manuk)); memcpy(mob->splendide, mob_splendide, sizeof(mob->splendide)); /* */ @@ -4855,10 +5080,16 @@ void mob_defaults(void) { mob->clone_delete = mob_clone_delete; mob->drop_adjust = mob_drop_adjust; mob->item_dropratio_adjust = item_dropratio_adjust; - mob->parse_dbrow = mob_parse_dbrow; - mob->readdb_sub = mob_readdb_sub; + mob->lookup_const = mob_lookup_const; + mob->get_const = mob_get_const; mob->readdb = mob_readdb; - mob->read_sqldb = mob_read_sqldb; + mob->read_libconfig = mob_read_libconfig; + mob->read_db_additional_fields = mob_read_db_additional_fields; + mob->read_db_sub = mob_read_db_sub; + mob->read_db_drops_sub = mob_read_db_drops_sub; + mob->read_db_mvpdrops_sub = mob_read_db_mvpdrops_sub; + mob->read_db_mode_sub = mob_read_db_mode_sub; + mob->read_db_stats_sub = mob_read_db_stats_sub; mob->name_constants = mob_name_constants; mob->readdb_mobavail = mob_readdb_mobavail; mob->read_randommonster = mob_read_randommonster; @@ -4866,7 +5097,6 @@ void mob_defaults(void) { mob->readchatdb = mob_readchatdb; mob->parse_row_mobskilldb = mob_parse_row_mobskilldb; mob->readskilldb = mob_readskilldb; - mob->read_sqlskilldb = mob_read_sqlskilldb; mob->readdb_race2 = mob_readdb_race2; mob->readdb_itemratio = mob_readdb_itemratio; mob->load = mob_load; |