// ============================================================================
// Blackjack (UOX3 / SpiderMonkey 1.8.5 / ES5)
// ScriptID: 7511
// Command: blackjack
// Style: Same gump + onGumpPress pattern as the Yahtzee script
// Notes:
// - Per-player TempTags: bj_state, bj_chips, bj_bet, bj_deck, bj_p, bj_d, bj_msg, bj_canDouble
// - Payouts: Blackjack 3:2, regular win 1:1, push returns bet
// - No splits/insurance (easy to add later)
// ============================================================================
/* --------------------------- Tunables --------------------------- */
var BJ_GUMP_ID = 0xB7A1; // unique per script (like Yahtzee’s)
var BJ_STARTING_CHIPS = 1000;
var BJ_MIN_BET = 10;
var BJ_MAX_BET = 50000;
/* --------------------------- Command Registration --------------------------- */
function CommandRegistration()
{
// allow normal players to open
RegisterCommand("blackjack", 0, true);
}
function command_BLACKJACK(socket, cmdString)
{
var p = socket.currentChar; if (!ValidateObject(p)) return;
BJ_InitIfNeeded(p);
BJ_OpenGump(p, "Welcome! Set your bet, then Deal.");
}
/* --------------------------- onGumpPress (Yahtzee-style) --------------------------- */
// EXACT same signature shape we used for Yahtzee:
function onGumpPress(pSock, pButton, gumpData)
{
var p = pSock.currentChar; if (!ValidateObject(p)) return;
switch (pButton)
{
// bet adjusters
case 101: BJ_AdjustBet(p, -1000); break;
case 102: BJ_AdjustBet(p, -100); break;
case 103: BJ_AdjustBet(p, -10); break;
case 104: BJ_AdjustBet(p, 10); break;
case 105: BJ_AdjustBet(p, 100); break;
case 106: BJ_AdjustBet(p, 1000); break;
case 107: BJ_SetBet(p, BJ_MIN_BET); break;
case 108: BJ_MaxBet(p); break;
// flow
case 201: BJ_Deal(p); break;
case 202: BJ_Hit(p); break;
case 203: BJ_Stand(p); break;
case 204: BJ_Double(p); break;
case 205: BJ_NewHand(p); break;
case 0:
default: break; // closed or unknown
}
}
/* --------------------------- Core State --------------------------- */
function BJ_InitIfNeeded(p)
{
if ((p.GetTempTag("bj_inited") | 0) !== 1)
{
p.SetTempTag("bj_inited", 1);
p.SetTempTag("bj_chips", BJ_STARTING_CHIPS);
p.SetTempTag("bj_bet", BJ_MIN_BET);
p.SetTempTag("bj_state", "idle"); // idle | betting | inplay | result
p.SetTempTag("bj_p", "");
p.SetTempTag("bj_d", "");
p.SetTempTag("bj_deck", "");
p.SetTempTag("bj_canDouble", 0);
p.SetTempTag("bj_msg", "");
}
}
function BJ_NewRound(p)
{
BJ_SetDeck(p, BJ_ShuffledDeck());
BJ_SetHand(p, "bj_p", []);
BJ_SetHand(p, "bj_d", []);
p.SetTempTag("bj_state", "betting");
p.SetTempTag("bj_msg", "");
p.SetTempTag("bj_canDouble", 0);
}
function BJ_NewHand(p) { BJ_NewRound(p); BJ_OpenGump(p, "New hand. Adjust bet or Deal."); }
/* --------------------------- Deck/Hand Encoding --------------------------- */
function BJ_ShuffledDeck()
{
var deck = []; for (var i = 0; i < 52; i++) deck.push(i);
for (var j = deck.length - 1; j > 0; j--) { var k = RandomNumber(0, j); var t = deck[j]; deck[j] = deck[k]; deck[k] = t; }
return deck;
}
function BJ_GetDeck(p)
{
var raw = p.GetTempTag("bj_deck") || ""; if (!raw) return [];
var s = raw.split(","), out = [];
for (var i = 0; i < s.length; i++) { var v = parseInt(s[i], 10); if (!isNaN(v)) out.push(v); }
return out;
}
function BJ_SetDeck(p, arr) { p.SetTempTag("bj_deck", arr.join(",")); }
function BJ_Draw(p) { var d = BJ_GetDeck(p); if (d.length === 0) d = BJ_ShuffledDeck(); var c = d.pop(); BJ_SetDeck(p, d); return c; }
function BJ_GetHand(p, key)
{
var raw = p.GetTempTag(key) || ""; if (!raw) return [];
var s = raw.split(","), out = [];
for (var i = 0; i < s.length; i++) { var v = parseInt(s[i], 10); if (!isNaN(v)) out.push(v); }
return out;
}
function BJ_SetHand(p, key, arr) { p.SetTempTag(key, arr.join(",")); }
/* --------------------------- Math --------------------------- */
function BJ_Rank(card) { return card % 13; } // 0..12 (A=0, 10..12 = J/Q/K)
function BJ_Suit(card) { return (card / 13) | 0; } // 0..3
function BJ_Label(card)
{
var r = BJ_Rank(card), s = BJ_Suit(card);
var R = (r === 0 ? "A" : (r >= 10 ? ["J", "Q", "K"][r - 10] : "" + (r + 1)));
var S = ["♠", "♥", "♦", "♣"][s];
return R + S;
}
function BJ_HandInfo(cards)
{
var total = 0, aces = 0, i;
for (i = 0; i < cards.length; i++)
{
var r = BJ_Rank(cards[i]);
if (r === 0) { total += 11; aces++; }
else if (r >= 10) total += 10;
else total += (r + 1);
}
while (total > 21 && aces > 0) { total -= 10; aces--; }
var blackjack = (cards.length === 2 && total === 21);
var busted = (total > 21);
var soft = (aces > 0); // any Ace still counted as 11
return { total: total, blackjack: blackjack, busted: busted, soft: soft };
}
/* --------------------------- Chips & Bet --------------------------- */
function BJ_GetChips(p) { return parseInt(p.GetTempTag("bj_chips"), 10) || 0; }
function BJ_SetChips(p, n) { p.SetTempTag("bj_chips", (n | 0)); }
function BJ_GetBet(p)
{
var b = parseInt(p.GetTempTag("bj_bet"), 10) || BJ_MIN_BET;
if (b < BJ_MIN_BET) b = BJ_MIN_BET;
if (b > BJ_MAX_BET) b = BJ_MAX_BET;
var chips = BJ_GetChips(p);
if (b > chips) b = chips;
return b;
}
function BJ_SetBet(p, b)
{
var chips = BJ_GetChips(p);
if (b < BJ_MIN_BET) b = BJ_MIN_BET;
if (b > BJ_MAX_BET) b = BJ_MAX_BET;
if (b > chips) b = chips;
p.SetTempTag("bj_bet", (b | 0));
BJ_OpenGump(p, "");
}
function BJ_AdjustBet(p, delta) { BJ_SetBet(p, BJ_GetBet(p) + (delta | 0)); }
function BJ_MaxBet(p) { BJ_SetBet(p, Math.max(BJ_MIN_BET, Math.min(BJ_MAX_BET, BJ_GetChips(p)))); }
/* --------------------------- Round Flow --------------------------- */
function BJ_Deal(p)
{
var state = p.GetTempTag("bj_state") || "idle";
if (state !== "idle" && state !== "betting" && state !== "result") { BJ_OpenGump(p, ""); return; }
var chips = BJ_GetChips(p), bet = BJ_GetBet(p);
if (bet < BJ_MIN_BET) { BJ_OpenGump(p, "Bet below table minimum."); return; }
if (bet > chips) { BJ_OpenGump(p, "Not enough chips."); return; }
// lock bet by removing chips, save live bet
BJ_SetChips(p, chips - bet);
p.SetTempTag("bj_betLive", bet | 0);
// (re)shuffle if thin
if (BJ_GetDeck(p).length < 15) BJ_SetDeck(p, BJ_ShuffledDeck());
// clear hands
BJ_SetHand(p, "bj_p", []);
BJ_SetHand(p, "bj_d", []);
// initial deal P,D,P,D
var ph = BJ_GetHand(p, "bj_p"), dh = BJ_GetHand(p, "bj_d");
ph.push(BJ_Draw(p)); dh.push(BJ_Draw(p));
ph.push(BJ_Draw(p)); dh.push(BJ_Draw(p));
BJ_SetHand(p, "bj_p", ph); BJ_SetHand(p, "bj_d", dh);
p.SetTempTag("bj_state", "inplay");
p.SetTempTag("bj_canDouble", 1);
p.SetTempTag("bj_msg", "");
// naturals
var pi = BJ_HandInfo(ph), di = BJ_HandInfo(dh);
if (pi.blackjack || di.blackjack) { BJ_Finish(p, true); return; }
BJ_OpenGump(p, "");
}
function BJ_Hit(p)
{
if ((p.GetTempTag("bj_state") || "") !== "inplay") { BJ_OpenGump(p, ""); return; }
var ph = BJ_GetHand(p, "bj_p"); ph.push(BJ_Draw(p)); BJ_SetHand(p, "bj_p", ph);
p.SetTempTag("bj_canDouble", 0);
var pi = BJ_HandInfo(ph);
if (pi.busted) { BJ_Finish(p, false); return; }
BJ_OpenGump(p, "");
}
function BJ_Stand(p)
{
if ((p.GetTempTag("bj_state") || "") !== "inplay") { BJ_OpenGump(p, ""); return; }
BJ_DealerPlay(p);
BJ_Finish(p, false);
}
function BJ_Double(p)
{
if ((p.GetTempTag("bj_state") || "") !== "inplay") { BJ_OpenGump(p, ""); return; }
if ((p.GetTempTag("bj_canDouble") | 0) !== 1) { BJ_OpenGump(p, "You can’t double now."); return; }
var bet = parseInt(p.GetTempTag("bj_betLive"), 10) || 0;
var chips = BJ_GetChips(p);
if (chips < bet) { BJ_OpenGump(p, "Not enough chips to double."); return; }
// take extra bet; one draw; then stand
BJ_SetChips(p, chips - bet);
p.SetTempTag("bj_betLive", bet * 2);
p.SetTempTag("bj_canDouble", 0);
var ph = BJ_GetHand(p, "bj_p"); ph.push(BJ_Draw(p)); BJ_SetHand(p, "bj_p", ph);
var pi = BJ_HandInfo(ph);
if (!pi.busted) BJ_DealerPlay(p);
BJ_Finish(p, false);
}
function BJ_DealerPlay(p)
{
var dh = BJ_GetHand(p, "bj_d"), di = BJ_HandInfo(dh);
// dealer hits to 17; hit soft 17
while (di.total < 17 || (di.total === 17 && di.soft)) { dh.push(BJ_Draw(p)); di = BJ_HandInfo(dh); }
BJ_SetHand(p, "bj_d", dh);
}
/* --------------------------- Resolution --------------------------- */
function BJ_Finish(p, naturalCheck)
{
var ph = BJ_GetHand(p, "bj_p"), dh = BJ_GetHand(p, "bj_d");
var pi = BJ_HandInfo(ph), di = BJ_HandInfo(dh);
var betLive = parseInt(p.GetTempTag("bj_betLive"), 10) || 0;
var chips = BJ_GetChips(p);
var msg = "", payout = 0;
if (naturalCheck)
{
if (pi.blackjack && di.blackjack) { msg = "Push (both Blackjack)."; payout = betLive; }
else if (pi.blackjack) { msg = "Blackjack! Payout 3:2."; payout = (betLive * 5 / 2) | 0; }
else if (di.blackjack) { msg = "Dealer Blackjack. You lose."; payout = 0; }
else { BJ_OpenGump(p, ""); return; }
}
else
{
if (pi.busted) { msg = "You bust. Dealer wins."; payout = 0; }
else if (di.busted) { msg = "Dealer busts. You win!"; payout = betLive * 2; }
else if (pi.total > di.total) { msg = "You win!"; payout = betLive * 2; }
else if (pi.total < di.total) { msg = "Dealer wins."; payout = 0; }
else { msg = "Push."; payout = betLive; }
}
BJ_SetChips(p, chips + payout);
p.SetTempTag("bj_state", "result");
p.SetTempTag("bj_msg", msg + " (" + payout + " returned)");
p.SetTempTag("bj_canDouble", 0);
BJ_OpenGump(p, "");
}
/* --------------------------- Gump (Yahtzee-style build/send) --------------------------- */
function BJ_OpenGump(p, inlineMsg)
{
var sock = p.socket; if (!sock) return;
var g = new Gump();
g.gumpID = BJ_GUMP_ID; // gate clicks like in Yahtzee
g.AddPage(0);
g.AddBackground(0, 0, 420, 340, 5054);
// header
g.AddText(16, 14, 1152, "BLACKJACK");
var chips = BJ_GetChips(p), bet = BJ_GetBet(p);
g.AddText(16, 38, 0x34, "Chips: " + chips);
g.AddText(180, 38, 0x34, "Bet: " + bet);
// bet controls (IDs 101..108) – same consistent ID scheme as Yahtzee
g.AddButton(16, 62, 0x0FA5, 1, 0, 101); g.AddText(38, 64, 0x480, "-1000");
g.AddButton(86, 62, 0x0FA5, 1, 0, 102); g.AddText(108, 64, 0x480, "-100");
g.AddButton(156, 62, 0x0FA5, 1, 0, 103); g.AddText(178, 64, 0x480, "-10");
g.AddButton(226, 62, 0x0FA5, 1, 0, 104); g.AddText(248, 64, 0x480, "+10");
g.AddButton(286, 62, 0x0FA5, 1, 0, 105); g.AddText(308, 64, 0x480, "+100");
g.AddButton(346, 62, 0x0FA5, 1, 0, 106); g.AddText(368, 64, 0x480, "+1000");
g.AddButton(16, 86, 0x0FA5, 1, 0, 107); g.AddText(38, 88, 0x480, "Min");
g.AddButton(86, 86, 0x0FA5, 1, 0, 108); g.AddText(108, 88, 0x480, "Max");
// hands
var ph = BJ_GetHand(p, "bj_p"), dh = BJ_GetHand(p, "bj_d");
var pi = BJ_HandInfo(ph), di = BJ_HandInfo(dh);
var state = p.GetTempTag("bj_state") || "idle";
var hideHole = (state === "inplay"); // conceal dealer’s 2nd card while inplay
g.AddText(16, 120, 0x34, "Dealer:");
g.AddText(16, 140, 0x480, BJ_RenderCards(dh, hideHole));
if (!hideHole) g.AddText(16, 158, 0x59, "Total: " + di.total);
g.AddText(16, 188, 0x34, "You:");
g.AddText(16, 208, 0x480, BJ_RenderCards(ph, false));
g.AddText(16, 226, 0x59, "Total: " + pi.total);
// action buttons (like Yahtzee: grouped and gated by state)
if (state === "idle" || state === "betting" || state === "result")
{
g.AddButton(300, 250, 0x0FA5, 1, 0, 201); g.AddText(322, 252, 0x480, "Deal");
if (state === "result")
{
g.AddButton(300, 278, 0x0FA5, 1, 0, 205); g.AddText(322, 280, 0x480, "New");
}
}
else if (state === "inplay")
{
g.AddButton(300, 236, 0x0FA5, 1, 0, 202); g.AddText(322, 238, 0x480, "Hit");
g.AddButton(300, 264, 0x0FA5, 1, 0, 203); g.AddText(322, 266, 0x480, "Stand");
if ((p.GetTempTag("bj_canDouble") | 0) === 1)
{
g.AddButton(300, 292, 0x0FA5, 1, 0, 204); g.AddText(322, 294, 0x480, "Double");
}
}
// footer
var msg = inlineMsg || (p.GetTempTag("bj_msg") || "");
if (msg) g.AddText(16, 312, 0x25, msg);
// send exactly like Yahtzee
g.Send(sock);
g.Free();
}
function BJ_RenderCards(cards, hideHole)
{
if (!cards || cards.length === 0) return "(none)";
var out = [], i;
for (i = 0; i < cards.length; i++)
{
if (hideHole && i === 1) out.push("[??]");
else out.push("[" + BJ_Label(cards[i]) + "]");
}
return out.join(" ");
}
// Put this in the same blackjack.js if you want item double-click to work globally
function onUseChecked(pUser, iUsed)
{
if (!ValidateObject(pUser) || !ValidateObject(iUsed)) return false;
// Change to match your table’s sectionID if you only want specific items:
// if (iUsed.sectionID !== "blackjacktable") return false;
BJ_InitIfNeeded(pUser);
BJ_OpenGump(pUser, "");
return true;
}
// Blackjack (UOX3 / SpiderMonkey 1.8.5 / ES5)
// ScriptID: 7511
// Command: blackjack
// Style: Same gump + onGumpPress pattern as the Yahtzee script
// Notes:
// - Per-player TempTags: bj_state, bj_chips, bj_bet, bj_deck, bj_p, bj_d, bj_msg, bj_canDouble
// - Payouts: Blackjack 3:2, regular win 1:1, push returns bet
// - No splits/insurance (easy to add later)
// ============================================================================
/* --------------------------- Tunables --------------------------- */
var BJ_GUMP_ID = 0xB7A1; // unique per script (like Yahtzee’s)
var BJ_STARTING_CHIPS = 1000;
var BJ_MIN_BET = 10;
var BJ_MAX_BET = 50000;
/* --------------------------- Command Registration --------------------------- */
function CommandRegistration()
{
// allow normal players to open
RegisterCommand("blackjack", 0, true);
}
function command_BLACKJACK(socket, cmdString)
{
var p = socket.currentChar; if (!ValidateObject(p)) return;
BJ_InitIfNeeded(p);
BJ_OpenGump(p, "Welcome! Set your bet, then Deal.");
}
/* --------------------------- onGumpPress (Yahtzee-style) --------------------------- */
// EXACT same signature shape we used for Yahtzee:
function onGumpPress(pSock, pButton, gumpData)
{
var p = pSock.currentChar; if (!ValidateObject(p)) return;
switch (pButton)
{
// bet adjusters
case 101: BJ_AdjustBet(p, -1000); break;
case 102: BJ_AdjustBet(p, -100); break;
case 103: BJ_AdjustBet(p, -10); break;
case 104: BJ_AdjustBet(p, 10); break;
case 105: BJ_AdjustBet(p, 100); break;
case 106: BJ_AdjustBet(p, 1000); break;
case 107: BJ_SetBet(p, BJ_MIN_BET); break;
case 108: BJ_MaxBet(p); break;
// flow
case 201: BJ_Deal(p); break;
case 202: BJ_Hit(p); break;
case 203: BJ_Stand(p); break;
case 204: BJ_Double(p); break;
case 205: BJ_NewHand(p); break;
case 0:
default: break; // closed or unknown
}
}
/* --------------------------- Core State --------------------------- */
function BJ_InitIfNeeded(p)
{
if ((p.GetTempTag("bj_inited") | 0) !== 1)
{
p.SetTempTag("bj_inited", 1);
p.SetTempTag("bj_chips", BJ_STARTING_CHIPS);
p.SetTempTag("bj_bet", BJ_MIN_BET);
p.SetTempTag("bj_state", "idle"); // idle | betting | inplay | result
p.SetTempTag("bj_p", "");
p.SetTempTag("bj_d", "");
p.SetTempTag("bj_deck", "");
p.SetTempTag("bj_canDouble", 0);
p.SetTempTag("bj_msg", "");
}
}
function BJ_NewRound(p)
{
BJ_SetDeck(p, BJ_ShuffledDeck());
BJ_SetHand(p, "bj_p", []);
BJ_SetHand(p, "bj_d", []);
p.SetTempTag("bj_state", "betting");
p.SetTempTag("bj_msg", "");
p.SetTempTag("bj_canDouble", 0);
}
function BJ_NewHand(p) { BJ_NewRound(p); BJ_OpenGump(p, "New hand. Adjust bet or Deal."); }
/* --------------------------- Deck/Hand Encoding --------------------------- */
function BJ_ShuffledDeck()
{
var deck = []; for (var i = 0; i < 52; i++) deck.push(i);
for (var j = deck.length - 1; j > 0; j--) { var k = RandomNumber(0, j); var t = deck[j]; deck[j] = deck[k]; deck[k] = t; }
return deck;
}
function BJ_GetDeck(p)
{
var raw = p.GetTempTag("bj_deck") || ""; if (!raw) return [];
var s = raw.split(","), out = [];
for (var i = 0; i < s.length; i++) { var v = parseInt(s[i], 10); if (!isNaN(v)) out.push(v); }
return out;
}
function BJ_SetDeck(p, arr) { p.SetTempTag("bj_deck", arr.join(",")); }
function BJ_Draw(p) { var d = BJ_GetDeck(p); if (d.length === 0) d = BJ_ShuffledDeck(); var c = d.pop(); BJ_SetDeck(p, d); return c; }
function BJ_GetHand(p, key)
{
var raw = p.GetTempTag(key) || ""; if (!raw) return [];
var s = raw.split(","), out = [];
for (var i = 0; i < s.length; i++) { var v = parseInt(s[i], 10); if (!isNaN(v)) out.push(v); }
return out;
}
function BJ_SetHand(p, key, arr) { p.SetTempTag(key, arr.join(",")); }
/* --------------------------- Math --------------------------- */
function BJ_Rank(card) { return card % 13; } // 0..12 (A=0, 10..12 = J/Q/K)
function BJ_Suit(card) { return (card / 13) | 0; } // 0..3
function BJ_Label(card)
{
var r = BJ_Rank(card), s = BJ_Suit(card);
var R = (r === 0 ? "A" : (r >= 10 ? ["J", "Q", "K"][r - 10] : "" + (r + 1)));
var S = ["♠", "♥", "♦", "♣"][s];
return R + S;
}
function BJ_HandInfo(cards)
{
var total = 0, aces = 0, i;
for (i = 0; i < cards.length; i++)
{
var r = BJ_Rank(cards[i]);
if (r === 0) { total += 11; aces++; }
else if (r >= 10) total += 10;
else total += (r + 1);
}
while (total > 21 && aces > 0) { total -= 10; aces--; }
var blackjack = (cards.length === 2 && total === 21);
var busted = (total > 21);
var soft = (aces > 0); // any Ace still counted as 11
return { total: total, blackjack: blackjack, busted: busted, soft: soft };
}
/* --------------------------- Chips & Bet --------------------------- */
function BJ_GetChips(p) { return parseInt(p.GetTempTag("bj_chips"), 10) || 0; }
function BJ_SetChips(p, n) { p.SetTempTag("bj_chips", (n | 0)); }
function BJ_GetBet(p)
{
var b = parseInt(p.GetTempTag("bj_bet"), 10) || BJ_MIN_BET;
if (b < BJ_MIN_BET) b = BJ_MIN_BET;
if (b > BJ_MAX_BET) b = BJ_MAX_BET;
var chips = BJ_GetChips(p);
if (b > chips) b = chips;
return b;
}
function BJ_SetBet(p, b)
{
var chips = BJ_GetChips(p);
if (b < BJ_MIN_BET) b = BJ_MIN_BET;
if (b > BJ_MAX_BET) b = BJ_MAX_BET;
if (b > chips) b = chips;
p.SetTempTag("bj_bet", (b | 0));
BJ_OpenGump(p, "");
}
function BJ_AdjustBet(p, delta) { BJ_SetBet(p, BJ_GetBet(p) + (delta | 0)); }
function BJ_MaxBet(p) { BJ_SetBet(p, Math.max(BJ_MIN_BET, Math.min(BJ_MAX_BET, BJ_GetChips(p)))); }
/* --------------------------- Round Flow --------------------------- */
function BJ_Deal(p)
{
var state = p.GetTempTag("bj_state") || "idle";
if (state !== "idle" && state !== "betting" && state !== "result") { BJ_OpenGump(p, ""); return; }
var chips = BJ_GetChips(p), bet = BJ_GetBet(p);
if (bet < BJ_MIN_BET) { BJ_OpenGump(p, "Bet below table minimum."); return; }
if (bet > chips) { BJ_OpenGump(p, "Not enough chips."); return; }
// lock bet by removing chips, save live bet
BJ_SetChips(p, chips - bet);
p.SetTempTag("bj_betLive", bet | 0);
// (re)shuffle if thin
if (BJ_GetDeck(p).length < 15) BJ_SetDeck(p, BJ_ShuffledDeck());
// clear hands
BJ_SetHand(p, "bj_p", []);
BJ_SetHand(p, "bj_d", []);
// initial deal P,D,P,D
var ph = BJ_GetHand(p, "bj_p"), dh = BJ_GetHand(p, "bj_d");
ph.push(BJ_Draw(p)); dh.push(BJ_Draw(p));
ph.push(BJ_Draw(p)); dh.push(BJ_Draw(p));
BJ_SetHand(p, "bj_p", ph); BJ_SetHand(p, "bj_d", dh);
p.SetTempTag("bj_state", "inplay");
p.SetTempTag("bj_canDouble", 1);
p.SetTempTag("bj_msg", "");
// naturals
var pi = BJ_HandInfo(ph), di = BJ_HandInfo(dh);
if (pi.blackjack || di.blackjack) { BJ_Finish(p, true); return; }
BJ_OpenGump(p, "");
}
function BJ_Hit(p)
{
if ((p.GetTempTag("bj_state") || "") !== "inplay") { BJ_OpenGump(p, ""); return; }
var ph = BJ_GetHand(p, "bj_p"); ph.push(BJ_Draw(p)); BJ_SetHand(p, "bj_p", ph);
p.SetTempTag("bj_canDouble", 0);
var pi = BJ_HandInfo(ph);
if (pi.busted) { BJ_Finish(p, false); return; }
BJ_OpenGump(p, "");
}
function BJ_Stand(p)
{
if ((p.GetTempTag("bj_state") || "") !== "inplay") { BJ_OpenGump(p, ""); return; }
BJ_DealerPlay(p);
BJ_Finish(p, false);
}
function BJ_Double(p)
{
if ((p.GetTempTag("bj_state") || "") !== "inplay") { BJ_OpenGump(p, ""); return; }
if ((p.GetTempTag("bj_canDouble") | 0) !== 1) { BJ_OpenGump(p, "You can’t double now."); return; }
var bet = parseInt(p.GetTempTag("bj_betLive"), 10) || 0;
var chips = BJ_GetChips(p);
if (chips < bet) { BJ_OpenGump(p, "Not enough chips to double."); return; }
// take extra bet; one draw; then stand
BJ_SetChips(p, chips - bet);
p.SetTempTag("bj_betLive", bet * 2);
p.SetTempTag("bj_canDouble", 0);
var ph = BJ_GetHand(p, "bj_p"); ph.push(BJ_Draw(p)); BJ_SetHand(p, "bj_p", ph);
var pi = BJ_HandInfo(ph);
if (!pi.busted) BJ_DealerPlay(p);
BJ_Finish(p, false);
}
function BJ_DealerPlay(p)
{
var dh = BJ_GetHand(p, "bj_d"), di = BJ_HandInfo(dh);
// dealer hits to 17; hit soft 17
while (di.total < 17 || (di.total === 17 && di.soft)) { dh.push(BJ_Draw(p)); di = BJ_HandInfo(dh); }
BJ_SetHand(p, "bj_d", dh);
}
/* --------------------------- Resolution --------------------------- */
function BJ_Finish(p, naturalCheck)
{
var ph = BJ_GetHand(p, "bj_p"), dh = BJ_GetHand(p, "bj_d");
var pi = BJ_HandInfo(ph), di = BJ_HandInfo(dh);
var betLive = parseInt(p.GetTempTag("bj_betLive"), 10) || 0;
var chips = BJ_GetChips(p);
var msg = "", payout = 0;
if (naturalCheck)
{
if (pi.blackjack && di.blackjack) { msg = "Push (both Blackjack)."; payout = betLive; }
else if (pi.blackjack) { msg = "Blackjack! Payout 3:2."; payout = (betLive * 5 / 2) | 0; }
else if (di.blackjack) { msg = "Dealer Blackjack. You lose."; payout = 0; }
else { BJ_OpenGump(p, ""); return; }
}
else
{
if (pi.busted) { msg = "You bust. Dealer wins."; payout = 0; }
else if (di.busted) { msg = "Dealer busts. You win!"; payout = betLive * 2; }
else if (pi.total > di.total) { msg = "You win!"; payout = betLive * 2; }
else if (pi.total < di.total) { msg = "Dealer wins."; payout = 0; }
else { msg = "Push."; payout = betLive; }
}
BJ_SetChips(p, chips + payout);
p.SetTempTag("bj_state", "result");
p.SetTempTag("bj_msg", msg + " (" + payout + " returned)");
p.SetTempTag("bj_canDouble", 0);
BJ_OpenGump(p, "");
}
/* --------------------------- Gump (Yahtzee-style build/send) --------------------------- */
function BJ_OpenGump(p, inlineMsg)
{
var sock = p.socket; if (!sock) return;
var g = new Gump();
g.gumpID = BJ_GUMP_ID; // gate clicks like in Yahtzee
g.AddPage(0);
g.AddBackground(0, 0, 420, 340, 5054);
// header
g.AddText(16, 14, 1152, "BLACKJACK");
var chips = BJ_GetChips(p), bet = BJ_GetBet(p);
g.AddText(16, 38, 0x34, "Chips: " + chips);
g.AddText(180, 38, 0x34, "Bet: " + bet);
// bet controls (IDs 101..108) – same consistent ID scheme as Yahtzee
g.AddButton(16, 62, 0x0FA5, 1, 0, 101); g.AddText(38, 64, 0x480, "-1000");
g.AddButton(86, 62, 0x0FA5, 1, 0, 102); g.AddText(108, 64, 0x480, "-100");
g.AddButton(156, 62, 0x0FA5, 1, 0, 103); g.AddText(178, 64, 0x480, "-10");
g.AddButton(226, 62, 0x0FA5, 1, 0, 104); g.AddText(248, 64, 0x480, "+10");
g.AddButton(286, 62, 0x0FA5, 1, 0, 105); g.AddText(308, 64, 0x480, "+100");
g.AddButton(346, 62, 0x0FA5, 1, 0, 106); g.AddText(368, 64, 0x480, "+1000");
g.AddButton(16, 86, 0x0FA5, 1, 0, 107); g.AddText(38, 88, 0x480, "Min");
g.AddButton(86, 86, 0x0FA5, 1, 0, 108); g.AddText(108, 88, 0x480, "Max");
// hands
var ph = BJ_GetHand(p, "bj_p"), dh = BJ_GetHand(p, "bj_d");
var pi = BJ_HandInfo(ph), di = BJ_HandInfo(dh);
var state = p.GetTempTag("bj_state") || "idle";
var hideHole = (state === "inplay"); // conceal dealer’s 2nd card while inplay
g.AddText(16, 120, 0x34, "Dealer:");
g.AddText(16, 140, 0x480, BJ_RenderCards(dh, hideHole));
if (!hideHole) g.AddText(16, 158, 0x59, "Total: " + di.total);
g.AddText(16, 188, 0x34, "You:");
g.AddText(16, 208, 0x480, BJ_RenderCards(ph, false));
g.AddText(16, 226, 0x59, "Total: " + pi.total);
// action buttons (like Yahtzee: grouped and gated by state)
if (state === "idle" || state === "betting" || state === "result")
{
g.AddButton(300, 250, 0x0FA5, 1, 0, 201); g.AddText(322, 252, 0x480, "Deal");
if (state === "result")
{
g.AddButton(300, 278, 0x0FA5, 1, 0, 205); g.AddText(322, 280, 0x480, "New");
}
}
else if (state === "inplay")
{
g.AddButton(300, 236, 0x0FA5, 1, 0, 202); g.AddText(322, 238, 0x480, "Hit");
g.AddButton(300, 264, 0x0FA5, 1, 0, 203); g.AddText(322, 266, 0x480, "Stand");
if ((p.GetTempTag("bj_canDouble") | 0) === 1)
{
g.AddButton(300, 292, 0x0FA5, 1, 0, 204); g.AddText(322, 294, 0x480, "Double");
}
}
// footer
var msg = inlineMsg || (p.GetTempTag("bj_msg") || "");
if (msg) g.AddText(16, 312, 0x25, msg);
// send exactly like Yahtzee
g.Send(sock);
g.Free();
}
function BJ_RenderCards(cards, hideHole)
{
if (!cards || cards.length === 0) return "(none)";
var out = [], i;
for (i = 0; i < cards.length; i++)
{
if (hideHole && i === 1) out.push("[??]");
else out.push("[" + BJ_Label(cards[i]) + "]");
}
return out.join(" ");
}
// Put this in the same blackjack.js if you want item double-click to work globally
function onUseChecked(pUser, iUsed)
{
if (!ValidateObject(pUser) || !ValidateObject(iUsed)) return false;
// Change to match your table’s sectionID if you only want specific items:
// if (iUsed.sectionID !== "blackjacktable") return false;
BJ_InitIfNeeded(pUser);
BJ_OpenGump(pUser, "");
return true;
}