Compare commits

...

17 Commits

Author SHA1 Message Date
Daegaladh
d2c9e90725 Fixed enemy target and moved it back to skill.cpp 2024-09-18 17:04:00 +02:00
Daegaladh
4c5ad8d667 Merge branch 'master' into hotfix/vanilmirth_skills 2024-09-18 12:55:34 +02:00
Lemongrass3110
7796284d7d Added a static_cast 2024-09-18 12:01:30 +02:00
Lemongrass3110
0a23c33181 HVAN_CHAOTIC 2024-09-18 11:40:30 +02:00
Lemongrass3110
d0990e6ee1 HVAN_EXPLOSION 2024-09-18 11:21:26 +02:00
Lemongrass3110
255e69d181 HVAN_CHAOTIC 2024-09-18 11:16:10 +02:00
Lemongrass3110
364818e582 array => vector 2024-09-18 11:06:08 +02:00
Lemongrass3110
d85c709ccb Converted CZ_REQUEST_MOVENPC to struct 2024-09-18 10:39:27 +02:00
Lemongrass3110
7880596b03 Fixed clif_guild_positioninfolist
Fixes #8648

Thanks to @thanna, @laziem, @skstrife and @AoShinRO
2024-09-17 17:10:27 +02:00
Jittapan Pluemsumran
f3f8e63448 Don't allow setting "first pincode" if pincode is already set (#8654) 2024-09-17 16:29:36 +02:00
Daegaladh
b07fcd3c7c Small fix from merging 2024-09-16 20:49:30 +02:00
Daegaladh
13abe0c66b Merge branch 'master' into hotfix/vanilmirth_skills 2024-09-16 20:46:54 +02:00
Daegaladh
b3c827fbcf Fixed coordinates check 2024-09-16 20:24:38 +02:00
Daegaladh
ae501999bb Improved wording 2024-09-16 19:00:53 +02:00
Daegaladh
c07f685a31 Fixes Vanilmirth skills 2024-09-16 18:36:05 +02:00
Kanin Temsrisuk
29671fa5cd Fixed Realgar_Shooter_EXE location (#8647) 2024-09-15 09:41:05 +02:00
AoShinHo
077f714e05 Converted ZC_SKILLINFO_DELETE to struct (#8595)
Co-authored-by: Atemo <Atemo@users.noreply.github.com>
Co-authored-by: Lemongrass3110 <lemongrass@kstp.at>
2024-09-15 02:49:31 +02:00
14 changed files with 146 additions and 130 deletions

View File

@@ -31026,19 +31026,7 @@ Body:
MaxLevel: 5
Type: Magic
TargetType: Attack
Range: 15
Hit: Single
HitCount:
- Level: 1
Count: 1
- Level: 2
Count: 2
- Level: 3
Count: 3
- Level: 4
Count: 4
- Level: 5
Count: 5
Range: 9
Requires:
SpCost:
- Level: 1
@@ -31055,10 +31043,10 @@ Body:
Name: HVAN_CHAOTIC
Description: Benediction of Chaos
MaxLevel: 5
Type: Magic
TargetType: Self
DamageFlags:
NoDamage: true
Hit: Single
AfterCastWalkDelay: 1500
Requires:
SpCost: 40
@@ -31066,8 +31054,6 @@ Body:
Name: HVAN_INSTRUCT
Description: Instruct
MaxLevel: 5
DamageFlags:
NoDamage: true
- Id: 8016
Name: HVAN_EXPLOSION
Description: Bio Explosion
@@ -31079,10 +31065,13 @@ Body:
IgnoreElement: true
IgnoreFlee: true
IgnoreDefCard: true
Flags:
TargetTrap: true
Hit: Single
HitCount: 1
Element: Weapon
SplashArea: 4
Element: Neutral
SplashArea: 5
Duration1: 1500
Requires:
SpCost: 1
- Id: 8018

View File

@@ -83053,7 +83053,7 @@ Body:
Hunter: true
Rogue: true
Locations:
Right_Hand: true
Both_Hand: true
WeaponLevel: 4
EquipLevelMin: 70
Refineable: true

View File

@@ -43312,19 +43312,7 @@ Body:
MaxLevel: 5
Type: Magic
TargetType: Attack
Range: 15
Hit: Single
HitCount:
- Level: 1
Count: 1
- Level: 2
Count: 2
- Level: 3
Count: 3
- Level: 4
Count: 4
- Level: 5
Count: 5
Range: 9
Cooldown:
- Level: 1
Time: 2000
@@ -43352,10 +43340,10 @@ Body:
Name: HVAN_CHAOTIC
Description: Benediction of Chaos
MaxLevel: 5
Type: Magic
TargetType: Self
DamageFlags:
NoDamage: true
Hit: Single
AfterCastWalkDelay: 1500
Cooldown: 3000
Requires:
@@ -43364,8 +43352,6 @@ Body:
Name: HVAN_INSTRUCT
Description: Instruct
MaxLevel: 5
DamageFlags:
NoDamage: true
- Id: 8016
Name: HVAN_EXPLOSION
Description: Bio Explosion
@@ -43377,11 +43363,13 @@ Body:
IgnoreElement: true
IgnoreFlee: true
IgnoreDefCard: true
Flags:
TargetTrap: true
Hit: Single
HitCount: 1
Element: Weapon
SplashArea: 4
AfterCastActDelay: 5000
Element: Neutral
SplashArea: 5
Duration1: 1500
CoolDown: 1000
Requires:
SpCost: 1

View File

@@ -309,8 +309,10 @@ int chclif_parse_pincode_setnew( int fd, struct char_session_data* sd ){
if( charserv_config.pincode_config.pincode_enabled==0 || RFIFOL(fd,2) != sd->account_id ) {
set_eof(fd);
return 1;
}
else {
} else if (strnlen(sd->pincode, PINCODE_LENGTH) > 0) {
set_eof(fd);
return 1;
} else {
char newpin[PINCODE_LENGTH+1];
memset(newpin,0,PINCODE_LENGTH+1);
strncpy( newpin, RFIFOCP(fd,6), PINCODE_LENGTH );
@@ -561,7 +563,7 @@ int chclif_parse_char_delete2_req(int fd, struct char_session_data* sd) {
chclif_char_delete2_ack(fd, char_id, 5, 0);
return 1;
}
// success
delete_date = time(nullptr)+(charserv_config.char_config.char_del_delay);
@@ -985,7 +987,7 @@ void chclif_accessible_maps( int fd ){
}else{
p->maps[count].status = 0;
}
mapindex_getmapname_ext( accessible_map.map, p->maps[count].map );
p->packetLength += sizeof( p->maps[0] );
@@ -1527,7 +1529,7 @@ void chclif_reject(int fd, uint8 errCode){
int chclif_parse_reqcaptcha(int fd){
//FIFOSD_CHECK(8)
RFIFOSKIP(fd,8);
chclif_ack_captcha(fd);
chclif_ack_captcha(fd);
return 1;
}

View File

@@ -3792,7 +3792,7 @@ ACMD_FUNC(lostskill)
sd->status.skill[sk_idx].lv = 0;
sd->status.skill[sk_idx].flag = SKILL_FLAG_PERMANENT;
clif_deleteskill(sd,skill_id);
clif_deleteskill(*sd,skill_id);
clif_displaymessage(fd, msg_txt(sd,71)); // You have forgotten the skill.
return 0;

View File

@@ -1065,7 +1065,7 @@ int chrif_deadopt(uint32 father_id, uint32 mother_id, uint32 child_id) {
sd->status.skill[idx].id = 0;
sd->status.skill[idx].lv = 0;
sd->status.skill[idx].flag = SKILL_FLAG_PERMANENT;
clif_deleteskill(sd,WE_CALLBABY);
clif_deleteskill(*sd,WE_CALLBABY);
}
if( mother_id && ( sd = map_charid2sd(mother_id) ) != nullptr && sd->status.child == child_id ) {
@@ -1073,7 +1073,7 @@ int chrif_deadopt(uint32 father_id, uint32 mother_id, uint32 child_id) {
sd->status.skill[idx].id = 0;
sd->status.skill[idx].lv = 0;
sd->status.skill[idx].flag = SKILL_FLAG_PERMANENT;
clif_deleteskill(sd,WE_CALLBABY);
clif_deleteskill(*sd,WE_CALLBABY);
}
return 0;

View File

@@ -5698,28 +5698,26 @@ void clif_addskill(map_session_data *sd, int skill_id)
}
/// Deletes a skill from the skill tree (ZC_SKILLINFO_DELETE).
/// 0441 <skill id>.W
void clif_deleteskill(map_session_data *sd, int skill_id, bool skip_infoblock)
{
#if PACKETVER >= 20081217
nullpo_retv(sd);
int fd = sd->fd;
/// Deletes a skill from the skill tree.
/// 0441 <skill id>.W (ZC_SKILLINFO_DELETE)
void clif_deleteskill(map_session_data& sd, uint16 skill_id, bool skip_infoblock){
#if PACKETVER >= 20081126
uint16 idx = skill_get_index(skill_id);
if (!session_isActive(fd) || !idx)
if (idx == 0)
return;
WFIFOHEAD(fd,packet_len(0x441));
WFIFOW(fd,0) = 0x441;
WFIFOW(fd,2) = skill_id;
WFIFOSET(fd,packet_len(0x441));
PACKET_ZC_SKILLINFO_DELETE p{};
p.packetType = HEADER_ZC_SKILLINFO_DELETE;
p.skillID = skill_id;
clif_send(&p,sizeof(p),&sd.bl,SELF);
#endif
#if PACKETVER_MAIN_NUM >= 20190807 || PACKETVER_RE_NUM >= 20190807 || PACKETVER_ZERO_NUM >= 20190918
if (!skip_infoblock)
#endif
clif_skillinfoblock(sd);
clif_skillinfoblock(&sd);
}
/// Updates a skill in the skill tree (ZC_SKILLINFO_UPDATE).
@@ -8873,13 +8871,14 @@ static void clif_guild_positioninfolist(map_session_data& sd){
for(size_t i=0;i<MAX_GUILDPOSITION;i++){
guild_position& gp = g->guild.position[i];
PACKET_ZC_POSITION_INFO_sub& position = p->posInfo[i];
p->posInfo[i].positionID = i;
p->posInfo[i].right = gp.mode;
p->posInfo[i].ranking = i;
p->posInfo[i].payRate = gp.exp_mode;
position.positionID = static_cast<decltype(position.positionID)>( i );
position.right = gp.mode;
position.ranking = static_cast<decltype(position.ranking)>( i );
position.payRate = gp.exp_mode;
p->PacketLength += static_cast<decltype(p->PacketLength)>( sizeof( p->posInfo[0] ) );
p->PacketLength += static_cast<decltype(p->PacketLength)>( sizeof( position ) );
}
clif_send(p,p->PacketLength,&sd.bl,SELF);
@@ -15461,24 +15460,28 @@ void clif_parse_HomMoveToMaster(int fd, map_session_data *sd){
}
/// Request to move homunculus/mercenary (CZ_REQUEST_MOVENPC).
/// 0232 <id>.L <position data>.3B
/// Request to move homunculus/mercenary.
/// 0232 <id>.L <position data>.3B (CZ_REQUEST_MOVENPC)
void clif_parse_HomMoveTo(int fd, map_session_data *sd){
struct s_packet_db* info = &packet_db[RFIFOW(fd,0)];
int id = RFIFOL(fd,info->pos[0]); // Mercenary or Homunculus
#if PACKETVER >= 20050425
const PACKET_CZ_REQUEST_MOVENPC* p = reinterpret_cast<const PACKET_CZ_REQUEST_MOVENPC*>( RFIFOP( fd, 0 ) );
struct block_list *bl = nullptr;
short x, y;
RFIFOPOS(fd, info->pos[1], &x, &y, nullptr);
RBUFPOS( p->PosDir, 0, &x, &y, nullptr );
if( sd->md && sd->md->bl.id == id )
if( sd->md && sd->md->bl.id == p->GID )
bl = &sd->md->bl; // Moving Mercenary
else if( hom_is_active(sd->hd) && sd->hd->bl.id == id )
else if( hom_is_active(sd->hd) && sd->hd->bl.id == p->GID )
bl = &sd->hd->bl; // Moving Homunculus
else
return;
if (x < 0 || y < 0 || (x == bl->x && y == bl->y))
return;
unit_walktoxy(bl, x, y, 4);
#endif
}
@@ -15500,7 +15503,6 @@ void clif_parse_HomAttack(int fd,map_session_data *sd)
bl = &sd->md->bl;
else return;
unit_stop_attack(bl);
unit_attack(bl, target_id, action_type != 0);
}

View File

@@ -933,7 +933,7 @@ void clif_skillinfoblock(map_session_data *sd);
void clif_skillup(map_session_data *sd, uint16 skill_id, int lv, int range, int upgradable);
void clif_skillinfo(map_session_data *sd,int skill_id, int inf);
void clif_addskill(map_session_data *sd, int skill_id);
void clif_deleteskill(map_session_data *sd, int skill_id, bool skip_infoblock = false);
void clif_deleteskill(map_session_data& sd, uint16 skill_id, bool skip_infoblock = false);
void clif_skillcasting(struct block_list* bl, int src_id, int dst_id, int dst_x, int dst_y, uint16 skill_id, uint16 skill_lv, int property, int casttime);
void clif_skillcastcancel( block_list& bl );

View File

@@ -563,7 +563,7 @@
// 2005-04-25aSakexe
#if PACKETVER >= 20050425
parseable_packet(0x022d,5,clif_parse_HomMenu,2,4);
parseable_packet(0x0232,9,clif_parse_HomMoveTo,2,6);
parseable_packet( HEADER_CZ_REQUEST_MOVENPC, sizeof( PACKET_CZ_REQUEST_MOVENPC ), clif_parse_HomMoveTo, 0 );
parseable_packet(0x0233,11,clif_parse_HomAttack,2,6,10);
parseable_packet(0x0234,6,clif_parse_HomMoveToMaster,2);
#endif
@@ -1209,7 +1209,6 @@
// 2008-11-26aSakexe
#if PACKETVER >= 20081126
packet(0x01a2,37);
packet(0x0441,4);
#endif
// 2008-12-10aSakexe

View File

@@ -1118,6 +1118,12 @@ struct PACKET_ZC_SKILL_DISAPPEAR {
} __attribute__((packed));
DEFINE_PACKET_HEADER(ZC_SKILL_DISAPPEAR, 0x120);
struct PACKET_ZC_SKILLINFO_DELETE {
uint16 packetType;
uint16 skillID;
} __attribute__((packed));
DEFINE_PACKET_HEADER(ZC_SKILLINFO_DELETE, 0x441);
struct PACKET_ZC_SKILL_UPDATE {
int16 packetType;
uint32 GID;
@@ -1356,6 +1362,13 @@ struct PACKET_ZC_NOTIFY_ACT{
DEFINE_PACKET_HEADER(ZC_NOTIFY_ACT, 0x8a);
#endif
struct PACKET_CZ_REQUEST_MOVENPC{
int16 packetType;
uint32 GID;
uint8 PosDir[3];
} __attribute__((packed));
DEFINE_PACKET_HEADER(CZ_REQUEST_MOVENPC, 0x232);
// NetBSD 5 and Solaris don't like pragma pack but accept the packed attribute
#if !defined( sun ) && ( !defined( __NETBSD__ ) || __NetBSD_Version__ >= 600000000 )
#pragma pack( pop )

View File

@@ -4998,15 +4998,17 @@ struct PACKET_ZC_POSITION_ID_NAME_INFO {
} __attribute__((packed));
DEFINE_PACKET_HEADER(ZC_POSITION_ID_NAME_INFO, 0x0166);
struct PACKET_ZC_POSITION_INFO_sub {
int positionID;
int right;
int ranking;
int payRate;
} __attribute__((packed));
struct PACKET_ZC_POSITION_INFO {
int16 PacketType;
int16 PacketLength;
struct {
int positionID;
int right;
int ranking;
int payRate;
} posInfo[MAX_GUILDPOSITION];
struct PACKET_ZC_POSITION_INFO_sub posInfo[];
} __attribute__((packed));
DEFINE_PACKET_HEADER(ZC_POSITION_INFO, 0x0160);

View File

@@ -5345,7 +5345,7 @@ bool pc_skill(map_session_data* sd, uint16 skill_id, int level, enum e_addskill_
sd->status.skill[idx].flag = SKILL_FLAG_PERMANENT;
if (level == 0) { //Remove skill.
sd->status.skill[idx].id = 0;
clif_deleteskill(sd,skill_id);
clif_deleteskill(*sd,skill_id);
} else
clif_addskill(sd,skill_id);
if (!skill_get_inf(skill_id) || pc_checkskill_summoner(sd, SUMMONER_POWER_LAND) >= 20 || pc_checkskill_summoner(sd, SUMMONER_POWER_SEA) >= 20) //Only recalculate for passive skills.
@@ -5380,7 +5380,7 @@ bool pc_skill(map_session_data* sd, uint16 skill_id, int level, enum e_addskill_
sd->status.skill[idx].flag = SKILL_FLAG_PERM_GRANTED;
if (level == 0) { //Remove skill.
sd->status.skill[idx].id = 0;
clif_deleteskill(sd,skill_id);
clif_deleteskill(*sd,skill_id);
} else
clif_addskill(sd,skill_id);
if (!skill_get_inf(skill_id) || pc_checkskill_summoner(sd, SUMMONER_POWER_LAND) >= 20 || pc_checkskill_summoner(sd, SUMMONER_POWER_SEA) >= 20) //Only recalculate for passive skills.
@@ -5462,7 +5462,7 @@ bool pc_skill_plagiarism_reset(map_session_data &sd, uint8 type)
sd.status.skill[idx].id = 0;
sd.status.skill[idx].lv = 0;
sd.status.skill[idx].flag = SKILL_FLAG_PERMANENT;
clif_deleteskill(&sd, skill_id);
clif_deleteskill(sd, skill_id);
if (type == 1) {
sd.cloneskill_idx = 0;

View File

@@ -3395,7 +3395,7 @@ static void skill_do_copy(struct block_list* src,struct block_list *bl, uint16 s
case 1: //Copied by Plagiarism
{
if (tsd->cloneskill_idx > 0 && tsd->status.skill[tsd->cloneskill_idx].flag == SKILL_FLAG_PLAGIARIZED) {
clif_deleteskill(tsd,tsd->status.skill[tsd->cloneskill_idx].id, true);
clif_deleteskill(*tsd,tsd->status.skill[tsd->cloneskill_idx].id, true);
tsd->status.skill[tsd->cloneskill_idx].id = 0;
tsd->status.skill[tsd->cloneskill_idx].lv = 0;
tsd->status.skill[tsd->cloneskill_idx].flag = SKILL_FLAG_PERMANENT;
@@ -3415,7 +3415,7 @@ static void skill_do_copy(struct block_list* src,struct block_list *bl, uint16 s
//Skill level copied depends on Reproduce skill that used
lv = (tsc) ? tsc->getSCE(SC__REPRODUCE)->val1 : 1;
if( tsd->reproduceskill_idx > 0 && tsd->status.skill[tsd->reproduceskill_idx].flag == SKILL_FLAG_PLAGIARIZED ) {
clif_deleteskill(tsd,tsd->status.skill[tsd->reproduceskill_idx].id, true);
clif_deleteskill(*tsd,tsd->status.skill[tsd->reproduceskill_idx].id, true);
tsd->status.skill[tsd->reproduceskill_idx].id = 0;
tsd->status.skill[tsd->reproduceskill_idx].lv = 0;
tsd->status.skill[tsd->reproduceskill_idx].flag = SKILL_FLAG_PERMANENT;
@@ -3812,12 +3812,12 @@ int64 skill_attack (int attack_type, struct block_list* src, struct block_list *
dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion, damage, dmg.div_, CR_HOLYCROSS, -1, DMG_SPLASH);
break;
//Skills that need be passed as a normal attack for the client to display correctly.
case HVAN_EXPLOSION:
case NPC_SELFDESTRUCTION:
if(src->type == BL_PC)
dmg.blewcount = 10;
dmg.amotion = 0; //Disable delay or attack will do no damage since source is dead by the time it takes effect. [Skotlex]
[[fallthrough]];
case HVAN_EXPLOSION:
case KN_AUTOCOUNTER:
case NPC_CRITICALSLASH:
case TF_DOUBLE:
@@ -4762,6 +4762,9 @@ static TIMER_FUNC(skill_timerskill){
case ABC_FRENZY_SHOT:
skill_castend_damage_id(src, target, skl->skill_id, skl->skill_lv, tick, skl->flag);
break;
case HVAN_EXPLOSION:
status_kill(src);
break;
default:
skill_attack(skl->type,src,src,target,skl->skill_id,skl->skill_lv,tick,skl->flag);
break;
@@ -6195,16 +6198,9 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint
case HVAN_CAPRICE: //[blackhole89]
{
int ran=rnd()%4;
int sid = 0;
switch(ran)
{
case 0: sid=MG_COLDBOLT; break;
case 1: sid=MG_FIREBOLT; break;
case 2: sid=MG_LIGHTNINGBOLT; break;
case 3: sid=WZ_EARTHSPIKE; break;
}
skill_attack(BF_MAGIC,src,src,bl,sid,skill_lv,tick,flag|SD_LEVEL);
static std::vector<e_skill> subskills = { MG_COLDBOLT, MG_FIREBOLT, MG_LIGHTNINGBOLT, WZ_EARTHSPIKE };
e_skill subskill_id = util::vector_random( subskills );
skill_attack(skill_get_type(subskill_id), src, src, bl, subskill_id, skill_lv, tick, flag);
}
break;
@@ -6324,7 +6320,7 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint
[[fallthrough]];
case HVAN_EXPLOSION:
if (src != bl)
skill_attack(BF_MISC,src,src,bl,skill_id,skill_lv,tick,flag);
skill_attack(skill_get_type(skill_id),src,src,bl,skill_id,skill_lv,tick,flag);
break;
// Celest
@@ -8817,13 +8813,11 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
BF_MAGIC, src, src, skill_id, skill_lv, tick, flag, BCT_ENEMY);
break;
case HVAN_EXPLOSION: //[orn]
case NPC_SELFDESTRUCTION:
//Self Destruction hits everyone in range (allies+enemies)
//Except for Summoned Marine spheres on non-versus maps, where it's just enemy.
i = ((!md || md->special_state.ai == AI_SPHERE) && !map_flag_vs(src->m))?
BCT_ENEMY:BCT_ALL;
clif_skill_nodamage(src, *src, skill_id, -1);
map_delblock(src); //Required to prevent chain-self-destructions hitting back.
map_foreachinshootrange(skill_area_sub, bl,
skill_get_splash(skill_id, skill_lv), BL_CHAR|BL_SKILL,
@@ -8833,14 +8827,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
map_freeblock_unlock();
return 1;
}
status_damage(src, src, sstatus->max_hp,0,0,1, skill_id);
if(skill_id == HVAN_EXPLOSION && src->type == BL_HOM) {
homun_data& hd = reinterpret_cast<homun_data&>( *src );
hd.homunculus.intimacy = hom_intimacy_grade2intimacy(HOMGRADE_HATE_WITH_PASSION);
clif_send_homdata( hd, SP_INTIMATE );
}
status_kill(src);
break;
case AL_ANGELUS:
#ifdef RENEWAL
@@ -10703,26 +10690,58 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
else if (sd != nullptr)
clif_skill_fail( *sd, skill_id );
break;
case HVAN_CHAOTIC: //[orn]
case HVAN_CHAOTIC:
{
static const int per[5][2]={{20,50},{50,60},{25,75},{60,64},{34,67}};
int r = rnd()%100;
i = (skill_lv-1)%5;
if(r<per[i][0]) //Self
bl = src;
else if(r<per[i][1]) //Master
bl = battle_get_master(src);
else //Enemy
bl = map_id2bl(battle_gettarget(src));
// Chance per skill level
static const uint8 chance_homunculus[5] = {
20,
50,
25,
50,
34
};
static const uint8 chance_master[5] = {
static_cast<uint8>(chance_homunculus[0] + 30),
static_cast<uint8>(chance_homunculus[1] + 10),
static_cast<uint8>(chance_homunculus[2] + 50),
static_cast<uint8>(chance_homunculus[3] + 4),
static_cast<uint8>(chance_homunculus[4] + 33)
};
if (!bl) bl = src;
i = skill_calc_heal(src, bl, skill_id, 1+rnd()%skill_lv, true);
//Eh? why double skill packet?
clif_skill_nodamage(src,*bl,AL_HEAL,i);
clif_skill_nodamage(src,*bl,skill_id,i);
status_heal(bl, i, 0, 0);
}
break;
uint8 chance = rnd_value(1, 100);
// Homunculus
if (chance <= chance_homunculus[skill_lv - 1])
bl = src;
// Master
else if (chance <= chance_master[skill_lv - 1])
bl = battle_get_master(src);
// Enemy (A random enemy targeting the master)
else
bl = battle_gettargeted(battle_get_master(src));
// If there's no enemy the chance reverts to the homunculus
if (bl == nullptr)
bl = src;
int32 heal = skill_calc_heal(src, bl, skill_id, rnd_value<uint16>(1, skill_lv), true);
// Official servers send the Heal skill packet with the healed amount, and then the skill packet with 1 as healed amount
clif_skill_nodamage(src, *bl, AL_HEAL, heal);
clif_skill_nodamage(src, *bl, skill_id, 1);
status_heal(bl, heal, 0, 0);
} break;
case HVAN_EXPLOSION:
if( hd != nullptr ){
clif_skill_nodamage(src, *src, skill_id, skill_lv, 1);
map_foreachinshootrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), BL_CHAR | BL_SKILL, src, skill_id, skill_lv, tick, flag | BCT_ENEMY, skill_castend_damage_id);
hd->homunculus.intimacy = hom_intimacy_grade2intimacy(HOMGRADE_HATE_WITH_PASSION);
clif_send_homdata(*hd, SP_INTIMATE);
// There's a delay between the explosion and the homunculus death
skill_addtimerskill(src, tick + skill_get_time(skill_id, skill_lv), src->id, 0, 0, skill_id, skill_lv, 0, flag);
} break;
// Homun single-target support skills [orn]
case HLIF_CHANGE:
#ifndef RENEWAL

View File

@@ -4914,7 +4914,7 @@ int status_calc_pc_sub(map_session_data* sd, uint8 opt)
// Client doesn't delete unavailable skills even if we refresh the skill tree, individually delete them.
for (i = 0; i < MAX_SKILL; i++) {
if (b_skill[i].id != 0 && sd->status.skill[i].id == 0)
clif_deleteskill(sd, b_skill[i].id, true);
clif_deleteskill(*sd, b_skill[i].id, true);
}
#endif
clif_skillinfoblock(sd);
@@ -5046,8 +5046,10 @@ int status_calc_homunculus_(struct homun_data *hd, uint8 opt)
status->def += skill_lv * 4;
if((skill_lv = hom_checkskill(hd, HVAN_INSTRUCT)) > 0) {
status->int_ += 1 + skill_lv / 2 + skill_lv / 4 + skill_lv / 5;
status->str += 1 + skill_lv / 3 + skill_lv / 3 + skill_lv / 4;
static const uint8 bonus_int[] = { 1, 2, 2, 4, 5 };
static const uint8 bonus_str[] = { 1, 1, 3, 4, 4 };
status->int_ += bonus_int[skill_lv - 1];
status->str += bonus_str[skill_lv - 1];
}
if((skill_lv = hom_checkskill(hd, HAMI_SKIN)) > 0)