diff options
author | Haru <haru@dotalux.com> | 2020-02-09 21:18:26 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-02-09 21:18:26 +0100 |
commit | 481d92bfa0fac1320aebf0ab4f4ceb8365fa3ff3 (patch) | |
tree | 807314fa875ca53ab1508f347bcf5ad12e9f3348 /src | |
parent | 9e68350a054d5ec181a40528728d8de185a4f43c (diff) | |
parent | d84aee4cd917fd1a151d7ec0b3a2d7e32df02441 (diff) | |
download | hercules-481d92bfa0fac1320aebf0ab4f4ceb8365fa3ff3.tar.gz hercules-481d92bfa0fac1320aebf0ab4f4ceb8365fa3ff3.tar.bz2 hercules-481d92bfa0fac1320aebf0ab4f4ceb8365fa3ff3.tar.xz hercules-481d92bfa0fac1320aebf0ab4f4ceb8365fa3ff3.zip |
Merge pull request #2590 from Kenpachi2k13/issue#2530
Change unload NPC behavior to kill mobs that were spawned by unloaded NPC (non-permanent monster spawns) [Issue #2530]
Diffstat (limited to 'src')
-rw-r--r-- | src/common/utils.c | 44 | ||||
-rw-r--r-- | src/common/utils.h | 9 | ||||
-rw-r--r-- | src/map/atcommand.c | 193 | ||||
-rw-r--r-- | src/map/atcommand.h | 2 | ||||
-rw-r--r-- | src/map/clif.c | 4 | ||||
-rw-r--r-- | src/map/instance.c | 2 | ||||
-rw-r--r-- | src/map/map.c | 2 | ||||
-rw-r--r-- | src/map/mob.c | 852 | ||||
-rw-r--r-- | src/map/mob.h | 9 | ||||
-rw-r--r-- | src/map/npc.c | 329 | ||||
-rw-r--r-- | src/map/npc.h | 7 | ||||
-rw-r--r-- | src/map/script.c | 149 | ||||
-rw-r--r-- | src/map/skill.c | 12 | ||||
-rw-r--r-- | src/plugins/HPMHooking/HPMHooking.Defs.inc | 30 | ||||
-rw-r--r-- | src/plugins/HPMHooking/HPMHooking_map.HPMHooksCore.inc | 4 | ||||
-rw-r--r-- | src/plugins/HPMHooking/HPMHooking_map.HookingPoints.inc | 1 | ||||
-rw-r--r-- | src/plugins/HPMHooking/HPMHooking_map.Hooks.inc | 117 |
17 files changed, 1094 insertions, 672 deletions
diff --git a/src/common/utils.c b/src/common/utils.c index 48ce539b6..084080df7 100644 --- a/src/common/utils.c +++ b/src/common/utils.c @@ -132,7 +132,7 @@ void findfile(const char *p, const char *pat, void (func)(const char *, void *co { WIN32_FIND_DATAA FindFileData; HANDLE hFind; - char tmppath[MAX_PATH+1]; + char tmppath[MAX_DIR_PATH + 1]; const char *path = (p ==NULL)? "." : p; const char *pattern = (pat==NULL)? "" : pat; @@ -166,9 +166,26 @@ void findfile(const char *p, const char *pat, void (func)(const char *, void *co } return; } -#else -#define MAX_DIR_PATH 2048 +/** + * Checks if the passed path points to a file. + * + * @param path The path which should be checked. + * @return true if the passed path points to a file, otherwise false. + * + **/ +bool is_file(const char *path) +{ + nullpo_retr(false, path); + + char path_tmp[MAX_DIR_PATH + 1]; + + checkpath(path_tmp, path); + + return ((GetFileAttributesA(path_tmp) & FILE_ATTRIBUTE_DIRECTORY) == 0); +} + +#else static char *checkpath(char *path, const char *srcpath) { @@ -235,6 +252,27 @@ void findfile(const char *p, const char *pat, void (func)(const char *, void *co closedir(dir); } + +/** + * Checks if the passed path points to a file. + * + * @param path The path which should be checked. + * @return true if the passed path points to a file, otherwise false. + * + **/ +bool is_file(const char *path) +{ + nullpo_retr(false, path); + + char path_tmp[MAX_DIR_PATH + 1]; + + checkpath(path_tmp, path); + + struct stat path_stat; + + return (stat(path_tmp, &path_stat) == 0 && S_ISREG(path_stat.st_mode)); +} + #endif bool exists(const char *filename) diff --git a/src/common/utils.h b/src/common/utils.h index a0590db7f..3e0d73e40 100644 --- a/src/common/utils.h +++ b/src/common/utils.h @@ -31,6 +31,14 @@ /* [HCache] 1-byte key to ensure our method is the latest, we can modify to ensure the method matches */ #define HCACHE_KEY 'k' +#ifndef MAX_DIR_PATH +#ifdef WIN32 +#define MAX_DIR_PATH MAX_PATH +#else +#define MAX_DIR_PATH 2048 +#endif +#endif + //Caps values to min/max #define cap_value(a, min, max) (((a) >= (max)) ? (max) : ((a) <= (min)) ? (min) : (a)) @@ -40,6 +48,7 @@ void WriteDump(FILE* fp, const void* buffer, size_t length); void ShowDump(const void* buffer, size_t length); void findfile(const char *p, const char *pat, void (func)(const char *, void *), void *context); +bool is_file(const char *path); bool exists(const char* filename); /// calculates the value of A / B, in percent (rounded down) diff --git a/src/map/atcommand.c b/src/map/atcommand.c index 5d95e2c14..c9191d27d 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -4483,59 +4483,90 @@ ACMD(loadnpc) return true; } +/** + * Unloads a specific NPC. + * + * @code{.herc} + * @unloadnpc <NPC_name> {<flag>} + * @endcode + * + **/ ACMD(unloadnpc) { - struct npc_data *nd; - char NPCname[NAME_LENGTH+1]; - - memset(NPCname, '\0', sizeof(NPCname)); + char npc_name[NAME_LENGTH + 1] = {'\0'}; + int flag = 1; - if (!*message || sscanf(message, "%24[^\n]", NPCname) < 1) { - clif->message(fd, msg_fd(fd,1133)); // Please enter a NPC name (usage: @npcoff <NPC_name>). + if (*message == '\0' || sscanf(message, "%24s %1d", npc_name, &flag) < 1) { + clif->message(fd, msg_fd(fd, 1133)); /// Please enter a NPC name (Usage: @unloadnpc <NPC_name> {<flag>}). return false; } - - if ((nd = npc->name2id(NPCname)) == NULL) { - clif->message(fd, msg_fd(fd,111)); // This NPC doesn't exist. + + struct npc_data *nd = npc->name2id(npc_name); + + if (nd == NULL) { + clif->message(fd, msg_fd(fd, 111)); /// This NPC doesn't exist. return false; } - npc->unload_duplicates(nd); - npc->unload(nd,true); + npc->unload_duplicates(nd, (flag != 0)); + npc->unload(nd, true, (flag != 0)); npc->read_event_script(); - clif->message(fd, msg_fd(fd,112)); // Npc Disabled. + clif->message(fd, msg_fd(fd, 112)); /// Npc Disabled. return true; } -/// Unload existing NPC within the NPC file and reload it. -/// Usage: @reloadnpc npc/sample_npc.txt +/** + * Unloads a script file and reloads it. + * Note: Be aware that some changes made by NPC are not reverted on unload. See doc/atcommands.txt for details. + * + * @code{.herc} + * @reloadnpc <path> {<flag>} + * @endcode + * + **/ ACMD(reloadnpc) { - if (!*message) { - clif->message(fd, msg_fd(fd, 1385)); // Usage: @unloadnpcfile <file name> + char format[20]; + + snprintf(format, sizeof(format), "%%%ds %%1d", MAX_DIR_PATH); + + char file_path[MAX_DIR_PATH + 1] = {'\0'}; + int flag = 1; + + if (*message == '\0' || (sscanf(message, format, file_path, &flag) < 1)) { + clif->message(fd, msg_fd(fd, 1516)); /// Usage: @reloadnpc <path> {<flag>} return false; - } else if (npc->unloadfile(message) == true) { - clif->message(fd, msg_fd(fd, 1386)); // File unloaded. Be aware that mapflags and monsters spawned directly are not removed. + } - FILE *fp = fopen(message, "r"); - // check if script file exists - if (fp == NULL) { - clif->message(fd, msg_fd(fd, 261)); - return false; - } - fclose(fp); + if (!exists(file_path)) { + clif->message(fd, msg_fd(fd, 1387)); /// File not found. + return false; + } + + if (!is_file(file_path)) { + clif->message(fd, msg_fd(fd, 1518)); /// Not a file. + return false; + } - // add to list of script sources and run it - npc->addsrcfile(message); - npc->parsesrcfile(message, true); - npc->read_event_script(); + FILE *fp = fopen(file_path, "r"); - clif->message(fd, msg_fd(fd, 262)); - } else { - clif->message(fd, msg_fd(fd, 1387)); // File not found. + if (fp == NULL) { + clif->message(fd, msg_fd(fd, 1519)); /// Can't open file. return false; } + fclose(fp); + + if (!npc->unloadfile(file_path, (flag != 0))) { + clif->message(fd, msg_fd(fd, 1517)); /// Script could not be unloaded. + return false; + } + + clif->message(fd, msg_fd(fd, 1386)); /// File unloaded. Be aware that... + npc->addsrcfile(file_path); + npc->parsesrcfile(file_path, true); + npc->read_event_script(); + clif->message(fd, msg_fd(fd, 262)); /// Script loaded. return true; } @@ -6576,47 +6607,52 @@ ACMD(reset) /*========================================== * *------------------------------------------*/ + +/** + * Spawns mobs which treats the invoking as its master. + * + * @code{.herc} + * @summon <monster name/ID> {<duration>} + * @endcode + * + **/ ACMD(summon) { - char name[NAME_LENGTH]; - int mob_id = 0; + char name[NAME_LENGTH + 1] = {'\0'}; int duration = 0; - struct mob_data *md; - int64 tick=timer->gettick(); - if (!*message || sscanf(message, "%23s %12d", name, &duration) < 1) - { - clif->message(fd, msg_fd(fd,1225)); // Please enter a monster name (usage: @summon <monster name> {duration}). + if (*message == '\0' || sscanf(message, "%24s %12d", name, &duration) < 1) { + clif->message(fd, msg_fd(fd, 1225)); /// Please enter a monster name (usage: @summon <monster name> {duration}). return false; } - if (duration < 1) - duration =1; - else if (duration > 60) - duration =60; + int mob_id = atoi(name); - if ((mob_id = atoi(name)) == 0) + if (mob_id == 0) mob_id = mob->db_searchname(name); - if(mob_id == 0 || mob->db_checkid(mob_id) == 0) - { - clif->message(fd, msg_fd(fd,40)); // Invalid monster ID or name. + + if (mob_id == 0 || mob->db_checkid(mob_id) == 0) { + clif->message(fd, msg_fd(fd, 40)); /// Invalid monster ID or name. return false; } - md = mob->once_spawn_sub(&sd->bl, sd->bl.m, -1, -1, DEFAULT_MOB_JNAME, mob_id, "", SZ_SMALL, AI_NONE); + struct mob_data *md = mob->once_spawn_sub(&sd->bl, sd->bl.m, -1, -1, DEFAULT_MOB_JNAME, mob_id, "", + SZ_SMALL, AI_NONE, 0); - if(!md) + if (md == NULL) return false; md->master_id = sd->bl.id; md->special_state.ai = AI_ATTACK; - md->deletetimer = timer->add(tick+(duration*60000),mob->timer_delete,md->bl.id,0); - clif->specialeffect(&md->bl,344,AREA); - mob->spawn(md); - sc_start4(NULL,&md->bl, SC_MODECHANGE, 100, 1, 0, MD_AGGRESSIVE, 0, 60000); - clif->skill_poseffect(&sd->bl,AM_CALLHOMUN,1,md->bl.x,md->bl.y,tick); - clif->message(fd, msg_fd(fd,39)); // All monster summoned! + const int64 tick = timer->gettick(); + + md->deletetimer = timer->add(tick + (int64)cap_value(duration, 1, 60) * 60000, mob->timer_delete, md->bl.id, 0); + clif->specialeffect(&md->bl, 344, AREA); + mob->spawn(md); + sc_start4(NULL, &md->bl, SC_MODECHANGE, 100, 1, 0, MD_AGGRESSIVE, 0, 60000); + clif->skill_poseffect(&sd->bl, AM_CALLHOMUN, 1, md->bl.x, md->bl.y, tick); + clif->message(fd, msg_fd(fd, 39)); /// All monster summoned! return true; } @@ -9009,19 +9045,54 @@ ACMD(addperm) return true; } +/** + * Unloads a script file. + * Note: Be aware that some changes made by NPC are not reverted on unload. See doc/atcommands.txt for details. + * + * @code{.herc} + * @unloadnpcfile <path> {<flag>} + * @endcode + * + **/ ACMD(unloadnpcfile) { - if (!*message) { - clif->message(fd, msg_fd(fd,1385)); // Usage: @unloadnpcfile <file name> + char format[20]; + + snprintf(format, sizeof(format), "%%%ds %%1d", MAX_DIR_PATH); + + char file_path[MAX_DIR_PATH + 1] = {'\0'}; + int flag = 1; + + if (*message == '\0' || (sscanf(message, format, file_path, &flag) < 1)) { + clif->message(fd, msg_fd(fd, 1385)); /// Usage: @unloadnpcfile <path> {<flag>} return false; } - if (npc->unloadfile(message)) { - clif->message(fd, msg_fd(fd,1386)); // File unloaded. Be aware that mapflags and monsters spawned directly are not removed. - } else { - clif->message(fd, msg_fd(fd,1387)); // File not found. + if (!exists(file_path)) { + clif->message(fd, msg_fd(fd, 1387)); /// File not found. return false; } + + if (!is_file(file_path)) { + clif->message(fd, msg_fd(fd, 1518)); /// Not a file. + return false; + } + + FILE *fp = fopen(file_path, "r"); + + if (fp == NULL) { + clif->message(fd, msg_fd(fd, 1519)); /// Can't open file. + return false; + } + + fclose(fp); + + if (!npc->unloadfile(file_path, (flag != 0))) { + clif->message(fd, msg_fd(fd, 1517)); /// Script could not be unloaded. + return false; + } + + clif->message(fd, msg_fd(fd, 1386)); /// File unloaded. Be aware that... return true; } diff --git a/src/map/atcommand.h b/src/map/atcommand.h index f3b1be51b..66827b3b2 100644 --- a/src/map/atcommand.h +++ b/src/map/atcommand.h @@ -41,7 +41,7 @@ struct config_setting_t; * Defines **/ #define ATCOMMAND_LENGTH 50 -#define MAX_MSG 1516 +#define MAX_MSG 1520 #define msg_txt(idx) atcommand->msg(idx) #define msg_sd(sd,msg_number) atcommand->msgsd((sd),(msg_number)) #define msg_fd(fd,msg_number) atcommand->msgfd((fd),(msg_number)) diff --git a/src/map/clif.c b/src/map/clif.c index d41a17e0b..868117a96 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -15280,8 +15280,8 @@ static void clif_parse_GMKick(int fd, struct map_session_data *sd) clif->GM_kickack(sd, 0); return; } - npc->unload_duplicates(nd); - npc->unload(nd,true); + npc->unload_duplicates(nd, true); + npc->unload(nd, true, true); npc->read_event_script(); } break; diff --git a/src/map/instance.c b/src/map/instance.c index 90f2217b1..1104b7e88 100644 --- a/src/map/instance.c +++ b/src/map/instance.c @@ -446,7 +446,7 @@ static int instance_cleanup_sub(struct block_list *bl, va_list ap) map->quit(BL_UCAST(BL_PC, bl)); break; case BL_NPC: - npc->unload(BL_UCAST(BL_NPC, bl), true); + npc->unload(BL_UCAST(BL_NPC, bl), true, true); break; case BL_MOB: unit->free(bl,CLR_OUTSIGHT); diff --git a/src/map/map.c b/src/map/map.c index 9a45c4d21..8b52fc761 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -6092,7 +6092,7 @@ static int cleanup_sub(struct block_list *bl, va_list ap) map->quit(BL_UCAST(BL_PC, bl)); break; case BL_NPC: - npc->unload(BL_UCAST(BL_NPC, bl), false); + npc->unload(BL_UCAST(BL_NPC, bl), false, true); break; case BL_MOB: unit->free(bl,CLR_OUTSIGHT); diff --git a/src/map/mob.c b/src/map/mob.c index 4b74abc8f..6c1076114 100644 --- a/src/map/mob.c +++ b/src/map/mob.c @@ -337,15 +337,23 @@ static int mob_parse_dataset(struct spawn_data *data) return 1; } -/*========================================== - * Generates the basic mob data using the spawn_data provided. - *------------------------------------------*/ -static struct mob_data *mob_spawn_dataset(struct spawn_data *data) + +/** + * Generates basic mob data by using the passed spawn data. + * + * @param data The mobs spawn data. + * @param npc_id If spawned by NPC script, this holds the ID of the invoking NPC. + * @return The generated mob data, later used to spawn the mob. + * + **/ +static struct mob_data *mob_spawn_dataset(struct spawn_data *data, int npc_id) { - struct mob_data *md = NULL; nullpo_retr(NULL, data); - CREATE(md, struct mob_data, 1); - md->bl.id= npc->get_new_npc_id(); + + struct mob_data *md = (struct mob_data *)aCalloc(1, sizeof(struct mob_data)); + + memcpy(md->name, data->name, NAME_LENGTH); + md->bl.id = npc->get_new_npc_id(); md->bl.type = BL_MOB; md->bl.m = data->m; md->bl.x = data->x; @@ -353,24 +361,29 @@ static struct mob_data *mob_spawn_dataset(struct spawn_data *data) md->class_ = data->class_; md->state.boss = data->state.boss; md->db = mob->db(md->class_); + md->npc_id = npc_id; + md->spawn_timer = INVALID_TIMER; + md->deletetimer = INVALID_TIMER; + md->skill_idx = -1; + if (data->level > 0 && data->level <= MAX_LEVEL) md->level = data->level; - memcpy(md->name, data->name, NAME_LENGTH); - if (data->state.ai) + + if (data->state.ai > 0) md->special_state.ai = data->state.ai; - if (data->state.size) + + if (data->state.size > 0) md->special_state.size = data->state.size; - if (data->eventname[0] && strlen(data->eventname) >= 4) + + if (data->eventname[0] != '\0' && strlen(data->eventname) >= 4) memcpy(md->npc_event, data->eventname, 50); - if(md->db->status.mode&MD_LOOTER) - md->lootitem = (struct item *)aCalloc(LOOTITEM_SIZE,sizeof(struct item)); - md->spawn_timer = INVALID_TIMER; - md->deletetimer = INVALID_TIMER; - md->skill_idx = -1; + + if ((md->db->status.mode & MD_LOOTER) == MD_LOOTER) + md->lootitem = (struct item *)aCalloc(LOOTITEM_SIZE, sizeof(struct item)); + status->set_viewdata(&md->bl, md->class_); status->change_init(&md->bl); unit->dataset(&md->bl); - map->addiddb(&md->bl); return md; } @@ -503,7 +516,24 @@ static bool mob_ksprotected(struct block_list *src, struct block_list *target) return false; } -static struct mob_data *mob_once_spawn_sub(struct block_list *bl, int16 m, int16 x, int16 y, const char *mobname, int class_, const char *event, unsigned int size, unsigned int ai) +/** + * Prepares a mob's spawn data. + * + * @param bl The invoking character's block list. + * @param m The ID of the map where the mob should be spawned. + * @param x The x coordinate where the mob should be spawned. + * @param y The y coordinate where the mob should be spawned. + * @param mobname The mob's display name. + * @param class_ The mob's ID in database. + * @param event The name of the event which should be executed when the mob is killed. + * @param size The mob's size. + * @param ai The mob's AI. + * @param npc_id If spawned by NPC script, this holds the ID of the invoking NPC. + * @return The mob data generated by mob->spawn_dataset(). + * + **/ +static struct mob_data *mob_once_spawn_sub(struct block_list *bl, int16 m, int16 x, int16 y, const char *mobname, int class_, + const char *event, unsigned int size, unsigned int ai, int npc_id) { struct spawn_data data; @@ -521,76 +551,100 @@ static struct mob_data *mob_once_spawn_sub(struct block_list *bl, int16 m, int16 else strcpy(data.name, DEFAULT_MOB_JNAME); - if (event) + if (event != NULL) safestrncpy(data.eventname, event, sizeof(data.eventname)); - // Locate spot next to player. - if (bl && (x < 0 || y < 0)) + /** Locate spot next to player. **/ + if (bl != NULL && (x < 0 || y < 0)) map->search_freecell(bl, m, &x, &y, 1, 1, 0); - // if none found, pick random position on map + /** If none found, pick random position on map. **/ if (x <= 0 || x >= map->list[m].xs || y <= 0 || y >= map->list[m].ys) map->search_freecell(NULL, m, &x, &y, -1, -1, 1); data.x = x; data.y = y; - if (!mob->parse_dataset(&data)) + if (mob->parse_dataset(&data) == 0) return NULL; - return mob->spawn_dataset(&data); + return mob->spawn_dataset(&data, npc_id); } -/*========================================== - * Spawn a single mob on the specified coordinates. - *------------------------------------------*/ -static int mob_once_spawn(struct map_session_data *sd, int16 m, int16 x, int16 y, const char *mobname, int class_, int amount, const char *event, unsigned int size, unsigned int ai) +/** + * Spawns a given amount of mobs. + * + * @param sd The invoking character. + * @param m The ID of the map where the mob should be spawned. + * @param x The x coordinate where the mob should be spawned. + * @param y The y coordinate where the mob should be spawned. + * @param mobname The mob's display name. + * @param class_ The mob's ID in database. + * @param amount The amount of mobs to spawn. + * @param event The name of the event which should be executed when the mob is killed. + * @param size The mob's size. + * @param ai The mob's AI. + * @return The last spawned mob's GID, or 0 if spawning failed. + * + **/ +static int mob_once_spawn(struct map_session_data *sd, int16 m, int16 x, int16 y, const char *mobname, int class_, + int amount, const char *event, unsigned int size, unsigned int ai) { - struct mob_data* md = NULL; - int count, lv; bool no_guardian_data = false; - if( ai && ai&0x200 ) { + if (ai > 0 && (ai & 0x200) == 0x200) { no_guardian_data = true; - ai &=~ 0x200; + ai &= ~0x200; } if (m < 0 || amount <= 0) - return 0; // invalid input + return 0; - lv = (sd) ? sd->status.base_level : 255; + struct mob_data *md = NULL; + + for (int i = 0; i < amount; i++) { + int mob_id = class_; + + if (mob_id < 0) { + mob_id = mob->get_random_id(-class_ - 1, (battle_config.random_monster_checklv == 1) ? 3 : 1, + (sd != NULL) ? sd->status.base_level : 255); + } - for (count = 0; count < amount; count++) { - int c = (class_ >= 0) ? class_ : mob->get_random_id(-class_ - 1, (battle_config.random_monster_checklv) ? 3 : 1, lv); - md = mob->once_spawn_sub((sd) ? &sd->bl : NULL, m, x, y, mobname, c, event, size, ai); + md = mob->once_spawn_sub((sd != NULL) ? &sd->bl : NULL, m, x, y, mobname, mob_id, event, size, ai, + (sd != NULL) ? sd->npc_id : 0); - if (!md) + if (md == NULL) continue; if (class_ == MOBID_EMPELIUM && !no_guardian_data) { - struct guild_castle* gc = guild->mapindex2gc(map_id2index(m)); - struct guild* g = (gc) ? guild->search(gc->guild_id) : NULL; - if( gc ) { + struct guild_castle *gc = guild->mapindex2gc(map_id2index(m)); + + if (gc != NULL) { + struct guild *g = guild->search(gc->guild_id); + md->guardian_data = (struct guardian_data*)aCalloc(1, sizeof(struct guardian_data)); md->guardian_data->castle = gc; md->guardian_data->number = MAX_GUARDIANS; - if( g ) + + if (g != NULL) md->guardian_data->g = g; - else if( gc->guild_id ) //Guild not yet available, retry in 5. - timer->add(timer->gettick()+5000,mob->spawn_guardian_sub,md->bl.id,gc->guild_id); + else if (gc->guild_id > 0) /// Guild not yet available, retry in 5s. + timer->add(timer->gettick() + 5000, mob->spawn_guardian_sub, md->bl.id, + gc->guild_id); } - } // end addition [Valaris] + } mob->spawn(md); - if (class_ < 0 && battle_config.dead_branch_active) { - //Behold Aegis's masterful decisions yet again... - //"I understand the "Aggressive" part, but the "Can Move" and "Can Attack" is just stupid" - Poki#3 - sc_start4(NULL, &md->bl, SC_MODECHANGE, 100, 1, 0, MD_AGGRESSIVE|MD_CANATTACK|MD_CANMOVE|MD_ANGRY, 0, 60000); + if (class_ < 0 && battle_config.dead_branch_active == 1) { + /// Behold Aegis' masterful decisions yet again... + /// "I understand the "Aggressive" part, but the "Can Move" and "Can Attack" is just stupid" [Poki] + sc_start4(NULL, &md->bl, SC_MODECHANGE, 100, 1, 0, MD_AGGRESSIVE|MD_CANATTACK|MD_CANMOVE|MD_ANGRY, + 0, 60000); } } - return (md) ? md->bl.id : 0; // id of last spawned mob + return (md != NULL) ? md->bl.id : 0; /// ID of last spawned mob. } /*========================================== @@ -649,196 +703,236 @@ static int mob_once_spawn_area(struct map_session_data *sd, int16 m, int16 x0, i } /** - * Sets a guardian's guild data and liberates castle if couldn't retrieve guild data - * @param data (int)guild_id - * @retval Always 0 + * Sets a guardian's guild data and liberates castle if couldn't retrieve guild data. + * Required because the guild data may not be available at guardian spawn time. + * + * @param tid Required parameter for timer functions. Unused inside the function. + * @param tick Required parameter for timer functions. Unused inside the function. + * @param id The guardian mob's GID. + * @param data The guild ID. + * @return 1 on success, 0 on failure. + * * @author Skotlex + * **/ static int mob_spawn_guardian_sub(int tid, int64 tick, int id, intptr_t data) { - //Needed because the guild data may not be available at guardian spawn time. - struct block_list* bl = map->id2bl(id); - struct mob_data* md; - struct guild* g; + struct block_list *bl = map->id2bl(id); - if( bl == NULL ) //It is possible mob was already removed from map when the castle has no owner. [Skotlex] + if (bl == NULL || bl->type != BL_MOB) /// It is possible mob was already removed from map when the castle has no owner. [Skotlex] return 0; - Assert_ret(bl->type == BL_MOB); - md = BL_UCAST(BL_MOB, bl); + struct mob_data *md = BL_UCAST(BL_MOB, bl); - nullpo_ret(md->guardian_data); - g = guild->search((int)data); + if (md->guardian_data == NULL) + return 0; + + struct guild *g = guild->search((int)data); - if( g == NULL ) { //Liberate castle, if the guild is not found this is an error! [Skotlex] + if (g == NULL) { /// Liberate castle, if the guild is not found this is an error! [Skotlex] ShowError("mob_spawn_guardian_sub: Couldn't load guild %d!\n", (int)data); - //Not sure this is the best way, but otherwise we'd be invoking this for ALL guardians spawned later on. - if (md->class_ == MOBID_EMPELIUM && md->guardian_data) { + + /// Not sure this is the best way, but otherwise we'd be invoking this for ALL guardians spawned later on. + if (md->class_ == MOBID_EMPELIUM) { md->guardian_data->g = NULL; - if( md->guardian_data->castle->guild_id ) {//Free castle up. - ShowNotice("Clearing ownership of castle %d (%s)\n", md->guardian_data->castle->castle_id, md->guardian_data->castle->castle_name); + + if (md->guardian_data->castle->guild_id > 0) { /// Free castle up. + ShowNotice("mob_spawn_guardian_sub: Clearing ownership of castle %d (%s).\n", + md->guardian_data->castle->castle_id, + md->guardian_data->castle->castle_name); guild->castledatasave(md->guardian_data->castle->castle_id, 1, 0); } } else { - if( md->guardian_data && md->guardian_data->number >= 0 && md->guardian_data->number < MAX_GUARDIANS - && md->guardian_data->castle->guardian[md->guardian_data->number].visible ) - guild->castledatasave(md->guardian_data->castle->castle_id, 10+md->guardian_data->number,0); + if (md->guardian_data->number >= 0 && md->guardian_data->number < MAX_GUARDIANS && + md->guardian_data->castle->guardian[md->guardian_data->number].visible == 1) + guild->castledatasave(md->guardian_data->castle->castle_id, + 10 + md->guardian_data->number, 0); - unit->free(&md->bl,CLR_OUTSIGHT); // Remove guardian. + unit->free(&md->bl, CLR_OUTSIGHT); /// Remove guardian. } + return 0; } - if( guild->checkskill(g,GD_GUARDUP) ) - status_calc_mob(md, SCO_NONE); // Give bonuses. + if (guild->checkskill(g, GD_GUARDUP) > 0) + status_calc_mob(md, SCO_NONE); /// Give bonuses. - return 0; + return 1; } -/*========================================== - * Summoning Guardians [Valaris] - *------------------------------------------*/ -static int mob_spawn_guardian(const char *mapname, short x, short y, const char *mobname, int class_, const char *event, int guardian, bool has_index) +/** + * Summons a castle guardian mob. + * + * @param mapname The name of the map where the guardian should be spawned. + * @param x The x coordinate where the guardian should be spawned. + * @param y The y coordinate where the guardian should be spawned. + * @param mobname The guardian's display name. + * @param class_ The guardian's mob ID in database. + * @param event The name of the event which should be executed when the guardian is killed. + * @param guardian The guardian's index. + * @param has_index If false, the guardian will be temporarily. + * @param npc_id If spawned by NPC script, this holds the ID of the invoking NPC. + * @return The spawned guardian's GID, or 0 if spawning failed. + * + * @author Valaris + * + **/ +static int mob_spawn_guardian(const char *mapname, short x, short y, const char *mobname, int class_, const char *event, + int guardian, bool has_index, int npc_id) { - struct mob_data *md=NULL; - struct spawn_data data; - struct guild *g=NULL; - struct guild_castle *gc; - int16 m; - nullpo_ret(mapname); nullpo_ret(mobname); nullpo_ret(event); - memset(&data, 0, sizeof(struct spawn_data)); - data.num = 1; + const int map_id = map->mapname2mapid(mapname); - m=map->mapname2mapid(mapname); - - if(m<0) - { + if (map_id == INDEX_NOT_FOUND) { ShowWarning("mob_spawn_guardian: Map [%s] not found.\n", mapname); return 0; } - data.m = m; - data.num = 1; - if(class_<=0) { - class_ = mob->get_random_id(-class_-1, 1, 99); - if (!class_) return 0; + + if ((x <= 0 || y <= 0) && map->search_freecell(NULL, map_id, &x, &y, -1, -1, 1) == 0) { + ShowWarning("mob_spawn_guardian: Couldn't locate a spawn cell for guardian class %d (index %d) on castle map %s.\n", + class_, guardian, mapname); + return 0; } - data.class_ = class_; + if (class_ <= 0 && (class_ = mob->get_random_id(-class_ - 1, 1, 99)) == 0) + return 0; - if( !has_index ) { + if (!has_index) { guardian = -1; - } else if( guardian < 0 || guardian >= MAX_GUARDIANS ) { - ShowError("mob_spawn_guardian: Invalid guardian index %d for guardian %d (castle map %s)\n", guardian, class_, map->list[m].name); + } else if (guardian < 0 || guardian >= MAX_GUARDIANS) { + ShowError("mob_spawn_guardian: Invalid guardian index %d for guardian %d on castle map %s.\n", + guardian, class_, mapname); return 0; } - if((x<=0 || y<=0) && !map->search_freecell(NULL, m, &x, &y, -1,-1, 1)) { - ShowWarning("mob_spawn_guardian: Couldn't locate a spawn cell for guardian class %d (index %d) at castle map %s\n",class_, guardian, map->list[m].name); - return 0; - } + struct spawn_data data; + + memset(&data, 0, sizeof(struct spawn_data)); + data.num = 1; + data.m = map_id; + data.class_ = class_; data.x = x; data.y = y; safestrncpy(data.name, mobname, sizeof(data.name)); safestrncpy(data.eventname, event, sizeof(data.eventname)); - if (!mob->parse_dataset(&data)) + + if (mob->parse_dataset(&data) == 0) return 0; - gc=guild->mapname2gc(map->list[m].name); + struct guild_castle *gc = guild->mapname2gc(mapname); + if (gc == NULL) { - ShowError("mob_spawn_guardian: No castle set at map %s\n", map->list[m].name); + ShowError("mob_spawn_guardian: No castle set on map %s.\n", mapname); return 0; } - if (!gc->guild_id) - ShowWarning("mob_spawn_guardian: Spawning guardian %d on a castle with no guild (castle map %s)\n", class_, map->list[m].name); + + struct guild *g = NULL; + + if (gc->guild_id == 0) + ShowWarning("mob_spawn_guardian: Spawning guardian %d on a castle map %s with no guild.\n", + class_, mapname); else g = guild->search(gc->guild_id); - if( has_index && gc->guardian[guardian].id ) { - //Check if guardian already exists, refuse to spawn if so. - struct block_list *bl2 = map->id2bl(gc->guardian[guardian].id); // TODO: Why does this not use map->id2md? - struct mob_data *md2 = BL_CAST(BL_MOB, bl2); - if (md2 != NULL && md2->guardian_data != NULL && md2->guardian_data->number == guardian) { - ShowError("mob_spawn_guardian: Attempted to spawn guardian in position %d which already has a guardian (castle map %s)\n", guardian, map->list[m].name); + if (has_index && gc->guardian[guardian].id != 0) { /// Check if guardian already exists, refuse to spawn if so. + struct mob_data *md = map->id2md(gc->guardian[guardian].id); + + if (md != NULL && md->guardian_data != NULL && md->guardian_data->number == guardian) { + ShowError("mob_spawn_guardian: Attempted to spawn guardian in position %d which already has a guardian on castle map %s.\n", + guardian, mapname); return 0; } } - md = mob->spawn_dataset(&data); + struct mob_data *md = mob->spawn_dataset(&data, npc_id); + md->guardian_data = (struct guardian_data*)aCalloc(1, sizeof(struct guardian_data)); md->guardian_data->number = guardian; md->guardian_data->castle = gc; - if( has_index ) - {// permanent guardian + + if (has_index) { /// Permanent guardian. gc->guardian[guardian].id = md->bl.id; - } - else - {// temporary guardian + } else { /// Temporary guardian. int i; + ARR_FIND(0, gc->temp_guardians_max, i, gc->temp_guardians[i] == 0); - if( i == gc->temp_guardians_max ) - { + + if (i == gc->temp_guardians_max) { ++(gc->temp_guardians_max); RECREATE(gc->temp_guardians, int, gc->temp_guardians_max); } + gc->temp_guardians[i] = md->bl.id; } - if( g ) + + if (g != NULL) md->guardian_data->g = g; - else if( gc->guild_id ) - timer->add(timer->gettick()+5000,mob->spawn_guardian_sub,md->bl.id,gc->guild_id); - mob->spawn(md); + else if (gc->guild_id > 0) + timer->add(timer->gettick() + 5000, mob->spawn_guardian_sub, md->bl.id, gc->guild_id); + mob->spawn(md); return md->bl.id; } -/*========================================== - * Summoning BattleGround [Zephyrus] - *------------------------------------------*/ -static int mob_spawn_bg(const char *mapname, short x, short y, const char *mobname, int class_, const char *event, unsigned int bg_id) +/** + * Spawn a mob with allegiance to the given battle group. + * + * @param mapname The name of the map where the mob should be spawned. + * @param x The x coordinate where the mob should be spawned. + * @param y The y coordinate where the mob should be spawned. + * @param mobname The mob's display name. + * @param class_ The mob's mob ID in database. + * @param event The name of the event which should be executed when the mob is killed. + * @param bg_id The battle group ID. + * @param npc_id If spawned by NPC script, this holds the ID of the invoking NPC. + * @return The spawned mob's GID, or 0 if spawning failed. + * + * @author Zephyrus + * + **/ +static int mob_spawn_bg(const char *mapname, short x, short y, const char *mobname, int class_, const char *event, + unsigned int bg_id, int npc_id) { - struct mob_data *md = NULL; - struct spawn_data data; - int16 m; - nullpo_ret(mapname); nullpo_ret(mobname); nullpo_ret(event); - if( (m = map->mapname2mapid(mapname)) < 0 ) { + const int map_id = map->mapname2mapid(mapname); + + if (map_id == INDEX_NOT_FOUND) { ShowWarning("mob_spawn_bg: Map [%s] not found.\n", mapname); return 0; } - memset(&data, 0, sizeof(struct spawn_data)); - data.m = m; - data.num = 1; - if( class_ <= 0 ) - { - class_ = mob->get_random_id(-class_-1,1,99); - if( !class_ ) return 0; + if ((x <= 0 || y <= 0) && map->search_freecell(NULL, map_id, &x, &y, -1, -1, 1) == 0) { + ShowWarning("mob_spawn_bg: Couldn't locate a spawn cell for guardian class %d (bg_id %u) on map %s.\n", class_, bg_id, mapname); + return 0; } - data.class_ = class_; - if( (x <= 0 || y <= 0) && !map->search_freecell(NULL, m, &x, &y, -1,-1, 1) ) { - ShowWarning("mob_spawn_bg: Couldn't locate a spawn cell for guardian class %d (bg_id %u) at map %s\n", class_, bg_id, map->list[m].name); + if (class_ <= 0 && (class_ = mob->get_random_id(-class_ - 1, 1, 99)) == 0) return 0; - } + struct spawn_data data; + + memset(&data, 0, sizeof(struct spawn_data)); + data.num = 1; + data.m = map_id; + data.class_ = class_; data.x = x; data.y = y; safestrncpy(data.name, mobname, sizeof(data.name)); safestrncpy(data.eventname, event, sizeof(data.eventname)); - if( !mob->parse_dataset(&data) ) + + if (mob->parse_dataset(&data) == 0) return 0; - md = mob->spawn_dataset(&data); - mob->spawn(md); - md->bg_id = bg_id; // BG Team ID + struct mob_data *md = mob->spawn_dataset(&data, npc_id); + mob->spawn(md); + md->bg_id = bg_id; return md->bl.id; } @@ -3100,18 +3194,25 @@ static int mob_countslave(struct block_list *bl) return map->foreachinmap(mob->countslave_sub, bl->m, BL_MOB,bl->id); } -/*========================================== - * Summons amount slaves contained in the value[5] array using round-robin. [adapted by Skotlex] - *------------------------------------------*/ +/** + * Summons amount slaves contained in the value[5] array using round-robin. + * + * @param md2 The mob which summons the slaves. + * @param value Array with slave mob IDs. + * @param amount The amount of slaves to spawn. + * @param skill_id The Id of the skill which summons the slaves. + * @return 1 on success, 0 on failure. + * + * @author Skotlex + * + **/ static int mob_summonslave(struct mob_data *md2, int *value, int amount, uint16 skill_id) { - struct mob_data *md; - struct spawn_data data; - int count = 0,k=0,hp_rate=0; - nullpo_ret(md2); nullpo_ret(value); + struct spawn_data data; + memset(&data, 0, sizeof(struct spawn_data)); data.m = md2->bl.m; data.x = md2->bl.x; @@ -3120,31 +3221,42 @@ static int mob_summonslave(struct mob_data *md2, int *value, int amount, uint16 data.state.size = md2->special_state.size; data.state.ai = md2->special_state.ai; - if(mob->db_checkid(value[0]) == 0) + if (mob->db_checkid(value[0]) == 0) return 0; - /** - * Flags this monster is able to summon; saves a worth amount of memory upon deletion - **/ - md2->can_summon = 1; - while(count < 5 && mob->db_checkid(value[count])) count++; - if(count < 1) return 0; - if (amount > 0 && amount < count) { //Do not start on 0, pick some random sub subset [Skotlex] - k = rnd()%count; - amount+=k; //Increase final value by same amount to preserve total number to summon. + md2->can_summon = 1; /// Flags this monster is able to summon; saves a worth amount of memory upon deletion. + + int count = 0; + + while (count < 5 && mob->db_checkid(value[count]) != 0) + count++; + + if (count < 1) + return 0; + + int k = 0; + + if (amount > 0 && amount < count) { /// Do not start on 0, pick some random sub subset. [Skotlex] + k = rnd() % count; + amount += k; /// Increase final value by same amount to preserve total number to summon. } - if (!battle_config.monster_class_change_recover && - (skill_id == NPC_TRANSFORMATION || skill_id == NPC_METAMORPHOSIS)) + int hp_rate = 0; + + if ((skill_id == NPC_TRANSFORMATION || skill_id == NPC_METAMORPHOSIS) && + battle_config.monster_class_change_recover == 0) hp_rate = get_percentage(md2->status.hp, md2->status.max_hp); - for(;k<amount;k++) { - short x,y; - data.class_ = value[k%count]; //Summon slaves in round-robin fashion. [Skotlex] + for (; k < amount; k++) { + data.class_ = value[k % count]; /// Summon slaves in round-robin fashion. [Skotlex] + if (mob->db_checkid(data.class_) == 0) continue; - if (map->search_freecell(&md2->bl, 0, &x, &y, MOB_SLAVEDISTANCE, MOB_SLAVEDISTANCE, 0)) { + short x; + short y; + + if (map->search_freecell(&md2->bl, 0, &x, &y, MOB_SLAVEDISTANCE, MOB_SLAVEDISTANCE, 0) != 0) { data.x = x; data.y = y; } else { @@ -3152,49 +3264,52 @@ static int mob_summonslave(struct mob_data *md2, int *value, int amount, uint16 data.y = md2->bl.y; } - //These two need to be loaded from the db for each slave. if (battle_config.override_mob_names == 1) strcpy(data.name, DEFAULT_MOB_NAME); else strcpy(data.name, DEFAULT_MOB_JNAME); - if (!mob->parse_dataset(&data)) + if (mob->parse_dataset(&data) == 0) continue; - md= mob->spawn_dataset(&data); - if(skill_id == NPC_SUMMONSLAVE){ - md->master_id=md2->bl.id; + struct mob_data *md = mob->spawn_dataset(&data, 0); + + if (skill_id == NPC_SUMMONSLAVE) { + md->master_id = md2->bl.id; md->special_state.ai = md2->special_state.ai; } + mob->spawn(md); - if (hp_rate) //Scale HP - md->status.hp = md->status.max_hp*hp_rate/100; + if (hp_rate > 0) /// Scale HP. + md->status.hp = md->status.max_hp * hp_rate / 100; - //Inherit the aggressive mode of the master. - if (battle_config.slaves_inherit_mode && md->master_id) { + /** Inherit the aggressive mode of the master. **/ + if (battle_config.slaves_inherit_mode > 0 && md->master_id > 0) { switch (battle_config.slaves_inherit_mode) { - case 1: //Always aggressive - if (!(md->status.mode&MD_AGGRESSIVE)) - sc_start4(NULL, &md->bl, SC_MODECHANGE, 100,1,0, MD_AGGRESSIVE, 0, 0); + case 1: /// Always aggressive. + if ((md->status.mode & MD_AGGRESSIVE) == 0) + sc_start4(NULL, &md->bl, SC_MODECHANGE, 100, 1, 0, MD_AGGRESSIVE, 0, 0); + break; - case 2: //Always passive - if (md->status.mode&MD_AGGRESSIVE) - sc_start4(NULL, &md->bl, SC_MODECHANGE, 100,1,0, 0, MD_AGGRESSIVE, 0); + case 2: /// Always passive. + if ((md->status.mode & MD_AGGRESSIVE) == MD_AGGRESSIVE) + sc_start4(NULL, &md->bl, SC_MODECHANGE, 100, 1, 0, 0, MD_AGGRESSIVE, 0); + break; - default: //Copy master. - if (md2->status.mode&MD_AGGRESSIVE) - sc_start4(NULL, &md->bl, SC_MODECHANGE, 100,1,0, MD_AGGRESSIVE, 0, 0); + default: /// Copy master. + if ((md2->status.mode & MD_AGGRESSIVE) == MD_AGGRESSIVE) + sc_start4(NULL, &md->bl, SC_MODECHANGE, 100, 1, 0, MD_AGGRESSIVE, 0, 0); else - sc_start4(NULL, &md->bl, SC_MODECHANGE, 100,1,0, 0, MD_AGGRESSIVE, 0); + sc_start4(NULL, &md->bl, SC_MODECHANGE, 100, 1, 0, 0, MD_AGGRESSIVE, 0); break; } } - clif->skill_nodamage(&md->bl,&md->bl,skill_id,amount,1); + clif->skill_nodamage(&md->bl, &md->bl, skill_id, amount, 1); } - return 0; + return 1; } /*========================================== @@ -3588,211 +3703,238 @@ static int mob_is_clone(int class_) return class_; } -//Flag values: -//&1: Set special AI (fight mobs, not players) -//If mode is not passed, a default aggressive mode is used. -//If master_id is passed, clone is attached to him. -//Returns: ID of newly crafted copy. -static int mob_clone_spawn(struct map_session_data *sd, int16 m, int16 x, int16 y, const char *event, int master_id, uint32 mode, int flag, unsigned int duration) +/** + * Spawns a mob which is a clone of another character. + * + * @param sd The character which should be cloned. + * @param m The ID of the map where the clone should be spawned. + * @param x The x coordinate where the clone should be spawned. + * @param y The y coordinate where the clone should be spawned. + * @param event The name of the event which should be executed when the clone is killed. + * @param master_id If passed, the clone will be attached to this account ID. + * @param mode The clone's mob mode(s). (Defaults to MD_CANMOVE|MD_AGGRESSIVE|MD_ASSIST|MD_CANATTACK.) + * @param flag 0 - target characters; 1 - target mobs. + * @param duration How long the clone will live before it is auto-removed. (ms) + * @return The spawned clone's GID, or 0 if spawning failed. + * + **/ +static int mob_clone_spawn(struct map_session_data *sd, int16 m, int16 x, int16 y, const char *event, int master_id, + uint32 mode, int flag, unsigned int duration) { - int class_; - int i,j,h,inf, fd; - struct mob_data *md; - struct mob_skill *ms; - struct mob_db* db; - struct status_data *mstatus; - nullpo_ret(sd); - if(pc_isdead(sd) && master_id && flag&1) + if (pc_isdead(sd) && master_id != 0 && flag == 1) return 0; - ARR_FIND( MOB_CLONE_START, MOB_CLONE_END, class_, mob->db_data[class_] == NULL ); - if(class_ < 0 || class_ >= MOB_CLONE_END) + int class_; + + ARR_FIND(MOB_CLONE_START, MOB_CLONE_END, class_, mob->db_data[class_] == NULL); + + if (class_ < 0 || class_ >= MOB_CLONE_END) return 0; - db = mob->db_data[class_]=(struct mob_db*)aCalloc(1, sizeof(struct mob_db)); - mstatus = &db->status; - strcpy(db->sprite,sd->status.name); - strcpy(db->name,sd->status.name); - strcpy(db->jname,sd->status.name); - db->lv=status->get_lv(&sd->bl); + mob->db_data[class_] = (struct mob_db*)aCalloc(1, sizeof(struct mob_db)); + + struct mob_db *db = mob->db_data[class_]; + struct status_data *mstatus = &db->status; + + strcpy(db->sprite, sd->status.name); + strcpy(db->name, sd->status.name); + strcpy(db->jname, sd->status.name); + db->lv = status->get_lv(&sd->bl); memcpy(mstatus, &sd->base_status, sizeof(struct status_data)); - mstatus->rhw.atk2= mstatus->dex + mstatus->rhw.atk + mstatus->rhw.atk2; //Max ATK - mstatus->rhw.atk = mstatus->dex; //Min ATK - if (mstatus->lhw.atk) { - mstatus->lhw.atk2= mstatus->dex + mstatus->lhw.atk + mstatus->lhw.atk2; //Max ATK - mstatus->lhw.atk = mstatus->dex; //Min ATK + mstatus->rhw.atk2 = mstatus->dex + mstatus->rhw.atk + mstatus->rhw.atk2; /// Max ATK. + mstatus->rhw.atk = mstatus->dex; /// Min ATK. + + if (mstatus->lhw.atk > 0) { + mstatus->lhw.atk2 = mstatus->dex + mstatus->lhw.atk + mstatus->lhw.atk2; /// Max ATK. + mstatus->lhw.atk = mstatus->dex; /// Min ATK. } - if (mode != MD_NONE) //User provided mode. + + if (mode != MD_NONE) /// User provided mode. mstatus->mode = mode; - else if (flag&1) //Friendly Character, remove looting. + else if (flag == 1) /// Friendly Character, remove looting. mstatus->mode &= ~MD_LOOTER; + mstatus->hp = mstatus->max_hp; mstatus->sp = mstatus->max_sp; memcpy(&db->vd, &sd->vd, sizeof(struct view_data)); - db->base_exp=1; - db->job_exp=1; - db->range2=AREA_SIZE; //Let them have the same view-range as players. - db->range3=AREA_SIZE; //Min chase of a screen. - db->option=sd->sc.option; + db->base_exp = 1; + db->job_exp = 1; + db->range2 = AREA_SIZE; /// Let them have the same view-range as players. + db->range3 = AREA_SIZE; /// Min chase of a screen. + db->option = sd->sc.option; - //Skill copy [Skotlex] - ms = &db->skill[0]; + const int fd = sd->fd; - /** - * We temporarily disable sd's fd so it doesn't receive the messages from skill_check_condition_castbegin - **/ - fd = sd->fd; - sd->fd = 0; - - //Go Backwards to give better priority to advanced skills. - for (i=0,j = MAX_SKILL_TREE-1;j>=0 && i< MAX_MOBSKILL ;j--) { - int idx = pc->skill_tree[pc->class2idx(sd->status.class)][j].idx; - int skill_id = pc->skill_tree[pc->class2idx(sd->status.class)][j].id; - if (!skill_id || sd->status.skill[idx].lv < 1 || - (skill->dbs->db[idx].inf2&(INF2_WEDDING_SKILL|INF2_GUILD_SKILL)) - ) + sd->fd = 0; /// Temporarily disable sd's fd so it doesn't receive the messages from skill_check_condition_castbegin. + + struct mob_skill *mob_skills = &db->skill[0]; + + /// Go Backwards to give better priority to advanced skills. + for (int i = 0, j = MAX_SKILL_TREE - 1; j >= 0 && i < MAX_MOBSKILL; j--) { + const int idx = pc->skill_tree[pc->class2idx(sd->status.class)][j].idx; + const int skill_id = pc->skill_tree[pc->class2idx(sd->status.class)][j].id; + + if (skill_id == 0 || sd->status.skill[idx].lv < 1 || + (skill->dbs->db[idx].inf2 & (INF2_WEDDING_SKILL | INF2_GUILD_SKILL)) > 0) continue; - for(h = 0; h < map->list[sd->bl.m].zone->disabled_skills_count; h++) { - if( skill_id == map->list[sd->bl.m].zone->disabled_skills[h]->nameid && map->list[sd->bl.m].zone->disabled_skills[h]->subtype == MZS_CLONE ) { + + int h; + + for (h = 0; h < map->list[sd->bl.m].zone->disabled_skills_count; h++) { + if (skill_id == map->list[sd->bl.m].zone->disabled_skills[h]->nameid && + map->list[sd->bl.m].zone->disabled_skills[h]->subtype == MZS_CLONE) break; - } } - if( h < map->list[sd->bl.m].zone->disabled_skills_count ) + + if (h < map->list[sd->bl.m].zone->disabled_skills_count) continue; - //Normal aggressive mob, disable skills that cannot help them fight - //against players (those with flags UF_NOMOB and UF_NOPC are specific - //to always aid players!) [Skotlex] - if (!(flag&1) && - skill->get_unit_id(skill_id, 0) && - skill->get_unit_flag(skill_id)&(UF_NOMOB|UF_NOPC)) + + /// Normal aggressive mob. Disable skills that cannot help fighting against players. (Those with flags UF_NOMOB and UF_NOPC are specific to always aid players!) [Skotlex] + if (flag == 0 && skill->get_unit_id(skill_id, 0) != 0 && + (skill->get_unit_flag(skill_id) & (UF_NOMOB | UF_NOPC)) > 0) continue; - /** - * The clone should be able to cast the skill (e.g. have the required weapon) bugreport:5299) - **/ - if( !skill->check_condition_castbegin(sd,skill_id,sd->status.skill[idx].lv) ) + + /// The clone should be able to cast the skill. (E.g. have the required weapon.) [bugreport:5299] + if (skill->check_condition_castbegin(sd, skill_id, sd->status.skill[idx].lv) == 0) continue; - memset (&ms[i], 0, sizeof(struct mob_skill)); - ms[i].skill_id = skill_id; - ms[i].skill_lv = sd->status.skill[idx].lv; - ms[i].state = MSS_ANY; - ms[i].permillage = 500*battle_config.mob_skill_rate/100; //Default chance of all skills: 5% - ms[i].emotion = -1; - ms[i].cancel = 0; - ms[i].casttime = skill->cast_fix(&sd->bl,skill_id, ms[i].skill_lv); - ms[i].delay = 5000+skill->delay_fix(&sd->bl,skill_id, ms[i].skill_lv); - - inf = skill->dbs->db[idx].inf; - if (inf&INF_ATTACK_SKILL) { - ms[i].target = MST_TARGET; - ms[i].cond1 = MSC_ALWAYS; - if (skill->get_range(skill_id, ms[i].skill_lv) > 3) - ms[i].state = MSS_ANYTARGET; + memset(&mob_skills[i], 0, sizeof(struct mob_skill)); + mob_skills[i].skill_id = skill_id; + mob_skills[i].skill_lv = sd->status.skill[idx].lv; + mob_skills[i].state = MSS_ANY; + mob_skills[i].permillage = 500 * battle_config.mob_skill_rate / 100; /// Default chance of all skills: 5% + mob_skills[i].emotion = -1; + mob_skills[i].cancel = 0; + mob_skills[i].casttime = skill->cast_fix(&sd->bl, skill_id, mob_skills[i].skill_lv); + mob_skills[i].delay = 5000 + skill->delay_fix(&sd->bl, skill_id, mob_skills[i].skill_lv); + + const int inf = skill->dbs->db[idx].inf; + + if ((inf & INF_ATTACK_SKILL) == INF_ATTACK_SKILL) { + mob_skills[i].target = MST_TARGET; + mob_skills[i].cond1 = MSC_ALWAYS; + + if (skill->get_range(skill_id, mob_skills[i].skill_lv) > 3) + mob_skills[i].state = MSS_ANYTARGET; else - ms[i].state = MSS_BERSERK; - } else if(inf&INF_GROUND_SKILL) { - if (skill->get_inf2(skill_id)&INF2_TRAP) { //Traps! - ms[i].state = MSS_IDLE; - ms[i].target = MST_AROUND2; - ms[i].delay = 60000; - } else if (skill->get_unit_target(skill_id) == BCT_ENEMY) { //Target Enemy - ms[i].state = MSS_ANYTARGET; - ms[i].target = MST_TARGET; - ms[i].cond1 = MSC_ALWAYS; - } else { //Target allies - ms[i].target = MST_FRIEND; - ms[i].cond1 = MSC_FRIENDHPLTMAXRATE; - ms[i].cond2 = 95; + mob_skills[i].state = MSS_BERSERK; + } else if ((inf & INF_GROUND_SKILL) == INF_GROUND_SKILL) { + if ((skill->get_inf2(skill_id) & INF2_TRAP) == INF2_TRAP) { /// Traps! + mob_skills[i].state = MSS_IDLE; + mob_skills[i].target = MST_AROUND2; + mob_skills[i].delay = 60000; + } else if (skill->get_unit_target(skill_id) == BCT_ENEMY) { /// Target Enemy. + mob_skills[i].state = MSS_ANYTARGET; + mob_skills[i].target = MST_TARGET; + mob_skills[i].cond1 = MSC_ALWAYS; + } else { /// Target allies. + mob_skills[i].target = MST_FRIEND; + mob_skills[i].cond1 = MSC_FRIENDHPLTMAXRATE; + mob_skills[i].cond2 = 95; } - } else if (inf&INF_SELF_SKILL) { - if (skill->get_inf2(skill_id)&INF2_NO_TARGET_SELF) { //auto-select target skill. - ms[i].target = MST_TARGET; - ms[i].cond1 = MSC_ALWAYS; - if (skill->get_range(skill_id, ms[i].skill_lv) > 3) { - ms[i].state = MSS_ANYTARGET; - } else { - ms[i].state = MSS_BERSERK; - } - } else { //Self skill - ms[i].target = MST_SELF; - ms[i].cond1 = MSC_MYHPLTMAXRATE; - ms[i].cond2 = 90; - ms[i].permillage = 2000; - //Delay: Remove the stock 5 secs and add half of the support time. - ms[i].delay += -5000 +(skill->get_time(skill_id, ms[i].skill_lv) + skill->get_time2(skill_id, ms[i].skill_lv))/2; - if (ms[i].delay < 5000) - ms[i].delay = 5000; //With a minimum of 5 secs. + } else if ((inf & INF_SELF_SKILL) == INF_SELF_SKILL) { + if ((skill->get_inf2(skill_id) & INF2_NO_TARGET_SELF) == INF2_NO_TARGET_SELF) { /// Auto-select target skill. + mob_skills[i].target = MST_TARGET; + mob_skills[i].cond1 = MSC_ALWAYS; + + if (skill->get_range(skill_id, mob_skills[i].skill_lv) > 3) + mob_skills[i].state = MSS_ANYTARGET; + else + mob_skills[i].state = MSS_BERSERK; + } else { /// Self skill. + mob_skills[i].target = MST_SELF; + mob_skills[i].cond1 = MSC_MYHPLTMAXRATE; + mob_skills[i].cond2 = 90; + mob_skills[i].permillage = 2000; + + const int time1 = skill->get_time(skill_id, mob_skills[i].skill_lv); + const int time2 = skill->get_time2(skill_id, mob_skills[i].skill_lv); + + /** Delay: Remove the stock 5 secs and add half of the support time. **/ + mob_skills[i].delay += -5000 + (time1 + time2) / 2; + + if (mob_skills[i].delay < 5000) + mob_skills[i].delay = 5000; /// With a minimum of 5 seconds. } - } else if (inf&INF_SUPPORT_SKILL) { - ms[i].target = MST_FRIEND; - ms[i].cond1 = MSC_FRIENDHPLTMAXRATE; - ms[i].cond2 = 90; + } else if ((inf & INF_SUPPORT_SKILL) == INF_SUPPORT_SKILL) { + mob_skills[i].target = MST_FRIEND; + mob_skills[i].cond1 = MSC_FRIENDHPLTMAXRATE; + mob_skills[i].cond2 = 90; + if (skill_id == AL_HEAL) - ms[i].permillage = 5000; //Higher skill rate usage for heal. + mob_skills[i].permillage = 5000; /// Higher skill rate usage for heal. else if (skill_id == ALL_RESURRECTION) - ms[i].cond2 = 1; - //Delay: Remove the stock 5 secs and add half of the support time. - ms[i].delay += -5000 +(skill->get_time(skill_id, ms[i].skill_lv) + skill->get_time2(skill_id, ms[i].skill_lv))/2; - if (ms[i].delay < 2000) - ms[i].delay = 2000; //With a minimum of 2 secs. - - if (i+1 < MAX_MOBSKILL) { //duplicate this so it also triggers on self. - memcpy(&ms[i+1], &ms[i], sizeof(struct mob_skill)); + mob_skills[i].cond2 = 1; + + const int time1 = skill->get_time(skill_id, mob_skills[i].skill_lv); + const int time2 = skill->get_time2(skill_id, mob_skills[i].skill_lv); + + /** Delay: Remove the stock 5 secs and add half of the support time. **/ + mob_skills[i].delay += -5000 + (time1 + time2) / 2; + + if (mob_skills[i].delay < 2000) + mob_skills[i].delay = 2000; /// With a minimum of 2 seconds. + + if (i + 1 < MAX_MOBSKILL) { /// Duplicate this so it also triggers on self. + memcpy(&mob_skills[i + 1], &mob_skills[i], sizeof(struct mob_skill)); db->maxskill = ++i; - ms[i].target = MST_SELF; - ms[i].cond1 = MSC_MYHPLTMAXRATE; + mob_skills[i].target = MST_SELF; + mob_skills[i].cond1 = MSC_MYHPLTMAXRATE; } } else { - switch (skill_id) { //Certain Special skills that are passive, and thus, never triggered. - case MO_TRIPLEATTACK: - case TF_DOUBLE: - case GS_CHAINACTION: - ms[i].state = MSS_BERSERK; - ms[i].target = MST_TARGET; - ms[i].cond1 = MSC_ALWAYS; - ms[i].permillage = skill_id==MO_TRIPLEATTACK?(3000-ms[i].skill_lv*100):(ms[i].skill_lv*500); - ms[i].delay -= 5000; //Remove the added delay as these could trigger on "all hits". - break; - default: //Untreated Skill - continue; + switch (skill_id) { /// Certain special skills that are passive, and thus, never triggered. + case MO_TRIPLEATTACK: + FALLTHROUGH + case TF_DOUBLE: + FALLTHROUGH + case GS_CHAINACTION: + mob_skills[i].state = MSS_BERSERK; + mob_skills[i].target = MST_TARGET; + mob_skills[i].cond1 = MSC_ALWAYS; + + if (skill_id == MO_TRIPLEATTACK) + mob_skills[i].permillage = 3000 - mob_skills[i].skill_lv * 100; + else + mob_skills[i].permillage = mob_skills[i].skill_lv * 500; + + mob_skills[i].delay -= 5000; /// Remove the added delay as these could trigger on "all hits". + break; + default: /// Untreated skill. + continue; } } - if (battle_config.mob_skill_rate!= 100) - ms[i].permillage = ms[i].permillage*battle_config.mob_skill_rate/100; - if (battle_config.mob_skill_delay != 100) - ms[i].delay = ms[i].delay*battle_config.mob_skill_delay/100; + mob_skills[i].permillage *= battle_config.mob_skill_rate / 100; + mob_skills[i].delay *= battle_config.mob_skill_delay / 100; db->maxskill = ++i; } - /** - * We grant the session it's fd value back. - **/ - sd->fd = fd; + sd->fd = fd; /// We grant the session it's fd value back. + + /// Finally spawn it. + struct mob_data *md = mob->once_spawn_sub(&sd->bl, m, x, y, DEFAULT_MOB_NAME, class_, event, SZ_SMALL, AI_NONE, 0); - //Finally, spawn it. - md = mob->once_spawn_sub(&sd->bl, m, x, y, DEFAULT_MOB_NAME, class_, event, SZ_SMALL, AI_NONE); - if (!md) return 0; //Failed? + if (md == NULL) + return 0; /// Failed? md->special_state.clone = 1; - if (master_id || flag || duration) { //Further manipulate crafted char. - if (flag&1) //Friendly Character - md->special_state.ai = AI_ATTACK; - if (master_id) //Attach to Master - md->master_id = master_id; - if (duration) //Auto Delete after a while. - { - if( md->deletetimer != INVALID_TIMER ) - timer->delete(md->deletetimer, mob->timer_delete); - md->deletetimer = timer->add(timer->gettick() + duration, mob->timer_delete, md->bl.id, 0); - } + if (flag == 1) /// Friendly character. + md->special_state.ai = AI_ATTACK; + + if (master_id != 0) /// Attach to master. + md->master_id = master_id; + + if (duration > 0) { /// Auto delete after a while. + if (md->deletetimer != INVALID_TIMER) + timer->delete(md->deletetimer, mob->timer_delete); + + md->deletetimer = timer->add(timer->gettick() + duration, mob->timer_delete, md->bl.id, 0); } mob->spawn(md); - return md->bl.id; } diff --git a/src/map/mob.h b/src/map/mob.h index 8fd16f191..8839809f2 100644 --- a/src/map/mob.h +++ b/src/map/mob.h @@ -255,6 +255,7 @@ struct mob_data { int areanpc_id; //Required in OnTouchNPC (to avoid multiple area touchs) unsigned int bg_id; // BattleGround System int clan_id; // Clan System + int npc_id; // NPC ID if spawned with monster/areamonster/guardian/bg_monster/atcommand("@monster xy") (Used to kill mob on NPC unload.) int64 next_walktime, last_thinktime, last_linktime, last_pcneartime, dmgtick; short move_fail_count; @@ -507,14 +508,14 @@ struct mob_interface { int (*db_checkid) (const int id); struct view_data* (*get_viewdata) (int class_); int (*parse_dataset) (struct spawn_data *data); - struct mob_data* (*spawn_dataset) (struct spawn_data *data); + struct mob_data* (*spawn_dataset) (struct spawn_data *data, int npc_id); int (*get_random_id) (int type, int flag, int lv); bool (*ksprotected) (struct block_list *src, struct block_list *target); - struct mob_data* (*once_spawn_sub) (struct block_list *bl, int16 m, int16 x, int16 y, const char *mobname, int class_, const char *event, unsigned int size, unsigned int ai); + struct mob_data* (*once_spawn_sub) (struct block_list *bl, int16 m, int16 x, int16 y, const char *mobname, int class_, const char *event, unsigned int size, unsigned int ai, int npc_id); int (*once_spawn) (struct map_session_data *sd, int16 m, int16 x, int16 y, const char *mobname, int class_, int amount, const char *event, unsigned int size, unsigned int ai); int (*once_spawn_area) (struct map_session_data *sd, int16 m, int16 x0, int16 y0, int16 x1, int16 y1, const char *mobname, int class_, int amount, const char *event, unsigned int size, unsigned int ai); - int (*spawn_guardian) (const char *mapname, short x, short y, const char *mobname, int class_, const char *event, int guardian, bool has_index); - int (*spawn_bg) (const char *mapname, short x, short y, const char *mobname, int class_, const char *event, unsigned int bg_id); + int (*spawn_guardian) (const char *mapname, short x, short y, const char *mobname, int class_, const char *event, int guardian, bool has_index, int npc_id); + int (*spawn_bg) (const char *mapname, short x, short y, const char *mobname, int class_, const char *event, unsigned int bg_id, int npc_id); int (*can_reach) (struct mob_data *md, struct block_list *bl, int range, int state); int (*linksearch) (struct block_list *bl, va_list ap); int (*delayspawn) (int tid, int64 tick, int id, intptr_t data); diff --git a/src/map/npc.c b/src/map/npc.c index 657fb987e..d369aca82 100644 --- a/src/map/npc.c +++ b/src/map/npc.c @@ -2935,126 +2935,193 @@ static int npc_unload_ev_label(union DBKey key, struct DBData *data, va_list ap) return 0; } -//Chk if npc matches src_id, then unload. -//Sub-function used to find duplicates. +/** + * Unloads a NPC if it's a duplicate of the passed one. + * + * @param nd The NPC to check. + * @param args List of arguments. + * @return Always 0. + * + **/ static int npc_unload_dup_sub(struct npc_data *nd, va_list args) { - int src_id; - nullpo_ret(nd); - src_id = va_arg(args, int); + + const int src_id = va_arg(args, int); + const int unload_mobs = va_arg(args, int); + if (nd->src_id == src_id) - npc->unload(nd, true); + npc->unload(nd, true, (unload_mobs == 1)); + return 0; } -//Removes all npcs that are duplicates of the passed one. [Skotlex] -static void npc_unload_duplicates(struct npc_data *nd) +/** + * Unloads all NPCs which are duplicates of the passed one. + * + * @param nd The source NPC. + * @param unload_mobs If true, mobs spawned by duplicates will be removed. + * + * @author Skotlex + * + **/ +static void npc_unload_duplicates(struct npc_data *nd, bool unload_mobs) { nullpo_retv(nd); - map->foreachnpc(npc->unload_dup_sub,nd->bl.id); + + map->foreachnpc(npc->unload_dup_sub, nd->bl.id, unload_mobs); +} + +/** + * Removes a mob, which was spawned by a NPC (monster/areamonster/guardian/bg_monster/atcommand("@monster xy")). + * + * @param md The mob to remove. + * @param args List of arguments. + * @return 1 on success, 0 on failure. + * + * @author Kenpachi + * + **/ +static int npc_unload_mob(struct mob_data *md, va_list args) +{ + nullpo_ret(md); + + const int npc_id = va_arg(args, int); + + if (md->npc_id == npc_id) { + md->state.npc_killmonster = 1; + status_kill(&md->bl); + return 1; + } + + return 0; } -//Removes an npc from map and db. -//Single is to free name (for duplicates). -static int npc_unload(struct npc_data *nd, bool single) +/** + * Removes a NPC from map and database. + * + * @param nd The NPC which should be removed. + * @param single If true, names are freed. (For duplicates.) + * @param unload_mobs If true, mobs spawned by the NPC will be removed. + * @return Always 0. + * + **/ +static int npc_unload(struct npc_data *nd, bool single, bool unload_mobs) { nullpo_ret(nd); - if( nd->ud && nd->ud != &npc->base_ud ) { + if (nd->ud != NULL && nd->ud != &npc->base_ud) skill->clear_unitgroup(&nd->bl); - } npc->remove_map(nd); map->deliddb(&nd->bl); - if( single ) + + if (single) strdb_remove(npc->name_db, nd->exname); - if (nd->chat_id) // remove npc chatroom object and kick users + if (nd->chat_id != 0) /// Remove NPC chatroom object and kick users. chat->delete_npc_chat(nd); - npc_chat->finalize(nd); // deallocate npc PCRE data structures + npc_chat->finalize(nd); /// Deallocate NPC PCRE data structures. if (single && nd->path != NULL) { npc->releasepathreference(nd->path); nd->path = NULL; } - if (single && nd->bl.m != -1) + if (single && nd->bl.m != INDEX_NOT_FOUND) map->remove_questinfo(nd->bl.m, nd); + npc->questinfo_clear(nd); - if (nd->src_id == 0 && ( nd->subtype == SHOP || nd->subtype == CASHSHOP)) { - //src check for duplicate shops [Orcao] - aFree(nd->u.shop.shop_item); + if (nd->src_id == 0 && (nd->subtype == SHOP || nd->subtype == CASHSHOP)) { + aFree(nd->u.shop.shop_item); /// src check for duplicate shops. [Orcao] } else if (nd->subtype == SCRIPT) { - struct s_mapiterator *iter; - struct map_session_data *sd = NULL; + char evname[EVENT_NAME_LENGTH]; + + snprintf(evname, ARRAYLENGTH(evname), "%s::OnNPCUnload", nd->exname); + + struct event_data *ev = strdb_get(npc->ev_db, evname); + + if (ev != NULL) + script->run_npc(nd->u.scr.script, ev->pos, 0, nd->bl.id); /// Run OnNPCUnload. - if( single ) { - npc->ev_db->foreach(npc->ev_db,npc->unload_ev,nd->exname); //Clean up all events related - npc->ev_label_db->foreach(npc->ev_label_db,npc->unload_ev_label,nd); + if (single) { + npc->ev_db->foreach(npc->ev_db, npc->unload_ev, nd->exname); /// Clean up all related events. + npc->ev_label_db->foreach(npc->ev_label_db, npc->unload_ev_label, nd); } - iter = mapit_geteachpc(); - for (sd = BL_UCAST(BL_PC, mapit->first(iter)); mapit->exists(iter); sd = BL_UCAST(BL_PC, mapit->next(iter))) { - if (sd->npc_timer_id != INVALID_TIMER ) { + struct s_mapiterator *iter = mapit_geteachpc(); + struct map_session_data *sd = BL_UCAST(BL_PC, mapit->first(iter)); + + for (; mapit->exists(iter); sd = BL_UCAST(BL_PC, mapit->next(iter))) { + if (sd->npc_timer_id != INVALID_TIMER) { const struct TimerData *td = timer->get(sd->npc_timer_id); - if( td && td->id != nd->bl.id ) + if (td != NULL && td->id != nd->bl.id) continue; - if( td && td->data ) + if (td != NULL && td->data != 0) ers_free(npc->timer_event_ers, (void*)td->data); + timer->delete(sd->npc_timer_id, npc->timerevent); sd->npc_timer_id = INVALID_TIMER; } } + mapit->free(iter); if (nd->u.scr.timerid != INVALID_TIMER) { - const struct TimerData *td; - td = timer->get(nd->u.scr.timerid); - if (td && td->data) + const struct TimerData *td = timer->get(nd->u.scr.timerid); + + if (td != NULL && td->data != 0) ers_free(npc->timer_event_ers, (void*)td->data); + timer->delete(nd->u.scr.timerid, npc->timerevent); } - if (nd->u.scr.timer_event) + + if (nd->u.scr.timer_event != NULL) aFree(nd->u.scr.timer_event); + if (nd->src_id == 0) { - if(nd->u.scr.script) { + if (nd->u.scr.script != NULL) { script->free_code(nd->u.scr.script); nd->u.scr.script = NULL; } - if (nd->u.scr.label_list) { + + if (nd->u.scr.label_list != NULL) { aFree(nd->u.scr.label_list); nd->u.scr.label_list = NULL; nd->u.scr.label_list_num = 0; } - if(nd->u.scr.shop) { - if(nd->u.scr.shop->item) { + + if (nd->u.scr.shop != NULL) { + if (nd->u.scr.shop->item != NULL) { for (int i = 0; i < nd->u.scr.shop->items; i ++) { - if (nd->u.scr.shop->item[i].currency) + if (nd->u.scr.shop->item[i].currency != NULL) aFree(nd->u.scr.shop->item[i].currency); } aFree(nd->u.scr.shop->item); } + aFree(nd->u.scr.shop); } } - if( nd->u.scr.guild_id ) + + if (nd->u.scr.guild_id > 0) guild->flag_remove(nd); } - if( nd->ud && nd->ud != &npc->base_ud ) { + if (nd->ud != NULL && nd->ud != &npc->base_ud) { aFree(nd->ud); nd->ud = NULL; } - HPM->data_store_destroy(&nd->hdata); + if (unload_mobs) + map->foreachmob(npc->unload_mob, nd->bl.id); + HPM->data_store_destroy(&nd->hdata); aFree(nd); - return 0; } @@ -4431,18 +4498,23 @@ static const char *npc_parse_function(const char *w1, const char *w2, const char return end; } -/*========================================== - * Parse Mob 1 - Parse mob list into each map - * Parse Mob 2 - Actually Spawns Mob - * [Wizputer] - *------------------------------------------*/ +/** + * Spawns a mob by using the passed spawn data. (Permanent mob spawns.) + * npc_parse_mob() - Parses mob list into each map. + * npc_parse_mob2() - Actually spawns mob. + * + * @param mobspawn The mobs spawn data. + * + * @author Wizputer + * + **/ static void npc_parse_mob2(struct spawn_data *mobspawn) { - int i; - nullpo_retv(mobspawn); - for( i = mobspawn->active; i < mobspawn->num; ++i ) { - struct mob_data* md = mob->spawn_dataset(mobspawn); + + for (int i = mobspawn->active; i < mobspawn->num; ++i) { + struct mob_data *md = mob->spawn_dataset(mobspawn, 0); + md->spawn = mobspawn; md->spawn->active++; mob->spawn(md); @@ -5499,129 +5571,143 @@ static void npc_process_files(int npc_min) npc->npc_id - npc_min, npc->npc_warp, npc->npc_shop, npc->npc_script, npc->npc_mob, npc->npc_cache_mob, npc->npc_delay_mob); } -//Clear then reload npcs files +/** + * Clears and then reloads all NPC files. + * + * @return Always 0. + * + **/ static int npc_reload(void) { - int npc_new_min = npc->npc_id; - struct s_mapiterator* iter; - struct block_list* bl; - - if (map->retval == EXIT_FAILURE) - map->retval = EXIT_SUCCESS; // Clear return status in case something failed before. - - /* clear guild flag cache */ - guild->flags_clear(); + if (map->retval == EXIT_FAILURE) /// Clear return status in case something failed before. + map->retval = EXIT_SUCCESS; + guild->flags_clear(); /// Clear guild flag cache. npc->path_db->clear(npc->path_db, npc->path_db_clear_sub); - db_clear(npc->name_db); db_clear(npc->ev_db); npc->ev_label_db->clear(npc->ev_label_db, npc->ev_label_db_clear_sub); - npc->npc_last_npd = NULL; npc->npc_last_path = NULL; npc->npc_last_ref = NULL; + + const int npc_new_min = npc->npc_id; + struct s_mapiterator *iter = mapit_geteachiddb(); - //Remove all npcs/mobs. [Skotlex] - iter = mapit_geteachiddb(); - for (bl = mapit->first(iter); mapit->exists(iter); bl = mapit->next(iter)) { - switch(bl->type) { - case BL_NPC: - if( bl->id != npc->fake_nd->bl.id )// don't remove fake_nd - npc->unload(BL_UCAST(BL_NPC, bl), false); - break; - case BL_MOB: - unit->free(bl,CLR_OUTSIGHT); - break; + /** Remove all NPCs/mobs. [Skotlex] **/ + for (struct block_list *bl = mapit->first(iter); mapit->exists(iter); bl = mapit->next(iter)) { + switch (bl->type) { + case BL_NPC: + if (bl->id != npc->fake_nd->bl.id) /// Don't remove fake_nd. + npc->unload(BL_UCAST(BL_NPC, bl), false, false); + + break; + case BL_MOB: + unit->free(bl, CLR_OUTSIGHT); + break; + default: + break; } } + mapit->free(iter); - if(battle_config.dynamic_mobs) {// dynamic check by [random] - int16 m; - for (m = 0; m < map->count; m++) { - int16 i; - for (i = 0; i < MAX_MOB_LIST_PER_MAP; i++) { + if (battle_config.dynamic_mobs) { /// Dynamic check. [random] + for (int m = 0; m < map->count; m++) { + for (int i = 0; i < MAX_MOB_LIST_PER_MAP; i++) { if (map->list[m].moblist[i] != NULL) { aFree(map->list[m].moblist[i]); map->list[m].moblist[i] = NULL; } - if( map->list[m].mob_delete_timer != INVALID_TIMER ) - { // Mobs were removed anyway,so delete the timer [Inkfish] + + if (map->list[m].mob_delete_timer != INVALID_TIMER) { /// Mobs were removed anyway, so delete the timer. [Inkfish] timer->delete(map->list[m].mob_delete_timer, map->removemobs_timer); map->list[m].mob_delete_timer = INVALID_TIMER; } } + if (map->list[m].npc_num > 0) - ShowWarning("npc_reload: %d npcs weren't removed at map %s!\n", map->list[m].npc_num, map->list[m].name); + ShowWarning("npc_reload: %d NPCs weren't removed from map %s!\n", + map->list[m].npc_num, map->list[m].name); } } - // clear mob spawn lookup index mob->clear_spawninfo(); - - npc->npc_warp = npc->npc_shop = npc->npc_script = 0; - npc->npc_mob = npc->npc_cache_mob = npc->npc_delay_mob = 0; - - // reset mapflags + npc->npc_warp = 0; + npc->npc_shop = 0; + npc->npc_script = 0; + npc->npc_mob = 0; + npc->npc_cache_mob = 0; + npc->npc_delay_mob = 0; map->zone_reload(); map->flags_init(); - - // Reprocess npc files and reload constants itemdb->name_constants(); clan->set_constants(); - npc_process_files( npc_new_min ); - + npc_process_files(npc_new_min); instance->reload(); - map->zone_init(); - - npc->motd = npc->name2id("HerculesMOTD"); /* [Ind/Hercules] */ - - //Re-read the NPC Script Events cache. + npc->motd = npc->name2id("HerculesMOTD"); /// [Ind/Hercules] npc->read_event_script(); - // Execute main initialisation events - // The correct initialisation order is: - // OnInit -> OnInterIfInit -> OnInterIfInitOnce -> OnAgitInit -> OnAgitInit2 - npc->event_do_oninit( true ); + /** + * Execute main initialization events + * The correct initialization order is: + * OnInit -> OnInterIfInit -> OnInterIfInitOnce -> OnAgitInit -> OnAgitInit2 + * + **/ + npc->event_do_oninit(true); + npc->market_fromsql(); npc->barter_fromsql(); npc->expanded_barter_fromsql(); - // Execute rest of the startup events if connected to char-server. [Lance] - // Executed when connection is established with char-server in chrif_connectack - if( !intif->CheckForCharServer() ) { - ShowStatus("Event '"CL_WHITE"OnInterIfInit"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc->event_doall("OnInterIfInit")); - ShowStatus("Event '"CL_WHITE"OnInterIfInitOnce"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc->event_doall("OnInterIfInitOnce")); - } - // Refresh guild castle flags on both woe setups - // These events are only executed after receiving castle information from char-server + + /* + * Execute rest of the startup events if connected to char-server. [Lance] + * Executed when connection is established with char-server in chrif_connectack(). + */ + if (intif->CheckForCharServer() == 0) { + ShowStatus("Event '"CL_WHITE"OnInterIfInit"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", + npc->event_doall("OnInterIfInit")); + ShowStatus("Event '"CL_WHITE"OnInterIfInitOnce"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", + npc->event_doall("OnInterIfInitOnce")); + } + + /* + * Refresh guild castle flags on both WoE setups. + * These events are only executed after receiving castle information from char-server. + */ npc->event_doall("OnAgitInit"); npc->event_doall("OnAgitInit2"); return 0; } -//Unload all npc in the given file -static bool npc_unloadfile(const char *filepath) +/** + * Unloads all NPCs in the given file. + * + * @param filepath Path to the file which should be unloaded. + * @param unload_mobs If true, mobs spawned by NPCs in the file will be removed. + * @return true if at least one NPC was unloaded, otherwise false. + * + **/ +static bool npc_unloadfile(const char *filepath, bool unload_mobs) { + nullpo_retr(false, filepath); + struct DBIterator *iter = db_iterator(npc->name_db); - struct npc_data* nd = NULL; bool found = false; - nullpo_retr(false, filepath); - - for( nd = dbi_first(iter); dbi_exists(iter); nd = dbi_next(iter) ) { - if( nd->path && strcasecmp(nd->path,filepath) == 0 ) { // FIXME: This can break in case-sensitive file systems + for (struct npc_data *nd = dbi_first(iter); dbi_exists(iter); nd = dbi_next(iter)) { + if (nd->path != NULL && strcasecmp(nd->path, filepath) == 0) { // FIXME: This can break in case-sensitive file systems. found = true; - npc->unload_duplicates(nd);/* unload any npcs which could duplicate this but be in a different file */ - npc->unload(nd, true); + npc->unload_duplicates(nd, unload_mobs); /// Unload any NPC which could duplicate this but be in a different file. + npc->unload(nd, true, unload_mobs); } } dbi_destroy(iter); - if( found ) /* refresh event cache */ + if (found) /// Refresh event cache. npc->read_event_script(); return found; @@ -5854,6 +5940,7 @@ void npc_defaults(void) npc->unload_ev_label = npc_unload_ev_label; npc->unload_dup_sub = npc_unload_dup_sub; npc->unload_duplicates = npc_unload_duplicates; + npc->unload_mob = npc_unload_mob; npc->unload = npc_unload; npc->clearsrcfile = npc_clearsrcfile; npc->addsrcfile = npc_addsrcfile; diff --git a/src/map/npc.h b/src/map/npc.h index 392911046..65c9796d9 100644 --- a/src/map/npc.h +++ b/src/map/npc.h @@ -270,8 +270,9 @@ struct npc_interface { int (*unload_ev) (union DBKey key, struct DBData *data, va_list ap); int (*unload_ev_label) (union DBKey key, struct DBData *data, va_list ap); int (*unload_dup_sub) (struct npc_data *nd, va_list args); - void (*unload_duplicates) (struct npc_data *nd); - int (*unload) (struct npc_data *nd, bool single); + void (*unload_duplicates) (struct npc_data *nd, bool unload_mobs); + int (*unload_mob) (struct mob_data *md, va_list args); + int (*unload) (struct npc_data *nd, bool single, bool unload_mobs); void (*clearsrcfile) (void); void (*addsrcfile) (const char *name); void (*delsrcfile) (const char *name); @@ -313,7 +314,7 @@ struct npc_interface { int (*path_db_clear_sub) (union DBKey key, struct DBData *data, va_list args); int (*ev_label_db_clear_sub) (union DBKey key, struct DBData *data, va_list args); int (*reload) (void); - bool (*unloadfile) (const char *filepath); + bool (*unloadfile) (const char *filepath, bool unload_mobs); void (*do_clear_npc) (void); void (*debug_warps_sub) (struct npc_data *nd); void (*debug_warps) (void); diff --git a/src/map/script.c b/src/map/script.c index 3879d8efb..b89176dba 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -14576,47 +14576,53 @@ static BUILDIN(strmobinfo) return true; } -/*========================================== - * Summon guardians [Valaris] - * guardian("<map name>",<x>,<y>,"<name to show>",<mob id>{,"<event label>"}{,<guardian index>}) -> <id> - *------------------------------------------*/ +/** + * Summons a castle guardian mob. + * + * @code{.herc} + * guardian("<map name>", <x>, <y>, "<name to show>", <mob id>{, <guardian index>}); + * guardian("<map name>", <x>, <y>, "<name to show>", <mob id>{, "<event label>"{, <guardian index>}}); + * @endcode + * + * @author Valaris + * + **/ static BUILDIN(guardian) { - int class_ = 0, x = 0, y = 0, guardian = 0; - const char *str, *mapname, *evt=""; bool has_index = false; + int guardian = 0; + const char *event = ""; - mapname = script_getstr(st,2); - x = script_getnum(st,3); - y = script_getnum(st,4); - str = script_getstr(st,5); - class_ = script_getnum(st,6); - - if( script_hasdata(st,8) ) - {// "<event label>",<guardian index> - evt=script_getstr(st,7); - guardian=script_getnum(st,8); + if (script_hasdata(st, 8)) { /// "<event label>", <guardian index> + event = script_getstr(st, 7); + script->check_event(st, event); + guardian = script_getnum(st, 8); has_index = true; - } else if( script_hasdata(st,7) ) { - struct script_data *data = script_getdata(st,7); - script->get_val(st,data); // Dereference if it's a variable - if( data_isstring(data) ) { - // "<event label>" - evt=script_getstr(st,7); - } else if( data_isint(data) ) { - // <guardian index> - guardian=script_getnum(st,7); + } else if (script_hasdata(st, 7)) { + struct script_data *data = script_getdata(st, 7); + + script->get_val(st, data); /// Dereference if it's a variable. + + if (data_isstring(data)) { /// "<event label>" + event = script_getstr(st, 7); + script->check_event(st, event); + } else if (data_isint(data)) { /// <guardian index> + guardian = script_getnum(st, 7); has_index = true; } else { - ShowError("script:guardian: invalid data type for argument #6 (from 1)\n"); + ShowError("script:guardian: Invalid data type for argument #6!\n"); script->reportdata(data); return false; } } - script->check_event(st, evt); - script_pushint(st, mob->spawn_guardian(mapname,x,y,str,class_,evt,guardian,has_index)); + const char *mapname = script_getstr(st, 2); + const char *name = script_getstr(st, 5); + const int x = script_getnum(st, 3); + const int y = script_getnum(st, 4); + const int mob_id = script_getnum(st, 6); + script_pushint(st, mob->spawn_guardian(mapname, x, y, name, mob_id, event, guardian, has_index, st->oid)); return true; } /*========================================== @@ -16944,38 +16950,54 @@ static BUILDIN(logmes) return true; } +/** + * Summons a mob which will act as a slave for the invoking character. + * + * @code{.herc} + * summon("mob name", <mob id>{, <timeout>{, "event label"}}); + * @endcode + * + * @author Celest + * + **/ static BUILDIN(summon) { - int class_, timeout=0; - const char *str,*event=""; - struct mob_data *md; - int64 tick = timer->gettick(); struct map_session_data *sd = script->rid2sd(st); + if (sd == NULL) return true; - str = script_getstr(st,2); - class_ = script_getnum(st,3); - if( script_hasdata(st,4) ) - timeout=script_getnum(st,4); - if( script_hasdata(st,5) ) { - event=script_getstr(st,5); + const int64 tick = timer->gettick(); + + clif->skill_poseffect(&sd->bl, AM_CALLHOMUN, 1, sd->bl.x, sd->bl.y, tick); + + const char *event = ""; + + if (script_hasdata(st, 5)) { + event = script_getstr(st, 5); script->check_event(st, event); } - clif->skill_poseffect(&sd->bl,AM_CALLHOMUN,1,sd->bl.x,sd->bl.y,tick); + const char *name = script_getstr(st, 2); + const int mob_id = script_getnum(st, 3); + struct mob_data *md = mob->once_spawn_sub(&sd->bl, sd->bl.m, sd->bl.x, sd->bl.y, name, mob_id, event, + SZ_SMALL, AI_NONE, 0); - md = mob->once_spawn_sub(&sd->bl, sd->bl.m, sd->bl.x, sd->bl.y, str, class_, event, SZ_SMALL, AI_NONE); - if (md) { - md->master_id=sd->bl.id; + if (md != NULL) { + md->master_id = sd->bl.id; md->special_state.ai = AI_ATTACK; - if( md->deletetimer != INVALID_TIMER ) + + if (md->deletetimer != INVALID_TIMER) timer->delete(md->deletetimer, mob->timer_delete); - md->deletetimer = timer->add(tick+(timeout>0?timeout*1000:60000),mob->timer_delete,md->bl.id,0); - mob->spawn (md); //Now it is ready for spawning. - clif->specialeffect(&md->bl,344,AREA); + + const int timeout = script_hasdata(st, 4) ? script_getnum(st, 4) * 1000 : 60000; + + md->deletetimer = timer->add(tick + ((timeout == 0) ? 60000 : timeout), mob->timer_delete, md->bl.id, 0); + mob->spawn(md); + clif->specialeffect(&md->bl, 344, AREA); sc_start4(NULL, &md->bl, SC_MODECHANGE, 100, 1, 0, MD_AGGRESSIVE, 0, 60000); } + return true; } @@ -22407,20 +22429,31 @@ static BUILDIN(bg_warp) return true; } +/** + * Spawns a mob with allegiance to the given battle group. + * + * @code{.herc} + * bg_monster(<battle group>, "<map name>", <x>, <y>, "<name to show>", <mob id>{, "<event label>"}); + * @endcode + * + **/ static BUILDIN(bg_monster) { - int class_ = 0, x = 0, y = 0, bg_id = 0; - const char *str, *mapname, *evt=""; - - bg_id = script_getnum(st,2); - mapname = script_getstr(st,3); - x = script_getnum(st,4); - y = script_getnum(st,5); - str = script_getstr(st,6); - class_ = script_getnum(st,7); - if( script_hasdata(st,8) ) evt = script_getstr(st,8); - script->check_event(st, evt); - script_pushint(st, mob->spawn_bg(mapname,x,y,str,class_,evt,bg_id)); + const char *event = ""; + + if (script_hasdata(st, 8)) { + event = script_getstr(st, 8); + script->check_event(st, event); + } + + const char *mapname = script_getstr(st, 3); + const char *name = script_getstr(st, 6); + const int bg_id = script_getnum(st, 2); + const int x = script_getnum(st, 4); + const int y = script_getnum(st, 5); + const int mob_id = script_getnum(st, 7); + + script_pushint(st, mob->spawn_bg(mapname, x, y, name, mob_id, event, bg_id, st->oid)); return true; } diff --git a/src/map/skill.c b/src/map/skill.c index 09e216804..a488e3eaf 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -10417,7 +10417,7 @@ static int skill_castend_nodamage_id(struct block_list *src, struct block_list * if(sd) { struct mob_data *summon_md; - summon_md = mob->once_spawn_sub(src, src->m, src->x, src->y, clif->get_bl_name(src), MOBID_KO_KAGE, "", SZ_SMALL, AI_NONE); + summon_md = mob->once_spawn_sub(src, src->m, src->x, src->y, clif->get_bl_name(src), MOBID_KO_KAGE, "", SZ_SMALL, AI_NONE, 0); if( summon_md ) { summon_md->master_id = src->id; summon_md->special_state.ai = AI_ZANZOU; @@ -10600,7 +10600,7 @@ static int skill_castend_nodamage_id(struct block_list *src, struct block_list * for (i = 0; i < summons[skill_lv-1].quantity; i++) { struct mob_data *summon_md = mob->once_spawn_sub(src, src->m, src->x, src->y, clif->get_bl_name(src), - summons[skill_lv-1].mob_id, "", SZ_SMALL, AI_ATTACK); + summons[skill_lv-1].mob_id, "", SZ_SMALL, AI_ATTACK, 0); if (summon_md != NULL) { summon_md->master_id = src->id; if (summon_md->deletetimer != INVALID_TIMER) @@ -11383,7 +11383,7 @@ static int skill_castend_pos2(struct block_list *src, int x, int y, uint16 skill } // Correct info, don't change any of this! [Celest] - md = mob->once_spawn_sub(src, src->m, x, y, clif->get_bl_name(src), class_, "", SZ_SMALL, AI_NONE); + md = mob->once_spawn_sub(src, src->m, x, y, clif->get_bl_name(src), class_, "", SZ_SMALL, AI_NONE, 0); if (md) { md->master_id = src->id; md->special_state.ai = (skill_id == AM_SPHEREMINE) ? AI_SPHERE : AI_FLORA; @@ -11485,7 +11485,7 @@ static int skill_castend_pos2(struct block_list *src, int x, int y, uint16 skill clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); } else { int mob_id = skill_lv < 2 ? MOBID_BLACK_MUSHROOM + rnd()%2 : MOBID_RED_PLANT + rnd()%6; - struct mob_data *md = mob->once_spawn_sub(src, src->m, x, y, DEFAULT_MOB_JNAME, mob_id, "", SZ_SMALL, AI_NONE); + struct mob_data *md = mob->once_spawn_sub(src, src->m, x, y, DEFAULT_MOB_JNAME, mob_id, "", SZ_SMALL, AI_NONE, 0); int i; if (md == NULL) break; @@ -11631,7 +11631,7 @@ static int skill_castend_pos2(struct block_list *src, int x, int y, uint16 skill case NC_SILVERSNIPER: { - struct mob_data *md = mob->once_spawn_sub(src, src->m, x, y, clif->get_bl_name(src), MOBID_SILVERSNIPER, "", SZ_SMALL, AI_NONE); + struct mob_data *md = mob->once_spawn_sub(src, src->m, x, y, clif->get_bl_name(src), MOBID_SILVERSNIPER, "", SZ_SMALL, AI_NONE, 0); if (md) { md->master_id = src->id; md->special_state.ai = AI_FLORA; @@ -18793,7 +18793,7 @@ static int skill_magicdecoy(struct map_session_data *sd, int nameid) break; } - md = mob->once_spawn_sub(&sd->bl, sd->bl.m, x, y, sd->status.name, class_, "", SZ_SMALL, AI_NONE); + md = mob->once_spawn_sub(&sd->bl, sd->bl.m, x, y, sd->status.name, class_, "", SZ_SMALL, AI_NONE, 0); if( md ) { md->master_id = sd->bl.id; md->special_state.ai = AI_FLORA; diff --git a/src/plugins/HPMHooking/HPMHooking.Defs.inc b/src/plugins/HPMHooking/HPMHooking.Defs.inc index 206934222..8c6f43433 100644 --- a/src/plugins/HPMHooking/HPMHooking.Defs.inc +++ b/src/plugins/HPMHooking/HPMHooking.Defs.inc @@ -5336,22 +5336,22 @@ typedef struct view_data* (*HPMHOOK_pre_mob_get_viewdata) (int *class_); typedef struct view_data* (*HPMHOOK_post_mob_get_viewdata) (struct view_data* retVal___, int class_); typedef int (*HPMHOOK_pre_mob_parse_dataset) (struct spawn_data **data); typedef int (*HPMHOOK_post_mob_parse_dataset) (int retVal___, struct spawn_data *data); -typedef struct mob_data* (*HPMHOOK_pre_mob_spawn_dataset) (struct spawn_data **data); -typedef struct mob_data* (*HPMHOOK_post_mob_spawn_dataset) (struct mob_data* retVal___, struct spawn_data *data); +typedef struct mob_data* (*HPMHOOK_pre_mob_spawn_dataset) (struct spawn_data **data, int *npc_id); +typedef struct mob_data* (*HPMHOOK_post_mob_spawn_dataset) (struct mob_data* retVal___, struct spawn_data *data, int npc_id); typedef int (*HPMHOOK_pre_mob_get_random_id) (int *type, int *flag, int *lv); typedef int (*HPMHOOK_post_mob_get_random_id) (int retVal___, int type, int flag, int lv); typedef bool (*HPMHOOK_pre_mob_ksprotected) (struct block_list **src, struct block_list **target); typedef bool (*HPMHOOK_post_mob_ksprotected) (bool retVal___, struct block_list *src, struct block_list *target); -typedef struct mob_data* (*HPMHOOK_pre_mob_once_spawn_sub) (struct block_list **bl, int16 *m, int16 *x, int16 *y, const char **mobname, int *class_, const char **event, unsigned int *size, unsigned int *ai); -typedef struct mob_data* (*HPMHOOK_post_mob_once_spawn_sub) (struct mob_data* retVal___, struct block_list *bl, int16 m, int16 x, int16 y, const char *mobname, int class_, const char *event, unsigned int size, unsigned int ai); +typedef struct mob_data* (*HPMHOOK_pre_mob_once_spawn_sub) (struct block_list **bl, int16 *m, int16 *x, int16 *y, const char **mobname, int *class_, const char **event, unsigned int *size, unsigned int *ai, int *npc_id); +typedef struct mob_data* (*HPMHOOK_post_mob_once_spawn_sub) (struct mob_data* retVal___, struct block_list *bl, int16 m, int16 x, int16 y, const char *mobname, int class_, const char *event, unsigned int size, unsigned int ai, int npc_id); typedef int (*HPMHOOK_pre_mob_once_spawn) (struct map_session_data **sd, int16 *m, int16 *x, int16 *y, const char **mobname, int *class_, int *amount, const char **event, unsigned int *size, unsigned int *ai); typedef int (*HPMHOOK_post_mob_once_spawn) (int retVal___, struct map_session_data *sd, int16 m, int16 x, int16 y, const char *mobname, int class_, int amount, const char *event, unsigned int size, unsigned int ai); typedef int (*HPMHOOK_pre_mob_once_spawn_area) (struct map_session_data **sd, int16 *m, int16 *x0, int16 *y0, int16 *x1, int16 *y1, const char **mobname, int *class_, int *amount, const char **event, unsigned int *size, unsigned int *ai); typedef int (*HPMHOOK_post_mob_once_spawn_area) (int retVal___, struct map_session_data *sd, int16 m, int16 x0, int16 y0, int16 x1, int16 y1, const char *mobname, int class_, int amount, const char *event, unsigned int size, unsigned int ai); -typedef int (*HPMHOOK_pre_mob_spawn_guardian) (const char **mapname, short *x, short *y, const char **mobname, int *class_, const char **event, int *guardian, bool *has_index); -typedef int (*HPMHOOK_post_mob_spawn_guardian) (int retVal___, const char *mapname, short x, short y, const char *mobname, int class_, const char *event, int guardian, bool has_index); -typedef int (*HPMHOOK_pre_mob_spawn_bg) (const char **mapname, short *x, short *y, const char **mobname, int *class_, const char **event, unsigned int *bg_id); -typedef int (*HPMHOOK_post_mob_spawn_bg) (int retVal___, const char *mapname, short x, short y, const char *mobname, int class_, const char *event, unsigned int bg_id); +typedef int (*HPMHOOK_pre_mob_spawn_guardian) (const char **mapname, short *x, short *y, const char **mobname, int *class_, const char **event, int *guardian, bool *has_index, int *npc_id); +typedef int (*HPMHOOK_post_mob_spawn_guardian) (int retVal___, const char *mapname, short x, short y, const char *mobname, int class_, const char *event, int guardian, bool has_index, int npc_id); +typedef int (*HPMHOOK_pre_mob_spawn_bg) (const char **mapname, short *x, short *y, const char **mobname, int *class_, const char **event, unsigned int *bg_id, int *npc_id); +typedef int (*HPMHOOK_post_mob_spawn_bg) (int retVal___, const char *mapname, short x, short y, const char *mobname, int class_, const char *event, unsigned int bg_id, int npc_id); typedef int (*HPMHOOK_pre_mob_can_reach) (struct mob_data **md, struct block_list **bl, int *range, int *state); typedef int (*HPMHOOK_post_mob_can_reach) (int retVal___, struct mob_data *md, struct block_list *bl, int range, int state); typedef int (*HPMHOOK_pre_mob_linksearch) (struct block_list **bl, va_list ap); @@ -5686,10 +5686,12 @@ typedef int (*HPMHOOK_pre_npc_unload_ev_label) (union DBKey *key, struct DBData typedef int (*HPMHOOK_post_npc_unload_ev_label) (int retVal___, union DBKey key, struct DBData *data, va_list ap); typedef int (*HPMHOOK_pre_npc_unload_dup_sub) (struct npc_data **nd, va_list args); typedef int (*HPMHOOK_post_npc_unload_dup_sub) (int retVal___, struct npc_data *nd, va_list args); -typedef void (*HPMHOOK_pre_npc_unload_duplicates) (struct npc_data **nd); -typedef void (*HPMHOOK_post_npc_unload_duplicates) (struct npc_data *nd); -typedef int (*HPMHOOK_pre_npc_unload) (struct npc_data **nd, bool *single); -typedef int (*HPMHOOK_post_npc_unload) (int retVal___, struct npc_data *nd, bool single); +typedef void (*HPMHOOK_pre_npc_unload_duplicates) (struct npc_data **nd, bool *unload_mobs); +typedef void (*HPMHOOK_post_npc_unload_duplicates) (struct npc_data *nd, bool unload_mobs); +typedef int (*HPMHOOK_pre_npc_unload_mob) (struct mob_data **md, va_list args); +typedef int (*HPMHOOK_post_npc_unload_mob) (int retVal___, struct mob_data *md, va_list args); +typedef int (*HPMHOOK_pre_npc_unload) (struct npc_data **nd, bool *single, bool *unload_mobs); +typedef int (*HPMHOOK_post_npc_unload) (int retVal___, struct npc_data *nd, bool single, bool unload_mobs; typedef void (*HPMHOOK_pre_npc_clearsrcfile) (void); typedef void (*HPMHOOK_post_npc_clearsrcfile) (void); typedef void (*HPMHOOK_pre_npc_addsrcfile) (const char **name); @@ -5772,8 +5774,8 @@ typedef int (*HPMHOOK_pre_npc_ev_label_db_clear_sub) (union DBKey *key, struct D typedef int (*HPMHOOK_post_npc_ev_label_db_clear_sub) (int retVal___, union DBKey key, struct DBData *data, va_list args); typedef int (*HPMHOOK_pre_npc_reload) (void); typedef int (*HPMHOOK_post_npc_reload) (int retVal___); -typedef bool (*HPMHOOK_pre_npc_unloadfile) (const char **filepath); -typedef bool (*HPMHOOK_post_npc_unloadfile) (bool retVal___, const char *filepath); +typedef bool (*HPMHOOK_pre_npc_unloadfile) (const char **filepath, bool *unload_mobs); +typedef bool (*HPMHOOK_post_npc_unloadfile) (bool retVal___, const char *filepath, bool unload_mobs); typedef void (*HPMHOOK_pre_npc_do_clear_npc) (void); typedef void (*HPMHOOK_post_npc_do_clear_npc) (void); typedef void (*HPMHOOK_pre_npc_debug_warps_sub) (struct npc_data **nd); diff --git a/src/plugins/HPMHooking/HPMHooking_map.HPMHooksCore.inc b/src/plugins/HPMHooking/HPMHooking_map.HPMHooksCore.inc index d70ee0024..53ba3403c 100644 --- a/src/plugins/HPMHooking/HPMHooking_map.HPMHooksCore.inc +++ b/src/plugins/HPMHooking/HPMHooking_map.HPMHooksCore.inc @@ -4280,6 +4280,8 @@ struct { struct HPMHookPoint *HP_npc_unload_dup_sub_post; struct HPMHookPoint *HP_npc_unload_duplicates_pre; struct HPMHookPoint *HP_npc_unload_duplicates_post; + struct HPMHookPoint *HP_npc_unload_mob_pre; + struct HPMHookPoint *HP_npc_unload_mob_post; struct HPMHookPoint *HP_npc_unload_pre; struct HPMHookPoint *HP_npc_unload_post; struct HPMHookPoint *HP_npc_clearsrcfile_pre; @@ -11145,6 +11147,8 @@ struct { int HP_npc_unload_dup_sub_post; int HP_npc_unload_duplicates_pre; int HP_npc_unload_duplicates_post; + int HP_npc_unload_mob_pre; + int HP_npc_unload_mob_post; int HP_npc_unload_pre; int HP_npc_unload_post; int HP_npc_clearsrcfile_pre; diff --git a/src/plugins/HPMHooking/HPMHooking_map.HookingPoints.inc b/src/plugins/HPMHooking/HPMHooking_map.HookingPoints.inc index c755e0bec..0904a1dac 100644 --- a/src/plugins/HPMHooking/HPMHooking_map.HookingPoints.inc +++ b/src/plugins/HPMHooking/HPMHooking_map.HookingPoints.inc @@ -2192,6 +2192,7 @@ struct HookingPointData HookingPoints[] = { { HP_POP(npc->unload_ev_label, HP_npc_unload_ev_label) }, { HP_POP(npc->unload_dup_sub, HP_npc_unload_dup_sub) }, { HP_POP(npc->unload_duplicates, HP_npc_unload_duplicates) }, + { HP_POP(npc->unload_mob, HP_npc_unload_mob) }, { HP_POP(npc->unload, HP_npc_unload) }, { HP_POP(npc->clearsrcfile, HP_npc_clearsrcfile) }, { HP_POP(npc->addsrcfile, HP_npc_addsrcfile) }, diff --git a/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc b/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc index d0ddcf28a..319e675a8 100644 --- a/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc +++ b/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc @@ -52053,15 +52053,15 @@ int HP_mob_parse_dataset(struct spawn_data *data) { } return retVal___; } -struct mob_data* HP_mob_spawn_dataset(struct spawn_data *data) { +struct mob_data* HP_mob_spawn_dataset(struct spawn_data *data, int npc_id) { int hIndex = 0; struct mob_data* retVal___ = NULL; if (HPMHooks.count.HP_mob_spawn_dataset_pre > 0) { - struct mob_data* (*preHookFunc) (struct spawn_data **data); + struct mob_data* (*preHookFunc) (struct spawn_data **data, int *npc_id); *HPMforce_return = false; for (hIndex = 0; hIndex < HPMHooks.count.HP_mob_spawn_dataset_pre; hIndex++) { preHookFunc = HPMHooks.list.HP_mob_spawn_dataset_pre[hIndex].func; - retVal___ = preHookFunc(&data); + retVal___ = preHookFunc(&data, &npc_id); } if (*HPMforce_return) { *HPMforce_return = false; @@ -52069,13 +52069,13 @@ struct mob_data* HP_mob_spawn_dataset(struct spawn_data *data) { } } { - retVal___ = HPMHooks.source.mob.spawn_dataset(data); + retVal___ = HPMHooks.source.mob.spawn_dataset(data, npc_id); } if (HPMHooks.count.HP_mob_spawn_dataset_post > 0) { - struct mob_data* (*postHookFunc) (struct mob_data* retVal___, struct spawn_data *data); + struct mob_data* (*postHookFunc) (struct mob_data* retVal___, struct spawn_data *data, int npc_id); for (hIndex = 0; hIndex < HPMHooks.count.HP_mob_spawn_dataset_post; hIndex++) { postHookFunc = HPMHooks.list.HP_mob_spawn_dataset_post[hIndex].func; - retVal___ = postHookFunc(retVal___, data); + retVal___ = postHookFunc(retVal___, data, npc_id); } } return retVal___; @@ -52134,15 +52134,15 @@ bool HP_mob_ksprotected(struct block_list *src, struct block_list *target) { } return retVal___; } -struct mob_data* HP_mob_once_spawn_sub(struct block_list *bl, int16 m, int16 x, int16 y, const char *mobname, int class_, const char *event, unsigned int size, unsigned int ai) { +struct mob_data* HP_mob_once_spawn_sub(struct block_list *bl, int16 m, int16 x, int16 y, const char *mobname, int class_, const char *event, unsigned int size, unsigned int ai, int npc_id) { int hIndex = 0; struct mob_data* retVal___ = NULL; if (HPMHooks.count.HP_mob_once_spawn_sub_pre > 0) { - struct mob_data* (*preHookFunc) (struct block_list **bl, int16 *m, int16 *x, int16 *y, const char **mobname, int *class_, const char **event, unsigned int *size, unsigned int *ai); + struct mob_data* (*preHookFunc) (struct block_list **bl, int16 *m, int16 *x, int16 *y, const char **mobname, int *class_, const char **event, unsigned int *size, unsigned int *ai, int *npc_id); *HPMforce_return = false; for (hIndex = 0; hIndex < HPMHooks.count.HP_mob_once_spawn_sub_pre; hIndex++) { preHookFunc = HPMHooks.list.HP_mob_once_spawn_sub_pre[hIndex].func; - retVal___ = preHookFunc(&bl, &m, &x, &y, &mobname, &class_, &event, &size, &ai); + retVal___ = preHookFunc(&bl, &m, &x, &y, &mobname, &class_, &event, &size, &ai, &npc_id); } if (*HPMforce_return) { *HPMforce_return = false; @@ -52150,13 +52150,13 @@ struct mob_data* HP_mob_once_spawn_sub(struct block_list *bl, int16 m, int16 x, } } { - retVal___ = HPMHooks.source.mob.once_spawn_sub(bl, m, x, y, mobname, class_, event, size, ai); + retVal___ = HPMHooks.source.mob.once_spawn_sub(bl, m, x, y, mobname, class_, event, size, ai, npc_id); } if (HPMHooks.count.HP_mob_once_spawn_sub_post > 0) { - struct mob_data* (*postHookFunc) (struct mob_data* retVal___, struct block_list *bl, int16 m, int16 x, int16 y, const char *mobname, int class_, const char *event, unsigned int size, unsigned int ai); + struct mob_data* (*postHookFunc) (struct mob_data* retVal___, struct block_list *bl, int16 m, int16 x, int16 y, const char *mobname, int class_, const char *event, unsigned int size, unsigned int ai, int npc_id); for (hIndex = 0; hIndex < HPMHooks.count.HP_mob_once_spawn_sub_post; hIndex++) { postHookFunc = HPMHooks.list.HP_mob_once_spawn_sub_post[hIndex].func; - retVal___ = postHookFunc(retVal___, bl, m, x, y, mobname, class_, event, size, ai); + retVal___ = postHookFunc(retVal___, bl, m, x, y, mobname, class_, event, size, ai, npc_id); } } return retVal___; @@ -52215,15 +52215,15 @@ int HP_mob_once_spawn_area(struct map_session_data *sd, int16 m, int16 x0, int16 } return retVal___; } -int HP_mob_spawn_guardian(const char *mapname, short x, short y, const char *mobname, int class_, const char *event, int guardian, bool has_index) { +int HP_mob_spawn_guardian(const char *mapname, short x, short y, const char *mobname, int class_, const char *event, int guardian, bool has_index, int npc_id) { int hIndex = 0; int retVal___ = 0; if (HPMHooks.count.HP_mob_spawn_guardian_pre > 0) { - int (*preHookFunc) (const char **mapname, short *x, short *y, const char **mobname, int *class_, const char **event, int *guardian, bool *has_index); + int (*preHookFunc) (const char **mapname, short *x, short *y, const char **mobname, int *class_, const char **event, int *guardian, bool *has_index, int *npc_id); *HPMforce_return = false; for (hIndex = 0; hIndex < HPMHooks.count.HP_mob_spawn_guardian_pre; hIndex++) { preHookFunc = HPMHooks.list.HP_mob_spawn_guardian_pre[hIndex].func; - retVal___ = preHookFunc(&mapname, &x, &y, &mobname, &class_, &event, &guardian, &has_index); + retVal___ = preHookFunc(&mapname, &x, &y, &mobname, &class_, &event, &guardian, &has_index, &npc_id); } if (*HPMforce_return) { *HPMforce_return = false; @@ -52231,26 +52231,26 @@ int HP_mob_spawn_guardian(const char *mapname, short x, short y, const char *mob } } { - retVal___ = HPMHooks.source.mob.spawn_guardian(mapname, x, y, mobname, class_, event, guardian, has_index); + retVal___ = HPMHooks.source.mob.spawn_guardian(mapname, x, y, mobname, class_, event, guardian, has_index, npc_id); } if (HPMHooks.count.HP_mob_spawn_guardian_post > 0) { - int (*postHookFunc) (int retVal___, const char *mapname, short x, short y, const char *mobname, int class_, const char *event, int guardian, bool has_index); + int (*postHookFunc) (int retVal___, const char *mapname, short x, short y, const char *mobname, int class_, const char *event, int guardian, bool has_index, int npc_id); for (hIndex = 0; hIndex < HPMHooks.count.HP_mob_spawn_guardian_post; hIndex++) { postHookFunc = HPMHooks.list.HP_mob_spawn_guardian_post[hIndex].func; - retVal___ = postHookFunc(retVal___, mapname, x, y, mobname, class_, event, guardian, has_index); + retVal___ = postHookFunc(retVal___, mapname, x, y, mobname, class_, event, guardian, has_index, npc_id); } } return retVal___; } -int HP_mob_spawn_bg(const char *mapname, short x, short y, const char *mobname, int class_, const char *event, unsigned int bg_id) { +int HP_mob_spawn_bg(const char *mapname, short x, short y, const char *mobname, int class_, const char *event, unsigned int bg_id, int npc_id) { int hIndex = 0; int retVal___ = 0; if (HPMHooks.count.HP_mob_spawn_bg_pre > 0) { - int (*preHookFunc) (const char **mapname, short *x, short *y, const char **mobname, int *class_, const char **event, unsigned int *bg_id); + int (*preHookFunc) (const char **mapname, short *x, short *y, const char **mobname, int *class_, const char **event, unsigned int *bg_id, int *npc_id); *HPMforce_return = false; for (hIndex = 0; hIndex < HPMHooks.count.HP_mob_spawn_bg_pre; hIndex++) { preHookFunc = HPMHooks.list.HP_mob_spawn_bg_pre[hIndex].func; - retVal___ = preHookFunc(&mapname, &x, &y, &mobname, &class_, &event, &bg_id); + retVal___ = preHookFunc(&mapname, &x, &y, &mobname, &class_, &event, &bg_id, &npc_id); } if (*HPMforce_return) { *HPMforce_return = false; @@ -52258,13 +52258,13 @@ int HP_mob_spawn_bg(const char *mapname, short x, short y, const char *mobname, } } { - retVal___ = HPMHooks.source.mob.spawn_bg(mapname, x, y, mobname, class_, event, bg_id); + retVal___ = HPMHooks.source.mob.spawn_bg(mapname, x, y, mobname, class_, event, bg_id, npc_id); } if (HPMHooks.count.HP_mob_spawn_bg_post > 0) { - int (*postHookFunc) (int retVal___, const char *mapname, short x, short y, const char *mobname, int class_, const char *event, unsigned int bg_id); + int (*postHookFunc) (int retVal___, const char *mapname, short x, short y, const char *mobname, int class_, const char *event, unsigned int bg_id, int npc_id); for (hIndex = 0; hIndex < HPMHooks.count.HP_mob_spawn_bg_post; hIndex++) { postHookFunc = HPMHooks.list.HP_mob_spawn_bg_post[hIndex].func; - retVal___ = postHookFunc(retVal___, mapname, x, y, mobname, class_, event, bg_id); + retVal___ = postHookFunc(retVal___, mapname, x, y, mobname, class_, event, bg_id, npc_id); } } return retVal___; @@ -56811,14 +56811,14 @@ int HP_npc_unload_dup_sub(struct npc_data *nd, va_list args) { } return retVal___; } -void HP_npc_unload_duplicates(struct npc_data *nd) { +void HP_npc_unload_duplicates(struct npc_data *nd, bool unload_mobs) { int hIndex = 0; if (HPMHooks.count.HP_npc_unload_duplicates_pre > 0) { - void (*preHookFunc) (struct npc_data **nd); + void (*preHookFunc) (struct npc_data **nd, bool *unload_mobs); *HPMforce_return = false; for (hIndex = 0; hIndex < HPMHooks.count.HP_npc_unload_duplicates_pre; hIndex++) { preHookFunc = HPMHooks.list.HP_npc_unload_duplicates_pre[hIndex].func; - preHookFunc(&nd); + preHookFunc(&nd, &unload_mobs); } if (*HPMforce_return) { *HPMforce_return = false; @@ -56826,26 +56826,59 @@ void HP_npc_unload_duplicates(struct npc_data *nd) { } } { - HPMHooks.source.npc.unload_duplicates(nd); + HPMHooks.source.npc.unload_duplicates(nd, unload_mobs); } if (HPMHooks.count.HP_npc_unload_duplicates_post > 0) { - void (*postHookFunc) (struct npc_data *nd); + void (*postHookFunc) (struct npc_data *nd, bool unload_mobs); for (hIndex = 0; hIndex < HPMHooks.count.HP_npc_unload_duplicates_post; hIndex++) { postHookFunc = HPMHooks.list.HP_npc_unload_duplicates_post[hIndex].func; - postHookFunc(nd); + postHookFunc(nd, unload_mobs); } } return; } -int HP_npc_unload(struct npc_data *nd, bool single) { +int HP_npc_unload_mob(struct mob_data *md, va_list args) { + int hIndex = 0; + int retVal___ = 0; + if (HPMHooks.count.HP_npc_unload_mob_pre > 0) { + int (*preHookFunc) (struct mob_data **md, va_list args); + *HPMforce_return = false; + for (hIndex = 0; hIndex < HPMHooks.count.HP_npc_unload_mob_pre; hIndex++) { + va_list args___copy; va_copy(args___copy, args); + preHookFunc = HPMHooks.list.HP_npc_unload_mob_pre[hIndex].func; + retVal___ = preHookFunc(&md, args___copy); + va_end(args___copy); + } + if (*HPMforce_return) { + *HPMforce_return = false; + return retVal___; + } + } + { + va_list args___copy; va_copy(args___copy, args); + retVal___ = HPMHooks.source.npc.unload_mob(md, args___copy); + va_end(args___copy); + } + if (HPMHooks.count.HP_npc_unload_mob_post > 0) { + int (*postHookFunc) (int retVal___, struct mob_data *md, va_list args); + for (hIndex = 0; hIndex < HPMHooks.count.HP_npc_unload_mob_post; hIndex++) { + va_list args___copy; va_copy(args___copy, args); + postHookFunc = HPMHooks.list.HP_npc_unload_mob_post[hIndex].func; + retVal___ = postHookFunc(retVal___, md, args___copy); + va_end(args___copy); + } + } + return retVal___; +} +int HP_npc_unload(struct npc_data *nd, bool single, bool unload_mobs) { int hIndex = 0; int retVal___ = 0; if (HPMHooks.count.HP_npc_unload_pre > 0) { - int (*preHookFunc) (struct npc_data **nd, bool *single); + int (*preHookFunc) (struct npc_data **nd, bool *single, bool *unload_mobs); *HPMforce_return = false; for (hIndex = 0; hIndex < HPMHooks.count.HP_npc_unload_pre; hIndex++) { preHookFunc = HPMHooks.list.HP_npc_unload_pre[hIndex].func; - retVal___ = preHookFunc(&nd, &single); + retVal___ = preHookFunc(&nd, &single, &unload_mobs); } if (*HPMforce_return) { *HPMforce_return = false; @@ -56853,13 +56886,13 @@ int HP_npc_unload(struct npc_data *nd, bool single) { } } { - retVal___ = HPMHooks.source.npc.unload(nd, single); + retVal___ = HPMHooks.source.npc.unload(nd, single, unload_mobs); } if (HPMHooks.count.HP_npc_unload_post > 0) { - int (*postHookFunc) (int retVal___, struct npc_data *nd, bool single); + int (*postHookFunc) (int retVal___, struct npc_data *nd, bool single, bool unload_mobs); for (hIndex = 0; hIndex < HPMHooks.count.HP_npc_unload_post; hIndex++) { postHookFunc = HPMHooks.list.HP_npc_unload_post[hIndex].func; - retVal___ = postHookFunc(retVal___, nd, single); + retVal___ = postHookFunc(retVal___, nd, single, unload_mobs); } } return retVal___; @@ -57974,15 +58007,15 @@ int HP_npc_reload(void) { } return retVal___; } -bool HP_npc_unloadfile(const char *filepath) { +bool HP_npc_unloadfile(const char *filepath, bool unload_mobs) { int hIndex = 0; bool retVal___ = false; if (HPMHooks.count.HP_npc_unloadfile_pre > 0) { - bool (*preHookFunc) (const char **filepath); + bool (*preHookFunc) (const char **filepath, bool *unload_mobs); *HPMforce_return = false; for (hIndex = 0; hIndex < HPMHooks.count.HP_npc_unloadfile_pre; hIndex++) { preHookFunc = HPMHooks.list.HP_npc_unloadfile_pre[hIndex].func; - retVal___ = preHookFunc(&filepath); + retVal___ = preHookFunc(&filepath, &unload_mobs); } if (*HPMforce_return) { *HPMforce_return = false; @@ -57990,13 +58023,13 @@ bool HP_npc_unloadfile(const char *filepath) { } } { - retVal___ = HPMHooks.source.npc.unloadfile(filepath); + retVal___ = HPMHooks.source.npc.unloadfile(filepath, unload_mobs); } if (HPMHooks.count.HP_npc_unloadfile_post > 0) { - bool (*postHookFunc) (bool retVal___, const char *filepath); + bool (*postHookFunc) (bool retVal___, const char *filepath, bool unload_mobs); for (hIndex = 0; hIndex < HPMHooks.count.HP_npc_unloadfile_post; hIndex++) { postHookFunc = HPMHooks.list.HP_npc_unloadfile_post[hIndex].func; - retVal___ = postHookFunc(retVal___, filepath); + retVal___ = postHookFunc(retVal___, filepath, unload_mobs); } } return retVal___; |