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; } }