Cocos2d-x制作《单机斗地主》源码解剖4:叫地主
依照游戏规则,发完牌后就进入叫地主环节。首先由玩家(人)开始叫地主,后续回合中,所有玩家(包括电脑玩家)按逆时针顺序叫地主。下面我们详细分析叫地主的模块。
void GameScene::Call(float dt) {
// 判断是否所有玩家都已叫过地主
if (!m_player->getCall() || !m_npcOne->getCall() || !m_npcTwo->getCall()) {
m_iCallTime %= 3;
switch (m_iCallTime) {
case 0: // 玩家选择地主分数
m_menu->setVisible(true);
// 处理1分选项
if (m_bCall[0]) {
CCMenuItemFont* itemFont = (CCMenuItemFont*)m_menu->getChildByTag(1);
itemFont->setEnabled(false);
} else {
CCMenuItemFont* itemFont = (CCMenuItemFont*)m_menu->getChildByTag(1);
itemFont->setEnabled(true);
}
// 处理2分选项
if (m_bCall[1]) {
CCMenuItemFont* itemFont = (CCMenuItemFont*)m_menu->getChildByTag(2);
itemFont->setEnabled(false);
} else {
CCMenuItemFont* itemFont = (CCMenuItemFont*)m_menu->getChildByTag(2);
itemFont->setEnabled(true);
}
// 处理3分选项
if (m_bCall[2]) {
CCMenuItemFont* itemFont = (CCMenuItemFont*)m_menu->getChildByTag(3);
itemFont->setEnabled(false);
} else {
CCMenuItemFont* itemFont = (CCMenuItemFont*)m_menu->getChildByTag(3);
itemFont->setEnabled(true);
}
break;
case 1: // 电脑1选择地主分数
++m_iCallTime;
NpcCall(m_npcTwo, m_npcOne); // 电脑玩家叫地主
ShowFenShu(ccp(m_npcTwo->getPoint().x - 88, m_npcTwo->getPoint().y), m_npcTwo->getCallNum());
break;
case 2: // 电脑2选择地主分数
++m_iCallTime;
NpcCall(m_npcOne, m_npcTwo);
ShowFenShu(ccp(m_npcOne->getPoint().x + 88, m_npcOne->getPoint().y), m_npcOne->getCallNum());
break;
}
} else {
// 判断谁是地主并把三张牌给他
// 如果所有玩家都没叫地主,将地主给一开始选地主的玩家
if (m_player->getCallNum() == 0 && m_npcOne->getCallNum() == 0 && m_npcTwo->getCallNum() == 0) {
switch (m_iCall % 3) {
case 0:
m_player->setCallNum(3);
break;
case 1:
m_npcTwo->setCallNum(3);
break;
case 2:
m_npcOne->setCallNum(3);
break;
default:
break;
}
}
// 比较各玩家叫分,分数最高者成为地主
CCObject* object;
if (m_player->getCallNum() > m_npcOne->getCallNum() && m_player->getCallNum() > m_npcTwo->getCallNum()) {
CCArray* arrTem = CCArray::create();
CCARRAY_FOREACH(m_Three->getArrPk(), object) {
Poker* pk = (Poker*)object;
Poker* pkCopy = pk->copy();
arrTem->addObject(pkCopy);
addChild(pkCopy);
m_player->getArrPk()->addObject(pk);
m_player->setIsDiZhu(true);
m_npcOne->setIsDiZhu(false);
m_npcTwo->setIsDiZhu(false);
m_iOutCard = 0;
}
m_Three->getArrPk()->removeAllObjects();
m_Three->getArrPk()->addObjectsFromArray(arrTem);
m_Three->updatePkWeiZhi();
m_player->updatePkWeiZhi();
// 显示地主标签
m_lableDiZhu->setPosition(playerDiZhuLablePt);
m_lableDiZhu->setVisible(true);
} else if (m_npcOne->getCallNum() > m_player->getCallNum() && m_npcOne->getCallNum() > m_npcTwo->getCallNum()) {
CCArray* arrTem = CCArray::create();
CCARRAY_FOREACH(m_Three->getArrPk(), object) {
Poker* pk = (Poker*)object;
Poker* pkCopy = pk->copy();
arrTem->addObject(pkCopy);
addChild(pkCopy);
m_npcOne->getArrPk()->addObject(pk);
m_player->setIsDiZhu(false);
m_npcOne->setIsDiZhu(true);
m_npcTwo->setIsDiZhu(false);
m_iOutCard = 2;
}
m_Three->getArrPk()->removeAllObjects();
m_Three->getArrPk()->addObjectsFromArray(arrTem);
m_Three->updatePkWeiZhi();
// 重新分拆牌
m_npcOne->m_vecPX.clear();
m_npcOne->updatePkWeiZhi();
FenChaiNpcPai(m_npcOne);
// 显示地主标签
m_lableDiZhu->setPosition(npcOneDiZhuLablePt);
m_lableDiZhu->setVisible(true);
} else if (m_npcTwo->getCallNum() > m_npcOne->getCallNum() && m_npcTwo->getCallNum() > m_player->getCallNum()) {
CCArray* arrTem = CCArray::create();
CCARRAY_FOREACH(m_Three->getArrPk(), object) {
Poker* pk = (Poker*)object;
Poker* pkCopy = pk->copy();
arrTem->addObject(pkCopy);
addChild(pkCopy);
m_npcTwo->getArrPk()->addObject(pk);
m_player->setIsDiZhu(false);
m_npcOne->setIsDiZhu(false);
m_npcTwo->setIsDiZhu(true);
m_iOutCard = 1;
}
m_Three->getArrPk()->removeAllObjects();
m_Three->getArrPk()->addObjectsFromArray(arrTem);
m_Three->updatePkWeiZhi();
// 重新分拆牌
m_npcTwo->m_vecPX.clear();
m_npcTwo->updatePkWeiZhi();
FenChaiNpcPai(m_npcTwo);
// 显示地主标签
m_lableDiZhu->setPosition(npcTwoDiZhuLablePt);
m_lableDiZhu->setVisible(true);
}
m_iState = 2;
++m_iCall;
m_iCallTime = m_iCall;
unschedule(schedule_selector(GameScene::Call));
// 选定地主后显示三张牌
CCARRAY_FOREACH(m_Three->getArrPk(), object) {
Poker* pk = (Poker*)object;
pk->showFront();
}
// 移除叫地主分数的显示
this->removeChildByTag(FenShu);
this->removeChildByTag(FenShu);
this->removeChildByTag(FenShu);
// 使主玩家的牌为可点击状态
CCARRAY_FOREACH(m_player->getArrPk(), object) {
Poker* pk = (Poker*)object;
pk->setDianJi(true);
}
}
}
上述代码中的判断逻辑较为清晰:首先检查各玩家是否都已完成叫分。若未全部叫分,则根据当前回合顺序,依次让玩家和电脑玩家进行叫分操作;若所有玩家都已叫分,则比较各玩家的叫分,分数最高者成为地主,并将三张底牌给予地主。若所有玩家都未叫分,则将地主身份给予最先开始叫分的玩家。
接下来,我们分析电脑玩家的叫分逻辑。为简化处理,电脑玩家采用随机选择分数的方式,代码如下:
void GameScene::NpcCall(Player* npc, Player* npc1) {
int i = rand() % 4;
if (i == 3) { // 3表示不叫
npc->setCall(true); // 设置已叫地主
npc->setCallNum(0); // 分数设置为0
} else {
while (m_bCall[i] == true) { // bool m_bCall[3]; 分数是否被叫,m_bCall[0]:1分,m_bCall[1]:2分,m_bCall[2]:3分
i = rand() % 3; // 重新选择分数
}
m_bCall[i] = true;
npc->setCall(true);
npc->setCallNum(i + 1);
// 如果叫3分,设置其他玩家已叫地主状态
if (i == 2) {
m_player->setCall(true);
npc1->setCall(true);
}
}
}
在这段代码中,电脑玩家随机生成一个0 - 3的整数。若为3,则表示不叫地主;若为0 - 2,则表示叫相应分数。若所选分数已被其他玩家叫过,则重新随机选择,直至选到未被叫过的分数。若电脑玩家叫了3分,则将其他玩家的叫地主状态设置为已叫,结束叫地主环节。