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

Card game with huge amount of cards and complex actions

本文关键字:复杂 纸牌游戏      更新时间:2023-10-16

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

另一件事是,这张卡可以做一些基于条件的长操作链的硬复杂动作(可选或强制),例如:抽一张牌,如果它是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 !";

我也有马斯开关的情况,但光是想想就恶心......

如果您在卡中创建一个嵌套接口,则可以使用从该接口派生的完全不同的行为类填充它。


编写一个算法来添加类型的卡片,当这些卡片缺少您需要的百分比时。这样,如果您有 52 张或 123456789.0f 张卡就无关紧要了

在 IType 十进器中添加一个静态 int,以便在每次添加卡时添加 1。当您处理大量卡片时,这将节省大量处理时间。


您可以考虑更改:

virtual bool CSpell::use( int action )

virtual bool CSpell::use( void* action )

这样,您就可以传入所有新内容,特别是每种类型所需的内容。不仅可以传递整数,还可以传递整个结构和类。


例如:

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

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

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 ++有点生疏:)

你不能硬编码任何东西,并使用lua这样的东西动态执行你的卡片操作(从来没有用过它,只是听说过它)。这有几个优点:

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

也有一些缺点:

  • 这更难
  • 会慢一点

你可以像你说的那样设计类:一个baseCard类,然后是10个左右的类来扩展它,代表你的多种类型。然后,您可以拥有一个 csv* 文件,其中包含有关 1000 张不同卡片中每张卡的信息,格式如下:[类型] 、[值]、[实例数]、[其他属性]。然后,您可以读取文本文件,并通过对文件进行一些基本解析,使用给定的信息初始化必要类的各种实例。这样,您可以将类的设计与实例分开。实际上,您可以创建一个执行所有这些初始化的 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 代表逗号分隔值,是一个基本文本文件,其值以逗号分隔。