MessageBox.Show being displayed too fast - c#

I'm trying to create my own version of "Fleet battle", but I'm stuck at 99.9%. And here is why. After a ship was destroyed, its color should change to red and then a MessageBox "You won" or "You lost" should come. So now, if the AI destroys my last ship, everything is going fine, all the ships are becoming red one by one and then comes this "You lost" message. But in the opposite case - if I win, the AI ships are becoming red, but the last cell of the last ship becomes red after I click "OK" at the MessageBox "You won". I'm using the same algorithm in both cases and wondering what I'm doing wrong with it. Here is the AI part:
// AI hits a ship
if ((arrayOccupiedCellsBattlefield1[rowIndex, columnIndex] == "1") || (arrayOccupiedCellsBattlefield1[rowIndex, columnIndex] == "2") ||
(arrayOccupiedCellsBattlefield1[rowIndex, columnIndex] == "3") || (arrayOccupiedCellsBattlefield1[rowIndex, columnIndex] == "4"))
{
AIHuntingAShip = true;
dataGridView1.Rows[rowIndex].Cells[columnIndex].Value = " X";
arrayShipsHitbyAIMap[rowIndex, columnIndex] = " X"; // AI marks the burning enemy ships
dataGridView1.Rows[rowIndex].Cells[columnIndex].Style.BackColor = Color.Red;
arrayAITakenShots[transformCoordinatesToCellNumber(rowIndex, columnIndex)] = transformCoordinatesToCellNumber(rowIndex, columnIndex);
storeRowIndex = rowIndex;
storeColumnIndex = columnIndex;
arrayShipsHitbyAIXY[detectedShipLength] = transformCoordinatesToCellNumber(rowIndex, columnIndex);
detectedShipLength++;
if (detectedShipLength == int.Parse(arrayOccupiedCellsBattlefield1[rowIndex, columnIndex])) // AI destroys a ship (the array stores the length of the ship in each of the ship's cells)
{
for (int i = 0; i < detectedShipLength; i++) // placing " o"s around the destroyed ship
{
restrictArea(int.Parse(tranformCellNumberToCoordinates(arrayShipsHitbyAIXY[i])[0].ToString()),
int.Parse(tranformCellNumberToCoordinates(arrayShipsHitbyAIXY[i])[1].ToString()),
dataGridView1,
arrayShipsHitbyAIMap,
arrayAITakenShots);
}
detectedShipLength = 0;
AIHuntingAShip = false;
playerShips--;
textBox6.Text = playerShips.ToString();
if (playerShips == 0)
{
MessageBox.Show("You lost!", "Try it again!", MessageBoxButtons.OK);
dataGridView2.Enabled = false;
}
}
and here is my part:
// player hits a ship
if ((arrayOccupiedCellsBattlefield2[e.RowIndex, e.ColumnIndex] != null) && (arrayOccupiedCellsBattlefield2[e.RowIndex, e.ColumnIndex] != " ."))
{
dataGridView2.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = " X";
arrayShipsHitbyPlayerMap[e.RowIndex, e.ColumnIndex] = " X";
dataGridView2.Rows[e.RowIndex].Cells[e.ColumnIndex].Style.BackColor = Color.Red;
arrayPlayerTakenShots[transformCoordinatesToCellNumber(e.RowIndex, e.ColumnIndex)] = transformCoordinatesToCellNumber(e.RowIndex, e.ColumnIndex);
int shipIdent = int.Parse(arrayOccupiedCellsBattlefield2[e.RowIndex, e.ColumnIndex][0].ToString()); // ship ID
int playersShipLength = int.Parse(arrayOccupiedCellsBattlefield2[e.RowIndex, e.ColumnIndex][1].ToString()); // ship length
arrayForPlayersShipsXY[shipIdent] += e.RowIndex.ToString() + e.ColumnIndex.ToString() + ";"; // save the coordinates of this particular ship
arrayForPlayerCounters[shipIdent]++; // increase this particular cell counter basing on the "shipIdent"
if (arrayForPlayerCounters[shipIdent] == playersShipLength) // ship destroyed, so mark the area with " o"
{
for (int i = 0; i < playersShipLength; i++)
{
string xy = extractXYForPlayersShip(arrayForPlayersShipsXY[shipIdent])[i].ToString();
int x = int.Parse(xy.ToString()[0].ToString());
int y = int.Parse(xy.ToString()[1].ToString());
restrictArea(x, y, dataGridView2, arrayShipsHitbyPlayerMap, arrayPlayerTakenShots);
}
AIShips--;
textBox7.Text = AIShips.ToString();
}
if (AIShips == 0)
{
dataGridView2.Enabled = false;
MessageBox.Show("You won!", "Congrats!", MessageBoxButtons.OK);
}
}
Does anybody have an idea? Thank you for your help.

The algorithms might seem the same, but they are not. There is a lot of repeated code, and it is hard to make sure they both do the "same thing". Try and break out parts of this code into smaller methods so you can reuse them.
Without seeing everything, I think this is the issue:
AI:
if (...) //Ship is destroyed
{
// ...
if (ships == 0)
{
MessageBox.Show(...);
}
}
Player
if (...) //ship is destroyed
{
// ...
}
if (ships == 0)
{
MessageBox.Show(...);
}
MessageBox.Show is called inside vs. outside the "ship destroyed" if block.
This question is not exactly the best for StackOverflow because the code is fairly messy and not simplified. Check out this link for how to ask good questions. But keep at it!

As you said both algorithms are equal but windows refresh your form after you hit the 'OK' button.
So I suggest refresh it yourself before displaying msgbox.
if (AIShips == 0)
{
dataGridView2.Refresh(); // <-------- +
dataGridView2.Enabled = false;
MessageBox.Show("You won!", "Congrats!", MessageBoxButtons.OK);
}
Your code is a bit messy I'm not sure about the grid view name, If your problem is about the first one, try this: dataGridView1.Refresh();

Related

Can't get method to exit and skip turn properly on keypress

So I'm building this RPG turn based game for a class. I'm not trying to get my homework done for me, but this one problem is REALLY spinning me out, I've been at this for like 3 hours and I can't figure out how to fix this problem.
Essentially, I have an update method that controls whos turn it is (either the player or the computer)
public bool Update()
{
// clears console for a fresh start
Console.Clear();
//branch who's turn it is and add a line indicating so
if (playerTurn)
{
//print that it's the player's turn
Console.WriteLine("It's Your Turn to Attack!");
//run the player turn
PlayerTurn();
}
else
{
//print the rivals turn label
Console.WriteLine("It's the Enemy's Turn to Attack!");
}
//run the rivals turn
RivalsTurn();
{
}
//end game check
return EndTurn();
}
Then, I have the player turn method ---
void PlayerTurn()
{
// print instructions to select an attacker
Console.WriteLine($"Select which character will attack!\n(1) To Select {playerArray[0].Name()}\n(2) To Select {playerArray[1].Name()}\n(3) To Select {playerArray[2].Name()}\n(4) to view your teams current status\n(5) To Heal An Ally");
// Loop until an attacker is chosen
while (Attacker == null)
{
// use num 1-3 to select player party member that is the attacker
ConsoleKeyInfo k = Console.ReadKey();
if (k.KeyChar == '1')
{
Attacker = playerArray[0];
Console.WriteLine($"\nYou've chosen to attack with {Attacker.Name()}");
}
else if (k.KeyChar == '2')
{
Attacker = playerArray[1];
Console.WriteLine($"You've chosen to attack with {Attacker.Name()}");
}
else if (k.KeyChar == '3')
{
Attacker = playerArray[2];
Console.WriteLine($"You've chosen to attack with {Attacker.Name()}");
}
else if(k.KeyChar == '4')
{
PrintParties();
continue;
} else if (k.KeyChar == '5') {
HealAlly();
break;
}
// start a new line after user input
Console.WriteLine();
// Data Validation: make sure the key typed is a valid key
if (k.KeyChar < '1' || k.KeyChar > '5')
{
Console.WriteLine("Please Enter (1), (2), or (3) to select your attacking character. Press (4) to view your stats, and (5) to Heal an Ally");
continue;
}
else // convert from key input (1-3) to array element space (0-2)
curSelection = int.Parse(k.KeyChar.ToString()) - 1;
{
}
if (Attacker.GetHP() <= 0)
{
//check to make sure the selected character is alive HP > 0
//print the attackers name
//character's dead choose again
Console.WriteLine($"{Attacker.Name()} is dead! Choose someone who is alive!");
Console.WriteLine("Please Enter (1), (2), or (3) to select your attacking character. Press (4) to view your stats, and (5) to Heal an Ally");
Attacker = null;
}
else
{
Console.WriteLine($"{Attacker.Name()} will attack!");
Thread.Sleep(1000);
}
}
//print instructions for choosing a rival.
Console.WriteLine($"Select which enemy to attack!\n(1) To Attack {enemyArray[0].Name()}\n(2) To Attack {enemyArray[1].Name()}\n(3) To Attack {enemyArray[2].Name()}\n(4) To see the Enemies Current Stats.");
//loop until a defender is choosen
while (Defender == null)
{
// use 1-3 to select player party member that is the attacker
ConsoleKeyInfo k = Console.ReadKey();
if (k.KeyChar == '1')
{
Defender = enemyArray[0];
Console.WriteLine($"\nYou will attack {Defender.Name()}");
} else if(k.KeyChar == '2') {
Defender = enemyArray[1];
Console.WriteLine($"\nYou will attack {Defender.Name()}");
}
else if (k.KeyChar == '3')
{
Defender = enemyArray[2];
Console.WriteLine($"\nYou will attack {Defender.Name()}");
} else if (k.KeyChar == '4')
{
playerTurn = false;
PrintParties();
playerTurn = true;
}
{ }
//add a new line aft er the user input
Console.WriteLine();
// Data Validation: make sure the key typed is a valid key
if (k.KeyChar < '1' || k.KeyChar > '3')
{
// repeat instructions
Console.WriteLine("Select an enemy to attack by pressing either 1, 2, or 3.\n");
// loop again
continue;
}
else // convert from key input (1-3) to array element space (0-2)
curSelection = int.Parse(k.KeyChar.ToString()) - 1; //minus one to use as index
//check to make sure the selected character is alive HP > 0
//print the defenders name
//assign the selected character as the defender
if (Defender.GetHP() <= 0)
{
//print instructions again
Console.WriteLine($"{Defender.Name()} is already dead! Pick another enemy!");
Console.WriteLine("Select an enemy to attack by pressing either 1, 2, or 3.\n");
Defender = null;
} else
{
Console.WriteLine($"{Attacker.Name()} attacks {Defender.Name()}!");
Thread.Sleep(2000);
}
}
//damage the defender by the attacker's Strength value
Defender.ApplyDamage(Attacker.GetStrength());
//change color for rival team
Console.BackgroundColor = Attacker.GetTeamColor();
//print the new rival's health
Console.WriteLine($"{Defender.Name()} was hit by {Attacker.Name()}! {Defender.Name()} now only has {Defender.GetHP()} HP!");
EndTurn();
//change color back for normal
Console.BackgroundColor = ConsoleColor.Black;
//pause for 2 seconds
Thread.Sleep(2000);
//reset attacker/defender for next attack
Attacker = null;
Defender = null;
}
What I am trying to do is when the player selects "5" and runs the HealAlly() Method, I want the program to Take the users next input (who to heal), tell the player whether or not they can heal (if the character is either dead or full health) and then switch turns.
Instead, the code jumps back to the middle of the PlayerTurn method and tries to execute from there, but since the Attacker was never set (the user hit 5 so they didn't pick an Attacker) the game will crash.
Here is the HealAlly() method:
void HealAlly()
{
float healing = 3;
while (Healee == null)
{
Console.WriteLine("\nWhich Ally Do You Want To Heal?");
Console.WriteLine($"(1) for {playerArray[0].Name()}\n(2) for {playerArray[1].Name()}\n(3) for {playerArray[2].Name()}");
// use 1-3 to select player party member that is being healed
ConsoleKeyInfo k = Console.ReadKey();
if (k.KeyChar == '1')
{
Healee = playerArray[0];
Console.WriteLine($"\nYou will heal {Healee.Name()}");
Console.WriteLine("Healing...");
}
else if (k.KeyChar == '2')
{
Healee = playerArray[1];
Console.WriteLine($"\nYou will heal {Healee.Name()}");
Console.WriteLine("Healing...");
}
else if (k.KeyChar == '3')
{
Healee = playerArray[2];
Console.WriteLine($"\nYou will heal {Healee.Name()}");
Thread.Sleep(1000);
Console.WriteLine("Healing...");
}
else if (k.KeyChar > 3 || k.KeyChar < 1)
{
Console.WriteLine("Choose which character you want to heal!");
continue;
}
if (Healee.GetHP() >= 12)
{
Thread.Sleep(1000);
Console.WriteLine($"{Healee.Name()} has full health! You can't heal them!");
EndTurn();
}
else if (Healee.GetHP() <= 0)
{
Thread.Sleep(1000);
Console.WriteLine($"{Healee.Name()} is dead! You can't heal them!");
EndTurn();
}
else
{
Healee.ApplyHealing(healing);
Thread.Sleep(2000);
Console.WriteLine($"{Healee.Name()} has been healed, and now has {Healee.GetHP()} HP!");
EndTurn();
}
I thought that adding the call to EndTurn() at the end of the HealAlly() method would cause the turn to end, but instead all it does is give me the delay message and then shoot back to the middle of the PlayerTurn() method. I'm sure it's because there was no Attacker set, but that's not what I want, because I don't want the player to be able to attack after healing.
Here is the EndTurn() method if that will help
bool EndTurn()
{
//switch turns for next loop
playerTurn = !playerTurn;
// loop through players to see if they're alive and store in a variable counting if they're alive or not
bool playersAlive = true;
for (int i = 0; i < playerArray.Length; i++)
{
if (playerArray[0].GetHP() <= 0 && playerArray[1].GetHP() <= 0 && playerArray[2].GetHP() <= 0)
{
playersAlive = false;
}
}
// same for rivals
bool rivalsAlive = true;
for (int i = 0; i < enemyArray.Length; i++)
{
if (enemyArray[0].GetHP() <= 0 && enemyArray[1].GetHP() <= 0 && enemyArray[2].GetHP() <= 0)
{
rivalsAlive = false;
}
}
// if both have things alive start the next round, pause the game, and return true to continue playing
if (playersAlive && rivalsAlive)
{
Console.WriteLine("Next Round Starts in 5 seconds");
Thread.Sleep(5000);
return true;
} // if only the players have members alive you win
else if (playersAlive)
{
//clear screen for results
Console.Clear();
//print you've won and parties
Console.WriteLine("Congrats you win! Final Standings:");
playerTurn = false;
PrintParties();
playerTurn = true;
PrintParties();
Thread.Sleep(5000);
Console.WriteLine("Thanks For Playing!");
Console.WriteLine("Press Any Key to Play Again");
Console.ReadKey();
Console.Clear();
Init();
return false;
} // only rival members are alive
else
{
//clear screen for results
Console.Clear();
//Print you've lost and parties
Console.WriteLine("You Lose :( Final Standings:");
playerTurn = false;
PrintParties();
playerTurn = true;
PrintParties();
Thread.Sleep(5000);
Console.WriteLine("Thanks For Playing!");
Console.WriteLine("Press Any Key to Play Again");
Console.ReadKey();
Console.Clear();
Init();
return false;
}
}
This is bugging me, can anyone help?
void PlayerTurn()
{
// ...
while (Attacker == null)
{
// ...
if (k.KeyChar == '5') {
HealAlly();
break;
}
}
// ...code that depends on Attacker...
}
The break statement here only causes the flow to break out of the while loop and continue with the code the depends on Attacker. If you want to leave the PlayerTurn() method altogether, replace the break; with a return;

C# Go to beginning of loop (continue or break?)

This program randomly creates a number between 1 and 50 that the user needs to guess. I want to restart the while loop when the user's guess is incorrect. I have tried using break and continue to do this, but either I am doing it wrong or it is not the best solution. Also, when the user input is incorrect they are asked if they want to guess again. I also tried exiting that loop with break or continue with similar results. Any suggestions?
Console.WriteLine("I am thinking of a whole number between 1 and 50. Enter your guess.");
string userGuess;
Random rnd = new Random();
int compNum = rnd.Next(1, 50);
int guessToInt;
int numOfTries = 0;
string cont;
bool correct = false;
//TODO Delete this line when finished
Console.WriteLine($"Answer " + compNum);
//Trying to get new user input if guess is wrong
while (correct == false)
{
//correct = false;
numOfTries++;
userGuess = Console.ReadLine();
guessToInt = Int32.Parse(userGuess);
if (guessToInt == compNum)
{
numOfTries++;
correct = true;
Console.WriteLine($"Your guess is correct! The number was " + compNum + ". Congratulations. It only took you " + numOfTries + " guess(es).");
}
else if (guessToInt < 1 || guessToInt > 50)
{
Console.WriteLine("Incorrect input. Enter a whole number between 1 and 50. Try again.");
//numOfTries++;
correct = false;
continue;
}
else if (guessToInt != compNum)
{
//How do I get new user input?
Console.WriteLine("Your guess is incorrect. Would you like to try again? Y or N?");
//numOfTries++;
correct = false;
cont = Console.ReadLine();
if (cont == "Y")
{
continue;
}
else Console.WriteLine("Bye."); Environment.Exit(0);
}
}
Few problems:
You don't have to set the boolean to false each time. It is already set to false at start
else Console.WriteLine("Bye."); Environment.Exit(0);
Since you didn't add curly brackets (which I strongly advise you to never do),
Only the first command will operate under the statement.
The "Environment.Exit" will always occur.
For the rest, I added the fixes to your code.
Console.WriteLine("I am thinking of a whole number between 1 and 50. Enter your guess.");
string userGuess;
Random rnd = new Random();
int compNum = rnd.Next(1, 50);
int guessToInt;
int numOfTries = 0;
string cont;
bool correct = false;
//TODO Delete this line when finished
Console.WriteLine($"Answer " + compNum);
//Trying to get new user input if guess is wrong
while (correct == false)
{
//correct = false;
numOfTries++;
userGuess = Console.ReadLine();
guessToInt = Int32.Parse(userGuess);
if (guessToInt == compNum)
{
numOfTries++;
correct = true;
Console.WriteLine($"Your guess is correct! The number was " + compNum + ". Congratulations. It only took you " + numOfTries + " guess(es).");
}
else if (guessToInt < 1 || guessToInt > 50)
{
Console.WriteLine("Incorrect input. Enter a whole number between 1 and 50. Try again.");
//numOfTries++;
//**correct = false; --> No need, correct was already false
//**continue; --> No need, will continue anyway
}
else if (guessToInt != compNum)
{
//How do I get new user input?
Console.WriteLine("Your guess is incorrect. Would you like to try again? Y or N?");
cont = Console.ReadLine();
//numOfTries++;
//**correct = false; --> No need, correct was already false
//** if (cont == "Y") --> No need, will continue anyway
//** {
//** continue;
//** }
// else Console.WriteLine("Bye."); Environment.Exit(0); -->
//** "else" without brackets will
if (cont != "Y")
{
break;
}
Console.WriteLine("Enter your next guess:");
}
}
NOTE: I haven't touched the "numOfTries" feature since not mentioned in the question

Why won't the printing message print in my C# code?

I am trying to print out the message "printing..." to the txtMessage.Text text box before the loop runs but it never does print to the text box before the loop runs. Any idea why?
else
{
txtMessage.Text = "Printing...";
for (int i = numberCopies; i != 0; i--)
{
int paper = Convert.ToInt32(lblPaperAmount.Text);
paper--;
if (paper == 480 || paper == 380 || paper == 400 || paper == 200)
{
MessageBox.Show("There is a paper Jam! Please remove the Jam and then hit the ok button to continue!", "Important Message", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
lblPaperAmount.Text = Convert.ToString(Convert.ToInt32(lblPaperAmount.Text) - 1);
lblTonerAmount.Text = Convert.ToString(Convert.ToInt32(lblTonerAmount.Text) - 1);
Thread.Sleep(1000);
}
txtMessage.Text = "Job is completed!";
}
Try adding a call to Refresh after setting the text. It's possible you're entering the loop quickly enough the refresh does not happen until you exit.
else
{
txtMessage.Text = "Printing...";
txtMessage.Refresh(); //force the control to redraw
for (int i = numberCopies; i != 0; i--)
{
int paper = Convert.ToInt32(lblPaperAmount.Text);
paper--;
if (paper == 480 || paper == 380 || paper == 400 || paper == 200)
{
MessageBox.Show("There is a paper Jam! Please remove the Jam and then hit the ok button to continue!", "Important Message", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
lblPaperAmount.Text = Convert.ToString(Convert.ToInt32(lblPaperAmount.Text) - 1);
lblTonerAmount.Text = Convert.ToString(Convert.ToInt32(lblTonerAmount.Text) - 1);
Thread.Sleep(1000);
}
txtMessage.Text = "Job is completed!";
}
Your code is running in a single thread. You should set your text to "Printing..." then spin off a new thread to do your paper jam for loop.
Please see this question and the top 3 answers.

While Loop Crash in C# game

I am creating a Kinect Text Adventure game for a project. I know the code isn't that great, but the project features more on effects of the game than the game itself, so it doesn't have to be complicated. This section of code is used to progress through the game using certain skeletal positions. The problem I am having is that I am trying to use a while loop so that depending on the current value of f, it will go to that specific room. The only problem is when I use a while loop, it always crashes before it's even started. I'm not sure what the problem is
private void ProcessGesture(Joint head, Joint handleft, Joint handright)
{
while (f!=0)
{
if (handright.Position.Y > head.Position.Y && handleft.Position.Y > head.Position.Y && f == 1)
{
txtBox1.Text = "You find yourself at the foot of a large mountain, with a gaping cave at the front. \nYou have come here to find the Lost Sword of Gaia and you have heard that it lies \nhere in the Cave of Borlak the Red. You can move east";
f = 2;
this.btnangle.Visibility = Visibility.Hidden;
this.slider1.Visibility = Visibility.Hidden;
this.Degree.Visibility = Visibility.Hidden;
this.helpbtn.Visibility = Visibility.Hidden;
}
if (handright.Position.X > 0.3 && f == 2)
{
txtBox1.Text = "You walk up to the entrance of the cave and spot and \nlittle goblin wearing a suit of armour and holding a spear. \n'I'm so bored' he says. 'What I need is a good high five'\nYou can go west";
f = 3;
this.sadGoblin.Visibility = Visibility.Visible;
}
if ((f == 3 && handright.Position.Y > head.Position.Y) || (f == 3 && handleft.Position.Y > head.Position.Y))
{
Uri uri1 = new Uri("/SkeletalTracking;component/Resources/hgoblin.jpg", UriKind.Relative);
ImageSource source1 = new BitmapImage(uri1);
this.sadGoblin.Source = source1;
txtBox1.Text = "'Ah, thank you kind stranger, for that I shall let you in'.\n The goblin steps to the side ";
f = 4;
}
if (f == 4 && handright.Position.Y > head.Position.Y && handleft.Position.Y > head.Position.Y)
{
this.sadGoblin.Visibility = Visibility.Hidden;
txtBox1.Text = "You are now in the main hall of the mountain. You can hear chanting and singing from the north.\nYou see a statue in the middle of the room and two doors to the left and right of it";
f = 5;
}
if (f == 5 && handleft.Position.X < -0.3)
{
txtBox1.Text = "This is Borlak's treasure room. In here are all of the things he has colleted over the years\nSome bought and some not so bought.\nYour eyes are drawn to a necklace in a glass case in the center of the room.\nThere is also a picture of Borlak holding a giant chunk of ham\nYou can go east";
f = 6;
}
if (f == 6 && handright.Position.X > 0.3)
{
f = 5;
}
//else if (f == 5 && handright.Position.X > 0.3)
// {
// txtBox1.Text = "";
// }
}
}
Instead of using a while loop use a switch statement:
int caseSwitch = 1;
switch (caseSwitch)
{
case 1:
Console.WriteLine("Case 1");
break;
case 2:
Console.WriteLine("Case 2");
break;
default:
Console.WriteLine("Default case");
break;
}
Where do you exit the loop? Looks like you're stuck in an infinite loop. Games usually a check like this every update. I don't think you should stay in this loop all in one frame of game play. You should check what f is and decide what to do each frame.
Edit: also, please post what error you get. It would be easier to tell what happens.
In my opinion, take away the while loop, and just let it check 'f' every frame.
The default value of f is 0. If f is not being set ( >=1 ) before this method is called then it would not enter the while loop.
Since all the code isn't shown, and there is no error shown, could "crash" mean immediate exit?

Space Invaders health/array issues

I've been doing some exercises to learn C#. I've been doing XNA and making a space invaders clone.
So far, everything is dandy, but I have come across some walls when coding.
The issues and the supporting code are as follows:
My top of row of invaders have 2 health points, take 2 hits to destroy and yield more points. However, when one is hit, and destroyed, the rest of the top of row are reduced to 1 HP, and take 1 hit to destroy - which is not my desired result.
The offending code I suspect is:
if (playerBullet != null && Type1Invaders != null)
{
Rectangle rectMissile = new Rectangle((int)playerBullet.getX(), playerBullet.getY(), playerBulletIMG.Width, playerBulletIMG.Height);
for (int count = 0; count < 11; count++)
{
Rectangle rectInvader = new Rectangle(Type1Invaders[count].getX(), Type1Invaders[count].getY(), invader1.Width, invader1.Height);
if (Type1Invaders[count].getVis() && rectMissile.Intersects(rectInvader))
{
Type1Invaders[count].setHP(Type1Invaders[count].getHP() - 1);
shootTimer = 0;
if (Type1Invaders[count].getHP() == 0)
{
explosionInstance.Play();
playerBullet = null;
Type1Invaders[count].setVis(false);
score = score + Type1Invaders[count].getPointValue();
}
}
}
}
My second error resides in how I'm detecting the leftmost and rightmost invaders in a row. When an entire row has been destroyed, I get a nullreferenceerror. (Those are a nightmare..)
Anyway, this is the offending code
The method of finding the left and right most invaders
var LeftMost5 = Type5Invaders.Where(i => i.getVis()).FirstOrDefault();
var RightMost5 = Type5Invaders.Where(i => i.getVis()).LastOrDefault();
And the if statement is throwing the null error
if (RightMost5.getX() >= RightGameEdge)
{
Type5.setDir(-1);
for (int count = 0; count < 11; count++)
{
invaderMoveInstance5.Play();
Type5Invaders[count].MoveY(8);
}
}
It only happens with the rightmost, but I can assume it will happen to the left too - I'm assuming I can apply the same logic to fix this error to the left side too.
I can supply more information and snippets if this is not sufficient.
Thanks in advance for the assistance!
For the first issue. I suppose that when a bullet kills an invader, you can say that that bullet won't kill another invader. Therefore, you can add a break; to stop looping. Like this:
if (Type1Invaders[count].getVis() && rectMissile.Intersects(rectInvader))
{
Type1Invaders[count].setHP(Type1Invaders[count].getHP() - 1);
shootTimer = 0;
if (Type1Invaders[count].getHP() == 0)
{
explosionInstance.Play();
playerBullet = null;
Type1Invaders[count].setVis(false);
score = score + Type1Invaders[count].getPointValue();
}
break;
}
For the second error, the FirstOrDefault method returns null in case your collection is empty (after you have killed all type 5 invaders). You simply need to check if it is null or not, like this:
var LeftMost5 = Type5Invaders.Where(i => i.getVis()).FirstOrDefault();
var RightMost5 = Type5Invaders.Where(i => i.getVis()).LastOrDefault();
if(RightMost5 != null)
{
// this means we have a non-null invader
if (RightMost5.getX() >= RightGameEdge)
{
Type5.setDir(-1);
for (int count = 0; count < 11; count++)
{
invaderMoveInstance5.Play();
Type5Invaders[count].MoveY(8);
}
}
}
else
{
//this means that the invader does not exist anymore, so we do nothing
}

Categories