From b291dacfb5d89e13d849c048005b39d222183334 Mon Sep 17 00:00:00 2001
From: cybermind <iddqd_mail@mail.ru>
Date: Tue, 29 May 2012 19:28:27 +0600
Subject: [PATCH] [+] Added ability not to overlap the unit voice if unit
 currently plays a voice. [-] More check for valid orders

---
 src/action/actions.cpp     | 11 ++++++++--
 src/action/command.cpp     |  2 +-
 src/ai/ai.cpp              | 34 ++++++++++++++++---------------
 src/ai/ai_force.cpp        |  2 +-
 src/include/sound_server.h | 12 ++++++++++-
 src/sound/sound.cpp        | 17 +++++++++++++---
 src/sound/sound_server.cpp | 41 +++++++++++++++++++++++++++++++++++---
 src/unit/unit.cpp          | 39 +++++++++++++++++++-----------------
 8 files changed, 113 insertions(+), 45 deletions(-)

diff --git a/src/action/actions.cpp b/src/action/actions.cpp
index 93565cf24..477c4ff34 100644
--- a/src/action/actions.cpp
+++ b/src/action/actions.cpp
@@ -348,8 +348,15 @@ static void HandleUnitAction(CUnit &unit)
 				return;
 			}
 
-			delete unit.Orders[0];
-			unit.Orders.erase(unit.Orders.begin());
+			do {
+				delete unit.Orders[0];
+				unit.Orders.erase(unit.Orders.begin());
+			} while (unit.Orders[0]->IsValid() == false && unit.Orders.size() > 1);
+
+			if (unit.Orders[0]->IsValid() == false && unit.Orders.size() == 1) {
+				delete unit.Orders[0];
+				unit.Orders[0] = COrder::NewActionStill();
+			}
 
 			unit.State = 0;
 			unit.Wait = 0;
diff --git a/src/action/command.cpp b/src/action/command.cpp
index d3afac0ed..c665e9e1d 100644
--- a/src/action/command.cpp
+++ b/src/action/command.cpp
@@ -311,7 +311,7 @@ void CommandAttack(CUnit &unit, const Vec2i &pos, CUnit *target, int flush)
 			return;
 		}
 	}
-	if (target) {
+	if (target && target->IsAlive()) {
 		*order = COrder::NewActionAttack(unit, *target);
 	} else {
 		*order = COrder::NewActionAttack(unit, pos);
diff --git a/src/ai/ai.cpp b/src/ai/ai.cpp
index 8f7e34186..ff0c02390 100644
--- a/src/ai/ai.cpp
+++ b/src/ai/ai.cpp
@@ -697,28 +697,30 @@ void AiHelpMe(const CUnit *attacker, CUnit &defender)
 			// if brother is idle or attack no-agressive target and
 			// can attack our attacker then ask for help
 			// FIXME ad support for help from Coward type units
-			if (aiunit.IsAgressive() && CanTarget(aiunit.Type, attacker->Type)) {
-				bool shouldAttack = aiunit.IsIdle();
+			if (aiunit.IsAgressive() && CanTarget(aiunit.Type, attacker->Type)
+				&& aiunit.CurrentOrder()->GetGoal() != attacker) {
+					bool shouldAttack = aiunit.IsIdle();
 
-				if (aiunit.CurrentAction() == UnitActionAttack) {
-					const COrder_Attack &orderAttack = *static_cast<COrder_Attack *>(aiunit.CurrentOrder());
-					const CUnit *oldGoal = orderAttack.GetGoal();
+					if (aiunit.CurrentAction() == UnitActionAttack) {
+						const COrder_Attack &orderAttack = *static_cast<COrder_Attack *>(aiunit.CurrentOrder());
+						const CUnit *oldGoal = orderAttack.GetGoal();
 
-					if (oldGoal == NULL || oldGoal->IsAgressive() == false
-						|| ThreatCalculate(defender, *attacker) < ThreatCalculate(defender, *oldGoal)) {
-						shouldAttack = true;
+						if (oldGoal == NULL || oldGoal->IsAgressive() == false
+							|| (ThreatCalculate(defender, *attacker) < ThreatCalculate(defender, *oldGoal)
+							&& aiunit.MapDistanceTo(defender) <= aiunit.Stats->Variables[ATTACKRANGE_INDEX].Max)) {
+								shouldAttack = true;
+						}
 					}
-				}
 
-				if (shouldAttack) {
-					CommandAttack(aiunit, attacker->tilePos, const_cast<CUnit *>(attacker), FlushCommands);
-					COrder *savedOrder = COrder::NewActionAttack(aiunit, attacker->tilePos);
+					if (shouldAttack) {
+						CommandAttack(aiunit, attacker->tilePos, const_cast<CUnit *>(attacker), FlushCommands);
+						COrder *savedOrder = COrder::NewActionAttack(aiunit, attacker->tilePos);
 
-					if (aiunit.StoreOrder(savedOrder) == false) {
-						delete savedOrder;
-						savedOrder = NULL;
+						if (aiunit.StoreOrder(savedOrder) == false) {
+							delete savedOrder;
+							savedOrder = NULL;
+						}
 					}
-				}
 			}
 		}
 		if (!aiForce.Defending && aiForce.State > 0) {
diff --git a/src/ai/ai_force.cpp b/src/ai/ai_force.cpp
index c0827b9cd..7aeb4056e 100644
--- a/src/ai/ai_force.cpp
+++ b/src/ai/ai_force.cpp
@@ -286,7 +286,7 @@ void AiForce::Attack(const Vec2i &pos)
 		// Remember the original force position so we can return there after attack
 		if (this->Role == AiForceRoleDefend
 			|| (this->Role == AiForceRoleAttack && this->State == AiForceAttackingState_Waiting)) {
-			this->HomePos = this->Units[0]->tilePos;
+			this->HomePos = this->Units[this->Units.size()-1]->tilePos;
 		}
 		this->Attacking = true;
 	}
diff --git a/src/include/sound_server.h b/src/include/sound_server.h
index b8d32aa91..0a164d7a1 100644
--- a/src/include/sound_server.h
+++ b/src/include/sound_server.h
@@ -33,6 +33,12 @@
 
 //@{
 
+/*----------------------------------------------------------------------------
+--  Includes
+----------------------------------------------------------------------------*/
+
+#include "sound.h"
+
 /*----------------------------------------------------------------------------
 --  Definitons
 ----------------------------------------------------------------------------*/
@@ -93,10 +99,14 @@ extern void StopChannel(int channel);
 /// Stop all channels
 extern void StopAllChannels();
 
+/// Check if this unit plays some sound
+extern bool UnitSoundIsPlaying(Origin *origin);
+/// Check, if this sample is already playing
+extern bool SampleIsPlaying(CSample *sample);
 /// Load a sample
 extern CSample *LoadSample(const std::string &name);
 /// Play a sample
-extern int PlaySample(CSample *sample);
+extern int PlaySample(CSample *sample, Origin *origin = NULL);
 /// Play a sound file
 extern int PlaySoundFile(const std::string &name);
 
diff --git a/src/sound/sound.cpp b/src/sound/sound.cpp
index 657eda4cc..7807ea458 100644
--- a/src/sound/sound.cpp
+++ b/src/sound/sound.cpp
@@ -106,7 +106,7 @@ static CSample *ChooseSample(CSound *sound, bool selection, Origin &source)
 	CSample *result = NULL;
 
 	if (!sound || !SoundEnabled()) {
-		return NULL;
+			return NULL;
 	}
 
 	if (sound->Number == TWO_GROUPS) {
@@ -276,7 +276,11 @@ void PlayUnitSound(const CUnit &unit, UnitVoiceGroup voice)
 	bool selection = (voice == VoiceSelected || voice == VoiceBuilding);
 	Origin source = {&unit, unit.Slot};
 
-	int channel = PlaySample(ChooseSample(sound, selection, source));
+	if (UnitSoundIsPlaying(&source)) {
+		return;
+	}
+
+	int channel = PlaySample(ChooseSample(sound, selection, source), &source);
 	if (channel == -1) {
 		return;
 	}
@@ -295,6 +299,7 @@ void PlayUnitSound(const CUnit &unit, UnitVoiceGroup voice)
 void PlayUnitSound(const CUnit &unit, CSound *sound)
 {
 	Origin source = {&unit, unit.Slot};
+
 	int channel = PlaySample(ChooseSample(sound, false, source));
 	if (channel == -1) {
 		return;
@@ -336,7 +341,13 @@ void PlayGameSound(CSound *sound, unsigned char volume)
 {
 	Origin source = {NULL, 0};
 
-	int channel = PlaySample(ChooseSample(sound, false, source));
+	CSample *sample = ChooseSample(sound, false, source);
+
+	if (SampleIsPlaying(sample)) {
+		return;
+	}
+
+	int channel = PlaySample(sample);
 	if (channel == -1) {
 		return;
 	}
diff --git a/src/sound/sound_server.cpp b/src/sound/sound_server.cpp
index 4a7eb9b78..caba693b5 100644
--- a/src/sound/sound_server.cpp
+++ b/src/sound/sound_server.cpp
@@ -64,6 +64,7 @@ static bool EffectsEnabled = true;
 /// Channels for sound effects and unit speach
 struct SoundChannel {
 	CSample *Sample;       /// sample to play
+	Origin *Unit;          /// pointer to unit, who plays the sound, if any
 	unsigned char Volume;  /// Volume of this channel
 	signed char Stereo;    /// stereo location of sound (-128 left, 0 center, 127 right)
 
@@ -346,6 +347,30 @@ static void FillAudio(void *, Uint8 *stream, int len)
 --  Effects
 ----------------------------------------------------------------------------*/
 
+/**
+**  Check if this sound is already playing
+*/
+bool SampleIsPlaying(CSample *sample)
+{
+	for (int i = 0; i < MaxChannels; ++i) {
+		if (Channels[i].Sample == sample && Channels[i].Playing) {
+			return true;
+		}
+	}
+	return false;
+}
+
+bool UnitSoundIsPlaying(Origin *origin)
+{
+	for (int i = 0; i < MaxChannels; ++i) {
+		if (origin && Channels[i].Unit && origin->Id && Channels[i].Unit->Id
+			&& origin->Id == Channels[i].Unit->Id && Channels[i].Playing) {
+				return true;
+		}
+	}
+	return false;
+}
+
 /**
 **  A channel is finished playing
 */
@@ -355,6 +380,10 @@ static void ChannelFinished(int channel)
 		Channels[channel].FinishedCallback(channel);
 	}
 
+	if (Channels[channel].Unit) {
+		delete Channels[channel].Unit;
+		Channels[channel].Unit = NULL;
+	}
 	Channels[channel].Playing = false;
 	Channels[channel].Point = NextFreeChannel;
 	NextFreeChannel = channel;
@@ -363,7 +392,7 @@ static void ChannelFinished(int channel)
 /**
 **  Put a sound request in the next free channel.
 */
-static int FillChannel(CSample *sample, unsigned char volume, char stereo)
+static int FillChannel(CSample *sample, unsigned char volume, char stereo, Origin *origin)
 {
 	Assert(NextFreeChannel < MaxChannels);
 
@@ -376,6 +405,12 @@ static int FillChannel(CSample *sample, unsigned char volume, char stereo)
 	Channels[NextFreeChannel].Sample = sample;
 	Channels[NextFreeChannel].Stereo = stereo;
 	Channels[NextFreeChannel].FinishedCallback = NULL;
+	if (origin && origin->Base) {
+		Origin *source = new Origin;
+		source->Base = origin->Base;
+		source->Id = origin->Id;
+		Channels[NextFreeChannel].Unit = source;
+	}
 
 	NextFreeChannel = next_free;
 
@@ -547,14 +582,14 @@ CSample *LoadSample(const std::string &name)
 **
 **  @return        Channel number, -1 for error
 */
-int PlaySample(CSample *sample)
+int PlaySample(CSample *sample, Origin *origin)
 {
 	int channel = -1;
 
 	SDL_LockAudio();
 
 	if (SoundEnabled() && EffectsEnabled && sample && NextFreeChannel != MaxChannels) {
-		channel = FillChannel(sample, EffectsVolume, 0);
+			channel = FillChannel(sample, EffectsVolume, 0, origin);
 	}
 
 	SDL_UnlockAudio();
diff --git a/src/unit/unit.cpp b/src/unit/unit.cpp
index dae4efb56..5adac60dd 100644
--- a/src/unit/unit.cpp
+++ b/src/unit/unit.cpp
@@ -404,7 +404,7 @@ bool CUnit::StoreOrder(COrder *order)
 {
 	Assert(order);
 
-	if (order && order->Finished == true) {
+	if (order && order->Finished == true && order->IsValid() == false) {
 		return false;
 	}
 	if (this->SavedOrder != NULL) {
@@ -3023,27 +3023,30 @@ void HitUnit(CUnit *attacker, CUnit &target, int damage)
 		}
 
 		// Calculate the best target we could attack
-		best = oldgoal ? oldgoal : (goal ? goal : attacker);
-		if (best && best != attacker) {
-			if (goal && ((goal->IsAgressive() && best->IsAgressive() == false)
-						 || (ThreatCalculate(target, *goal) < ThreatCalculate(target, *best)))) {
+		if (!best || (goal && ((goal->IsAgressive() && best->IsAgressive() == false)
+			|| (ThreatCalculate(target, *goal) < ThreatCalculate(target, *best))))) {
 				best = goal;
-			}
-			if (!RevealAttacker && (best->IsAgressive() == false || ThreatCalculate(target, *attacker) < ThreatCalculate(target, *best))) {
+		}
+		if (CanTarget(target.Type, attacker->Type) && (!best || (attacker && goal != attacker
+			&& ((attacker->IsAgressive() && best->IsAgressive() == false)
+			|| (ThreatCalculate(target, *attacker) < ThreatCalculate(target, *best)))))) {
 				best = attacker;
+		}
+		if (best) {
+			if (target.MapDistanceTo(*best) <= target.Stats->Variables[ATTACKRANGE_INDEX].Max) {
+				CommandAttack(target, best->tilePos, best, FlushCommands);
+			} else {
+				CommandAttack(target, best->tilePos, NoUnitP, FlushCommands);
+			}
+			// Set threshold value only for agressive units
+			if (best->IsAgressive()) {
+				target.Threshold = threshold;
+			}
+			if (target.StoreOrder(savedOrder) == false) {
+				delete savedOrder;
+				savedOrder = NULL;
 			}
 		}
-		CommandAttack(target, best->tilePos, NoUnitP, FlushCommands);
-
-		// Set threshold value only for agressive units
-		if (best->IsAgressive()) {
-			target.Threshold = threshold;
-		}
-		if (target.StoreOrder(savedOrder) == false) {
-			delete savedOrder;
-			savedOrder = NULL;
-		}
-		return;
 	}
 
 	/*