二七王服务端:按 design.md 整改结算/算奖/甩牌,roomtype 改位串

结算与算奖(§6.3/§7/§8)
- 扣底倍数改线性:单张×1、主对×2、N连对×2N
- 算子实现常规算子 + 爬坡两套(大光×3、Q=40 / 爬坡分段),X=基础子数×判定倍率
- 常规算奖只计庄家;连对链修复(传实际主花色);移除 ≥10 老主误加奖与设计外的
  「冲关必叫/四王必踢」作废;算奖快照取庄家埋牌后28张(新增 get_seat_cards_award)
- 算奖并入结算改为 X×(2Ni−Nj−Nk);傍王按房间开关、庄闲每王1奖

出牌与甩牌(§5.4)
- 副牌绝对禁甩;甩牌最大性按对手全部手牌逐分量判定(新增 trump_rank/
  decompose_trump/opp_can_beat_flush);甩错惩罚(收回、只打最小一张、下发 shuaicuo)
- 修复 can_followcard 引用未定义 tuolaji_list 的崩溃、can_playcard 14张上限失效

开局/投降/暗牌(§4)
- 投降改为选主阶段(step2)与选主互斥、不埋牌、算奖用庄家36张;埋牌后直进出牌(删废弃step4)
- 70分暗牌摸牌前向所有玩家亮3秒(+ancard3s),非70分不再泄露暗牌给闲家;叫分上限≤70

查牌/亮牌(§8.2/§9)
- 新增亮牌 get_liangpai(庄家埋牌后28张按阈值统计);报无主统计与亮牌按查牌位门控
- 新增 mingpai 明牌 RPC(可查牌+已报无主时查看他家全部主牌)

房间与配置(§10)
- 局数6/12、房主扣卡2/4;新增 class.config.js 以位串解析 roomtype(唯一入口 SSOT)
- 清理 do_choiceflower 死代码

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-04 22:32:53 +08:00
co-authored by Claude Opus 4.8
parent 96372d713c
commit 6120580505
5 changed files with 601 additions and 387 deletions
+38 -101
View File
@@ -7,79 +7,32 @@ var cls_youle_erqiwang_export = cls_youle_erqiwang_export || {
var exp = {};
//解析roomtype获取开房所需房卡
//解析roomtype获取开房所需房卡(design §10.1)
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;
var cfg = cls_youle_erqiwang_config.parse(roomtype);
if (cfg.deductAA){
//AA每人扣卡:6局每人1张 / 12局每人2张
return cfg.aset12 ? 2 : 1;
}
//房主扣卡:6局2张 / 12局4张
return cfg.aset12 ? 4 : 2;
}
//解析roomtype获取总局数
//解析roomtype获取总局数(design §10.1:6局 / 12局)
exp.get_asetcount = function(roomtype){
switch (roomtype[0])
{
case 1:
return 2;
break;
case 2:
return 4;
break;
default:
return 4;
break;
}
var cfg = cls_youle_erqiwang_config.parse(roomtype);
return cfg.aset12 ? 12 : 6;
}
//加入房间时根据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;
var cfg = cls_youle_erqiwang_config.parse(roomtype);
if (cfg.deductAA){
//AA每人扣卡:加入者按自己那份扣(6局1张 / 12局2张)
return cfg.aset12 ? 2 : 1;
}
//房主扣卡:加入者不扣
return 0;
}
//开战
@@ -138,11 +91,13 @@ var cls_youle_erqiwang_export = cls_youle_erqiwang_export || {
//庄家叫分倍数
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();
deskinfo.ChooseMain.countdown = o_room.o_desk.method.get_countdown_xuanzhu();
//底牌:暗牌只有庄家可见(70分的3秒亮牌是上庄时的一次性事件,重连不重放)
if (seat == paiju.banker){
deskinfo.ChooseMain.bottomcards = paiju.method.get_bottomcards();
}
//是否可投降
if (paiju.call < 65){
if (paiju.call != 70){
deskinfo.ChooseMain.touxiang = 0;
} else {
deskinfo.ChooseMain.touxiang = 1;
@@ -157,40 +112,14 @@ var cls_youle_erqiwang_export = cls_youle_erqiwang_export || {
//庄家叫分倍数
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.countdown = o_room.o_desk.method.get_countdown_maipai();
//底牌:暗牌只有庄家可见
if (seat == paiju.banker){
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 = {};
@@ -217,8 +146,16 @@ var cls_youle_erqiwang_export = cls_youle_erqiwang_export || {
deskinfo.PushCards.grade = jian_grade.grade;
//当前的出牌情况
deskinfo.PushCards.playproc = paiju.playproc;
//座位列表
deskinfo.PushCards.seatlist = paiju.seatlist;
//座位列表(报副统计)与亮牌:仅可查牌模式下展示(§9/§8.2)
if (!cls_youle_erqiwang_config.parse(o_room.roomtype).nocheck){
deskinfo.PushCards.seatlist = paiju.seatlist;
if (seat != paiju.banker){
var _lp = paiju.method.get_liangpai();
if (_lp){
deskinfo.PushCards.liangpai = _lp;
}
}
}
//出牌历史
deskinfo.PushCards.pushlist = [];
for (var i = 0; i < paiju.cards.length; i++) {