首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >基于eos的Dapp开发--元素战争(五)

基于eos的Dapp开发--元素战争(五)

作者头像
用户2569546
发布2021-11-23 10:41:11
7140
发布2021-11-23 10:41:11
举报
文章被收录于专栏:eosfanseosfans

前面我们介绍了元素战争这个游戏最基本的组成要素,只有一些基本的东西是无法实现整个游戏的过程的,接下来我们继续来探讨整个游戏资源,游戏规则,游戏流程。在本节内容中我们将继续来完善智能合约中的startgame和playcard两个action。

我们要知道在eos系统中保存数据的方式就是多索引表,因此为了保存每一局游戏的信息,我们需要创建一个多索引表对应的数据结构。同时每一个玩家都有其独立的游戏信息,因此我们把user_info结构体中添加游戏的具体信息:

    struct game {
      int8_t          life_player = 5;
      int8_t          life_ai = 5;
      vector<card_id> deck_player = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17};
      vector<card_id> deck_ai = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17};
      vector<card_id> hand_player = {0, 0, 0, 0};
      vector<card_id> hand_ai = {0, 0, 0, 0};
      card_id         selected_card_player = 0;
      card_id         selected_card_ai = 0;
      uint8_t         life_lost_player = 0;
      uint8_t         life_lost_ai = 0;
      int8_t          status = ONGOING;
    };

每把游戏都有三个状态,即:

  • ONGOING--游戏正在进行中。
  • PLAYER_WON--游戏已经结束且玩家获胜。
  • PLAYER_LOST--游戏已经结束且玩家失败。

游戏的初始状态我们设置为ONGING,同时为了增加可读性,我们用一个枚举来表示游戏的状态:

    enum game_status: int8_t  {
      ONGOING     = 0,
      PLAYER_WON   = 1,
      PLAYER_LOST  = -1
    };

如何来判断这局游戏结束了呢,我们稍微玩过一些游戏的人都知道有个HP值,代表玩家的血条值,当HP变为0的时候,代表玩家已经死亡,游戏结束,那么这个游戏中还包含有哪些元素呢:

  • 元素战争中有11种独立的卡牌。
  • 一张卡牌只有一种元素属性。
  • 每张卡牌都有一定的攻击力。
  • 游戏开始,每个玩家都拥有17张相同的卡牌。
  • 部分元素类型具有元素的兼容性。

卡牌具有以下五种元素类型,元素兼容性到底是什么样的以及这个游戏该怎么玩呢?来和我一起玩就知道了:

每种元素的卡牌所拥有的攻击力如下:

同时为了记录卡牌的类型,我们也用枚举来示例,考虑到一些可能产生的异常情况,我们将EMPTY这种类型也添加进来:

    enum card_type: uint8_t {
      EMPTY = 0, // Represents empty slot in hand
      FIRE = 1,
      WOOD = 2,
      WATER = 3,
      NEUTRAL = 4,
      VOID = 5
    };

我们前面说过每张卡牌都有其具体的攻击力,可以简单的用一个map表来存储这17张卡牌信息:

    const map<card_id, card> card_dict = {
      { 0, {EMPTY, 0} },
      { 1, {FIRE, 1} },
      { 2, {FIRE, 1} },
      { 3, {FIRE, 2} },
      { 4, {FIRE, 2} },
      { 5, {FIRE, 3} },
      { 6, {WOOD, 1} },
      { 7, {WOOD, 1} },
      { 8, {WOOD, 2} },
      { 9, {WOOD, 2} },
      { 10, {WOOD, 3} },
      { 11, {WATER, 1} },
      { 12, {WATER, 1} },
      { 13, {WATER, 2} },
      { 14, {WATER, 2} },
      { 15, {WATER, 3} },
      { 16, {NEUTRAL, 3} },
      { 17, {VOID, 0} }
    };

了解了卡牌的信息我们来看每一轮游戏中都包含有哪些重要信息,注释在代码里:

    struct game {
      //玩家和AI拥有的血量
      int8_t          life_player = 5;
      int8_t          life_ai = 5;
      //用两个vector分别来存储玩家和AI的初始化的卡牌信息(和我们刚才定义的map表对应)
      vector<card_id> deck_player = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17};
      vector<card_id> deck_ai = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17};
      //再用两个vector分别存储游戏玩家和AI当前手中所持有的卡牌信息,刚开始的时候都是空的,因此我们用0即EMPTY来初始化这些信息
      vector<card_id> hand_player = {0, 0, 0, 0};
      vector<card_id> hand_ai = {0, 0, 0, 0};
      //每一轮游戏玩家和AI都会选择出牌,我们用接下来两个变量来存储出牌的信息
      card_id         selected_card_player = 0;
      card_id         selected_card_ai = 0;
     //每一轮游戏玩家和AI掉的血量记录在下面两个变量
      uint8_t         life_lost_player = 0;
      uint8_t         life_lost_ai = 0;
      int8_t          status = ONGOING;
    };

上面介绍了游戏,玩家,卡牌的信息,为了增加可玩性我们添加一些随机的元素在里面,当然这个随机数的生成方法只是简单的在元素战争游戏里使用,对公平性要求较高的游戏不推荐使用该方法生成随机数,这个随机数方法主要有以下两个功能:

  • 决定哪张卡牌被攻击。
  • 决定AI选用哪种出牌策略。

我们知道随机数的生成必然少不了随机数种子,在这里我们使用的是blocktime,为了让这个随机数种子在玩家之间公平传递,我们也要把随机数种子存储在多索引表中,当随机数方法被调用的时候,该表将会更新:

    // @abi table seed
    struct seed {
      uint64_t        key = 1;
      uint32_t        value = 1;

      auto primary_key() const { return key; }
    };

我们来看随机数是如何生成的,如我们前面说的,这种随机数生成的方式不建议使用在对公平性要求很高的Dapp游戏开发中,尤其是菠菜游戏:

int cardgame::random(const int range) {
  auto seed_iterator = _seed.begin();

  // 先查找表中是否已经存在随机数种子,如果不存在就初始化存储一个
  if (seed_iterator == _seed.end()) {
    seed_iterator = _seed.emplace( _self, [&]( auto& seed ) { });
  }

  // 生成一个新的随机数种子
  int prime = 65537;
  auto new_seed_value = (seed_iterator->value + now()) % prime;

  // 将生成的随机数种子更新到表中
  _seed.modify( seed_iterator, _self, [&]( auto& s ) {
    s.value = new_seed_value;
  });

  // 获取一定范围内随机数的结果并返回
  int random_result = new_seed_value % range;
  return random_result;
}

现在让我们来开始游戏,具体做了什么也在代码的注释里:

void cardgame::startgame(account_name username) {
  // 玩家权限确认,必不可少
  require_auth(username);
  //查看用户是不是第一次玩
  auto& user = _users.get(username, "User doesn't exist");
  _users.modify(user, username, [&](auto& modified_user) {
    // 为用户创建一个新游戏并给玩家和AI分别分配卡牌,卡牌的分发使用了我们刚刚说的随机数的生成
    game game_data;
    for (uint8_t i = 0; i < 4; i++) {
      draw_one_card(game_data.deck_player, game_data.hand_player);
      draw_one_card(game_data.deck_ai, game_data.hand_ai);
    }
    modified_user.game_data = game_data;
  });
}

游戏开始之后我们出牌的规则如下:

void cardgame::playcard(account_name username, uint8_t player_card_idx) {
  // 1.确认玩家权限 2.检查卡牌的id是否有效(每个人手中只有四张牌)3.确认游戏正在进行
  require_auth(username);

  eosio_assert(player_card_idx < 4, "playcard: Invalid hand index");

  auto& user = _users.get(username, "User doesn't exist");


  eosio_assert(user.game_data.status == ONGOING,
               "playcard: This game has ended. Please start a new one");
  eosio_assert(user.game_data.selected_card_player == 0,
               "playcard: The player has played his card this turn!");

  _users.modify(user, username, [&](auto& modified_user) {
    game& game_data = modified_user.game_data;

    game_data.selected_card_player = game_data.hand_player[player_card_idx];
    game_data.hand_player[player_card_idx] = 0;
  });
}

现在智能合约中的action都编写完成了,我们再来看前端中分别展示了什么:

GameMat:

  • PlayerInfo展示当前玩家的血量值。
  • HandCards展示当前玩家手中持有的牌。

GameInfo:

  • Rules展示游戏规则。
  • Quit放弃游戏回到主界面。

玩家手中持有的卡牌:

OK,所有的内容都介绍结束之后我们点击前端的开始按钮:

  1. 用户前端点击开始按钮。
  2. 调用PlayerProfile中的onStartGame方法。
  3. 调用Game.jsx中的handleGameStart方法。
  4. 调用ApiService中的startGame方法获取到上一局游戏的信息。
  5. startGame调用成功之后载入用户信息。
  6. loadUser调用成功展示用户和游戏信息。

而玩家出牌的代码流程如下:

  1. 用户选择卡牌。
  2. 调用HandCard组件中的onPlayCard方法。
  3. 调用Game.jsx中的handlePlayCard方法。
  4. 调用ApiService中的playCard方法,从智能合约中返回上一轮游戏的状态。
  5. playCard结束之后调用loadUser方法。
  6. loadUser方法调用了ApiService中的getUserByName用来获取玩家的状态信息。

至此一个开始游戏和出牌的过程就完成了。

本文介绍了元素战争游戏中如何编写开始游戏和出牌的逻辑,其中包含有游戏的主要元素,卡牌的属性值,一个简单的随机数的生成等,更多的内容我们接下来也会继续分析。如果对该游戏感兴趣,可以一起来玩。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-11-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 eosfans 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
区块链
云链聚未来,协同无边界。腾讯云区块链作为中国领先的区块链服务平台和技术提供商,致力于构建技术、数据、价值、产业互联互通的区块链基础设施,引领区块链底层技术及行业应用创新,助力传统产业转型升级,推动实体经济与数字经济深度融合。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档