diff --git a/games/casino.py b/games/casino.py index 29ff089..7120905 100644 --- a/games/casino.py +++ b/games/casino.py @@ -767,7 +767,9 @@ class CasinoGame(BaseGame): if action in ['open', '开启', '开始']: return await self._open_blackjack(sub_args, chat_id, user_id) - elif action in ['bet', '下注', '押']: + elif action in ['join', '加入']: + return await self._join_blackjack(chat_id, user_id) + elif action in ['bet', '下注', '押', '加注']: return await self._bet_blackjack(sub_args, chat_id, user_id) elif action in ['deal', '发牌']: return await self._deal_blackjack(chat_id, user_id) @@ -791,23 +793,19 @@ class CasinoGame(BaseGame): try: parts = args.split() if len(parts) < 2: - return "❌ 参数格式错误!\n\n正确格式:`.赌场 21点 open <最小下注> <最大下注> [黑杰克倍数]`\n\n示例:`.赌场 21点 open 10 100 1.5`" + return "❌ 参数格式错误!\n\n正确格式:`.赌场 21点 open <底注> <黑杰克倍数>`\n\n示例:`.赌场 21点 open 50 1.5`" try: - min_bet = int(parts[0]) - max_bet = int(parts[1]) - blackjack_multiplier = float(parts[2]) if len(parts) > 2 else 1.5 + base_bet = int(parts[0]) + blackjack_multiplier = float(parts[1]) except ValueError: return "❌ 参数必须是数字!" - if min_bet <= 0 or max_bet <= 0: - return "❌ 下注金额必须大于0!" + if base_bet <= 0: + return "❌ 底注必须大于0!" - if min_bet > max_bet: - return "❌ 最小下注不能大于最大下注!" - - if max_bet > 10000: - return "❌ 最大下注不能超过10000分!" + if base_bet > 10000: + return "❌ 底注不能超过10000分!" if blackjack_multiplier <= 0: return "❌ 黑杰克倍数必须大于0!" @@ -820,8 +818,8 @@ class CasinoGame(BaseGame): chat_id=chat_id, game_type='21点', banker_id=user_id, - min_bet=min_bet, - max_bet=max_bet, + min_bet=base_bet, # 底注作为最小下注 + max_bet=base_bet * 10, # 最大下注设为底注的10倍 multiplier=1.0, # 21点标准赔率1:1 house_fee=0.05, current_phase='betting', @@ -831,12 +829,11 @@ class CasinoGame(BaseGame): banker_display_name = self.db.get_user_display_name(user_id) text = f"## 🎰 21点游戏已开启\n\n" text += f"**庄家**:{banker_display_name}\n\n" - text += f"**最小下注**:{min_bet} 分\n\n" - text += f"**最大下注**:{max_bet} 分\n\n" + text += f"**底注**:{base_bet} 分\n\n" text += f"**黑杰克倍数**:{blackjack_multiplier} 倍\n\n" text += f"**抽水率**:5%\n\n" text += "---\n\n" - text += "💡 提示:玩家下注后,庄家使用 `.赌场 21点 deal` 发牌" + text += f"💡 提示:玩家使用 `.赌场 21点 join` 加入游戏(需要至少{base_bet}分积分)" return text @@ -844,47 +841,49 @@ class CasinoGame(BaseGame): logger.error(f"开启21点游戏失败: {e}", exc_info=True) return f"❌ 开启游戏失败: {str(e)}" - async def _bet_blackjack(self, args: str, chat_id: int, user_id: int) -> str: - """玩家下注21点""" + async def _join_blackjack(self, chat_id: int, user_id: int) -> str: + """玩家加入21点游戏""" try: - try: - amount = int(args.strip()) - except ValueError: - return "❌ 下注金额必须是数字!\n\n正确格式:`.赌场 21点 bet <金额>`" - session = self.db.get_active_casino_session(chat_id, '21点') if not session: return "❌ 当前没有进行中的游戏,请等待庄家开启游戏。" if session['current_phase'] != 'betting': - return "❌ 当前不是下注阶段,无法下注。" + return "❌ 当前不是加入阶段,游戏已开始。" - if amount < session['min_bet']: - return f"❌ 下注金额太小!最小下注:{session['min_bet']} 分" + # 检查是否已经加入 + bets = self.db.get_pending_bets(chat_id, '21点') + existing_bet = next((b for b in bets if b['user_id'] == user_id), None) + if existing_bet: + return f"❌ 您已经加入游戏,当前下注:{existing_bet['amount']} 分" - if amount > session['max_bet']: - return f"❌ 下注金额太大!最大下注:{session['max_bet']} 分" + base_bet = session['min_bet'] + # 检查积分是否足够底注 user_points = self.db.get_user_points(user_id) - if user_points['points'] < amount: - return f"❌ 积分不足!需要 {amount} 分,当前可用 {user_points['points']} 分" + if user_points['points'] < base_bet: + return f"❌ 积分不足!需要至少 {base_bet} 分才能加入,当前可用 {user_points['points']} 分" - if not self.db.consume_points(user_id, amount, 'casino_bet', "21点游戏下注"): + # 扣除底注 + if not self.db.consume_points(user_id, base_bet, 'casino_bet', "21点游戏加入(底注)"): return "❌ 扣除积分失败!" + # 创建下注记录(使用底注) bet_id = self.db.create_casino_bet( chat_id=chat_id, game_type='21点', user_id=user_id, bet_type='标准', - amount=amount, + amount=base_bet, multiplier=1.0, # 标准赔率1:1 hand_status='playing' ) updated_points = self.db.get_user_points(user_id) - text = f"## 🎲 下注成功\n\n" - text += f"**下注金额**:{amount} 分\n\n" + player_name = self.db.get_user_display_name(user_id) + text = f"## 🎲 加入成功\n\n" + text += f"**玩家**:{player_name}\n\n" + text += f"**底注**:{base_bet} 分\n\n" text += f"**剩余积分**:{updated_points['points']} 分\n\n" text += "---\n\n" text += "💡 等待庄家发牌..." @@ -892,8 +891,74 @@ class CasinoGame(BaseGame): return text except Exception as e: - logger.error(f"21点下注失败: {e}", exc_info=True) - return f"❌ 下注失败: {str(e)}" + logger.error(f"21点加入失败: {e}", exc_info=True) + return f"❌ 加入失败: {str(e)}" + + async def _bet_blackjack(self, args: str, chat_id: int, user_id: int) -> str: + """玩家加注(游戏中加注)""" + try: + try: + amount = int(args.strip()) + except ValueError: + return "❌ 加注金额必须是数字!\n\n正确格式:`.赌场 21点 bet <加注金额>`\n\n加注金额必须不低于底注" + + session = self.db.get_active_casino_session(chat_id, '21点') + if not session: + return "❌ 当前没有进行中的游戏。" + + if session['current_phase'] != 'playing': + return "❌ 当前不是游戏阶段,无法加注。" + + base_bet = session['min_bet'] + if amount < base_bet: + return f"❌ 加注金额太小!必须不低于底注:{base_bet} 分" + + # 检查玩家是否有下注 + bets = self.db.get_pending_bets(chat_id, '21点') + user_bet = next((b for b in bets if b['user_id'] == user_id), None) + if not user_bet: + return "❌ 您没有加入游戏,无法加注" + + # 检查玩家手牌状态 + session_id = session['id'] + hand = self.db.get_blackjack_hand(session_id, user_id) + if not hand or hand['hand_status'] != 'playing': + return f"❌ 当前手牌状态不允许加注(状态:{hand['hand_status'] if hand else '未找到'})" + + # 检查积分是否足够 + user_points = self.db.get_user_points(user_id) + if user_points['points'] < amount: + return f"❌ 积分不足!需要 {amount} 分,当前可用 {user_points['points']} 分" + + # 扣除加注金额 + if not self.db.consume_points(user_id, amount, 'casino_bet', "21点游戏加注"): + return "❌ 扣除积分失败!" + + # 更新下注金额 + cursor = self.db.conn.cursor() + cursor.execute(""" + UPDATE casino_bets + SET amount = amount + ? + WHERE id = ? + """, (amount, user_bet['id'])) + + updated_points = self.db.get_user_points(user_id) + player_name = self.db.get_user_display_name(user_id) + new_total = user_bet['amount'] + amount + + text = f"## 🎲 加注成功\n\n" + text += f"**玩家**:{player_name}\n\n" + text += f"**加注金额**:{amount} 分\n\n" + text += f"**总下注**:{new_total} 分(原底注 {user_bet['amount']} + 加注 {amount})\n\n" + text += f"**剩余积分**:{updated_points['points']} 分\n\n" + text += "---\n\n" + text += "💡 可以继续要牌或停牌" + + return text + + except Exception as e: + logger.error(f"21点加注失败: {e}", exc_info=True) + return f"❌ 加注失败: {str(e)}" def _draw_card(self) -> int: """抽取一张牌(1-13,其中1=A,11=J,12=Q,13=K)""" @@ -930,17 +995,28 @@ class CasinoGame(BaseGame): session_id = session['id'] - # 为庄家发两张牌 - banker_cards = [self._draw_card(), self._draw_card()] - banker_points = self._calculate_points(banker_cards) - banker_status = 'blackjack' if self._check_blackjack(banker_cards) else 'playing' + # 标准发牌顺序: + # 1. 先给每个玩家发一张牌 + # 2. 再给庄家发一张明牌 + # 3. 再给每个玩家发第二张牌 + # 4. 最后给庄家发第二张暗牌 - self.db.create_blackjack_hand(session_id, 0, banker_cards, banker_status) - - # 为每个下注的玩家发两张牌 player_hands = {} + player_first_cards = {} + + # 第一轮:给每个玩家发一张牌 for bet in bets: - player_cards = [self._draw_card(), self._draw_card()] + first_card = self._draw_card() + player_first_cards[bet['user_id']] = [first_card] + + # 给庄家发第一张明牌 + banker_first_card = self._draw_card() + banker_cards = [banker_first_card] + + # 第二轮:给每个玩家发第二张牌 + for bet in bets: + second_card = self._draw_card() + player_cards = player_first_cards[bet['user_id']] + [second_card] player_points = self._calculate_points(player_cards) player_status = 'blackjack' if self._check_blackjack(player_cards) else 'playing' @@ -951,6 +1027,15 @@ class CasinoGame(BaseGame): 'status': player_status } + # 给庄家发第二张暗牌 + banker_second_card = self._draw_card() + banker_cards.append(banker_second_card) + banker_points = self._calculate_points(banker_cards) + banker_status = 'blackjack' if self._check_blackjack(banker_cards) else 'playing' + + # 存储庄家完整手牌(包含暗牌) + self.db.create_blackjack_hand(session_id, 0, banker_cards, banker_status) + # 更新session阶段 cursor = self.db.conn.cursor() cursor.execute(""" @@ -959,9 +1044,9 @@ class CasinoGame(BaseGame): WHERE id = ? """, (session_id,)) - # 构建发牌结果 + # 构建发牌结果(庄家只显示明牌) text = f"## 🎰 发牌完成\n\n" - text += f"**庄家手牌**:{self._format_cards(banker_cards)} ({banker_points}点)\n\n" + text += f"**庄家手牌**:{self._format_cards([banker_first_card])} ? (隐藏一张暗牌)\n\n" if banker_status == 'blackjack': text += "**庄家黑杰克!**\n\n" @@ -976,7 +1061,7 @@ class CasinoGame(BaseGame): text += "\n" text += "\n---\n\n" - text += "💡 玩家可以使用 `.赌场 21点 hit` 要牌或 `.赌场 21点 stand` 停牌" + text += "💡 玩家可以使用 `.赌场 21点 hit` 要牌、`.赌场 21点 stand` 停牌或 `.赌场 21点 bet <金额>` 加注" return text @@ -1051,6 +1136,30 @@ class CasinoGame(BaseGame): elif points == 21: text += "**21点!** ✅\n\n" + # 如果爆牌,检查是否所有玩家都已完成 + if status == 'busted': + all_hands = self.db.get_all_blackjack_hands(session_id) + bets = self.db.get_pending_bets(chat_id, '21点') + all_players_done = True + + for bet in bets: + player_hand = all_hands.get(bet['user_id']) + if player_hand and player_hand['hand_status'] == 'playing': + all_players_done = False + break + + if all_players_done: + text += "---\n\n" + text += "✅ 所有玩家已完成操作,自动结算中...\n\n" + # 调用结算方法(使用庄家ID) + try: + settlement_result = await self._settle_blackjack(chat_id, session['banker_id']) + return settlement_result + except Exception as e: + logger.error(f"自动结算失败: {e}", exc_info=True) + text += f"❌ 自动结算失败: {str(e)}" + return text + text += "---\n\n" if status == 'playing': text += "💡 可以继续要牌或停牌" @@ -1088,11 +1197,37 @@ class CasinoGame(BaseGame): points = self._calculate_points(hand['hand_data']) player_name = self.db.get_user_display_name(user_id) + + # 检查是否所有玩家都已停牌或爆牌 + all_hands = self.db.get_all_blackjack_hands(session_id) + bets = self.db.get_pending_bets(chat_id, '21点') + all_players_done = True + + for bet in bets: + player_hand = all_hands.get(bet['user_id']) + if player_hand and player_hand['hand_status'] == 'playing': + all_players_done = False + break + text = f"## 🎲 停牌\n\n" text += f"**玩家**:{player_name}\n\n" text += f"**最终手牌**:{self._format_cards(hand['hand_data'])} ({points}点)\n\n" - text += "---\n\n" - text += "💡 已停牌,等待其他玩家操作或庄家结算" + + # 如果所有玩家都已完成,自动结算 + if all_players_done: + text += "---\n\n" + text += "✅ 所有玩家已完成操作,自动结算中...\n\n" + # 调用结算方法(使用庄家ID) + try: + settlement_result = await self._settle_blackjack(chat_id, session['banker_id']) + return settlement_result + except Exception as e: + logger.error(f"自动结算失败: {e}", exc_info=True) + text += f"❌ 自动结算失败: {str(e)}" + return text + else: + text += "---\n\n" + text += "💡 已停牌,等待其他玩家操作" return text @@ -1118,13 +1253,20 @@ class CasinoGame(BaseGame): text += f"**最大下注**:{session['max_bet']} 分\n\n" text += f"**黑杰克倍数**:{session.get('blackjack_multiplier', 1.5)} 倍\n\n" - # 显示庄家手牌 + # 显示庄家手牌(隐藏暗牌) banker_hand = hands.get(0) if banker_hand: - banker_points = self._calculate_points(banker_hand['cards']) - text += f"**庄家手牌**:{self._format_cards(banker_hand['cards'])} ({banker_points}点)" - if banker_hand['status'] == 'blackjack': - text += " **黑杰克!**" + banker_cards = banker_hand['cards'] + if session['current_phase'] == 'playing' and len(banker_cards) >= 2: + # 游戏阶段:只显示第一张明牌,隐藏暗牌 + visible_card = banker_cards[0] + text += f"**庄家手牌**:{self._format_cards([visible_card])} ? (隐藏一张暗牌)" + else: + # 已结算或发牌前:显示完整手牌 + banker_points = self._calculate_points(banker_cards) + text += f"**庄家手牌**:{self._format_cards(banker_cards)} ({banker_points}点)" + if banker_hand['status'] == 'blackjack': + text += " **黑杰克!**" text += "\n\n" text += "---\n\n" @@ -1293,28 +1435,39 @@ class CasinoGame(BaseGame): """获取21点游戏帮助信息""" return """## 🎰 21点游戏帮助 +### 游戏流程 +1. **庄家开启**:`.赌场 21点 open <底注> <黑杰克倍数>` + - 示例:`.赌场 21点 open 50 1.5` +2. **玩家加入**:`.赌场 21点 join`(需要至少底注的积分) +3. **庄家发牌**:`.赌场 21点 deal` +4. **玩家操作**:要牌、停牌或加注 +5. **自动结算**:所有玩家停牌后自动结算 + ### 庄家命令 -- `.赌场 21点 open <最小> <最大> [黑杰克倍数]` - 开启游戏 - - 示例:`.赌场 21点 open 10 100 1.5` -- `.赌场 21点 deal` - 发牌(所有玩家下注后) -- `.赌场 21点 settle` - 结算游戏(自动要牌到17点以上后结算) +- `.赌场 21点 open <底注> <黑杰克倍数>` - 开启游戏 + - 示例:`.赌场 21点 open 50 1.5` +- `.赌场 21点 deal` - 发牌(玩家加入后) +- `.赌场 21点 settle` - 手动结算(可选,会自动结算) - `.赌场 21点 cancel` - 放弃游戏(返还所有下注) ### 玩家命令 -- `.赌场 21点 bet <金额>` - 下注 +- `.赌场 21点 join` - 加入游戏(扣除底注,需要至少底注的积分) +- `.赌场 21点 bet <金额>` - 加注(游戏中,不低于底注) - 示例:`.赌场 21点 bet 50` - `.赌场 21点 hit` - 要牌 -- `.赌场 21点 stand` - 停牌 +- `.赌场 21点 stand` - 停牌(所有玩家停牌后自动结算) ### 通用命令 - `.赌场 21点 status` - 查看当前状态 ### 游戏规则 -- 目标:手牌点数尽可能接近21点但不能超过 -- 黑杰克(A+10/J/Q/K):赢得1.5倍(默认) -- 玩家点数>庄家点数且未爆牌:赢得1:1 -- 平局:返还下注 -- 爆牌:失去下注 -- 庄家自动要牌到17点以上 -- 系统抽水5%""" +- **目标**:手牌点数尽可能接近21点但不能超过 +- **发牌**:标准发牌顺序,庄家隐藏一张暗牌 +- **黑杰克**(A+10/J/Q/K):赢得设定倍数(默认1.5倍) +- **玩家点数>庄家点数且未爆牌**:赢得1:1 +- **平局**:返还下注 +- **爆牌**:失去下注 +- **庄家规则**:自动要牌到17点以上 +- **自动结算**:所有玩家停牌后,最后一名玩家停牌时立即结算 +- **系统抽水**:5%"""