规则文档理清楚,开始代码层面对比和校对
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,185 @@
|
||||
///////////////////////////////////////////////////
|
||||
////////// cls_youle_erqiwang_desk: 牌桌 //////////
|
||||
///////////////////////////////////////////////////
|
||||
var cls_youle_erqiwang_desk = cls_youle_erqiwang_desk || {
|
||||
|
||||
countdown_jiaofen: 15, //叫分倒计时
|
||||
countdown_xuanzhu: 20, //选主倒计时
|
||||
countdown_maipai: 25, //埋牌倒计时
|
||||
countdown_chupai: 30, //出牌倒计时
|
||||
|
||||
//创建牌桌对象
|
||||
new: function(o_room){
|
||||
|
||||
var desk = {};
|
||||
|
||||
desk.o_room = o_room; //桌所属的房间对象
|
||||
desk.seatlist = []; //玩家得分
|
||||
desk.seatlist.push([0, []]); //第一位为累积得分,第二位为每局得分
|
||||
desk.seatlist.push([0, []]);
|
||||
desk.seatlist.push([0, []]);
|
||||
desk.prepare = [0,0,0]; //玩家准备状态
|
||||
desk.paiju_list = []; //牌局列表
|
||||
|
||||
//方法
|
||||
desk.method = {};
|
||||
|
||||
//当前牌局
|
||||
desk.method.curr_paiju = function(){
|
||||
return desk.paiju_list[desk.paiju_list.length - 1];
|
||||
}
|
||||
|
||||
//新牌局
|
||||
desk.method.do_new_paiju = function(firstseat){
|
||||
cls_youle_erqiwang_desk.do_new_paiju(desk, firstseat);
|
||||
}
|
||||
|
||||
//叫分倒计时
|
||||
desk.method.get_countdown_jiaofen = function(){
|
||||
return cls_youle_erqiwang_desk.countdown_jiaofen;
|
||||
}
|
||||
|
||||
//叫主倒计时
|
||||
desk.method.get_countdown_xuanzhu = function(){
|
||||
return cls_youle_erqiwang_desk.countdown_xuanzhu;
|
||||
}
|
||||
|
||||
//埋牌倒计时
|
||||
desk.method.get_countdown_maipai = function(){
|
||||
return cls_youle_erqiwang_desk.countdown_maipai;
|
||||
}
|
||||
|
||||
//出牌倒计时
|
||||
desk.method.get_countdown_chupai = function(){
|
||||
return cls_youle_erqiwang_desk.countdown_chupai;
|
||||
}
|
||||
|
||||
//准备
|
||||
desk.method.do_prepare = function(seat){
|
||||
return cls_youle_erqiwang_desk.do_prepare(desk, seat);
|
||||
}
|
||||
|
||||
//获取大局结算包
|
||||
desk.method.get_desk_account = function(msg){
|
||||
return cls_youle_erqiwang_desk.get_desk_account(desk, msg);
|
||||
}
|
||||
|
||||
return desk;
|
||||
},
|
||||
|
||||
//新开一局
|
||||
do_new_paiju: function(o_desk, firstseat){
|
||||
//恢复玩家准备状态
|
||||
o_desk.prepare = [0,0,0];
|
||||
|
||||
if (o_desk.paiju_list.length > 0){
|
||||
delete o_desk.paiju_list[o_desk.paiju_list.length - 1].tmp_jiesuan_aset;
|
||||
}
|
||||
|
||||
//新开一局
|
||||
cls_youle_erqiwang_paiju.new(o_desk, firstseat);
|
||||
|
||||
var msg = {};
|
||||
msg.app = "youle";
|
||||
msg.route = "erqiwang";
|
||||
msg.rpc = "fapai";
|
||||
msg.data = {};
|
||||
msg.data.asetidx = o_desk.paiju_list.length;
|
||||
msg.data.asetcount = o_desk.o_room.asetcount;
|
||||
msg.data.seat = o_desk.method.curr_paiju().method.get_callgrade_seat();
|
||||
msg.data.countdown = o_desk.method.get_countdown_jiaofen();
|
||||
for (var i = 0; i < o_desk.o_room.seatlist.length; i++) {
|
||||
msg.conmode = o_desk.o_room.seatlist[i].conmode;
|
||||
msg.fromid = o_desk.o_room.seatlist[i].fromid;
|
||||
msg.data.cards = o_desk.method.curr_paiju().method.get_seat_cards(i);
|
||||
youle_erqiwang.app.SendPack(msg);
|
||||
}
|
||||
},
|
||||
|
||||
//准备
|
||||
do_prepare: function(o_desk, seat){
|
||||
o_desk.prepare[seat] = 1;
|
||||
if (o_desk.prepare[0] && o_desk.prepare[1] && o_desk.prepare[2]){
|
||||
//新开一局
|
||||
var paiju = o_desk.method.curr_paiju();
|
||||
var firstseat = paiju.banker;
|
||||
if (paiju.result == 1 || paiju.result == 2){
|
||||
firstseat = (firstseat + 1) % 3;
|
||||
}
|
||||
cls_youle_erqiwang_desk.do_new_paiju(o_desk, firstseat);
|
||||
} else {
|
||||
var msg = {};
|
||||
msg.app = "youle";
|
||||
msg.route = "erqiwang";
|
||||
msg.rpc = "zhunbei";
|
||||
msg.data = {};
|
||||
msg.data.seat = seat;
|
||||
o_desk.o_room.method.sendpack_toother(msg, -1);
|
||||
}
|
||||
},
|
||||
|
||||
//大局结算包
|
||||
get_desk_account: function(o_desk, msg){
|
||||
//保存战绩
|
||||
var do_save_grade = function(){
|
||||
var o_gameinfo1 = {};
|
||||
o_gameinfo1.roomcode = o_desk.o_room.roomcode;
|
||||
o_gameinfo1.asetcount = o_desk.paiju_list.length;
|
||||
o_gameinfo1.createtime = o_desk.o_room.createtime;
|
||||
o_gameinfo1.makewartime = o_desk.o_room.makewartime;
|
||||
o_gameinfo1.players = [];
|
||||
for (var i = 0; i < o_desk.o_room.seatlist.length; i++){
|
||||
var _player = {};
|
||||
_player.seat = i;
|
||||
_player.playerid = o_desk.o_room.seatlist[i].playerid;
|
||||
_player.name = o_desk.o_room.seatlist[i].nickname;
|
||||
_player.avatar = o_desk.o_room.seatlist[i].avatar;
|
||||
_player.score = o_desk.seatlist[i][0];
|
||||
o_gameinfo1.players.push(_player);
|
||||
}
|
||||
|
||||
var o_gameinfo2 = [];
|
||||
for (var i = 0; i < o_desk.paiju_list.length; i++) {
|
||||
var _paiju = {};
|
||||
_paiju.starttime = o_desk.paiju_list[i].starttime;
|
||||
_paiju.endtime = o_desk.paiju_list[i].endtime;
|
||||
_paiju.seatlist = [0, 0, 0];
|
||||
_paiju.seatlist[0] = o_desk.seatlist[0][1][i];
|
||||
_paiju.seatlist[1] = o_desk.seatlist[1][1][i];
|
||||
_paiju.seatlist[2] = o_desk.seatlist[2][1][i];
|
||||
_paiju.callproc = o_desk.paiju_list[i].callproc;
|
||||
_paiju.banker = o_desk.paiju_list[i].banker;
|
||||
_paiju.call = o_desk.paiju_list[i].call;
|
||||
_paiju.flower = o_desk.paiju_list[i].flower;
|
||||
_paiju.result = o_desk.paiju_list[i].result;
|
||||
//数据量太多,json转数组后进行存储
|
||||
_paiju.cards = [];
|
||||
for (var j = 0; j < o_desk.paiju_list[i].cards.length; j++) {
|
||||
var _pai = o_desk.paiju_list[i].cards[j];
|
||||
var _card = [];
|
||||
_card.push(_pai.id);
|
||||
_card.push(_pai.flower);
|
||||
_card.push(_pai.number);
|
||||
_card.push(_pai.score);
|
||||
_card.push(_pai.dealowner);
|
||||
_card.push(_pai.playround);
|
||||
_card.push(_pai.playindex);
|
||||
_card.push(_pai.playowner);
|
||||
_paiju.cards.push(_card);
|
||||
}
|
||||
o_gameinfo2.push(_paiju);
|
||||
}
|
||||
|
||||
//累积游戏得分
|
||||
for (var i = 0; i < o_desk.o_room.seatlist.length; i++) {
|
||||
o_desk.o_room.seatlist[i].gameinfo.grade = o_desk.seatlist[i][0];
|
||||
}
|
||||
//保存战绩
|
||||
youle_erqiwang.import.save_grade(o_desk.o_room, o_gameinfo1, o_gameinfo2, 1);
|
||||
}
|
||||
min_ontimeout(do_save_grade, 1000);
|
||||
|
||||
msg.data.account = o_desk.seatlist;
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
///////////////////////////////////////////////////
|
||||
/////// cls_youle_erqiwang_export: 输出接口 ///////
|
||||
///////////////////////////////////////////////////
|
||||
var cls_youle_erqiwang_export = cls_youle_erqiwang_export || {
|
||||
|
||||
new: function() {
|
||||
|
||||
var exp = {};
|
||||
|
||||
//解析roomtype获取开房所需房卡
|
||||
exp.get_needroomcard = function(roomtype){
|
||||
switch (roomtype[2])
|
||||
{
|
||||
case 1: //房主扣卡
|
||||
switch (roomtype[0])
|
||||
{
|
||||
case 1:
|
||||
return 1;
|
||||
break;
|
||||
case 2:
|
||||
return 2;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 2: //每人扣卡
|
||||
switch (roomtype[0])
|
||||
{
|
||||
case 1:
|
||||
return 1;
|
||||
break;
|
||||
case 2:
|
||||
return 2;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//解析roomtype获取总局数
|
||||
exp.get_asetcount = function(roomtype){
|
||||
switch (roomtype[0])
|
||||
{
|
||||
case 1:
|
||||
return 2;
|
||||
break;
|
||||
case 2:
|
||||
return 4;
|
||||
break;
|
||||
default:
|
||||
return 4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//加入房间时根据roomtype获取需要的房卡数量
|
||||
exp.get_needroomcard_joinroom = function(roomtype){
|
||||
switch (roomtype[2])
|
||||
{
|
||||
case 1: //房主扣卡
|
||||
return 0;
|
||||
break;
|
||||
case 2: //每人扣卡
|
||||
switch (roomtype[0])
|
||||
{
|
||||
case 1:
|
||||
return 1;
|
||||
break;
|
||||
case 2:
|
||||
return 2;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//开战
|
||||
exp.makewar = function(o_room){
|
||||
//牌桌对象
|
||||
o_room.o_desk = cls_youle_erqiwang_desk.new(o_room);
|
||||
for (var i = 0; i < o_room.seatlist.length; i++) {
|
||||
o_room.seatlist[i].gameinfo.isbet = 1;
|
||||
}
|
||||
|
||||
//开始第一局
|
||||
var do_newpaiju = function(){
|
||||
o_room.o_desk.method.do_new_paiju(0);
|
||||
}
|
||||
min_ontimeout(do_newpaiju, 1000);
|
||||
}
|
||||
|
||||
//获取某个玩家的牌桌整桌当前信息
|
||||
exp.get_deskinfo = function(o_room, seat){
|
||||
if (!o_room.o_desk){
|
||||
return null;
|
||||
}
|
||||
var paiju = o_room.o_desk.method.curr_paiju();
|
||||
var deskinfo = {};
|
||||
deskinfo.count = o_room.asetcount; //总局数
|
||||
deskinfo.idx = paiju.idx; //当前局数
|
||||
deskinfo.PlayerInfo = []; //玩家当前累积得分
|
||||
for (var i = 0; i < o_room.o_desk.seatlist.length; i++) {
|
||||
deskinfo.PlayerInfo.push(o_room.o_desk.seatlist[i][0]);
|
||||
};
|
||||
deskinfo.step = paiju.step; //牌局状态
|
||||
deskinfo.MyCards = paiju.method.get_seat_cards(seat); //我手上的牌
|
||||
switch (paiju.step){
|
||||
case 1: //发完牌叫分
|
||||
deskinfo.CallRun = {};
|
||||
//当前叫分位置
|
||||
deskinfo.CallRun.seat = paiju.method.get_callgrade_seat();
|
||||
//叫分倒计时
|
||||
deskinfo.CallRun.countdown = o_room.o_desk.method.get_countdown_jiaofen();
|
||||
//当前叫分
|
||||
deskinfo.CallRun.nowcall = paiju.method.get_callgrade_value();
|
||||
//当前叫分倍数
|
||||
deskinfo.CallRun.multiple = cls_youle_erqiwang_arith.get_multiple_bycall(deskinfo.CallRun.nowcall);
|
||||
//所有玩家的叫分情况
|
||||
deskinfo.CallRun.call = [null, null, null];
|
||||
deskinfo.CallRun.call[0] = paiju.method.get_seat_callgrade(0);
|
||||
deskinfo.CallRun.call[1] = paiju.method.get_seat_callgrade(1);
|
||||
deskinfo.CallRun.call[2] = paiju.method.get_seat_callgrade(2);
|
||||
break;
|
||||
case 2: //选主
|
||||
deskinfo.ChooseMain = {};
|
||||
//庄家
|
||||
deskinfo.ChooseMain.banker = paiju.banker;
|
||||
//庄家叫分
|
||||
deskinfo.ChooseMain.call = paiju.call;
|
||||
//庄家叫分倍数
|
||||
deskinfo.ChooseMain.multiple = cls_youle_erqiwang_arith.get_multiple_bycall(paiju.call);
|
||||
//选主倒计时
|
||||
deskinfo.ChooseMain.countdown = o_room.o_desk.method.get_countdown_xuanzhu();
|
||||
//底牌
|
||||
deskinfo.ChooseMain.bottomcards = paiju.method.get_bottomcards();
|
||||
//是否可投降
|
||||
if (paiju.call < 65){
|
||||
deskinfo.ChooseMain.touxiang = 0;
|
||||
} else {
|
||||
deskinfo.ChooseMain.touxiang = 1;
|
||||
}
|
||||
break;
|
||||
case 3: //埋牌
|
||||
deskinfo.BuryCards = {};
|
||||
//庄家
|
||||
deskinfo.BuryCards.banker = paiju.banker;
|
||||
//庄家叫分
|
||||
deskinfo.BuryCards.call = paiju.call;
|
||||
//庄家叫分倍数
|
||||
deskinfo.BuryCards.multiple = cls_youle_erqiwang_arith.get_multiple_bycall(paiju.call);
|
||||
//埋牌倒计时
|
||||
deskinfo.BuryCards.countdown = o_room.o_desk.method.get_countdown_maipai();
|
||||
//底牌
|
||||
deskinfo.BuryCards.bottomcards = paiju.method.get_bottomcards();
|
||||
//主牌花色
|
||||
deskinfo.BuryCards.flower = paiju.flower;
|
||||
//是否可投降
|
||||
if (paiju.call < 65){
|
||||
deskinfo.BuryCards.touxiang = 0;
|
||||
} else {
|
||||
deskinfo.BuryCards.touxiang = 1;
|
||||
}
|
||||
break;
|
||||
case 4: //投降
|
||||
deskinfo.Surrender = {};
|
||||
//庄家
|
||||
deskinfo.Surrender.banker = paiju.banker;
|
||||
//庄家叫分
|
||||
deskinfo.Surrender.call = paiju.call;
|
||||
//庄家叫分倍数
|
||||
deskinfo.Surrender.multiple = cls_youle_erqiwang_arith.get_multiple_bycall(paiju.call);
|
||||
//出牌倒计时
|
||||
deskinfo.Surrender.countdown = o_room.o_desk.method.get_countdown_chupai();
|
||||
//主牌花色
|
||||
deskinfo.Surrender.flower = paiju.flower;
|
||||
if (seat == paiju.banker){
|
||||
//埋的牌
|
||||
deskinfo.Surrender.bottomcards = paiju.method.get_burycard();
|
||||
}
|
||||
//是否可投降
|
||||
if (paiju.call < 65){
|
||||
deskinfo.Surrender.touxiang = 0;
|
||||
} else {
|
||||
deskinfo.Surrender.touxiang = 1;
|
||||
}
|
||||
break;
|
||||
case 5: //出牌
|
||||
deskinfo.PushCards = {};
|
||||
//庄家
|
||||
deskinfo.PushCards.banker = paiju.banker;
|
||||
//庄家叫分
|
||||
deskinfo.PushCards.call = paiju.call;
|
||||
//庄家叫分倍数
|
||||
deskinfo.PushCards.multiple = cls_youle_erqiwang_arith.get_multiple_bycall(paiju.call);
|
||||
//出牌倒计时
|
||||
deskinfo.PushCards.countdown = o_room.o_desk.method.get_countdown_chupai();
|
||||
//主牌花色
|
||||
deskinfo.PushCards.flower = paiju.flower;
|
||||
//捡分
|
||||
var jian_grade = paiju.method.get_jian_grade();
|
||||
if (seat == paiju.banker){
|
||||
//埋的牌
|
||||
deskinfo.PushCards.bottomcards = paiju.method.get_burycard();
|
||||
} else {
|
||||
//闲家当前的捡分分牌
|
||||
deskinfo.PushCards.gradecards = jian_grade.cards;
|
||||
}
|
||||
//闲家当前的捡分分数
|
||||
deskinfo.PushCards.grade = jian_grade.grade;
|
||||
//当前的出牌情况
|
||||
deskinfo.PushCards.playproc = paiju.playproc;
|
||||
//座位列表
|
||||
deskinfo.PushCards.seatlist = paiju.seatlist;
|
||||
//出牌历史
|
||||
deskinfo.PushCards.pushlist = [];
|
||||
for (var i = 0; i < paiju.cards.length; i++) {
|
||||
var pai = paiju.cards[i];
|
||||
if (pai.playround > 0){
|
||||
if (deskinfo.PushCards.pushlist.length < pai.playround){
|
||||
deskinfo.PushCards.pushlist.length = pai.playround;
|
||||
}
|
||||
if (!deskinfo.PushCards.pushlist[pai.playround - 1]){
|
||||
deskinfo.PushCards.pushlist[pai.playround - 1] = [[], [], []];
|
||||
}
|
||||
|
||||
var owner = pai.dealowner;
|
||||
if (owner == 0){
|
||||
owner = paiju.banker + 1;
|
||||
}
|
||||
deskinfo.PushCards.pushlist[pai.playround - 1][owner - 1].push(i);
|
||||
}
|
||||
}
|
||||
//按大小排序
|
||||
for (var i = 0; i < deskinfo.PushCards.pushlist.length; i++) {
|
||||
for (var j = 0; j < deskinfo.PushCards.pushlist.length; j++) {
|
||||
deskinfo.PushCards.pushlist[i][j] = cls_youle_erqiwang_arith.order_cards(pai.flower, deskinfo.PushCards.pushlist[i][j]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 6: //结算
|
||||
deskinfo.Balance = {};
|
||||
//玩家的准备状态
|
||||
deskinfo.Balance.readystate = o_room.o_desk.prepare;
|
||||
if (o_room.o_desk.prepare[seat] == 0){
|
||||
//结算情况
|
||||
deskinfo.Balance.aset = paiju.tmp_jiesuan_aset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return deskinfo;
|
||||
}
|
||||
|
||||
//解散房间
|
||||
exp.get_disbandRoom = function(o_room){
|
||||
var msg = {};
|
||||
return o_room.o_desk.method.curr_paiju().method.get_paiju_account(2, msg);
|
||||
}
|
||||
|
||||
return exp;
|
||||
}
|
||||
}
|
||||
|
||||
//对内输出接口
|
||||
youle_erqiwang.export = cls_youle_erqiwang_export.new();
|
||||
@@ -0,0 +1,30 @@
|
||||
///////////////////////////////////////////////////
|
||||
/////// cls_youle_platform_import: 输入接口 ///////
|
||||
///////////////////////////////////////////////////
|
||||
var cls_youle_erqiwang_import = cls_youle_erqiwang_import || {
|
||||
|
||||
new: function() {
|
||||
|
||||
var imp = {};
|
||||
|
||||
//检查玩家
|
||||
imp.check_player = function(agentid, gameid, roomcode, seat, playerid, conmode, fromid){
|
||||
return youle_erqiwang.app.youle_room.export.check_player(agentid, gameid, roomcode, seat, playerid, conmode, fromid);
|
||||
}
|
||||
|
||||
//第一局结算扣除房卡
|
||||
imp.deduct_roomcard = function(o_room){
|
||||
return youle_erqiwang.app.youle_room.export.deduct_roomcard(o_room);
|
||||
}
|
||||
|
||||
//保存战绩
|
||||
imp.save_grade = function(o_room, o_gameinfo1, o_gameinfo2, freeroomflag){
|
||||
youle_erqiwang.app.youle_room.export.save_grade(o_room, o_gameinfo1, o_gameinfo2, freeroomflag);
|
||||
}
|
||||
|
||||
return imp;
|
||||
}
|
||||
}
|
||||
|
||||
// 输入接口
|
||||
youle_erqiwang.import = cls_youle_erqiwang_import.new();
|
||||
@@ -0,0 +1,61 @@
|
||||
///////////////////////////////////////////////////
|
||||
////////// cls_youle_erqiwang_pai: 牌 //////////
|
||||
///////////////////////////////////////////////////
|
||||
var cls_youle_erqiwang_pai = cls_youle_erqiwang_pai || {
|
||||
|
||||
//创建单张牌对象
|
||||
new: function(id, flower, number, score, dealowner){
|
||||
/*
|
||||
参数说明:
|
||||
id 绝对id
|
||||
flower 牌面花色
|
||||
number 牌面数值
|
||||
code 牌编码
|
||||
|
||||
扑克牌的统一编码规则:
|
||||
1,牌面花色的定义 5:王 4:黑桃 3:红心 2:梅花 1:方块
|
||||
2,牌面数值的定义 01:A 02:2 03:3 ... 09:9 10:10 11:J 12:Q 13:K 53:小王 54:大王
|
||||
3,牌编码的定义
|
||||
3.1 常主的牌编码定义:
|
||||
未叫主之前:
|
||||
2的牌编码定义 黑桃2:2402 红心2:2302 梅花2:2202 方块2:2102
|
||||
7的牌编码定义 黑桃7:7407 红心2:7307 梅花7:7207 方块7:7107
|
||||
王的牌编码定义 小王:9553 大王:9554
|
||||
叫主之后:
|
||||
主2牌编码定义 原有牌编码的基础上+1000,例如红心为主牌,则红心2的牌编码变为3302
|
||||
主7牌编码定义 原有牌编码的基础上+1000,例如红心为主牌,则红心7的牌编码变为8307
|
||||
3.2 普通牌的牌编码定义
|
||||
A的定义:14
|
||||
未叫主之前:
|
||||
3位数字 第一位为牌面花色 第二三位为牌面数值
|
||||
叫主之后:
|
||||
主牌花色 4位数字,即在原有牌编码的基础上+1000,表示是主牌
|
||||
非主牌花色 3位数字,即原有牌编码不变
|
||||
3.3 综上所述
|
||||
牌编码是3位的,即小于1000的都是非主牌
|
||||
牌编码是4位的,即大于1000的都是主牌
|
||||
可根据牌编码进行牌大小的比较和排序
|
||||
4 牌型定义
|
||||
>100: 单张, 101一张单张,102两张单张,103三张单张 ...
|
||||
>200: 对子, 201一对,202两对,203三对 ...
|
||||
>300: 拖拉机,302两连对拖拉机,303三连对拖拉机 ...
|
||||
|
||||
*/
|
||||
var pai = {};
|
||||
pai.id = id; //绝对id,即数组下标,从0开始计数
|
||||
pai.flower = flower; //牌面花色
|
||||
pai.number = number; //牌面数值
|
||||
pai.score = score; //牌在游戏中的分值
|
||||
pai.dealowner = dealowner; //发牌状态
|
||||
//-1:规则去除的牌
|
||||
// 0:未发的牌,即底牌
|
||||
//>0:发牌发到谁手上,从1开始计数,即位置编号+1,位置编号是从0开始计数的
|
||||
pai.playround = -1; //出牌状态
|
||||
//-1:未出的牌
|
||||
// 0:埋牌
|
||||
//>0:牌是第几轮出出去的,从1开始计数
|
||||
pai.playindex = -1; //本轮中的出牌顺序,从1开始计数
|
||||
pai.playowner = -1; //出牌后被谁得到,与位置编号对应
|
||||
return pai;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,863 @@
|
||||
///////////////////////////////////////////////////
|
||||
////////// cls_youle_erqiwang_paiju: 牌局 /////////
|
||||
///////////////////////////////////////////////////
|
||||
var cls_youle_erqiwang_paiju = cls_youle_erqiwang_paiju || {
|
||||
|
||||
new: function(o_desk, firstseat) {
|
||||
var paiju = {};
|
||||
o_desk.paiju_list.push(paiju);
|
||||
|
||||
// ================= 实例的私有属性 ================= //
|
||||
//发牌过程中待发的位置列表
|
||||
var tmpdeal = [];
|
||||
//三家需要各发28张牌
|
||||
for (var i = 0; i < 28; i++){
|
||||
tmpdeal.push(0); //0表示第一个玩家的位置
|
||||
tmpdeal.push(1); //1表示第二个玩家的位置
|
||||
tmpdeal.push(2); //2表示第三个玩家的位置
|
||||
}
|
||||
//8张底牌
|
||||
for (var i = 0; i < 8; i++){
|
||||
tmpdeal.push(-1); //-1 表示是底牌
|
||||
}
|
||||
|
||||
// ================= 实例的私有方法 ================= //
|
||||
|
||||
//初始化座位列表
|
||||
var init_seatlist = function(){
|
||||
//第一层是玩家列表,与位置序号对应
|
||||
//第二层前4个数组是花色列表,与花色-1对应
|
||||
//第三层的第一位是无该花色的牌标志
|
||||
//第三层的第二位是该花色无对标志
|
||||
//第二层第5个数组是报副情况
|
||||
//第三层的第一位是剩余主牌数量
|
||||
//第三层的第二位是剩余主牌对子
|
||||
//0号位
|
||||
paiju.seatlist.push([[0,0],[0,0],[0,0],[0,0],[-1,-1]]);
|
||||
//1号位
|
||||
paiju.seatlist.push([[0,0],[0,0],[0,0],[0,0],[-1,-1]]);
|
||||
//2号位
|
||||
paiju.seatlist.push([[0,0],[0,0],[0,0],[0,0],[-1,-1]]);
|
||||
}
|
||||
|
||||
//初始化牌列表并发牌
|
||||
var init_cards = function(){
|
||||
//两副牌
|
||||
for (var i = 1; i <= 2; i++){
|
||||
//四种花色
|
||||
for (var j = 1; j <= 4; j++){
|
||||
for (var k = 1; k <= 13; k++){
|
||||
//牌id
|
||||
var id = (i - 1) * 54 + (j - 1) * 13 + k - 1;
|
||||
//分值
|
||||
var score = 0;
|
||||
//发牌状态
|
||||
var dealowner = 0;
|
||||
//特殊处理
|
||||
switch (k)
|
||||
{
|
||||
case 5:
|
||||
score = 5;
|
||||
break;
|
||||
case 10:
|
||||
score = 10;
|
||||
break;
|
||||
case 13:
|
||||
score = 10;
|
||||
break;
|
||||
case 3:
|
||||
dealowner = -1;
|
||||
break;
|
||||
case 4:
|
||||
dealowner = -1;
|
||||
break;
|
||||
}
|
||||
var pai = cls_youle_erqiwang_pai.new(id, j, k, score, dealowner);
|
||||
paiju.cards.push(pai);
|
||||
//发牌
|
||||
if (k != 3 && k != 4){
|
||||
do_dealpai(pai);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//小王
|
||||
var pai = cls_youle_erqiwang_pai.new((i - 1) * 54 + 53 - 1, 5, 53, 0, 0);
|
||||
paiju.cards.push(pai);
|
||||
do_dealpai(pai);
|
||||
|
||||
//大王
|
||||
var pai = cls_youle_erqiwang_pai.new((i - 1) * 54 + 54 - 1, 5, 54, 0, 0);
|
||||
paiju.cards.push(pai);
|
||||
do_dealpai(pai);
|
||||
}
|
||||
}
|
||||
|
||||
//将单张牌随机发到玩家手中或成为底牌
|
||||
var do_dealpai = function(o_pai){
|
||||
var idx = min_random(0, tmpdeal.length - 1);
|
||||
if (tmpdeal[idx] == -1) { //底牌
|
||||
o_pai.dealowner = 0;
|
||||
} else {
|
||||
o_pai.dealowner = tmpdeal[idx] + 1;
|
||||
}
|
||||
//将数组的最后一位至该位置
|
||||
if (idx < tmpdeal.length - 1) {
|
||||
tmpdeal[idx] = tmpdeal[tmpdeal.length - 1];
|
||||
};
|
||||
//数组长度减一
|
||||
tmpdeal.length = tmpdeal.length - 1;
|
||||
}
|
||||
|
||||
//进入叫分阶段
|
||||
var start_call = function(){
|
||||
paiju.step = 1;
|
||||
var o_call = {};
|
||||
o_call.seat = paiju.firstseat;
|
||||
o_call.call = null; //null:未叫分 0:不叫分 >0:叫分分数
|
||||
paiju.callproc.push(o_call);
|
||||
}
|
||||
|
||||
// ================= 实例的公有属性 ================= //
|
||||
paiju.o_desk = o_desk;
|
||||
//牌局号
|
||||
paiju.idx = o_desk.paiju_list.length;
|
||||
//第一个叫分者的位置
|
||||
paiju.firstseat = firstseat;
|
||||
//开始时间
|
||||
paiju.starttime = min_now();
|
||||
//结束时间
|
||||
paiju.endtime = null;
|
||||
//座位列表
|
||||
paiju.seatlist = [];
|
||||
//牌列表
|
||||
paiju.cards = [];
|
||||
//当前阶段
|
||||
paiju.step = -1; //1发完牌叫分 2选主 3埋牌 4投降 5出牌 6结算
|
||||
//叫分过程
|
||||
paiju.callproc = [];
|
||||
//庄家位置
|
||||
paiju.banker = -1;
|
||||
//庄家叫分
|
||||
paiju.call = -1;
|
||||
//主牌花色
|
||||
paiju.flower = -1;
|
||||
//当前出牌情况
|
||||
paiju.playproc = {};
|
||||
//输赢结果 0:庄赢 1:闲赢 2:庄投降 3:中途解散
|
||||
paiju.result = null;
|
||||
//结算包,供断线重连时使用,新开一局时清除
|
||||
paiju.tmp_jiesuan_aset = null;
|
||||
|
||||
//初始化座位列表
|
||||
init_seatlist();
|
||||
//初始化牌列表并发牌
|
||||
init_cards();
|
||||
//进入叫分阶段
|
||||
start_call();
|
||||
|
||||
// ================= 实例的公有方法 ================= //
|
||||
paiju.method = {};
|
||||
|
||||
//获取某个位置玩家手上的牌
|
||||
paiju.method.get_seat_cards = function(seat){
|
||||
return cls_youle_erqiwang_paiju.get_seat_cards(paiju, seat);
|
||||
}
|
||||
|
||||
//获取当前的叫分位置
|
||||
paiju.method.get_callgrade_seat = function(){
|
||||
return paiju.callproc[paiju.callproc.length - 1].seat;
|
||||
}
|
||||
|
||||
//获取当前的叫分分数
|
||||
paiju.method.get_callgrade_value = function(){
|
||||
return cls_youle_erqiwang_paiju.get_callgrade_value(paiju);
|
||||
}
|
||||
|
||||
//获取某个玩家的当前叫分情况
|
||||
paiju.method.get_seat_callgrade = function(seat){
|
||||
return cls_youle_erqiwang_paiju.get_seat_callgrade(paiju, seat);
|
||||
}
|
||||
|
||||
//叫分或不叫
|
||||
paiju.method.do_callgrade = function(call){
|
||||
cls_youle_erqiwang_paiju.do_callgrade(paiju, call);
|
||||
}
|
||||
|
||||
//获取底牌
|
||||
paiju.method.get_bottomcards = function(){
|
||||
return cls_youle_erqiwang_paiju.get_bottomcards(paiju);
|
||||
}
|
||||
|
||||
//选主
|
||||
paiju.method.do_choiceflower = function(flower){
|
||||
cls_youle_erqiwang_paiju.do_choiceflower(paiju, flower);
|
||||
}
|
||||
|
||||
//判断牌是否在玩家手上
|
||||
paiju.method.check_cards_inhand = function(cards, seat){
|
||||
return cls_youle_erqiwang_paiju.check_cards_inhand(paiju, cards, seat);
|
||||
}
|
||||
|
||||
//埋牌
|
||||
paiju.method.do_burycard = function(cards){
|
||||
cls_youle_erqiwang_paiju.do_burycard(paiju, cards);
|
||||
}
|
||||
|
||||
//检查选中的牌是否可出,并出牌
|
||||
paiju.method.do_playcard = function(cards){
|
||||
return cls_youle_erqiwang_paiju.do_playcard(paiju, cards);
|
||||
}
|
||||
|
||||
//是否有人报副
|
||||
paiju.method.have_baofu = function(){
|
||||
return (paiju.seatlist[0][4][0] == 0 || paiju.seatlist[1][4][0] == 0 || paiju.seatlist[2][4][0] == 0);
|
||||
}
|
||||
|
||||
//在牌列表中获取分牌和分值
|
||||
paiju.method.get_grade_incard = function(cards){
|
||||
return cls_youle_erqiwang_paiju.get_grade_incard(paiju, cards);
|
||||
}
|
||||
|
||||
//获取埋牌
|
||||
paiju.method.get_burycard= function(){
|
||||
return cls_youle_erqiwang_paiju.get_burycard(paiju);
|
||||
}
|
||||
|
||||
//获取抠底包
|
||||
paiju.method.get_bottom_account = function(msg){
|
||||
return cls_youle_erqiwang_paiju.get_bottom_account(paiju, msg);
|
||||
}
|
||||
|
||||
//小局结算,type 0正常结算 1投降结算 2解散结算
|
||||
paiju.method.get_paiju_account = function(type, msg){
|
||||
return cls_youle_erqiwang_paiju.get_paiju_account(paiju, type, msg);
|
||||
}
|
||||
|
||||
//获取闲家当前的捡分分数和分牌
|
||||
paiju.method.get_jian_grade = function(){
|
||||
return cls_youle_erqiwang_paiju.get_jian_grade(paiju);
|
||||
}
|
||||
|
||||
return paiju;
|
||||
},
|
||||
|
||||
//下一个位置
|
||||
get_nextseat: function(seat){
|
||||
return (seat + 1) % 3;
|
||||
},
|
||||
|
||||
//获取某个位置玩家手上的牌
|
||||
get_seat_cards: function(o_paiju, seat){
|
||||
var aryCardIDs = [];
|
||||
for (var i = 0; i < o_paiju.cards.length; i++){
|
||||
var o_card = o_paiju.cards[i];
|
||||
if (o_card.playround == -1){ //未出的牌
|
||||
if ((o_card.dealowner == seat + 1) || //发到手上
|
||||
(o_card.dealowner == 0 && seat == o_paiju.banker)){ //庄家包括底牌
|
||||
aryCardIDs.push(o_paiju.cards[i].id);
|
||||
}
|
||||
}
|
||||
}
|
||||
return cls_youle_erqiwang_arith.order_cards(o_paiju.flower, aryCardIDs);
|
||||
},
|
||||
|
||||
//获取某个位置玩家发到手上的牌
|
||||
get_seat_cards_deal: function(o_paiju, seat){
|
||||
var aryCardIDs = [];
|
||||
for (var i = 0; i < o_paiju.cards.length; i++){
|
||||
var o_card = o_paiju.cards[i];
|
||||
if (o_card.dealowner == seat + 1){
|
||||
aryCardIDs.push(o_paiju.cards[i].id);
|
||||
}
|
||||
}
|
||||
return cls_youle_erqiwang_arith.order_cards(o_paiju.flower, aryCardIDs);
|
||||
},
|
||||
|
||||
//获取某个位置玩家发到手上的牌(庄家包含底牌)
|
||||
get_seat_cards_owner: function(o_paiju, seat){
|
||||
var aryCardIDs = [];
|
||||
for (var i = 0; i < o_paiju.cards.length; i++){
|
||||
var o_card = o_paiju.cards[i];
|
||||
if ((o_card.dealowner == seat + 1) || //发到手上
|
||||
(o_card.dealowner == 0 && seat == o_paiju.banker)){ //庄家包括底牌
|
||||
aryCardIDs.push(o_paiju.cards[i].id);
|
||||
}
|
||||
}
|
||||
return cls_youle_erqiwang_arith.order_cards(o_paiju.flower, aryCardIDs);
|
||||
},
|
||||
|
||||
//获取某个位置玩家手上的主牌
|
||||
get_seat_zhucards: function(o_paiju, seat){
|
||||
var aryCardIDs = [];
|
||||
for (var i = 0; i < o_paiju.cards.length; i++){
|
||||
var o_card = o_paiju.cards[i];
|
||||
if (o_card.playround == -1){ //未出的牌
|
||||
if ((o_card.dealowner == seat + 1) || //发到手上
|
||||
(o_card.dealowner == 0 && seat == o_paiju.banker)){ //庄家包括底牌
|
||||
if (o_card.flower == o_paiju.flower || o_card.flower == 5 || o_card.number == 2 || o_card.number == 7){
|
||||
aryCardIDs.push(o_paiju.cards[i].id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return cls_youle_erqiwang_arith.order_cards(o_paiju.flower, aryCardIDs);
|
||||
},
|
||||
|
||||
//获取当前的叫分分数
|
||||
get_callgrade_value: function(o_paiju){
|
||||
var value = 0;
|
||||
for (var i = o_paiju.callproc.length - 2; i >= 0; i--) {
|
||||
if (o_paiju.callproc[i].call) {
|
||||
value = o_paiju.callproc[i].call;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
},
|
||||
|
||||
//获取某个位置的叫分 返回值:null未叫 0不叫 >0当前叫分
|
||||
get_seat_callgrade: function(o_paiju, seat){
|
||||
for (var i = o_paiju.callproc.length - 2; i >= 0; i--){
|
||||
if (o_paiju.callproc[i].seat == seat){
|
||||
return o_paiju.callproc[i].call;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
//叫分
|
||||
do_callgrade: function(o_paiju, call){
|
||||
var o_call = o_paiju.callproc[o_paiju.callproc.length - 1];
|
||||
o_call.call = call;
|
||||
|
||||
//等待下一个叫分者叫分
|
||||
var do_wait_nextseat_call = function(seat){
|
||||
var o_call = {};
|
||||
o_call.seat = seat;
|
||||
o_call.call = null;
|
||||
o_paiju.callproc.push(o_call);
|
||||
}
|
||||
|
||||
//上庄
|
||||
var do_up_banker = function(seat, call){
|
||||
//庄家位置
|
||||
o_paiju.banker = seat;
|
||||
//庄家叫分
|
||||
o_paiju.call = call;
|
||||
//进入选主阶段
|
||||
o_paiju.step = 2;
|
||||
}
|
||||
|
||||
//叫了5分直接上庄
|
||||
if (call == 5){
|
||||
do_up_banker(o_call.seat, call);
|
||||
return;
|
||||
}
|
||||
//叫了分
|
||||
if (call > 0){
|
||||
//下一个位置
|
||||
var nextseat = cls_youle_erqiwang_paiju.get_nextseat(o_call.seat);
|
||||
//下一个位置的叫分
|
||||
var nextcall = cls_youle_erqiwang_paiju.get_seat_callgrade(o_paiju, nextseat);
|
||||
//下一个位置不叫分
|
||||
if (nextcall == 0){
|
||||
//再下一个位置
|
||||
nextseat = cls_youle_erqiwang_paiju.get_nextseat(nextseat);
|
||||
}
|
||||
//等待下一个叫分者叫分
|
||||
do_wait_nextseat_call(nextseat);
|
||||
return;
|
||||
}
|
||||
|
||||
//不叫
|
||||
if (call == 0){
|
||||
//下一个位置
|
||||
var nextseat_1 = cls_youle_erqiwang_paiju.get_nextseat(o_call.seat);
|
||||
//下一个位置的叫分
|
||||
var nextcall_1 = cls_youle_erqiwang_paiju.get_seat_callgrade(o_paiju, nextseat_1);
|
||||
//再下一个位置
|
||||
var nextseat_2 = cls_youle_erqiwang_paiju.get_nextseat(nextseat_1);
|
||||
//再下一个位置的叫分
|
||||
var nextcall_2 = cls_youle_erqiwang_paiju.get_seat_callgrade(o_paiju, nextseat_2);
|
||||
|
||||
if (nextcall_1 != 0 && nextcall_2 != 0){
|
||||
//另外两家都没有不叫分
|
||||
do_wait_nextseat_call(nextseat_1);
|
||||
return;
|
||||
}
|
||||
if (nextcall_1 != 0 && nextcall_2 == 0){
|
||||
//nextseat_1上庄
|
||||
do_up_banker(nextseat_1, nextcall_1);
|
||||
return;
|
||||
}
|
||||
if (nextcall_1 == 0 && nextcall_2 != 0){
|
||||
//nextseat_2上庄
|
||||
do_up_banker(nextseat_2, nextcall_2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
//获取底牌
|
||||
get_bottomcards: function(o_paiju){
|
||||
var aryCardIDs = [];
|
||||
for (var i = 0; i < o_paiju.cards.length; i++){
|
||||
if (o_paiju.cards[i].dealowner == 0){
|
||||
aryCardIDs.push(o_paiju.cards[i].id);
|
||||
}
|
||||
}
|
||||
return cls_youle_erqiwang_arith.order_cards(o_paiju.flower, aryCardIDs);
|
||||
},
|
||||
|
||||
//选主
|
||||
do_choiceflower: function(o_paiju, flower){
|
||||
o_paiju.flower = flower;
|
||||
//修改主牌的牌编码
|
||||
for (var i = 1; i <= 2; i++){ //两副牌
|
||||
for (var j = 0; j < 13; j++){
|
||||
var o_card = o_paiju.cards[(i - 1) * 54 + (flower - 1) * 13 + j];
|
||||
}
|
||||
}
|
||||
//进入埋牌阶段
|
||||
o_paiju.step = 3;
|
||||
},
|
||||
|
||||
//判断牌是否在玩家手上
|
||||
check_cards_inhand: function(o_paiju, cards, seat){
|
||||
for (var i = 0; i < cards.length; i++) {
|
||||
var o_card = o_paiju.cards[cards[i]];
|
||||
if (o_card.playround != -1){
|
||||
return;
|
||||
}
|
||||
if (seat == o_paiju.banker){
|
||||
//是庄家
|
||||
if (o_card.dealowner != seat + 1 && o_card.dealowner != 0){
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
//是闲家
|
||||
if (o_card.dealowner != seat + 1){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
//埋牌
|
||||
do_burycard: function(o_paiju, cards){
|
||||
for (var i = 0; i < cards.length; i++) {
|
||||
o_paiju.cards[cards[i]].playround = 0;
|
||||
};
|
||||
//进入投降阶段
|
||||
o_paiju.step = 4;
|
||||
cls_youle_erqiwang_paiju.new_playround(o_paiju, 1, o_paiju.banker);
|
||||
},
|
||||
|
||||
//新一轮出牌
|
||||
new_playround: function(o_paiju, round, startseat){
|
||||
o_paiju.playproc.round = round; //第几轮
|
||||
o_paiju.playproc.start = startseat; //本轮的起始出牌位置
|
||||
o_paiju.playproc.currseat = startseat; //本轮的当前的出牌位置
|
||||
o_paiju.playproc.startcount = -1; //本轮第一个出牌的张数
|
||||
o_paiju.playproc.startflower = -1; //本轮第一个出牌的花色
|
||||
o_paiju.playproc.starttype = -1; //本轮第一个出牌的牌型
|
||||
o_paiju.playproc.maxseat = -1; //谁的牌最大,位置
|
||||
o_paiju.playproc.maxcard = -1; //满足第一个玩家出牌的牌型最大牌
|
||||
o_paiju.playproc.cards = []; //本轮各位置出的牌,数组与位置序号对应
|
||||
o_paiju.playproc.cards.length= 3;
|
||||
},
|
||||
|
||||
//在牌列表中获取分牌和分值
|
||||
get_grade_incard: function(o_paiju, cards){
|
||||
var re = {};
|
||||
re.grade = 0;
|
||||
re.cards = [];
|
||||
for (var i = 0; i < cards.length; i++) {
|
||||
if (o_paiju.cards[cards[i]].score > 0){
|
||||
re.grade = re.grade + o_paiju.cards[cards[i]].score;
|
||||
re.cards.push(cards[i]);
|
||||
}
|
||||
}
|
||||
return re;
|
||||
},
|
||||
|
||||
//检查选中的牌是否可出,并出牌
|
||||
do_playcard: function(o_paiju, cards){
|
||||
//出牌
|
||||
var doplay = function(playindex){
|
||||
for (var i = 0; i < cards.length; i++) {
|
||||
o_paiju.cards[cards[i]].playround = o_paiju.playproc.round;
|
||||
o_paiju.cards[cards[i]].playindex = playindex;
|
||||
};
|
||||
o_paiju.playproc.cards[o_paiju.playproc.currseat] = cards;
|
||||
|
||||
//进入出牌阶段
|
||||
o_paiju.step = 5;
|
||||
|
||||
//是否报副处理
|
||||
if (o_paiju.seatlist[o_paiju.playproc.currseat][4][0] != 0){
|
||||
var _zhu = cls_youle_erqiwang_paiju.get_seat_zhucards(o_paiju, o_paiju.playproc.currseat);
|
||||
if (_zhu.length == 0){
|
||||
o_paiju.seatlist[o_paiju.playproc.currseat][4][0] = 0;
|
||||
o_paiju.seatlist[o_paiju.playproc.currseat][4][1] = 0;
|
||||
}
|
||||
if (o_paiju.method.have_baofu()){
|
||||
var _parilist = cls_youle_erqiwang_arith.get_pairlist(o_paiju.flower, _zhu);
|
||||
o_paiju.seatlist[o_paiju.playproc.currseat][4][0] = _zhu.length;
|
||||
o_paiju.seatlist[o_paiju.playproc.currseat][4][1] = _parilist.length;
|
||||
|
||||
if (o_paiju.seatlist[o_paiju.playproc.currseat][4][0] == 0){
|
||||
o_paiju.seatlist[o_paiju.playproc.currseat][o_paiju.flower - 1][0] = 1;
|
||||
o_paiju.seatlist[o_paiju.playproc.currseat][o_paiju.flower - 1][1] = 1;
|
||||
} else if (o_paiju.seatlist[o_paiju.playproc.currseat][4][1] == 0){
|
||||
o_paiju.seatlist[o_paiju.playproc.currseat][o_paiju.flower - 1][1] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//本轮中的第二个出牌位置
|
||||
var nextseat_1 = cls_youle_erqiwang_paiju.get_nextseat(o_paiju.playproc.start);
|
||||
//本轮中的第三个出牌位置
|
||||
var nextseat_2 = cls_youle_erqiwang_paiju.get_nextseat(nextseat_1);
|
||||
|
||||
if (o_paiju.playproc.currseat == o_paiju.playproc.start){ //是本轮的第一个出牌
|
||||
var can = cls_youle_erqiwang_arith.can_playcard(o_paiju.flower, cards, o_paiju.playproc.currseat, o_paiju.seatlist);
|
||||
if (!can.result){
|
||||
var re = {};
|
||||
re.result = false;
|
||||
return re;
|
||||
}
|
||||
//出牌
|
||||
doplay(1);
|
||||
//记录下牌型、花色、牌型最大牌
|
||||
o_paiju.playproc.startcount = cards.length;
|
||||
o_paiju.playproc.startflower = can.flower;
|
||||
o_paiju.playproc.starttype = can.cardtype;
|
||||
o_paiju.playproc.maxseat = o_paiju.playproc.currseat;
|
||||
o_paiju.playproc.maxcard = can.cardvalue;
|
||||
o_paiju.playproc.currseat = nextseat_1;
|
||||
//返回结果
|
||||
var re1 = {};
|
||||
re1.result = true;
|
||||
re1.idx = 1;
|
||||
re1.count = cards.length;
|
||||
re1.flower = can.flower;
|
||||
re1.cardtype = can.cardtype;
|
||||
return re1;
|
||||
}
|
||||
|
||||
//不是本轮中的第一个出牌,是跟牌
|
||||
var cardsinhand = cls_youle_erqiwang_paiju.get_seat_cards(o_paiju, o_paiju.playproc.currseat);
|
||||
var can = cls_youle_erqiwang_arith.can_followcard(o_paiju.flower, cardsinhand, cards, o_paiju.playproc.startcount, o_paiju.playproc.startflower, o_paiju.playproc.starttype);
|
||||
if (!can.result){
|
||||
var re = {};
|
||||
re.result = false;
|
||||
return re;
|
||||
}
|
||||
|
||||
//与之前的出牌比较大小
|
||||
if (can.cardvalue > o_paiju.playproc.maxcard){
|
||||
o_paiju.playproc.maxseat = o_paiju.playproc.currseat;
|
||||
o_paiju.playproc.maxcard = can.cardvalue;
|
||||
}
|
||||
|
||||
//无花色、无对子处理
|
||||
if (can.nopair){
|
||||
o_paiju.seatlist[o_paiju.playproc.currseat][o_paiju.playproc.startflower - 1][1] = 1;
|
||||
}
|
||||
if (can.noflower){
|
||||
o_paiju.seatlist[o_paiju.playproc.currseat][o_paiju.playproc.startflower - 1][0] = 1;
|
||||
o_paiju.seatlist[o_paiju.playproc.currseat][o_paiju.playproc.startflower - 1][1] = 1;
|
||||
}
|
||||
|
||||
//是本轮中的第二个出牌
|
||||
if (o_paiju.playproc.currseat == nextseat_1){
|
||||
//出牌
|
||||
doplay(2);
|
||||
//下一个出牌者
|
||||
o_paiju.playproc.currseat = nextseat_2;
|
||||
//返回结果
|
||||
var re2 = {};
|
||||
re2.idx = 2;
|
||||
re2.result = true;
|
||||
return re2;
|
||||
}
|
||||
|
||||
//是本轮中的第三个出牌
|
||||
//出牌
|
||||
doplay(3);
|
||||
//本轮出牌结束
|
||||
for (var i = 0; i < o_paiju.playproc.cards.length; i++) {
|
||||
for (var j = 0; j < o_paiju.playproc.cards[i].length; j++) {
|
||||
o_paiju.cards[o_paiju.playproc.cards[i][j]].playowner = o_paiju.playproc.maxseat;
|
||||
}
|
||||
}
|
||||
|
||||
//返回结果
|
||||
var re3 = {};
|
||||
re3.result = true;
|
||||
re3.idx = 3;
|
||||
re3.maxseat = o_paiju.playproc.maxseat;
|
||||
if (o_paiju.playproc.maxseat != o_paiju.banker){
|
||||
re3.grade = 0;
|
||||
// re3.gradecards = [];
|
||||
for (var i = 0; i < o_paiju.playproc.cards.length; i++) {
|
||||
var grade_incards = cls_youle_erqiwang_paiju.get_grade_incard(o_paiju, o_paiju.playproc.cards[i]);
|
||||
re3.grade = re3.grade + grade_incards.grade;
|
||||
// re3.gradecards = re3.gradecards.concat(grade_incards.cards);
|
||||
}
|
||||
}
|
||||
|
||||
//判断是否还有牌在玩家手上未出
|
||||
var havecard_inhand = function(){
|
||||
for (var i = 0; i < o_paiju.cards.length; i++) {
|
||||
if (o_paiju.cards[i].playround == -1 && o_paiju.cards[i].number != 3 && o_paiju.cards[i].number != 4){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// if (!havecard_inhand() || o_paiju.playproc.round == 2){ //测试用
|
||||
if (!havecard_inhand()){
|
||||
//进入结算阶段
|
||||
o_paiju.step = 6;
|
||||
} else {
|
||||
//新一轮出牌
|
||||
cls_youle_erqiwang_paiju.new_playround(o_paiju, o_paiju.playproc.round + 1, o_paiju.playproc.maxseat);
|
||||
}
|
||||
return re3;
|
||||
},
|
||||
|
||||
//获取埋牌
|
||||
get_burycard: function(o_paiju){
|
||||
var aryCardIDs = [];
|
||||
for (var i = 0; i < o_paiju.cards.length; i++){
|
||||
if (o_paiju.cards[i].playround == 0){
|
||||
aryCardIDs.push(o_paiju.cards[i].id);
|
||||
}
|
||||
}
|
||||
return cls_youle_erqiwang_arith.order_cards(o_paiju.flower, aryCardIDs);
|
||||
},
|
||||
|
||||
//获取抠底包
|
||||
get_bottom_account: function(o_paiju, msg){
|
||||
msg.data.bottom = {};
|
||||
msg.data.bottom.cards = o_paiju.method.get_burycard();
|
||||
if (o_paiju.playproc.maxseat != o_paiju.banker){ //闲家抠底
|
||||
var multiple = cls_youle_erqiwang_arith.get_bottom_multiple(o_paiju.flower, o_paiju.playproc.cards[o_paiju.playproc.maxseat]);
|
||||
if (multiple > 0){
|
||||
msg.data.bottom.multiple = multiple;
|
||||
var get = o_paiju.method.get_grade_incard(msg.data.bottom.cards);
|
||||
msg.data.bottom.grade1 = get.grade;
|
||||
msg.data.bottom.grade2 = multiple * msg.data.bottom.grade1;
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
},
|
||||
|
||||
//获取闲家捡分(不包含抠底)
|
||||
get_jian_grade: function(o_paiju){
|
||||
var re = {};
|
||||
re.grade = 0;
|
||||
re.cards = [];
|
||||
for (var i = 0; i < o_paiju.cards.length; i++){
|
||||
if (o_paiju.cards[i].playowner > -1 && o_paiju.cards[i].playowner != o_paiju.banker){
|
||||
if (o_paiju.cards[i].score > 0){
|
||||
re.grade = re.grade + o_paiju.cards[i].score;
|
||||
re.cards.push(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return re;
|
||||
},
|
||||
|
||||
//获取小局结算包,type 0正常结算 1投降结算 2解散结算
|
||||
get_paiju_account: function(o_paiju, type, msg){
|
||||
o_paiju.endtime = min_now();
|
||||
//第一局结算扣除房卡
|
||||
if (o_paiju.idx == 1 && type != 2){
|
||||
var do_deduct_roomcard = function(){
|
||||
youle_erqiwang.import.deduct_roomcard(o_paiju.o_desk.o_room);
|
||||
}
|
||||
min_ontimeout(do_deduct_roomcard, 1000);
|
||||
}
|
||||
|
||||
if (msg.rpc != "jiesuan"){
|
||||
msg.rpc = "jiesuan";
|
||||
}
|
||||
if (!msg.data){
|
||||
msg.data = {};
|
||||
}
|
||||
msg.data.aset = {};
|
||||
msg.data.aset.banker = o_paiju.banker;
|
||||
msg.data.aset.call = o_paiju.call;
|
||||
msg.data.aset.multiple = cls_youle_erqiwang_arith.get_multiple_bycall(o_paiju.call);
|
||||
msg.data.aset.flower = o_paiju.flower;
|
||||
var jian_grade = cls_youle_erqiwang_paiju.get_jian_grade(o_paiju);
|
||||
msg.data.aset.grade = jian_grade.grade;
|
||||
if (msg.data.bottom && msg.data.bottom.grade2){
|
||||
msg.data.aset.grade = msg.data.aset.grade + msg.data.bottom.grade2;
|
||||
}
|
||||
switch (type){
|
||||
case 0:
|
||||
msg.data.aset.upgrade = cls_youle_erqiwang_arith.get_upgrade(o_paiju.call, msg.data.aset.grade);
|
||||
if (msg.data.aset.upgrade > 0){
|
||||
o_paiju.result = 0;
|
||||
} else {
|
||||
o_paiju.result = 1;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
msg.data.aset.upgrade = -99;
|
||||
o_paiju.result = 2;
|
||||
break;
|
||||
case 2:
|
||||
msg.data.aset.upgrade = 0;
|
||||
o_paiju.result = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
msg.data.aset.seatlist = [];
|
||||
var _player0 = {};
|
||||
var _player1 = {};
|
||||
var _player2 = {};
|
||||
//获取玩家的牌
|
||||
var _card0 = cls_youle_erqiwang_paiju.get_seat_cards_owner(o_paiju, 0);
|
||||
var _card1 = cls_youle_erqiwang_paiju.get_seat_cards_owner(o_paiju, 1);
|
||||
var _card2 = cls_youle_erqiwang_paiju.get_seat_cards_owner(o_paiju, 2);
|
||||
//计算冲关或王牌
|
||||
var _flower = o_paiju.flower;
|
||||
if (type != 2){
|
||||
_flower = -1;
|
||||
}
|
||||
var _chongguan0 = cls_youle_erqiwang_arith.get_chongguan(_flower, _card0);
|
||||
var _chongguan1 = cls_youle_erqiwang_arith.get_chongguan(_flower, _card1);
|
||||
var _chongguan2 = cls_youle_erqiwang_arith.get_chongguan(_flower, _card2);
|
||||
//冲关的牌或王牌
|
||||
_player0.cards = _chongguan0.cards;
|
||||
_player1.cards = _chongguan1.cards;
|
||||
_player2.cards = _chongguan2.cards;
|
||||
//关数
|
||||
_player0.chongguan = _chongguan0.count;
|
||||
_player1.chongguan = _chongguan1.count;
|
||||
_player2.chongguan = _chongguan2.count;
|
||||
//是否作废
|
||||
_player0.obsolete = 0;
|
||||
_player1.obsolete = 0;
|
||||
_player2.obsolete = 0;
|
||||
//根据叫分判断冲关是否作废
|
||||
var do_obsolete_with_call = function(player, seat){
|
||||
var _dealcards = cls_youle_erqiwang_paiju.get_seat_cards_deal(o_paiju, seat);
|
||||
var _dealchongguan = cls_youle_erqiwang_arith.get_chongguan(-1, _dealcards);
|
||||
if (_dealchongguan.count == 0){
|
||||
return;
|
||||
}
|
||||
var _call = null;
|
||||
for (var i = 0; i < o_paiju.callproc.length; i++){
|
||||
if (o_paiju.callproc[i].seat == seat){
|
||||
_call = o_paiju.callproc[i].call;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!_call){ //冲关必叫
|
||||
player.obsolete = 1;
|
||||
} else if (_dealchongguan.wang == 4 && _call > 60){ //四王必踢
|
||||
player.obsolete = 1;
|
||||
}
|
||||
}
|
||||
|
||||
do_obsolete_with_call(_player0, 0);
|
||||
do_obsolete_with_call(_player1, 1);
|
||||
do_obsolete_with_call(_player2, 2);
|
||||
//计算冲关得分
|
||||
var _cg0 = _player0.chongguan;
|
||||
var _cg1 = _player1.chongguan;
|
||||
var _cg2 = _player2.chongguan;
|
||||
if (_player0.obsolete){
|
||||
_cg0 = 0;
|
||||
}
|
||||
if (_player1.obsolete){
|
||||
_cg1 = 0;
|
||||
}
|
||||
if (_player2.obsolete){
|
||||
_cg2 = 0;
|
||||
}
|
||||
_player0.grade_cg = _cg0 * 2 - _cg1 - _cg2;
|
||||
_player1.grade_cg = _cg1 * 2 - _cg2 - _cg0;
|
||||
_player2.grade_cg = _cg2 * 2 - _cg0 - _cg1;
|
||||
//计算傍王得分
|
||||
var _w0 = _chongguan0.wang;
|
||||
var _w1 = _chongguan1.wang;
|
||||
var _w2 = _chongguan2.wang;
|
||||
if (_player0.obsolete){
|
||||
_w0 = 0;
|
||||
}
|
||||
if (_player1.obsolete){
|
||||
_w1 = 0;
|
||||
}
|
||||
if (_player2.obsolete){
|
||||
_w2 = 0;
|
||||
}
|
||||
_player0.grade_w = _w0 * 2 - _w1 - _w2;
|
||||
_player1.grade_w = _w1 * 2 - _w2 - _w0;
|
||||
_player2.grade_w = _w2 * 2 - _w0 - _w1;
|
||||
//计算捡分得分
|
||||
var _upgrade = msg.data.aset.upgrade;
|
||||
if (_upgrade == -99){
|
||||
_upgrade = -2;
|
||||
}
|
||||
switch (o_paiju.banker){
|
||||
case -1:
|
||||
_player0.grade_jf = 0;
|
||||
_player1.grade_jf = 0;
|
||||
_player2.grade_jf = 0;
|
||||
break;
|
||||
case 0:
|
||||
_player0.grade_jf = _upgrade * msg.data.aset.multiple * 2;
|
||||
_player1.grade_jf = _upgrade * msg.data.aset.multiple * -1;
|
||||
_player2.grade_jf = _player1.grade_jf;
|
||||
break;
|
||||
case 1:
|
||||
_player0.grade_jf = _upgrade * msg.data.aset.multiple * -1;
|
||||
_player1.grade_jf = _upgrade * msg.data.aset.multiple * 2;
|
||||
_player2.grade_jf = _player0.grade_jf;
|
||||
break;
|
||||
case 2:
|
||||
_player0.grade_jf = _upgrade * msg.data.aset.multiple * -1;
|
||||
_player1.grade_jf = _player0.grade_jf;
|
||||
_player2.grade_jf = _upgrade * msg.data.aset.multiple * 2;
|
||||
break;
|
||||
}
|
||||
//计算本局总得分
|
||||
_player0.grade = _player0.grade_cg + _player0.grade_w + _player0.grade_jf;
|
||||
_player1.grade = _player1.grade_cg + _player1.grade_w + _player1.grade_jf;
|
||||
_player2.grade = _player2.grade_cg + _player2.grade_w + _player2.grade_jf;
|
||||
//大局累积得分
|
||||
o_paiju.o_desk.seatlist[0][0] = o_paiju.o_desk.seatlist[0][0] + _player0.grade;
|
||||
o_paiju.o_desk.seatlist[1][0] = o_paiju.o_desk.seatlist[1][0] + _player1.grade;
|
||||
o_paiju.o_desk.seatlist[2][0] = o_paiju.o_desk.seatlist[2][0] + _player2.grade;
|
||||
_player0.score = o_paiju.o_desk.seatlist[0][0];
|
||||
_player1.score = o_paiju.o_desk.seatlist[1][0];
|
||||
_player2.score = o_paiju.o_desk.seatlist[2][0];
|
||||
//保存每局得分
|
||||
o_paiju.o_desk.seatlist[0][1].push(_player0.grade);
|
||||
o_paiju.o_desk.seatlist[1][1].push(_player1.grade);
|
||||
o_paiju.o_desk.seatlist[2][1].push(_player2.grade);
|
||||
|
||||
msg.data.aset.seatlist.push(_player0);
|
||||
msg.data.aset.seatlist.push(_player1);
|
||||
msg.data.aset.seatlist.push(_player2);
|
||||
|
||||
if (o_paiju.idx >= o_paiju.o_desk.o_room.asetcount || type == 2){
|
||||
msg = o_paiju.o_desk.method.get_desk_account(msg);
|
||||
}
|
||||
|
||||
//保存结算包,供断线重连时使用
|
||||
o_paiju.tmp_jiesuan_aset = msg.data.aset;
|
||||
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,633 @@
|
||||
# 二七王 玩法设计文档
|
||||
|
||||
> 本文档是二七王玩法的唯一权威规则说明。内容随讨论持续与规则设计者逐项确认、修订;文中标注"待确认"的地方是当前仍未有定论的开放问题,其余内容均已确认生效。
|
||||
|
||||
## 目录
|
||||
|
||||
1. [术语约定](#1-术语约定)
|
||||
2. [牌局构成](#2-牌局构成)
|
||||
3. [主牌顺序](#3-主牌顺序)
|
||||
4. [开局与坐庄流程](#4-开局与坐庄流程)
|
||||
5. [出牌规则](#5-出牌规则)
|
||||
- [5.1 跟牌基本原则](#51-跟牌基本原则)
|
||||
- [5.2 按牌型跟牌](#52-按牌型跟牌)
|
||||
- [5.3 拖拉机定义与相邻关系](#53-拖拉机定义与相邻关系)
|
||||
- [5.4 甩牌详细规则](#54-甩牌详细规则)
|
||||
6. [捡分与扣底](#6-捡分与扣底)
|
||||
- [6.1 分牌](#61-分牌)
|
||||
- [6.2 捡分规则](#62-捡分规则)
|
||||
- [6.3 扣底](#63-扣底)
|
||||
7. [结算:子数与升级](#7-结算子数与升级)
|
||||
- [7.0 四层关系总览 / 查表索引](#70-四层关系总览)
|
||||
- [7.1 常规算子:叫分 → 基础子数](#71-常规算子叫分-→-基础子数未勾选爬坡时的默认规则)
|
||||
- [7.2 常规算子的判定表](#72-捡分-vs-叫分庄家的过庄-小光-大光闲家的升级常规算子)
|
||||
- [7.3 爬坡](#73-爬坡叫分-→-基础子数与保小光分段可选规则开房时勾选爬坡生效)
|
||||
8. [算奖规则](#8-算奖规则)
|
||||
- [8.1 常规算奖](#81-常规算奖)
|
||||
- [8.2 亮牌规则](#82-亮牌规则)
|
||||
- [8.3 傍王(可选规则)](#83-傍王可选规则)
|
||||
- [8.4 算奖如何影响最终结算](#84-算奖如何影响最终结算)
|
||||
9. [牌局查看模式](#9-牌局查看模式)
|
||||
10. [房间设置选项](#10-房间设置选项)
|
||||
11. [牌局交互提示](#11-牌局交互提示)
|
||||
|
||||
---
|
||||
|
||||
## 1. 术语约定
|
||||
|
||||
| 术语 | 含义 |
|
||||
| --- | --- |
|
||||
| 主牌 / 副牌 | 庄家选定的花色为"主牌",其余三个花色为"副牌";主牌大于副牌 |
|
||||
| 固定主牌 | 不论选择哪个花色为主,大王、小王、以及所有花色的"2""7"恒为主牌 |
|
||||
| 正2 / 正7 | 选定花色的"2""7",大于其余花色的"2""7" |
|
||||
| 副2 / 副7 | 非选定花色的"2""7" |
|
||||
| 暗牌 | 发牌时留在桌面的 8 张牌;庄家确认后可摸起查看,摸起后就不再是"暗牌",而是并入庄家手牌 |
|
||||
| 底牌 | 庄家"埋牌"时从手牌中选出重新扣下的 8 张牌;"暗牌"与"底牌"是两批不同的牌,不通用、不互换 |
|
||||
| 埋牌 | 庄家看到暗牌、摸起查看后,从手牌中选出 8 张重新扣下,作为"底牌" |
|
||||
| 捡分 | 出牌中赢下的分牌(5、10、K)计入闲家总得分的过程 |
|
||||
| 扣底 | 出完所有手牌后的最后一轮,若被闲家用主牌牌型压过庄家,则埋下的 8 张底牌也计入闲家捡分,并按牌型翻倍 |
|
||||
| 拖拉机 | 同一花色(含主牌)里点数相邻的连续对子,如 K K Q Q 为"两托"(两连对) |
|
||||
| 甩牌 | 首家出牌时,一次性打出多组主牌组合(单张 / 对子 / 拖拉机自由搭配);仅主牌可以甩牌,副牌禁止甩牌,只能分单张 / 对子 / 拖拉机依次打出(见第 5.4 节) |
|
||||
| 毙牌 | 跟牌时手中没有本轮花色(缺门),改用主牌打出、且牌面大过本轮目前所有人的牌(见第 5.1 / 5.2 节) |
|
||||
| 垫牌 | 跟牌时手中没有本轮花色(缺门)、且不用主牌毙牌,随意出一张其他副牌,不参与、不争夺本轮的出牌权(见第 5.1 节) |
|
||||
| 子 | 结算时用于计算输赢筹码的单位,与"倍数"是两个独立概念(见第 7 节) |
|
||||
| 常规算奖 | 局末对固定组合(三王/四王、六 2/七 2/八 2、六 7/七 7/八 7、连续对子链等)的额外奖励结算,只计入庄家的手牌结构(见第 8.1 节) |
|
||||
| 傍王 | 可选房间规则:勾选后每张王额外算一奖,庄家、闲家都要算;这是在 8.1 常规算奖之外**额外叠加**的奖励,与常规算奖互不冲突(见第 8.3 节) |
|
||||
| 爬坡 | 可选房间规则:改变叫分对应的基础子数与保小光分数线,未勾选时按"常规算子"结算(见第 7 节) |
|
||||
|
||||
---
|
||||
|
||||
## 2. 牌局构成
|
||||
|
||||
两副扑克牌共 108 张,去掉两副牌中的"3"和"4"(2 种点数 × 4 花色 × 2 副 = 16 张),剩余 **92 张**:
|
||||
|
||||
- 点数 5、6、7、8、9、10、J、Q、K、A、2:共 11 种点数,每种 8 张(2 副 × 4 花色)
|
||||
- 大王、小王:各 2 张(每副各 1 对)
|
||||
|
||||
花色为黑桃 ♠、红桃 ♥、梅花 ♣、方块 ♦。
|
||||
|
||||
---
|
||||
|
||||
## 3. 主牌顺序
|
||||
|
||||
每局开局前庄家选择一个花色作为本局主牌,其余三个花色为副牌。
|
||||
|
||||
固定主牌(大王、小王、所有花色的 2 和 7)不论选择哪个花色为主,恒定属于主牌;选定花色里的其余普通牌也算主牌。
|
||||
|
||||
主牌从大到小排列:
|
||||
|
||||
| 顺序 | 牌 |
|
||||
| --- | --- |
|
||||
| 1 | 大王 |
|
||||
| 2 | 小王 |
|
||||
| 3 | 正 7(选定花色的 7) |
|
||||
| 4 | 副 7(其余花色的 7) |
|
||||
| 5 | 正 2(选定花色的 2) |
|
||||
| 6 | 副 2(其余花色的 2) |
|
||||
| 7 及以下 | 选定花色的其余普通牌:A、K、Q、J、10、9、8、6、5 |
|
||||
|
||||
副牌(非选定花色,且非 2、7、王)从大到小:A、K、Q、J、10、9、8、6、5。
|
||||
|
||||
---
|
||||
|
||||
## 4. 开局与坐庄流程
|
||||
|
||||
1. 每人摸 28 张牌,剩余 8 张扣在桌面作为"暗牌"。
|
||||
2. 由暂定庄家开始,按逆时针方向依次叫分:
|
||||
- 叫分范围 5 ~ 70 分,步进 5 分。
|
||||
- 叫分数值代表"庄家承诺闲家捡分总数将严格低于该数值"(即闲家捡分只要达到或超过这个数值,庄家就算没达成承诺,见第 7 节);数值越低代表叫分者越有信心,因此后叫的人必须叫出比当前更低的分数才能压过前者,不能叫相同或更高的分数。
|
||||
- **暂定庄家必须叫分,不能"不叫"**:叫分从暂定庄家开始,他必须报出一个 5 ~ 70 之间的具体分数,不存在"暂定庄家弃权、三家都不叫"这种情况;起始叫分确定之后,后面的玩家才可以选择"叫出更低的分数"或"不叫"。
|
||||
- 若某玩家直接叫出 5 分,则无人能再压过,立即确定为庄家。
|
||||
- 若一家叫分后,另外两家都选择"不叫",则该叫分玩家成为本局庄家。
|
||||
3. 庄家确定后可以查看暗牌(只有庄家可见),将 8 张暗牌摸到手上,同时从手牌中选出 8 张牌重新扣下("埋牌"),作为"底牌",供最后判断"扣底"使用。
|
||||
4. **若叫分为 70 分**:埋牌完成后,庄家必须立即在"投降"与"打牌"之间二选一(埋牌步骤本身不受这个选择影响,两种情况都要先正常埋牌):
|
||||
- **选择投降**:不再进行下面的选主牌步骤,本局直接结束,按 7.1 节"70 分坐庄 · 投降"的固定结果结算(基础子数 1 个,庄家直接输)。
|
||||
- **选择打牌**:进入下一步,正常选主牌花色、开始出牌,按 7.1 / 7.2 节"70 分坐庄 · 打牌"的完整判定流程结算。
|
||||
- 叫分低于 70 分时没有这个分支,直接进入下一步。
|
||||
5. 庄家选定一个花色作为本局主牌(见第 3 节)。
|
||||
6. 坐庄轮换规则(决定下一局的"暂定庄家"):
|
||||
- 第一局的暂定庄家为最0号座位的玩家。
|
||||
- 若庄家本局获胜(庄赢),下一局暂定庄家仍为该玩家(连庄)。
|
||||
- 若庄家本局失败(闲家捡分达标 / 庄家投降),下一局暂定庄家变为该玩家的下家(按逆时针顺延)。
|
||||
|
||||
---
|
||||
|
||||
## 5. 出牌规则
|
||||
|
||||
### 5.1 跟牌基本原则
|
||||
|
||||
- 每一轮由上一轮牌面最大的一方先出牌;**每一局(每一手牌)的第一轮固定由庄家先出**——此时还没有"上一轮"可以比较,只能由庄家开局。
|
||||
- 首家出什么花色,其余两家必须跟出同花色的牌;若手中没有该花色的牌或数量不够(缺门),可以任选以下一种方式补齐:
|
||||
- **毙牌**:改用主牌打出,且牌面要大过本轮目前所有人的牌,即可抢下本轮的出牌权;
|
||||
- **垫牌**:随意出一张其他副牌,不参与、不争夺本轮出牌权。
|
||||
- 首家若出主牌,其余两家必须跟出主牌,规则同上(此时缺门只能垫副牌,不存在再用主牌毙牌的情况)。
|
||||
|
||||
### 5.2 按牌型跟牌
|
||||
|
||||
| 首家牌型 | 跟牌要求 |
|
||||
| --- | --- |
|
||||
| 单张 | 跟同花色任意单张;不够则任意牌补齐 |
|
||||
| 对子 | 必须跟同花色对子;没有对子则用两张同花色单牌;同花色不足两张则任意牌补齐 |
|
||||
| 拖拉机(连续对子) | 有同花色拖拉机必须跟出拖拉机;没有拖拉机但有两对不连续的对子,也必须出这两对;都没有则优先同花色任意牌补齐 |
|
||||
|
||||
毙牌(缺门时改用主牌顶替首家副牌)的特殊情况:
|
||||
|
||||
- 若首家出副牌单张,其余玩家没有该副牌花色的,可以任意出牌(含主牌);但要压过首家,出的主牌也必须是单张(数量对应)。
|
||||
- 若首家出副牌对子,其余玩家没有该副牌花色的,要压过首家,出的主牌必须也是对子(不能用任意两张主牌顶替)。
|
||||
- 若首家出副牌拖拉机(N 连对),其余玩家没有该副牌花色的同长度拖拉机,要压过首家,出的主牌必须也是同样连对组数(N 连对)的主拖拉机,总张数完全对应;单张主牌或零散的主对子都不能顶替拖拉机。
|
||||
|
||||
### 5.3 拖拉机定义与相邻关系
|
||||
|
||||
同一花色内点数相邻的两个(或以上)对子构成"拖拉机",如 K K Q Q 为两连对(两托),Q Q J J K K 为三连对(三托)。7 不可以和 6、8 组成拖拉机;6 和 8 可以组成拖拉机。
|
||||
|
||||
拖拉机的相邻顺序(从大到小):
|
||||
|
||||
```
|
||||
大王 - 小王 - 正7 - 副7 - 正2 - 副2 - A - K - Q - J - 10 - 9 - 8 - 6 - 5
|
||||
```
|
||||
|
||||
### 5.4 甩牌详细规则
|
||||
|
||||
#### 5.4.1 基础定义
|
||||
|
||||
**甩牌**:当你是本轮首家出牌人时,一次性打出多组主牌混合牌型(单张、对子、拖拉机自由组合),一轮打完多张主牌,无需分多轮依次打出。
|
||||
|
||||
两条硬性基础禁令:
|
||||
|
||||
1. **仅主牌允许甩牌,所有副牌完全禁止甩牌**:无论外面是否剩余该花色副牌,副牌只能分开单出、出对子、出拖拉机,不能一次性打包甩出。
|
||||
2. **只有本轮先手才能执行甩牌**:若他人先出牌,你只能跟牌、垫牌、用主牌毙牌,不具备甩牌资格。
|
||||
|
||||
#### 5.4.2 生效条件(最大性原则)与报无主
|
||||
|
||||
想要成功甩牌,必须同时满足:**剩余两名对手手中,不存在任何一张、一对、一组拖拉机能压制你甩出的整套主牌组合**。简单说,全场所有更大的主牌、更大的主对子、更长的主拖拉机必须全部在你手中,两家闲家没有任何牌型能盖过你的甩牌组合。
|
||||
|
||||
**合法性由服务端在甩牌提交时自动判定**:服务端掌握全部玩家的手牌,甩牌一旦提交,立即检查另外两名玩家手中是否"持有"(不要求对方真正打出)能压过这套组合任意一部分的主牌;只要有人持有,就直接判定为甩错(见 5.4.5),不需要、也不会等到对方实际出牌反制才判定。
|
||||
|
||||
"报无主"是给玩家参考的辅助信息,**不是合法性判定的依据**(合法性判定始终由服务端按全部手牌精确计算):对局中非最后一轮,任意玩家手里没有主牌了,必须口头报无主,方便其他玩家在决定要不要甩牌前,大致判断风险:
|
||||
|
||||
- **另外两家都报了无主**:全场只剩你自己持有主牌,甩牌必然合法,无需记牌。
|
||||
- **另外两家只有一家报了无主、还有一家没报**:那一家仍持有主牌,你若甩牌需要自行记牌估算风险;但即使判断失误,最终是否算甩错仍由服务端精确判定,不取决于你的记牌是否正确。
|
||||
|
||||
#### 5.4.3 甩牌可包含的牌型组合
|
||||
|
||||
甩牌仅限本局主牌(固定主牌 + 选定主花色的普通牌),内部可自由混搭任意数量、任意种类牌型:
|
||||
|
||||
- **纯单张**:大王、小王、正 7、副 7、正 2、副 2、主花色单牌混合;
|
||||
- **纯对子**:大王对 / 小王对(各自成对,大王与小王点数不同,不能混成一对)、正七对、副七对、正二对、主花色数字对子;
|
||||
- **主拖拉机**:大小王拖拉机、七七连对、二二连对、主花色数字连对;
|
||||
- **混合搭配**:单张 + 对子 + 多连拖拉机一同甩出,无组合数量限制。
|
||||
|
||||
#### 5.4.4 其余两家应对甩牌的强制跟牌规则
|
||||
|
||||
甩牌一旦被判定合法(见 5.4.2:服务端已确认两名闲家都不持有能盖压这套组合任何一部分的主牌),闲家就不可能再用主牌把它压下去——"能不能盖压"在甩牌提交的那一刻就已经由服务端裁定完毕,不会走到"闲家出牌盖压"这一步。
|
||||
|
||||
甩牌本身可能是单张、对子、多个不同长度拖拉机自由混搭的复杂组合(见 5.4.3)。闲家跟牌时,**要把这套甩牌拆解成一个个独立的分量,逐个分量分别匹配**,而不是把整套甩牌当成一个笼统的整体来对待:
|
||||
|
||||
1. **每一组拖拉机分量**:闲家手中若有同等连对数的主拖拉机,必须优先拆出来跟这一组;没有同等连对数的拖拉机,退而求其次用同等张数的主对子顶替;主对子也不够,才能拆主单张顶替。甩牌里若有多组不同长度的拖拉机,逐组分别按此规则处理。
|
||||
2. **每一组单独的对子分量**:闲家手中若有主对子,必须优先拆出来跟这一组;没有主对子,才能拆主单张顶替。
|
||||
3. **每一组单独的单张分量**:用主单张顶替即可。
|
||||
4. **闲家手中的主牌拆解完仍不够覆盖甩牌剩余的分量**:不够的那部分只能随意垫副牌。
|
||||
5. **闲家完全没有主牌**:只能随意垫任意副牌,本轮无法压制你的甩牌,本轮出牌权归你。
|
||||
|
||||
无论怎么拆解跟牌,都不影响这套甩牌本身的合法性——合法性在提交时已经由服务端按 5.4.2 判定完毕,闲家的跟牌动作只是"凑够同等张数、同等结构"的手牌垫上去,不可能真正反超压制。
|
||||
|
||||
#### 5.4.5 甩错判定与惩罚规则
|
||||
|
||||
**甩错判定**:甩牌提交时,服务端按全部玩家的真实手牌自动检查——只要两名对手任意一人**持有**(不要求已经打出)能盖压你甩牌内任意一组牌型的主牌,本次甩牌立即判定为甩错,对方是否愿意打出来压制不影响判定结果。
|
||||
|
||||
**统一惩罚标准**:
|
||||
|
||||
- 本次甩牌全部收回手牌;
|
||||
- 本轮仅强制打出你甩出组合里最小的那一张牌;
|
||||
- 本轮失去再次甩牌、一次性多出牌的资格,剩余手牌只能分轮正常打出;
|
||||
- 线下娱乐、线上棋牌平台均统一执行该惩罚,不额外扣分。
|
||||
|
||||
甩错判罚后,本轮实际只算打出了那一张最小的单张主牌,其余两家按 5.2 节的"单张"跟牌规则正常应对即可,不再涉及甩牌相关规则。
|
||||
|
||||
---
|
||||
|
||||
## 6. 捡分与扣底
|
||||
|
||||
### 6.1 分牌
|
||||
|
||||
牌面 5 记 5 分,牌面 10 记 10 分,牌面 K 记 10 分,其余牌不计分。
|
||||
|
||||
### 6.2 捡分规则
|
||||
|
||||
每一轮出完牌后比较牌面大小:
|
||||
|
||||
- 若闲家最大,则本轮牌面上的分牌全部计入闲家捡分(两个闲家谁大不影响,都算闲家捡到)。
|
||||
- 若庄家最大,则本轮牌面上的分牌作废——不计入闲家捡分总数,也不属于庄家的个人分数(庄家本身没有"捡分"这个概念,庄家的输赢只看闲家捡分总数是否达到叫分,见第 7 节)。
|
||||
|
||||
### 6.3 扣底
|
||||
|
||||
所有手牌出完后,若最后一轮闲家**用主牌**(不论单张、对子、拖拉机)**牌面压过庄家**、赢下最后一轮,才视为"扣底"(若闲家赢了最后一轮但用的不是主牌,则不算扣底)。触发后,庄家埋下的 8 张底牌翻开,若有分则计入闲家捡分。
|
||||
|
||||
底牌的分要不要翻倍、翻几倍,取决于闲家扣底所用的那手牌(赢下最后一轮的牌)本身是什么牌型:
|
||||
|
||||
| 闲家扣底所用牌型 | 倍数 |
|
||||
| --- | --- |
|
||||
| 单张主牌 | 不翻倍,按原始分值计入 |
|
||||
| 主对子(一对主牌) | 2 倍 |
|
||||
| 两连对拖拉机 | 4 倍 |
|
||||
| 三连对拖拉机 | 6 倍 |
|
||||
| N 连对拖拉机 | 2 × N 倍,以此类推递增 |
|
||||
|
||||
若最后一轮闲家是用第 5.4 节的**甩牌**(单张 + 对子 + 拖拉机混合的组合)扣底,取这套组合里**最高规格牌型**对应的倍数计算,其余较低规格的部分不额外叠加。例如甩出"单张 + 一对 + 两连对拖拉机"并以此扣底,最高规格是两连对拖拉机,整手按 4 倍计算。
|
||||
|
||||
> **重要区分**:扣底翻的是**闲家的捡分得分(grade)**本身,属于"算分"阶段;这与第 7 节里"大光 ×3""小光 ×2""升 N 级 ×N"这些子数倍率是完全不同的两件事——那些乘的是**子数**,属于"算子"阶段。两者的先后顺序是:扣底先把底牌分(可能翻倍)计入闲家的捡分总数,这个最终捡分总数再拿去跟庄家叫分比较,判定过庄 / 小光 / 大光 / 闲家赢(升级),最后才由判定结果决定子数乘几倍。
|
||||
|
||||
---
|
||||
|
||||
## 7. 结算:子数与升级
|
||||
|
||||
叫分与结算涉及两套并存的数值:**倍数**(用于计算"捡分得分" grade)与**子数**(用于计算最终筹码输赢),两者是独立概念,不能混用。
|
||||
|
||||
> **重要区分**:本节里提到的倍率(大光 ×3、小光 ×2、过庄 ×1、升 N 级 ×N),乘的都是**子数**(最终结算给玩家的筹码单位),不是对局过程中闲家捡到的分数(捡分得分)本身。捡分得分只用来判定庄家的过庄 / 小光 / 大光,或闲家的升级级数,一旦等级判定完成,实际相乘的对象是这一局最终要结算的子数。
|
||||
|
||||
> **算子模式的选择**:7.1 + 7.2 描述的"常规算子"是默认结算方式;开房时勾选"爬坡"后,才改用 7.3 的子数梯度和保小光分段;未勾选"爬坡"时,一律按常规算子(7.1 + 7.2)结算,两套规则不会同时生效。
|
||||
|
||||
### 7.0 四层关系总览
|
||||
|
||||
结算分四层,前三层是"算子"(本节),第四层是"算奖"(第 8 节),一层的输出是下一层的输入:
|
||||
|
||||
1. **叫分 → 基础子数**:叫分档位本身决定这一局的基础子数(见 7.1 / 7.3 的对照表),叫分越低(庄家承诺越苛刻),基础子数越高。
|
||||
2. **捡分 vs 叫分 → 判定结果**:局末闲家的捡分总数(含扣底,见 6.3 节)与庄家叫分作比较,只会落入互斥的两个分支之一:
|
||||
- **闲家没达到叫分**(庄家赢)→ 结果记在**庄家**名下,分三级:**过庄 / 小光 / 大光**。
|
||||
- **闲家达到或超过叫分**(闲家赢,庄家倒庄)→ 结果记在**闲家**名下,只有一个概念:**升级**(第几级)。
|
||||
- **大光 / 小光和升级是两套完全独立的命名体系,分别只属于庄家一侧和闲家一侧,不能混用或类比**:庄家赢的时候没有"升级",闲家赢的时候也没有"大光 / 小光"。
|
||||
3. **判定结果 → 子数倍率**:过庄 / 小光 / 大光 / 升级第几级,各自对应一个子数倍率,乘上第 1 层的基础子数,得到这一局的**子数结算结果 `X`**(前三层,即"算子",到此为止)。`X` 是庄家与**每一个**闲家之间"一对一"结算的基础金额:庄家赢(过庄 / 小光 / 大光)时,两个闲家各输给庄家 `X` 子;闲家赢(升级)时,庄家各输给两个闲家 `X` 子。
|
||||
4. **算奖 → 结算倍率**:第 8 节算出的"奖数"(8.1 常规算奖 + 8.2 傍王,各玩家分别求和)**是"持有奖数的这个人"从其余两名玩家那里各自多收一笔钱的独立结算线,作用对象是这个人本身,不是整场结算的统一系数**——某玩家 P 这一局总共有 `N_P` 奖,则**另外两名玩家(不分庄闲)都要各自额外付给 P:`X × N_P` 子**。三名玩家可能同时都持有各自的奖数,会产生最多 3 条互相独立的额外支付线,与第 1~3 层的基础输赢结算加总,才是每个人这一局的最终盈亏。算奖的计算过程本身不受叫分、过庄 / 小光 / 大光、升级影响(只看手牌结构),详见 8.3 节的完整举例。
|
||||
|
||||
> **例外**:70 分坐庄选择投降时不走第 1~3 层模型——直接固定基础子数 1 个、庄家输掉,不比较捡分、不判定过庄 / 小光 / 大光,见 7.1 末尾说明;但第 4 层算奖仍然照常叠加(庄家埋牌后的手牌结构依然存在,见 8.4 节)。
|
||||
|
||||
**查表索引**:本节按叫分档位给出了完整判定表,位置如下:
|
||||
|
||||
| 叫分 | 常规算子(未勾选"爬坡") | 爬坡(勾选"爬坡") |
|
||||
| --- | --- | --- |
|
||||
| 70 分 | 7.2.1 末尾"70 分坐庄" | 与常规算子一致,见 7.3.3 末尾说明 |
|
||||
| 65 / 60 / 55 / 50 / 45 分 | 7.2.1 对应表格 | 与常规算子数值一致(45 分子数不同),见 7.3.3 末尾说明 |
|
||||
| 40 / 35 / 30 / 25 / 20 / 15 / 10 分 | 7.2.1"40 分及以下坐庄"通用规则 | 7.3.3 对应表格(子数、`Q` 值均与常规算子不同) |
|
||||
| 5 分 | 7.2.1"40 分及以下坐庄"内的"5 分坐庄没有小光"说明 | 7.3.3"5 分坐庄"(没有小光) |
|
||||
|
||||
### 7.1 常规算子:叫分 → 基础子数(未勾选"爬坡"时的默认规则)
|
||||
|
||||
叫分范围是 5 ~ 70 分(见第 4 节),常规算子下完整的"叫分 → 基础子数"对照:
|
||||
|
||||
| 叫分 | 65 分 | 60 分 | 55 分 | 50 分及以下(50 / 45 / 40 / 35 / 30 / 25 / 20 / 15 / 10 / 5) |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 基础子数 | 2 个子 | 3 个子 | 4 个子 | 统一 6 个子,不再随叫分继续增加 |
|
||||
|
||||
即叫分从 65 分往下每降 5 分子数依次是 2 → 3 → 4,但**到 50 分封顶**:50 分及以下所有档位(50、45、40、35、30、25、20、15、10、5)基础子数都固定是 6 个子,不会像爬坡那样继续往上加(爬坡的区别见 7.3)。
|
||||
|
||||
70 分叫庄是特例,基础子数不接着上面的阶梯往下走,而是在"投降"和"打牌"两条路径下分别取值:
|
||||
|
||||
- **选择投降**:基础子数按 **1 个**算,庄家直接输 1 个子(此时局面还没有真正出牌捡分,不经过 7.2 的大光 / 小光 / 升级判定,是一次性的固定结算)。
|
||||
- **选择打牌**:基础子数按 **2 个**算(与 65 分相同),之后**照样按 7.2 节的方法,用捡分总数判定大光 / 小光 / 过庄 / 升级**,不是不细分——完整判定表见 7.2.1。
|
||||
|
||||
70 分叫庄时,发牌时桌面留下的那 8 张暗牌(见第 4 节;不是埋牌埋下的底牌)需要向两个闲家亮出来 3 秒,投降和打牌两种情况都适用。
|
||||
|
||||
### 7.2 捡分 vs 叫分:庄家的过庄 / 小光 / 大光,闲家的升级(常规算子)
|
||||
|
||||
#### 7.2.0 判定方法(对任意叫分档位通用)
|
||||
|
||||
**大光的判定条件是固定的、不随叫分或算子模式变化**:闲家捡分 `grade == 0`(一分未捡到)就是大光,常规算子和爬坡完全一样。会随叫分变化的,是**小光 / 过庄**之间的分界线——常规算子下这条分界线**固定为 40 分**,不随叫分变化(这是与爬坡最核心的区别,爬坡的分界线 `Q` 会随叫分变化,见 7.3.2)。设叫分为 `call`,闲家捡分为 `grade`,按以下顺序依次判断(**必须先判断第 1 步,即是否达标,再判断大光 / 小光 / 过庄**,顺序不能颠倒):
|
||||
|
||||
1. **先判断是否达标**:若 `grade ≥ call`,闲家赢,判为**升级**——先升 1 级,此后每再多捡 40 分再升 1 级;子数倍率就是当前的级数本身:升 1 级 ×1、升 2 级 ×2、升 3 级 ×3……逐级 **+1**,不是逐级翻倍。
|
||||
2. **未达标(`grade < call`)时,再看捡分具体落在哪个区间**:
|
||||
- `grade == 0`(一分未捡到)→ **庄家:大光**,子数倍率 ×3。
|
||||
- `0 < grade < 40`(捡了一点,但没满 40 分)→ **庄家:小光**,子数倍率 ×2。
|
||||
- `40 ≤ grade < call`(捡满 40 分,但仍没达标)→ **庄家:过庄**,子数倍率 ×1(即基础子数,不翻倍)。
|
||||
|
||||
之所以要先判断"是否达标":当叫分本身 ≤ 40 时(比如叫 35 分坐庄),闲家只要捡到 35 分就已经达标升级了,根本不可能出现"捡到 40 分却还没达标"这种情况——"过庄"这一档这时候是空的,直接从"小光"跳到"升级"(完整例子见 7.2.1"40 分及以下坐庄")。
|
||||
|
||||
**大光 / 小光只描述庄家(闲家没达标时,庄家赢得有多彻底);升级只描述闲家(闲家达标之后,赢得有多彻底),两套命名不通用、不能互换。**
|
||||
|
||||
#### 7.2.1 常规算子完整判定表
|
||||
|
||||
按上面的方法,叫分 > 40 分的档位(65 / 60 / 55 / 50 / 45)"过庄"区间是存在的;叫分 ≤ 40 分的档位(40 / 35 / 30 / 25 / 20 / 15 / 10 / 5)"过庄"区间不存在,闲家捡分只要达到叫分就直接升级。
|
||||
|
||||
**65 分坐庄(基础 2 个子)**
|
||||
|
||||
| 闲家捡分 | 判定 | 倍率 | 最终子数 |
|
||||
| --- | --- | --- | --- |
|
||||
| 0 分 | 庄家:大光 | ×3 | 6 个子 |
|
||||
| 1 ~ 39 分 | 庄家:小光 | ×2 | 4 个子 |
|
||||
| 40 ~ 64 分 | 庄家:过庄 | ×1 | 2 个子 |
|
||||
| 65 ~ 104 分 | 闲家:升 1 级 | ×1 | 2 个子 |
|
||||
| 105 ~ 144 分 | 闲家:升 2 级 | ×2 | 4 个子 |
|
||||
| 145 分起,每再多 40 分 | 闲家:再升 1 级 | 再 +1 | 逐级 +2 个子 |
|
||||
|
||||
**60 分坐庄(基础 3 个子)**
|
||||
|
||||
| 闲家捡分 | 判定 | 倍率 | 最终子数 |
|
||||
| --- | --- | --- | --- |
|
||||
| 0 分 | 庄家:大光 | ×3 | 9 个子 |
|
||||
| 1 ~ 39 分 | 庄家:小光 | ×2 | 6 个子 |
|
||||
| 40 ~ 59 分 | 庄家:过庄 | ×1 | 3 个子 |
|
||||
| 60 ~ 99 分 | 闲家:升 1 级 | ×1 | 3 个子 |
|
||||
| 100 ~ 139 分 | 闲家:升 2 级 | ×2 | 6 个子 |
|
||||
| 140 分起,每再多 40 分 | 闲家:再升 1 级 | 再 +1 | 逐级 +3 个子 |
|
||||
|
||||
**55 分坐庄(基础 4 个子)**
|
||||
|
||||
| 闲家捡分 | 判定 | 倍率 | 最终子数 |
|
||||
| --- | --- | --- | --- |
|
||||
| 0 分 | 庄家:大光 | ×3 | 12 个子 |
|
||||
| 1 ~ 39 分 | 庄家:小光 | ×2 | 8 个子 |
|
||||
| 40 ~ 54 分 | 庄家:过庄 | ×1 | 4 个子 |
|
||||
| 55 ~ 94 分 | 闲家:升 1 级 | ×1 | 4 个子 |
|
||||
| 95 ~ 134 分 | 闲家:升 2 级 | ×2 | 8 个子 |
|
||||
| 135 分起,每再多 40 分 | 闲家:再升 1 级 | 再 +1 | 逐级 +4 个子 |
|
||||
|
||||
**50 分坐庄(基础 6 个子)**
|
||||
|
||||
| 闲家捡分 | 判定 | 倍率 | 最终子数 |
|
||||
| --- | --- | --- | --- |
|
||||
| 0 分 | 庄家:大光 | ×3 | 18 个子 |
|
||||
| 1 ~ 39 分 | 庄家:小光 | ×2 | 12 个子 |
|
||||
| 40 ~ 49 分 | 庄家:过庄 | ×1 | 6 个子 |
|
||||
| 50 ~ 89 分 | 闲家:升 1 级 | ×1 | 6 个子 |
|
||||
| 90 ~ 129 分 | 闲家:升 2 级 | ×2 | 12 个子 |
|
||||
| 130 分起,每再多 40 分 | 闲家:再升 1 级 | 再 +1 | 逐级 +6 个子 |
|
||||
|
||||
**45 分坐庄(基础 6 个子——从这一档开始进入"50 分及以下统一 6 个子"的范围)**
|
||||
|
||||
| 闲家捡分 | 判定 | 倍率 | 最终子数 |
|
||||
| --- | --- | --- | --- |
|
||||
| 0 分 | 庄家:大光 | ×3 | 18 个子 |
|
||||
| 1 ~ 39 分 | 庄家:小光 | ×2 | 12 个子 |
|
||||
| 40 ~ 44 分 | 庄家:过庄 | ×1 | 6 个子 |
|
||||
| 45 ~ 84 分 | 闲家:升 1 级 | ×1 | 6 个子 |
|
||||
| 85 ~ 124 分 | 闲家:升 2 级 | ×2 | 12 个子 |
|
||||
| 125 分起,每再多 40 分 | 闲家:再升 1 级 | 再 +1 | 逐级 +6 个子 |
|
||||
|
||||
**40 分及以下坐庄(40 / 35 / 30 / 25 / 20 / 15 / 10 / 5,基础都是 6 个子;此时 `call ≤ 40`,没有"过庄"这一档)**
|
||||
|
||||
| 闲家捡分 | 判定 | 倍率 | 最终子数 |
|
||||
| --- | --- | --- | --- |
|
||||
| 0 分 | 庄家:大光 | ×3 | 18 个子 |
|
||||
| 1 分 ~(叫分 − 1)分 | 庄家:小光 | ×2 | 12 个子 |
|
||||
| 达到叫分 | 闲家:升 1 级 | ×1 | 6 个子 |
|
||||
| 达到叫分 + 40 分 | 闲家:升 2 级 | ×2 | 12 个子 |
|
||||
| 此后每再多 40 分 | 闲家:再升 1 级 | 再 +1 | 逐级 +6 个子 |
|
||||
|
||||
例如叫 10 分坐庄(基础 6 个子):0 分为大光(18 子);1 ~ 9 分为小光(12 子);捡到 10 分起升 1 级(6 子);捡到 50 分起升 2 级(12 子);之后每再多 40 分再升 1 级(每级再 +6 子)。
|
||||
|
||||
> **5 分坐庄没有"小光"**:分牌只有 5 分、10 分两种面值(见 6.1 节),闲家捡分永远是 5 的倍数。上面"1 分 ~(叫分 − 1)分为小光"这一行,在叫分为 5 分时对应区间是 `1 ~ 4 分`,里面不存在任何 5 的倍数,是空区间。所以叫 5 分坐庄时,闲家捡分只有 0(大光)和达到 5 分及以上(直接升 1 级,之后每再多 40 分再升 1 级)两种结果,没有小光这一档;这是"叫分 ≤ 40 分"这组档位里唯一的特例,其余档位(40/35/30/25/20/15/10)小光区间都至少包含一个 5 的倍数,正常存在。
|
||||
|
||||
**70 分坐庄**
|
||||
|
||||
- **投降**:基础子数按 1 个算,庄家直接输 1 个子,不经过大光 / 小光 / 过庄 / 升级判定(还没出牌捡分,直接放弃)。
|
||||
- **打牌**:基础子数按 2 个算,之后按 7.2.0 的方法正常判定,`Q` 同样固定为 40 分——判定表和 65 分坐庄完全一致,只是把"基础 2 个子"原样代入(数值恰好和 65 分相同):
|
||||
|
||||
| 闲家捡分 | 判定 | 倍率 | 最终子数 |
|
||||
| --- | --- | --- | --- |
|
||||
| 0 分 | 庄家:大光 | ×3 | 6 个子 |
|
||||
| 1 ~ 39 分 | 庄家:小光 | ×2 | 4 个子 |
|
||||
| 40 ~ 69 分 | 庄家:过庄 | ×1 | 2 个子 |
|
||||
| 70 ~ 109 分 | 闲家:升 1 级 | ×1 | 2 个子 |
|
||||
| 110 ~ 149 分 | 闲家:升 2 级 | ×2 | 4 个子 |
|
||||
| 150 分起,每再多 40 分 | 闲家:再升 1 级 | 再 +1 | 逐级 +2 个子 |
|
||||
|
||||
### 7.3 爬坡:叫分 → 基础子数与保小光分段(可选规则,开房时勾选"爬坡"生效)
|
||||
|
||||
#### 7.3.1 基础子数:叫分 → 子数的完整对照
|
||||
|
||||
叫分从 65 分(2 个子)开始,每叫低 5 分加 1 个子,直到 50 分(6 个子)——这一段与常规算子的四档数值完全相同;50 分以下,继续按每叫低 5 分加 1 个子延伸,一直到叫分范围的下限 5 分:
|
||||
|
||||
| 叫分 | 65 | 60 | 55 | 50 | 45 | 40 | 35 | 30 | 25 | 20 | 15 | 10 | 5 |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
| 基础子数 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|
||||
|
||||
#### 7.3.2 保小光分段:叫分 → 小光 / 过庄分界线(Q 值)
|
||||
|
||||
判定方法与 7.2.0 完全相同,只是把固定的 `Q = 40` 换成按叫分区间取值的 `Q`;**大光的判定条件不受 `Q` 影响,永远是 `grade == 0`**,常规算子和爬坡完全一样——`Q` 只决定"小光"和"过庄"之间的分界线:
|
||||
|
||||
| 叫分区间 | 小光 / 过庄的分界线(Q) |
|
||||
| --- | --- |
|
||||
| ≥ 45 分(含 65 / 60 / 55 / 50 / 45) | 40 分 |
|
||||
| 40 / 35 分 | 20 分 |
|
||||
| 30 / 25 分 | 15 分 |
|
||||
| 20 / 15 分 | 10 分 |
|
||||
| 10 分 | 5 分 |
|
||||
| 5 分 | 5 分(升级级距仍是 5,只是"小光"这一档因为区间为空而不存在,见下方说明) |
|
||||
|
||||
`≥ 45 分`这一档的 `Q = 40`,与 7.2 节常规算子沿用的分界线数值相同,只是子数改按 7.3.1 的公式取值(65/60/55/50 与常规算子数值一致,45 分是爬坡独有的新增档位)。
|
||||
|
||||
**5 分档没有"小光"**:分牌只有牌面 5(5 分)和牌面 10 / K(10 分)两种面值(见 6.1 节),闲家的捡分总数(`grade`)因此永远是 5 的倍数(0、5、10、15…),不可能出现 1 ~ 4 这样的中间值。当叫分是 5 分时,"小光"原本对应的区间是 `0 < grade < 5`,这个开区间里不存在任何 5 的倍数,也就是说这个区间**必然是空的**——闲家捡分要么是 0(大光),要么一旦捡到任何分牌(最少 5 分)就已经达到叫分、直接升级,中间不存在"捡到了一点、但没达标"的"小光"状态。所以 5 分档只有**大光**和**升级**两种结果,没有"小光"这一档;但 `Q` 值本身仍然是 5(与 10 分档一样),只是在"小光 / 过庄分界线"这个用途上区间恰好收窄为空,`Q = 5` 仍然继续作为"升级级距"生效(见 7.3.3 的 5 分坐庄判定表)。
|
||||
|
||||
**升级级距的通用规则**:不管哪个叫分档位,闲家赢了之后,"超过叫分多少分算升一级",用的分数差**就是该档位的 `Q` 值**——即上表里每一档的 `Q` 值,既是"小光 / 过庄"的分界线,也是"升级"的级距单位,两者共用同一个数字。例如叫分 40 分时 `Q = 20`:闲家捡分达到 40 分先升 1 级,之后每再多捡 20 分(60、80、100…)就再升一级。
|
||||
|
||||
#### 7.3.3 各分段的完整判定表(按 7.2.0 的方法代入对应 `call`、`Q`、基础子数)
|
||||
|
||||
**40 分坐庄(基础 8 个子,Q = 20)**
|
||||
|
||||
| 闲家捡分 | 判定 | 倍率 | 最终子数 |
|
||||
| --- | --- | --- | --- |
|
||||
| 0 分 | 庄家:大光 | ×3 | 24 个子 |
|
||||
| 1 ~ 19 分 | 庄家:小光 | ×2 | 16 个子 |
|
||||
| 20 ~ 39 分 | 庄家:过庄 | ×1 | 8 个子 |
|
||||
| 40 ~ 59 分 | 闲家:升 1 级 | ×1 | 8 个子 |
|
||||
| 60 ~ 79 分 | 闲家:升 2 级 | ×2 | 16 个子 |
|
||||
| 80 分起,每再多 20 分 | 闲家:再升 1 级 | 再 +1 | 逐级 +8 个子 |
|
||||
|
||||
**35 分坐庄(基础 9 个子,Q = 20)**
|
||||
|
||||
| 闲家捡分 | 判定 | 倍率 | 最终子数 |
|
||||
| --- | --- | --- | --- |
|
||||
| 0 分 | 庄家:大光 | ×3 | 27 个子 |
|
||||
| 1 ~ 19 分 | 庄家:小光 | ×2 | 18 个子 |
|
||||
| 20 ~ 34 分 | 庄家:过庄 | ×1 | 9 个子 |
|
||||
| 35 ~ 54 分 | 闲家:升 1 级 | ×1 | 9 个子 |
|
||||
| 55 ~ 74 分 | 闲家:升 2 级 | ×2 | 18 个子 |
|
||||
| 75 分起,每再多 20 分 | 闲家:再升 1 级 | 再 +1 | 逐级 +9 个子 |
|
||||
|
||||
**30 分坐庄(基础 10 个子,Q = 15)**
|
||||
|
||||
| 闲家捡分 | 判定 | 倍率 | 最终子数 |
|
||||
| --- | --- | --- | --- |
|
||||
| 0 分 | 庄家:大光 | ×3 | 30 个子 |
|
||||
| 1 ~ 14 分 | 庄家:小光 | ×2 | 20 个子 |
|
||||
| 15 ~ 29 分 | 庄家:过庄 | ×1 | 10 个子 |
|
||||
| 30 ~ 44 分 | 闲家:升 1 级 | ×1 | 10 个子 |
|
||||
| 45 ~ 59 分 | 闲家:升 2 级 | ×2 | 20 个子 |
|
||||
| 60 分起,每再多 15 分 | 闲家:再升 1 级 | 再 +1 | 逐级 +10 个子 |
|
||||
|
||||
**25 分坐庄(基础 11 个子,Q = 15)**
|
||||
|
||||
| 闲家捡分 | 判定 | 倍率 | 最终子数 |
|
||||
| --- | --- | --- | --- |
|
||||
| 0 分 | 庄家:大光 | ×3 | 33 个子 |
|
||||
| 1 ~ 14 分 | 庄家:小光 | ×2 | 22 个子 |
|
||||
| 15 ~ 24 分 | 庄家:过庄 | ×1 | 11 个子 |
|
||||
| 25 ~ 39 分 | 闲家:升 1 级 | ×1 | 11 个子 |
|
||||
| 40 ~ 54 分 | 闲家:升 2 级 | ×2 | 22 个子 |
|
||||
| 55 分起,每再多 15 分 | 闲家:再升 1 级 | 再 +1 | 逐级 +11 个子 |
|
||||
|
||||
**20 分坐庄(基础 12 个子,Q = 10)**
|
||||
|
||||
| 闲家捡分 | 判定 | 倍率 | 最终子数 |
|
||||
| --- | --- | --- | --- |
|
||||
| 0 分 | 庄家:大光 | ×3 | 36 个子 |
|
||||
| 1 ~ 9 分 | 庄家:小光 | ×2 | 24 个子 |
|
||||
| 10 ~ 19 分 | 庄家:过庄 | ×1 | 12 个子 |
|
||||
| 20 ~ 29 分 | 闲家:升 1 级 | ×1 | 12 个子 |
|
||||
| 30 ~ 39 分 | 闲家:升 2 级 | ×2 | 24 个子 |
|
||||
| 40 分起,每再多 10 分 | 闲家:再升 1 级 | 再 +1 | 逐级 +12 个子 |
|
||||
|
||||
**15 分坐庄(基础 13 个子,Q = 10)**
|
||||
|
||||
| 闲家捡分 | 判定 | 倍率 | 最终子数 |
|
||||
| --- | --- | --- | --- |
|
||||
| 0 分 | 庄家:大光 | ×3 | 39 个子 |
|
||||
| 1 ~ 9 分 | 庄家:小光 | ×2 | 26 个子 |
|
||||
| 10 ~ 14 分 | 庄家:过庄 | ×1 | 13 个子 |
|
||||
| 15 ~ 24 分 | 闲家:升 1 级 | ×1 | 13 个子 |
|
||||
| 25 ~ 34 分 | 闲家:升 2 级 | ×2 | 26 个子 |
|
||||
| 35 分起,每再多 10 分 | 闲家:再升 1 级 | 再 +1 | 逐级 +13 个子 |
|
||||
|
||||
**10 分坐庄(基础 14 个子,Q = 5——这一档同样没有"小光")**
|
||||
|
||||
分牌只有 5 分、10 分两种面值,捡分永远是 5 的倍数。`Q = 5` 时,"小光"对应的区间 `0 < grade < 5` 里不存在任何 5 的倍数,同样是空区间:闲家捡分不可能停在 1 ~ 4 分,只要捡到分就已经是 5 分(直接落入"过庄"区间)。
|
||||
|
||||
| 闲家捡分 | 判定 | 倍率 | 最终子数 |
|
||||
| --- | --- | --- | --- |
|
||||
| 0 分 | 庄家:大光 | ×3 | 42 个子 |
|
||||
| 5 ~ 9 分 | 庄家:过庄(没有小光这一档) | ×1 | 14 个子 |
|
||||
| 10 ~ 14 分 | 闲家:升 1 级 | ×1 | 14 个子 |
|
||||
| 15 ~ 19 分 | 闲家:升 2 级 | ×2 | 28 个子 |
|
||||
| 20 分起,每再多 5 分 | 闲家:再升 1 级 | 再 +1 | 逐级 +14 个子 |
|
||||
|
||||
**5 分坐庄(基础 15 个子,没有"小光",见 7.3.2 的说明)**
|
||||
|
||||
5 分是叫分范围的下限,捡分只能是 5 的倍数:一旦捡到任何分牌就已经是 5 分,等于直接达标(`grade ≥ call = 5`)。所以"小光"和"过庄"这两个区间都不存在,闲家捡分只有 0(大光)和达到 5 分及以上(直接升 1 级)两种结果。
|
||||
|
||||
| 闲家捡分 | 判定 | 倍率 | 最终子数 |
|
||||
| --- | --- | --- | --- |
|
||||
| 0 分 | 庄家:大光 | ×3 | 45 个子 |
|
||||
| 5 ~ 9 分 | 闲家:升 1 级(没有小光、过庄这两档) | ×1 | 15 个子 |
|
||||
| 10 ~ 14 分 | 闲家:升 2 级 | ×2 | 30 个子 |
|
||||
| 15 分起,每再多 5 分 | 闲家:再升 1 级 | 再 +1 | 逐级 +15 个子 |
|
||||
|
||||
`≥ 45` 分档(65 / 60 / 55 / 50 / 45)判定逻辑与 7.2.1 完全相同(Q = 40),只是 45 分档基础子数按 7.3.1 取 7 个子,其余数值套用 7.2.1 对应叫分的表即可,此处不重复列出。
|
||||
|
||||
70 分叫庄时与常规算子规则一致(见 7.2.1 末尾"70 分坐庄":投降固定基础子数 1 个,打牌基础子数 2 个并正常套用大光 / 小光 / 过庄 / 升级判定),不适用本节的保小光分段。
|
||||
|
||||
---
|
||||
|
||||
## 8. 算奖规则
|
||||
|
||||
算奖在**每个小局结算时**计算(与第 7 节的子数结算属于同一次小局结算,不是等大局所有局数打完才统一算,也不是在出牌过程中途算)。8.1 常规算奖只计入庄家;8.3 傍王是额外叠加的独立奖励,庄家、闲家都要算——两者的计入范围不同,注意区分。
|
||||
|
||||
**8.1 常规算奖只认庄家的身份,不看闲家的手牌是否达标**:即使某位闲家的手牌结构凑巧也满足 8.1 表里的条件(比如手里也有 3 张王),这份常规算奖也**不会**计入这位闲家——常规算奖从规则上就只针对庄家一人。闲家想要获得算奖,只能靠 8.3 傍王(且必须房间勾选了这条可选规则)。
|
||||
|
||||
**算奖依据的手牌快照时间点**:不管是 8.1(只看庄家)还是 8.3 傍王(庄闲都算),都按**静态初始手牌**计算——庄家是埋牌完成后的那一刻手牌,闲家是发牌完成后的那 28 张手牌;这个快照全程固定,不随之后的出牌、被吃、被打出而改变。
|
||||
|
||||
**算奖(8.1 / 8.3)与亮牌(8.2)是两个互不干涉的独立概念**:算奖是结算阶段"该给多少额外结算分"的规则;亮牌只是出牌开始前"要不要向对手公开一部分手牌统计信息"的展示规则。两者各自有自己的触发条件,只是恰好都以庄家埋牌后的手牌结构为依据,因此部分门槛数值相同——但达成亮牌的条件不代表一定触发算奖,反之亦然,具体对照见 8.2 末尾的说明。
|
||||
|
||||
### 8.1 常规算奖
|
||||
|
||||
| 组合 | 奖数 |
|
||||
| --- | --- |
|
||||
| 三个王 | 1 奖 |
|
||||
| 四个王 | 3 奖 |
|
||||
| 六个 7 | 1 奖 |
|
||||
| 七个 7 | 2 奖 |
|
||||
| 八个 7 | 3 奖 |
|
||||
| 六个 2 | 1 奖 |
|
||||
| 七个 2 | 2 奖 |
|
||||
| 八个 2 | 3 奖 |
|
||||
|
||||
三王 / 四王在拥有"正 7"及以后连续对子的情况下,每多一对连续对子再加 1 奖,连续顺序为:
|
||||
|
||||
```
|
||||
正7 - 副7 - 正2 - 副2 - A - K - Q - J - 10 - 9 - 8 - 6 - 5
|
||||
```
|
||||
|
||||
其中从 A 到 5(即 A、K、Q、J、10、9、8、6、5 这一段普通点数)都必须是选定花色的主牌,才能计入连续对子——非主花色的普通对子不算数,链条到这里就断了。
|
||||
|
||||
### 8.2 亮牌规则
|
||||
|
||||
**亮牌与算奖(8.1 / 8.3)是两个独立概念,互不干涉**:亮牌只是"要不要向对手公开一部分手牌统计信息"的展示规则,本身不产生任何奖数、不影响结算;算奖该怎么算、算多少,完全按 8.1 / 8.3 的规则来,不因为触发了亮牌而增加或减少。
|
||||
|
||||
**亮牌要求**(仅针对庄家):庄家埋牌后,若剩余手牌满足以下任一条件,出牌开始需要向两个闲家亮出对应的统计信息——**只亮数量 / 结构,不亮具体是哪几张牌**:
|
||||
|
||||
| 触发条件 | 亮出的信息 |
|
||||
| --- | --- |
|
||||
| 固定主牌(王 + 2 + 7)总数 ≥ 10 张 | 主牌总数、其中的对子数、组成的拖拉机数(如"x 张主,x 对主带 x 拖") |
|
||||
| 王 ≥ 3 张 | 王的数量(如"x 张王") |
|
||||
| 7 ≥ 6 张 | 7 的数量(如"x 张 7") |
|
||||
| 2 ≥ 6 张 | 2 的数量(如"x 张 2") |
|
||||
|
||||
同时满足多个条件时,对应的信息都要组合展示。若房间勾选了"不查牌"模式,则闲家看不到这些信息。
|
||||
|
||||
**亮牌门槛与 8.1 算奖起点的数值巧合,不代表两者是同一条规则**:王 ≥ 3 张 / 7 ≥ 6 张 / 2 ≥ 6 张这三个门槛,数值上恰好分别与 8.1 表里"三个王""六个 7""六个 2"的算奖起点相同,达到时会同时触发"亮牌"和"算奖"这两件独立的事——但它们是各自独立判定后凑巧同时发生,不是"亮牌导致算奖"或者"算奖导致亮牌"。这一点在"固定主牌 ≥ 10 张"这一档表现得最清楚:它只在亮牌这条规则里有定义、会触发亮牌,但 8.1 的算奖表里根本没有为这一档定义奖数,所以**不触发任何算奖**——如果亮牌和算奖是同一回事,这里就该矛盾;现在不矛盾,正说明两者互不干涉。
|
||||
|
||||
### 8.3 傍王(可选规则)
|
||||
|
||||
开房时勾选"傍王"后,每张王额外算一奖:这个奖是**庄家、闲家都要各自计算**(每个人按自己手里的王算,不限庄家)。
|
||||
|
||||
**傍王奖是在 8.1 常规算奖之外额外叠加的一份奖励,与常规算奖互不冲突、不互相替代**:庄家照常按 8.1 的规则算常规算奖(只计庄家),同时如果勾选了傍王,庄家、闲家还要**各自另外**再按自己手里的王数算一份傍王奖;两份奖励分开计算,最后相加。
|
||||
|
||||
### 8.4 算奖如何影响最终结算
|
||||
|
||||
**算奖是"持有奖数的那个人"从其余两名玩家那里各自多收一笔钱的独立结算线,不是把第 7 节算出的输赢结果整体重新乘一遍**:设第 7 节前三层算出的结算子数为 `X`(庄家与每个闲家之间"一对一"结算的基础金额,见 7.0 第 3 层),玩家 P 这一局总共积累了 `N_P` 奖(8.1 常规算奖 + 8.3 傍王,各自求和后相加;只有庄家可能有 8.1 的分量,8.3 傍王则庄闲都可能有):
|
||||
|
||||
- 若 `N_P > 0`,**另外两名玩家(不分是庄家还是闲家)都要各自额外付给 P:`X × N_P` 子**;
|
||||
- 若 `N_P = 0`,P 没有额外收入。
|
||||
|
||||
三名玩家可能同时都持有各自的奖数,这时会产生最多 3 条互相独立、方向不同的额外支付线,互不冲突、按各自的 `N` 值同时生效,最后与第 1~3 层的基础输赢结算加总,才是每个人这一局的最终盈亏。
|
||||
|
||||
**举例**(`X = 6` 个子,庄家为 A,闲家为 B、C):
|
||||
|
||||
- 若庄家 A 本局有 1 奖(`N_A = 1`):B、C 各自额外付给 A `6 × 1 = 6` 子。
|
||||
- 若闲家 B 同时勾选了"傍王"、本局有 2 奖(`N_B = 2`):A、C 各自额外付给 B `6 × 2 = 12` 子。
|
||||
- 这两条支付线同时成立、互不影响:A 从 B、C 各多收 6 子,B 从 A、C 各多收 12 子——包括"闲家 C 要付给闲家 B"这样的支付,即使 B、C 之间在第 1~3 层的基础输赢结算里并不直接结算(基础结算只发生在"庄家 vs 单个闲家"之间),算奖这一层是单独叠加在所有玩家两两之间的。
|
||||
|
||||
以下三点已确认:
|
||||
|
||||
- **庄家倒庄(闲家达标升级)时,8.1 常规算奖仍然生效**:算奖只看庄家埋牌后的手牌结构,与本局谁输谁赢无关。
|
||||
- **70 分投降时,仍叠加算奖倍率**:投降只是跳过了第 1~3 层的叫分 / 捡分判定,庄家埋牌后的手牌结构依然存在,8.1(以及勾选了傍王时的 8.3)照常计算并叠加。
|
||||
- **算奖对象的身份限制会带进 `N` 的计算**:8.1 只算庄家、8.3 傍王勾选后才不分庄闲(见第 8 节开头说明),把这名玩家实际适用的分量求和,才是他自己的 `N`;"其余两人向他支付 `X × N`"这条支付规则本身不因身份而改变,但闲家的 `N` 从一开始就不可能包含 8.1 的分量。
|
||||
|
||||
---
|
||||
|
||||
## 9. 牌局查看模式
|
||||
|
||||
- **可查牌模式**:
|
||||
- 当前牌局中,所有玩家已经打出的牌,任何时候都可以查看。
|
||||
- 一旦有玩家的主牌全部打空(即 5.4.2 节所说的"报无主"状态;"主牌"指大王、小王、所有花色的 2、所有花色的 7,以及主花色的其余普通牌,见第 3 节),系统就为**全体三人**(含刚刚报无主的这位玩家自己)显示另外两家各自的主牌数量、以及这些主牌里有多少对子。
|
||||
- 同时界面上会出现一个"明牌"按钮:点击后可以查看另外两家手中全部主牌的**具体牌面**(不只是数量 / 对子结构),再点一次取消查看、恢复隐藏。
|
||||
- 庄家若触发了 8.2 节的"亮牌要求",两个闲家可以看到对应的统计信息(只亮数量 / 结构,不亮具体牌面,见 8.2 节)。
|
||||
- **不可查牌模式**:不提供以上任何查看 / 明牌功能——即使有玩家报无主,也不会显示任何人的主牌数量、对子结构,也没有"明牌"按钮;庄家即使触发了 8.2 节的亮牌条件,闲家也看不到相应的统计信息(见 8.2 节)。
|
||||
|
||||
---
|
||||
|
||||
## 10. 房间设置选项
|
||||
|
||||
### 10.1 牌局扣卡方式
|
||||
|
||||
| 扣卡方式 | 6 局 | 12 局 |
|
||||
| --- | --- | --- |
|
||||
| AA 制(每人扣卡) | 每人 1 张 | 每人 2 张 |
|
||||
| 房主扣卡 | 2 张 | 4 张 |
|
||||
|
||||
### 10.2 查牌模式
|
||||
|
||||
可查牌 / 不查牌(见第 9 节)。
|
||||
|
||||
### 10.3 附加规则
|
||||
|
||||
傍王、爬坡:两者可以同时勾选(互不冲突)。
|
||||
|
||||
---
|
||||
|
||||
## 11. 牌局交互提示
|
||||
|
||||
- 首家出主牌时可以甩牌,甩牌的生效条件、跟牌规则、甩错惩罚详见 5.4 节。
|
||||
- 牌局结束需要亮出底牌。
|
||||
- 闲家在出牌时需要有 3 个提示选项:
|
||||
1. **踩**:提示对家"我能大过庄家"。
|
||||
2. **没分**:提示对家"我手上没分了"。
|
||||
3. **有分**:提示对家"我手上有分"。
|
||||
@@ -0,0 +1,431 @@
|
||||
# 二七王协议包列表
|
||||
|
||||
说明:`data` 均为发送方与接收方之间约定的 JSON 内容;成败判定只看推送包里的 `data.success`。
|
||||
|
||||
---
|
||||
|
||||
## 1. 发牌(fapai)
|
||||
|
||||
发包者:服务器 收包者:游戏 app:youle route:erqiwang rpc:fapai
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| asetidx | 整数 | 当前局数 |
|
||||
| asetcount | 整数 | 总局数 |
|
||||
| cards | 数组 | 自己得到的牌id列表 |
|
||||
| seat | 整数 | 当前等待叫分者的位置 |
|
||||
| countdown | 整数 | 叫分倒计时 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 叫分或不叫(jiaofen,客户端→服务器)
|
||||
|
||||
发包者:游戏 收包者:服务器 app:youle route:erqiwang rpc:jiaofen
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| agentid | 字符 | 代理id |
|
||||
| playerid | 整数 | 玩家id |
|
||||
| gameid | 字符 | 游戏id |
|
||||
| roomcode | 整数 | 房间号 |
|
||||
| seat | 整数 | 叫分者的位置序号 |
|
||||
| call | 整数 | 分数,0表示不叫 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 叫分或不叫(jiaofen,服务器→客户端)
|
||||
|
||||
发包者:服务器 收包者:游戏 app:youle route:erqiwang rpc:jiaofen
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| seat | 整数 | 叫分者的位置序号 |
|
||||
| call | 整数 | 分数,0表示不叫 |
|
||||
| currcall | 整数 | 当前叫到的分数 |
|
||||
| multiple | 整数 | 倍数 |
|
||||
| nextseat | 整数 | 下一个叫分者的位置序号 |
|
||||
| countdown | 整数 | 叫分倒计时 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 上庄(shangzhuang)
|
||||
|
||||
发包者:服务器 收包者:游戏 app:youle route:erqiwang rpc:shangzhuang
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| seat | 整数 | 叫分者的位置序号 |
|
||||
| call | 整数 | 分数,0表示不叫 |
|
||||
| banker | 整数 | 庄家的位置序号 |
|
||||
| grade | 整数 | 庄家的叫分 |
|
||||
| multiple | 整数 | 倍数 |
|
||||
| bottomcards | 数组 | 底牌 |
|
||||
| cards | 数组 | 拿了底牌后手上的牌,包括底牌,庄家才有此属性,闲家没有该属性 |
|
||||
| countdown | 整数 | 选主倒计时 |
|
||||
| touxiang | 整数 | 是否允许投降 0:不允许 1:允许 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 投降(touxiang,客户端→服务器)
|
||||
|
||||
发包者:游戏 收包者:服务器 app:youle route:erqiwang rpc:touxiang
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| agentid | 字符 | 代理id |
|
||||
| playerid | 整数 | 玩家id |
|
||||
| gameid | 字符 | 游戏id |
|
||||
| roomcode | 整数 | 房间号 |
|
||||
| seat | 整数 | 庄家的位置序号(`mod.js` 中校验 `o_paiju.banker != seat`,只有庄家能投降;选主/埋牌/投降阶段均可发起) |
|
||||
|
||||
---
|
||||
|
||||
## 6. 选主(xuanzhu,客户端→服务器)
|
||||
|
||||
发包者:游戏 收包者:服务器 app:youle route:erqiwang rpc:xuanzhu
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| agentid | 字符 | 代理id |
|
||||
| playerid | 整数 | 玩家id |
|
||||
| gameid | 字符 | 游戏id |
|
||||
| roomcode | 整数 | 房间号 |
|
||||
| seat | 整数 | 选主者的位置序号 |
|
||||
| flower | 整数 | 花色 1方块 2梅花 3红心 4黑桃 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 选主(xuanzhu,服务器→客户端)
|
||||
|
||||
发包者:服务器 收包者:游戏 app:youle route:erqiwang rpc:xuanzhu
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| banker | 整数 | 庄家的位置序号 |
|
||||
| flower | 整数 | 花色 1方块 2梅花 3红心 4黑桃 |
|
||||
| countdown | 整数 | 埋牌倒计时 |
|
||||
| cards | 数组 | 选主后自己手上的牌id列表 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 埋牌(maipai,客户端→服务器)
|
||||
|
||||
发包者:游戏 收包者:服务器 app:youle route:erqiwang rpc:maipai
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| agentid | 字符 | 代理id |
|
||||
| playerid | 整数 | 玩家id |
|
||||
| gameid | 字符 | 游戏id |
|
||||
| roomcode | 整数 | 房间号 |
|
||||
| seat | 整数 | 埋牌者的位置序号 |
|
||||
| cards | 数组 | 埋牌的id列表 |
|
||||
|
||||
---
|
||||
|
||||
## 9. 埋牌(maipai,服务器→客户端)
|
||||
|
||||
发包者:服务器 收包者:游戏 app:youle route:erqiwang rpc:maipai
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| cards | 数组 | 埋牌后手上的牌,去掉了埋牌,庄家才有此属性,闲家没有该属性 |
|
||||
| bottomcards | 数组 | 埋的牌,庄家才有此属性,闲家没有该属性 |
|
||||
| seat | 整数 | 出牌者的位置序号 |
|
||||
| countdown | 整数 | 出牌倒计时 |
|
||||
|
||||
---
|
||||
|
||||
## 10. 出牌(chupai,客户端→服务器)
|
||||
|
||||
发包者:游戏 收包者:服务器 app:youle route:erqiwang rpc:chupai
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| agentid | 字符 | 代理id |
|
||||
| playerid | 整数 | 玩家id |
|
||||
| gameid | 字符 | 游戏id |
|
||||
| roomcode | 整数 | 房间号 |
|
||||
| seat | 整数 | 出牌者的位置序号 |
|
||||
| cards | 数组 | 出牌的id列表 |
|
||||
|
||||
---
|
||||
|
||||
## 11. 第一个玩家出牌(chupai1)
|
||||
|
||||
发包者:服务器 收包者:游戏 app:youle route:erqiwang rpc:chupai1
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| seat | 整数 | 出牌者的位置序号 |
|
||||
| cards | 数组 | 出的牌id列表 |
|
||||
| info | 数组 | 出牌者的座位牌况 `o_paiju.seatlist[seat]`,5 元素数组:前 4 个对应花色1~4 的 `[无该花色标志, 该花色无对标志]`(0/1),第 5 个为报副情况 `[剩余主牌数, 剩余主对数]`(初始 `[-1,-1]`,报无主后为实际数量) |
|
||||
| count | 整数 | 出牌数量 |
|
||||
| flower | 整数 | 出牌花色 |
|
||||
| cardtype | 整数 | 出牌牌型 |
|
||||
| nextseat | 整数 | 下一个出牌者的位置序号 |
|
||||
| countdown | 整数 | 下一个出牌者的出牌倒计时 |
|
||||
| baozhu | 整数 | 是否已有玩家报无主(主牌出空)0否 1是;对应服务端 `have_baofu()`(任一玩家 `seatlist[seat][4][0]==0` 即为 1),供客户端在可查牌模式下展示他家主牌数量/对子结构 |
|
||||
| cardsinhand | 数组 | 出牌者出牌后手上剩下的牌id列表,只有出牌者才有此属性 |
|
||||
|
||||
---
|
||||
|
||||
## 12. 第二个玩家出牌(chupai2)
|
||||
|
||||
发包者:服务器 收包者:游戏 app:youle route:erqiwang rpc:chupai2
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| seat | 整数 | 出牌者的位置序号 |
|
||||
| cards | 数组 | 出的牌id列表 |
|
||||
| info | 数组 | 出牌者的座位牌况 `o_paiju.seatlist[seat]`,5 元素数组:前 4 个对应花色1~4 的 `[无该花色标志, 该花色无对标志]`(0/1),第 5 个为报副情况 `[剩余主牌数, 剩余主对数]`(初始 `[-1,-1]`,报无主后为实际数量) |
|
||||
| nextseat | 整数 | 下一个出牌者的位置序号 |
|
||||
| countdown | 整数 | 出牌倒计时 |
|
||||
| baozhu | 整数 | 是否已有玩家报无主(主牌出空)0否 1是;对应服务端 `have_baofu()`(任一玩家 `seatlist[seat][4][0]==0` 即为 1),供客户端在可查牌模式下展示他家主牌数量/对子结构 |
|
||||
| cardsinhand | 数组 | 出牌后出牌者手上剩下的牌id列表,只有出牌者才有此属性 |
|
||||
|
||||
---
|
||||
|
||||
## 13. 第三个玩家出牌(chupai3)
|
||||
|
||||
发包者:服务器 收包者:游戏 app:youle route:erqiwang rpc:chupai3
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| seat | 整数 | 出牌者的位置序号 |
|
||||
| cards | 数组 | 出的牌id列表 |
|
||||
| info | 数组 | 出牌者的座位牌况 `o_paiju.seatlist[seat]`,5 元素数组:前 4 个对应花色1~4 的 `[无该花色标志, 该花色无对标志]`(0/1),第 5 个为报副情况 `[剩余主牌数, 剩余主对数]`(初始 `[-1,-1]`,报无主后为实际数量) |
|
||||
| nextseat | 整数 | 下一个出牌者的位置序号 |
|
||||
| countdown | 整数 | 出牌倒计时 |
|
||||
| baozhu | 整数 | 是否已有玩家报无主(主牌出空)0否 1是;对应服务端 `have_baofu()`(任一玩家 `seatlist[seat][4][0]==0` 即为 1),供客户端在可查牌模式下展示他家主牌数量/对子结构 |
|
||||
| cardsinhand | 数组 | 出牌后出牌者手上剩下的牌id列表,只有出牌者才有此属性 |
|
||||
| maxseat | 整数 | 本轮出牌谁最大,只有本轮最后一个玩家出牌后才有此属性 |
|
||||
| grade | 整数 | 本轮闲家得分,只有本轮最后一个玩家出牌后且闲家有得分才有此属性 |
|
||||
|
||||
---
|
||||
|
||||
## 14. 结算(jiesuan)
|
||||
|
||||
发包者:服务器 收包者:游戏 app:youle route:erqiwang rpc:jiesuan
|
||||
|
||||
结算包由以下几个子结构拼装而成。三种结算来源的组成不同:
|
||||
|
||||
- **正常出牌结算**(`mod.chupai` 中最后一张牌出完,`get_paiju_account(0, ...)`):含 `chupai` + `bottom` + `aset`(末局再加 `account`)。
|
||||
- **投降结算**(`mod.touxiang`,`get_paiju_account(1, ...)`):只含 `aset`(末局再加 `account`),无 `chupai`、无 `bottom`。
|
||||
- **解散结算**(`export.get_disbandRoom`,`get_paiju_account(2, ...)`):只含 `aset` + `account`,无 `chupai`、无 `bottom`。
|
||||
|
||||
**chupai(出牌包,仅正常出牌结算存在)**
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| seat | 整数 | 出牌者的位置序号,投降和解散无此属性 |
|
||||
| cards | 数组 | 出的牌id列表,投降和解散无此属性 |
|
||||
| maxseat | 整数 | 本轮出牌谁最大,投降和解散无此属性 |
|
||||
| grade | 整数 | 本轮闲家得分,投降和解散无此属性 |
|
||||
| gradecards | 数组 | 本轮闲家得分分牌列表,投降和解散无此属性。**注意:当前源码中赋值语句被注释(`class.paiju.js`/`mod.js` 中相关行均被注释掉),该字段实际永远不会出现在下发的包里,属于失效字段** |
|
||||
|
||||
**bottom(抠底包,仅正常出牌结算存在;`cards` 恒有,其余仅闲家抠底时有)**
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| cards | 数组 | 底牌,投降和解散无此属性 |
|
||||
| multiple | 整数 | 闲家抠底倍数,闲家没抠底无此属性,投降和解散无此属性 |
|
||||
| grade1 | 整数 | 底牌分数,闲家没抠底无此属性,投降和解散无此属性 |
|
||||
| grade2 | 整数 | 闲家抠底得分,闲家没抠底无此属性,投降和解散无此属性 |
|
||||
|
||||
**aset(单局结算包)**
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| banker | 整数 | 庄家的位置序号,-1:无庄 |
|
||||
| call | 整数 | 庄家叫分,-1:无叫分 |
|
||||
| multiple | 整数 | 叫分倍数 `get_multiple_bycall(call)`:call>60→1,call>40→2,call>0→4;无叫分(call≤0)→0 |
|
||||
| flower | 整数 | 主牌花色,-1:无主牌花色 |
|
||||
| grade | 整数 | 闲家得分 |
|
||||
| upgrade | 整数 | 升级级数:0中途解散 1过庄 2小光 4大光 -1倒庄 <-1升几级 -99投降 |
|
||||
| seatlist | 数组 | 玩家列表,元素结构见下 |
|
||||
|
||||
`seatlist` 数组元素结构:
|
||||
|
||||
```json
|
||||
{
|
||||
"cards": [], // 王牌或冲关牌列表
|
||||
"obsolete": 0, // 冲关作废标志 0不作废 1作废
|
||||
"chongguan": 0, // 冲关数
|
||||
"grade_cg": 0, // 冲关得分
|
||||
"grade_w": 0, // 傍王得分
|
||||
"grade_jf": 0, // 捡分得分
|
||||
"grade": 0, // 本局总分
|
||||
"score": 0 // 累计得分
|
||||
}
|
||||
```
|
||||
|
||||
**account(大局结算包)**
|
||||
|
||||
> 仅当打到最后一局(`o_paiju.idx >= o_room.asetcount`)或房间中途解散(解散结算)时,`jiesuan` 包才会带上此分组;普通的中间局结算包没有 `account` 字段(`class.paiju.js` `get_paiju_account`)。
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| account | 数组 | 玩家列表,第一位为累积得分,第二位为每局得分,结构见下 |
|
||||
|
||||
```json
|
||||
[
|
||||
[0, []],
|
||||
[0, []],
|
||||
[0, []]
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 15. 准备(zhunbei,客户端→服务器)
|
||||
|
||||
发包者:游戏 收包者:服务器 app:youle route:erqiwang rpc:zhunbei
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| agentid | 字符 | 代理id |
|
||||
| playerid | 整数 | 玩家id |
|
||||
| gameid | 字符 | 游戏id |
|
||||
| roomcode | 整数 | 房间号 |
|
||||
| seat | 整数 | 位置序号 |
|
||||
|
||||
---
|
||||
|
||||
## 16. 准备(zhunbei,服务器→客户端)
|
||||
|
||||
发包者:服务器 收包者:游戏 app:youle route:erqiwang rpc:zhunbei
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| seat | 整数 | 位置序号 |
|
||||
|
||||
---
|
||||
|
||||
## 断线重连(deskinfo)
|
||||
|
||||
> 由平台在玩家进入房间/断线重连时回调 `youle_erqiwang.export.get_deskinfo(o_room, seat)` 生成并下发(服务器→客户端);下发的 app/route/rpc 由平台的进房/重连流程决定,不在本子游戏代码内固定。下列字段即该函数返回的 `deskinfo` 对象结构,按当前 `paiju.step` 只带对应阶段的分组。
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| count | 整数 | 总局数 |
|
||||
| idx | 整数 | 当前局数 |
|
||||
| PlayerInfo | 数组 | 三个玩家目前的总积分 `[0,0,0]` |
|
||||
| step | 整数 | 牌桌状态:1发完牌叫分 2选主 3埋牌 4投降 5出牌 6结算 |
|
||||
| MyCards | 数组 | 自己手上的牌 |
|
||||
|
||||
**CallRun(不在叫分阶段无此属性)**
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| seat | 整数 | 当前叫分位置 |
|
||||
| countdown | 整数 | 叫分倒计时 |
|
||||
| nowcall | 整数 | 当前叫分 |
|
||||
| multiple | 整数 | 倍数 |
|
||||
| call | 数组 | 三家叫分 `[null,0,65]`,null还未叫分,0不叫,>0叫了多少分 |
|
||||
|
||||
**ChooseMain(不在选主阶段无此属性)**
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| banker | 整数 | 庄家 |
|
||||
| call | 整数 | 叫分 |
|
||||
| multiple | 整数 | 倍数 |
|
||||
| countdown | 整数 | 选主倒计时 |
|
||||
| bottomcards | 数组 | 底牌 |
|
||||
| touxiang | 整数 | 是否允许投降 0:不允许 1:允许 |
|
||||
|
||||
**BuryCards(不在埋牌阶段无此属性)**
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| banker | 整数 | 庄家 |
|
||||
| call | 整数 | 叫分 |
|
||||
| multiple | 整数 | 倍数 |
|
||||
| flower | 整数 | 主牌花色 |
|
||||
| countdown | 整数 | 埋牌倒计时 |
|
||||
| bottomcards | 数组 | 底牌 |
|
||||
| touxiang | 整数 | 是否允许投降 0:不允许 1:允许 |
|
||||
|
||||
**Surrender(不在投降阶段无此属性)**
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| banker | 整数 | 庄家 |
|
||||
| call | 整数 | 叫分 |
|
||||
| multiple | 整数 | 倍数 |
|
||||
| flower | 整数 | 主牌花色 |
|
||||
| countdown | 整数 | 出牌倒计时 |
|
||||
| bottomcards | 数组 | 埋的牌,只有庄家有此属性 |
|
||||
| touxiang | 整数 | 是否允许投降 0:不允许 1:允许 |
|
||||
|
||||
**PushCards(不在出牌阶段无此属性)**
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| banker | 整数 | 庄家 |
|
||||
| call | 整数 | 叫分 |
|
||||
| multiple | 整数 | 倍数 |
|
||||
| flower | 整数 | 主牌花色 |
|
||||
| countdown | 整数 | 出牌倒计时 |
|
||||
| bottomcards | 数组 | 埋的牌,只有庄家有此属性 |
|
||||
| grade | 整数 | 当前的捡分分数 |
|
||||
| gradecards | 数组 | 当前的捡分分牌,只有闲家有此属性 |
|
||||
| playproc | json | 当前出牌情况 `o_paiju.playproc`:`round` 第几轮、`start` 本轮首出位置、`currseat` 当前出牌位置、`startcount` 首出张数、`startflower` 首出花色、`starttype` 首出牌型、`maxseat` 本轮最大者位置、`maxcard` 最大牌编码、`cards` 本轮三家各自出的牌(数组下标=位置序号) |
|
||||
| seatlist | 数组 | 三家座位牌况,元素结构同上文 chupai 包的 `info`(每个玩家一个 5 元素数组:4 个花色的 `[无该花色,无对]` + 报副 `[剩余主牌数,剩余主对数]`) |
|
||||
| pushlist | 数组 | 出牌历史 `[[[], [], []], [[], [], []], ...]`,外层下标=轮次,内层 3 个数组按位置序号存该轮各家出的牌id(已按大小排序) |
|
||||
|
||||
**Balance(不在结算阶段无此属性)**
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| readystate | 数组 | 所有玩家的准备状态 |
|
||||
| aset | json | 单局结算包,同上面结算包中的 `aset`,只有当自己的准备状态为0时才有此属性 |
|
||||
|
||||
---
|
||||
|
||||
## 战绩(大局列表)gameinfo1
|
||||
|
||||
> 非客户端收发包:大局结束/解散时由 `class.desk.js` `get_desk_account` 组装,经 `import.save_grade(o_room, o_gameinfo1, o_gameinfo2, 1)` 传给平台战绩服务持久化(服务器→平台)。
|
||||
|
||||
| 参数名 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| roomcode | 整数 | 房号 |
|
||||
| asetcount | 整数 | 实际局数 |
|
||||
| createtime | 字符 | 开房时间 |
|
||||
| makewartime | 字符 | 开战时间 |
|
||||
| players | 数组 | 玩家列表,元素结构见下 |
|
||||
|
||||
`players` 数组元素结构:
|
||||
|
||||
```json
|
||||
{
|
||||
"seat": 0, // 座位
|
||||
"playerid": 100001, // 玩家ID
|
||||
"name": "", // 昵称
|
||||
"avatar": "", // 头像
|
||||
"score": 0 // 成绩
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 战绩(大局)gameinfo2
|
||||
|
||||
> 非客户端收发包:与 gameinfo1 同批,由 `import.save_grade` 的第 3 个参数传给平台战绩服务(服务器→平台)。
|
||||
|
||||
牌局列表,数组元素结构:
|
||||
|
||||
```json
|
||||
{
|
||||
"starttime": "", // 开始时间
|
||||
"endtime": "", // 结束时间
|
||||
"seatlist": [6, -6, 0], // 玩家成绩
|
||||
"callproc": [], // 叫分过程,详情见代码中的注释
|
||||
"banker": 0, // 庄
|
||||
"call": 0, // 叫分
|
||||
"flower": 0, // 主牌花色
|
||||
"result": 0, // 牌局结果
|
||||
"cards": [] // 发牌出牌情况,详情见代码中的注释
|
||||
}
|
||||
```
|
||||
Binary file not shown.
@@ -0,0 +1,457 @@
|
||||
//////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////咻一咻/////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
var youle_erqiwang = youle_erqiwang || cls_mod.new("youle_erqiwang", "erqiwang", youle_app);
|
||||
|
||||
//牌类
|
||||
min_loadJsFile("games/erqiwang/class.pai.js", function(){
|
||||
//牌局类
|
||||
min_loadJsFile("games/erqiwang/class.paiju.js", function(){
|
||||
//牌桌类
|
||||
min_loadJsFile("games/erqiwang/class.desk.js", function(){
|
||||
//算法类
|
||||
min_loadJsFile("games/erqiwang/class.arith.js", function(){
|
||||
//对内的输入接口类
|
||||
min_loadJsFile("games/erqiwang/class.import.js", function(){
|
||||
//对内的输出接口类
|
||||
min_loadJsFile("games/erqiwang/class.export.js", function(){
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
//对内的输入接口类(需要的外部接口)
|
||||
youle_erqiwang.import = null;
|
||||
//对内的输出接口类(提供给外部使用的接口)
|
||||
youle_erqiwang.export = null;
|
||||
|
||||
//叫分或不叫
|
||||
youle_erqiwang.jiaofen = function(pack){
|
||||
var agentid = pack.data.agentid;
|
||||
var playerid = parseInt(pack.data.playerid);
|
||||
var gameid = pack.data.gameid;
|
||||
var roomcode = parseInt(pack.data.roomcode);
|
||||
var seat = parseInt(pack.data.seat);
|
||||
var call = parseInt(pack.data.call);
|
||||
//检查玩家的位置是否正确
|
||||
var o_room = youle_erqiwang.import.check_player(agentid, gameid, roomcode, seat, playerid, pack.conmode, pack.fromid);
|
||||
if (!o_room){
|
||||
return;
|
||||
}
|
||||
//牌桌
|
||||
var o_desk = o_room.o_desk;
|
||||
if (!o_desk){
|
||||
return;
|
||||
}
|
||||
//牌局
|
||||
var o_paiju = o_desk.method.curr_paiju();
|
||||
if (!o_paiju){
|
||||
return;
|
||||
}
|
||||
//检查当前的游戏阶段是否是叫分阶段
|
||||
if (o_paiju.step != 1){
|
||||
return;
|
||||
}
|
||||
//检查当前的叫分位置
|
||||
if (o_paiju.method.get_callgrade_seat() != seat){
|
||||
return;
|
||||
}
|
||||
//检查当前的叫分分值必须大于0,且是5的整数倍
|
||||
if (call < 0 || call % 5 != 0){
|
||||
return;
|
||||
}
|
||||
var curr_call = o_paiju.method.get_callgrade_value();
|
||||
//第一家不能不叫分
|
||||
if (curr_call == 0 && call == 0){
|
||||
return;
|
||||
}
|
||||
//叫分必须小于之前的叫分
|
||||
if (curr_call != 0 && call >= curr_call){
|
||||
return;
|
||||
}
|
||||
|
||||
//叫分成功
|
||||
o_paiju.method.do_callgrade(call);
|
||||
|
||||
//返回前端
|
||||
if (o_paiju.step == 1){
|
||||
//等待下一家叫分
|
||||
var msg = {};
|
||||
msg.app = "youle";
|
||||
msg.route = "erqiwang";
|
||||
msg.rpc = "jiaofen";
|
||||
msg.data = {};
|
||||
msg.data.seat = seat;
|
||||
msg.data.call = call;
|
||||
msg.data.currcall = o_paiju.method.get_callgrade_value();
|
||||
msg.data.multiple = cls_youle_erqiwang_arith.get_multiple_bycall(msg.data.currcall);
|
||||
msg.data.nextseat = o_paiju.method.get_callgrade_seat();
|
||||
msg.data.countdown = o_desk.method.get_countdown_jiaofen();
|
||||
o_room.method.sendpack_toother(msg, -1);
|
||||
return;
|
||||
}
|
||||
if (o_paiju.step == 2){
|
||||
//等待庄家叫主
|
||||
var msg = {};
|
||||
msg.app = "youle";
|
||||
msg.route = "erqiwang";
|
||||
msg.rpc = "shangzhuang";
|
||||
msg.data = {};
|
||||
msg.data.seat = seat;
|
||||
msg.data.call = call;
|
||||
msg.data.banker = o_paiju.banker;
|
||||
msg.data.grade = o_paiju.call;
|
||||
msg.data.multiple = cls_youle_erqiwang_arith.get_multiple_bycall(msg.data.grade);
|
||||
msg.data.bottomcards = o_paiju.method.get_bottomcards();
|
||||
msg.data.countdown = o_desk.method.get_countdown_xuanzhu();
|
||||
if (o_paiju.call < 65){
|
||||
msg.data.touxiang = 0;
|
||||
} else {
|
||||
msg.data.touxiang = 1;
|
||||
}
|
||||
|
||||
for (var i = 0; i < o_room.seatlist.length; i++) {
|
||||
msg.conmode = o_room.seatlist[i].conmode;
|
||||
msg.fromid = o_room.seatlist[i].fromid;
|
||||
if (i == o_paiju.banker){
|
||||
msg.data.cards = o_paiju.method.get_seat_cards(i);
|
||||
} else {
|
||||
delete msg.data.cards;
|
||||
}
|
||||
youle_erqiwang.app.SendPack(msg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//选主
|
||||
youle_erqiwang.xuanzhu = function(pack){
|
||||
var agentid = pack.data.agentid;
|
||||
var playerid = parseInt(pack.data.playerid);
|
||||
var gameid = pack.data.gameid;
|
||||
var roomcode = parseInt(pack.data.roomcode);
|
||||
var seat = parseInt(pack.data.seat);
|
||||
var flower = parseInt(pack.data.flower);
|
||||
//检查玩家的位置是否正确
|
||||
var o_room = youle_erqiwang.import.check_player(agentid, gameid, roomcode, seat, playerid, pack.conmode, pack.fromid);
|
||||
if (!o_room){
|
||||
return;
|
||||
}
|
||||
//牌桌
|
||||
var o_desk = o_room.o_desk;
|
||||
if (!o_desk){
|
||||
return;
|
||||
}
|
||||
//牌局
|
||||
var o_paiju = o_desk.method.curr_paiju();
|
||||
if (!o_paiju){
|
||||
return;
|
||||
}
|
||||
//检查当前的游戏阶段是否是选主阶段
|
||||
if (o_paiju.step != 2){
|
||||
return;
|
||||
}
|
||||
//检查庄家位置
|
||||
if (o_paiju.banker != seat){
|
||||
return;
|
||||
}
|
||||
//检查花色
|
||||
if (flower != 1 && flower != 2 && flower != 3 && flower != 4){
|
||||
return;
|
||||
}
|
||||
|
||||
//选主成功
|
||||
o_paiju.method.do_choiceflower(flower);
|
||||
|
||||
//返回前端
|
||||
var msg = {};
|
||||
msg.app = "youle";
|
||||
msg.route = "erqiwang";
|
||||
msg.rpc = "xuanzhu";
|
||||
msg.data = {};
|
||||
msg.data.banker = o_paiju.banker;
|
||||
msg.data.flower = flower;
|
||||
msg.data.countdown = o_desk.method.get_countdown_maipai();
|
||||
for (var i = 0; i < o_room.seatlist.length; i++) {
|
||||
msg.conmode = o_room.seatlist[i].conmode;
|
||||
msg.fromid = o_room.seatlist[i].fromid;
|
||||
msg.data.cards = o_paiju.method.get_seat_cards(i);
|
||||
youle_erqiwang.app.SendPack(msg);
|
||||
}
|
||||
}
|
||||
|
||||
//埋牌
|
||||
youle_erqiwang.maipai = function(pack){
|
||||
var agentid = pack.data.agentid;
|
||||
var playerid = parseInt(pack.data.playerid);
|
||||
var gameid = pack.data.gameid;
|
||||
var roomcode = parseInt(pack.data.roomcode);
|
||||
var seat = parseInt(pack.data.seat);
|
||||
var cards = pack.data.cards;
|
||||
//检查玩家的位置是否正确
|
||||
var o_room = youle_erqiwang.import.check_player(agentid, gameid, roomcode, seat, playerid, pack.conmode, pack.fromid);
|
||||
if (!o_room){
|
||||
return;
|
||||
}
|
||||
//牌桌
|
||||
var o_desk = o_room.o_desk;
|
||||
if (!o_desk){
|
||||
return;
|
||||
}
|
||||
//牌局
|
||||
var o_paiju = o_desk.method.curr_paiju();
|
||||
if (!o_paiju){
|
||||
return;
|
||||
}
|
||||
//检查当前的游戏阶段是否是埋牌阶段
|
||||
if (o_paiju.step != 3){
|
||||
return;
|
||||
}
|
||||
//检查庄家位置
|
||||
if (o_paiju.banker != seat){
|
||||
return;
|
||||
}
|
||||
//检查埋牌是否是8张
|
||||
if (cards.length != 8){
|
||||
return;
|
||||
}
|
||||
//检查埋牌是否都在庄家手上
|
||||
if (!o_paiju.method.check_cards_inhand(cards, seat)){
|
||||
return;
|
||||
}
|
||||
|
||||
//埋牌成功
|
||||
o_paiju.method.do_burycard(cards);
|
||||
|
||||
//返回前端
|
||||
var msg = {};
|
||||
msg.app = "youle";
|
||||
msg.route = "erqiwang";
|
||||
msg.rpc = "maipai";
|
||||
msg.data = {};
|
||||
msg.data.seat = o_paiju.playproc.currseat;
|
||||
msg.data.countdown = o_desk.method.get_countdown_chupai();
|
||||
for (var i = 0; i < o_room.seatlist.length; i++) {
|
||||
msg.conmode = o_room.seatlist[i].conmode;
|
||||
msg.fromid = o_room.seatlist[i].fromid;
|
||||
if (i == o_paiju.banker){
|
||||
msg.data.cards = o_paiju.method.get_seat_cards(i);
|
||||
msg.data.bottomcards = cards;
|
||||
} else {
|
||||
delete msg.data.cards;
|
||||
delete msg.data.bottomcards;
|
||||
}
|
||||
youle_erqiwang.app.SendPack(msg);
|
||||
}
|
||||
}
|
||||
|
||||
//投降
|
||||
youle_erqiwang.touxiang = function(pack){
|
||||
var agentid = pack.data.agentid;
|
||||
var playerid = parseInt(pack.data.playerid);
|
||||
var gameid = pack.data.gameid;
|
||||
var roomcode = parseInt(pack.data.roomcode);
|
||||
var seat = parseInt(pack.data.seat);
|
||||
//检查玩家的位置是否正确
|
||||
var o_room = youle_erqiwang.import.check_player(agentid, gameid, roomcode, seat, playerid, pack.conmode, pack.fromid);
|
||||
if (!o_room){
|
||||
return;
|
||||
}
|
||||
//牌桌
|
||||
var o_desk = o_room.o_desk;
|
||||
if (!o_desk){
|
||||
return;
|
||||
}
|
||||
//牌局
|
||||
var o_paiju = o_desk.method.curr_paiju();
|
||||
if (!o_paiju){
|
||||
return;
|
||||
}
|
||||
//检查当前的游戏阶段是否是叫分、埋牌、投降阶段
|
||||
if (o_paiju.step != 2 && o_paiju.step != 3 && o_paiju.step != 4){
|
||||
return;
|
||||
}
|
||||
//检查庄家位置
|
||||
if (o_paiju.banker != seat){
|
||||
return;
|
||||
}
|
||||
//检查是否允许投降
|
||||
if (o_paiju.call < 65){
|
||||
return;
|
||||
}
|
||||
|
||||
//投降结算
|
||||
o_paiju.step = 6;
|
||||
var msg = {};
|
||||
msg.app = "youle";
|
||||
msg.route = "erqiwang";
|
||||
msg.rpc = "jiesuan";
|
||||
msg.data = {};
|
||||
msg = o_paiju.method.get_paiju_account(1, msg);
|
||||
o_room.method.sendpack_toother(msg, -1);
|
||||
}
|
||||
|
||||
//出牌
|
||||
youle_erqiwang.chupai = function(pack){
|
||||
var agentid = pack.data.agentid;
|
||||
var playerid = parseInt(pack.data.playerid);
|
||||
var gameid = pack.data.gameid;
|
||||
var roomcode = parseInt(pack.data.roomcode);
|
||||
var seat = parseInt(pack.data.seat);
|
||||
var cards = pack.data.cards;
|
||||
//检查玩家的位置是否正确
|
||||
var o_room = youle_erqiwang.import.check_player(agentid, gameid, roomcode, seat, playerid, pack.conmode, pack.fromid);
|
||||
if (!o_room){
|
||||
return;
|
||||
}
|
||||
//牌桌
|
||||
var o_desk = o_room.o_desk;
|
||||
if (!o_desk){
|
||||
return;
|
||||
}
|
||||
//牌局
|
||||
var o_paiju = o_desk.method.curr_paiju();
|
||||
if (!o_paiju){
|
||||
return;
|
||||
}
|
||||
//检查当前的游戏阶段是否是出牌阶段
|
||||
if (o_paiju.step != 4 && o_paiju.step != 5){
|
||||
return;
|
||||
}
|
||||
//检查出牌位置
|
||||
if (seat != o_paiju.playproc.currseat){
|
||||
return;
|
||||
}
|
||||
//检查出牌
|
||||
if (cards.length == 0){
|
||||
return;
|
||||
}
|
||||
//检查出牌是否都在玩家手上
|
||||
if (!o_paiju.method.check_cards_inhand(cards, seat)){
|
||||
return;
|
||||
}
|
||||
|
||||
//检查选中的牌是否可出,并出牌
|
||||
var re = o_paiju.method.do_playcard(cards);
|
||||
if (!re.result){
|
||||
return;
|
||||
}
|
||||
|
||||
//返回前端
|
||||
var msg = {};
|
||||
msg.app = "youle";
|
||||
msg.route = "erqiwang";
|
||||
msg.data = {};
|
||||
msg.data.seat = seat;
|
||||
msg.data.cards = cards;
|
||||
msg.data.info = o_paiju.seatlist[seat];
|
||||
msg.data.nextseat = o_paiju.playproc.currseat;
|
||||
msg.data.countdown = o_desk.method.get_countdown_chupai();
|
||||
if (o_paiju.method.have_baofu()){
|
||||
msg.data.baozhu = 1;
|
||||
} else {
|
||||
msg.data.baozhu = 0;
|
||||
}
|
||||
|
||||
switch (re.idx)
|
||||
{
|
||||
case 1: //第一个出牌
|
||||
msg.rpc = "chupai1";
|
||||
msg.data.count = re.count;
|
||||
msg.data.flower = re.flower;
|
||||
msg.data.cardtype = re.cardtype;
|
||||
break;
|
||||
case 2: //第二个出牌
|
||||
msg.rpc = "chupai2";
|
||||
break;
|
||||
case 3: //第三个出牌
|
||||
msg.rpc = "chupai3";
|
||||
msg.data.maxseat = re.maxseat;
|
||||
if (re.grade > 0){
|
||||
msg.data.grade = re.grade;
|
||||
// msg.data.gradecards = re.gradecards;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (o_paiju.step == 5){
|
||||
for (var i = 0; i < o_room.seatlist.length; i++){
|
||||
msg.conmode = o_room.seatlist[i].conmode;
|
||||
msg.fromid = o_room.seatlist[i].fromid;
|
||||
if (i == seat){
|
||||
msg.data.cardsinhand = o_paiju.method.get_seat_cards(i);
|
||||
} else {
|
||||
delete msg.data.cardsinhand;
|
||||
}
|
||||
youle_erqiwang.app.SendPack(msg);
|
||||
}
|
||||
} else if (o_paiju.step == 6){
|
||||
//小局结算
|
||||
//出牌包的调整
|
||||
msg.data.chupai = {};
|
||||
msg.data.chupai.seat = msg.data.seat;
|
||||
msg.data.chupai.cards = msg.data.cards;
|
||||
msg.data.chupai.maxseat = msg.data.maxseat;
|
||||
if (msg.data.grade){
|
||||
msg.data.chupai.grade = msg.data.grade;
|
||||
}
|
||||
if (msg.data.gradecards){
|
||||
msg.data.chupai.gradecards = msg.data.gradecards;
|
||||
}
|
||||
delete msg.data.seat;
|
||||
delete msg.data.cards;
|
||||
delete msg.data.info;
|
||||
delete msg.data.nextseat;
|
||||
delete msg.data.countdown;
|
||||
delete msg.data.baozhu;
|
||||
delete msg.data.cardsinhand;
|
||||
delete msg.data.maxseat;
|
||||
delete msg.data.grade;
|
||||
delete msg.data.gradecards;
|
||||
|
||||
//获取抠底包
|
||||
msg = o_paiju.method.get_bottom_account(msg);
|
||||
//获取小局结算包
|
||||
msg = o_paiju.method.get_paiju_account(0, msg);
|
||||
o_room.method.sendpack_toother(msg, -1);
|
||||
}
|
||||
}
|
||||
|
||||
//准备
|
||||
youle_erqiwang.zhunbei = function(pack){
|
||||
var agentid = pack.data.agentid;
|
||||
var playerid = parseInt(pack.data.playerid);
|
||||
var gameid = pack.data.gameid;
|
||||
var roomcode = parseInt(pack.data.roomcode);
|
||||
var seat = parseInt(pack.data.seat);
|
||||
//检查玩家的位置是否正确
|
||||
var o_room = youle_erqiwang.import.check_player(agentid, gameid, roomcode, seat, playerid, pack.conmode, pack.fromid);
|
||||
if (!o_room){
|
||||
return;
|
||||
}
|
||||
//牌桌
|
||||
var o_desk = o_room.o_desk;
|
||||
if (!o_desk){
|
||||
return;
|
||||
}
|
||||
//牌局
|
||||
var o_paiju = o_desk.method.curr_paiju();
|
||||
if (!o_paiju){
|
||||
return;
|
||||
}
|
||||
//检查当前的游戏阶段是否是结算阶段
|
||||
if (o_paiju.step != 6){
|
||||
return;
|
||||
}
|
||||
//检查当前局数小于总局数
|
||||
if (o_desk.paiju_list.length >= o_room.asetcount){
|
||||
return;
|
||||
}
|
||||
|
||||
//准备成功
|
||||
o_desk.method.do_prepare(seat);
|
||||
}
|
||||
Reference in New Issue
Block a user