浏览代码

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

ultramage 17 年之前
父节点
当前提交
f6d2d9a360
共有 8 个文件被更改,包括 137 次插入126 次删除
  1. 2 2
      src/map/charcommand.c
  2. 18 19
      src/map/map.c
  3. 3 3
      src/map/map.h
  4. 3 1
      src/map/mob.c
  5. 98 93
      src/map/npc.c
  6. 1 1
      src/map/npc.h
  7. 1 1
      src/map/skill.c
  8. 11 6
      src/map/unit.c

+ 2 - 2
src/map/charcommand.c

@@ -3800,14 +3800,14 @@ static CharCommandInfo* get_charcommandinfo_byname(const char* name)
 	int i;
 	if( *name == charcommand_symbol ) name++; // for backwards compatibility
 	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)
 {
 	int i;
 	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;
 }
 
 

+ 18 - 19
src/map/map.c

@@ -2004,7 +2004,7 @@ void map_spawnmobs(int m)
 		if(map[m].moblist[i]!=NULL)
 		{
 			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)
@@ -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;
 	nullpo_retr(0, md);
 
 	//When not to remove:
-	//Mob is not in cache
-	if (!md->special_state.cached)
+	//Mob respawn data is not in cache
+	if( md->spawn && !md->spawn->state.dynamic )
 		return 0;
 	//Mob is damaged and mob_remove_damaged is off
-	if (!battle_config.mob_remove_damaged && md->status.hp < md->status.max_hp)
-	{
-		if( md->spawn ) //Do not respawn mob later.
-			md->spawn->skip++;
+	if( !battle_config.mob_remove_damaged && md->status.hp < md->status.max_hp )
 		return 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 k;
-	if (id < 0 || id >= MAX_MAP_PER_SERVER)
+	int count;
+	const int m = id;
+
+	if (m < 0 || m >= MAX_MAP_PER_SERVER)
 	{	//Incorrect map id!
-		ShowError("map_removemobs_timer error: timer %d points to invalid map %d\n",tid, id);
+		ShowError("map_removemobs_timer error: timer %d points to invalid map %d\n",tid, m);
 		return 0;
 	}
-	if (map[id].mob_delete_timer != tid)
+	if (map[m].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);
+		ShowError("map_removemobs_timer mismatch: %d != %d (map %s)\n",map[m].mob_delete_timer, tid, map[m].name);
 		return 0;
 	}
-	map[id].mob_delete_timer = -1;
-	if (map[id].users > 0) //Map not empty!
+	map[m].mob_delete_timer = -1;
+	if (map[m].users > 0) //Map not empty!
 		return 1;
-	k = map_foreachinmap(mob_cache_cleanup_sub, id, BL_MOB);
 
-	if (battle_config.etc_log && k > 0)
-		ShowStatus("Map %s: Removed '"CL_WHITE"%d"CL_RESET"' mobs.\n",map[id].name, k);
+	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;
 }

+ 3 - 3
src/map/map.h

@@ -865,13 +865,14 @@ struct spawn_data {
 	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)
 	signed short xs,ys;
-	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 num; //Number of mobs using this structure
+	unsigned short active; //Number of mobs that are already spawned (for mob_remove_damaged: no)
 	unsigned int level; //Custom level.
 	unsigned int delay1,delay2; //Min delay before respawning after spawn/death
 	struct {
 		unsigned size :2; //Holds if mob has to be tiny/large
 		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;
 	char name[NAME_LENGTH],eventname[50]; //Name/event
 };
@@ -888,7 +889,6 @@ struct mob_data {
 	char name[NAME_LENGTH];
 	struct {
 		unsigned size : 2; //Small/Big monsters.
-		unsigned cached : 1; //Cached mobs for dynamic mob unloading [Skotlex]
 		unsigned ai : 2; //Special ai for summoned monsters.
 							//0: Normal mob.
 							//1: Standard summon, attacks mobs.

+ 3 - 1
src/map/mob.c

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

+ 98 - 93
src/map/npc.c

@@ -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 2 - Actually Spawns Mob
  * [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;
-	struct mob_data *md;
 
-	for (i = mob->skip; i < mob->num; i++) {
-		md = mob_spawn_dataset(mob);
+	for( i = mob->active; i < mob->num; ++i )
+	{
+		struct mob_data* md = mob_spawn_dataset(mob);
 		md->spawn = mob;
-		md->special_state.cached = cached;	//If mob is cached on map, it is dynamically removed
 		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)
 {
-	int level, num, class_, mode, x,y,xs,ys;
+	int level, num, class_, mode, x,y,xs,ys, i,j;
 	char mapname[32];
 	char mobname[128];
 	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.active = 0;
 	mob.class_ = (short) class_;
 	mob.x = (unsigned short)x;
 	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
 		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));
 		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
-			db->spawn[x].qty += mob.num;
+			db->spawn[i].qty += mob.num;
 			//Re-sort list
-			for (y = x; y>0 && db->spawn[y-1].qty < db->spawn[x].qty; y--);
-			if (y != x)
+			for( j = i; j > 0 && db->spawn[j-1].qty < db->spawn[i].qty; --j );
+			if( j != i )
 			{
-				xs = db->spawn[x].mapindex;
-				ys = db->spawn[x].qty;
-				memmove(&db->spawn[y+1], &db->spawn[y], (x-y)*sizeof(db->spawn[0]));
-				db->spawn[y].mapindex = xs;
-				db->spawn[y].qty = ys;
+				xs = db->spawn[i].mapindex;
+				ys = db->spawn[i].qty;
+				memmove(&db->spawn[j+1], &db->spawn[j], (i-j)*sizeof(db->spawn[0]));
+				db->spawn[j].mapindex = xs;
+				db->spawn[j].qty = ys;
 			}
 			break;
 		}
-		if (mob.num > db->spawn[x].qty)
+		if (mob.num > db->spawn[i].qty)
 		{	//Insert into list
-			memmove(&db->spawn[x+1], &db->spawn[x], sizeof(db->spawn) -(x+1)*sizeof(db->spawn[0]));
-			db->spawn[x].mapindex = map[mob.m].index;
-			db->spawn[x].qty = mob.num;
+			memmove(&db->spawn[i+1], &db->spawn[i], sizeof(db->spawn) -(i+1)*sizeof(db->spawn[0]));
+			db->spawn[i].mapindex = map[mob.m].index;
+			db->spawn[i].qty = mob.num;
 			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));
 	memcpy(data, &mob, sizeof(struct spawn_data));
 	
-	if( !battle_config.dynamic_mobs || mob.delay1 || mob.delay2 ) {
-		npc_parse_mob2(data,false);
-		npc_delay_mob += mob.num;
+	if( !battle_config.dynamic_mobs || data->delay1 || data->delay2 ) {
+		data->state.dynamic = false;
+		npc_parse_mob2(data);
+		npc_delay_mob += data->num;
 	} else {
 		int index = map_addmobtolist(data->m, data);
 		if( index >= 0 ) {
+			data->state.dynamic = true;
 			// check if target map has players
 			// (usually shouldn't occur when map server is just starting,
 			// but not the case when we do @reloadscript
-			if (map[mob.m].users > 0)
-				npc_parse_mob2(data,true);
-			npc_cache_mob += mob.num;
+			if (map[data->m].users > 0)
+				npc_parse_mob2(data);
+			npc_cache_mob += data->num;
 		} else {
 			// mobcache is full
 			// create them as delayed with one second
-			mob.delay1 = 1000;
-			mob.delay2 = 1000;
-			npc_parse_mob2(data,false);
-			npc_delay_mob += mob.num;
+			data->state.dynamic = false;
+			data->delay1 = 1000;
+			data->delay2 = 1000;
+			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"))
 		map[m].flag.guildlock=state;
 	else
-	{
-		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));
-	}
+		ShowError("npc_parse_mapflag: unrecognized mapflag '%s' (file '%s', line '%d').\n", w3, filepath, strline(buffer,start-buffer));
 
 	return strchr(start,'\n');// continue
 }
@@ -2746,26 +2744,6 @@ int npc_script_event(struct map_session_data* sd, enum npce_event type)
 	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)
 {
 	int i;
@@ -2783,16 +2761,39 @@ void npc_read_event_script(void)
 		{"Kill NPC Event",script_config.kill_mob_event_name},
 	};
 
-	for (i = 0; i < NPCE_MAX; i++) {
-		char buf[64]="::";
+	for (i = 0; i < NPCE_MAX; i++)
+	{
+		DBIterator* iter;
+		DBKey key;
+		void* data;
+
+		char name[64]="::";
+		strncpy(name+2,config[i].event_name,62);
+
 		script_event[i].event_count = 0;
-		//Use an array of Events
-		strncpy(buf+2,config[i].event_name,62);
-		ev_db->foreach(ev_db,npc_read_event_script_sub,buf,
-			&script_event[i].event,
-			&script_event[i].event_name,
-			&script_event[i].event_count);
+		iter = ev_db->iterator(ev_db);
+		for( data = iter->first(iter,&key); iter->exists(iter); data = iter->next(iter,&key) )
+		{
+			const char* p = key.str;
+			struct event_data* ed = (struct event_data*) data;
+			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) {
 		//Print summary.
 		for (i = 0; i < NPCE_MAX; i++)
@@ -2800,41 +2801,45 @@ void npc_read_event_script(void)
 	}
 }
 
-static int npc_cleanup_dbsub(DBKey key, void* data, va_list ap)
-{
-	struct block_list* bl = (struct block_list*)data;
-	nullpo_retr(0, bl);
-
-	switch(bl->type) {
-	case BL_NPC:
-		if( bl->id != fake_nd->bl.id )// don't remove fake_nd
-			npc_unload((struct npc_data *)bl);
-		break;
-	case BL_MOB:
-		unit_free(bl,0);
-		break;
-	}
-
-	return 0;
-}
-
 int npc_reload(void)
 {
 	struct npc_src_list *nsl;
 	int m, i;
 	int npc_new_min = npc_id;
+	struct s_mapiterator* iter;
+	struct block_list* bl;
 
 	//Remove all npcs/mobs. [Skotlex]
-	map_foreachiddb(npc_cleanup_dbsub);
-	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++)
-				if (map[m].moblist[i]) aFree(map[m].moblist[i]);
-			memset (map[m].moblist, 0, sizeof(map[m].moblist));
+	iter = mapit_geteachiddb();
+	for( bl = mapit_first(iter); mapit_exists(iter); bl = mapit_next(iter) )
+	{
+		switch(bl->type) {
+		case BL_NPC:
+			if( bl->id != fake_nd->bl.id )// don't remove fake_nd
+				npc_unload((struct npc_data *)bl);
+			break;
+		case BL_MOB:
+			unit_free(bl,0);
+			break;
+		}
+	}
+	mapit_free(iter);
+
+	if(battle_config.dynamic_mobs)
+	{// dynamic check by [random]
+		for (m = 0; m < map_num; m++) {
+			for (i = 0; i < MAX_MOB_LIST_PER_MAP; i++) {
+				if (map[m].moblist[i] != NULL) {
+					aFree(map[m].moblist[i]);
+					map[m].moblist[i] = NULL;
+				}
+			}
 		}
 		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);
 	}
+
+	// clear mob spawn lookup index
 	mob_clear_spawninfo();
 
 	// 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"' Shops\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 Not Cached\n",
 		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"' Shops\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 Not Cached\n",
 		npc_id - START_NPC_NUM, npc_warp, npc_shop, npc_script, npc_mob, npc_cache_mob, npc_delay_mob);

+ 1 - 1
src/map/npc.h

@@ -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_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_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);
 int npc_globalmessage(const char* name,const char* mes);
 

+ 1 - 1
src/map/skill.c

@@ -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];
 		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,
 		//you must have this % or less of HP to cast it.
 		clif_skill_fail(sd,skill,2,0);

+ 11 - 6
src/map/unit.c

@@ -1883,18 +1883,23 @@ int unit_free(struct block_list *bl, int clrtype)
 			aFree(md->lootitem);
 			md->lootitem=NULL;
 		}
-		if (md->guardian_data)
+		if(md->guardian_data)
 		{
 			if (md->guardian_data->number < MAX_GUARDIANS)
 				md->guardian_data->castle->guardian[md->guardian_data->number].id = 0;
 			aFree(md->guardian_data);
 			md->guardian_data = NULL;
 		}
-		if (md->spawn && !md->special_state.cached && --(md->spawn->num) == 0)
-		{	//Spawning data is not attached to the map, so free it
-			//if this is the last mob who is pointing at it.
-			aFree(md->spawn);
-			md->spawn = NULL;
+		if(md->spawn)
+		{
+			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);
+				md->spawn = NULL;
+			}
 		}
 		if(md->base_status) {
 			aFree(md->base_status);