具有大量卡片和复杂动作的纸牌游戏

时间:2015-01-04 23:56:22

标签: c++ oop logic

我正在尝试编写一些未来派的纸牌游戏模拟器。游戏本身有复杂的规则和很多卡(总共约1000)。卡的类型很少(< 10,类似法术,治疗,移动,攻击,防守等等)。每张卡属于exacly one类型,但他们所做的动作可以是各种各样的,例如:Card1属于Healing类型,其动作是治疗1点生命值,Card2也属于Healing类型,但它的动作是治疗和ex。丢掉另一张卡片。

另一件事是卡片可以做一些复杂的动作,这是基于条件的长链操作(可选或强制),例如:绘制一张卡片,如果它是X颜色,则保持它,如果不放弃它并重新获得Y点法力值,如果你想要,也是一种祝福

问题是如何设计类的结构来制作卡片类型并为游戏中的每张卡片定义动作,然后将它们全部存储在某个阵列中以便以后在游戏中使用它们?

我脑海中唯一想到的是某种类型的继承:

class CardBase
{
public:
    virtual bool use(int action) = 0; // Every card should implement this

private:
    std::string _name;
    // type ?
}

// Split into types maybe ?

class CardOne: public CardBase
{
    bool use(int action) { /*do whatever card should do on given action ID ex. heal*/ }
}

class CardTwo: public CardBase
...
// 1000 cards later... is this still a good idea... ?

以后存储和使用如:

std::vector<CardBase*> cards; // build card deck

if (cards.at(0)->use())
    std::cout << "Card used !";
else
    std::cout << "You cannot do that !";

我也有masive switch case,但它的恶心只是想着它......

4 个答案:

答案 0 :(得分:1)

如果您使用该卡创建嵌套界面,则可以使用该界面中未完成的完整不同的动作类填充它。


编写一种算法,以便在缺少那些需要的百分比时添加类型的卡。这样,如果您有52张或123456789.0f张卡

,这无关紧要

在IType后代中添加一个静态int,每次添加一张卡时添加1。当你获得更多的卡时,这将节省大量的处理时间。


你可以考虑改变:

virtual bool CSpell::use( int action )

virtual bool CSpell::use( void* action )

通过这种方式,您可以根据自己的需要传递新的所有内容。不仅可以传递整数,还可以传递整个结构和类。


e.g:

class IType { public: virtual bool use( int action ) = 0; };  
class ISpell : public IType { }; class CSpell : public ISpell { public: bool use( int action ){ return false; } };

class IAbillity {};
class CAbillity : public IAbillity {  };
class CSpecialAbillity : public IAbillity {  };

class CCard
{
public:
    enum{ Spell, Trap, Monster, Wizard };
    CCard( std::string name, IType* type, bool gc = false ) : _name( name ), _type( type ) { }
    ~CCard(){ if( _type /* && _gc_responsible */ ) delete _type; }

    virtual bool use( int action ){ return _type ? _type->use( action ) : false; }

private:
    std::string _name;

    // Whatever type you have: call use()
    IType*      _type       = 0;

    // An abillity espacially for a monster, or a wizard, can be set here
    IAbillity*  _abillity   = 0;

};

class CDeck : std::vector<CCard*>
{
    public:

    int load( std::string filename ){}
    int save( std::string filename ){}
    int addCards( int amount )
    {

        // Make your algorithm
        // Run it each time you want to add cards

        // loop
        // Check how many cards you have from each type
        // the one that lacks the most (percentage wise), add it

    }
    private:

};


int main()
{

    CDeck deck;

    deck.addCards( 10 );
    // play, eat, sleep, invite people
    deck.addCards( 10 );
    // play, eat, sleep, invite people
    deck.addCards( 20 );
    // cool, aparty, invite people
    deck.addCards( 1000 );

}

答案 1 :(得分:0)

类似的卡片(例如治疗卡片)可以由同一个类表示。然后,您可以使用特定值创建实例。

class HealerCard: public CardBase
{
    int amount;
    HealerCard(int amount) {this->amount = amount;}
    bool use(int action) { /*heal by this->amount*/ }
}

通过上述课程,您可以创建不同的治疗卡实例:

//... Let's create 10 of each amount of healer cards from 1 to 10
int amount, i, cnt = 0;
for (amount=1; amount <= 10; amount++) {
  cards[cnt++] = new HealerCard(amount);
}

注意:语法可能有些偏差,我的c ++有点生锈:)

答案 2 :(得分:0)

您无法对任何内容进行硬编码,并使用lua(从未使用它,只是听说过)来动态执行您的卡片操作。这有一些好处:

  • 您无需重新编译即可更改卡
  • 不太可能出现错误
  • 您可以轻松制作新卡
  • 您可以从文件中加载不同的卡片组
  • 用户可以轻松制作自己的卡

还有一些缺点:

  • 这样更难
  • 会慢一点

答案 3 :(得分:0)

您可以设计类似于您声明的类:一个baseCard类,然后是10个左右的类,它们可以扩展代表您的多种类型。然后你可以有一个csv *文件,其中包含1000个不同卡中的每一个的信息,其格式如下:[type],[value],[number of instances],[other attributes]。然后,您可以读取文本文件,并通过对文件进行一些基本解析,使用给定的信息初始化必要类的各种实例。这样,您可以将类的设计与实例分开。实际上,您可以创建一个完成所有初始化的deck类,然后在程序中使用deck类的单个实例。

Healer , 1 , 10, ? ,
Healer , 3 , 5 , ? ,
Healer , 5 , 2 , ? ,
Healer , 10 , 1 , ? ,
Spell , 1 , 20, ? ,
Spell , 3 , 10 , ? ,
Defense , 1 , 7, ? ,
Defense , 3 , 4, ? ,
...

* csv代表逗号分隔值,是一个基本文本文件,其值以逗号分隔。