Implemented new monster range (#2604)

With this the internal static array was changed to a std::map which manages it's memory dynamically when needed.

Dropped mob_dummy since it was useless to have it in the first place.

Replaced a lot of direct accesses to the mob db by the "get" function.

With this you can now use 20021-31998 for monsters and also 4000-20020 will be used for player clones whenever needed.

Dropped mobdb_exists
Replaced almost all references to MAX_MOB_DB

Moved MAX_MOB_DB from the header directly into the source file to make it only visible to the .cpp file itself and not to any other.

Moved the mob drop calculation function from itemdb.cpp into mob.cpp, because it needs to iterate over the whole mob db.

Added a few missing clone checks.

Thanks to @lighta, @Jeybla and @aleos89.
This commit is contained in:
Lemongrass3110 2017-11-19 01:22:29 +01:00 committed by GitHub
parent 34817c9715
commit b2ab9724fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 212 additions and 267 deletions

View File

@ -1027,7 +1027,7 @@ struct achievement_db *achievement_read_db_sub(yamlwrapper *wrapper, int n, cons
for (tt = yaml_iterator_first(it); yaml_iterator_has_next(it) && entry->target_count < MAX_ACHIEVEMENT_OBJECTIVES; tt = yaml_iterator_next(it)) { for (tt = yaml_iterator_first(it); yaml_iterator_has_next(it) && entry->target_count < MAX_ACHIEVEMENT_OBJECTIVES; tt = yaml_iterator_next(it)) {
int mobid = 0, count = 0; int mobid = 0, count = 0;
if (yaml_node_is_defined(tt, "MobID") && (mobid = yaml_get_int(tt, "MobID")) && !mobdb_exists(mobid)) { // The mob ID field is not required if (yaml_node_is_defined(tt, "MobID") && (mobid = yaml_get_int(tt, "MobID")) && mob_db(mobid) == NULL) { // The mob ID field is not required
ShowError("achievement_read_db_sub: Invalid mob ID %d for achievement %d in \"%s\", skipping.\n", mobid, achievement_id, source); ShowError("achievement_read_db_sub: Invalid mob ID %d for achievement %d in \"%s\", skipping.\n", mobid, achievement_id, source);
continue; continue;
} }

View File

@ -1913,40 +1913,6 @@ static int itemdb_randomopt_free(DBKey key, DBData *data, va_list ap) {
return 1; return 1;
} }
/**
* Re-link monster drop data with item data
* Fixes the need of a @reloadmobdb after a @reloaditemdb
* @author Epoque
*/
void itemdb_reload_itemmob_data(void) {
int i;
for( i = 0; i < MAX_MOB_DB; i++ ) {
struct mob_db *entry = mob_db(i);
int d, k;
for(d = 0; d < MAX_MOB_DROP_TOTAL; d++) {
struct item_data *id;
if( !entry->dropitem[d].nameid )
continue;
id = itemdb_search(entry->dropitem[d].nameid);
for (k = 0; k < MAX_SEARCH; k++) {
if (id->mob[k].chance <= entry->dropitem[d].p)
break;
}
if (k == MAX_SEARCH)
continue;
if (id->mob[k].id != i)
memmove(&id->mob[k+1], &id->mob[k], (MAX_SEARCH-k-1)*sizeof(id->mob[0]));
id->mob[k].chance = entry->dropitem[d].p;
id->mob[k].id = i;
}
}
}
/** /**
* Reload Item DB * Reload Item DB
*/ */
@ -1969,7 +1935,7 @@ void itemdb_reload(void) {
if (battle_config.feature_roulette) if (battle_config.feature_roulette)
itemdb_parse_roulette_db(); itemdb_parse_roulette_db();
itemdb_reload_itemmob_data(); mob_reload_itemmob_data();
// readjust itemdb pointer cache for each player // readjust itemdb pointer cache for each player
iter = mapit_geteachpc(); iter = mapit_geteachpc();

View File

@ -954,7 +954,6 @@ bool itemdb_parse_roulette_db(void);
struct s_random_opt_data *itemdb_randomopt_exists(short id); struct s_random_opt_data *itemdb_randomopt_exists(short id);
struct s_random_opt_group *itemdb_randomopt_group_exists(int id); struct s_random_opt_group *itemdb_randomopt_group_exists(int id);
void itemdb_reload_itemmob_data(void);
void itemdb_reload(void); void itemdb_reload(void);
void do_final_itemdb(void); void do_final_itemdb(void);

View File

@ -4,6 +4,7 @@
#include "mob.hpp" #include "mob.hpp"
#include <stdlib.h> #include <stdlib.h>
#include <map>
#include <math.h> #include <math.h>
#include "../common/cbasetypes.h" #include "../common/cbasetypes.h"
@ -52,7 +53,6 @@
#define MOB_MAX_DELAY (24*3600*1000) #define MOB_MAX_DELAY (24*3600*1000)
#define MAX_MINCHASE 30 //Max minimum chase value to use for mobs. #define MAX_MINCHASE 30 //Max minimum chase value to use for mobs.
#define RUDE_ATTACKED_COUNT 1 //After how many rude-attacks should the skill be used? #define RUDE_ATTACKED_COUNT 1 //After how many rude-attacks should the skill be used?
#define MAX_MOB_CHAT 50 //Max Skill's messages
// On official servers, monsters will only seek targets that are closer to walk to than their // On official servers, monsters will only seek targets that are closer to walk to than their
// search range. The search range is affected depending on if the monster is walking or not. // search range. The search range is affected depending on if the monster is walking or not.
@ -63,19 +63,39 @@
// Disable this to make monsters not do any path search when looking for a target (old behavior). // Disable this to make monsters not do any path search when looking for a target (old behavior).
#define ACTIVEPATHSEARCH #define ACTIVEPATHSEARCH
//Dynamic mob database, allows saving of memory when there's big gaps in the mob_db [Skotlex] // Limits for the monster database
//!NOTE: Mob ID is used also as index in mob db #define MIN_MOB_DB 1000
struct mob_db *mob_db_data[MAX_MOB_DB+1]; #define MAX_MOB_DB 3999
struct mob_db *mob_dummy = NULL; //Dummy mob to be returned when a non-existant one is requested. #define MIN_MOB_DB2 20020
#define MAX_MOB_DB2 31999
struct mob_db *mob_db(int mob_id) { if (mob_id < 0 || mob_id > MAX_MOB_DB || mob_db_data[mob_id] == NULL) return mob_dummy; return mob_db_data[mob_id]; } // These define the range of available IDs for clones. [Valaris]
#define MOB_CLONE_START MAX_MOB_DB
#define MOB_CLONE_END MIN_MOB_DB2
//Dynamic mob database
std::map<uint16, struct mob_db> mob_db_data;
struct mob_db *mob_db( int mob_id ){
try{
return &mob_db_data.at(mob_id);
}catch( std::out_of_range ){
return NULL;
}
}
// holds Monster Spawn informations // holds Monster Spawn informations
std::unordered_map<uint16, std::vector<spawn_info>> mob_spawn_data; std::unordered_map<uint16, std::vector<spawn_info>> mob_spawn_data;
//Dynamic mob chat database //Dynamic mob chat database
struct mob_chat *mob_chat_db[MAX_MOB_CHAT+1]; std::map<short,struct mob_chat> mob_chat_db;
struct mob_chat *mob_chat(short id) { if(id<=0 || id>MAX_MOB_CHAT || mob_chat_db[id]==NULL) return (struct mob_chat*)NULL; return mob_chat_db[id]; } struct mob_chat *mob_chat(short id) {
try{
return &mob_chat_db.at(id);
}catch( std::out_of_range ){
return NULL;
}
}
//Dynamic item drop ratio database for per-item drop ratio modifiers overriding global drop ratios. //Dynamic item drop ratio database for per-item drop ratio modifiers overriding global drop ratios.
#define MAX_ITEMRATIO_MOBS 10 #define MAX_ITEMRATIO_MOBS 10
@ -94,14 +114,6 @@ struct s_mob_skill {
}; };
static DBMap *mob_skill_db; /// Monster skill temporary db. s_mob_skill -> mobid static DBMap *mob_skill_db; /// Monster skill temporary db. s_mob_skill -> mobid
struct mob_db *mobdb_exists(uint16 mob_id) {
struct mob_db *db = mob_db(mob_id);
if (db == mob_dummy)
return NULL;
return db;
}
static struct eri *item_drop_ers; //For loot drops delay structures. static struct eri *item_drop_ers; //For loot drops delay structures.
static struct eri *item_drop_list_ers; static struct eri *item_drop_list_ers;
@ -121,7 +133,6 @@ static DBMap *mob_summon_db; /// Random Summon DB. struct s_randomsummon_group -
/*========================================== /*==========================================
* Local prototype declaration (only required thing) * Local prototype declaration (only required thing)
*------------------------------------------*/ *------------------------------------------*/
static int mob_makedummymobdb(int);
static int mob_spawn_guardian_sub(int tid, unsigned int tick, int id, intptr_t data); static int mob_spawn_guardian_sub(int tid, unsigned int tick, int id, intptr_t data);
int mob_skill_id2skill_idx(int mob_id,uint16 skill_id); int mob_skill_id2skill_idx(int mob_id,uint16 skill_id);
@ -315,7 +326,8 @@ static bool mobdb_searchname_sub(uint16 mob_id, const char * const str, bool ful
*/ */
uint16 mobdb_searchname_(const char * const str, bool full_cmp) uint16 mobdb_searchname_(const char * const str, bool full_cmp)
{ {
for(uint16 mob_id = 0; mob_id <= MAX_MOB_DB; mob_id++) { for( auto const &mobdb_pair : mob_db_data ) {
const uint16 mob_id = mobdb_pair.first;
if( mobdb_searchname_sub(mob_id, str, full_cmp) ) if( mobdb_searchname_sub(mob_id, str, full_cmp) )
return mob_id; return mob_id;
} }
@ -332,12 +344,15 @@ uint16 mobdb_searchname(const char * const str)
int mobdb_searchname_array_(const char *str, uint16 * out, int size, bool full_cmp) int mobdb_searchname_array_(const char *str, uint16 * out, int size, bool full_cmp)
{ {
unsigned short count = 0; unsigned short count = 0;
for(uint16 mob_id = 0; mob_id <= MAX_MOB_DB && count < size; mob_id++) { for( auto const &mobdb_pair : mob_db_data ) {
const uint16 mob_id = mobdb_pair.first;
if( mobdb_searchname_sub(mob_id, str, full_cmp) ) { if( mobdb_searchname_sub(mob_id, str, full_cmp) ) {
out[count] = mob_id; if( count < size )
out[count] = mob_id;
count++; count++;
} }
} }
return count; return count;
} }
@ -351,7 +366,7 @@ int mobdb_searchname_array(const char *str, uint16 * out, int size)
*------------------------------------------*/ *------------------------------------------*/
int mobdb_checkid(const int id) int mobdb_checkid(const int id)
{ {
if (mob_db(id) == mob_dummy) if (mob_db(id) == NULL)
return 0; return 0;
if (mob_is_clone(id)) //checkid is used mostly for random ID based code, therefore clone mobs are out of the question. if (mob_is_clone(id)) //checkid is used mostly for random ID based code, therefore clone mobs are out of the question.
return 0; return 0;
@ -363,9 +378,13 @@ int mobdb_checkid(const int id)
*------------------------------------------*/ *------------------------------------------*/
struct view_data * mob_get_viewdata(int mob_id) struct view_data * mob_get_viewdata(int mob_id)
{ {
if (mob_db(mob_id) == mob_dummy) struct mob_db* db = mob_db(mob_id);
if( db == NULL ){
return NULL; return NULL;
return &mob_db(mob_id)->vd; }
return &db->vd;
} }
/** /**
@ -507,7 +526,7 @@ int mob_get_random_id(int type, int flag, int lv)
mob_id = entry->mob_id; mob_id = entry->mob_id;
mob = mob_db(mob_id); mob = mob_db(mob_id);
} while ((rand == 0 || // Skip default first } while ((rand == 0 || // Skip default first
mob == mob_dummy || mob == NULL ||
mob_is_clone(mob_id) || mob_is_clone(mob_id) ||
(flag&0x01 && (entry->rate < 1000000 && entry->rate <= rnd() % 1000000)) || (flag&0x01 && (entry->rate < 1000000 && entry->rate <= rnd() % 1000000)) ||
(flag&0x02 && lv < mob->lv) || (flag&0x02 && lv < mob->lv) ||
@ -2954,7 +2973,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
} }
if (sd) { if (sd) {
struct mob_db *mission_mdb = mobdb_exists(sd->mission_mobid); struct mob_db *mission_mdb = mob_db(sd->mission_mobid);
if ((sd->mission_mobid == md->mob_id) || if ((sd->mission_mobid == md->mob_id) ||
(battle_config.taekwon_mission_mobname == 1 && mission_mdb && status_get_race2(&md->bl) == RC2_GOBLIN && mission_mdb->race2 == RC2_GOBLIN) || (battle_config.taekwon_mission_mobname == 1 && mission_mdb && status_get_race2(&md->bl) == RC2_GOBLIN && mission_mdb->race2 == RC2_GOBLIN) ||
@ -3789,7 +3808,7 @@ int mob_is_clone(int mob_id)
{ {
if(mob_id < MOB_CLONE_START || mob_id > MOB_CLONE_END) if(mob_id < MOB_CLONE_START || mob_id > MOB_CLONE_END)
return 0; return 0;
if (mob_db(mob_id) == mob_dummy) if (mob_db(mob_id) == NULL)
return 0; return 0;
return mob_id; return mob_id;
} }
@ -3828,11 +3847,17 @@ int mob_clone_spawn(struct map_session_data *sd, int16 m, int16 x, int16 y, cons
if(pc_isdead(sd) && master_id && flag&1) if(pc_isdead(sd) && master_id && flag&1)
return 0; return 0;
ARR_FIND( MOB_CLONE_START, MOB_CLONE_END, mob_id, mob_db_data[mob_id] == NULL ); ARR_FIND( MOB_CLONE_START, MOB_CLONE_END, mob_id, mob_db(mob_id) == NULL );
if(mob_id >= MOB_CLONE_END) if(mob_id >= MOB_CLONE_END)
return 0; return 0;
db = mob_db_data[mob_id]=(struct mob_db*)aCalloc(1, sizeof(struct mob_db)); try{
db = &mob_db_data[mob_id];
}catch( std::bad_alloc ){
ShowError( "mob_clone_spawn: Memory allocation for clone %hu failed.\n", mob_id );
return 0;
}
status = &db->status; status = &db->status;
strcpy(db->sprite,sd->status.name); strcpy(db->sprite,sd->status.name);
strcpy(db->name,sd->status.name); strcpy(db->name,sd->status.name);
@ -4012,69 +4037,19 @@ int mob_clone_spawn(struct map_session_data *sd, int16 m, int16 x, int16 y, cons
return md->bl.id; return md->bl.id;
} }
int mob_clone_delete(struct mob_data *md) int mob_clone_delete(struct mob_data *md){
{
const int mob_id = md->mob_id; const int mob_id = md->mob_id;
if (mob_id >= MOB_CLONE_START && mob_id < MOB_CLONE_END if (mob_id >= MOB_CLONE_START && mob_id < MOB_CLONE_END
&& mob_db_data[mob_id]!=NULL) { && mob_db(mob_id)!=NULL) {
aFree(mob_db_data[mob_id]); mob_db_data.erase(mob_id);
mob_db_data[mob_id]=NULL;
//Clear references to the db //Clear references to the db
md->db = mob_dummy; md->db = NULL;
md->vd = NULL; md->vd = NULL;
return 1; return 1;
} }
return 0; return 0;
} }
//
// Initialization
//
/*==========================================
* Since un-setting [ mob ] up was used, it is an initial provisional value setup.
*------------------------------------------*/
static int mob_makedummymobdb(int mob_id)
{
if (mob_dummy != NULL)
{
if (mob_db(mob_id) == mob_dummy)
return 1; //Using the mob_dummy data already. [Skotlex]
if (mob_id > 0 && mob_id <= MAX_MOB_DB)
{ //Remove the mob data so that it uses the dummy data instead.
aFree(mob_db_data[mob_id]);
mob_db_data[mob_id] = NULL;
}
return 0;
}
//Initialize dummy data.
mob_dummy = (struct mob_db*)aCalloc(1, sizeof(struct mob_db)); //Initializing the dummy mob.
sprintf(mob_dummy->sprite,"DUMMY");
sprintf(mob_dummy->name,"Dummy");
sprintf(mob_dummy->jname,"Dummy");
mob_dummy->lv=1;
mob_dummy->status.max_hp=1000;
mob_dummy->status.max_sp=1;
mob_dummy->status.rhw.range=1;
mob_dummy->status.rhw.atk=7;
mob_dummy->status.rhw.atk2=10;
mob_dummy->status.str=1;
mob_dummy->status.agi=1;
mob_dummy->status.vit=1;
mob_dummy->status.int_=1;
mob_dummy->status.dex=6;
mob_dummy->status.luk=2;
mob_dummy->status.speed=300;
mob_dummy->status.adelay=1000;
mob_dummy->status.amotion=500;
mob_dummy->status.dmotion=500;
mob_dummy->base_exp=2;
mob_dummy->job_exp=1;
mob_dummy->range2=10;
mob_dummy->range3=10;
return 0;
}
//Adjusts the drop rate of item according to the criteria given. [Skotlex] //Adjusts the drop rate of item according to the criteria given. [Skotlex]
static unsigned int mob_drop_adjust(int baserate, int rate_adjust, unsigned short rate_min, unsigned short rate_max) static unsigned int mob_drop_adjust(int baserate, int rate_adjust, unsigned short rate_min, unsigned short rate_max)
{ {
@ -4126,39 +4101,29 @@ static bool mob_parse_dbrow(char** str)
mob_id = atoi(str[0]); mob_id = atoi(str[0]);
if (mob_id <= 1000 || mob_id > MAX_MOB_DB) { if (!((mob_id > MIN_MOB_DB && mob_id < MAX_MOB_DB) || (mob_id > MIN_MOB_DB2 && mob_id < MAX_MOB_DB2))) {
ShowError("mob_parse_dbrow: Invalid monster ID %d, must be in range %d-%d.\n", mob_id, 1000, MAX_MOB_DB); ShowError("mob_parse_dbrow: Invalid monster ID %d, must be in range %d-%d or %d-%d.\n", mob_id, MIN_MOB_DB, MAX_MOB_DB);
return false;
}
if (pcdb_checkid(mob_id)) {
ShowError("mob_parse_dbrow: Invalid monster ID %d, reserved for player classes.\n", mob_id);
return false;
}
if (mob_id >= MOB_CLONE_START && mob_id < 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", mob_id, MOB_CLONE_START, MOB_CLONE_END-1, MAX_MOB_DB);
return false; return false;
} }
memset(&entry, 0, sizeof(entry)); memset(&entry, 0, sizeof(entry));
db = &entry; status = &entry.status;
status = &db->status;
db->vd.class_ = mob_id; entry.vd.class_ = mob_id;
safestrncpy(db->sprite, str[1], sizeof(db->sprite)); safestrncpy(entry.sprite, str[1], sizeof(entry.sprite));
safestrncpy(db->jname, str[2], sizeof(db->jname)); safestrncpy(entry.jname, str[2], sizeof(entry.jname));
safestrncpy(db->name, str[3], sizeof(db->name)); safestrncpy(entry.name, str[3], sizeof(entry.name));
db->lv = atoi(str[4]); entry.lv = atoi(str[4]);
db->lv = cap_value(db->lv, 1, USHRT_MAX); entry.lv = cap_value(entry.lv, 1, USHRT_MAX);
status->max_hp = atoi(str[5]); status->max_hp = atoi(str[5]);
status->max_sp = atoi(str[6]); status->max_sp = atoi(str[6]);
exp = (double)atoi(str[7]) * (double)battle_config.base_exp_rate / 100.; exp = (double)atoi(str[7]) * (double)battle_config.base_exp_rate / 100.;
db->base_exp = (unsigned int)cap_value(exp, 0, UINT_MAX); entry.base_exp = (unsigned int)cap_value(exp, 0, UINT_MAX);
exp = (double)atoi(str[8]) * (double)battle_config.job_exp_rate / 100.; exp = (double)atoi(str[8]) * (double)battle_config.job_exp_rate / 100.;
db->job_exp = (unsigned int)cap_value(exp, 0, UINT_MAX); entry.job_exp = (unsigned int)cap_value(exp, 0, UINT_MAX);
status->rhw.range = atoi(str[9]); status->rhw.range = atoi(str[9]);
status->rhw.atk = atoi(str[10]); status->rhw.atk = atoi(str[10]);
@ -4179,20 +4144,20 @@ static bool mob_parse_dbrow(char** str)
if (status->dex < 1) status->dex = 1; if (status->dex < 1) status->dex = 1;
if (status->luk < 1) status->luk = 1; if (status->luk < 1) status->luk = 1;
db->range2 = atoi(str[20]); entry.range2 = atoi(str[20]);
db->range3 = atoi(str[21]); entry.range3 = atoi(str[21]);
if (battle_config.view_range_rate != 100) { if (battle_config.view_range_rate != 100) {
db->range2 = db->range2 * battle_config.view_range_rate / 100; entry.range2 = entry.range2 * battle_config.view_range_rate / 100;
if (db->range2 < 1) if (entry.range2 < 1)
db->range2 = 1; entry.range2 = 1;
} }
if (battle_config.chase_range_rate != 100) { if (battle_config.chase_range_rate != 100) {
db->range3 = db->range3 * battle_config.chase_range_rate / 100; entry.range3 = entry.range3 * battle_config.chase_range_rate / 100;
if (db->range3 < db->range2) if (entry.range3 < entry.range2)
db->range3 = db->range2; entry.range3 = entry.range2;
} }
//Tests showed that chase range is effectively 2 cells larger than expected [Playtester] //Tests showed that chase range is effectively 2 cells larger than expected [Playtester]
db->range3 += 2; entry.range3 += 2;
status->size = atoi(str[22]); status->size = atoi(str[22]);
status->race = atoi(str[23]); status->race = atoi(str[23]);
@ -4234,18 +4199,18 @@ static bool mob_parse_dbrow(char** str)
// Fill in remaining status data by using a dummy monster. // Fill in remaining status data by using a dummy monster.
data.bl.type = BL_MOB; data.bl.type = BL_MOB;
data.level = db->lv; data.level = entry.lv;
memcpy(&data.status, status, sizeof(struct status_data)); memcpy(&data.status, status, sizeof(struct status_data));
status_calc_misc(&data.bl, status, db->lv); status_calc_misc(&data.bl, status, entry.lv);
// MVP EXP Bonus: MEXP // MVP EXP Bonus: MEXP
// Some new MVP's MEXP multipled by high exp-rate cause overflow. [LuzZza] // 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.; exp = (double)atoi(str[30]) * (double)battle_config.mvp_exp_rate / 100.;
db->mexp = (unsigned int)cap_value(exp, 0, UINT_MAX); entry.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] //Now that we know if it is an mvp or not, apply battle_config modifiers [Skotlex]
maxhp = (double)status->max_hp; maxhp = (double)status->max_hp;
if (db->mexp > 0) { //Mvp if (entry.mexp > 0) { //Mvp
if (battle_config.mvp_hp_rate != 100) if (battle_config.mvp_hp_rate != 100)
maxhp = maxhp * (double)battle_config.mvp_hp_rate / 100.; maxhp = maxhp * (double)battle_config.mvp_hp_rate / 100.;
} else //Normal mob } else //Normal mob
@ -4261,46 +4226,54 @@ static bool mob_parse_dbrow(char** str)
// MVP Drops: MVP1id,MVP1per,MVP2id,MVP2per,MVP3id,MVP3per // MVP Drops: MVP1id,MVP1per,MVP2id,MVP2per,MVP3id,MVP3per
for(i = 0; i < MAX_MVP_DROP; i++) { for(i = 0; i < MAX_MVP_DROP; i++) {
db->mvpitem[i].nameid = atoi(str[31+i*2]); entry.mvpitem[i].nameid = atoi(str[31+i*2]);
if( db->mvpitem[i].nameid ){ if( entry.mvpitem[i].nameid ){
if( itemdb_search(db->mvpitem[i].nameid) ){ if( itemdb_search(entry.mvpitem[i].nameid) ){
db->mvpitem[i].p = atoi(str[32+i*2]); entry.mvpitem[i].p = atoi(str[32+i*2]);
continue; continue;
}else{ }else{
ShowWarning( "Monster \"%s\"(id: %d) is dropping an unknown item \"%s\"(MVP-Drop %d)\n", db->name, mob_id, str[31+i*2], ( i / 2 ) + 1 ); ShowWarning( "Monster \"%s\"(id: %d) is dropping an unknown item \"%s\"(MVP-Drop %d)\n", entry.name, mob_id, str[31+i*2], ( i / 2 ) + 1 );
} }
} }
// Delete the item // Delete the item
db->mvpitem[i].nameid = 0; entry.mvpitem[i].nameid = 0;
db->mvpitem[i].p = 0; entry.mvpitem[i].p = 0;
} }
for(i = 0; i < MAX_MOB_DROP; i++) { for(i = 0; i < MAX_MOB_DROP; i++) {
int k = 31 + MAX_MVP_DROP*2 + i*2; int k = 31 + MAX_MVP_DROP*2 + i*2;
db->dropitem[i].nameid = atoi(str[k]); entry.dropitem[i].nameid = atoi(str[k]);
if( db->dropitem[i].nameid ){ if( entry.dropitem[i].nameid ){
if( itemdb_search( db->dropitem[i].nameid ) ){ if( itemdb_search( entry.dropitem[i].nameid ) ){
db->dropitem[i].p = atoi(str[k+1]); entry.dropitem[i].p = atoi(str[k+1]);
continue; continue;
}else{ }else{
ShowWarning( "Monster \"%s\"(id: %d) is dropping an unknown item \"%s\"(Drop %d)\n", db->name, mob_id, str[k], ( i / 2 ) + 1 ); ShowWarning( "Monster \"%s\"(id: %d) is dropping an unknown item \"%s\"(Drop %d)\n", entry.name, mob_id, str[k], ( i / 2 ) + 1 );
} }
} }
// Delete the item // Delete the item
db->dropitem[i].nameid = 0; entry.dropitem[i].nameid = 0;
db->dropitem[i].p = 0; entry.dropitem[i].p = 0;
} }
// Finally insert monster's data into the database. db = mob_db(mob_id);
if (mob_db_data[mob_id] == NULL)
mob_db_data[mob_id] = (struct mob_db*)aCalloc(1, sizeof(struct mob_db));
memcpy(mob_db_data[mob_id], db, sizeof(struct mob_db)); // Finally insert monster's data into the database.
if (db == NULL) {
try{
db = &mob_db_data[mob_id];
}catch( std::bad_alloc ){
ShowError( "Memory allocation for monster %hu failed.\n", mob_id );
return false;
}
}
memcpy(db, &entry, sizeof(struct mob_db));
return true; return true;
} }
@ -4371,10 +4344,11 @@ static int mob_read_sqldb(void)
static bool mob_readdb_mobavail(char* str[], int columns, int current) static bool mob_readdb_mobavail(char* str[], int columns, int current)
{ {
int mob_id, sprite_id; int mob_id, sprite_id;
struct mob_db *db;
mob_id = atoi(str[0]); mob_id = atoi(str[0]);
if(mob_db(mob_id) == mob_dummy) // invalid class (probably undefined in db) if( (db=mob_db(mob_id)) == NULL) // invalid class (probably undefined in db)
{ {
ShowWarning("mob_readdb_mobavail: Unknown mob id %d.\n", mob_id); ShowWarning("mob_readdb_mobavail: Unknown mob id %d.\n", mob_id);
return false; return false;
@ -4382,31 +4356,31 @@ static bool mob_readdb_mobavail(char* str[], int columns, int current)
sprite_id = atoi(str[1]); sprite_id = atoi(str[1]);
memset(&mob_db_data[mob_id]->vd, 0, sizeof(struct view_data)); memset(&db->vd, 0, sizeof(struct view_data));
mob_db_data[mob_id]->vd.class_ = sprite_id; db->vd.class_ = sprite_id;
//Player sprites //Player sprites
if(pcdb_checkid(sprite_id) && columns==12) { if(pcdb_checkid(sprite_id) && columns==12) {
mob_db_data[mob_id]->vd.sex=atoi(str[2]); db->vd.sex=atoi(str[2]);
mob_db_data[mob_id]->vd.hair_style=atoi(str[3]); db->vd.hair_style=atoi(str[3]);
mob_db_data[mob_id]->vd.hair_color=atoi(str[4]); db->vd.hair_color=atoi(str[4]);
mob_db_data[mob_id]->vd.weapon=atoi(str[5]); db->vd.weapon=atoi(str[5]);
mob_db_data[mob_id]->vd.shield=atoi(str[6]); db->vd.shield=atoi(str[6]);
mob_db_data[mob_id]->vd.head_top=atoi(str[7]); db->vd.head_top=atoi(str[7]);
mob_db_data[mob_id]->vd.head_mid=atoi(str[8]); db->vd.head_mid=atoi(str[8]);
mob_db_data[mob_id]->vd.head_bottom=atoi(str[9]); db->vd.head_bottom=atoi(str[9]);
mob_db_data[mob_id]->option=atoi(str[10])&~(OPTION_HIDE|OPTION_CLOAK|OPTION_INVISIBLE); db->option=atoi(str[10])&~(OPTION_HIDE|OPTION_CLOAK|OPTION_INVISIBLE);
mob_db_data[mob_id]->vd.cloth_color=atoi(str[11]); // Monster player dye option - Valaris db->vd.cloth_color=atoi(str[11]); // Monster player dye option - Valaris
#ifdef NEW_CARTS #ifdef NEW_CARTS
if( mob_db_data[mob_id]->option & OPTION_CART ){ if( db->option & OPTION_CART ){
ShowWarning("mob_readdb_mobavail: You tried to use a cart for mob id %d. This does not work with setting an option anymore.\n", mob_id ); ShowWarning("mob_readdb_mobavail: You tried to use a cart for mob id %d. This does not work with setting an option anymore.\n", mob_id );
mob_db_data[mob_id]->option &= ~OPTION_CART; db->option &= ~OPTION_CART;
} }
#endif #endif
} }
else if(columns==3) else if(columns==3)
mob_db_data[mob_id]->vd.head_bottom=atoi(str[2]); // mob equipment [Valaris] db->vd.head_bottom=atoi(str[2]); // mob equipment [Valaris]
else if( columns != 2 ) else if( columns != 2 )
return false; return false;
@ -4431,13 +4405,13 @@ static bool mob_readdb_group(char* str[], int columns, int current){
} }
mob_id = atoi(str[1]); mob_id = atoi(str[1]);
if (mob_id != 0 && mob_db(mob_id) == mob_dummy) { if (mob_id != 0 && mob_db(mob_id) == NULL) {
ShowError("mob_readdb_group: Invalid random monster group '%s'\n", str[0]); ShowError("mob_readdb_group: Invalid random monster group '%s'\n", str[0]);
return false; return false;
} }
else if (mob_id == 0){ else if (mob_id == 0){
mob_id = atoi(str[3]); mob_id = atoi(str[3]);
if (mob_db(mob_id) == mob_dummy) { if (mob_db(mob_id) == NULL) {
ShowError("mob_readdb_group: Invalid random monster group '%s'\n", str[0]); ShowError("mob_readdb_group: Invalid random monster group '%s'\n", str[0]);
return false; return false;
} }
@ -4474,15 +4448,22 @@ static bool mob_parse_row_chatdb(char* fields[], int columns, int current)
msg_id = atoi(fields[0]); msg_id = atoi(fields[0]);
if (msg_id <= 0 || msg_id > MAX_MOB_CHAT){ if (msg_id <= 0){
ShowError("mob_parse_row_chatdb: Invalid chat ID '%d' in line %d\n", msg_id, current); ShowError("mob_parse_row_chatdb: Invalid chat ID '%d' in line %d\n", msg_id, current);
return false; return false;
} }
if (mob_chat_db[msg_id] == NULL) ms = mob_chat(msg_id);
mob_chat_db[msg_id] = (struct mob_chat*)aCalloc(1, sizeof (struct mob_chat));
if( ms == NULL ){
try{
ms = &mob_chat_db[msg_id];
}catch( std::bad_alloc ){
ShowError( "mob_parse_row_chatdb: Memory allocation for chat ID '%d' failed.\n", msg_id );
return false;
}
}
ms = mob_chat_db[msg_id];
//MSG ID //MSG ID
ms->msg_id=msg_id; ms->msg_id=msg_id;
//Color //Color
@ -4595,10 +4576,11 @@ static bool mob_parse_row_mobskilldb(char** str, int columns, int current)
struct mob_skill *ms = NULL; struct mob_skill *ms = NULL;
int mob_id; int mob_id;
int i = 0, j, tmp; int i = 0, j, tmp;
struct mob_db *mob;
mob_id = atoi(str[0]); mob_id = atoi(str[0]);
if (mob_id > 0 && mob_db(mob_id) == mob_dummy) if (mob_id > 0 && (mob = mob_db(mob_id)) == NULL)
{ {
if (mob_id != last_mob_id) { if (mob_id != last_mob_id) {
ShowError("mob_parse_row_mobskilldb: Non existant Mob id %d\n", mob_id); ShowError("mob_parse_row_mobskilldb: Non existant Mob id %d\n", mob_id);
@ -4624,7 +4606,7 @@ static bool mob_parse_row_mobskilldb(char** str, int columns, int current)
if( i == MAX_MOBSKILL ) if( i == MAX_MOBSKILL )
{ {
if (mob_id != last_mob_id) { if (mob_id != last_mob_id) {
ShowError("mob_parse_row_mobskilldb: Too many skills for monster %d[%s]\n", mob_id, mob_db_data[mob_id]->sprite); ShowError("mob_parse_row_mobskilldb: Too many skills for monster %d[%s]\n", mob_id, mob->sprite);
last_mob_id = mob_id; last_mob_id = mob_id;
} }
return false; return false;
@ -4648,7 +4630,7 @@ static bool mob_parse_row_mobskilldb(char** str, int columns, int current)
if (mob_id < 0) if (mob_id < 0)
ShowError("mob_parse_row_mobskilldb: Invalid Skill ID (%d) for all mobs\n", j); ShowError("mob_parse_row_mobskilldb: Invalid Skill ID (%d) for all mobs\n", j);
else else
ShowError("mob_parse_row_mobskilldb: Invalid Skill ID (%d) for mob %d (%s)\n", j, mob_id, mob_db_data[mob_id]->sprite); ShowError("mob_parse_row_mobskilldb: Invalid Skill ID (%d) for mob %d (%s)\n", j, mob_id, mob->sprite);
return false; return false;
} }
ms->skill_id = j; ms->skill_id = j;
@ -4693,13 +4675,13 @@ static bool mob_parse_row_mobskilldb(char** str, int columns, int current)
{ {
ShowWarning("mob_parse_row_mobskilldb: Wrong mob skill target for ground skill %d (%s) for %s.\n", ShowWarning("mob_parse_row_mobskilldb: Wrong mob skill target for ground skill %d (%s) for %s.\n",
ms->skill_id, skill_get_name(ms->skill_id), ms->skill_id, skill_get_name(ms->skill_id),
mob_id < 0 ? "all mobs" : mob_db_data[mob_id]->sprite); mob_id < 0 ? "all mobs" : mob->sprite);
ms->target = MST_TARGET; ms->target = MST_TARGET;
} }
} else if (ms->target > MST_MASTER) { } 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", ShowWarning("mob_parse_row_mobskilldb: Wrong mob skill target 'around' for non-ground skill %d (%s) for %s.\n",
ms->skill_id, skill_get_name(ms->skill_id), ms->skill_id, skill_get_name(ms->skill_id),
mob_id < 0 ? "all mobs" : mob_db_data[mob_id]->sprite); mob_id < 0 ? "all mobs" : mob->sprite);
ms->target = MST_TARGET; ms->target = MST_TARGET;
} }
@ -4746,7 +4728,7 @@ static bool mob_parse_row_mobskilldb(char** str, int columns, int current)
else else
ms->emotion = -1; ms->emotion = -1;
if(str[18] != NULL && mob_chat_db[atoi(str[18])]!=NULL) if(str[18] != NULL && mob_chat(atoi(str[18]))!=NULL)
ms->msg_id = atoi(str[18]); ms->msg_id = atoi(str[18]);
else else
ms->msg_id = 0; ms->msg_id = 0;
@ -4845,18 +4827,19 @@ static bool mob_readdb_race2(char* fields[], int columns, int current)
for(i = 1; i < columns; i++) { for(i = 1; i < columns; i++) {
int mob_id = atoi(fields[i]); int mob_id = atoi(fields[i]);
struct mob_db* db = mob_db(mob_id);
if (mob_db(mob_id) == mob_dummy) { if (db == NULL) {
ShowWarning("mob_readdb_race2: Unknown mob id %d for race2 %d.\n", mob_id, race); ShowWarning("mob_readdb_race2: Unknown mob id %d for race2 %d.\n", mob_id, race);
continue; continue;
} }
mob_db_data[mob_id]->race2 = (enum e_race2)race; db->race2 = (enum e_race2)race;
// Apply Aegis Class // Apply Aegis Class
if (race == RC2_GUARDIAN) if (race == RC2_GUARDIAN)
mob_db_data[mob_id]->status.class_ = CLASS_GUARDIAN; db->status.class_ = CLASS_GUARDIAN;
else if (race == RC2_BATTLEFIELD) else if (race == RC2_BATTLEFIELD)
mob_db_data[mob_id]->status.class_ = CLASS_BATTLEFIELD; db->status.class_ = CLASS_BATTLEFIELD;
} }
return true; return true;
} }
@ -4885,7 +4868,7 @@ static bool mob_readdb_itemratio(char* str[], int columns, int current)
memset(item_ratio->mob_id, 0, sizeof(item_ratio->mob_id)); memset(item_ratio->mob_id, 0, sizeof(item_ratio->mob_id));
for (i = 0; i < columns-2; i++) { for (i = 0; i < columns-2; i++) {
uint16 mob_id = atoi(str[i+2]); uint16 mob_id = atoi(str[i+2]);
if (mob_db(mob_id) == mob_dummy) if (mob_db(mob_id) == NULL)
ShowError("mob_readdb_itemratio: Invalid monster with ID %hu (Item:%hu Col:%d).\n", mob_id, nameid, columns); ShowError("mob_readdb_itemratio: Invalid monster with ID %hu (Item:%hu Col:%d).\n", mob_id, nameid, columns);
else else
item_ratio->mob_id[i] = atoi(str[i+2]); item_ratio->mob_id[i] = atoi(str[i+2]);
@ -4910,7 +4893,7 @@ static bool mob_readdb_drop(char* str[], int columns, int current) {
struct s_mob_drop *drop; struct s_mob_drop *drop;
mobid = atoi(str[0]); mobid = atoi(str[0]);
if ((mob = mob_db(mobid)) == mob_dummy) { if ((mob = mob_db(mobid)) == NULL) {
ShowError("mob_readdb_drop: Invalid monster with ID %s.\n", str[0]); ShowError("mob_readdb_drop: Invalid monster with ID %s.\n", str[0]);
return false; return false;
} }
@ -4987,23 +4970,19 @@ static int mob_item_drop_ratio_free(DBKey key, DBData *data, va_list ap) {
* Adjust drop ratio for each monster * Adjust drop ratio for each monster
**/ **/
static void mob_drop_ratio_adjust(void){ static void mob_drop_ratio_adjust(void){
unsigned short i; for( auto &pair : mob_db_data ){
for( i = 0; i <= MAX_MOB_DB; i++ ){
struct mob_db *mob; struct mob_db *mob;
struct item_data *id; struct item_data *id;
unsigned short nameid; unsigned short nameid;
int j, rate, rate_adjust = 0, mob_id; int j, rate, rate_adjust = 0, mob_id;
mob = mob_db(i); mob_id = pair.first;
mob = &pair.second;
// Skip dummy mobs. if( mob_is_clone( mob_id ) ){
if( mob == mob_dummy ){
continue; continue;
} }
mob_id = i;
for( j = 0; j < MAX_MVP_DROP_TOTAL; j++ ){ for( j = 0; j < MAX_MVP_DROP_TOTAL; j++ ){
nameid = mob->mvpitem[j].nameid; nameid = mob->mvpitem[j].nameid;
rate = mob->mvpitem[j].p; rate = mob->mvpitem[j].p;
@ -5026,7 +5005,7 @@ static void mob_drop_ratio_adjust(void){
// Item is not known anymore(should never happen) // Item is not known anymore(should never happen)
if( !id ){ if( !id ){
ShowWarning( "Monster \"%s\"(id:%d) is dropping an unknown item(id: %d)\n", mob->name, mob_id, nameid ); ShowWarning( "Monster \"%s\"(id:%hu) is dropping an unknown item(id: %d)\n", mob->name, mob_id, nameid );
mob->mvpitem[j].nameid = 0; mob->mvpitem[j].nameid = 0;
mob->mvpitem[j].p = 0; mob->mvpitem[j].p = 0;
continue; continue;
@ -5056,7 +5035,7 @@ static void mob_drop_ratio_adjust(void){
// Item is not known anymore(should never happen) // Item is not known anymore(should never happen)
if( !id ){ if( !id ){
ShowWarning( "Monster \"%s\"(id:%d) is dropping an unknown item(id: %d)\n", mob->name, mob_id, nameid ); ShowWarning( "Monster \"%s\"(id:%hu) is dropping an unknown item(id: %d)\n", mob->name, mob_id, nameid );
mob->dropitem[j].nameid = 0; mob->dropitem[j].nameid = 0;
mob->dropitem[j].p = 0; mob->dropitem[j].p = 0;
continue; continue;
@ -5172,30 +5151,27 @@ static void mob_skill_db_set_single_sub(struct mob_db *mob, struct s_mob_skill *
* @param skill * @param skill
**/ **/
static void mob_skill_db_set_single(struct s_mob_skill *skill) { static void mob_skill_db_set_single(struct s_mob_skill *skill) {
struct mob_db *mob = NULL;
nullpo_retv(skill); nullpo_retv(skill);
// Specific monster // Specific monster
if (skill->mob_id >= 0) { if (skill->mob_id >= 0) {
mob = mob_db(skill->mob_id); struct mob_db *mob = mob_db(skill->mob_id);
if (mob != mob_dummy) if (mob != NULL)
//memcpy(&mob->skill, skill, sizeof(skill));
mob_skill_db_set_single_sub(mob, skill); mob_skill_db_set_single_sub(mob, skill);
} }
// Global skill // Global skill
else { else {
uint16 i, id = skill->mob_id; uint16 id = skill->mob_id;
id *= -1; id *= -1;
for (i = 0; i < MAX_MOB_DB; i++) { for( auto &pair : mob_db_data ){
mob = mob_db(i); if ( mob_is_clone(pair.first) ){
if (mob == mob_dummy)
continue; continue;
if ( (!(id&1) && status_has_mode(&mob->status,MD_STATUS_IMMUNE)) // Bosses }
|| (!(id&2) && !status_has_mode(&mob->status,MD_STATUS_IMMUNE)) // Normal monsters if ( (!(id&1) && status_has_mode(&pair.second.status,MD_STATUS_IMMUNE)) // Bosses
|| (!(id&2) && !status_has_mode(&pair.second.status,MD_STATUS_IMMUNE)) // Normal monsters
) )
continue; continue;
mob_skill_db_set_single_sub(mob, skill); mob_skill_db_set_single_sub(&pair.second, skill);
} }
} }
@ -5299,7 +5275,7 @@ static void mob_load(void)
sv_readdb(dbsubpath1, "mob_avail.txt", ',', 2, 12, -1, &mob_readdb_mobavail,silent); sv_readdb(dbsubpath1, "mob_avail.txt", ',', 2, 12, -1, &mob_readdb_mobavail,silent);
sv_readdb(dbsubpath2, "mob_race2_db.txt", ',', 2, MAX_RACE2_MOBS, -1, &mob_readdb_race2, silent); sv_readdb(dbsubpath2, "mob_race2_db.txt", ',', 2, MAX_RACE2_MOBS, -1, &mob_readdb_race2, silent);
sv_readdb(dbsubpath1, "mob_item_ratio.txt", ',', 2, 2+MAX_ITEMRATIO_MOBS, -1, &mob_readdb_itemratio, silent); sv_readdb(dbsubpath1, "mob_item_ratio.txt", ',', 2, 2+MAX_ITEMRATIO_MOBS, -1, &mob_readdb_itemratio, silent);
sv_readdb(dbsubpath1, "mob_chat_db.txt", '#', 3, 3, MAX_MOB_CHAT, &mob_parse_row_chatdb, silent); sv_readdb(dbsubpath1, "mob_chat_db.txt", '#', 3, 3, -1, &mob_parse_row_chatdb, silent);
sv_readdb(dbsubpath2, "mob_random_db.txt", ',', 4, 4, -1, &mob_readdb_group, silent); sv_readdb(dbsubpath2, "mob_random_db.txt", ',', 4, 4, -1, &mob_readdb_group, silent);
sv_readdb(dbsubpath2, "mob_branch.txt", ',', 4, 4, -1, &mob_readdb_group, silent); sv_readdb(dbsubpath2, "mob_branch.txt", ',', 4, 4, -1, &mob_readdb_group, silent);
sv_readdb(dbsubpath2, "mob_poring.txt", ',', 4, 4, -1, &mob_readdb_group, silent); sv_readdb(dbsubpath2, "mob_poring.txt", ',', 4, 4, -1, &mob_readdb_group, silent);
@ -5320,9 +5296,6 @@ static void mob_load(void)
* Initialize monster data * Initialize monster data
*/ */
void mob_db_load(bool is_reload){ void mob_db_load(bool is_reload){
memset(mob_db_data,0,sizeof(mob_db_data)); //Clear the array
mob_db_data[0] = (struct mob_db*)aCalloc(1, sizeof (struct mob_db)); //This mob is used for random spawns
mob_makedummymobdb(0); //The first time this is invoked, it creates the dummy mob
if( !is_reload ) { if( !is_reload ) {
// on mobdbreload it's not neccessary to execute this // on mobdbreload it's not neccessary to execute this
// item ers needs to be allocated only once // item ers needs to be allocated only once
@ -5335,6 +5308,41 @@ void mob_db_load(bool is_reload){
mob_load(); mob_load();
} }
/**
* Re-link monster drop data with item data
* Fixes the need of a @reloadmobdb after a @reloaditemdb
* @author Epoque
*/
void mob_reload_itemmob_data(void) {
for( auto const &pair : mob_db_data ){
int d, k;
if( mob_is_clone( pair.first ) ){
continue;
}
for(d = 0; d < MAX_MOB_DROP_TOTAL; d++) {
struct item_data *id;
if( !pair.second.dropitem[d].nameid )
continue;
id = itemdb_search(pair.second.dropitem[d].nameid);
for (k = 0; k < MAX_SEARCH; k++) {
if (id->mob[k].chance <= pair.second.dropitem[d].p)
break;
}
if (k == MAX_SEARCH)
continue;
if (id->mob[k].id != pair.first)
memmove(&id->mob[k+1], &id->mob[k], (MAX_SEARCH-k-1)*sizeof(id->mob[0]));
id->mob[k].chance = pair.second.dropitem[d].p;
id->mob[k].id = pair.first;
}
}
}
/** /**
* Apply the proper view data on monsters during mob_db reload. * Apply the proper view data on monsters during mob_db reload.
* @param md: Mob to adjust * @param md: Mob to adjust
@ -5424,28 +5432,9 @@ void do_init_mob(void){
* Clean memory usage. * Clean memory usage.
*------------------------------------------*/ *------------------------------------------*/
void do_final_mob(bool is_reload){ void do_final_mob(bool is_reload){
int i; mob_db_data.clear();
if (mob_dummy) mob_chat_db.clear();
{
aFree(mob_dummy);
mob_dummy = NULL;
}
for (i = 0; i <= MAX_MOB_DB; i++)
{
if (mob_db_data[i] != NULL)
{
aFree(mob_db_data[i]);
mob_db_data[i] = NULL;
}
}
for (i = 0; i <= MAX_MOB_CHAT; i++)
{
if (mob_chat_db[i] != NULL)
{
aFree(mob_chat_db[i]);
mob_chat_db[i] = NULL;
}
}
mob_item_drop_ratio->destroy(mob_item_drop_ratio,mob_item_drop_ratio_free); mob_item_drop_ratio->destroy(mob_item_drop_ratio,mob_item_drop_ratio_free);
mob_skill_db->destroy(mob_skill_db, mob_skill_db_free); mob_skill_db->destroy(mob_skill_db, mob_skill_db_free);
mob_summon_db->destroy(mob_summon_db, mob_summon_db_free); mob_summon_db->destroy(mob_summon_db, mob_summon_db_free);

View File

@ -13,11 +13,6 @@
struct guardian_data; struct guardian_data;
// Change this to increase the table size in your mob_db to accomodate a larger mob database.
// Be sure to note that IDs 4001 to 4048 are reserved for advanced/baby/expanded classes.
// Notice that the last 1000 entries are used for player clones, so always set this to desired value +1000
#define MAX_MOB_DB 5000
//The number of drops all mobs have and the max drop-slot that the steal skill will attempt to steal from. //The number of drops all mobs have and the max drop-slot that the steal skill will attempt to steal from.
#define MAX_MOB_DROP 10 #define MAX_MOB_DROP 10
#define MAX_MVP_DROP 3 #define MAX_MVP_DROP 3
@ -39,10 +34,6 @@ struct guardian_data;
//Distance that slaves should keep from their master. //Distance that slaves should keep from their master.
#define MOB_SLAVEDISTANCE 2 #define MOB_SLAVEDISTANCE 2
// These define the range of available IDs for clones. [Valaris]
#define MOB_CLONE_START (MAX_MOB_DB-999)
#define MOB_CLONE_END MAX_MOB_DB
//Used to determine default enemy type of mobs (for use in eachinrange calls) //Used to determine default enemy type of mobs (for use in eachinrange calls)
#define DEFAULT_ENEMY_TYPE(md) (md->special_state.ai?BL_CHAR:BL_MOB|BL_PC|BL_HOM|BL_MER) #define DEFAULT_ENEMY_TYPE(md) (md->special_state.ai?BL_CHAR:BL_MOB|BL_PC|BL_HOM|BL_MER)
@ -294,7 +285,6 @@ struct item_drop_list {
}; };
struct mob_db *mob_db(int mob_id); struct mob_db *mob_db(int mob_id);
struct mob_db *mobdb_exists(uint16 mob_id);
uint16 mobdb_searchname(const char * const str); uint16 mobdb_searchname(const char * const str);
int mobdb_searchname_array(const char *str, uint16 * out, int size); int mobdb_searchname_array(const char *str, uint16 * out, int size);
int mobdb_checkid(const int id); int mobdb_checkid(const int id);
@ -359,6 +349,7 @@ int mob_is_clone(int mob_id);
int mob_clone_spawn(struct map_session_data *sd, int16 m, int16 x, int16 y, const char *event, int master_id, enum e_mode mode, int flag, unsigned int duration); int mob_clone_spawn(struct map_session_data *sd, int16 m, int16 x, int16 y, const char *event, int master_id, enum e_mode mode, int flag, unsigned int duration);
int mob_clone_delete(struct mob_data *md); int mob_clone_delete(struct mob_data *md);
void mob_reload_itemmob_data(void);
void mob_reload(void); void mob_reload(void);
void mob_add_spawn(uint16 mob_id, const struct spawn_info& new_spawn); void mob_add_spawn(uint16 mob_id, const struct spawn_info& new_spawn);

View File

@ -514,7 +514,7 @@ void quest_read_txtdb(void)
if (!mob_id) if (!mob_id)
continue; continue;
if (mobdb_exists(mob_id) == NULL) { if (mob_db(mob_id) == NULL) {
ShowWarning("quest_read_txtdb: Invalid monster as objective '%d' in line %d.\n", mob_id, ln); ShowWarning("quest_read_txtdb: Invalid monster as objective '%d' in line %d.\n", mob_id, ln);
continue; continue;
} }
@ -529,7 +529,7 @@ void quest_read_txtdb(void)
if (!nameid) if (!nameid)
continue; continue;
if (!itemdb_exists(nameid) || (mob_id && mobdb_exists(mob_id) == NULL)) { if (!itemdb_exists(nameid) || (mob_id && mob_db(mob_id) == NULL)) {
ShowWarning("quest_read_txtdb: Invalid item reward '%d' (mob %d, optional) in line %d.\n", nameid, mob_id, ln); ShowWarning("quest_read_txtdb: Invalid item reward '%d' (mob %d, optional) in line %d.\n", nameid, mob_id, ln);
continue; continue;
} }

View File

@ -17069,7 +17069,7 @@ BUILDIN_FUNC(addmonsterdrop)
if(c) { //Fill in the slot with the item and rate if(c) { //Fill in the slot with the item and rate
mob->dropitem[c].nameid = item_id; mob->dropitem[c].nameid = item_id;
mob->dropitem[c].p = (rate > 10000)?10000:rate; mob->dropitem[c].p = (rate > 10000)?10000:rate;
itemdb_reload_itemmob_data(); // Reload the mob search data stored in the item_data mob_reload_itemmob_data(); // Reload the mob search data stored in the item_data
script_pushint(st,1); script_pushint(st,1);
} else //No place to put the new drop } else //No place to put the new drop
script_pushint(st,0); script_pushint(st,0);
@ -17115,7 +17115,7 @@ BUILDIN_FUNC(delmonsterdrop)
if(mob->dropitem[i].nameid == item_id) { if(mob->dropitem[i].nameid == item_id) {
mob->dropitem[i].nameid = 0; mob->dropitem[i].nameid = 0;
mob->dropitem[i].p = 0; mob->dropitem[i].p = 0;
itemdb_reload_itemmob_data(); // Reload the mob search data stored in the item_data mob_reload_itemmob_data(); // Reload the mob search data stored in the item_data
script_pushint(st,1); script_pushint(st,1);
return SCRIPT_CMD_SUCCESS; return SCRIPT_CMD_SUCCESS;
} }