Browse Source

Implemented missing ZC_HO_PAR_CHANGE packet (#8538)

Daegaladh 7 tháng trước cách đây
mục cha
commit
95316736b5

+ 2 - 2
sql-files/main.sql

@@ -672,8 +672,8 @@ CREATE TABLE IF NOT EXISTS `homunculus` (
   `luk` smallint(4) unsigned NOT NULL default '0',
   `hp` int(11) unsigned NOT NULL default '0',
   `max_hp` int(11) unsigned NOT NULL default '0',
-  `sp` int(11) NOT NULL default '0',
-  `max_sp` int(11) NOT NULL default '0',
+  `sp` int(11) unsigned NOT NULL default '0',
+  `max_sp` int(11) unsigned NOT NULL default '0',
   `skill_point` smallint(4) unsigned NOT NULL default '0',
   `alive` tinyint(2) NOT NULL default '1',
   `rename_flag` tinyint(2) NOT NULL default '0',

+ 3 - 0
sql-files/upgrades/upgrade_20241005.sql

@@ -0,0 +1,3 @@
+ALTER TABLE `homunculus`
+	CHANGE COLUMN `sp` `sp` INT(11) UNSIGNED NOT NULL DEFAULT '0',
+	CHANGE COLUMN `max_sp` `max_sp` INT(11) UNSIGNED NOT NULL DEFAULT '0';

+ 4 - 4
src/char/int_homun.cpp

@@ -189,10 +189,10 @@ bool mapif_homunculus_load(int homun_id, struct s_homunculus* hd)
 	Sql_GetData(sql_handle, 12, &data, nullptr); hd->int_ = atoi(data);
 	Sql_GetData(sql_handle, 13, &data, nullptr); hd->dex = atoi(data);
 	Sql_GetData(sql_handle, 14, &data, nullptr); hd->luk = atoi(data);
-	Sql_GetData(sql_handle, 15, &data, nullptr); hd->hp = atoi(data);
-	Sql_GetData(sql_handle, 16, &data, nullptr); hd->max_hp = atoi(data);
-	Sql_GetData(sql_handle, 17, &data, nullptr); hd->sp = atoi(data);
-	Sql_GetData(sql_handle, 18, &data, nullptr); hd->max_sp = atoi(data);
+	Sql_GetData(sql_handle, 15, &data, nullptr); hd->hp = strtoul(data, nullptr, 10);
+	Sql_GetData(sql_handle, 16, &data, nullptr); hd->max_hp = strtoul(data, nullptr, 10);
+	Sql_GetData(sql_handle, 17, &data, nullptr); hd->sp = strtoul(data, nullptr, 10);
+	Sql_GetData(sql_handle, 18, &data, nullptr); hd->max_sp = strtoul(data, nullptr, 10);
 	Sql_GetData(sql_handle, 19, &data, nullptr); hd->skillpts = atoi(data);
 	Sql_GetData(sql_handle, 20, &data, nullptr); hd->rename_flag = atoi(data);
 	Sql_GetData(sql_handle, 21, &data, nullptr); hd->vaporize = atoi(data);

+ 1 - 1
src/common/mmo.hpp

@@ -497,7 +497,7 @@ struct s_homunculus {	//[orn]
 	uint32 char_id;
 	short class_;
 	short prev_class;
-	int hp,max_hp,sp,max_sp;
+	uint32 hp,max_hp,sp,max_sp;
 	unsigned int intimacy;	//[orn]
 	short hunger;
 	struct s_skill hskill[MAX_HOMUNSKILL]; //albator

+ 88 - 25
src/map/clif.cpp

@@ -1748,6 +1748,63 @@ int clif_spawn( struct block_list *bl, bool walking ){
 	return 0;
 }
 
+/// Notifies client of a change in an homunculus' status parameter.
+/// 0x7db <type>.W <value>.L (ZC_HO_PAR_CHANGE)
+/// 0xba5 <type>.W <value>.Q (ZC_HO_PAR_CHANGE2)
+void clif_homunculus_updatestatus(map_session_data& sd, _sp type) {
+#if PACKETVER >= 20090610
+	if( !hom_is_active(sd.hd) )
+		return;
+
+	PACKET_ZC_HO_PAR_CHANGE p = {};
+
+	p.packetType = HEADER_ZC_HO_PAR_CHANGE;
+	p.type = static_cast<decltype(p.type)>(type);
+	status_data* status = &sd.hd->battle_status;
+
+	switch (type) {
+	case SP_BASEEXP:
+		p.value = static_cast<decltype(p.value)>( std::min<decltype(sd.hd->homunculus.exp)>( sd.hd->homunculus.exp, std::numeric_limits<decltype(p.value)>::max() ) );
+		break;
+	case SP_HP:
+		// Homunculus HP and SP bars will screw up if the percentage calculation exceeds signed values
+		// Any value above will screw up the bars
+		// Since max values depend on PACKET_ZC_PROPERTY_HOMUN we check for each version of that packet
+#if PACKETVER_MAIN_NUM >= 20200819 || PACKETVER_RE_NUM >= 20200723 || PACKETVER_ZERO_NUM >= 20190626
+		// Tested maximum: 2147483647 (INT32_MAX)
+		if (status->max_hp > INT32_MAX)
+#elif PACKETVER_MAIN_NUM >= 20131230 || PACKETVER_RE_NUM >= 20131230 || defined(PACKETVER_ZERO)
+		// Tested maximum: 21474836 (INT32_MAX / 100)
+		if (status->max_hp > INT32_MAX / 100)
+#else
+		// Tested maximum: 32767 (INT16_MAX)
+		if (status->max_hp > INT16_MAX)
+#endif
+			p.value = status->hp / (status->max_hp / 100);
+		else
+			p.value = static_cast<decltype(p.value)>(status->hp);
+		break;
+	case SP_SP:
+#if PACKETVER_MAIN_NUM >= 20200819 || PACKETVER_RE_NUM >= 20200723 || PACKETVER_ZERO_NUM >= 20190626
+		// Tested maximum: 2147483647 (INT32_MAX)
+		if (status->max_sp > INT32_MAX)
+#else
+		// Tested maximum: 32767 (INT16_MAX)
+		if (status->max_sp > INT16_MAX)
+#endif
+			p.value = status->sp / (status->max_sp / 100);
+		else
+			p.value = static_cast<decltype(p.value)>(status->sp);
+		break;
+	default:
+		ShowError("clif_homunculus_updatestatus: Unsupported type %d.\n", type);
+		return;
+	}
+
+	clif_send(&p, sizeof(p), &sd.bl, SELF);
+#endif
+}
+
 /// Sends information about owned homunculus to the client . [orn]
 /// 022e <name>.24B <modified>.B <level>.W <hunger>.W <intimacy>.W <equip id>.W <atk>.W <matk>.W <hit>.W <crit>.W <def>.W <mdef>.W <flee>.W <aspd>.W <hp>.W <max hp>.W <sp>.W <max sp>.W <exp>.L <max exp>.L <skill points>.W <atk range>.W	(ZC_PROPERTY_HOMUN)
 /// 09f7 <name>.24B <modified>.B <level>.W <hunger>.W <intimacy>.W <equip id>.W <atk>.W <matk>.W <hit>.W <crit>.W <def>.W <mdef>.W <flee>.W <aspd>.W <hp>.L <max hp>.L <sp>.W <max sp>.W <exp>.L <max exp>.L <skill points>.W <atk range>.W (ZC_PROPERTY_HOMUN_2)
@@ -1786,39 +1843,42 @@ void clif_hominfo( map_session_data *sd, struct homun_data *hd, int flag ){
 #endif
 	p.flee = status->flee;
 	p.amotion = (flag) ? 0 : status->amotion;
-#if PACKETVER >= 20141016
-	// Homunculus HP bar will screw up if the percentage calculation exceeds signed values
-	// Tested maximum: 21474836(=INT32_MAX/100), any value above will screw up the HP bar
-	if( status->max_hp > ( INT32_MAX / 100 ) ){
-		p.hp = status->hp / ( status->max_hp / 100 );
-		p.maxHp = 100;
-	}else{
-		p.hp = status->hp;
-		p.maxHp = status->max_hp;
-	}
+	// Homunculus HP and SP bars will screw up if the percentage calculation exceeds signed values
+	// Any value above will screw up the bars
+#if PACKETVER_MAIN_NUM >= 20200819 || PACKETVER_RE_NUM >= 20200723 || PACKETVER_ZERO_NUM >= 20190626
+	// Tested maximum: 2147483647 (INT32_MAX)
+	if (status->max_hp > INT32_MAX)
+#elif PACKETVER_MAIN_NUM >= 20131230 || PACKETVER_RE_NUM >= 20131230 || defined(PACKETVER_ZERO)
+	// Tested maximum: 21474836 (INT32_MAX / 100)
+	if (status->max_hp > INT32_MAX / 100)
 #else
-	if( status->max_hp > INT16_MAX ){
+	// Tested maximum: 32767 (INT16_MAX)
+	if (status->max_hp > INT16_MAX)
+#endif
+	{
 		p.hp = status->hp / ( status->max_hp / 100 );
 		p.maxHp = 100;
 	}else{
-		p.hp = status->hp;
-		p.maxHp = status->max_hp;
+		p.hp = static_cast<decltype(p.hp)>(status->hp);
+		p.maxHp = static_cast<decltype(p.maxHp)>(status->max_hp);
 	}
+#if PACKETVER_MAIN_NUM >= 20200819 || PACKETVER_RE_NUM >= 20200723 || PACKETVER_ZERO_NUM >= 20190626
+	// Tested maximum: 2147483647 (INT32_MAX)
+	if (status->max_sp > INT32_MAX)
+#else
+	// Tested maximum: 32767 (INT16_MAX)
+	if (status->max_sp > INT16_MAX)
 #endif
-	if( status->max_sp > INT16_MAX ){
+	{
 		p.sp = status->sp / ( status->max_sp / 100 );
 		p.maxSp = 100;
 	}else{
-		p.sp = status->sp;
-		p.maxSp = status->max_sp;
+		p.sp = static_cast<decltype(p.sp)>(status->sp);
+		p.maxSp = static_cast<decltype(p.maxSp)>(status->max_sp);
 	}
-#if PACKETVER_MAIN_NUM >= 20210303 || PACKETVER_RE_NUM >= 20211103
-	p.exp = hd->homunculus.exp;
-	p.expNext = hd->exp_next;
-#else
-	p.exp = (uint32)hd->homunculus.exp;
-	p.expNext = (uint32)hd->exp_next;
-#endif
+	p.exp = static_cast<decltype(p.exp)>( std::min<decltype(hd->homunculus.exp)>( hd->homunculus.exp, std::numeric_limits<decltype(p.exp)>::max() ) );
+	p.expNext = static_cast<decltype(p.expNext)>( std::min<decltype(hd->exp_next)>( hd->exp_next, std::numeric_limits<decltype(p.expNext)>::max() ) );
+
 	switch( hom_class2type( hd->homunculus.class_ ) ){
 		case HT_REG:
 		case HT_EVO:
@@ -10782,8 +10842,11 @@ void clif_parse_LoadEndAck(int fd,map_session_data *sd)
 			return;
 		clif_spawn(&sd->hd->bl);
 		clif_send_homdata( *sd->hd, SP_ACK );
-		clif_hominfo(sd,sd->hd,1);
-		clif_hominfo(sd,sd->hd,0); //for some reason, at least older clients want this sent twice
+		// For some reason, official servers send the homunculus info twice, then update the HP/SP again.
+		clif_hominfo(sd, sd->hd, 1);
+		clif_hominfo(sd, sd->hd, 0);
+		clif_homunculus_updatestatus(*sd, SP_HP);
+		clif_homunculus_updatestatus(*sd, SP_SP);
 		clif_homskillinfoblock( *sd->hd );
 		status_calc_bl(&sd->hd->bl, { SCB_SPEED });
 		if( !(battle_config.hom_setting&HOMSET_NO_INSTANT_LAND_SKILL) )

+ 1 - 0
src/map/clif.hpp

@@ -1161,6 +1161,7 @@ void clif_homskillinfoblock( homun_data& hd );
 void clif_homskillup( homun_data& hd, uint16 skill_id );
 void clif_hom_food( map_session_data& sd, int32 foodid, bool success );
 void clif_send_homdata( homun_data& hd, e_hom_state2 state );
+void clif_homunculus_updatestatus(map_session_data& sd, _sp type);
 
 void clif_configuration( map_session_data* sd, enum e_config_type type, bool enabled );
 void clif_viewequip_ack( map_session_data& sd, map_session_data& tsd );

+ 52 - 58
src/map/homunculus.cpp

@@ -226,15 +226,6 @@ void hom_delspiritball(TBL_HOM *hd, int count, int type) {
 		clif_spiritball(&hd->bl);
 }
 
-/**
-* Update homunculus info to its master after receiving damage
-* @param hd
-*/
-void hom_damage(struct homun_data *hd) {
-	if (hd->master)
-		clif_hominfo(hd->master,hd,0);
-}
-
 /**
 * Set homunculus's dead status
 * @param hd
@@ -245,8 +236,6 @@ int hom_dead(struct homun_data *hd)
 	//There's no intimacy penalties on death (from Tharis)
 	map_session_data *sd = hd->master;
 
-	clif_emotion(&hd->bl, ET_KEK);
-
 	//Delete timers when dead.
 	hom_hungry_timer_delete(hd);
 	hd->homunculus.hp = 0;
@@ -254,8 +243,6 @@ int hom_dead(struct homun_data *hd)
 	if (!sd) //unit remove map will invoke unit free
 		return 3;
 
-	clif_emotion(&sd->bl, ET_CRY);
-
 #ifdef RENEWAL
 	status_change_end(&sd->bl, SC_HOMUN_TIME);
 #endif
@@ -306,11 +293,9 @@ int hom_vaporize(map_session_data *sd, int flag)
 
 /**
 * Delete a homunculus, completely "killing it".
-* Emote is the emotion the master should use, send negative to disable.
 * @param hd
-* @param emote
 */
-int hom_delete(struct homun_data *hd, int emote)
+int hom_delete(struct homun_data *hd)
 {
 	map_session_data *sd;
 	nullpo_ret(hd);
@@ -319,9 +304,6 @@ int hom_delete(struct homun_data *hd, int emote)
 	if (!sd)
 		return unit_free(&hd->bl,CLR_DEAD);
 
-	if (emote >= 0)
-		clif_emotion(&sd->bl, emote);
-
 	//This makes it be deleted right away.
 	hd->homunculus.intimacy = 0;
 	// Send homunculus_dead to client
@@ -573,6 +555,7 @@ int hom_levelup(struct homun_data *hd)
 	// Needed to update skill list for mutated homunculus so unlocked skills will appear when the needed level is reached.
 	status_calc_homunculus(hd,SCO_NONE);
 	clif_hominfo(hd->master,hd,0);
+	clif_homunculus_updatestatus(*hd->master, SP_BASEEXP);
 	clif_homskillinfoblock( *hd );
 
 	if ( hd->master && battle_config.homunculus_show_growth ) {
@@ -615,7 +598,7 @@ int hom_evolution(struct homun_data *hd)
 	nullpo_ret(hd);
 
 	if(!hd->homunculusDB->evo_class || hd->homunculus.class_ == hd->homunculusDB->evo_class) {
-		clif_emotion(&hd->bl, ET_SWEAT);
+		clif_emotion(&hd->bl, ET_SCRATCH);
 		return 0 ;
 	}
 
@@ -629,7 +612,7 @@ int hom_evolution(struct homun_data *hd)
 		return 0;
 	}
 
-	//Apply evolution bonuses
+	// Apply evolution bonuses
 	s_homunculus *hom = &hd->homunculus;
 	s_hom_stats *max = &hd->homunculusDB->emax;
 	s_hom_stats *min = &hd->homunculusDB->emin;
@@ -644,21 +627,18 @@ int hom_evolution(struct homun_data *hd)
 	hom->luk += 10*rnd_value(min->luk, max->luk);
 	hom->intimacy = battle_config.homunculus_evo_intimacy_reset;
 
-	hom_calc_skilltree(hd);
-
-	unit_remove_map(&hd->bl, CLR_OUTSIGHT);
-	if (map_addblock(&hd->bl))
-		return 0;
-
-	clif_spawn(&hd->bl);
-	clif_emotion(&sd->bl, ET_BEST);
-	clif_specialeffect(&hd->bl,EF_HO_UP,AREA);
+	clif_class_change(&hd->bl, hd->homunculusDB->evo_class, 0);
 
-	//status_Calc flag&1 will make current HP/SP be reloaded from hom structure
+	// status_Calc flag&1 will make current HP/SP be reloaded from hom structure
 	hom->hp = hd->battle_status.hp;
 	hom->sp = hd->battle_status.sp;
 	status_calc_homunculus(hd, SCO_FIRST);
 
+	clif_hominfo(sd, hd, 0);
+	// Official servers don't recaculate the skill tree after evolution
+	// but we do it for convenience
+	hom_calc_skilltree(hd);
+
 	if (!(battle_config.hom_setting&HOMSET_NO_INSTANT_LAND_SKILL))
 		skill_unit_move(&sd->hd->bl,gettick(),1); // apply land skills immediately
 
@@ -682,7 +662,7 @@ int hom_mutate(struct homun_data *hd, int homun_id)
 	m_id    = hom_class2mapid(homun_id);
 
 	if( m_class == -1 || m_id == -1 || !(m_class&HOM_EVO) || !(m_id&HOM_S) ) {
-		clif_emotion(&hd->bl, ET_SWEAT);
+		clif_emotion(&hd->bl, ET_SCRATCH);
 		return 0;
 	}
 
@@ -697,23 +677,20 @@ int hom_mutate(struct homun_data *hd, int homun_id)
 		return 0;
 	}
 
-	hom_calc_skilltree(hd);
-
-	unit_remove_map(&hd->bl, CLR_OUTSIGHT);
-	if(map_addblock(&hd->bl))
-		return 0;
-
-	clif_spawn(&hd->bl);
-	clif_emotion(&sd->bl, ET_BEST);
-	clif_specialeffect(&hd->bl,EF_HO_UP,AREA);
+	clif_class_change(&hd->bl, homun_id, 0);
 
-	//status_Calc flag&1 will make current HP/SP be reloaded from hom structure
+	// status_Calc flag&1 will make current HP/SP be reloaded from hom structure
 	hom = &hd->homunculus;
 	hom->hp = hd->battle_status.hp;
 	hom->sp = hd->battle_status.sp;
 	hom->prev_class = prev_class;
 	status_calc_homunculus(hd, SCO_FIRST);
 
+	clif_hominfo(sd, hd, 0);
+	// Official servers don't recaculate the skill tree after evolution
+	// but we do it for convenience
+	hom_calc_skilltree(hd);
+
 	if (!(battle_config.hom_setting&HOMSET_NO_INSTANT_LAND_SKILL))
 		skill_unit_move(&sd->hd->bl,gettick(),1); // apply land skills immediately
 
@@ -750,7 +727,7 @@ void hom_gainexp(struct homun_data *hd,t_exp exp)
 	hd->homunculus.exp += exp;
 
 	if (hd->master && hd->homunculus.exp < hd->exp_next) {
-		clif_hominfo(hd->master,hd,0);
+		clif_homunculus_updatestatus(*hd->master, SP_BASEEXP);
 		return;
 	}
 
@@ -807,12 +784,19 @@ int hom_decrease_intimacy(struct homun_data * hd, unsigned int value)
 }
 
 /**
-* Update homunculus info to master after healing
-* @param hd
+* Update homunculus status after healing/damage
+* @param hd Homunculus data
+* @param hp HP amount
+* @param sp SP amount
 */
-void hom_heal(struct homun_data *hd) {
-	if (hd->master)
-		clif_hominfo(hd->master,hd,0);
+void hom_heal(homun_data& hd, bool hp, bool sp) {
+	if (hd.master == nullptr)
+		return;
+
+	if (hp)
+		clif_homunculus_updatestatus(*hd.master, SP_HP);
+	if (sp)
+		clif_homunculus_updatestatus(*hd.master, SP_SP);
 }
 
 /**
@@ -853,7 +837,7 @@ void hom_menu(map_session_data *sd, int type)
 			hom_food(sd, sd->hd);
 			break;
 		case 2:
-			hom_delete(sd->hd, -1);
+			hom_delete(sd->hd);
 			break;
 		default:
 			ShowError("hom_menu : unknown menu choice : %d\n", type);
@@ -914,7 +898,7 @@ int hom_food(map_session_data *sd, struct homun_data *hd)
 
 	// Too much food :/
 	if(hd->homunculus.intimacy == 0)
-		return hom_delete(sd->hd, ET_HUK);
+		return hom_delete(sd->hd);
 
 	return 0;
 }
@@ -957,7 +941,7 @@ static TIMER_FUNC(hom_hungry){
 		hd->homunculus.hunger = 0;
 		// Delete the homunculus if intimacy <= 100
 		if (!hom_decrease_intimacy(hd, 100))
-			return hom_delete(hd, ET_HUK);
+			return hom_delete(hd);
 		clif_send_homdata( *hd, SP_INTIMATE );
 	}
 
@@ -1025,8 +1009,9 @@ void hom_change_name_ack(map_session_data *sd, char* name, int flag)
 		return;
 	}
 	safestrncpy(hd->homunculus.name,name,NAME_LENGTH);
-	clif_name_area(&hd->bl);
 	hd->homunculus.rename_flag = 1;
+	clif_hominfo(sd,hd,1);
+	clif_name_area(&hd->bl);
 	clif_hominfo(sd,hd,0);
 }
 
@@ -1136,8 +1121,11 @@ bool hom_call(map_session_data *sd)
 			return false;
 		clif_spawn(&hd->bl);
 		clif_send_homdata( *hd, SP_ACK );
-		clif_hominfo(sd,hd,1);
-		clif_hominfo(sd,hd,0); // send this x2. dunno why, but kRO does that [blackhole89]
+		// For some reason, official servers send the homunculus info twice, then update the HP/SP again.
+		clif_hominfo(sd, hd, 1);
+		clif_hominfo(sd, hd, 0);
+		clif_homunculus_updatestatus(*sd, SP_HP);
+		clif_homunculus_updatestatus(*sd, SP_SP);
 		clif_homskillinfoblock( *hd );
 		status_calc_bl(&hd->bl, { SCB_SPEED });
 		hom_save(hd);
@@ -1199,8 +1187,11 @@ int hom_recv_data(uint32 account_id, struct s_homunculus *sh, int flag)
 			return 0;
 		clif_spawn(&hd->bl);
 		clif_send_homdata( *hd, SP_ACK );
-		clif_hominfo(sd,hd,1);
-		clif_hominfo(sd,hd,0); // send this x2. dunno why, but kRO does that [blackhole89]
+		// For some reason, official servers send the homunculus info twice, then update the HP/SP again.
+		clif_hominfo(sd, hd, 1);
+		clif_hominfo(sd, hd, 0);
+		clif_homunculus_updatestatus(*sd, SP_HP);
+		clif_homunculus_updatestatus(*sd, SP_SP);
 		clif_homskillinfoblock( *hd );
 		hom_init_timers(hd);
 
@@ -1317,8 +1308,11 @@ void hom_revive(struct homun_data *hd, unsigned int hp, unsigned int sp)
 	if (!sd)
 		return;
 	clif_send_homdata( *hd, SP_ACK );
-	clif_hominfo(sd,hd,1);
-	clif_hominfo(sd,hd,0);
+	// For some reason, official servers send the homunculus info twice, then update the HP/SP again.
+	clif_hominfo(sd, hd, 1);
+	clif_hominfo(sd, hd, 0);
+	clif_homunculus_updatestatus(*sd, SP_HP);
+ 	clif_homunculus_updatestatus(*sd, SP_SP);
 	clif_homskillinfoblock( *hd );
 
 	if( hd->homunculus.class_ == MER_ELEANOR ){

+ 1 - 2
src/map/homunculus.hpp

@@ -207,7 +207,6 @@ int hom_recv_data(uint32 account_id, struct s_homunculus *sh, int flag); //albat
 struct view_data* hom_get_viewdata(int class_);
 int hom_class2mapid(int hom_class);
 enum homun_type hom_class2type(int class_);
-void hom_damage(struct homun_data *hd);
 int hom_dead(struct homun_data *hd);
 void hom_skillup(struct homun_data *hd,uint16 skill_id);
 void hom_calc_skilltree(homun_data *hd);
@@ -217,7 +216,7 @@ void hom_gainexp(struct homun_data *hd,t_exp exp);
 int hom_levelup(struct homun_data *hd);
 int hom_evolution(struct homun_data *hd);
 int hom_mutate(struct homun_data *hd,int homun_id);
-void hom_heal(struct homun_data *hd);
+void hom_heal(homun_data& hd, bool hp, bool sp);
 int hom_vaporize(map_session_data *sd, int flag);
 int hom_ressurect(map_session_data *sd, unsigned char per, short x, short y);
 void hom_revive(struct homun_data *hd, unsigned int hp, unsigned int sp);

+ 17 - 0
src/map/packets.hpp

@@ -1310,6 +1310,23 @@ struct PACKET_CZ_PARTY_JOIN_REQ_ACK{
 } __attribute__((packed));
 DEFINE_PACKET_HEADER(CZ_PARTY_JOIN_REQ_ACK, 0x2c7);
 
+#if PACKETVER_MAIN_NUM >= 20210000 || PACKETVER_RE_NUM >= 20211103 || PACKETVER_ZERO_NUM >= 20210217
+// PACKET_ZC_HO_PAR_CHANGE2
+struct PACKET_ZC_HO_PAR_CHANGE {
+	int16 packetType;
+	uint16 type;
+	uint64 value;
+} __attribute__((packed));
+DEFINE_PACKET_HEADER(ZC_HO_PAR_CHANGE, 0xba5);
+#else
+struct PACKET_ZC_HO_PAR_CHANGE {
+	int16 packetType;
+	uint16 type;
+	int32 value;
+} __attribute__((packed));
+DEFINE_PACKET_HEADER(ZC_HO_PAR_CHANGE, 0x7db);
+#endif
+
 struct PACKET_ZC_EL_PAR_CHANGE {
 	int16 packetType;
 	uint16 type;

+ 29 - 10
src/map/status.cpp

@@ -1584,11 +1584,20 @@ int status_damage(struct block_list *src,struct block_list *target,int64 dhp, in
 	// Need to pass original HP damage for the mob damage log
 	dhp = cap_value(dhp, INT_MIN, INT_MAX);
 	switch (target->type) {
-		case BL_PC:  pc_damage((TBL_PC*)target,src,hp,sp,ap); break;
-		case BL_MOB: mob_damage((TBL_MOB*)target, src, (int)dhp); break;
-		case BL_HOM: hom_damage((TBL_HOM*)target); break;
-		case BL_MER: mercenary_heal((TBL_MER*)target,hp,sp); break;
-		case BL_ELEM: elemental_heal((TBL_ELEM*)target,hp,sp); break;
+	case BL_PC:
+		pc_damage(reinterpret_cast<map_session_data*>(target), src, hp, sp, ap); break;
+	case BL_MOB:
+		mob_damage(reinterpret_cast<mob_data*>(target), src, (int)dhp);
+		break;
+	case BL_HOM:
+		hom_heal(reinterpret_cast<homun_data&>(*target), hp != 0, sp != 0);
+		break;
+	case BL_MER:
+		mercenary_heal(reinterpret_cast<s_mercenary_data*>(target), hp, sp);
+		break;
+	case BL_ELEM:
+		elemental_heal(reinterpret_cast<s_elemental_data*>(target), hp, sp);
+		break;
 	}
 
 	if( src && target->type == BL_PC && ((TBL_PC*)target)->disguise ) { // Stop walking when attacked in disguise to prevent walk-delay bug
@@ -1796,11 +1805,21 @@ int status_heal(struct block_list *bl,int64 hhp,int64 hsp, int64 hap, int flag)
 
 	// Send HP update to client
 	switch(bl->type) {
-		case BL_PC:  pc_heal((TBL_PC*)bl,hp,sp,ap,flag); break;
-		case BL_MOB: mob_heal((TBL_MOB*)bl,hp); break;
-		case BL_HOM: hom_heal((TBL_HOM*)bl); break;
-		case BL_MER: mercenary_heal((TBL_MER*)bl,hp,sp); break;
-		case BL_ELEM: elemental_heal((TBL_ELEM*)bl,hp,sp); break;
+	case BL_PC:
+		pc_heal(reinterpret_cast<map_session_data*>(bl), hp, sp, ap, flag);
+		break;
+	case BL_MOB:
+		mob_heal(reinterpret_cast<mob_data*>(bl), hp);
+		break;
+	case BL_HOM:
+		hom_heal(reinterpret_cast<homun_data&>(*bl), hp != 0, sp != 0);
+		break;
+	case BL_MER:
+		mercenary_heal(reinterpret_cast<s_mercenary_data*>(bl), hp, sp);
+		break;
+	case BL_ELEM:
+		elemental_heal(reinterpret_cast<s_elemental_data*>(bl), hp, sp);
+		break;
 	}
 
 	return (int)hp+sp+ap;

+ 0 - 1
src/map/unit.cpp

@@ -3363,7 +3363,6 @@ int unit_remove_map_(struct block_list *bl, clr_type clrtype, const char* file,
 
 			if( !hd->homunculus.intimacy && !(hd->master && !hd->master->state.active) ) {
 				// If logging out, this is deleted on unit_free
-				clif_emotion(bl, ET_CRY);
 				clif_clearunit_area( *bl, clrtype );
 				map_delblock(bl);
 				unit_free(bl,CLR_OUTSIGHT);