Reputation: 695
I am creating a card game in C#.
I have a Abstract class "Card":
public abstract class Card
{
}
Which is only used to provide a generic parent (I.E: A duke is a card, a captain is also a card)
The following abstract subclasses inherit from Card, and are used to further specify the type of Card (I.E: An Assasin is a targetCard, a duke is a moneycard) each of them contains an abstract method called "Action" with parameters relevant to itself: A MoneyCard has an abstract method which takes as parameters The Player and The ChipStack, A TargetCard has an abstract method which takes as input The Player and a TargetPlayer, etcetera.
public abstract class PassiveCard : Card
{
public abstract void Action();
}
public abstract class DeckInteractionCard : Card
{
public abstract void Action(Player player, Deck deck);
}
public abstract class MoneyCard : Card
{
public abstract void Action(Player player, ChipStack chipStack);
}
public abstract class TargetCard : Card
{
public abstract void Action(Player player, Player targetPlayer);
}
After that I have the concrete implementations of each type of card; each of them inherits from one of the abstract classes mentioned above which is relevant to itself.
Of course, each card also fully implements the abstract method that it gets from it's parent Abstract-class.
class Duke : MoneyCard
{
public override void Action(Player player, ChipStack chipStack)
{
for (int i = 0; i < 3; i++)
{
player.TakeChip(chipStack);
}
}
public override string ToString()
{
return "Duke";
}
}
class Captain : TargetCard
{
//this card gives passive protection: how do we do this
public override void Action(Player player, Player targetPlayer)
{
if (targetPlayer.PlayerChips.Count >= 2)
{
targetPlayer.GiveChip(2, player);
}
}
public override string ToString()
{
return "Captain";
}
}
class Contessa : PassiveCard
{
//this card gives passive protection: how do we do this:
//give the receiving player the option to bluff, show that he really has a contessa, or deny the other having an assasin
public override void Action()
{
throw new NotImplementedException();
}
public override string ToString()
{
return "Contessa";
}
}
class Ambassador : DeckInteractionCard
{
//this card gives passive protection: how do we do this
public override void Action(Player player, Deck deck)
{
//open dialog: which 2 cards would you like to keep?
//(now, this action just doubles the players hand)
for (int i = 0; i < 2; i++)
{
player.PlayerHand.HandContent.Add(deck.DrawCard());
}
}
public override string ToString()
{
return "Ambassador";
}
}
class Assassin : TargetCard
{
public override void Action(Player player, Player targetPlayer)
{
if (player.PlayerChips.Count >= 3 && targetPlayer.FoldedAllCards == false)
{
for (int i = 0; i < 3; i++)
{
player.PlayerChips.RemoveAt(player.PlayerChips.Count - 1);
}
targetPlayer.FoldCard();
}
}
public override string ToString()
{
return "Assassin";
}
}
Now for my actual problem:
I have a Player class. Each object of class player has a Hand (A hand being a collection of cards.) I want to create a method in the player class that takes as input a generic "Card", and then calls the action method relevant to that specific type of card. Something like this:
public void CardAction(Card card)
{
card.Action();
}
The above example does not work: The compiler complains that the Abstract class Card has no method called action (which makes sense)
So: How would I create a method that takes as input a generic Card, and calls the SPECIFIC Action() method relevant to the SPECIFIC card that we gave as input?
Thanks in advance,
Robert
Upvotes: 2
Views: 140
Reputation: 13197
I see two solutions here: you can either check for each card type and then call the appropriate method:
var pCard = card as PassiveCard;
if(pCard != null)
{
pCard.Action();
return;
}
var diCard = card as DeckInteractionCard;
if(diCard != null)
{
diCard.Action(player, deck);
return;
}
var mCard = card as MoneyCard;
if(mCard != null)
{
diCard.Action(player, chipStack);
return;
}
var tCard = card as TargetCard;
if(tCard != null)
{
tCard.Action(player, targetPlayer);
return;
}
or you can give Card
class an abstract method that contains all possible parameters:
public abstract class Card
{
public abstract void Action(Player player, Player targetPlayer, Deck deck, ChipStack chipStack);
}
then simply implement this method and ignore unnecessary parameters. Or if you don't want this method to be visible (showing only the method with the required parameters), you can make the base class an interface and make an explicit implementation.
public abstract class PassiveCard : Card
{
public abstract void Action();
public override void Action(Player player, Player targetPlayer, Deck deck, ChipStack chipStack)
{
Action();
}
}
public abstract class DeckInteractionCard : Card
{
public abstract void Action(Player player, Deck deck);
public override void Action(Player player, Player targetPlayer, Deck deck, ChipStack chipStack)
{
Action(player, deck);
}
}
etc.
Upvotes: 1
Reputation: 5144
I think that your Action
method is too much generalized, however, you can make it work.
Instead of making multiple signatures of your Action
, make a single one that receives only one parameter Context
. In this parameter, put all of the stuff that might be required by different implementations of the action.
Here is the example:
public class Context
{
public Player Player;
public Player TargetPlayer;
public ChipStack ChipStack;
...
}
public abstract class Card
{
public abstract void Action(Context c);
}
...
public class Duke : MoneyCard
{
public override void Action(Context c)
{
for (int i = 0; i < 3; i++)
{
c.Player.TakeChip(c.ChipStack);
}
}
public override string ToString()
{
return "Duke";
}
}
public void CardAction(Card card)
{
var c = new Context { Player = p1, TargetPlayer = p2, ChipStack = chips };
card.Action(c);
}
Upvotes: 2