|
@@ -35,6 +35,10 @@
|
|
#include "log.hpp"
|
|
#include "log.hpp"
|
|
#include "achievement.hpp"
|
|
#include "achievement.hpp"
|
|
|
|
|
|
|
|
+#include <vector>
|
|
|
|
+#include <unordered_map>
|
|
|
|
+#include <algorithm>
|
|
|
|
+
|
|
#define ACTIVE_AI_RANGE 2 //Distance added on top of 'AREA_SIZE' at which mobs enter active AI mode.
|
|
#define ACTIVE_AI_RANGE 2 //Distance added on top of 'AREA_SIZE' at which mobs enter active AI mode.
|
|
|
|
|
|
#define IDLE_SKILL_INTERVAL 10 //Active idle skills should be triggered every 1 second (1000/MIN_MOBTHINKTIME)
|
|
#define IDLE_SKILL_INTERVAL 10 //Active idle skills should be triggered every 1 second (1000/MIN_MOBTHINKTIME)
|
|
@@ -66,6 +70,9 @@ struct mob_db *mob_dummy = NULL; //Dummy mob to be returned when a non-existant
|
|
|
|
|
|
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]; }
|
|
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]; }
|
|
|
|
|
|
|
|
+// holds Monster Spawn informations
|
|
|
|
+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_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]; }
|
|
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]; }
|
|
@@ -118,34 +125,6 @@ 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);
|
|
|
|
|
|
-/*==========================================
|
|
|
|
- * Mob is searched with a name.
|
|
|
|
- *------------------------------------------*/
|
|
|
|
-int mobdb_searchname(const char *str)
|
|
|
|
-{
|
|
|
|
- int i;
|
|
|
|
- for(i=0;i<=MAX_MOB_DB;i++){
|
|
|
|
- struct mob_db *mob = mob_db(i);
|
|
|
|
- if(mob == mob_dummy) //Skip dummy mobs.
|
|
|
|
- continue;
|
|
|
|
- if(strcmpi(mob->name,str)==0 || strcmpi(mob->jname,str)==0 || strcmpi(mob->sprite,str)==0)
|
|
|
|
- return i;
|
|
|
|
- }
|
|
|
|
- return 0;
|
|
|
|
-}
|
|
|
|
-static int mobdb_searchname_array_sub(struct mob_db* mob, const char *str)
|
|
|
|
-{
|
|
|
|
- if (mob == mob_dummy)
|
|
|
|
- return 1;
|
|
|
|
- if(!mob->base_exp && !mob->job_exp && mob->spawn[0].qty < 1)
|
|
|
|
- return 1; // Monsters with no base/job exp and no spawn point are, by this criteria, considered "slave mobs" and excluded from search results
|
|
|
|
- if(stristr(mob->jname,str))
|
|
|
|
- return 0;
|
|
|
|
- if(stristr(mob->name,str))
|
|
|
|
- return 0;
|
|
|
|
- return strcmpi(mob->jname,str);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
/*========================================== [Playtester]
|
|
/*========================================== [Playtester]
|
|
* Removes all characters that spotted the monster but are no longer online
|
|
* Removes all characters that spotted the monster but are no longer online
|
|
* @param md: Monster whose spotted log should be cleaned
|
|
* @param md: Monster whose spotted log should be cleaned
|
|
@@ -304,26 +283,69 @@ void mvptomb_destroy(struct mob_data *md) {
|
|
md->tomb_nid = 0;
|
|
md->tomb_nid = 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * Sub function for mob namesearch. Here is defined which are accepted.
|
|
|
|
+*/
|
|
|
|
+static bool mobdb_searchname_sub(uint16 mob_id, const char * const str, bool full_cmp)
|
|
|
|
+{
|
|
|
|
+ const struct mob_db * const mob = mob_db(mob_id);
|
|
|
|
+
|
|
|
|
+ if( mobdb_checkid(mob_id) <= 0 )
|
|
|
|
+ return false; // invalid mob_id (includes clone check)
|
|
|
|
+ if(!mob->base_exp && !mob->job_exp && !mob->has_spawn())
|
|
|
|
+ return false; // Monsters with no base/job exp and no spawn point are, by this criteria, considered "slave mobs" and excluded from search results
|
|
|
|
+ if( full_cmp ) {
|
|
|
|
+ // str must equal the db value
|
|
|
|
+ if( strcmpi(mob->name, str) == 0 ||
|
|
|
|
+ strcmpi(mob->jname, str) == 0 ||
|
|
|
|
+ strcmpi(mob->sprite, str) == 0 )
|
|
|
|
+ return true;
|
|
|
|
+ } else {
|
|
|
|
+ // str must be in the db value
|
|
|
|
+ if( stristr(mob->name, str) != NULL ||
|
|
|
|
+ stristr(mob->jname, str) != NULL ||
|
|
|
|
+ stristr(mob->sprite, str) != NULL )
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ return false;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Searches for the Mobname
|
|
|
|
+*/
|
|
|
|
+uint16 mobdb_searchname_(const char * const str, bool full_cmp)
|
|
|
|
+{
|
|
|
|
+ for(uint16 mob_id = 0; mob_id <= MAX_MOB_DB; mob_id++) {
|
|
|
|
+ if( mobdb_searchname_sub(mob_id, str, full_cmp) )
|
|
|
|
+ return mob_id;
|
|
|
|
+ }
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+uint16 mobdb_searchname(const char * const str)
|
|
|
|
+{
|
|
|
|
+ return mobdb_searchname_(str, true);
|
|
|
|
+}
|
|
/*==========================================
|
|
/*==========================================
|
|
* Founds up to N matches. Returns number of matches [Skotlex]
|
|
* Founds up to N matches. Returns number of matches [Skotlex]
|
|
*------------------------------------------*/
|
|
*------------------------------------------*/
|
|
-int mobdb_searchname_array(struct mob_db** data, int size, const char *str)
|
|
|
|
|
|
+int mobdb_searchname_array_(const char *str, uint16 * out, int size, bool full_cmp)
|
|
{
|
|
{
|
|
- int count = 0, i;
|
|
|
|
- struct mob_db* mob;
|
|
|
|
- for(i=0;i<=MAX_MOB_DB;i++){
|
|
|
|
- mob = mob_db(i);
|
|
|
|
- if (mob == mob_dummy || mob_is_clone(i) ) //keep clones out (or you leak player stats)
|
|
|
|
- continue;
|
|
|
|
- if (!mobdb_searchname_array_sub(mob, str)) {
|
|
|
|
- if (count < size)
|
|
|
|
- data[count] = mob;
|
|
|
|
|
|
+ unsigned short count = 0;
|
|
|
|
+ for(uint16 mob_id = 0; mob_id <= MAX_MOB_DB && count < size; mob_id++) {
|
|
|
|
+ if( mobdb_searchname_sub(mob_id, str, full_cmp) ) {
|
|
|
|
+ out[count] = mob_id;
|
|
count++;
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return count;
|
|
return count;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+int mobdb_searchname_array(const char *str, uint16 * out, int size)
|
|
|
|
+{
|
|
|
|
+ return mobdb_searchname_array_(str, out, size, false);
|
|
|
|
+}
|
|
|
|
+
|
|
/*==========================================
|
|
/*==========================================
|
|
* Id Mob is checked.
|
|
* Id Mob is checked.
|
|
*------------------------------------------*/
|
|
*------------------------------------------*/
|
|
@@ -490,12 +512,14 @@ int mob_get_random_id(int type, int flag, int lv)
|
|
(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) ||
|
|
(flag&0x04 && status_has_mode(&mob->status,MD_STATUS_IMMUNE) ) ||
|
|
(flag&0x04 && status_has_mode(&mob->status,MD_STATUS_IMMUNE) ) ||
|
|
- (flag&0x08 && mob->spawn[0].qty < 1) ||
|
|
|
|
|
|
+ (flag&0x08 && !mob->has_spawn()) ||
|
|
(flag&0x10 && status_has_mode(&mob->status,MD_IGNOREMELEE|MD_IGNOREMAGIC|MD_IGNORERANGED|MD_IGNOREMISC) )
|
|
(flag&0x10 && status_has_mode(&mob->status,MD_IGNOREMELEE|MD_IGNOREMAGIC|MD_IGNORERANGED|MD_IGNOREMISC) )
|
|
) && (i++) < MAX_MOB_DB && msummon->count > 1);
|
|
) && (i++) < MAX_MOB_DB && msummon->count > 1);
|
|
|
|
|
|
- if (i >= MAX_MOB_DB && &msummon->list[0]) // no suitable monster found, use fallback for given list
|
|
|
|
|
|
+ if (i >= MAX_MOB_DB && &msummon->list[0]) {
|
|
|
|
+ ShowError("mob_get_random_id: no suitable monster found, use fallback for given list. Last_MobID: %d\n", mob_id);
|
|
mob_id = msummon->list[0].mob_id;
|
|
mob_id = msummon->list[0].mob_id;
|
|
|
|
+ }
|
|
return mob_id;
|
|
return mob_id;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -3085,7 +3109,7 @@ int mob_guardian_guildchange(struct mob_data *md)
|
|
/*==========================================
|
|
/*==========================================
|
|
* Pick a random class for the mob
|
|
* Pick a random class for the mob
|
|
*------------------------------------------*/
|
|
*------------------------------------------*/
|
|
-int mob_random_class (int *value, size_t count)
|
|
|
|
|
|
+int mob_random_class(int *value, size_t count)
|
|
{
|
|
{
|
|
nullpo_ret(value);
|
|
nullpo_ret(value);
|
|
|
|
|
|
@@ -3104,6 +3128,57 @@ int mob_random_class (int *value, size_t count)
|
|
return mobdb_checkid(value[rnd()%count]);
|
|
return mobdb_checkid(value[rnd()%count]);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+* Returns the SpawnInfos of the mob_db entry
|
|
|
|
+*/
|
|
|
|
+const std::vector<spawn_info> mob_db::get_spawns() const
|
|
|
|
+{
|
|
|
|
+ // Returns an empty std::vector<spawn_info> if mob_id is not in mob_spawn_data
|
|
|
|
+ return mob_spawn_data[this->get_mobid()];
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Checks if a monster is spawned. Returns true if yes, false otherwise.
|
|
|
|
+*/
|
|
|
|
+bool mob_db::has_spawn() const
|
|
|
|
+{
|
|
|
|
+ // It's enough to check if the monster is in mob_spawn_data, because
|
|
|
|
+ // none or empty spawns are ignored. Thus the monster is spawned.
|
|
|
|
+ return mob_spawn_data.find(this->get_mobid()) != mob_spawn_data.end();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Adds a spawn info to the specific mob. (To mob_spawn_data)
|
|
|
|
+ * @param mob_id - Monster ID spawned
|
|
|
|
+ * @param new_spawn - spawn_info holding the map and quantity of the spawn
|
|
|
|
+*/
|
|
|
|
+void mob_add_spawn(uint16 mob_id, const struct spawn_info& new_spawn)
|
|
|
|
+{
|
|
|
|
+ unsigned short m = new_spawn.mapindex;
|
|
|
|
+
|
|
|
|
+ if( new_spawn.qty <= 0 )
|
|
|
|
+ return; //ignore empty spawns
|
|
|
|
+
|
|
|
|
+ std::vector<spawn_info>& spawns = mob_spawn_data[mob_id];
|
|
|
|
+ // Search if the map is already in spawns
|
|
|
|
+ auto itSameMap = std::find_if(spawns.begin(), spawns.end(),
|
|
|
|
+ [&m] (const spawn_info &s) { return (s.mapindex == m); });
|
|
|
|
+
|
|
|
|
+ if( itSameMap != spawns.end() )
|
|
|
|
+ itSameMap->qty += new_spawn.qty; // add quantity, if map is found
|
|
|
|
+ else
|
|
|
|
+ spawns.push_back(new_spawn); // else, add the whole spawn info
|
|
|
|
+
|
|
|
|
+ // sort spawns by spawn quantity
|
|
|
|
+ std::sort(spawns.begin(), spawns.end(),
|
|
|
|
+ [](const spawn_info & a, const spawn_info & b) -> bool
|
|
|
|
+ { return a.qty > b.qty; });
|
|
|
|
+/** Note
|
|
|
|
+ Spawns are sorted after every addition. This makes reloadscript slower, but
|
|
|
|
+ some spawns may be added directly by loadscript or something similar.
|
|
|
|
+*/
|
|
|
|
+}
|
|
|
|
+
|
|
/*==========================================
|
|
/*==========================================
|
|
* Change mob base class
|
|
* Change mob base class
|
|
*------------------------------------------*/
|
|
*------------------------------------------*/
|
|
@@ -4221,9 +4296,6 @@ static bool mob_parse_dbrow(char** str)
|
|
// Finally insert monster's data into the database.
|
|
// Finally insert monster's data into the database.
|
|
if (mob_db_data[mob_id] == NULL)
|
|
if (mob_db_data[mob_id] == NULL)
|
|
mob_db_data[mob_id] = (struct mob_db*)aCalloc(1, sizeof(struct mob_db));
|
|
mob_db_data[mob_id] = (struct mob_db*)aCalloc(1, sizeof(struct mob_db));
|
|
- else
|
|
|
|
- //Copy over spawn data
|
|
|
|
- memcpy(&db->spawn, mob_db_data[mob_id]->spawn, sizeof(db->spawn));
|
|
|
|
|
|
|
|
memcpy(mob_db_data[mob_id], db, sizeof(struct mob_db));
|
|
memcpy(mob_db_data[mob_id], db, sizeof(struct mob_db));
|
|
return true;
|
|
return true;
|
|
@@ -5324,10 +5396,7 @@ void mob_reload(void) {
|
|
*/
|
|
*/
|
|
void mob_clear_spawninfo()
|
|
void mob_clear_spawninfo()
|
|
{ //Clears spawn related information for a script reload.
|
|
{ //Clears spawn related information for a script reload.
|
|
- int i;
|
|
|
|
- for (i = 0; i < MAX_MOB_DB; i++)
|
|
|
|
- if (mob_db_data[i])
|
|
|
|
- memset(&mob_db_data[i]->spawn,0,sizeof(mob_db_data[i]->spawn));
|
|
|
|
|
|
+ mob_spawn_data.clear();
|
|
}
|
|
}
|
|
|
|
|
|
/*==========================================
|
|
/*==========================================
|