summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHaru <haru@dotalux.com>2020-02-09 21:18:26 +0100
committerGitHub <noreply@github.com>2020-02-09 21:18:26 +0100
commit481d92bfa0fac1320aebf0ab4f4ceb8365fa3ff3 (patch)
tree807314fa875ca53ab1508f347bcf5ad12e9f3348
parent9e68350a054d5ec181a40528728d8de185a4f43c (diff)
parentd84aee4cd917fd1a151d7ec0b3a2d7e32df02441 (diff)
downloadhercules-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]
-rw-r--r--conf/messages.conf16
-rw-r--r--doc/atcommands.txt34
-rw-r--r--doc/script_commands.txt5
-rw-r--r--src/common/utils.c44
-rw-r--r--src/common/utils.h9
-rw-r--r--src/map/atcommand.c193
-rw-r--r--src/map/atcommand.h2
-rw-r--r--src/map/clif.c4
-rw-r--r--src/map/instance.c2
-rw-r--r--src/map/map.c2
-rw-r--r--src/map/mob.c852
-rw-r--r--src/map/mob.h9
-rw-r--r--src/map/npc.c329
-rw-r--r--src/map/npc.h7
-rw-r--r--src/map/script.c149
-rw-r--r--src/map/skill.c12
-rw-r--r--src/plugins/HPMHooking/HPMHooking.Defs.inc30
-rw-r--r--src/plugins/HPMHooking/HPMHooking_map.HPMHooksCore.inc4
-rw-r--r--src/plugins/HPMHooking/HPMHooking_map.HookingPoints.inc1
-rw-r--r--src/plugins/HPMHooking/HPMHooking_map.Hooks.inc117
20 files changed, 1136 insertions, 685 deletions
diff --git a/conf/messages.conf b/conf/messages.conf
index 787498e8e..e297c4fa5 100644
--- a/conf/messages.conf
+++ b/conf/messages.conf
@@ -1016,8 +1016,8 @@
// @loadnpc
1132: Please enter a script file name (usage: @loadnpc <file name>).
-// @npcoff
-1133: Please enter a NPC name (usage: @npcoff <NPC_name>).
+// @unloadnpc
+1133: Please enter a NPC name (Usage: @unloadnpc <NPC_name> {<flag>}).
// @jail
1134: Please enter a player name (usage: @jail <char_name>).
@@ -1455,8 +1455,8 @@
1384: User '%s' permissions updated successfully. The changes are temporary.
// @unloadnpcfile
-1385: Usage: @unloadnpcfile <file name>
-1386: File unloaded. Be aware that mapflags and monsters spawned directly are not removed.
+1385: Usage: @unloadnpcfile <path> {<flag>}
+1386: File unloaded. Be aware that some changes made by NPC are not reverted on unload. See doc/atcommands.txt for details.
1387: File not found.
// General command messages
@@ -1636,5 +1636,13 @@
1514: %d: Refine All Equip (Costume)
1515: %d: Refine All Equip (Shadow)
+// @reloadnpc
+1516: Usage: @reloadnpc <path> {<flag>}
+1517: Script could not be unloaded.
+
+// File name validation
+1518: Not a file.
+1519: Can't open file.
+
//Custom translations
import: conf/import/msg_conf.txt
diff --git a/doc/atcommands.txt b/doc/atcommands.txt
index 419cb0acd..0cddc1680 100644
--- a/doc/atcommands.txt
+++ b/doc/atcommands.txt
@@ -1273,31 +1273,47 @@ Example:
---------------------------------------
-@unloadnpc <npc name>
-
-Unloads an NPC.
+@unloadnpc <NPC_name> {<flag>}
+
+Unloads a NPC.
+Flag:
+ 0 - do not unload mobs spawned by NPCs in file
+ 1 - unload mobs spawned by NPCs in file
+ Default is 1; every number other than 0 will be treaten as 1.
+ Mobs that were spawned with monster/areamonster/guardian/bg_monster/atcommand("@monster xy") are affected.
+Note:
+ Be aware that some changes made by NPC are not reverted on unload.
+ Please use the OnNPCUnload label to clean things up by yourself. Commonly relevant for these script commands:
+ -> setmapflagnosave, setmapflag, removemapflags, setbattleflag, setcell, setwall
+ -> agitstart/agitstart2, agitend/agitend2, gvgon, gvgoff, pvpon, pvpoff
+ -> addmonsterdrop, setitemscript, npcshopitem, npcshopadditem, npcshopdelitem, hateffect, disguise
+ -> pcfollow, pcblockmove, setpcblock, setnpcdir, navigateto, viewpoint, add_group_command, skill/addtoskill
+ -> instance and battleground related stuff (excepting bg_monster)
Example:
-@unloadnpc Job Master
+@unloadnpc Job Master 0
---------------------------------------
-@unloadnpcfile <path>
+@unloadnpcfile <path> {<flag>}
Unloads all NPCs in a file.
+Flag: See @unloadnpc
+Note: See @unloadnpc
Example:
-@unloadnpcfile npc/custom/jobmaster.txt
+@unloadnpcfile npc/custom/jobmaster.txt 0
---------------------------------------
-@reloadnpc <path>
+@reloadnpc <path> {<flag>}
Unloads all NPCs in a file and reload it again.
-Note: Be aware that mapflags and monsters spawned directly are not removed.
+Flag: See @unloadnpc
+Note: See @unloadnpc
Example:
-@reloadnpc npc/custom/jobmaster.txt
+@reloadnpc npc/custom/jobmaster.txt 0
---------------------------------------
diff --git a/doc/script_commands.txt b/doc/script_commands.txt
index 6a97bae1d..1914285f0 100644
--- a/doc/script_commands.txt
+++ b/doc/script_commands.txt
@@ -1034,6 +1034,11 @@ will only execute once and will not execute if the map server reconnects
to the char server later. Note that all those events will be executed upon
scripts reloading.
+OnNPCUnload:
+
+OnNPCUnload will be executed when a NPC is unloaded.
+OnNPCUnload is called when @reloadscript, @reloadnpc, @unloadnpc or @unloadnpcfile is used.
+
OnAgitStart:
OnAgitEnd:
OnAgitInit:
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___;