Fixed a problem with walking NPCs (#4782)

Walking NPCs are not supported on official servers.
Therefore we have to respawn the NPC units as player units before they start walking.
After they stop walking we respawn them as NPC units again so that they show the bubbles for talking to them again.
Since player sprites have a small aura during spawning hardcoded in the client, this aura will be shown each time they begin walking now.

Fixes #4505

Thanks to @RagnaWay and @aleos89
This commit is contained in:
Lemongrass3110
2020-04-09 17:03:37 +02:00
committed by GitHub
parent 03fa1af7a8
commit 6fe0ed5819
4 changed files with 31 additions and 13 deletions

View File

@@ -279,7 +279,7 @@ uint16 clif_getport(void)
}
#if PACKETVER >= 20071106
static inline unsigned char clif_bl_type(struct block_list *bl) {
static inline unsigned char clif_bl_type(struct block_list *bl, bool walking) {
switch (bl->type) {
case BL_PC: return (disguised(bl) && !pcdb_checkid(status_get_viewdata(bl)->class_))? 0x1:0x0; //PC_TYPE
case BL_ITEM: return 0x2; //ITEM_TYPE
@@ -287,10 +287,13 @@ static inline unsigned char clif_bl_type(struct block_list *bl) {
case BL_CHAT: return 0x4; //UNKNOWN_TYPE
case BL_MOB: return pcdb_checkid(status_get_viewdata(bl)->class_)?0x0:0x5; //NPC_MOB_TYPE
case BL_NPC:
// From 2017-07-26 on NPC type units can also use player sprites.
// There is one exception and this is if they are walking.
// Since walking NPCs are not supported on official servers, the client does not know how to handle it.
#if PACKETVER >= 20170726
return 0x6; //NPC_EVT_TYPE
return ( pcdb_checkid(status_get_viewdata(bl)->class_) && walking ) ? 0x0 : 0x6; //NPC_EVT_TYPE
#else
return (pcdb_checkid(status_get_viewdata(bl)->class_) ? 0x0 : 0x6);
return pcdb_checkid(status_get_viewdata(bl)->class_) ? 0x0 : 0x6; //NPC_EVT_TYPE
#endif
case BL_PET: return pcdb_checkid(status_get_viewdata(bl)->class_)?0x0:0x7; //NPC_PET_TYPE
case BL_HOM: return 0x8; //NPC_HOM_TYPE
@@ -980,7 +983,7 @@ static int clif_setlevel(struct block_list* bl) {
/*==========================================
* Prepares 'unit standing/spawning' packet
*------------------------------------------*/
static int clif_set_unit_idle(struct block_list* bl, unsigned char* buffer, bool spawn, bool option, unsigned int option_val)
static int clif_set_unit_idle(struct block_list* bl, unsigned char* buffer, bool spawn, bool option, bool walking, unsigned int option_val)
{
struct map_session_data* sd;
struct status_change* sc = status_get_sc(bl);
@@ -1035,7 +1038,7 @@ static int clif_set_unit_idle(struct block_list* bl, unsigned char* buffer, bool
#else
WBUFW(buf,2) = (uint16)((spawn ? 79 : 80)+strlen(name));
#endif
WBUFB(buf,4) = clif_bl_type(bl);
WBUFB(buf,4) = clif_bl_type(bl,walking);
offset+=3;
buf = WBUFP(buffer,offset);
#elif PACKETVER >= 20071106
@@ -1236,7 +1239,7 @@ static int clif_set_unit_walking(struct block_list* bl, struct unit_data* ud, un
buf = WBUFP(buffer,offset);
#endif
#if PACKETVER >= 20071106
WBUFB(buf, 2) = clif_bl_type(bl);
WBUFB(buf, 2) = clif_bl_type(bl,true);
offset++;
buf = WBUFP(buffer,offset);
#endif
@@ -1465,7 +1468,7 @@ void clif_weather(int16 m)
/**
* Main function to spawn a unit on the client (player/mob/pet/etc)
**/
int clif_spawn(struct block_list *bl)
int clif_spawn(struct block_list *bl, bool walking)
{
unsigned char buf[128];
struct view_data *vd;
@@ -1481,7 +1484,7 @@ int clif_spawn(struct block_list *bl)
if(bl->type == BL_NPC && !((TBL_NPC*)bl)->chat_id && (((TBL_NPC*)bl)->sc.option&OPTION_INVISIBLE))
return 0;
len = clif_set_unit_idle(bl, buf, (bl->type == BL_NPC && vd->dead_sit ? false : true), false, 0);
len = clif_set_unit_idle(bl, buf, (bl->type == BL_NPC && vd->dead_sit ? false : true), false, walking, 0);
clif_send(buf, len, bl, AREA_WOS);
if (disguised(bl))
clif_setdisguise(bl, buf, len);
@@ -4732,7 +4735,7 @@ void clif_getareachar_unit(struct map_session_data* sd,struct block_list *bl)
if (std::find(sd->cloaked_npc.begin(), sd->cloaked_npc.end(), nd->bl.id) != sd->cloaked_npc.end())
option_val ^= OPTION_CLOAK;
}
len = ( ud && ud->walktimer != INVALID_TIMER ) ? clif_set_unit_walking(bl,ud,buf) : clif_set_unit_idle(bl,buf,false,option,option_val);
len = ( ud && ud->walktimer != INVALID_TIMER ) ? clif_set_unit_walking(bl,ud,buf) : clif_set_unit_idle(bl,buf,false,option,false,option_val);
clif_send(buf,len,&sd->bl,SELF);
if (vd->cloth_color)

View File

@@ -589,7 +589,7 @@ void clif_clearflooritem(struct flooritem_data *fitem, int fd);
void clif_clearunit_single(int id, clr_type type, int fd);
void clif_clearunit_area(struct block_list* bl, clr_type type);
void clif_clearunit_delayed(struct block_list* bl, clr_type type, t_tick tick);
int clif_spawn(struct block_list *bl); //area
int clif_spawn(struct block_list *bl, bool walking = false); //area
void clif_walkok(struct map_session_data *sd); // self
void clif_move(struct unit_data *ud); //area
void clif_changemap(struct map_session_data *sd, short m, int x, int y); //self

View File

@@ -121,6 +121,13 @@ int unit_walktoxy_sub(struct block_list *bl)
((TBL_PC *)bl)->head_dir = 0;
clif_walkok((TBL_PC*)bl);
}
#if PACKETVER >= 20170726
// If this is a walking NPC and it will use a player sprite
else if( bl->type == BL_NPC && pcdb_checkid( status_get_viewdata( bl )->class_ ) ){
// Respawn the NPC as player unit
unit_refresh( bl, true );
#endif
}
clif_move(ud);
if(ud->walkpath.path_pos>=ud->walkpath.path_len)
@@ -410,6 +417,14 @@ static TIMER_FUNC(unit_walktoxy_timer){
ud->walktimer = INVALID_TIMER;
if (bl->x == ud->to_x && bl->y == ud->to_y) {
#if PACKETVER >= 20170726
// If this was a walking NPC and it used a player sprite
if( bl->type == BL_NPC && pcdb_checkid( status_get_viewdata( bl )->class_ ) ){
// Respawn the NPC as NPC unit
unit_refresh( bl, false );
}
#endif
if (ud->walk_done_event[0]){
char walk_done_event[EVENT_NAME_LENGTH];
@@ -3158,7 +3173,7 @@ int unit_remove_map_(struct block_list *bl, clr_type clrtype, const char* file,
* Refresh the area with a change in display of a unit.
* @bl: Object to update
*/
void unit_refresh(struct block_list *bl) {
void unit_refresh(struct block_list *bl, bool walking) {
nullpo_retv(bl);
if (bl->m < 0)
@@ -3170,7 +3185,7 @@ void unit_refresh(struct block_list *bl) {
// Probably need to use another flag or other way to refresh it
if (mapdata->users) {
clif_clearunit_area(bl, CLR_TRICKDEAD); // Fade out
clif_spawn(bl); // Fade in
clif_spawn(bl,walking); // Fade in
}
}

View File

@@ -161,7 +161,7 @@ void unit_dataset(struct block_list *bl);
// Remove unit
struct unit_data* unit_bl2ud(struct block_list *bl);
void unit_remove_map_pc(struct map_session_data *sd, clr_type clrtype);
void unit_refresh(struct block_list *bl);
void unit_refresh(struct block_list *bl, bool walking = false);
void unit_free_pc(struct map_session_data *sd);
#define unit_remove_map(bl,clrtype) unit_remove_map_(bl,clrtype,__FILE__,__LINE__,__func__)
int unit_remove_map_(struct block_list *bl, clr_type clrtype, const char* file, int line, const char* func);