From a84e731f794ad6d1b6308a6f6e5e2aa6c85c886b Mon Sep 17 00:00:00 2001 From: Aleos Date: Mon, 23 Aug 2021 14:11:53 -0400 Subject: [PATCH] Cleans up the skill unit group storage (#6195) * Fixes #2217. * Adjusts the storage for skill unit group from DBMap to unordered_map. * Removes the MAX_SKILLUNITGROUP limit. * Cleans up memory management to utilize shared_ptr. Thanks to @Lemongrass3110! Co-authored-by: Lemongrass3110 --- src/map/battle.cpp | 11 +- src/map/chat.cpp | 10 +- src/map/guild.cpp | 24 +-- src/map/script.cpp | 2 +- src/map/skill.cpp | 359 ++++++++++++++++++++++----------------------- src/map/skill.hpp | 42 +++--- src/map/status.cpp | 30 ++-- src/map/unit.cpp | 13 ++ src/map/unit.hpp | 3 +- 9 files changed, 250 insertions(+), 244 deletions(-) diff --git a/src/map/battle.cpp b/src/map/battle.cpp index 6b11b37cf5..815a3d2221 100644 --- a/src/map/battle.cpp +++ b/src/map/battle.cpp @@ -468,7 +468,7 @@ int64 battle_attr_fix(struct block_list *src, struct block_list *target, int64 d if( target && target->type == BL_SKILL ) { if( atk_elem == ELE_FIRE && battle_getcurrentskill(target) == GN_WALLOFTHORN ) { struct skill_unit *su = (struct skill_unit*)target; - struct skill_unit_group *sg; + std::shared_ptr sg; struct block_list *src2; if( !su || !su->alive || (sg = su->group) == NULL || !sg || sg->val3 == -1 || @@ -1133,7 +1133,7 @@ bool battle_status_block_damage(struct block_list *src, struct block_list *targe // ATK_BLOCK Type if ((sce = sc->data[SC_SAFETYWALL]) && (flag&(BF_SHORT | BF_MAGIC)) == BF_SHORT) { - skill_unit_group *group = skill_id2group(sce->val3); + std::shared_ptr group = skill_id2group(sce->val3); if (group) { d->dmg_lv = ATK_BLOCK; @@ -8032,11 +8032,8 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t if( BL_PC&battle_config.land_skill_limit && (maxcount = skill_get_maxcount(r_skill, r_lv)) > 0 ) { - int v; - for(v=0;vud.skillunit[v] && maxcount;v++) { - if(sd->ud.skillunit[v]->skill_id == r_skill) - maxcount--; - } + unit_skillunit_maxcount(sd->ud, r_skill, maxcount); + if( maxcount == 0 ) type = -1; } diff --git a/src/map/chat.cpp b/src/map/chat.cpp index 010574ca92..c350d40de3 100644 --- a/src/map/chat.cpp +++ b/src/map/chat.cpp @@ -217,20 +217,16 @@ int chat_leavechat(struct map_session_data* sd, bool kicked) cd->usersd[i] = cd->usersd[i+1]; if( cd->users == 0 && cd->owner->type == BL_PC ) { // Delete empty chatroom - struct skill_unit* unit; - struct skill_unit_group* group; - clif_clearchat(cd, 0); db_destroy(cd->kick_list); map_deliddb(&cd->bl); map_delblock(&cd->bl); map_freeblock(&cd->bl); - unit = map_find_skill_unit_oncell(&sd->bl, sd->bl.x, sd->bl.y, AL_WARP, NULL, 0); - group = (unit != NULL) ? unit->group : NULL; + skill_unit *unit = map_find_skill_unit_oncell(&sd->bl, sd->bl.x, sd->bl.y, AL_WARP, nullptr, 0); - if (group != NULL) - ext_skill_unit_onplace(unit, &sd->bl, group->tick); + if (unit != nullptr && unit->group != nullptr) + ext_skill_unit_onplace(unit, &sd->bl, unit->group->tick); return 1; } diff --git a/src/map/guild.cpp b/src/map/guild.cpp index 9f662a2cbc..06b3fac20f 100644 --- a/src/map/guild.cpp +++ b/src/map/guild.cpp @@ -1523,13 +1523,15 @@ int guild_skillupack(int guild_id,uint16 skill_id,uint32 account_id) { } void guild_guildaura_refresh(struct map_session_data *sd, uint16 skill_id, uint16 skill_lv) { - struct skill_unit_group* group = NULL; - sc_type type = status_skill2sc(skill_id); if( !(battle_config.guild_aura&(is_agit_start()?2:1)) && !(battle_config.guild_aura&(map_flag_gvg2(sd->bl.m)?8:4)) ) return; if( !skill_lv ) return; + + std::shared_ptr group; + sc_type type = status_skill2sc(skill_id); + if( sd->sc.data[type] && (group = skill_id2group(sd->sc.data[type]->val4)) ) { skill_delunitgroup(group); status_change_end(&sd->bl,type,INVALID_TIMER); @@ -2058,24 +2060,22 @@ int guild_break(struct map_session_data *sd,char *name) { /* Regardless of char server allowing it, we clear the guild master's auras */ if ((ud = unit_bl2ud(&sd->bl))) { - int count = 0; - struct skill_unit_group *group[4]; + std::vector> group; - for(i = 0; i < MAX_SKILLUNITGROUP && ud->skillunit[i]; i++) { - switch(ud->skillunit[i]->skill_id) { + for (const auto su : ud->skillunits) { + switch (su->skill_id) { case GD_LEADERSHIP: case GD_GLORYWOUNDS: case GD_SOULCOLD: case GD_HAWKEYES: - if(count == 4) - ShowWarning("guild_break: '%s' got more than 4 guild aura instances! (%d)\n",sd->status.name,ud->skillunit[i]->skill_id); - else - group[count++] = ud->skillunit[i]; + group.push_back(su); break; } } - for (i = 0; i < count; i++) - skill_delunitgroup(group[i]); + + for (auto it = group.begin(); it != group.end(); it++) { + skill_delunitgroup(*it); + } } #ifdef BOUND_ITEMS diff --git a/src/map/script.cpp b/src/map/script.cpp index e92230ec11..86da652980 100644 --- a/src/map/script.cpp +++ b/src/map/script.cpp @@ -19412,7 +19412,7 @@ BUILDIN_FUNC(warpportal) unsigned short mapindex; int tpx; int tpy; - struct skill_unit_group* group; + std::shared_ptr group; struct block_list* bl; bl = map_id2bl(st->oid); diff --git a/src/map/skill.cpp b/src/map/skill.cpp index cf3c7b5330..e86e16abb9 100755 --- a/src/map/skill.cpp +++ b/src/map/skill.cpp @@ -67,7 +67,6 @@ using namespace rathena; static uint16 skilldb_id2idx[(UINT16_MAX + 1)]; /// Skill ID to Index lookup: skill_index = skill_get_index(skill_id) - [FWI] 20160423 the whole index thing should be removed. static uint16 skill_num = 1; /// Skill count, also as last index -static struct eri *skill_unit_ers = NULL; //For handling skill_unit's [Skotlex] static struct eri *skill_timer_ers = NULL; //For handling skill_timerskills [Skotlex] static DBMap* bowling_db = NULL; // int mob_id -> struct mob_data* @@ -326,12 +325,12 @@ int skill_tree_get_max(uint16 skill_id, int b_class) int skill_frostjoke_scream(struct block_list *bl,va_list ap); int skill_attack_area(struct block_list *bl,va_list ap); -struct skill_unit_group *skill_locate_element_field(struct block_list *bl); // [Skotlex] +std::shared_ptr skill_locate_element_field(struct block_list *bl); // [Skotlex] int skill_graffitiremover(struct block_list *bl, va_list ap); // [Valaris] int skill_greed(struct block_list *bl, va_list ap); static int skill_cell_overlap(struct block_list *bl, va_list ap); static int skill_trap_splash(struct block_list *bl, va_list ap); -struct skill_unit_group_tickset *skill_unitgrouptickset_search(struct block_list *bl,struct skill_unit_group *sg,t_tick tick); +struct skill_unit_group_tickset *skill_unitgrouptickset_search(struct block_list *bl,std::shared_ptr sg,t_tick tick); static int skill_unit_onplace(struct skill_unit *src,struct block_list *bl,t_tick tick); int skill_unit_onleft(uint16 skill_id, struct block_list *bl,t_tick tick); static int skill_unit_effect(struct block_list *bl,va_list ap); @@ -488,10 +487,8 @@ bool skill_pos_maxcount_check(struct block_list *src, int16 x, int16 y, uint16 s return false; } if (type&battle_config.land_skill_limit && (maxcount = skill_get_maxcount(skill_id, skill_lv)) > 0) { - for (int i = 0; i < MAX_SKILLUNITGROUP && ud->skillunit[i] && maxcount; i++) { - if (ud->skillunit[i]->skill_id == skill_id) - maxcount--; - } + unit_skillunit_maxcount(*ud, skill_id, maxcount); + if (maxcount == 0) { if (sd && display_failure) clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0); @@ -4445,14 +4442,16 @@ int skill_cleartimerskill (struct block_list *src) return 1; } static int skill_active_reverberation(struct block_list *bl, va_list ap) { - struct skill_unit *su = (TBL_SKILL*)bl; - struct skill_unit_group *sg = NULL; + skill_unit *su = (skill_unit*)bl; nullpo_ret(su); if (bl->type != BL_SKILL) return 0; - if (su->alive && (sg = su->group) && sg->skill_id == NPC_REVERBERATION) { + + std::shared_ptr sg = su->group; + + if (su->alive && sg && sg->skill_id == NPC_REVERBERATION) { map_foreachinallrange(skill_trap_splash, bl, skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, bl, gettick()); su->limit = DIFF_TICK(gettick(), sg->tick); sg->unit_id = UNT_USED_TRAPS; @@ -5876,8 +5875,8 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint if (tsc && tsc->data[SC__SHADOWFORM] && rnd() % 100 < 100 - tsc->data[SC__SHADOWFORM]->val1 * 10) // [100 - (Skill Level x 10)] % status_change_end(bl, SC__SHADOWFORM, INVALID_TIMER); // Should only end, no damage dealt. } else { - struct skill_unit *su = NULL; - struct skill_unit_group* sg; + skill_unit *su = BL_CAST(BL_SKILL, bl); + std::shared_ptr sg; if (su && (sg = su->group) && skill_get_inf2(sg->skill_id, INF2_ISTRAP)) { if( !(sg->unit_id == UNT_USED_TRAPS || (sg->unit_id == UNT_ANKLESNARE && sg->val2 != 0 )) ) @@ -8965,12 +8964,10 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui case MA_REMOVETRAP: case HT_REMOVETRAP: { - struct skill_unit* su; - struct skill_unit_group* sg = NULL; + skill_unit* su = BL_CAST(BL_SKILL, bl); + std::shared_ptr sg; std::shared_ptr skill_group; - su = BL_CAST(BL_SKILL, bl); - // Mercenaries can remove any trap // Players can only remove their own traps or traps on Vs maps. if( su && (sg = su->group) && (src->type == BL_MER || sg->src_id == src->id || map_flag_vs(bl->m)) && ( skill_group = skill_db.find(sg->skill_id) ) && skill_group->inf2[INF2_ISTRAP] ) @@ -12104,7 +12101,7 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui struct map_session_data* sd; struct status_change* sc; struct status_change_entry *sce; - struct skill_unit_group* sg; + std::shared_ptr sg; enum sc_type type; int i; @@ -12186,7 +12183,7 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui case SA_DELUGE: case SA_VIOLENTGALE: { //Does not consumes if the skill is already active. [Skotlex] - struct skill_unit_group *sg2; + std::shared_ptr sg2; if ((sg2= skill_locate_element_field(src)) != NULL && ( sg2->skill_id == SA_VOLCANO || sg2->skill_id == SA_DELUGE || sg2->skill_id == SA_VIOLENTGALE )) { if (sg2->limit - DIFF_TICK(gettick(), sg2->tick) > 0) @@ -12715,9 +12712,9 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui break; case SC_FEINTBOMB: { - struct skill_unit_group *group = skill_unitsetting(src,skill_id,skill_lv,x,y,0); // Set bomb on current Position + std::shared_ptr group = skill_unitsetting(src,skill_id,skill_lv,x,y,0); // Set bomb on current Position - if( group == NULL || group->unit == NULL ) { + if( group == nullptr || group->unit == nullptr ) { clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); return 1; } @@ -12770,16 +12767,15 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui } break; case GN_FIRE_EXPANSION: { - int i_su; struct unit_data *ud = unit_bl2ud(src); if( !ud ) break; - for(i_su = 0; i_su < MAX_SKILLUNITGROUP && ud->skillunit[i_su]; i_su++) { - struct skill_unit *su = ud->skillunit[i_su]->unit; - struct skill_unit_group *sg = ud->skillunit[i_su]->unit->group; + for (const auto itsu : ud->skillunits) { + skill_unit *su = itsu->unit; + std::shared_ptr sg = itsu->unit->group; - if (ud->skillunit[i_su]->skill_id == GN_DEMONIC_FIRE && distance_xy(x, y, su->bl.x, su->bl.y) < 4) { + if (itsu->skill_id == GN_DEMONIC_FIRE && distance_xy(x, y, su->bl.x, su->bl.y) < 4) { switch (skill_lv) { case 1: { // TODO: @@ -12987,7 +12983,7 @@ int skill_castend_map (struct map_session_data *sd, uint16 skill_id, const char case AL_WARP: { const struct point *p[4]; - struct skill_unit_group *group; + std::shared_ptr group; int i, lv, wx, wy; int maxcount=0; int x,y; @@ -13005,11 +13001,9 @@ int skill_castend_map (struct map_session_data *sd, uint16 skill_id, const char p[3] = &sd->status.memo_point[2]; if((maxcount = skill_get_maxcount(skill_id, sd->menuskill_val)) > 0) { - for(i=0;iud.skillunit[i] && maxcount;i++) { - if(sd->ud.skillunit[i]->skill_id == skill_id) - maxcount--; - } - if(!maxcount) { + unit_skillunit_maxcount(sd->ud, skill_id, maxcount); + + if (maxcount == 0) { clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); skill_failed(sd); return 0; @@ -13042,7 +13036,7 @@ int skill_castend_map (struct map_session_data *sd, uint16 skill_id, const char skill_consume_requirement(sd,sd->menuskill_id,lv,2); sd->skillitem = sd->skillitemlv = sd->skillitem_keep_requirement = 0; // Clear data that's skipped in 'skill_castend_pos' [Inkfish] - if((group=skill_unitsetting(&sd->bl,skill_id,lv,wx,wy,0))==NULL) { + if((group=skill_unitsetting(&sd->bl,skill_id,lv,wx,wy,0))==nullptr) { skill_failed(sd); return 0; } @@ -13116,10 +13110,10 @@ int skill_dance_overlap(struct skill_unit* unit, int flag) static bool skill_dance_switch(struct skill_unit* unit, int flag) { static int prevflag = 1; // by default the backup is empty - static struct skill_unit_group backup; - struct skill_unit_group* group; + static s_skill_unit_group backup; + std::shared_ptr group; - if( unit == NULL || (group = unit->group) == NULL ) + if( unit == nullptr || (group = unit->group) == nullptr ) return false; //val2&(1 << UF_ENSEMBLE) is a hack to indicate dissonance @@ -13174,11 +13168,11 @@ static bool skill_dance_switch(struct skill_unit* unit, int flag) * @param y Position y * @param flag &1: Used to determine when the skill 'morphs' (Warp portal becomes active, or Fire Pillar becomes active) * xx_METEOR: flag &1 contains if the unit can cause curse, flag is also the duration of the unit in milliseconds - * @return skill_unit_group + * @return s_skill_unit_group */ -struct skill_unit_group *skill_unitsetting(struct block_list *src, uint16 skill_id, uint16 skill_lv, int16 x, int16 y, int flag) +std::shared_ptr skill_unitsetting(struct block_list *src, uint16 skill_id, uint16 skill_lv, int16 x, int16 y, int flag) { - struct skill_unit_group *group; + std::shared_ptr group; int i, val1 = 0, val2 = 0, val3 = 0; t_tick limit; int link_group_id = 0; @@ -13193,7 +13187,7 @@ struct skill_unit_group *skill_unitsetting(struct block_list *src, uint16 skill_ bool hidden = false; struct map_data *mapdata; - nullpo_retr(NULL, src); + nullpo_retr(nullptr, src); std::shared_ptr skill = skill_db.find(skill_id); @@ -13232,10 +13226,10 @@ struct skill_unit_group *skill_unitsetting(struct block_list *src, uint16 skill_ limit=2000; else // previous implementation (not used anymore) { //Warp Portal morphing to active mode, extract relevant data from src. [Skotlex] - if( src->type != BL_SKILL ) return NULL; + if( src->type != BL_SKILL ) return nullptr; group = ((TBL_SKILL*)src)->group; src = map_id2bl(group->src_id); - if( !src ) return NULL; + if( !src ) return nullptr; val2 = group->val2; //Copy the (x,y) position you warp to val3 = group->val3; //as well as the mapindex to warp to. } @@ -13258,7 +13252,7 @@ struct skill_unit_group *skill_unitsetting(struct block_list *src, uint16 skill_ break; case WZ_FIREPILLAR: if( map_getcell(src->m, x, y, CELL_CHKLANDPROTECTOR) ) - return NULL; + return nullptr; if((flag&1)!=0) limit=1000; val1=skill_lv+2; @@ -13311,8 +13305,9 @@ struct skill_unit_group *skill_unitsetting(struct block_list *src, uint16 skill_ case SA_VIOLENTGALE: case SC_CHAOSPANIC: { - struct skill_unit_group *old_sg; - if ((old_sg = skill_locate_element_field(src)) != NULL) + std::shared_ptr old_sg = skill_locate_element_field(src); + + if (old_sg != nullptr) { //HelloKitty confirmed that these are interchangeable, //so you can change element and not consume gemstones. if (( @@ -13460,7 +13455,7 @@ struct skill_unit_group *skill_unitsetting(struct block_list *src, uint16 skill_ } case GC_POISONSMOKE: if( !(sc && sc->data[SC_POISONINGWEAPON]) ) - return NULL; + return nullptr; val2 = sc->data[SC_POISONINGWEAPON]->val2; // Type of Poison val3 = sc->data[SC_POISONINGWEAPON]->val1; limit = skill_get_time(skill_id, skill_lv); @@ -13490,7 +13485,7 @@ struct skill_unit_group *skill_unitsetting(struct block_list *src, uint16 skill_ case SO_WIND_INSIGNIA: case SO_EARTH_INSIGNIA: if( map_getcell(src->m, x, y, CELL_CHKLANDPROTECTOR) ) - return NULL; + return nullptr; break; case SO_CLOUD_KILL: case NPC_CLOUD_KILL: @@ -13551,7 +13546,11 @@ struct skill_unit_group *skill_unitsetting(struct block_list *src, uint16 skill_ } // Init skill unit group - nullpo_retr(NULL, (group = skill_initunitgroup(src,layout->count,skill_id,skill_lv,(flag & 1 ? skill->unit_id2 : skill->unit_id)+subunt, limit, interval))); + group = skill_initunitgroup(src, layout->count, skill_id, skill_lv, (flag & 1 ? skill->unit_id2 : skill->unit_id) + subunt, limit, interval); + + if (group == nullptr) + return nullptr; + group->val1 = val1; group->val2 = val2; group->val3 = val3; @@ -13687,7 +13686,7 @@ struct skill_unit_group *skill_unitsetting(struct block_list *src, uint16 skill_ if( !alive ) continue; - nullpo_retr(NULL, (unit = skill_initunit(group,i,ux,uy,unit_val1,unit_val2,hidden))); + nullpo_retr(nullptr, (unit = skill_initunit(group,i,ux,uy,unit_val1,unit_val2,hidden))); unit->limit = limit; unit->range = range; @@ -13705,7 +13704,7 @@ struct skill_unit_group *skill_unitsetting(struct block_list *src, uint16 skill_ if (!group->alive_count) { //No cells? Something that was blocked completely by Land Protector? skill_delunitgroup(group); - return NULL; + return nullptr; } //success, unit created. @@ -13736,7 +13735,7 @@ void ext_skill_unit_onplace(struct skill_unit *unit, struct block_list *bl, t_ti */ static int skill_unit_onplace(struct skill_unit *unit, struct block_list *bl, t_tick tick) { - struct skill_unit_group *sg; + struct block_list *ss; // Actual source that cast the skill unit struct status_change *sc; struct status_change_entry *sce; @@ -13750,7 +13749,11 @@ static int skill_unit_onplace(struct skill_unit *unit, struct block_list *bl, t_ if(bl->prev == NULL || !unit->alive || status_isdead(bl)) return 0; - nullpo_ret(sg = unit->group); + std::shared_ptr sg = unit->group; + + if (sg == nullptr) + return 0; + nullpo_ret(ss = map_id2bl(sg->src_id)); tstatus = status_get_status_data(bl); @@ -14078,7 +14081,6 @@ static int skill_unit_onplace(struct skill_unit *unit, struct block_list *bl, t_ */ int skill_unit_onplace_timer(struct skill_unit *unit, struct block_list *bl, t_tick tick) { - struct skill_unit_group *sg; struct block_list *ss; TBL_PC* tsd; struct status_data *tstatus; @@ -14094,7 +14096,11 @@ int skill_unit_onplace_timer(struct skill_unit *unit, struct block_list *bl, t_t if (bl->prev == NULL || !unit->alive || status_isdead(bl)) return 0; - nullpo_ret(sg = unit->group); + std::shared_ptr sg = unit->group; + + if (sg == nullptr) + return 0; + nullpo_ret(ss = map_id2bl(sg->src_id)); tsd = BL_CAST(BL_PC, bl); @@ -14800,14 +14806,18 @@ int skill_unit_onplace_timer(struct skill_unit *unit, struct block_list *bl, t_t */ int skill_unit_onout(struct skill_unit *src, struct block_list *bl, t_tick tick) { - struct skill_unit_group *sg; struct status_change *sc; struct status_change_entry *sce; enum sc_type type; nullpo_ret(src); nullpo_ret(bl); - nullpo_ret(sg=src->group); + + std::shared_ptr sg = src->group; + + if (sg == nullptr) + return 0; + sc = status_get_sc(bl); type = status_skill2sc(sg->skill_id); sce = (sc && type != -1)?sc->data[type]:NULL; @@ -15008,7 +15018,6 @@ int skill_unit_onleft(uint16 skill_id, struct block_list *bl, t_tick tick) static int skill_unit_effect(struct block_list* bl, va_list ap) { struct skill_unit* unit = va_arg(ap,struct skill_unit*); - struct skill_unit_group* group = unit->group; t_tick tick = va_arg(ap,t_tick); unsigned int flag = va_arg(ap,unsigned int); uint16 skill_id; @@ -15018,7 +15027,10 @@ static int skill_unit_effect(struct block_list* bl, va_list ap) if( (!unit->alive && !(flag&4)) || bl->prev == NULL ) return 0; - nullpo_ret(group); + std::shared_ptr group = unit->group; + + if (group == nullptr) + return 0; if( !(flag&8) ) { dissonance = skill_dance_switch(unit, 0); @@ -15058,10 +15070,12 @@ static int skill_unit_effect(struct block_list* bl, va_list ap) */ int64 skill_unit_ondamaged(struct skill_unit *unit, int64 damage) { - struct skill_unit_group *sg; - nullpo_ret(unit); - nullpo_ret(sg = unit->group); + + std::shared_ptr sg = unit->group; + + if (sg == nullptr) + return 0; switch( sg->unit_id ) { case UNT_BLASTMINE: @@ -17848,18 +17862,17 @@ int skill_attack_area(struct block_list *bl, va_list ap) */ int skill_clear_group(struct block_list *bl, int flag) { - struct unit_data *ud = NULL; - struct skill_unit_group *group[MAX_SKILLUNITGROUP]; - int i, count = 0; - nullpo_ret(bl); - if (!(ud = unit_bl2ud(bl))) + unit_data *ud = unit_bl2ud(bl); + + if (ud == nullptr) return 0; - // All groups to be deleted are first stored on an array since the array elements shift around when you delete them. [Skotlex] - for (i = 0; i < MAX_SKILLUNITGROUP && ud->skillunit[i]; i++) { - switch (ud->skillunit[i]->skill_id) { + size_t count = 0; + + for (auto it = ud->skillunits.begin(); it != ud->skillunits.end(); it++) { + switch ((*it)->skill_id) { case SA_DELUGE: case SA_VOLCANO: case SA_VIOLENTGALE: @@ -17869,47 +17882,52 @@ int skill_clear_group(struct block_list *bl, int flag) case SC_CHAOSPANIC: case MH_POISON_MIST: case MH_LAVA_SLIDE: - if (flag&1) - group[count++] = ud->skillunit[i]; + if (flag & 1) { + skill_delunitgroup(*it); + count++; + } break; case SO_CLOUD_KILL: case NPC_CLOUD_KILL: - if( flag&4 ) - group[count++] = ud->skillunit[i]; + if (flag & 4) { + skill_delunitgroup(*it); + count++; + } break; case SO_WARMER: - if( flag&8 ) - group[count++] = ud->skillunit[i]; + if (flag & 8) { + skill_delunitgroup(*it); + count++; + } break; default: - if (flag&2 && skill_get_inf2(ud->skillunit[i]->skill_id, INF2_ISTRAP)) - group[count++] = ud->skillunit[i]; + if (flag & 2 && skill_get_inf2((*it)->skill_id, INF2_ISTRAP)) { + skill_delunitgroup(*it); + count++; + } break; } - } - for (i = 0; i < count; i++) - skill_delunitgroup(group[i]); - return count; + + return static_cast(count); } /** * Returns the first element field found [Skotlex] * @param bl - * @return skill_unit_group + * @return s_skill_unit_group */ -struct skill_unit_group *skill_locate_element_field(struct block_list *bl) +std::shared_ptr skill_locate_element_field(struct block_list *bl) { - struct unit_data *ud = NULL; - int i; - nullpo_ret(bl); - if (!(ud = unit_bl2ud(bl))) - return NULL; + unit_data *ud = unit_bl2ud(bl); - for (i = 0; i < MAX_SKILLUNITGROUP && ud->skillunit[i]; i++) { - switch (ud->skillunit[i]->skill_id) { + if (ud == nullptr) + return nullptr; + + for (const auto su : ud->skillunits) { + switch (su->skill_id) { case SA_DELUGE: case SA_VOLCANO: case SA_VIOLENTGALE: @@ -17921,10 +17939,10 @@ struct skill_unit_group *skill_locate_element_field(struct block_list *bl) case SC_CHAOSPANIC: case MH_POISON_MIST: case MH_LAVA_SLIDE: - return ud->skillunit[i]; + return su; } } - return NULL; + return nullptr; } /// Graffiti cleaner [Valaris] @@ -17977,7 +17995,7 @@ int skill_detonator(struct block_list *bl, va_list ap) if (unit == nullptr) return 0; - skill_unit_group *group = unit->group; + std::shared_ptr group = unit->group; if (group == nullptr || group->src_id != src->id) return 0; @@ -18198,7 +18216,6 @@ static int skill_trap_splash(struct block_list *bl, va_list ap) struct block_list *src = va_arg(ap,struct block_list *); struct skill_unit *unit = NULL; t_tick tick = va_arg(ap,t_tick); - struct skill_unit_group *sg; struct block_list *ss; //Skill src bl nullpo_ret(src); @@ -18208,7 +18225,11 @@ static int skill_trap_splash(struct block_list *bl, va_list ap) if (!unit || !unit->alive || bl->prev == NULL) return 0; - nullpo_ret(sg = unit->group); + std::shared_ptr sg = unit->group; + + if (sg == nullptr) + return 0; + nullpo_ret(ss = map_id2bl(sg->src_id)); if (battle_check_target(src,bl,sg->target_flag) <= 0) @@ -18574,13 +18595,15 @@ void skill_getareachar_skillunit_visibilty_single(struct skill_unit *su, struct * @param val1 * @param val2 */ -struct skill_unit *skill_initunit(struct skill_unit_group *group, int idx, int x, int y, int val1, int val2, bool hidden) +struct skill_unit *skill_initunit(std::shared_ptr group, int idx, int x, int y, int val1, int val2, bool hidden) { - struct skill_unit *unit; + if (group == nullptr || group->unit == nullptr) + return nullptr; - nullpo_retr(NULL, group); - nullpo_retr(NULL, group->unit); // crash-protection against poor coding - nullpo_retr(NULL, (unit = &group->unit[idx])); + skill_unit *unit = &group->unit[idx]; + + if (unit == nullptr) + return nullptr; if( map_getcell(map_id2bl(group->src_id)->m, x, y, CELL_CHKMAELSTROM) ) return unit; @@ -18639,8 +18662,6 @@ struct skill_unit *skill_initunit(struct skill_unit_group *group, int idx, int x */ int skill_delunit(struct skill_unit* unit) { - struct skill_unit_group *group; - nullpo_ret(unit); if( !unit->alive ) @@ -18648,7 +18669,10 @@ int skill_delunit(struct skill_unit* unit) unit->alive = 0; - nullpo_ret(group = unit->group); + std::shared_ptr group = unit->group; + + if (group == nullptr) + return 0; if( group->state.song_dance&0x1 ) //Cancel dissonance effect. skill_dance_overlap(unit, 0); @@ -18714,11 +18738,11 @@ int skill_delunit(struct skill_unit* unit) } -static DBMap* skillunit_group_db = NULL; /// Skill unit group DB. Key int group_id -> struct skill_unit_group* +static std::unordered_map> skillunit_group_db; /// Skill unit group DB. Key int group_id -> struct s_skill_unit_group* -/// Returns the target skill_unit_group or NULL if not found. -struct skill_unit_group* skill_id2group(int group_id) { - return (struct skill_unit_group*)idb_get(skillunit_group_db, group_id); +/// Returns the target s_skill_unit_group or nullptr if not found. +std::shared_ptr skill_id2group(int group_id) { + return util::umap_find(skillunit_group_db, group_id); } static int skill_unit_group_newid = MAX_SKILL; /// Skill Unit Group ID @@ -18755,46 +18779,28 @@ static int skill_get_new_group_id(void) * @param unit_id Unit ID (see skill.hpp::e_skill_unit_id) * @param limit Lifetime for skill unit, uses skill_get_time(skill_id, skill_lv) * @param interval Time interval - * @return skill_unit_group + * @return s_skill_unit_group */ -struct skill_unit_group* skill_initunitgroup(struct block_list* src, int count, uint16 skill_id, uint16 skill_lv, int unit_id, t_tick limit, int interval) +std::shared_ptr skill_initunitgroup(struct block_list* src, int count, uint16 skill_id, uint16 skill_lv, int unit_id, t_tick limit, int interval) { - struct unit_data* ud = unit_bl2ud( src ); - struct skill_unit_group* group; - int i; + nullpo_retr(nullptr, src); - if(!(skill_id && skill_lv)) return 0; + unit_data *ud = unit_bl2ud(src); - nullpo_retr(NULL, src); - nullpo_retr(NULL, ud); + nullpo_retr(nullptr, ud); - // Find a free spot to store the new unit group - // TODO: Make this flexible maybe by changing this fixed array? - ARR_FIND( 0, MAX_SKILLUNITGROUP, i, ud->skillunit[i] == NULL ); - if(i == MAX_SKILLUNITGROUP) { - // Array is full, make room by discarding oldest group - int j = 0; - t_tick maxdiff = 0, tick = gettick(); - for(i = 0; i < MAX_SKILLUNITGROUP && ud->skillunit[i];i++){ - t_tick x = DIFF_TICK(tick,ud->skillunit[i]->tick); - if(x > maxdiff){ - maxdiff = x; - j = i; - } - } - skill_delunitgroup(ud->skillunit[j]); - // Since elements must have shifted, we use the last slot. - i = MAX_SKILLUNITGROUP-1; - } + if (skill_id == 0 || skill_lv == 0) + return 0; + + auto group = std::make_shared(); - group = ers_alloc(skill_unit_ers, struct skill_unit_group); group->src_id = src->id; group->party_id = status_get_party_id(src); group->guild_id = status_get_guild_id(src); group->bg_id = bg_team_get_id(src); group->group_id = skill_get_new_group_id(); group->link_group_id = 0; - group->unit = (struct skill_unit *)aCalloc(count,sizeof(struct skill_unit)); + group->unit = (skill_unit *)aCalloc(count, sizeof(skill_unit)); group->unit_count = count; group->alive_count = 0; group->val1 = 0; @@ -18807,12 +18813,13 @@ struct skill_unit_group* skill_initunitgroup(struct block_list* src, int count, group->limit = limit; group->interval = interval; group->tick = gettick(); - group->valstr = NULL; + group->valstr = nullptr; - ud->skillunit[i] = group; + ud->skillunits.push_back(group); + + // Stores this new group + skillunit_group_db.insert({ group->group_id, group }); - // Stores this new group to DBMap - idb_put(skillunit_group_db, group->group_id, group); return group; } @@ -18823,14 +18830,14 @@ struct skill_unit_group* skill_initunitgroup(struct block_list* src, int count, * @param line * @param *func */ -int skill_delunitgroup_(struct skill_unit_group *group, const char* file, int line, const char* func) +int skill_delunitgroup_(std::shared_ptr group, const char* file, int line, const char* func) { struct block_list* src; struct unit_data *ud; - short i, j; + short i; int link_group_id; - if( group == NULL ) { + if( group == nullptr ) { ShowDebug("skill_delunitgroup: group is NULL (source=%s:%d, %s)! Please report this! (#3504)\n", file, line, func); return 0; } @@ -18962,28 +18969,14 @@ int skill_delunitgroup_(struct skill_unit_group *group, const char* file, int li group->valstr = NULL; } - idb_remove(skillunit_group_db, group->group_id); - map_freeblock(&group->unit->bl); // schedules deallocation of whole array (HACK) - group->unit = NULL; - group->group_id = 0; - group->unit_count = 0; - link_group_id = group->link_group_id; - group->link_group_id = 0; - // locate this group, swap with the last entry and delete it - ARR_FIND( 0, MAX_SKILLUNITGROUP, i, ud->skillunit[i] == group ); - ARR_FIND( i, MAX_SKILLUNITGROUP, j, ud->skillunit[j] == NULL ); - j--; - if( i < MAX_SKILLUNITGROUP ) { - ud->skillunit[i] = ud->skillunit[j]; - ud->skillunit[j] = NULL; - ers_free(skill_unit_ers, group); - } else + if (skillunit_group_db.erase(group->group_id) != 1) ShowError("skill_delunitgroup: Group not found! (src_id: %d skill_id: %d)\n", group->src_id, group->skill_id); if(link_group_id) { - struct skill_unit_group* group_cur = skill_id2group(link_group_id); + std::shared_ptr group_cur = skill_id2group(link_group_id); + if(group_cur) skill_delunitgroup(group_cur); } @@ -18997,13 +18990,15 @@ int skill_delunitgroup_(struct skill_unit_group *group, const char* file, int li */ void skill_clear_unitgroup(struct block_list *src) { - struct unit_data *ud; - nullpo_retv(src); - nullpo_retv((ud = unit_bl2ud(src))); - while (ud->skillunit[0]) - skill_delunitgroup(ud->skillunit[0]); + unit_data *ud = unit_bl2ud(src); + + nullpo_retv(ud); + + for (auto it = ud->skillunits.begin(); it != ud->skillunits.end(); it++) { + skill_delunitgroup(*it); + } } /** @@ -19013,7 +19008,7 @@ void skill_clear_unitgroup(struct block_list *src) * @param tick * @return skill_unit_group_tickset if found */ -struct skill_unit_group_tickset *skill_unitgrouptickset_search(struct block_list *bl, struct skill_unit_group *group, t_tick tick) +struct skill_unit_group_tickset *skill_unitgrouptickset_search(struct block_list *bl, std::shared_ptr group, t_tick tick) { int i, j = -1, s, id; struct unit_data *ud; @@ -19059,7 +19054,6 @@ struct skill_unit_group_tickset *skill_unitgrouptickset_search(struct block_list int skill_unit_timer_sub_onplace(struct block_list* bl, va_list ap) { struct skill_unit* unit = va_arg(ap,struct skill_unit *); - struct skill_unit_group* group = NULL; t_tick tick = va_arg(ap,t_tick); nullpo_ret(unit); @@ -19067,7 +19061,10 @@ int skill_unit_timer_sub_onplace(struct block_list* bl, va_list ap) if( !unit->alive || bl->prev == NULL ) return 0; - nullpo_ret(group = unit->group); + std::shared_ptr group = unit->group; + + if (group == nullptr) + return 0; std::shared_ptr skill = skill_db.find(group->skill_id); @@ -19088,7 +19085,6 @@ int skill_unit_timer_sub_onplace(struct block_list* bl, va_list ap) static int skill_unit_timer_sub(DBKey key, DBData *data, va_list ap) { struct skill_unit* unit = (struct skill_unit*)db_data2ptr(data); - struct skill_unit_group* group = NULL; t_tick tick = va_arg(ap,t_tick); bool dissonance; struct block_list* bl = &unit->bl; @@ -19098,7 +19094,10 @@ static int skill_unit_timer_sub(DBKey key, DBData *data, va_list ap) if( !unit->alive ) return 0; - nullpo_ret(group = unit->group); + std::shared_ptr group = unit->group; + + if (group == nullptr) + return 0; // Check for expiration if( !group->state.guildaura && (DIFF_TICK(tick,group->tick) >= group->limit || DIFF_TICK(tick,group->tick) >= unit->limit) ) @@ -19370,7 +19369,6 @@ static int skill_unit_temp[20]; // temporary storage for tracking skill unit sk int skill_unit_move_sub(struct block_list* bl, va_list ap) { struct skill_unit* unit = (struct skill_unit *)bl; - struct skill_unit_group* group = NULL; struct block_list* target = va_arg(ap,struct block_list*); t_tick tick = va_arg(ap,t_tick); @@ -19385,7 +19383,10 @@ int skill_unit_move_sub(struct block_list* bl, va_list ap) if( !unit->alive || target->prev == NULL ) return 0; - nullpo_ret(group = unit->group); + std::shared_ptr group = unit->group; + + if (group == nullptr) + return 0; if( flag&1 && ( group->skill_id == PF_SPIDERWEB || group->skill_id == GN_THORNS_TRAP ) ) return 0; // Fiberlock is never supposed to trigger on skill_unit_move. [Inkfish] @@ -19536,7 +19537,7 @@ void skill_unit_move_unit(struct block_list *bl, int dx, int dy) { * @param dx * @param dy */ -void skill_unit_move_unit_group(struct skill_unit_group *group, int16 m, int16 dx, int16 dy) +void skill_unit_move_unit_group(std::shared_ptr group, int16 m, int16 dx, int16 dy) { int i, j; t_tick tick = gettick(); @@ -20727,13 +20728,12 @@ int skill_changematerial(struct map_session_data *sd, int n, unsigned short *ite */ static int skill_destroy_trap(struct block_list *bl, va_list ap) { - struct skill_unit *su = (struct skill_unit *)bl; - struct skill_unit_group *sg = NULL; - t_tick tick; + skill_unit *su = (struct skill_unit *)bl; nullpo_ret(su); - tick = va_arg(ap, t_tick); + std::shared_ptr sg; + t_tick tick = va_arg(ap, t_tick); if (su->alive && (sg = su->group) && skill_get_inf2(sg->skill_id, INF2_ISTRAP)) { switch( sg->unit_id ) { @@ -20934,8 +20934,8 @@ void skill_usave_add(struct map_session_data *sd, uint16 skill_id, uint16 skill_ */ void skill_usave_trigger(struct map_session_data *sd) { - struct skill_usave *sus = NULL; - struct skill_unit_group *group = NULL; + skill_usave *sus; + std::shared_ptr group; if (!(sus = static_cast(idb_get(skillusave_db,sd->status.char_id)))) return; @@ -23074,14 +23074,11 @@ void do_init_skill(void) { skill_readdb(); - skillunit_group_db = idb_alloc(DB_OPT_BASE); skillunit_db = idb_alloc(DB_OPT_BASE); skillusave_db = idb_alloc(DB_OPT_RELEASE_DATA); bowling_db = idb_alloc(DB_OPT_BASE); - skill_unit_ers = ers_new(sizeof(struct skill_unit_group),"skill.cpp::skill_unit_ers",ERS_CACHE_OPTIONS); skill_timer_ers = ers_new(sizeof(struct skill_timerskill),"skill.cpp::skill_timer_ers",ERS_CACHE_OPTIONS); - ers_chunk_size(skill_unit_ers, 150); ers_chunk_size(skill_timer_ers, 150); add_timer_func_list(skill_unit_timer,"skill_unit_timer"); @@ -23102,10 +23099,8 @@ void do_final_skill(void) reading_spellbook_db.clear(); skill_arrow_db.clear(); - db_destroy(skillunit_group_db); db_destroy(skillunit_db); db_destroy(skillusave_db); db_destroy(bowling_db); - ers_destroy(skill_unit_ers); ers_destroy(skill_timer_ers); } diff --git a/src/map/skill.hpp b/src/map/skill.hpp index bd5d91d775..969d7abb5d 100644 --- a/src/map/skill.hpp +++ b/src/map/skill.hpp @@ -24,7 +24,7 @@ enum e_battle_check_target : uint32; struct map_session_data; struct homun_data; struct skill_unit; -struct skill_unit_group; +struct s_skill_unit_group; struct status_change_entry; #define MAX_SKILL_PRODUCE_DB 282 /// Max Produce DB @@ -340,9 +340,19 @@ struct skill_timerskill { int flag; }; -#define MAX_SKILLUNITGROUP 25 /// Maximum skill unit group (for same skill each source) +/// Skill unit +struct skill_unit { + struct block_list bl; + std::shared_ptr group; /// Skill group reference + t_tick limit; + int val1, val2; + short range; + bool alive; + bool hidden; +}; + /// Skill unit group -struct skill_unit_group { +struct s_skill_unit_group { int src_id; /// Caster ID/RID, if player is account_id int party_id; /// Party ID int guild_id; /// Guild ID @@ -369,17 +379,11 @@ struct skill_unit_group { unsigned song_dance : 2; //0x1 Song/Dance, 0x2 Ensemble unsigned guildaura : 1; // Guild Aura } state; -}; -/// Skill unit -struct skill_unit { - struct block_list bl; - struct skill_unit_group *group; /// Skill group reference - t_tick limit; - int val1, val2; - short range; - unsigned alive : 1; - unsigned hidden : 1; + ~s_skill_unit_group() { + if (this->unit) + map_freeblock(&this->unit->bl); // schedules deallocation of whole array (HACK) + } }; #define MAX_SKILLUNITGROUPTICKSET 25 @@ -556,12 +560,12 @@ short skill_blown(struct block_list* src, struct block_list* target, char count, int skill_break_equip(struct block_list *src,struct block_list *bl, unsigned short where, int rate, int flag); int skill_strip_equip(struct block_list *src,struct block_list *bl, unsigned short where, int rate, int lv, int time); // Skills unit -struct skill_unit_group *skill_id2group(int group_id); -struct skill_unit_group *skill_unitsetting(struct block_list* src, uint16 skill_id, uint16 skill_lv, int16 x, int16 y, int flag); -struct skill_unit *skill_initunit (struct skill_unit_group *group, int idx, int x, int y, int val1, int val2, bool hidden); +std::shared_ptr skill_id2group(int group_id); +std::shared_ptr skill_unitsetting(struct block_list* src, uint16 skill_id, uint16 skill_lv, int16 x, int16 y, int flag); +struct skill_unit *skill_initunit (std::shared_ptr group, int idx, int x, int y, int val1, int val2, bool hidden); int skill_delunit(struct skill_unit *unit); -struct skill_unit_group *skill_initunitgroup(struct block_list* src, int count, uint16 skill_id, uint16 skill_lv, int unit_id, t_tick limit, int interval); -int skill_delunitgroup_(struct skill_unit_group *group, const char* file, int line, const char* func); +std::shared_ptr skill_initunitgroup(struct block_list* src, int count, uint16 skill_id, uint16 skill_lv, int unit_id, t_tick limit, int interval); +int skill_delunitgroup_(std::shared_ptr group, const char* file, int line, const char* func); #define skill_delunitgroup(group) skill_delunitgroup_(group,__FILE__,__LINE__,__func__) void skill_clear_unitgroup(struct block_list *src); int skill_clear_group(struct block_list *bl, int flag); @@ -591,7 +595,7 @@ bool skill_pos_maxcount_check(struct block_list *src, int16 x, int16 y, uint16 s int skill_check_pc_partner(struct map_session_data *sd, uint16 skill_id, uint16 *skill_lv, int range, int cast_flag); int skill_unit_move(struct block_list *bl,t_tick tick,int flag); -void skill_unit_move_unit_group( struct skill_unit_group *group, int16 m,int16 dx,int16 dy); +void skill_unit_move_unit_group( std::shared_ptr group, int16 m,int16 dx,int16 dy); void skill_unit_move_unit(struct block_list *bl, int dx, int dy); int skill_sit(struct map_session_data *sd, bool sitting); diff --git a/src/map/status.cpp b/src/map/status.cpp index 8353af52c8..9279ac046d 100644 --- a/src/map/status.cpp +++ b/src/map/status.cpp @@ -2356,7 +2356,8 @@ int status_damage(struct block_list *src,struct block_list *target,int64 dhp, in } #ifndef RENEWAL if ((sce=sc->data[SC_GRAVITATION]) && sce->val3 == BCT_SELF) { - struct skill_unit_group* sg = skill_id2group(sce->val4); + std::shared_ptr sg = skill_id2group(sce->val4); + if (sg) { skill_delunitgroup(sg); sce->val4 = 0; @@ -13422,7 +13423,7 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const return 0; if (type == SC_SPIDERWEB) { //Delete the unit group first to expire found in the status change - struct skill_unit_group *group = NULL, *group2 = NULL; + std::shared_ptr group, group2; t_tick tick = gettick(); int pos = 1; if (sce->val2) @@ -13620,16 +13621,11 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const } if(sce->val2) { // Erase associated land skill - struct skill_unit_group *group; - group = skill_id2group(sce->val2); - if( group == NULL ) { - ShowDebug("status_change_end: SC_DANCING is missing skill unit group (val1=%d, val2=%d, val3=%d, val4=%d, timer=%d, tid=%d, char_id=%d, map=%s, x=%d, y=%d). Please report this!\n", - sce->val1, sce->val2, sce->val3, sce->val4, sce->timer, tid, - sd ? sd->status.char_id : 0, - mapindex_id2name(map_id2index(bl->m)), bl->x, bl->y); - } + std::shared_ptr group = skill_id2group(sce->val2); + sce->val2 = 0; - skill_delunitgroup(group); + if (group) + skill_delunitgroup(group); } if((sce->val1&0xFFFF) == CG_MOONLIT) @@ -13712,7 +13708,8 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const break; case SC_GOSPEL: if (sce->val3) { // Clear the group. - struct skill_unit_group* group = skill_id2group(sce->val3); + std::shared_ptr group = skill_id2group(sce->val3); + sce->val3 = 0; if (group) skill_delunitgroup(group); @@ -13725,7 +13722,8 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const break; case SC_BASILICA: // Clear the skill area. [Skotlex] if (sce->val3 && sce->val4 == bl->id) { - struct skill_unit_group* group = skill_id2group(sce->val3); + std::shared_ptr group = skill_id2group(sce->val3); + sce->val3 = 0; if (group) skill_delunitgroup(group); @@ -13739,7 +13737,8 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const case SC__MANHOLE: case SC_BANDING: if (sce->val4) { // Clear the group. - struct skill_unit_group* group = skill_id2group(sce->val4); + std::shared_ptr group = skill_id2group(sce->val4); + sce->val4 = 0; if( group ) // Might have been cleared before status ended, e.g. land protector skill_delunitgroup(group); @@ -13825,7 +13824,8 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const case SC_NEUTRALBARRIER_MASTER: case SC_STEALTHFIELD_MASTER: if( sce->val2 ) { - struct skill_unit_group* group = skill_id2group(sce->val2); + std::shared_ptr group = skill_id2group(sce->val2); + sce->val2 = 0; if( group ) // Might have been cleared before status ended, e.g. land protector skill_delunitgroup(group); diff --git a/src/map/unit.cpp b/src/map/unit.cpp index ae3539e93d..98cd5e7c43 100644 --- a/src/map/unit.cpp +++ b/src/map/unit.cpp @@ -2910,6 +2910,19 @@ void unit_dataset(struct block_list *bl) ud->canmove_tick = gettick(); } +/** + * Returns the remaining max amount of skill units per object for a specific skill + * @param ud: Unit data + * @param skill_id: Skill to search for + * @param maxcount: Maximum amount of placeable units + */ +void unit_skillunit_maxcount(unit_data& ud, uint16 skill_id, int& maxcount) { + for (const auto su : ud.skillunits) { + if (su->skill_id == skill_id && --maxcount == 0 ) + break; + } +} + /** * Gets the number of units attacking another unit * @param bl: Object to check amount of targets diff --git a/src/map/unit.hpp b/src/map/unit.hpp index 5a40a7d530..51f78d823a 100644 --- a/src/map/unit.hpp +++ b/src/map/unit.hpp @@ -23,7 +23,7 @@ struct unit_data { struct block_list *bl; ///link to owner object BL_PC|BL_MOB|BL_PET|BL_NPC|BL_HOM|BL_MER|BL_ELEM struct walkpath_data walkpath; struct skill_timerskill *skilltimerskill[MAX_SKILLTIMERSKILL]; - struct skill_unit_group *skillunit[MAX_SKILLUNITGROUP]; + std::vector> skillunits; struct skill_unit_group_tickset skillunittick[MAX_SKILLUNITGROUPTICKSET]; short attacktarget_lv; short to_x, to_y; @@ -156,6 +156,7 @@ int unit_set_target(struct unit_data* ud, int target_id); // unit_data void unit_dataset(struct block_list *bl); +void unit_skillunit_maxcount(unit_data& ud, uint16 skill_id, int& maxcount); // Remove unit struct unit_data* unit_bl2ud(struct block_list *bl);