ソースを参照

Monster Damage Log Improvements (#8895)

- If a monster does a normal attack we now log the damage it dealt (will later be used for MVP reward)
- Official DAMAGELOG_SIZE of 20
- When there are more than 20 entries in the damage log, the oldest entry will be removed (this also updates the "first player")
- Related to #8847

---------

Co-authored-by: Lemongrass3110
Playtester 4 ヶ月 前
コミット
2a7c96b873
5 ファイル変更45 行追加24 行削除
  1. 3 1
      src/map/map.hpp
  2. 30 19
      src/map/mob.cpp
  3. 3 2
      src/map/mob.hpp
  4. 1 0
      src/map/pc.cpp
  5. 8 2
      src/map/status.cpp

+ 3 - 1
src/map/map.hpp

@@ -58,7 +58,9 @@ void map_msg_reload(void);
 
 #define MAX_NPC_PER_MAP 512
 #define AREA_SIZE battle_config.area_size
-#define DAMAGELOG_SIZE 30
+#ifndef DAMAGELOG_SIZE 
+	#define DAMAGELOG_SIZE 20
+#endif
 #define LOOTITEM_SIZE 10
 #define MAX_MOBSKILL 50		//Max 128, see mob skill_idx type if need this higher
 #define MAX_MOB_LIST_PER_MAP 128

+ 30 - 19
src/map/mob.cpp

@@ -1985,9 +1985,6 @@ static bool mob_ai_sub_hard(struct mob_data *md, t_tick tick)
 			// Target within range and potentially able to use normal attack, engage
 			if (md->ud.target != tbl->id || md->ud.attacktimer == INVALID_TIMER)
 			{ //Only attack if no more attack delay left
-				if (tbl->type == BL_PC)
-					mob_log_damage(md, tbl, 0); //Log interaction (counts as 'attacker' for the exp bonus)
-
 				if (!(mode&MD_RANDOMTARGET))
 					unit_attack(&md->bl,tbl->id,1);
 				else { // Attack once and find a new random target
@@ -2403,14 +2400,16 @@ TIMER_FUNC(mob_respawn){
 	return 1;
 }
 
-void mob_log_damage(struct mob_data *md, struct block_list *src, int32 damage)
+void mob_log_damage(mob_data* md, block_list* src, int32 damage, int32 damage_tanked)
 {
 	uint32 char_id = 0;
 	int32 flag = MDLF_NORMAL;
 
 	if( damage < 0 )
 		return; //Do nothing for absorbed damage.
-	if( !damage && !(src->type&DEFAULT_ENEMY_TYPE(md)) )
+	if (damage_tanked < 0)
+		return; //Just to make sure we don't subtract it (should not happen)
+	if( !damage && !damage_tanked && !(src->type&DEFAULT_ENEMY_TYPE(md)) )
 		return; //Do not log non-damaging effects from non-enemies.
 
 	switch( src->type )
@@ -2493,32 +2492,44 @@ void mob_log_damage(struct mob_data *md, struct block_list *src, int32 damage)
 
 	if( char_id )
 	{ //Log damage...
-		int32 i,minpos;
-		uint32 mindmg;
-		for(i=0,minpos=DAMAGELOG_SIZE-1,mindmg=UINT_MAX;i<DAMAGELOG_SIZE;i++){
+		size_t i;
+		for (i = 0; i < DAMAGELOG_SIZE; i++) {
+			// Character is already in damage log
 			if(md->dmglog[i].id==char_id &&
 				md->dmglog[i].flag==flag)
 				break;
-			if(md->dmglog[i].id==0) {	//Store data in first empty slot.
+			// Store data in first empty slot.
+			if (md->dmglog[i].id == 0) {
 				md->dmglog[i].id  = char_id;
 				md->dmglog[i].flag= flag;
+				// Damage is added outside the loop, we reset it here to be safe
+				md->dmglog[i].dmg = 0;
+				md->dmglog[i].dmg_tanked = 0;
 
 				if( md->get_bosstype() == BOSSTYPE_MVP )
 					pc_damage_log_add(map_charid2sd(char_id),md->bl.id);
 				break;
 			}
-			if(md->dmglog[i].dmg<mindmg && i)
-			{	//Never overwrite first hit slot (he gets double exp bonus)
-				minpos=i;
-				mindmg=md->dmglog[i].dmg;
-			}
 		}
-		if(i<DAMAGELOG_SIZE)
-			md->dmglog[i].dmg+=damage;
+		// Character or empty slot was found, just add damage to it
+		if (i < DAMAGELOG_SIZE) {
+			md->dmglog[i].dmg += damage;
+			md->dmglog[i].dmg_tanked += damage_tanked;
+		}
 		else {
-			md->dmglog[minpos].id  = char_id;
-			md->dmglog[minpos].flag= flag;
-			md->dmglog[minpos].dmg = damage;
+			// Damage log is full, remove oldest entry
+			for (i = 0; i < DAMAGELOG_SIZE-1; i++) {
+				md->dmglog[i].id = md->dmglog[i+1].id;
+				md->dmglog[i].flag = md->dmglog[i+1].flag;
+				md->dmglog[i].dmg = md->dmglog[i+1].dmg;
+				md->dmglog[i].dmg_tanked = md->dmglog[i+1].dmg_tanked;
+			}
+
+			// Add new character to damage log at last (newest) position
+			md->dmglog[DAMAGELOG_SIZE-1].id  = char_id;
+			md->dmglog[DAMAGELOG_SIZE-1].flag= flag;
+			md->dmglog[DAMAGELOG_SIZE-1].dmg = damage;
+			md->dmglog[DAMAGELOG_SIZE-1].dmg_tanked = damage_tanked;
 
 			if( md->get_bosstype() == BOSSTYPE_MVP )
 				pc_damage_log_add(map_charid2sd(char_id),md->bl.id);

+ 3 - 2
src/map/mob.hpp

@@ -350,7 +350,8 @@ struct mob_data {
 	struct s_dmglog {
 		int32 id; //char id
 		uint32 dmg;
-		uint32 flag : 2; //0: Normal. 1: Homunc exp. 2: Pet exp
+		uint32 dmg_tanked; //Damage tanked from normal attacks of the monster, MVP is the player with highest dmg+dmg_tanked
+		uint32 flag : 2; //0: Normal. 1: Homunc exp. 2: Pet exp. 3: Self.
 	} dmglog[DAMAGELOG_SIZE];
 	uint32 spotted_log[DAMAGELOG_SIZE];
 	struct spawn_data *spawn; //Spawn data.
@@ -515,7 +516,7 @@ int32 mob_spawn(struct mob_data *md);
 TIMER_FUNC(mob_delayspawn);
 int32 mob_setdelayspawn(struct mob_data *md);
 int32 mob_parse_dataset(struct spawn_data *data);
-void mob_log_damage(struct mob_data *md, struct block_list *src, int32 damage);
+void mob_log_damage(mob_data* md, block_list* src, int32 damage, int32 damage_tanked = 0);
 void mob_damage(struct mob_data *md, struct block_list *src, int32 damage);
 int32 mob_dead(struct mob_data *md, struct block_list *src, int32 type);
 void mob_revive(struct mob_data *md, uint32 hp);

+ 1 - 0
src/map/pc.cpp

@@ -14497,6 +14497,7 @@ static void pc_clear_log_damage_sub(uint32 char_id, struct mob_data *md)
 	if (i < DAMAGELOG_SIZE) {
 		md->dmglog[i].id = 0;
 		md->dmglog[i].dmg = 0;
+		md->dmglog[i].dmg_tanked = 0;
 		md->dmglog[i].flag = 0;
 	}
 }

+ 8 - 2
src/map/status.cpp

@@ -1585,9 +1585,10 @@ int32 status_damage(struct block_list *src,struct block_list *target,int64 dhp,
 	dhp = cap_value(dhp, INT_MIN, INT_MAX);
 	switch (target->type) {
 	case BL_PC:
-		pc_damage(reinterpret_cast<map_session_data*>(target), src, hp, sp, ap); break;
+		pc_damage(reinterpret_cast<map_session_data*>(target), src, hp, sp, ap);
+		break;
 	case BL_MOB:
-		mob_damage(reinterpret_cast<mob_data*>(target), src, (int32)dhp);
+		mob_damage(reinterpret_cast<mob_data*>(target), src, static_cast<int32>(dhp));
 		break;
 	case BL_HOM:
 		hom_heal(reinterpret_cast<homun_data&>(*target), hp != 0, sp != 0);
@@ -1600,6 +1601,11 @@ int32 status_damage(struct block_list *src,struct block_list *target,int64 dhp,
 		break;
 	}
 
+	// Normal attack damage is logged in the monster's dmglog as attack damage
+	// This counts as exp tap and is used for determining the MVP
+	if (src && src->type == BL_MOB && skill_id == 0)
+		mob_log_damage(reinterpret_cast<mob_data*>(src), target, 0, static_cast<int32>(dhp));
+
 	if( src && target->type == BL_PC && ((TBL_PC*)target)->disguise ) { // Stop walking when attacked in disguise to prevent walk-delay bug
 		unit_stop_walking( target, 1 );
 	}