优化游玩方式
This commit is contained in:
@@ -8,6 +8,7 @@ use app\api\util\ApiLang;
|
||||
use app\api\service\LotteryService;
|
||||
use app\dice\model\lottery_pool_config\DiceLotteryPoolConfig;
|
||||
use app\dice\model\play_record\DicePlayRecord;
|
||||
use app\dice\model\ante_config\DiceAnteConfig;
|
||||
use app\dice\model\player\DicePlayer;
|
||||
use app\dice\model\player_ticket_record\DicePlayerTicketRecord;
|
||||
use app\dice\model\player_wallet_record\DicePlayerWalletRecord;
|
||||
@@ -34,8 +35,12 @@ class PlayStartLogic
|
||||
/** 对局状态:超时/失败 */
|
||||
public const RECORD_STATUS_TIMEOUT = 0;
|
||||
|
||||
/** 开启对局最低余额 = |DiceRewardConfig 最小 real_ev + 100| */
|
||||
private const MIN_COIN_EXTRA = 100;
|
||||
/** 单注费用(对应原票价 100) */
|
||||
private const UNIT_COST = 100;
|
||||
/** 免费抽奖注数缓存 key 前缀(用于强制下一局注数一致) */
|
||||
private const FREE_ANTE_KEY_PREFIX = 'api:game:free_ante:';
|
||||
/** 免费抽奖注数缓存过期(秒) */
|
||||
private const FREE_ANTE_TTL = 86400 * 7;
|
||||
/** 豹子号中大奖额外平台币(无 BIGWIN 配置时兜底) */
|
||||
private const SUPER_WIN_BONUS = 500;
|
||||
/** 可触发超级大奖的 grid_number(5=全1 10=全2 15=全3 20=全4 25=全5 30=全6) */
|
||||
@@ -47,36 +52,60 @@ class PlayStartLogic
|
||||
* 执行一局游戏
|
||||
* @param int $playerId 玩家ID
|
||||
* @param int $direction 方向 0=无/顺时针 1=中奖/逆时针(前端 direction)
|
||||
* @param int $ante 注数(必须在 DiceAnteConfig.mult 中存在)
|
||||
* @return array 成功返回 DicePlayRecord 数据;余额不足时抛 ApiException,message 为约定文案
|
||||
*/
|
||||
public function run(int $playerId, int $direction): array
|
||||
public function run(int $playerId, int $direction, int $ante): array
|
||||
{
|
||||
$player = DicePlayer::find($playerId);
|
||||
if (!$player) {
|
||||
throw new ApiException('User not found');
|
||||
}
|
||||
|
||||
$minEv = DiceRewardConfig::getCachedMinRealEv();
|
||||
$minCoin = abs($minEv + self::MIN_COIN_EXTRA);
|
||||
$coin = (float) $player->coin;
|
||||
if ($coin < $minCoin) {
|
||||
throw new ApiException(ApiLang::translateParams('当前玩家余额%s小于%s无法继续游戏', [$coin, $minCoin]));
|
||||
if ($ante <= 0) {
|
||||
throw new ApiException('ante must be a positive integer');
|
||||
}
|
||||
|
||||
$paid = (int) ($player->paid_ticket_count ?? 0);
|
||||
$free = (int) ($player->free_ticket_count ?? 0);
|
||||
if ($paid + $free <= 0) {
|
||||
throw new ApiException('Insufficient lottery tickets');
|
||||
// 注数合规校验:ante 必须存在于 dice_ante_config.mult
|
||||
$anteConfigModel = new DiceAnteConfig();
|
||||
$exists = $anteConfigModel->where('mult', $ante)->count();
|
||||
if ($exists <= 0) {
|
||||
throw new ApiException('当前注数不合规,请选择正确的注数');
|
||||
}
|
||||
|
||||
// 免费抽奖:不再使用抽奖券作为开始条件,仅用 free_ticket_count 表示“免费抽奖次数”
|
||||
$freeCount = (int) ($player->free_ticket_count ?? 0);
|
||||
$isFree = $freeCount > 0;
|
||||
$ticketType = $isFree ? self::LOTTERY_TYPE_FREE : self::LOTTERY_TYPE_PAID;
|
||||
|
||||
// 若为免费抽奖:注数必须与上一次触发免费抽奖时的注数一致
|
||||
if ($isFree) {
|
||||
$requiredAnte = Cache::get(self::FREE_ANTE_KEY_PREFIX . $playerId);
|
||||
if ($requiredAnte !== null && $requiredAnte !== '' && (int) $requiredAnte !== $ante) {
|
||||
throw new ApiException('免费抽奖注数必须与上一次一致,请修改注数后继续');
|
||||
}
|
||||
}
|
||||
|
||||
$lotteryService = LotteryService::getOrCreate($playerId);
|
||||
$ticketType = LotteryService::drawTicketType($paid, $free);
|
||||
$configType0 = DiceLotteryPoolConfig::where('name', 'default')->find();
|
||||
$configType1 = DiceLotteryPoolConfig::where('name', 'killScore')->find();
|
||||
if (!$configType0) {
|
||||
throw new ApiException('Lottery pool config not found (name=default required)');
|
||||
}
|
||||
|
||||
// 余额校验:统一校验 ante * min(real_ev)
|
||||
$minEv = DiceRewardConfig::getCachedMinRealEv();
|
||||
$needMinBalance = abs((float) $minEv) * $ante;
|
||||
if ($coin < $needMinBalance) {
|
||||
throw new ApiException('未达抽奖余额 ' . $needMinBalance . ',无法开始游戏');
|
||||
}
|
||||
|
||||
// 付费抽奖:开始前扣除费用 ante * 100,不足则提示余额不足
|
||||
$paidAmount = $ticketType === self::LOTTERY_TYPE_PAID ? ($ante * self::UNIT_COST) : 0;
|
||||
if ($ticketType === self::LOTTERY_TYPE_PAID && $coin < $paidAmount) {
|
||||
throw new ApiException('余额不足');
|
||||
}
|
||||
|
||||
// 彩金池累计盈利:用于判断是否触发杀分(不再依赖单个玩家累计盈利)
|
||||
// 该值来自 dice_lottery_pool_config.profit_amount
|
||||
$poolProfitTotal = $configType0->profit_amount ?? 0;
|
||||
@@ -133,7 +162,8 @@ class PlayStartLogic
|
||||
$rollNumber = (int) ($chosen['grid_number'] ?? 0);
|
||||
$realEv = (float) ($chosen['real_ev'] ?? 0);
|
||||
$isTierT5 = (string) ($chosen['tier'] ?? '') === 'T5';
|
||||
$rewardWinCoin = $isTierT5 ? $realEv : (100 + $realEv);
|
||||
// 玩家始终增加:(100 + real_ev) * ante(费用已在开始前扣除;免费抽奖同样按该口径结算)
|
||||
$rewardWinCoin = (self::UNIT_COST + $realEv) * $ante;
|
||||
|
||||
// 豹子判定:5/30 必豹子;10/15/20/25 按 DiceRewardConfig 中 BIGWIN 该点数的 weight 判定(0-10000,10000=100%)
|
||||
// 杀分档位:不触发豹子,5/30 已在上方抽取时排除,10/15/20/25 仅生成非豹子组合
|
||||
@@ -166,7 +196,8 @@ class PlayStartLogic
|
||||
if ($doSuperWin) {
|
||||
$rollArray = $this->getSuperWinRollArray($rollNumber);
|
||||
$isWin = 1;
|
||||
$superWinCoin = $bigWinRealEv > 0 ? $bigWinRealEv : self::SUPER_WIN_BONUS;
|
||||
$bigWinEv = $bigWinRealEv > 0 ? $bigWinRealEv : self::SUPER_WIN_BONUS;
|
||||
$superWinCoin = (self::UNIT_COST + $bigWinEv) * $ante;
|
||||
// 中 BIGWIN 豹子:不走原奖励流程,不记录原奖励,不触发 T5 再来一次,仅发放豹子奖金
|
||||
$rewardWinCoin = 0;
|
||||
$realEv = 0;
|
||||
@@ -203,6 +234,8 @@ class PlayStartLogic
|
||||
$rewardId,
|
||||
$configName,
|
||||
$ticketType,
|
||||
$ante,
|
||||
$paidAmount,
|
||||
$winCoin,
|
||||
$superWinCoin,
|
||||
$rewardWinCoin,
|
||||
@@ -221,11 +254,13 @@ class PlayStartLogic
|
||||
'admin_id' => $adminId,
|
||||
'lottery_config_id' => $configId,
|
||||
'lottery_type' => $ticketType,
|
||||
'ante' => $ante,
|
||||
'paid_amount' => $paidAmount,
|
||||
'is_win' => $isWin,
|
||||
'win_coin' => $winCoin,
|
||||
'super_win_coin' => $superWinCoin,
|
||||
'reward_win_coin' => $rewardWinCoin,
|
||||
'use_coins' => 0,
|
||||
'use_coins' => $paidAmount,
|
||||
'direction' => $direction,
|
||||
'reward_config_id' => $rewardId,
|
||||
'start_index' => $startIndex,
|
||||
@@ -241,34 +276,40 @@ class PlayStartLogic
|
||||
throw new \RuntimeException('玩家不存在');
|
||||
}
|
||||
$coinBefore = (float) $p->coin;
|
||||
$coinAfter = $coinBefore + $winCoin;
|
||||
// 开始前先扣付费金额,再加中奖金额(免费抽奖 paid_amount=0)
|
||||
$coinAfter = $coinBefore - $paidAmount + $winCoin;
|
||||
$p->coin = $coinAfter;
|
||||
$p->total_ticket_count = max(0, (int) $p->total_ticket_count - 1);
|
||||
if ($ticketType === self::LOTTERY_TYPE_PAID) {
|
||||
$p->paid_ticket_count = max(0, (int) $p->paid_ticket_count - 1);
|
||||
} else {
|
||||
// 不再使用抽奖券作为抽奖条件:付费不扣抽奖次数;免费抽奖仅消耗 free_ticket_count
|
||||
if ($ticketType === self::LOTTERY_TYPE_FREE) {
|
||||
$p->free_ticket_count = max(0, (int) $p->free_ticket_count - 1);
|
||||
}
|
||||
|
||||
// 若本局中奖档位为 T5,则额外赠送 1 次免费抽奖次数(总次数也 +1),并记录抽奖券获取记录
|
||||
if ($isTierT5) {
|
||||
$p->free_ticket_count = (int) $p->free_ticket_count + 1;
|
||||
$p->total_ticket_count = (int) $p->total_ticket_count + 1;
|
||||
|
||||
DicePlayerTicketRecord::create([
|
||||
'player_id' => $playerId,
|
||||
'admin_id' => $adminId,
|
||||
'ante' => $ante,
|
||||
'free_ticket_count' => 1,
|
||||
'remark' => '中奖结果为T5',
|
||||
]);
|
||||
// 记录免费抽奖注数,用于强制下一局注数一致
|
||||
Cache::set(self::FREE_ANTE_KEY_PREFIX . $playerId, $ante, self::FREE_ANTE_TTL);
|
||||
} else {
|
||||
// 若本次消耗了最后一次免费抽奖,则清理注数锁
|
||||
if ($ticketType === self::LOTTERY_TYPE_FREE && (int) $p->free_ticket_count <= 0) {
|
||||
Cache::delete(self::FREE_ANTE_KEY_PREFIX . $playerId);
|
||||
}
|
||||
}
|
||||
|
||||
$p->save();
|
||||
|
||||
// 彩金池累计盈利累加在 name=default 彩金池上:
|
||||
// 付费券:每局按“当前中奖金额(含 BIGWIN) - 抽奖券费用 100”
|
||||
// 付费:每局按“当前中奖金额(含 BIGWIN) - 抽奖费用(ante*100)”
|
||||
// 免费券:取消票价成本 100,只计入中奖金额
|
||||
$perPlayProfit = ($ticketType === self::LOTTERY_TYPE_PAID) ? ($winCoin - 100.0) : $winCoin;
|
||||
$perPlayProfit = ($ticketType === self::LOTTERY_TYPE_PAID) ? ($winCoin - (float) $paidAmount) : $winCoin;
|
||||
$addProfit = $perPlayProfit;
|
||||
try {
|
||||
DiceLotteryPoolConfig::where('id', $type0ConfigId)->update([
|
||||
@@ -285,7 +326,8 @@ class PlayStartLogic
|
||||
DicePlayerWalletRecord::create([
|
||||
'player_id' => $playerId,
|
||||
'admin_id' => $adminId,
|
||||
'coin' => $winCoin,
|
||||
// 钱包流水记录本局净变化:-付费金额 + 中奖金额(免费抽奖付费金额为 0)
|
||||
'coin' => $winCoin - (float) $paidAmount,
|
||||
'type' => self::WALLET_TYPE_DRAW,
|
||||
'wallet_before' => $coinBefore,
|
||||
'wallet_after' => $coinAfter,
|
||||
@@ -336,7 +378,6 @@ class PlayStartLogic
|
||||
$arr['tier'] = $tier ?? '';
|
||||
// 记录完数据后返回当前玩家余额与抽奖次数
|
||||
$arr['coin'] = $updated ? (float) $updated->coin : 0;
|
||||
$arr['total_ticket_count'] = $updated ? (int) $updated->total_ticket_count : 0;
|
||||
return $arr;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user