Files
2026-06-03 17:04:06 +08:00

233 lines
7.0 KiB
GDScript

extends NpcScript
# Behaviour
const ThresholdHandValue : int = 17
# Card utilities
static var RANKS : PackedStringArray = ["王牌", "2", "3", "4", "5", "6", "7", "8", "9", "10", "侍从", "王后", "国王"]
static var SUITS : PackedStringArray = [
"黑桃",
"红心",
"方片",
"梅花"
]
# Game state
var deck : Array = []
var playerHand : Array = []
var dealerHand : Array = []
# Helper
static func _CardName(card : int) -> String:
return "%s%s" % [SUITS[floori(card / 13.0)], RANKS[card % 13]]
static func _CardValue(card : int) -> int:
var rank : int = card % 13
if rank == 0: return 11
if rank >= 10: return 10
return rank + 1
static func _HandValue(hand : Array) -> int:
var value : int = 0
var aces : int = 0
for card : int in hand:
value += _CardValue(card)
if card % 13 == 0:
aces += 1
while value > 21 and aces > 0:
value -= 10
aces -= 1
return value
static func _HandStr(hand : Array) -> String:
var parts : PackedStringArray = []
for card : int in hand:
parts.append(_CardName(card))
return ", ".join(parts) + " (%d)" % _HandValue(hand)
static func _NewDeck() -> Array:
var d : Array = []
d.resize(52)
for i : int in range(52):
d[i] = i
d.shuffle()
return d
# General flow
func OnStart():
Mes("欢迎,欢迎!你是来找人,还是来找一局好牌?")
Mes("听过 21 点 吗?别被名字吓到,规矩简单,胆量和分寸才是关键。")
Mes("坐下吧。图利姆沙 的风会吹乱很多东西,但吹不乱一副洗好的牌。")
DisplayChoices()
func DisplayChoices():
Choice("挑战 海兰德", StartDealer)
Choice("帮我找个对手", StartPvP)
Choice("讲讲规则", ShowRules)
Choice("今天先不玩", Decline)
func ShowRules():
Mes("规则很简单:尽量接近 21 点,但不能超过。")
Mes("数字牌按牌面算,花牌都算 10。王牌可以算 1,也可以算 11,看哪种对你更有利。")
Mes("比我点数高且不爆牌,你就赢。超过 21,就是爆牌。")
Mes("这游戏最难的不是加法,是知道什么时候该停手。许多商人一辈子都没学会。")
DisplayChoices()
func Decline():
Chat("想玩牌的时候来找我。好牌不会等人太久。")
func OnQuit():
if npc and npc.ownScript:
npc.ownScript.call("LeavePvP", own)
super.OnQuit()
# Player vs Dealer Mode
func StartDealer():
deck = _NewDeck()
playerHand = [deck.pop_back(), deck.pop_back()]
dealerHand = [deck.pop_back(), deck.pop_back()]
_DealerTurn()
func _DealerTurn():
if _HandValue(playerHand) == 21:
_DealerReveal()
else:
Mes("你的手牌:%s" % _HandStr(playerHand))
Mes("我明面上的牌:%s" % _CardName(dealerHand[0]))
Choice("要牌", _DealerHit)
Choice("停牌", _DealerReveal)
func _DealerHit():
playerHand.append(deck.pop_back())
var value : int = _HandValue(playerHand)
if value > 21:
Mes("你的手牌:%s" % _HandStr(playerHand))
Mes("哎呀,爆牌了!贪心常常只差一张牌,我见过太多次。")
Choice("再来一局", StartDealer)
Choice("离开", Decline)
elif value == 21:
_DealerReveal()
else:
_DealerTurn()
func _DealerReveal():
while _HandValue(dealerHand) < ThresholdHandValue:
dealerHand.append(deck.pop_back())
var pv : int = _HandValue(playerHand)
var dv : int = _HandValue(dealerHand)
Mes("你的手牌:%s" % _HandStr(playerHand))
Mes("我的手牌:%s" % _HandStr(dealerHand))
if dv > 21:
Mes("哈!老手也有伸手太长的时候。你赢得漂亮。")
elif pv > dv:
Mes("嗯,不错。你知道什么时候该停。")
elif dv > pv:
Mes("经验又赢了一次。别难过,多数人第一次都输给我。")
else:
Mes("%d 点平局!胆子不错,我承认这一点。" % pv)
Choice("再来一局", StartDealer)
Choice("离开", Decline)
# Player vs Player Mode
func StartPvP():
var result : int = npc.ownScript.call("JoinPvP", own)
if result == 0:
Mes("好,让我们看看还有谁有胆量坐上这张桌子。稍等。")
Choice("查看", _PvPCheck)
Choice("取消", _PvPCancel)
else:
_PvPBegin()
func _PvPCheck():
var game : Dictionary = npc.ownScript.call("GetGame", own)
if game.is_empty():
Mes("耐心点。好牌手要先学会等待。")
Choice("查看", _PvPCheck)
Choice("取消", _PvPCancel)
elif game.has("result"):
_PvPShowResult(game)
else:
_PvPBegin()
func _PvPCancel():
npc.ownScript.call("LeavePvP", own)
Chat("没有牌局?可惜。也许风向一变,你又想玩了。")
func _PvPBegin():
playerHand = npc.ownScript.call("GetHand", own)
Mes("哈!对手来了。让我看看你们谁更懂停手。")
_PvPTurn()
func _PvPTurn():
Mes("你的手牌:%s" % _HandStr(playerHand))
if _HandValue(playerHand) == 21:
Mes("21 点!")
_PvPStand()
else:
Choice("要牌", _PvPHit)
Choice("停牌", _PvPStand)
func _PvPHit():
var card : int = npc.ownScript.call("DrawCard", own)
if card < 0:
_PvPStand()
return
playerHand.append(card)
var value : int = _HandValue(playerHand)
if value > 21:
Mes("你的手牌:%s" % _HandStr(playerHand))
Mes("爆牌!刚才心里那点犹豫,其实已经给过你提醒了。")
npc.ownScript.call("FinishHand", own, value, true)
_PvPWait()
elif value == 21:
Mes("你的手牌:%s" % _HandStr(playerHand))
_PvPStand()
else:
_PvPTurn()
func _PvPStand():
npc.ownScript.call("FinishHand", own, _HandValue(playerHand), false)
_PvPWait()
func _PvPWait():
var game : Dictionary = npc.ownScript.call("GetGame", own)
if game.has("result"):
_PvPShowResult(game)
else:
Mes("你这边结束了。现在等对手做决定。")
Choice("查看", _PvPWaitCheck)
Choice("认输", _PvPForfeit)
func _PvPWaitCheck():
var game : Dictionary = npc.ownScript.call("GetGame", own)
if game.has("result"):
_PvPShowResult(game)
else:
Mes("还在思考。谨慎的人值得尊重,除非他只是忘了轮到自己。")
Choice("查看", _PvPWaitCheck)
Choice("认输", _PvPForfeit)
func _PvPShowResult(game : Dictionary):
var isP1 : bool = game.get("p1") == own
var myVal : int = game.get("p1Value") if isP1 else game.get("p2Value")
var oppBust : bool = game.get("p2Busted") if isP1 else game.get("p1Busted")
var oppHand : Array = game.get("p2Hand") if isP1 else game.get("p1Hand")
var myBust : bool = game.get("p1Busted") if isP1 else game.get("p2Busted")
Mes("你的点数:%d%s" % [myVal, "(爆牌)" if myBust else ""])
Mes("对手手牌:%s%s" % [_HandStr(oppHand), "(爆牌)" if oppBust else ""])
var result : String = game.get("result", "")
if result == "draw":
Mes("平局!势均力敌,这在我的桌上不常见。")
elif (result == "p1" and isP1) or (result == "p2" and not isP1):
Mes("胜利归你!今天牌也站在你这边。")
else:
Mes("可惜。牌有自己的脾气,但它总会再绕回来。")
npc.ownScript.call("CleanupGame", own)
Choice("再来一局", StartPvP)
Choice("离开", Decline)
func _PvPForfeit():
npc.ownScript.call("ForfeitPvP", own)
Mes("中途离桌?放在我年轻时,这至少要请全桌喝一轮。")
Choice("再来一局", StartPvP)
Choice("离开", Decline)