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 <lemongrass@kstp.at>
This commit is contained in:
Aleos 2021-08-23 14:11:53 -04:00 committed by GitHub
parent abe40d1eff
commit a84e731f79
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 250 additions and 244 deletions

View File

@ -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<s_skill_unit_group> 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<s_skill_unit_group> 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;v<MAX_SKILLUNITGROUP && sd->ud.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;
}

View File

@ -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;
}

View File

@ -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<s_skill_unit_group> 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<std::shared_ptr<s_skill_unit_group>> 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

View File

@ -19412,7 +19412,7 @@ BUILDIN_FUNC(warpportal)
unsigned short mapindex;
int tpx;
int tpy;
struct skill_unit_group* group;
std::shared_ptr<s_skill_unit_group> group;
struct block_list* bl;
bl = map_id2bl(st->oid);

View File

@ -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<s_skill_unit_group> 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<s_skill_unit_group> 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<s_skill_unit_group> 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<s_skill_unit_group> 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<s_skill_unit_group> sg;
std::shared_ptr<s_skill_db> 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<s_skill_unit_group> 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<s_skill_unit_group> 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<s_skill_unit_group> 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<s_skill_unit_group> 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<s_skill_unit_group> 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;i<MAX_SKILLUNITGROUP && sd->ud.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<s_skill_unit_group> 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<s_skill_unit_group> 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<s_skill_unit_group> 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<s_skill_db> 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<s_skill_unit_group> 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<s_skill_unit_group> 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<s_skill_unit_group> 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<s_skill_unit_group> 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<s_skill_unit_group> 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<s_skill_unit_group> 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<int>(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<s_skill_unit_group> 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<s_skill_unit_group> 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<s_skill_unit_group> 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<s_skill_unit_group> 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<s_skill_unit_group> 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<int, std::shared_ptr<s_skill_unit_group>> 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<s_skill_unit_group> 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<s_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)
{
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<s_skill_unit_group>();
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<s_skill_unit_group> 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<s_skill_unit_group> 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<s_skill_unit_group> 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<s_skill_unit_group> group = unit->group;
if (group == nullptr)
return 0;
std::shared_ptr<s_skill_db> 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<s_skill_unit_group> 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<s_skill_unit_group> 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<s_skill_unit_group> 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<s_skill_unit_group> 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<s_skill_unit_group> group;
if (!(sus = static_cast<skill_usave *>(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);
}

View File

@ -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<s_skill_unit_group> 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<s_skill_unit_group> skill_id2group(int group_id);
std::shared_ptr<s_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 (std::shared_ptr<s_skill_unit_group> 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<s_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_(std::shared_ptr<s_skill_unit_group> 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<s_skill_unit_group> 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);

View File

@ -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<s_skill_unit_group> 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<s_skill_unit_group> 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<s_skill_unit_group> 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<s_skill_unit_group> 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<s_skill_unit_group> 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<s_skill_unit_group> 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<s_skill_unit_group> group = skill_id2group(sce->val2);
sce->val2 = 0;
if( group ) // Might have been cleared before status ended, e.g. land protector
skill_delunitgroup(group);

View File

@ -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

View File

@ -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<std::shared_ptr<s_skill_unit_group>> 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);