Replaced some foreach-based functions by their inlined iterator equivalents.

Changed the dynamic mobs system, so that the flag that indicates whether a particular mob can be unloaded is stored in the mob's respawn data structure.
Cleaned up related parts of the source code.

git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@12358 54d463be-8e91-2dee-dedb-b68131a5f0ec
This commit is contained in:
ultramage 2008-03-13 22:09:58 +00:00
parent 4d9afc8f22
commit f6d2d9a360
8 changed files with 144 additions and 133 deletions

View File

@ -3800,14 +3800,14 @@ static CharCommandInfo* get_charcommandinfo_byname(const char* name)
int i; int i;
if( *name == charcommand_symbol ) name++; // for backwards compatibility if( *name == charcommand_symbol ) name++; // for backwards compatibility
ARR_FIND( 0, ARRAYLENGTH(charcommand_info), i, strcmpi(charcommand_info[i].command, name) == 0 ); ARR_FIND( 0, ARRAYLENGTH(charcommand_info), i, strcmpi(charcommand_info[i].command, name) == 0 );
return ( i != ARRAYLENGTH(charcommand_info) ) ? &charcommand_info[i] : NULL; return ( i < ARRAYLENGTH(charcommand_info) ) ? &charcommand_info[i] : NULL;
} }
static CharCommandInfo* get_charcommandinfo_byfunc(const CharCommandFunc func) static CharCommandInfo* get_charcommandinfo_byfunc(const CharCommandFunc func)
{ {
int i; int i;
ARR_FIND( 0, ARRAYLENGTH(charcommand_info), i, charcommand_info[i].func == func ); ARR_FIND( 0, ARRAYLENGTH(charcommand_info), i, charcommand_info[i].func == func );
return ( i != ARRAYLENGTH(charcommand_info) ) ? &charcommand_info[i] : NULL; return ( i < ARRAYLENGTH(charcommand_info) ) ? &charcommand_info[i] : NULL;
} }

View File

@ -2004,7 +2004,7 @@ void map_spawnmobs(int m)
if(map[m].moblist[i]!=NULL) if(map[m].moblist[i]!=NULL)
{ {
k+=map[m].moblist[i]->num; k+=map[m].moblist[i]->num;
npc_parse_mob2(map[m].moblist[i],true); npc_parse_mob2(map[m].moblist[i]);
} }
if (battle_config.etc_log && k > 0) if (battle_config.etc_log && k > 0)
@ -2013,22 +2013,18 @@ void map_spawnmobs(int m)
} }
} }
int mob_cache_cleanup_sub(struct block_list *bl, va_list ap) int map_removemobs_sub(struct block_list *bl, va_list ap)
{ {
struct mob_data *md = (struct mob_data *)bl; struct mob_data *md = (struct mob_data *)bl;
nullpo_retr(0, md); nullpo_retr(0, md);
//When not to remove: //When not to remove:
//Mob is not in cache //Mob respawn data is not in cache
if (!md->special_state.cached) if( md->spawn && !md->spawn->state.dynamic )
return 0; return 0;
//Mob is damaged and mob_remove_damaged is off //Mob is damaged and mob_remove_damaged is off
if( !battle_config.mob_remove_damaged && md->status.hp < md->status.max_hp ) if( !battle_config.mob_remove_damaged && md->status.hp < md->status.max_hp )
{
if( md->spawn ) //Do not respawn mob later.
md->spawn->skip++;
return 0; return 0;
}
unit_free(&md->bl,0); unit_free(&md->bl,0);
@ -2037,24 +2033,27 @@ int mob_cache_cleanup_sub(struct block_list *bl, va_list ap)
int map_removemobs_timer(int tid, unsigned int tick, int id, int data) int map_removemobs_timer(int tid, unsigned int tick, int id, int data)
{ {
int k; int count;
if (id < 0 || id >= MAX_MAP_PER_SERVER) const int m = id;
{ //Incorrect map id!
ShowError("map_removemobs_timer error: timer %d points to invalid map %d\n",tid, id);
return 0;
}
if (map[id].mob_delete_timer != tid)
{ //Incorrect timer call!
ShowError("map_removemobs_timer mismatch: %d != %d (map %s)\n",map[id].mob_delete_timer, tid, map[id].name);
return 0;
}
map[id].mob_delete_timer = -1;
if (map[id].users > 0) //Map not empty!
return 1;
k = map_foreachinmap(mob_cache_cleanup_sub, id, BL_MOB);
if (battle_config.etc_log && k > 0) if (m < 0 || m >= MAX_MAP_PER_SERVER)
ShowStatus("Map %s: Removed '"CL_WHITE"%d"CL_RESET"' mobs.\n",map[id].name, k); { //Incorrect map id!
ShowError("map_removemobs_timer error: timer %d points to invalid map %d\n",tid, m);
return 0;
}
if (map[m].mob_delete_timer != tid)
{ //Incorrect timer call!
ShowError("map_removemobs_timer mismatch: %d != %d (map %s)\n",map[m].mob_delete_timer, tid, map[m].name);
return 0;
}
map[m].mob_delete_timer = -1;
if (map[m].users > 0) //Map not empty!
return 1;
count = map_foreachinmap(map_removemobs_sub, m, BL_MOB);
if (battle_config.etc_log && count > 0)
ShowStatus("Map %s: Removed '"CL_WHITE"%d"CL_RESET"' mobs.\n",map[m].name, count);
return 1; return 1;
} }

View File

@ -865,13 +865,14 @@ struct spawn_data {
short class_; //Class, used because a mob can change it's class short class_; //Class, used because a mob can change it's class
unsigned short m,x,y; //Spawn information (map, point, spawn-area around point) unsigned short m,x,y; //Spawn information (map, point, spawn-area around point)
signed short xs,ys; signed short xs,ys;
unsigned short num; //Number of mobs using this structure. unsigned short num; //Number of mobs using this structure
unsigned short skip; //Number of mobs to skip when spawning them (for mob_remove_damageed: no) unsigned short active; //Number of mobs that are already spawned (for mob_remove_damaged: no)
unsigned int level; //Custom level. unsigned int level; //Custom level.
unsigned int delay1,delay2; //Min delay before respawning after spawn/death unsigned int delay1,delay2; //Min delay before respawning after spawn/death
struct { struct {
unsigned size :2; //Holds if mob has to be tiny/large unsigned size :2; //Holds if mob has to be tiny/large
unsigned ai :2; //Holds if mob is special ai. unsigned ai :2; //Holds if mob is special ai.
unsigned dynamic :1; //Whether this data is indexed by a map's dynamic mob list
} state; } state;
char name[NAME_LENGTH],eventname[50]; //Name/event char name[NAME_LENGTH],eventname[50]; //Name/event
}; };
@ -888,7 +889,6 @@ struct mob_data {
char name[NAME_LENGTH]; char name[NAME_LENGTH];
struct { struct {
unsigned size : 2; //Small/Big monsters. unsigned size : 2; //Small/Big monsters.
unsigned cached : 1; //Cached mobs for dynamic mob unloading [Skotlex]
unsigned ai : 2; //Special ai for summoned monsters. unsigned ai : 2; //Special ai for summoned monsters.
//0: Normal mob. //0: Normal mob.
//1: Standard summon, attacks mobs. //1: Standard summon, attacks mobs.

View File

@ -903,7 +903,10 @@ int mob_spawn (struct mob_data *md)
add_timer(tick+5000,mob_delayspawn,md->bl.id,0); add_timer(tick+5000,mob_delayspawn,md->bl.id,0);
return 1; return 1;
} }
md->spawn->active++;
} }
memset(&md->state, 0, sizeof(md->state)); memset(&md->state, 0, sizeof(md->state));
status_calc_mob(md, 1); status_calc_mob(md, 1);
md->attacked_id = 0; md->attacked_id = 0;
@ -2783,7 +2786,6 @@ int mob_summonslave(struct mob_data *md2,int *value,int amount,int skill_id)
continue; continue;
md= mob_spawn_dataset(&data); md= mob_spawn_dataset(&data);
md->special_state.cached= md2->special_state.cached; //[Skotlex]
if(skill_id == NPC_SUMMONSLAVE){ if(skill_id == NPC_SUMMONSLAVE){
md->master_id=md2->bl.id; md->master_id=md2->bl.id;
md->state.killer = md2->state.killer; md->state.killer = md2->state.killer;

View File

@ -2178,26 +2178,22 @@ static const char* npc_parse_function(char* w1, char* w2, char* w3, char* w4, co
* Parse Mob 1 - Parse mob list into each map * Parse Mob 1 - Parse mob list into each map
* Parse Mob 2 - Actually Spawns Mob * Parse Mob 2 - Actually Spawns Mob
* [Wizputer] * [Wizputer]
* If 'cached' is true, it is a dynamic cached mob
*------------------------------------------*/ *------------------------------------------*/
int npc_parse_mob2(struct spawn_data* mob, bool cached) void npc_parse_mob2(struct spawn_data* mob)
{ {
int i; int i;
struct mob_data *md;
for (i = mob->skip; i < mob->num; i++) { for( i = mob->active; i < mob->num; ++i )
md = mob_spawn_dataset(mob); {
struct mob_data* md = mob_spawn_dataset(mob);
md->spawn = mob; md->spawn = mob;
md->special_state.cached = cached; //If mob is cached on map, it is dynamically removed
mob_spawn(md); mob_spawn(md);
} }
mob->skip = 0;
return 1;
} }
static const char* npc_parse_mob(char* w1, char* w2, char* w3, char* w4, const char* start, const char* buffer, const char* filepath) static const char* npc_parse_mob(char* w1, char* w2, char* w3, char* w4, const char* start, const char* buffer, const char* filepath)
{ {
int level, num, class_, mode, x,y,xs,ys; int level, num, class_, mode, x,y,xs,ys, i,j;
char mapname[32]; char mapname[32];
char mobname[128]; char mobname[128];
struct spawn_data mob, *data; struct spawn_data mob, *data;
@ -2243,6 +2239,7 @@ static const char* npc_parse_mob(char* w1, char* w2, char* w3, char* w4, const c
} }
mob.num = (unsigned short)num; mob.num = (unsigned short)num;
mob.active = 0;
mob.class_ = (short) class_; mob.class_ = (short) class_;
mob.x = (unsigned short)x; mob.x = (unsigned short)x;
mob.y = (unsigned short)y; mob.y = (unsigned short)y;
@ -2299,34 +2296,36 @@ static const char* npc_parse_mob(char* w1, char* w2, char* w3, char* w4, const c
else else
strncpy(mob.name, mobname, NAME_LENGTH-1); strncpy(mob.name, mobname, NAME_LENGTH-1);
if( !mob_parse_dataset(&mob) ) //Verify dataset. //Verify dataset.
if( !mob_parse_dataset(&mob) )
{ {
ShowError("npc_parse_mob: Invalid dataset : %s %s (file '%s', line '%d').\n", w3, w4, filepath, strline(buffer,start-buffer)); ShowError("npc_parse_mob: Invalid dataset : %s %s (file '%s', line '%d').\n", w3, w4, filepath, strline(buffer,start-buffer));
return strchr(start,'\n');// skip and continue return strchr(start,'\n');// skip and continue
} }
for(x=0; x < ARRAYLENGTH(db->spawn); x++) //Update mob spawn lookup database
for( i = 0; i < ARRAYLENGTH(db->spawn); ++i )
{ {
if (map[mob.m].index == db->spawn[x].mapindex) if (map[mob.m].index == db->spawn[i].mapindex)
{ //Update total { //Update total
db->spawn[x].qty += mob.num; db->spawn[i].qty += mob.num;
//Re-sort list //Re-sort list
for (y = x; y>0 && db->spawn[y-1].qty < db->spawn[x].qty; y--); for( j = i; j > 0 && db->spawn[j-1].qty < db->spawn[i].qty; --j );
if (y != x) if( j != i )
{ {
xs = db->spawn[x].mapindex; xs = db->spawn[i].mapindex;
ys = db->spawn[x].qty; ys = db->spawn[i].qty;
memmove(&db->spawn[y+1], &db->spawn[y], (x-y)*sizeof(db->spawn[0])); memmove(&db->spawn[j+1], &db->spawn[j], (i-j)*sizeof(db->spawn[0]));
db->spawn[y].mapindex = xs; db->spawn[j].mapindex = xs;
db->spawn[y].qty = ys; db->spawn[j].qty = ys;
} }
break; break;
} }
if (mob.num > db->spawn[x].qty) if (mob.num > db->spawn[i].qty)
{ //Insert into list { //Insert into list
memmove(&db->spawn[x+1], &db->spawn[x], sizeof(db->spawn) -(x+1)*sizeof(db->spawn[0])); memmove(&db->spawn[i+1], &db->spawn[i], sizeof(db->spawn) -(i+1)*sizeof(db->spawn[0]));
db->spawn[x].mapindex = map[mob.m].index; db->spawn[i].mapindex = map[mob.m].index;
db->spawn[x].qty = mob.num; db->spawn[i].qty = mob.num;
break; break;
} }
} }
@ -2336,25 +2335,28 @@ static const char* npc_parse_mob(char* w1, char* w2, char* w3, char* w4, const c
data = aMalloc(sizeof(struct spawn_data)); data = aMalloc(sizeof(struct spawn_data));
memcpy(data, &mob, sizeof(struct spawn_data)); memcpy(data, &mob, sizeof(struct spawn_data));
if( !battle_config.dynamic_mobs || mob.delay1 || mob.delay2 ) { if( !battle_config.dynamic_mobs || data->delay1 || data->delay2 ) {
npc_parse_mob2(data,false); data->state.dynamic = false;
npc_delay_mob += mob.num; npc_parse_mob2(data);
npc_delay_mob += data->num;
} else { } else {
int index = map_addmobtolist(data->m, data); int index = map_addmobtolist(data->m, data);
if( index >= 0 ) { if( index >= 0 ) {
data->state.dynamic = true;
// check if target map has players // check if target map has players
// (usually shouldn't occur when map server is just starting, // (usually shouldn't occur when map server is just starting,
// but not the case when we do @reloadscript // but not the case when we do @reloadscript
if (map[mob.m].users > 0) if (map[data->m].users > 0)
npc_parse_mob2(data,true); npc_parse_mob2(data);
npc_cache_mob += mob.num; npc_cache_mob += data->num;
} else { } else {
// mobcache is full // mobcache is full
// create them as delayed with one second // create them as delayed with one second
mob.delay1 = 1000; data->state.dynamic = false;
mob.delay2 = 1000; data->delay1 = 1000;
npc_parse_mob2(data,false); data->delay2 = 1000;
npc_delay_mob += mob.num; npc_parse_mob2(data);
npc_delay_mob += data->num;
} }
} }
@ -2587,11 +2589,7 @@ static const char* npc_parse_mapflag(char* w1, char* w2, char* w3, char* w4, con
else if (!strcmpi(w3,"guildlock")) else if (!strcmpi(w3,"guildlock"))
map[m].flag.guildlock=state; map[m].flag.guildlock=state;
else else
{ ShowError("npc_parse_mapflag: unrecognized mapflag '%s' (file '%s', line '%d').\n", w3, filepath, strline(buffer,start-buffer));
char buf[256];
sv_escape_c(buf, w3, strlen(w3), NULL); // to handle \r properly
ShowError("npc_parse_mapflag: unrecognized mapflag '%s' (file '%s', line '%d').\n", buf, filepath, strline(buffer,start-buffer));
}
return strchr(start,'\n');// continue return strchr(start,'\n');// continue
} }
@ -2746,26 +2744,6 @@ int npc_script_event(struct map_session_data* sd, enum npce_event type)
return i; return i;
} }
static int npc_read_event_script_sub(DBKey key, void* data, va_list ap)
{
const char* p = key.str;
char* name = va_arg(ap, char *);
struct event_data** event_buf = va_arg(ap, struct event_data**);
const char** event_name = va_arg(ap,const char **);
unsigned char *count = va_arg(ap, unsigned char *);
if (*count >= UCHAR_MAX) return 0;
if((p=strchr(p,':')) && p && strcmpi(name,p)==0 )
{
event_buf[*count] = (struct event_data *)data;
event_name[*count] = key.str;
(*count)++;
return 1;
}
return 0;
}
void npc_read_event_script(void) void npc_read_event_script(void)
{ {
int i; int i;
@ -2783,16 +2761,39 @@ void npc_read_event_script(void)
{"Kill NPC Event",script_config.kill_mob_event_name}, {"Kill NPC Event",script_config.kill_mob_event_name},
}; };
for (i = 0; i < NPCE_MAX; i++) { for (i = 0; i < NPCE_MAX; i++)
char buf[64]="::"; {
DBIterator* iter;
DBKey key;
void* data;
char name[64]="::";
strncpy(name+2,config[i].event_name,62);
script_event[i].event_count = 0; script_event[i].event_count = 0;
//Use an array of Events iter = ev_db->iterator(ev_db);
strncpy(buf+2,config[i].event_name,62); for( data = iter->first(iter,&key); iter->exists(iter); data = iter->next(iter,&key) )
ev_db->foreach(ev_db,npc_read_event_script_sub,buf, {
&script_event[i].event, const char* p = key.str;
&script_event[i].event_name, struct event_data* ed = (struct event_data*) data;
&script_event[i].event_count); unsigned char count = script_event[i].event_count;
if( count >= ARRAYLENGTH(script_event[i].event) )
{
ShowWarning("npc_read_event_script: too many occurences of event '%s'!\n", config[i].event_name);
break;
} }
if( (p=strchr(p,':')) && p && strcmpi(name,p)==0 )
{
script_event[i].event[count] = ed;
script_event[i].event_name[count] = key.str;
script_event[i].event_count++;
}
}
iter->destroy(iter);
}
if (battle_config.etc_log) { if (battle_config.etc_log) {
//Print summary. //Print summary.
for (i = 0; i < NPCE_MAX; i++) for (i = 0; i < NPCE_MAX; i++)
@ -2800,11 +2801,18 @@ void npc_read_event_script(void)
} }
} }
static int npc_cleanup_dbsub(DBKey key, void* data, va_list ap) int npc_reload(void)
{ {
struct block_list* bl = (struct block_list*)data; struct npc_src_list *nsl;
nullpo_retr(0, bl); int m, i;
int npc_new_min = npc_id;
struct s_mapiterator* iter;
struct block_list* bl;
//Remove all npcs/mobs. [Skotlex]
iter = mapit_geteachiddb();
for( bl = mapit_first(iter); mapit_exists(iter); bl = mapit_next(iter) )
{
switch(bl->type) { switch(bl->type) {
case BL_NPC: case BL_NPC:
if( bl->id != fake_nd->bl.id )// don't remove fake_nd if( bl->id != fake_nd->bl.id )// don't remove fake_nd
@ -2814,27 +2822,24 @@ static int npc_cleanup_dbsub(DBKey key, void* data, va_list ap)
unit_free(bl,0); unit_free(bl,0);
break; break;
} }
return 0;
} }
mapit_free(iter);
int npc_reload(void) if(battle_config.dynamic_mobs)
{ {// dynamic check by [random]
struct npc_src_list *nsl;
int m, i;
int npc_new_min = npc_id;
//Remove all npcs/mobs. [Skotlex]
map_foreachiddb(npc_cleanup_dbsub);
for (m = 0; m < map_num; m++) { for (m = 0; m < map_num; m++) {
if(battle_config.dynamic_mobs) { //dynamic check by [random] for (i = 0; i < MAX_MOB_LIST_PER_MAP; i++) {
for (i = 0; i < MAX_MOB_LIST_PER_MAP; i++) if (map[m].moblist[i] != NULL) {
if (map[m].moblist[i]) aFree(map[m].moblist[i]); aFree(map[m].moblist[i]);
memset (map[m].moblist, 0, sizeof(map[m].moblist)); map[m].moblist[i] = NULL;
}
}
} }
if (map[m].npc_num > 0) if (map[m].npc_num > 0)
ShowWarning("npc_reload: %d npcs weren't removed at map %s!\n", map[m].npc_num, map[m].name); ShowWarning("npc_reload: %d npcs weren't removed at map %s!\n", map[m].npc_num, map[m].name);
} }
// clear mob spawn lookup index
mob_clear_spawninfo(); mob_clear_spawninfo();
// clear npc-related data structures // clear npc-related data structures
@ -2855,7 +2860,7 @@ int npc_reload(void)
"\t-'"CL_WHITE"%d"CL_RESET"' Warps\n" "\t-'"CL_WHITE"%d"CL_RESET"' Warps\n"
"\t-'"CL_WHITE"%d"CL_RESET"' Shops\n" "\t-'"CL_WHITE"%d"CL_RESET"' Shops\n"
"\t-'"CL_WHITE"%d"CL_RESET"' Scripts\n" "\t-'"CL_WHITE"%d"CL_RESET"' Scripts\n"
"\t-'"CL_WHITE"%d"CL_RESET"' Mobs\n" "\t-'"CL_WHITE"%d"CL_RESET"' Mob sets\n"
"\t-'"CL_WHITE"%d"CL_RESET"' Mobs Cached\n" "\t-'"CL_WHITE"%d"CL_RESET"' Mobs Cached\n"
"\t-'"CL_WHITE"%d"CL_RESET"' Mobs Not Cached\n", "\t-'"CL_WHITE"%d"CL_RESET"' Mobs Not Cached\n",
npc_id - npc_new_min, npc_warp, npc_shop, npc_script, npc_mob, npc_cache_mob, npc_delay_mob); npc_id - npc_new_min, npc_warp, npc_shop, npc_script, npc_mob, npc_cache_mob, npc_delay_mob);
@ -2966,7 +2971,7 @@ int do_init_npc(void)
"\t-'"CL_WHITE"%d"CL_RESET"' Warps\n" "\t-'"CL_WHITE"%d"CL_RESET"' Warps\n"
"\t-'"CL_WHITE"%d"CL_RESET"' Shops\n" "\t-'"CL_WHITE"%d"CL_RESET"' Shops\n"
"\t-'"CL_WHITE"%d"CL_RESET"' Scripts\n" "\t-'"CL_WHITE"%d"CL_RESET"' Scripts\n"
"\t-'"CL_WHITE"%d"CL_RESET"' Mobs\n" "\t-'"CL_WHITE"%d"CL_RESET"' Mob sets\n"
"\t-'"CL_WHITE"%d"CL_RESET"' Mobs Cached\n" "\t-'"CL_WHITE"%d"CL_RESET"' Mobs Cached\n"
"\t-'"CL_WHITE"%d"CL_RESET"' Mobs Not Cached\n", "\t-'"CL_WHITE"%d"CL_RESET"' Mobs Not Cached\n",
npc_id - START_NPC_NUM, npc_warp, npc_shop, npc_script, npc_mob, npc_cache_mob, npc_delay_mob); npc_id - START_NPC_NUM, npc_warp, npc_shop, npc_script, npc_mob, npc_cache_mob, npc_delay_mob);

View File

@ -51,7 +51,7 @@ struct npc_data* npc_checknear(struct map_session_data* sd, struct block_list* b
int npc_buysellsel(struct map_session_data* sd, int id, int type); int npc_buysellsel(struct map_session_data* sd, int id, int type);
int npc_buylist(struct map_session_data* sd,int n, unsigned short* item_list); int npc_buylist(struct map_session_data* sd,int n, unsigned short* item_list);
int npc_selllist(struct map_session_data* sd, int n, unsigned short* item_list); int npc_selllist(struct map_session_data* sd, int n, unsigned short* item_list);
int npc_parse_mob2(struct spawn_data* mob, bool cached); // [Wizputer] void npc_parse_mob2(struct spawn_data* mob);
struct npc_data* npc_add_warp(short from_mapid, short from_x, short from_y, short xs, short ys, unsigned short to_mapindex, short to_x, short to_y); struct npc_data* npc_add_warp(short from_mapid, short from_x, short from_y, short xs, short ys, unsigned short to_mapindex, short to_x, short to_y);
int npc_globalmessage(const char* name,const char* mes); int npc_globalmessage(const char* name,const char* mes);

View File

@ -7535,7 +7535,7 @@ int skill_check_condition(struct map_session_data* sd, short skill, short lv, in
itemid[i] = skill_db[j].itemid[i]; itemid[i] = skill_db[j].itemid[i];
amount[i] = skill_db[j].amount[i]; amount[i] = skill_db[j].amount[i];
} }
if(mhp > 0 && 100 * status->hp / status->max_hp > (unsigned int) mhp) { if(mhp > 0 && status_calc_life(status->hp, status->max_hp) > mhp) {
//mhp is the max-hp-requirement, that is, //mhp is the max-hp-requirement, that is,
//you must have this % or less of HP to cast it. //you must have this % or less of HP to cast it.
clif_skill_fail(sd,skill,2,0); clif_skill_fail(sd,skill,2,0);

View File

@ -1890,12 +1890,17 @@ int unit_free(struct block_list *bl, int clrtype)
aFree(md->guardian_data); aFree(md->guardian_data);
md->guardian_data = NULL; md->guardian_data = NULL;
} }
if (md->spawn && !md->special_state.cached && --(md->spawn->num) == 0) if(md->spawn)
{ //Spawning data is not attached to the map, so free it {
//if this is the last mob who is pointing at it. md->spawn->active--;
md->spawn->num--;
if( !md->spawn->state.dynamic && md->spawn->num == 0 )
{// Last freed mob is responsible for deallocating the group's spawn data.
aFree(md->spawn); aFree(md->spawn);
md->spawn = NULL; md->spawn = NULL;
} }
}
if(md->base_status) { if(md->base_status) {
aFree(md->base_status); aFree(md->base_status);
md->base_status = NULL; md->base_status = NULL;