namespace BombsAwayGame;
///
/// Represents a protagonist that chooses a standard (non-kamikaze) mission.
///
internal abstract class MissionSide : Side
{
///
/// Create instance using the given UI.
///
/// UI to use.
public MissionSide(IUserInterface ui)
: base(ui)
{
}
///
/// Reasonable upper bound for missions flown previously.
///
private const int MaxMissionCount = 160;
///
/// Choose a mission and attempt it. If attempt fails, perform an enemy counterattack.
///
public override void Play()
{
Mission mission = ChooseMission();
UI.Output(mission.Description);
int missionCount = MissionCountFromUI();
CommentOnMissionCount(missionCount);
AttemptMission(missionCount);
}
///
/// Choose a mission.
///
/// Mission chosen.
private Mission ChooseMission()
{
IList missions = AllMissions;
string[] missionNames = missions.Select(a => a.Name).ToArray();
int index = UI.Choose(ChooseMissionMessage, missionNames);
return missions[index];
}
///
/// Message to display when choosing a mission.
///
protected abstract string ChooseMissionMessage { get; }
///
/// All aviailable missions to choose from.
///
protected abstract IList AllMissions { get; }
///
/// Get mission count from UI. If mission count exceeds a reasonable maximum, ask UI again.
///
/// Mission count from UI.
private int MissionCountFromUI()
{
const string HowManyMissions = "HOW MANY MISSIONS HAVE YOU FLOWN?";
string inputMessage = HowManyMissions;
bool resultIsValid;
int result;
do
{
UI.Output(inputMessage);
result = UI.InputInteger();
if (result < 0)
{
UI.Output($"NUMBER OF MISSIONS CAN'T BE NEGATIVE.");
resultIsValid = false;
}
else if (result > MaxMissionCount)
{
resultIsValid = false;
UI.Output($"MISSIONS, NOT MILES...{MaxMissionCount} MISSIONS IS HIGH EVEN FOR OLD-TIMERS.");
inputMessage = "NOW THEN, " + HowManyMissions;
}
else
{
resultIsValid = true;
}
}
while (!resultIsValid);
return result;
}
///
/// Display a message about the given mission count, if it is unusually high or low.
///
/// Mission count to comment on.
private void CommentOnMissionCount(int missionCount)
{
if (missionCount >= 100)
{
UI.Output("THAT'S PUSHING THE ODDS!");
}
else if (missionCount < 25)
{
UI.Output("FRESH OUT OF TRAINING, EH?");
}
}
///
/// Attempt mission.
///
/// Number of missions previously flown. Higher mission counts will yield a higher probability of success.
private void AttemptMission(int missionCount)
{
if (missionCount < RandomInteger(0, MaxMissionCount))
{
MissedTarget();
}
else
{
MissionSucceeded();
}
}
///
/// Display message indicating that target was missed. Choose enemy artillery and perform a counterattack.
///
private void MissedTarget()
{
UI.Output("MISSED TARGET BY " + (2 + RandomInteger(0, 30)) + " MILES!");
UI.Output("NOW YOU'RE REALLY IN FOR IT !!");
// Choose enemy and counterattack.
EnemyArtillery enemyArtillery = ChooseEnemyArtillery();
if (enemyArtillery == Missiles)
{
EnemyCounterattack(enemyArtillery, hitRatePercent: 0);
}
else
{
int hitRatePercent = EnemyHitRatePercentFromUI();
if (hitRatePercent < MinEnemyHitRatePercent)
{
UI.Output("YOU LIE, BUT YOU'LL PAY...");
MissionFailed();
}
else
{
EnemyCounterattack(enemyArtillery, hitRatePercent);
}
}
}
///
/// Choose enemy artillery from UI.
///
/// Artillery chosen.
private EnemyArtillery ChooseEnemyArtillery()
{
EnemyArtillery[] artilleries = new EnemyArtillery[] { Guns, Missiles, Both };
string[] artilleryNames = artilleries.Select(a => a.Name).ToArray();
int index = UI.Choose("DOES THE ENEMY HAVE", artilleryNames);
return artilleries[index];
}
///
/// Minimum allowed hit rate percent.
///
private const int MinEnemyHitRatePercent = 10;
///
/// Maximum allowed hit rate percent.
///
private const int MaxEnemyHitRatePercent = 50;
///
/// Get the enemy hit rate percent from UI. Value must be between zero and .
/// If value is less than , mission fails automatically because the user is
/// assumed to be untruthful.
///
/// Enemy hit rate percent from UI.
private int EnemyHitRatePercentFromUI()
{
UI.Output($"WHAT'S THE PERCENT HIT RATE OF ENEMY GUNNERS ({MinEnemyHitRatePercent} TO {MaxEnemyHitRatePercent})");
bool resultIsValid;
int result;
do
{
result = UI.InputInteger();
// Let them enter a number below the stated minimum, as they will be caught and punished.
if (0 <= result && result <= MaxEnemyHitRatePercent)
{
resultIsValid = true;
}
else
{
resultIsValid = false;
UI.Output($"NUMBER MUST BE FROM {MinEnemyHitRatePercent} TO {MaxEnemyHitRatePercent}");
}
}
while (!resultIsValid);
return result;
}
}