From 06bff5ca4d55db05bd46868e551b60807920b29f Mon Sep 17 00:00:00 2001 From: ultramage Date: Thu, 13 Mar 2008 22:09:58 +0000 Subject: 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://rathena.svn.sourceforge.net/svnroot/rathena/trunk@12358 54d463be-8e91-2dee-dedb-b68131a5f0ec --- src/map/charcommand.c | 4 +- src/map/map.c | 37 +++++----- src/map/map.h | 6 +- src/map/mob.c | 4 +- src/map/npc.c | 191 ++++++++++++++++++++++++++------------------------ src/map/npc.h | 2 +- src/map/skill.c | 2 +- src/map/unit.c | 17 +++-- 8 files changed, 137 insertions(+), 126 deletions(-) diff --git a/src/map/charcommand.c b/src/map/charcommand.c index bde5e7d2e..cd2c31749 100644 --- a/src/map/charcommand.c +++ b/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; } diff --git a/src/map/map.c b/src/map/map.c index 30e16aa55..a956952b8 100644 --- a/src/map/map.c +++ b/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; } diff --git a/src/map/map.h b/src/map/map.h index 06dd27fb4..c15af9b79 100644 --- a/src/map/map.h +++ b/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. diff --git a/src/map/mob.c b/src/map/mob.c index dabee94ae..b9cc38ca9 100644 --- a/src/map/mob.c +++ b/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; diff --git a/src/map/npc.c b/src/map/npc.c index 86b3c260a..5ed3f4e2d 100644 --- a/src/map/npc.c +++ b/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); diff --git a/src/map/npc.h b/src/map/npc.h index f54f4d232..d2e87df5d 100644 --- a/src/map/npc.h +++ b/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); diff --git a/src/map/skill.c b/src/map/skill.c index d1e230247..87d109d87 100644 --- a/src/map/skill.c +++ b/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); diff --git a/src/map/unit.c b/src/map/unit.c index ed5980786..5a7a62b96 100644 --- a/src/map/unit.c +++ b/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); -- cgit v1.2.3-60-g2f50