← Back to team overview

widelands-dev team mailing list archive

[Merge] lp:~widelands-dev/widelands/bug-1817664-overlooping-eva_fail into lp:widelands

 

Benedikt Straub has proposed merging lp:~widelands-dev/widelands/bug-1817664-overlooping-eva_fail into lp:widelands.

Commit message:
Shorten a soldier's last evade_failure animation if the soldier is about to die to prevent overlooping. The health bar falls gradually during an attack instead of one-shot.

Requested reviews:
  Widelands Developers (widelands-dev)
Related bugs:
  Bug #1817664 in widelands: "Bug in fight"
  https://bugs.launchpad.net/widelands/+bug/1817664

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/bug-1817664-overlooping-eva_fail/+merge/363679

During battles, the evade_failure animation may overloop. If the soldier dies immediately afterwards, this can look like he is attacking while he dies (especially noticeable with ATL vs FRI). Shorten the last eva_fail anim if the soldier is going to die.

Debugging this, I also implemented that the soldier´s health bar falls gradually while he is being attacked, instead of one-shot half a second after the attack is visually over.
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/bug-1817664-overlooping-eva_fail into lp:widelands.
=== modified file 'src/logic/map_objects/tribes/battle.cc'
--- src/logic/map_objects/tribes/battle.cc	2019-02-23 11:00:49 +0000
+++ src/logic/map_objects/tribes/battle.cc	2019-02-26 18:55:11 +0000
@@ -137,6 +137,13 @@
 	return other_soldier;
 }
 
+uint32_t Battle::get_pending_damage(const Soldier* for_whom) const {
+	if (for_whom == (first_strikes_ ? first_ : second_)) {
+		return damage_;
+	}
+	return 0;
+}
+
 //  TODO(unknown): Couldn't this code be simplified tremendously by doing all scheduling
 //  for one soldier and letting the other sleep until the battle is over?
 //  Could be, but we need to be able change the animations of the soldiers
@@ -167,10 +174,11 @@
 	// Apply pending damage
 	if (damage_ && oneReadyToFight) {
 		// Current attacker is last defender, so damage goes to current attacker
-		if (first_strikes_)
+		if (first_strikes_) {
 			first_->damage(damage_);
-		else
+		} else {
 			second_->damage(damage_);
+		}
 		damage_ = 0;
 	}
 
@@ -184,8 +192,9 @@
 		return schedule_destroy(game);
 	}
 
-	if (!first_ || !second_)
+	if (!first_ || !second_) {
 		return soldier.skip_act();
+	}
 
 	// Here is a timeout to prevent battle freezes
 	if (waitingForOpponent && (game.get_gametime() - creationtime_) > 90 * 1000) {
@@ -248,10 +257,12 @@
 	molog("[battle] (%u) vs (%u) is %d, first strikes %d, last hit %d\n", soldier.serial(),
 	      opponent(soldier)->serial(), this_soldier_is, first_strikes_, last_attack_hits_);
 
+	bool shorten = false;
 	if (this_soldier_is == 1) {
 		if (first_strikes_) {
 			if (last_attack_hits_) {
 				what_anim = "evade_failure_e";
+				shorten = true;
 			} else {
 				what_anim = "evade_success_e";
 			}
@@ -272,13 +283,17 @@
 		} else {
 			if (last_attack_hits_) {
 				what_anim = "evade_failure_w";
+				shorten = true;
 			} else {
 				what_anim = "evade_success_w";
 			}
 		}
 	}
+	// If the soldier will die as soon as the animation is complete, don't
+	// show it for the full length to prevent overlooping (bug 1817664)
+	shorten &= damage_ >= soldier.get_current_health();
 	molog("[battle] Starting animation %s for soldier %d\n", what_anim.c_str(), soldier.serial());
-	soldier.start_task_idle(game, soldier.descr().get_rand_anim(game, what_anim.c_str()), 1000);
+	soldier.start_task_idle(game, soldier.descr().get_rand_anim(game, what_anim.c_str()), shorten ? 850 : 1000);
 }
 
 void Battle::calculate_round(Game& game) {

=== modified file 'src/logic/map_objects/tribes/battle.h'
--- src/logic/map_objects/tribes/battle.h	2019-02-23 11:00:49 +0000
+++ src/logic/map_objects/tribes/battle.h	2019-02-26 18:55:11 +0000
@@ -74,6 +74,8 @@
 		return second_;
 	}
 
+	uint32_t get_pending_damage(const Soldier* for_whom) const;
+
 	// Returns the other soldier involved in this battle. CHECKs that the given
 	// soldier is participating in this battle. Can return nullptr, probably when the
 	// opponent has died.

=== modified file 'src/logic/map_objects/tribes/soldier.cc'
--- src/logic/map_objects/tribes/soldier.cc	2019-02-23 11:00:49 +0000
+++ src/logic/map_objects/tribes/soldier.cc	2019-02-26 18:55:11 +0000
@@ -505,7 +505,26 @@
 	                         kSoldierHealthBarWidth * 2 * scale, 5 * scale);
 	dst->fill_rect(energy_outer, RGBColor(255, 255, 255));
 
-	int health_width = 2 * (kSoldierHealthBarWidth - 1) * current_health_ / get_max_health();
+	uint32_t health_to_show = current_health_;
+	if (battle_) {
+		uint32_t pending_damage = battle_->get_pending_damage(this);
+		if (pending_damage) {
+			int32_t timeshift = owner().egbase().get_gametime() - get_animstart();
+			if (timeshift < 0) {
+				timeshift = 0;
+			} else if (timeshift > 1000) {
+				timeshift = 1000;
+			}
+			pending_damage *= timeshift;
+			pending_damage /= 1000;
+			if (pending_damage > health_to_show) {
+				health_to_show = 0;
+			} else {
+				health_to_show -= pending_damage;
+			}
+		}
+	}
+	int health_width = 2 * (kSoldierHealthBarWidth - 1) * health_to_show / get_max_health();
 	Recti energy_inner(draw_position + Vector2i(-kSoldierHealthBarWidth + 1, 1) * scale,
 	                   health_width * scale, 3 * scale);
 	Recti energy_complement(energy_inner.origin() + Vector2i(health_width, 0) * scale,


Follow ups