游戏开始时选择武将后,服务端统一控制向玩家分发初始手牌,并向客户端发生绘制手牌的命令.跟踪的流程如下:服务端向客户端发送命令drawCards及随后的一串使用加号(+)连接起来的牌编号(drawCards 91+50+112+100+42+133+43+78).客户端解析这个命令串后调用drawCards函数(原理在上面的系列分析中已经阐述),并使用后面的字符串作为参数,在drawCards函数中解析出牌的编号,根据牌的编号得到Card对象实例,加入到cards列表及ClientPlayer类型全局对象(Self)的手牌列表.接着触发cards_drawed信号.

void Client::drawCards(const QString &cards_str){
    QList<const Card*> cards;
    QStringList card_list = cards_str.split("+");
    foreach(QString card_str, card_list){
        int card_id = card_str.toInt();
        const Card *card = Sanguosha->getCard(card_id);
        cards << card;
        Self->addCard(card, Player::Hand);
    }

pile_num -= cards.length();
    updatePileNum();

emit cards_drawed(cards);
}

roomScene类的构造函数中关联了cards_drawed信号:connect(ClientInstance, SIGNAL(cards_drawed(QList<const Card*>)), this, SLOT(drawCards(QList<const Card*>)));在这个信号处理函数中,遍历每张牌,生成新的CardItem对象.注意,Card类是一个从QObject继承的,而CardItem是从QGraphicsObject继承而来,因此CardItem可以加入到场景中并在视图中显示.接着设置显示位置和使能,加入到玩家的控制区域(dashboard,翻译为仪表盘),最后写日志信息.

void RoomScene::drawCards(const QList<const Card *> &cards){
    foreach(const Card * card, cards){
        CardItem *item = new CardItem(card);
        item->setPos(room_layout->drawpile);
        item->setEnabled(false);
        dashboard->addCardItem(item);
    }

log_box->appendLog("#DrawNCards", Self->getGeneralName(), QStringList(), QString(), QString::number(cards.length()));
}

Dashboard::addCardItem方法将代表牌的每个CardItem加入到自己的子列表中,使牌的图像可以显示在仪表盘中,并调整显示位置,添加到card_items中进行管理,设置card_item的信号处理曹函数.这里可以看到card_item的鼠标点击等信号的处理绑定.

void Dashboard::addCardItem(CardItem *card_item){
    card_item->filter(filter);

if(ClientInstance->getStatus() == Client::Playing)
        card_item->setEnabled(card_item->getFilteredCard()->isAvailable(Self));
    else{
        card_item->setEnabled(true);
        card_item->setEnabled(false);
    }

card_item->setPos(mapFromScene(card_item->pos()));
    card_item->setParentItem(this);
    card_item->setRotation(0.0);
    card_item->setFlags(ItemIsFocusable);
    card_item->setZValue(0.1);
    card_items << card_item;

connect(card_item, SIGNAL(clicked()), this, SLOT(onCardItemClicked()));
    connect(card_item, SIGNAL(thrown()), this, SLOT(onCardItemThrown()));
    connect(card_item, SIGNAL(enter_hover()), this, SLOT(onCardItemHover()));
    connect(card_item, SIGNAL(leave_hover()), this, SLOT(onCardItemLeaveHover()));

sortCards(sort_type);

handcard_num->setText(QString::number(Self->getHandcardNum()));
    //handcard_num->parentItem()->show();
}

看一下onCardItemClicked曹函数,这个函数没有参数,但借助于QT的信号曹机制,可以使用sender()获得信号的发出对象,即被点击的那张牌.借助设置牌的选择标志,触发card_selected信号,激活处理函数enableTargets(在roomScene构造函数中绑定connect(dashboard, SIGNAL(card_selected(const Card*)), this, SLOT(enableTargets(const Card*)));)这个函数去调整出牌界面的确定按钮的使能.

void Dashboard::onCardItemClicked(){
    CardItem *card_item = qobject_cast<CardItem *>(sender());
    if(!card_item)
        return;

if(view_as_skill){
        if(card_item->isPending()){
            card_item->unselect();
            pendings.removeOne(card_item);
        }else{
            card_item->select();
            pendings << card_item;
        }

updatePending();

}else{
        if(card_item->isPending()){
            unselectAll();
            emit card_selected(NULL);
        }else{
            unselectAll();
            card_item->select();
            //card_item->goBack();
            selected = card_item;

emit card_selected(selected->getFilteredCard());
        }
    }
}

出牌界面的确认按钮为ok_button,其点击事件处理函数为:

void RoomScene::doOkButton(){
    if(!ok_button->isEnabled())
        return;

useSelectedCard();
}

void RoomScene::useSelectedCard(){
    switch(ClientInstance->getStatus()){
    case Client::Playing:{
            const Card *card = dashboard->getSelected();
            if(card)
                useCard(card);
            break;
        }

......

}

过去客户端的状态,如果是正在游戏状态,则调用useCard函数:

void RoomScene::useCard(const Card *card){
    if(card->targetFixed() || card->targetsFeasible(selected_targets, Self))
        ClientInstance->onPlayerUseCard(card, selected_targets);

enableTargets(NULL);
}

onPlayerUseCard函数通知服务端玩家所出的牌和目标玩家:

void Client::onPlayerUseCard(const Card *card, const QList<const Player *> &targets){

if(card == NULL){
        replyToServer(S_COMMAND_USE_CARD, Json::Value::null);
        // request("useCard .");
    }else{
        Json::Value targetNames(Json::arrayValue);
        foreach(const Player *target, targets)
            targetNames.append(toJsonString(target->objectName()));

replyToServer(S_COMMAND_USE_CARD, toJsonArray(card->toString(), targetNames));

//if(target_names.isEmpty())
        //    request(QString("useCard %1->.").arg(card->toString()));
        //else
        //    request(QString("useCard %1->%2").arg(card->toString()).arg(target_names.join("+")));

if(status == Responsing)
            card_pattern.clear();
    }

setStatus(NotActive);
}

vs2008编译QT开源项目--太阳神三国杀源码分析(五) 分牌及出牌相关推荐

  1. vs2008编译QT开源项目--太阳神三国杀源码分析(一) 项目编译及整体分析

    请参看 http://tieba.baidu.com/f?kz=1508964881 按照上面的网址教程,下载三国杀源码,swig工具,并下载最新的QT4.8.2 for vs2008.我本机已经安装 ...

  2. vs2008编译QT开源项目--太阳神三国杀源码分析(四) 动画

    太阳神三国杀中,每当玩家出杀或吃桃子时,就会有一个动画效果,使界面非常生动绚丽.现在我们就分析一下QT中动画的原理,及实现方式,这里我们只分析吃桃子时的动画效果实现.由于三国杀有多个在线玩家同时游戏, ...

  3. vs2008编译QT开源项目--太阳神三国杀源码分析(三) 皮肤

    太阳神三国杀的界面很绚丽,界面上按钮的图标,鼠标移入移出时图标的变化,日志和聊天Widget的边框和半透明等效果,既可以通过代码来控制,也可以使用皮肤文件qss进行控制.下面我们分析一下三国杀的qss ...

  4. vs2008编译QT开源项目--太阳神三国杀源码分析(二) 客户端添加武将

    接着上篇文章继续分析,我们来看看进入到roomScene(房间场景)后,点击add a robot按钮,是如何创建武将的.首先找到add to robot按钮的创建代码: add_robot = ne ...

  5. Mac下编译太阳神三国杀源码

    Mac系统版本是10.13.2,Qt版本是5.11,参考官方博文:太阳神三国杀 Mac 版构建指南 安装Qt5 下载最新的Qt5安装包qt-opensource-mac-x64-5.11.0.dmg, ...

  6. 开源项目-太阳神三国杀-QT4编译教程

    待更中 转载于:https://www.cnblogs.com/sinpener/p/10734692.html

  7. android studio 项目源码_这个标星 2.3k+ 的项目带你 Android 源码分析从入门到放弃...

    上次写了一篇 Android 开发者的福利,介绍几款看源码的工具 ,这篇文章主要介绍了三款看 Android 源码的工具,后台有很多同学留言问,有没有分析源码的. 分析源码的文章也很多,但大多数文章不 ...

  8. (4.2.40)阿里开源路由框架ARouter的源码分析

    一需求背景 1 Android原生方案的不足 2 自定义路由框架的适用场景 3 对自定义路由框架的设想 二ARouter的概述 三ARouter的引入和使用 四源码分析 1 arouter-annot ...

  9. 一个完整的python项目源码-一个Python开源项目-哈勃沙箱源码剖析(下)

    前言 在上一篇中,我们讲解了哈勃沙箱的技术点,详细分析了静态检测和动态检测的流程.本篇接着对动态检测的关键技术点进行分析,包括strace,sysdig,volatility.volatility的介 ...

最新文章

  1. mysql的判断更新_mysql判断记录是否存在,存在则更新,不存在则插入
  2. 9个基于Java的搜索引擎框架
  3. 中国一线城市住宅地价同比增幅连续8个季度收窄
  4. sleep interrupted异常_Java高并发3中断线程以及isInterrupted与interrupted区别
  5. 浅析C#中的套接字编程
  6. 2018提高组训练Day2
  7. openresty + lua
  8. 纯Python包发布setup脚本编写示例
  9. YbtOJ#20072-[NOIP2020模拟赛B组Day6]相似子串【根号分治】
  10. Yii2.0 ActiveForm Input Fields
  11. python实时策略_Python策略模式
  12. 收藏版: 深度介绍分布式系统原理
  13. rust 手动关闭子线程_Rust入坑指南:齐头并进(上)
  14. js网页右下角弹窗js特效
  15. ardupilot-3.6.9 stabilize模式下(增稳模式)油门输入输出整理
  16. 关于在线银行卡支付限额问题 解决方案
  17. 访问学者博士后面签后的几种情况?
  18. 小论文撰写中常用单词的N种说法
  19. oracle数据库启动cannot mount database in EXCLUSIVE mode
  20. (附源码)Springboot税收风险管理系统 毕业设计 231058

热门文章

  1. GroupM小结1(14年8月28日)
  2. 程序员又“作死”了,用AI算法一键“脱”衣,遭全球网友炮轰
  3. 传感器集成温度传感器(DS1820)温度特性实验
  4. IBHLink S7++ 模块 AEG 调功器 Thyro-S 1S 400-100 HRL1
  5. JAVA社交平台项目第七天 JUC多线程
  6. 校验时间段是否与已有时间段存在交叉算法
  7. ue4 玩家控制器APlayerController
  8. 剑灵认证服务器系统出错,剑灵手游程序错误怎么办 和服务器断开官方解决
  9. WPT、WPCN、SWIPT的区别与联系
  10. AMD Opteron