深入解析棋牌湖南放炮罚,跑胡子手游后台源码(java版)
最近开发了一款湖南放炮罚的房卡模式带三级分销的手游,现在我就将我开发中的思路给朋友们分享一下。
首先介绍一下棋牌游戏最近的火热度吧。
最近微信群和朋友圈中忽然流行起了一款APP手机棋牌房卡麻将游戏,微信群各种被刷,朋友圈也被各霸了,约战的,晒成绩的,开好房间,约上好友,酣战一场!
这就是“ APP手机棋牌房卡麻将游戏”,一款将传统麻将机搬上智能手机,利用零散闲碎的空余时间与好友来一场说约就约的局的手机游戏。手机房卡麻将”来源于传统桌麻,游戏玩法与桌麻相差不大。玩家无需注册,可以直接微信登陆,方便快捷。登陆后便有一定数量免费房卡可供开房,开房后分享到微信群里,喊上好友输入房间号便可开局了。
说说放炮罚的游戏玩法,这个对理解开发很有作用。
下面就具体说说开发这一快吧,这里具体讲讲思路
一,设计数据库
数据库设计这块是根据现有的需求来设计的。比如现在我们是房卡模式,具有回放功能,战绩查询功能,发公告的功能。聊天等这里就可以根据自己的需求去设计。
二,通讯协议的拟定
这里的通讯协议像棋牌手游这种肯定是选用长连接。用soket协议,具体的自己去封装一下,这里不做具体的讲解。
三,创建房间
/**
* 创建房间
* @param avatar
*/
public void CreateRoom(Avatar avatar){
//这里设置房间的创建者是一个全局变量
createAvator = avatar;
//初始化一个集合用于装在这个房间的玩家VO
roomVO.setPlayerList(new ArrayList<AvatarVO>());
//这里初始化房间的玩家
playerList = new ArrayList<Avatar>();
//设置是否是庄家
avatar.avatarVO.setMain(true);
//这里将房间的信息设置到人员里面去,便于以后的调用
avatar.setRoomVO(roomVO);
//添加到玩牌的玩家集合中去
playerList.add(avatar);
roomVO.getPlayerList().add(avatar.avatarVO);
}
四,进入房间
/**
* 进入房间,
* @param avatar
*/
public boolean intoRoom(Avatar avatar){
//当玩家的人数等于三的时候就发送消息给该玩家房间人数满了。不能继续加入了
if(playerList.size() == 3){
try {
avatar.getSession().sendMsg(newErrorResponse(ErrorCode.Error_000011));
} catch (IOException e) {
e.printStackTrace();
}
return false;
}else {
//设置后面的加入房间的人都不是庄家因为我们默认是开房的人事庄家
avatar.avatarVO.setMain(false);
//房间号也放入avatarvo中
avatar.avatarVO.setRoomId(roomVO.getRoomId());
avatar.setRoomVO(roomVO);
//通知房间里面的其他几个玩家
noticJoinMess(avatar);
playerList.add(avatar);
roomVO.getPlayerList().add(avatar.avatarVO);
RoomManager.getInstance().addUuidAndRoomId(avatar.avatarVO.getAccount().getUuid()
,roomVO.getRoomId());
avatar.getSession().sendMsg(new JoinRoomResponse(1, roomVO));
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
return true;
}
}
}
五,检测是否可以开始游戏,退出房间,申请解散房间
这里就自己去想想实现代码了这里占不介绍。
六,游戏准备
当加入房间后我们去判断准备的人是不是三个人都准备了。如果都准备了我们就开始游戏。
七,初始化牌
由于现在我们是湖南的放炮罚。所以我们有八十张牌,
大的 壹、贰、叁、肆、伍、陆、柒、捌、玖、拾 各四张
小的 一、二、三、四、五、六、七、八、九、十 各四张
这里初始化一个全局的数组。size=20的 他的索引就是牌的值,数组对应索引出对应的值就是牌的数量。列如:
//代表的就是 二、二、三、三、三、四、四、四、五、六、七、七、九、九、九、十、叁、叁、叁这些牌
int[] paiArray={
0, 2, 3,3, 1, 1, 2, 0, 3, 1, 0, 0,3, 0, 0, 0, 0, 0, 0, 0};
下面就是初始化牌的代码
/**
* 初始化牌
*/
public void initCard(RoomVO value) {
roomVO = value;
paiCount = 20;
listCard = new ArrayList<Integer>();
for (int i = 0; i < paiCount; i++) {
for (int k = 0; k < 4; k++) {
listCard.add(i);
}
}
for (int i = 0; i < playerList.size(); i++) {
playerList.get(i).avatarVO.setPaiArray(new int[2][paiCount]);
}
// 洗牌
shuffleTheCards();
// 发牌
dealingTheCards();
}
/**
* 随机洗牌
*/
public void shuffleTheCards() {
Collections.shuffle(listCard);
Collections.shuffle(listCard);
}
当然发牌后就得检查是否有起手胡的情况和是否有提(放炮罚中是提其实就是相当于四川麻将的直杠)的情况了。如果有则给客户端发有胡的消息。或者是杠的消息。客户端操作后调用我们的胡的协议或者杠的协议。
八,出牌
/**
* 出牌
*
* @param avatar
* @param cardPoint
*/
public void putOffCard(Avatar avatar, int cardPoint) {
System.err.println("出牌:"+cardPoint);
System.err.println("出牌:玩家号"+playerList.indexOf(avatar)+"出牌前旧牌:"+avatar.printPaiString());
System.err.println("出牌:玩家号"+playerList.indexOf(avatar)+"出牌前新牌:"+avatar.printNewPaiString());
//添加当前的操作
for (int i = 0; i < playerList.size(); i++) {
playerList.get(i).avatarVO.addOperation(playerList.indexOf(avatar),"chupai",cardPoint);
}
avatar.avatarVO.setHasMopaiChupai(true);// 修改出牌 摸牌状态
// 已经出牌就清除所有的吃,碰,杠,胡的数组
clearAvatar();
//当前出的牌
putOffCardPoint = cardPoint;
System.out.println("出牌点数"+putOffCardPoint+"---出牌人索引:"+playerList.indexOf(avatar));
//出牌人索引
curAvatarIndex = playerList.indexOf(avatar);
PlayRecordOperation(curAvatarIndex, cardPoint, 1, -1, null, null);
avatar.pullCardFormList(putOffCardPoint);
for (int i = 0; i < playerList.size(); i++) {
// 不能返回给自己
if (i != curAvatarIndex) {
playerList
.get(i)
.getSession()
.sendMsg(
new ChuPaiResponse(1, putOffCardPoint,
curAvatarIndex));
// System.out.println("发送打牌消息----"+playerList.get(i).avatarVO.getAccount().getNickname());
} else {
playerList.get(i).gangIndex.clear();// 每次出牌就先清除缓存里面的可以杠的牌下标
}
}
// 出牌时,房间为可抢杠胡并且有癞子时才检测其他玩家有没胡的情况
Avatar ava;
StringBuffer sbs = new StringBuffer();
boolean checkGang=false;
boolean checkPeng=false;
boolean checkChi=false;
boolean checkHu=false;
int chick=0;
for (int i = 0; i < playerList.size(); i++) {
ava = playerList.get(i);
checkGang = ava.checkGang(putOffCardPoint);
checkPeng = ava.checkPeng(putOffCardPoint);
if(checkGang){
chick+=1;
}
if(checkPeng){
chick+=1;
}
}
for (int i = 0; i < playerList.size(); i++) {
ava = playerList.get(i);
StringBuffer sb = new StringBuffer();
ava.chiIndex.clear();
ava.biIndex.clear();
// 判断吃,碰, 胡 杠的时候需要把以前吃,碰,杠胡的牌踢出再计算
checkGang = ava.checkGang(putOffCardPoint);
checkPeng = ava.checkPeng(putOffCardPoint);
checkHu = ava.checkHu(putOffCardPoint);
checkChi = ava.checkChi(putOffCardPoint);
if (checkHu&&(ava.getUuId() != avatar.getUuId())&&!ava.guoHu) {
huAvatar.add(ava);
// 同时传会杠的牌的点数
sb.append("hu:" + putOffCardPoint + ",");
}
if (checkGang&&(ava.getUuId() != avatar.getUuId())) {
gangAvatar.add(ava);
System.err.println("打牌有杠 是否是二次杠的值:"+ava.isfirstGang);
if(ava.isfirstGang>0){
// 同时传会杠的牌的点数
sb.append("gang:" + putOffCardPoint + ","+"ergang"+",");
}else{
// 同时传会杠的牌的点数
sb.append("gang:" + putOffCardPoint + ","+""+",");
}
}
if (checkPeng&&!checkGang&&(ava.getUuId() != avatar.getUuId())&&!ava.guoHu) {
penAvatar.add(ava);
sb.append("peng:" + curAvatarIndex + ":"
+ putOffCardPoint + ",");
}
//放炮罚。。。检查吃只有下一家才能吃
if ( roomVO.getRoomType() == 4 &&checkChi&& getNextAvatarIndex() ==i&&!checkGang&&!checkPeng&&!ava.guoHu&&chick==0){
chiAvatar.add(ava);
sb.append("chi:"+curAvatarIndex+":"+ putOffCardPoint + ",");
}else{
ava.chiIndex.clear();
ava.biIndex.clear();
}
if (sb.length() > 1) {
/*
* try { Thread.sleep(300); } catch
* (InterruptedException e) { e.printStackTrace(); }
*/
try { Thread.sleep(2000); } catch
(InterruptedException e) { e.printStackTrace(); }
sbs.append(sb);
if(chiAvatar.size()>0&&!sb