diff --git a/src/map/mob.c b/src/map/mob.c index 980da6a2b1..f1fc875302 100644 --- a/src/map/mob.c +++ b/src/map/mob.c @@ -336,9 +336,49 @@ int mobdb_checkid(const int id) struct view_data * mob_get_viewdata(int mob_id) { if (mob_db(mob_id) == mob_dummy) - return 0; + return NULL; return &mob_db(mob_id)->vd; } + +/** + * Create unique view data associated to a spawned monster. + * @param md: Mob to adjust + */ +void mob_set_dynamic_viewdata( struct mob_data* md ){ + // If it is a valid monster and it has not already been created + if( md && !md->vd_changed ){ + // Allocate a dynamic entry + struct view_data* vd = (struct view_data*)aMalloc( sizeof( struct view_data ) ); + + // Copy the current values + memcpy( vd, md->vd, sizeof( struct view_data ) ); + + // Update the pointer to the new entry + md->vd = vd; + + // Flag it as changed so it is freed later on + md->vd_changed = true; + } +} + +/** + * Free any view data associated to a spawned monster. + * @param md: Mob to free + */ +void mob_free_dynamic_viewdata( struct mob_data* md ){ + // If it is a valid monster and it has already been allocated + if( md && md->vd_changed ){ + // Free it + aFree( md->vd ); + + // Remove the reference + md->vd = NULL; + + // Unflag it as changed + md->vd_changed = false; + } +} + /*========================================== * Cleans up mob-spawn data to make it "valid" *------------------------------------------*/ @@ -5189,6 +5229,9 @@ static void mob_load(void) mob_skill_db_set(); } +/** + * Initialize monster data + */ void mob_db_load(void){ memset(mob_db_data,0,sizeof(mob_db_data)); //Clear the array mob_db_data[0] = (struct mob_db*)aCalloc(1, sizeof (struct mob_db)); //This mob is used for random spawns @@ -5201,11 +5244,42 @@ void mob_db_load(void){ mob_load(); } +/** + * Apply the proper view data on monsters during mob_db reload. + * @param md: Mob to adjust + * @param args: va_list of arguments + * @return 0 + */ +static int mob_reload_sub( struct mob_data *md, va_list args ){ + if( md->bl.prev == NULL ){ + return 0; + } + + // If the view data was not overwritten manually + if( !md->vd_changed ){ + // Get the new view data from the mob database + md->vd = mob_get_viewdata(md->mob_id); + + // Respawn all mobs on client side so that they are displayed correctly(if their view id changed) + clif_clearunit_area(&md->bl, CLR_OUTSIGHT); + clif_spawn(&md->bl); + } + + return 0; +} + +/** + * Reload monster data + */ void mob_reload(void) { do_final_mob(); mob_db_load(); + map_foreachmob(mob_reload_sub); } +/** + * Clear spawn data for all monsters + */ void mob_clear_spawninfo() { //Clears spawn related information for a script reload. int i; diff --git a/src/map/mob.h b/src/map/mob.h index 36bedac84f..41adc25963 100644 --- a/src/map/mob.h +++ b/src/map/mob.h @@ -173,6 +173,7 @@ struct mob_data { struct block_list bl; struct unit_data ud; struct view_data *vd; + bool vd_changed; struct status_data status, *base_status; //Second one is in case of leveling up mobs, or tiny/large mobs. struct status_change sc; struct mob_db *db; //For quick data access (saves doing mob_db(md->mob_id) all the time) [Skotlex] @@ -298,6 +299,8 @@ int mobdb_searchname(const char *str); int mobdb_searchname_array(struct mob_db** data, int size, const char *str); int mobdb_checkid(const int id); struct view_data* mob_get_viewdata(int mob_id); +void mob_set_dynamic_viewdata( struct mob_data* md ); +void mob_free_dynamic_viewdata( struct mob_data* md ); struct mob_data *mob_once_spawn_sub(struct block_list *bl, int16 m, int16 x, int16 y, const char *mobname, int mob_id, const char *event, unsigned int size, unsigned int ai); diff --git a/src/map/script.c b/src/map/script.c index 269aea4c13..fb9d6401a6 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -17769,6 +17769,23 @@ BUILDIN_FUNC(setunitdata) md->base_status = (struct status_data*)aCalloc(1, sizeof(struct status_data)); memcpy(md->base_status, &md->db->status, sizeof(struct status_data)); } + + // Check if the view data will be modified + switch( type ){ + case UMOB_SEX: + //case UMOB_CLASS: // Called by status_set_viewdata + case UMOB_HAIRSTYLE: + case UMOB_HAIRCOLOR: + case UMOB_HEADBOTTOM: + case UMOB_HEADMIDDLE: + case UMOB_HEADTOP: + case UMOB_CLOTHCOLOR: + case UMOB_SHIELD: + case UMOB_WEAPON: + mob_set_dynamic_viewdata( md ); + break; + } + switch (type) { case UMOB_SIZE: md->base_status->size = (unsigned char)value; calc_status = true; break; case UMOB_LEVEL: md->level = (unsigned short)value; break; @@ -17782,8 +17799,8 @@ BUILDIN_FUNC(setunitdata) case UMOB_MODE: md->base_status->mode = (enum e_mode)value; calc_status = true; break; case UMOB_AI: md->special_state.ai = (enum mob_ai)value; break; case UMOB_SCOPTION: md->sc.option = (unsigned short)value; break; - case UMOB_SEX: md->vd->sex = (char)value; break; - case UMOB_CLASS: status_set_viewdata(bl, (unsigned short)value); break; + case UMOB_SEX: md->vd->sex = (char)value; clif_clearunit_area(bl, CLR_OUTSIGHT); clif_spawn(bl); break; + case UMOB_CLASS: status_set_viewdata(bl, (unsigned short)value); clif_clearunit_area(bl, CLR_OUTSIGHT); clif_spawn(bl); break; case UMOB_HAIRSTYLE: clif_changelook(bl, LOOK_HAIR, (unsigned short)value); break; case UMOB_HAIRCOLOR: clif_changelook(bl, LOOK_HAIR_COLOR, (unsigned short)value); break; case UMOB_HEADBOTTOM: clif_changelook(bl, LOOK_HEAD_BOTTOM, (unsigned short)value); break; diff --git a/src/map/status.c b/src/map/status.c index 4d13b2da7b..516aab0aca 100644 --- a/src/map/status.c +++ b/src/map/status.c @@ -7680,9 +7680,15 @@ void status_set_viewdata(struct block_list *bl, int class_) case BL_MOB: { TBL_MOB* md = (TBL_MOB*)bl; - if (vd) + if (vd){ + mob_free_dynamic_viewdata( md ); + md->vd = vd; - else + }else if( pcdb_checkid( class_ ) ){ + mob_set_dynamic_viewdata( md ); + + md->vd->class_ = class_; + }else ShowError("status_set_viewdata (MOB): No view data for class %d\n", class_); } break; diff --git a/src/map/unit.c b/src/map/unit.c index c6694f6567..ef713e1ed2 100644 --- a/src/map/unit.c +++ b/src/map/unit.c @@ -3305,6 +3305,8 @@ int unit_free(struct block_list *bl, clr_type clrtype) case BL_MOB: { struct mob_data *md = (struct mob_data*)bl; + mob_free_dynamic_viewdata( md ); + if( md->spawn_timer != INVALID_TIMER ) { delete_timer(md->spawn_timer,mob_delayspawn); md->spawn_timer = INVALID_TIMER;