|
@@ -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]
|
|
|
|
-//!NOTE: Mob ID is used also as index in mob db
|
|
|
|
-struct mob_db *mob_db_data[MAX_MOB_DB+1];
|
|
|
|
-struct mob_db *mob_dummy = NULL; //Dummy mob to be returned when a non-existant one is requested.
|
|
|
|
|
|
+// Limits for the monster database
|
|
|
|
+#define MIN_MOB_DB 1000
|
|
|
|
+#define MAX_MOB_DB 3999
|
|
|
|
+#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];
|
|
|
|
-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]; }
|
|
|
|
|
|
+std::map<short,struct mob_chat> mob_chat_db;
|
|
|
|
+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) {
|
|
|
|
- aFree(mob_db_data[mob_id]);
|
|
|
|
- mob_db_data[mob_id]=NULL;
|
|
|
|
|
|
+ && mob_db(mob_id)!=NULL) {
|
|
|
|
+ mob_db_data.erase(mob_id);
|
|
//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) {
|
|
|
|
- ShowError("mob_parse_dbrow: Invalid monster ID %d, must be in range %d-%d.\n", mob_id, 1000, 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);
|
|
|
|
|
|
+ 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 or %d-%d.\n", mob_id, MIN_MOB_DB, MAX_MOB_DB);
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
memset(&entry, 0, sizeof(entry));
|
|
memset(&entry, 0, sizeof(entry));
|
|
|
|
|
|
- db = &entry;
|
|
|
|
- status = &db->status;
|
|
|
|
|
|
+ status = &entry.status;
|
|
|
|
|
|
- db->vd.class_ = mob_id;
|
|
|
|
- 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);
|
|
|
|
|
|
+ entry.vd.class_ = mob_id;
|
|
|
|
+ safestrncpy(entry.sprite, str[1], sizeof(entry.sprite));
|
|
|
|
+ safestrncpy(entry.jname, str[2], sizeof(entry.jname));
|
|
|
|
+ safestrncpy(entry.name, str[3], sizeof(entry.name));
|
|
|
|
+ entry.lv = atoi(str[4]);
|
|
|
|
+ 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]);
|
|
|
|
- db->range3 = atoi(str[21]);
|
|
|
|
|
|
+ entry.range2 = atoi(str[20]);
|
|
|
|
+ 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;
|
|
|
|
- if (db->range2 < 1)
|
|
|
|
- db->range2 = 1;
|
|
|
|
|
|
+ entry.range2 = entry.range2 * battle_config.view_range_rate / 100;
|
|
|
|
+ if (entry.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;
|
|
|
|
- if (db->range3 < db->range2)
|
|
|
|
- db->range3 = db->range2;
|
|
|
|
|
|
+ entry.range3 = entry.range3 * battle_config.chase_range_rate / 100;
|
|
|
|
+ if (entry.range3 < entry.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( itemdb_search(db->mvpitem[i].nameid) ){
|
|
|
|
- db->mvpitem[i].p = atoi(str[32+i*2]);
|
|
|
|
|
|
+ if( entry.mvpitem[i].nameid ){
|
|
|
|
+ if( itemdb_search(entry.mvpitem[i].nameid) ){
|
|
|
|
+ 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;
|
|
|
|
- db->mvpitem[i].p = 0;
|
|
|
|
|
|
+ entry.mvpitem[i].nameid = 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( itemdb_search( db->dropitem[i].nameid ) ){
|
|
|
|
- db->dropitem[i].p = atoi(str[k+1]);
|
|
|
|
|
|
+ if( entry.dropitem[i].nameid ){
|
|
|
|
+ if( itemdb_search( entry.dropitem[i].nameid ) ){
|
|
|
|
+ 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;
|
|
|
|
- db->dropitem[i].p = 0;
|
|
|
|
|
|
+ entry.dropitem[i].nameid = 0;
|
|
|
|
+ entry.dropitem[i].p = 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ db = mob_db(mob_id);
|
|
|
|
+
|
|
// Finally insert monster's data into the database.
|
|
// Finally insert monster's data into the database.
|
|
- if (mob_db_data[mob_id] == NULL)
|
|
|
|
- mob_db_data[mob_id] = (struct mob_db*)aCalloc(1, sizeof(struct mob_db));
|
|
|
|
|
|
+ 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(mob_db_data[mob_id], db, sizeof(struct mob_db));
|
|
|
|
|
|
+ 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));
|
|
|
|
- mob_db_data[mob_id]->vd.class_ = sprite_id;
|
|
|
|
|
|
+ memset(&db->vd, 0, sizeof(struct view_data));
|
|
|
|
+ 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]);
|
|
|
|
- mob_db_data[mob_id]->vd.hair_style=atoi(str[3]);
|
|
|
|
- mob_db_data[mob_id]->vd.hair_color=atoi(str[4]);
|
|
|
|
- mob_db_data[mob_id]->vd.weapon=atoi(str[5]);
|
|
|
|
- mob_db_data[mob_id]->vd.shield=atoi(str[6]);
|
|
|
|
- mob_db_data[mob_id]->vd.head_top=atoi(str[7]);
|
|
|
|
- mob_db_data[mob_id]->vd.head_mid=atoi(str[8]);
|
|
|
|
- mob_db_data[mob_id]->vd.head_bottom=atoi(str[9]);
|
|
|
|
- mob_db_data[mob_id]->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.sex=atoi(str[2]);
|
|
|
|
+ db->vd.hair_style=atoi(str[3]);
|
|
|
|
+ db->vd.hair_color=atoi(str[4]);
|
|
|
|
+ db->vd.weapon=atoi(str[5]);
|
|
|
|
+ db->vd.shield=atoi(str[6]);
|
|
|
|
+ db->vd.head_top=atoi(str[7]);
|
|
|
|
+ db->vd.head_mid=atoi(str[8]);
|
|
|
|
+ db->vd.head_bottom=atoi(str[9]);
|
|
|
|
+ db->option=atoi(str[10])&~(OPTION_HIDE|OPTION_CLOAK|OPTION_INVISIBLE);
|
|
|
|
+ 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)
|
|
|
|
- mob_chat_db[msg_id] = (struct mob_chat*)aCalloc(1, sizeof (struct mob_chat));
|
|
|
|
|
|
+ ms = mob_chat(msg_id);
|
|
|
|
|
|
- ms = mob_chat_db[msg_id];
|
|
|
|
|
|
+ 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;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
//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( i = 0; i <= MAX_MOB_DB; i++ ){
|
|
|
|
|
|
+ for( auto &pair : mob_db_data ){
|
|
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 == mob_dummy ){
|
|
|
|
|
|
+ if( mob_is_clone( mob_id ) ){
|
|
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);
|
|
|
|
- if (mob != mob_dummy)
|
|
|
|
- //memcpy(&mob->skill, skill, sizeof(skill));
|
|
|
|
|
|
+ struct mob_db *mob = mob_db(skill->mob_id);
|
|
|
|
+ if (mob != NULL)
|
|
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++) {
|
|
|
|
- mob = mob_db(i);
|
|
|
|
- if (mob == mob_dummy)
|
|
|
|
|
|
+ for( auto &pair : mob_db_data ){
|
|
|
|
+ if ( mob_is_clone(pair.first) ){
|
|
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;
|
|
|
|
- if (mob_dummy)
|
|
|
|
- {
|
|
|
|
- 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_db_data.clear();
|
|
|
|
+ mob_chat_db.clear();
|
|
|
|
+
|
|
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);
|