Update English learning app with DeepSeek API integration and new dialogue scenes
This commit is contained in:
commit
079c863152
1
english-learning-uv
Submodule
1
english-learning-uv
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit d8122fe3d40ffbd244d0beb091ba52b0261278d5
|
||||
85
index.html
Normal file
85
index.html
Normal file
@ -0,0 +1,85 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>斗地主游戏</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="game-container">
|
||||
<header class="game-header">
|
||||
<h1>斗地主游戏</h1>
|
||||
<div class="game-info">
|
||||
<span id="game-status">等待开始游戏</span>
|
||||
<span id="current-player">当前玩家:</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="game-table">
|
||||
<!-- 地主牌区域 -->
|
||||
<div class="landlord-cards">
|
||||
<div id="landlord-indicator">地主牌:</div>
|
||||
<div id="landlord-cards-area"></div>
|
||||
</div>
|
||||
|
||||
<!-- 电脑玩家1(上) -->
|
||||
<div class="player player-1">
|
||||
<div class="player-info">
|
||||
<span class="player-name">电脑玩家1</span>
|
||||
<span id="player-1-cards-count" class="cards-count">17</span>
|
||||
<span id="player-1-role" class="player-role"></span>
|
||||
</div>
|
||||
<div id="player-1-cards" class="player-cards"></div>
|
||||
<div id="player-1-last-play" class="last-play"></div>
|
||||
</div>
|
||||
|
||||
<!-- 电脑玩家2(右) -->
|
||||
<div class="player player-2">
|
||||
<div class="player-info">
|
||||
<span class="player-name">电脑玩家2</span>
|
||||
<span id="player-2-cards-count" class="cards-count">17</span>
|
||||
<span id="player-2-role" class="player-role"></span>
|
||||
</div>
|
||||
<div id="player-2-cards" class="player-cards"></div>
|
||||
<div id="player-2-last-play" class="last-play"></div>
|
||||
</div>
|
||||
|
||||
<!-- 当前出牌区域 -->
|
||||
<div class="current-play-area">
|
||||
<div id="current-play-info">当前出牌:</div>
|
||||
<div id="current-play-cards"></div>
|
||||
</div>
|
||||
|
||||
<!-- 玩家自己(下) -->
|
||||
<div class="player player-self">
|
||||
<div class="player-info">
|
||||
<span class="player-name">我</span>
|
||||
<span id="self-cards-count" class="cards-count">17</span>
|
||||
<span id="self-role" class="player-role"></span>
|
||||
</div>
|
||||
<div id="self-cards" class="player-cards"></div>
|
||||
<div id="self-last-play" class="last-play"></div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<div class="game-controls">
|
||||
<button id="start-game">开始游戏</button>
|
||||
<div class="landlord-buttons" style="display: none;">
|
||||
<button id="call-landlord">叫地主</button>
|
||||
<button id="no-call">不叫</button>
|
||||
</div>
|
||||
<div class="play-buttons" style="display: none;">
|
||||
<button id="play-cards">出牌</button>
|
||||
<button id="pass">不出</button>
|
||||
</div>
|
||||
<div class="game-stats">
|
||||
<span>积分:</span>
|
||||
<span id="score">0</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
601
script.js
Normal file
601
script.js
Normal file
@ -0,0 +1,601 @@
|
||||
// 卡片类
|
||||
class Card {
|
||||
constructor(suit, rank, value) {
|
||||
this.suit = suit; // 花色:spades, hearts, clubs, diamonds, joker
|
||||
this.rank = rank; // 点数:A, 2, 3, ..., 10, J, Q, K, 小王, 大王
|
||||
this.value = value; // 牌值:用于比较大小
|
||||
this.selected = false;
|
||||
}
|
||||
|
||||
// 获取卡片的HTML表示
|
||||
getHTML(selected = false) {
|
||||
const cardDiv = document.createElement('div');
|
||||
cardDiv.className = `card ${this.suit} ${selected ? 'selected' : ''}`;
|
||||
cardDiv.dataset.suit = this.suit;
|
||||
cardDiv.dataset.rank = this.rank;
|
||||
cardDiv.dataset.value = this.value;
|
||||
|
||||
// 花色符号
|
||||
const suitSymbol = {
|
||||
spades: '♠',
|
||||
hearts: '♥',
|
||||
clubs: '♣',
|
||||
diamonds: '♦'
|
||||
}[this.suit] || this.rank;
|
||||
|
||||
// 创建卡片内容
|
||||
const rankTop = document.createElement('div');
|
||||
rankTop.className = 'rank-top';
|
||||
rankTop.textContent = this.rank;
|
||||
|
||||
const suitMiddle = document.createElement('div');
|
||||
suitMiddle.className = 'suit-middle';
|
||||
suitMiddle.textContent = suitSymbol;
|
||||
|
||||
const rankBottom = document.createElement('div');
|
||||
rankBottom.className = 'rank-bottom';
|
||||
rankBottom.textContent = this.rank;
|
||||
|
||||
// 组装卡片
|
||||
cardDiv.appendChild(rankTop);
|
||||
cardDiv.appendChild(suitMiddle);
|
||||
cardDiv.appendChild(rankBottom);
|
||||
|
||||
return cardDiv;
|
||||
}
|
||||
|
||||
// 获取背面HTML
|
||||
static getBackHTML() {
|
||||
const cardDiv = document.createElement('div');
|
||||
cardDiv.className = 'card card-back';
|
||||
return cardDiv;
|
||||
}
|
||||
}
|
||||
|
||||
// 游戏类
|
||||
class LandlordGame {
|
||||
constructor() {
|
||||
this.players = [
|
||||
{ id: 0, name: '我', cards: [], role: '', lastPlay: [] },
|
||||
{ id: 1, name: '电脑玩家1', cards: [], role: '', lastPlay: [] },
|
||||
{ id: 2, name: '电脑玩家2', cards: [], role: '', lastPlay: [] }
|
||||
];
|
||||
this.deck = [];
|
||||
this.landlordCards = [];
|
||||
this.currentPlayer = 0;
|
||||
this.gameStatus = 'waiting'; // waiting, calling, playing, ended
|
||||
this.landlord = -1;
|
||||
this.lastPlayPlayer = -1;
|
||||
this.lastPlayCards = [];
|
||||
this.score = 0;
|
||||
this.callLandlordCount = 0;
|
||||
|
||||
this.initializeElements();
|
||||
this.initializeEventListeners();
|
||||
this.initializeDeck();
|
||||
}
|
||||
|
||||
// 初始化DOM元素
|
||||
initializeElements() {
|
||||
this.elements = {
|
||||
startGame: document.getElementById('start-game'),
|
||||
gameStatus: document.getElementById('game-status'),
|
||||
currentPlayer: document.getElementById('current-player'),
|
||||
landlordCardsArea: document.getElementById('landlord-cards-area'),
|
||||
landlordButtons: document.querySelector('.landlord-buttons'),
|
||||
callLandlord: document.getElementById('call-landlord'),
|
||||
noCall: document.getElementById('no-call'),
|
||||
playButtons: document.querySelector('.play-buttons'),
|
||||
playCards: document.getElementById('play-cards'),
|
||||
pass: document.getElementById('pass'),
|
||||
currentPlayCards: document.getElementById('current-play-cards'),
|
||||
currentPlayInfo: document.getElementById('current-play-info'),
|
||||
playerCards: [
|
||||
document.getElementById('self-cards'),
|
||||
document.getElementById('player-1-cards'),
|
||||
document.getElementById('player-2-cards')
|
||||
],
|
||||
playerCardsCount: [
|
||||
document.getElementById('self-cards-count'),
|
||||
document.getElementById('player-1-cards-count'),
|
||||
document.getElementById('player-2-cards-count')
|
||||
],
|
||||
playerRoles: [
|
||||
document.getElementById('self-role'),
|
||||
document.getElementById('player-1-role'),
|
||||
document.getElementById('player-2-role')
|
||||
],
|
||||
lastPlay: [
|
||||
document.getElementById('self-last-play'),
|
||||
document.getElementById('player-1-last-play'),
|
||||
document.getElementById('player-2-last-play')
|
||||
],
|
||||
score: document.getElementById('score')
|
||||
};
|
||||
}
|
||||
|
||||
// 初始化事件监听器
|
||||
initializeEventListeners() {
|
||||
this.elements.startGame.addEventListener('click', () => this.startGame());
|
||||
this.elements.callLandlord.addEventListener('click', () => this.callLandlord());
|
||||
this.elements.noCall.addEventListener('click', () => this.noCall());
|
||||
this.elements.playCards.addEventListener('click', () => this.playSelectedCards());
|
||||
this.elements.pass.addEventListener('click', () => this.pass());
|
||||
}
|
||||
|
||||
// 初始化牌组
|
||||
initializeDeck() {
|
||||
this.deck = [];
|
||||
const suits = ['spades', 'hearts', 'clubs', 'diamonds'];
|
||||
const ranks = ['3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A', '2'];
|
||||
const values = [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
|
||||
|
||||
// 添加普通牌
|
||||
for (let i = 0; i < ranks.length; i++) {
|
||||
for (const suit of suits) {
|
||||
this.deck.push(new Card(suit, ranks[i], values[i]));
|
||||
}
|
||||
}
|
||||
|
||||
// 添加大小王
|
||||
this.deck.push(new Card('joker', '小王', 16));
|
||||
this.deck.push(new Card('joker', '大王', 17));
|
||||
}
|
||||
|
||||
// 洗牌算法
|
||||
shuffleDeck() {
|
||||
for (let i = this.deck.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[this.deck[i], this.deck[j]] = [this.deck[j], this.deck[i]];
|
||||
}
|
||||
}
|
||||
|
||||
// 开始游戏
|
||||
startGame() {
|
||||
this.gameStatus = 'calling';
|
||||
this.currentPlayer = 0;
|
||||
this.landlord = -1;
|
||||
this.lastPlayPlayer = -1;
|
||||
this.lastPlayCards = [];
|
||||
this.callLandlordCount = 0;
|
||||
|
||||
// 重置玩家数据
|
||||
for (const player of this.players) {
|
||||
player.cards = [];
|
||||
player.role = '';
|
||||
player.lastPlay = [];
|
||||
}
|
||||
|
||||
// 洗牌发牌
|
||||
this.initializeDeck();
|
||||
this.shuffleDeck();
|
||||
this.dealCards();
|
||||
|
||||
// 更新UI
|
||||
this.updateGameStatus('开始叫地主');
|
||||
this.updateCurrentPlayer();
|
||||
this.renderAllCards();
|
||||
this.elements.startGame.style.display = 'none';
|
||||
this.elements.landlordButtons.style.display = 'flex';
|
||||
this.elements.playButtons.style.display = 'none';
|
||||
this.clearCurrentPlay();
|
||||
}
|
||||
|
||||
// 发牌
|
||||
dealCards() {
|
||||
// 发牌给三个玩家
|
||||
for (let i = 0; i < 17; i++) {
|
||||
this.players[0].cards.push(this.deck.pop());
|
||||
this.players[1].cards.push(this.deck.pop());
|
||||
this.players[2].cards.push(this.deck.pop());
|
||||
}
|
||||
|
||||
// 剩余的3张作为地主牌
|
||||
this.landlordCards = [...this.deck];
|
||||
this.deck = [];
|
||||
|
||||
// 排序玩家的牌
|
||||
for (const player of this.players) {
|
||||
this.sortCards(player.cards);
|
||||
}
|
||||
}
|
||||
|
||||
// 排序卡片
|
||||
sortCards(cards) {
|
||||
cards.sort((a, b) => a.value - b.value);
|
||||
}
|
||||
|
||||
// 叫地主
|
||||
callLandlord() {
|
||||
this.landlord = this.currentPlayer;
|
||||
this.players[this.currentPlayer].role = '地主';
|
||||
this.players[(this.currentPlayer + 1) % 3].role = '农民';
|
||||
this.players[(this.currentPlayer + 2) % 3].role = '农民';
|
||||
|
||||
// 将地主牌加入地主手中
|
||||
this.players[this.currentPlayer].cards.push(...this.landlordCards);
|
||||
this.sortCards(this.players[this.currentPlayer].cards);
|
||||
|
||||
// 开始游戏
|
||||
this.gameStatus = 'playing';
|
||||
this.updateGameStatus('游戏开始,地主出牌');
|
||||
this.updatePlayerRoles();
|
||||
this.renderAllCards();
|
||||
this.elements.landlordButtons.style.display = 'none';
|
||||
this.elements.playButtons.style.display = 'flex';
|
||||
|
||||
// 如果是电脑地主,自动出牌
|
||||
if (this.currentPlayer !== 0) {
|
||||
setTimeout(() => this.computerPlay(), 1000);
|
||||
}
|
||||
}
|
||||
|
||||
// 不叫地主
|
||||
noCall() {
|
||||
this.callLandlordCount++;
|
||||
this.currentPlayer = (this.currentPlayer + 1) % 3;
|
||||
this.updateCurrentPlayer();
|
||||
|
||||
// 如果三个玩家都不叫,重新开始
|
||||
if (this.callLandlordCount === 3) {
|
||||
this.updateGameStatus('无人叫地主,重新开始');
|
||||
setTimeout(() => this.startGame(), 1500);
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果是电脑玩家,自动选择
|
||||
if (this.currentPlayer !== 0) {
|
||||
setTimeout(() => this.computerCallLandlord(), 1000);
|
||||
}
|
||||
}
|
||||
|
||||
// 电脑叫地主
|
||||
computerCallLandlord() {
|
||||
// 简单AI:如果有王或多张大牌,叫地主
|
||||
const hasBigCards = this.players[this.currentPlayer].cards.some(card =>
|
||||
card.value >= 16 ||
|
||||
(card.value >= 14 && this.players[this.currentPlayer].cards.filter(c => c.value >= 14).length >= 3)
|
||||
);
|
||||
|
||||
if (hasBigCards) {
|
||||
this.callLandlord();
|
||||
} else {
|
||||
this.noCall();
|
||||
}
|
||||
}
|
||||
|
||||
// 选择卡片
|
||||
toggleCardSelection(cardElement) {
|
||||
if (this.gameStatus !== 'playing' || this.currentPlayer !== 0) return;
|
||||
|
||||
cardElement.classList.toggle('selected');
|
||||
const selected = cardElement.classList.contains('selected');
|
||||
const suit = cardElement.dataset.suit;
|
||||
const rank = cardElement.dataset.rank;
|
||||
const value = parseInt(cardElement.dataset.value);
|
||||
|
||||
// 更新卡片对象的选中状态
|
||||
const card = this.players[0].cards.find(c =>
|
||||
c.suit === suit && c.rank === rank && c.value === value
|
||||
);
|
||||
if (card) {
|
||||
card.selected = selected;
|
||||
}
|
||||
}
|
||||
|
||||
// 出牌
|
||||
playSelectedCards() {
|
||||
if (this.gameStatus !== 'playing' || this.currentPlayer !== 0) return;
|
||||
|
||||
// 获取选中的卡片
|
||||
const selectedCards = this.players[0].cards.filter(card => card.selected);
|
||||
if (selectedCards.length === 0) return;
|
||||
|
||||
// 验证出牌是否合法
|
||||
if (this.isValidPlay(selectedCards)) {
|
||||
// 更新游戏状态
|
||||
this.players[0].lastPlay = selectedCards;
|
||||
this.lastPlayPlayer = 0;
|
||||
this.lastPlayCards = selectedCards;
|
||||
|
||||
// 从手中移除出的牌
|
||||
this.players[0].cards = this.players[0].cards.filter(card => !card.selected);
|
||||
|
||||
// 检查是否获胜
|
||||
if (this.players[0].cards.length === 0) {
|
||||
this.endGame(0);
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新UI
|
||||
this.renderPlayerCards(0);
|
||||
this.updateCardsCount(0);
|
||||
this.updateCurrentPlay(0, selectedCards);
|
||||
this.updateSelfLastPlay(selectedCards);
|
||||
|
||||
// 切换到下一个玩家
|
||||
this.currentPlayer = 1;
|
||||
this.updateCurrentPlayer();
|
||||
|
||||
// 电脑玩家自动出牌
|
||||
setTimeout(() => this.computerPlay(), 1000);
|
||||
} else {
|
||||
alert('出牌不合法,请重新选择');
|
||||
}
|
||||
}
|
||||
|
||||
// 不出牌
|
||||
pass() {
|
||||
if (this.gameStatus !== 'playing' || this.currentPlayer !== 0) return;
|
||||
|
||||
// 更新游戏状态
|
||||
this.players[0].lastPlay = [];
|
||||
this.updateGameStatus('玩家不出牌');
|
||||
|
||||
// 切换到下一个玩家
|
||||
this.currentPlayer = 1;
|
||||
this.updateCurrentPlayer();
|
||||
|
||||
// 电脑玩家自动出牌
|
||||
setTimeout(() => this.computerPlay(), 1000);
|
||||
}
|
||||
|
||||
// 电脑出牌
|
||||
computerPlay() {
|
||||
const player = this.players[this.currentPlayer];
|
||||
const playableCards = this.findPlayableCards(player.cards);
|
||||
|
||||
if (playableCards.length > 0) {
|
||||
// 选择最合适的牌组
|
||||
const selectedCards = this.chooseBestCards(playableCards);
|
||||
|
||||
// 更新游戏状态
|
||||
player.lastPlay = selectedCards;
|
||||
this.lastPlayPlayer = this.currentPlayer;
|
||||
this.lastPlayCards = selectedCards;
|
||||
|
||||
// 从手中移除出的牌
|
||||
player.cards = player.cards.filter(card => !selectedCards.includes(card));
|
||||
|
||||
// 检查是否获胜
|
||||
if (player.cards.length === 0) {
|
||||
this.endGame(this.currentPlayer);
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新UI
|
||||
this.renderPlayerCards(this.currentPlayer);
|
||||
this.updateCardsCount(this.currentPlayer);
|
||||
this.updateCurrentPlay(this.currentPlayer, selectedCards);
|
||||
this.updateComputerLastPlay(this.currentPlayer, selectedCards);
|
||||
|
||||
this.updateGameStatus(`${player.name} 出牌`);
|
||||
} else {
|
||||
// 不出牌
|
||||
player.lastPlay = [];
|
||||
this.updateGameStatus(`${player.name} 不出牌`);
|
||||
}
|
||||
|
||||
// 切换到下一个玩家
|
||||
this.currentPlayer = (this.currentPlayer + 1) % 3;
|
||||
this.updateCurrentPlayer();
|
||||
|
||||
// 如果下一个是电脑,继续自动出牌
|
||||
if (this.currentPlayer !== 0) {
|
||||
setTimeout(() => this.computerPlay(), 1000);
|
||||
}
|
||||
}
|
||||
|
||||
// 查找可出的牌组
|
||||
findPlayableCards(cards) {
|
||||
const playable = [];
|
||||
|
||||
// 如果是第一个出牌,可以出任何牌组
|
||||
if (this.lastPlayPlayer === -1) {
|
||||
// 简单实现:找出所有可能的单牌、对子、顺子等
|
||||
// 这里简化处理,只考虑单牌
|
||||
for (const card of cards) {
|
||||
playable.push([card]);
|
||||
}
|
||||
} else {
|
||||
// 必须出比上一次大的同类型牌
|
||||
const lastType = this.getCardType(this.lastPlayCards);
|
||||
const lastMaxValue = Math.max(...this.lastPlayCards.map(c => c.value));
|
||||
|
||||
if (lastType === 'single') {
|
||||
// 找比lastMaxValue大的单牌
|
||||
const singles = cards.filter(card => card.value > lastMaxValue);
|
||||
for (const card of singles) {
|
||||
playable.push([card]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return playable;
|
||||
}
|
||||
|
||||
// 选择最好的牌组
|
||||
chooseBestCards(playableCards) {
|
||||
// 简单AI:选择最小的可出牌组
|
||||
if (playableCards.length === 0) return [];
|
||||
|
||||
// 由于只允许单牌,直接选择最小的可出牌
|
||||
return playableCards[0];
|
||||
}
|
||||
|
||||
// 验证出牌是否合法
|
||||
isValidPlay(cards) {
|
||||
if (cards.length === 0) return false;
|
||||
|
||||
// 简化版:只允许出单牌
|
||||
if (cards.length !== 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 如果是第一个出牌
|
||||
if (this.lastPlayPlayer === -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 检查牌型是否相同(都是单牌)
|
||||
const currentType = this.getCardType(cards);
|
||||
const lastType = this.getCardType(this.lastPlayCards);
|
||||
|
||||
if (currentType !== 'single' || lastType !== 'single') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查大小
|
||||
const currentMax = Math.max(...cards.map(c => c.value));
|
||||
const lastMax = Math.max(...this.lastPlayCards.map(c => c.value));
|
||||
|
||||
return currentMax > lastMax;
|
||||
}
|
||||
|
||||
// 获取牌型
|
||||
getCardType(cards) {
|
||||
if (cards.length === 0) return 'empty';
|
||||
if (cards.length === 1) return 'single';
|
||||
if (cards.length === 2) {
|
||||
if (cards[0].value === cards[1].value) return 'pair';
|
||||
if (cards[0].value === 16 && cards[1].value === 17) return 'rocket';
|
||||
return 'invalid';
|
||||
}
|
||||
// 这里可以扩展更多牌型判断
|
||||
return 'invalid';
|
||||
}
|
||||
|
||||
// 结束游戏
|
||||
endGame(winner) {
|
||||
this.gameStatus = 'ended';
|
||||
const winnerPlayer = this.players[winner];
|
||||
const isLandlordWin = winnerPlayer.role === '地主';
|
||||
|
||||
if (isLandlordWin) {
|
||||
this.score += 200;
|
||||
this.updateGameStatus(`地主 ${winnerPlayer.name} 获胜!`);
|
||||
} else {
|
||||
this.score -= 100;
|
||||
this.updateGameStatus(`农民 ${winnerPlayer.name} 获胜!`);
|
||||
}
|
||||
|
||||
// 更新UI
|
||||
this.elements.landlordButtons.style.display = 'none';
|
||||
this.elements.playButtons.style.display = 'none';
|
||||
this.elements.startGame.style.display = 'block';
|
||||
this.elements.startGame.textContent = '再来一局';
|
||||
this.updateScore();
|
||||
}
|
||||
|
||||
// 更新游戏状态
|
||||
updateGameStatus(status) {
|
||||
this.elements.gameStatus.textContent = status;
|
||||
}
|
||||
|
||||
// 更新当前玩家
|
||||
updateCurrentPlayer() {
|
||||
this.elements.currentPlayer.textContent = `当前玩家:${this.players[this.currentPlayer].name}`;
|
||||
}
|
||||
|
||||
// 更新玩家角色
|
||||
updatePlayerRoles() {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
this.elements.playerRoles[i].textContent = this.players[i].role;
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染所有卡片
|
||||
renderAllCards() {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
this.renderPlayerCards(i);
|
||||
this.updateCardsCount(i);
|
||||
}
|
||||
this.renderLandlordCards();
|
||||
}
|
||||
|
||||
// 渲染玩家卡片
|
||||
renderPlayerCards(playerIndex) {
|
||||
const container = this.elements.playerCards[playerIndex];
|
||||
container.innerHTML = '';
|
||||
|
||||
const player = this.players[playerIndex];
|
||||
|
||||
if (playerIndex === 0) {
|
||||
// 自己的卡片,可以选择
|
||||
for (const card of player.cards) {
|
||||
const cardElement = card.getHTML(card.selected);
|
||||
cardElement.addEventListener('click', () => this.toggleCardSelection(cardElement));
|
||||
container.appendChild(cardElement);
|
||||
}
|
||||
} else {
|
||||
// 电脑玩家的卡片,显示背面
|
||||
for (let i = 0; i < player.cards.length; i++) {
|
||||
const cardElement = Card.getBackHTML();
|
||||
container.appendChild(cardElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染地主牌
|
||||
renderLandlordCards() {
|
||||
this.elements.landlordCardsArea.innerHTML = '';
|
||||
for (const card of this.landlordCards) {
|
||||
const cardElement = card.getHTML();
|
||||
this.elements.landlordCardsArea.appendChild(cardElement);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新牌数
|
||||
updateCardsCount(playerIndex) {
|
||||
this.elements.playerCardsCount[playerIndex].textContent = this.players[playerIndex].cards.length;
|
||||
}
|
||||
|
||||
// 更新当前出牌
|
||||
updateCurrentPlay(playerIndex, cards) {
|
||||
this.elements.currentPlayInfo.textContent = `${this.players[playerIndex].name} 出牌:`;
|
||||
this.elements.currentPlayCards.innerHTML = '';
|
||||
|
||||
for (const card of cards) {
|
||||
const cardElement = card.getHTML();
|
||||
this.elements.currentPlayCards.appendChild(cardElement);
|
||||
}
|
||||
}
|
||||
|
||||
// 清空当前出牌
|
||||
clearCurrentPlay() {
|
||||
this.elements.currentPlayInfo.textContent = '当前出牌:';
|
||||
this.elements.currentPlayCards.innerHTML = '';
|
||||
}
|
||||
|
||||
// 更新自己的上次出牌
|
||||
updateSelfLastPlay(cards) {
|
||||
const container = this.elements.lastPlay[0];
|
||||
container.innerHTML = '';
|
||||
|
||||
for (const card of cards) {
|
||||
const cardElement = card.getHTML();
|
||||
container.appendChild(cardElement);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新电脑玩家的上次出牌
|
||||
updateComputerLastPlay(playerIndex, cards) {
|
||||
const container = this.elements.lastPlay[playerIndex];
|
||||
container.innerHTML = '';
|
||||
|
||||
for (const card of cards) {
|
||||
const cardElement = card.getHTML();
|
||||
container.appendChild(cardElement);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新分数
|
||||
updateScore() {
|
||||
this.elements.score.textContent = this.score;
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化游戏
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
window.game = new LandlordGame();
|
||||
});
|
||||
354
style.css
Normal file
354
style.css
Normal file
@ -0,0 +1,354 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Arial', sans-serif;
|
||||
background-color: #1a532a;
|
||||
color: white;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.game-container {
|
||||
width: 90vw;
|
||||
max-width: 1200px;
|
||||
min-height: 90vh;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
border-radius: 20px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.game-header {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.game-header h1 {
|
||||
font-size: 2.5em;
|
||||
color: #ffd700;
|
||||
margin-bottom: 10px;
|
||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.game-info {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.game-table {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
min-height: 500px;
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 15px;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
/* 地主牌区域 */
|
||||
.landlord-cards {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
#landlord-indicator {
|
||||
font-size: 1.2em;
|
||||
margin-bottom: 10px;
|
||||
color: #ffd700;
|
||||
}
|
||||
|
||||
#landlord-cards-area {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
/* 玩家区域 */
|
||||
.player {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.player-info {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.player-name {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.cards-count {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
padding: 5px 10px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.player-role {
|
||||
color: #ff6b6b;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.player-cards {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 5px;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.last-play {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 5px;
|
||||
margin-top: 10px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* 自己的玩家区域 */
|
||||
.player-self {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.player-self .player-cards {
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
/* 卡片样式 */
|
||||
.card {
|
||||
width: 80px;
|
||||
height: 120px;
|
||||
background-color: white;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
padding: 5px;
|
||||
color: black;
|
||||
font-weight: bold;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
position: relative;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.card.selected {
|
||||
border-color: #ffd700;
|
||||
background-color: #fff9c4;
|
||||
}
|
||||
|
||||
.card-back {
|
||||
background-color: #424242;
|
||||
background-image: radial-gradient(circle, #666 20%, transparent 20%),
|
||||
radial-gradient(circle, #666 20%, transparent 20%);
|
||||
background-size: 10px 10px;
|
||||
background-position: 0 0, 5px 5px;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.card .rank-top {
|
||||
font-size: 1.5em;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.card .suit-middle {
|
||||
font-size: 2em;
|
||||
align-self: center;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.card .rank-bottom {
|
||||
font-size: 1.5em;
|
||||
align-self: flex-end;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
/* 花色颜色 */
|
||||
.card.spades, .card.clubs {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.card.hearts, .card.diamonds {
|
||||
color: red;
|
||||
}
|
||||
|
||||
/* 大王小王 */
|
||||
.card.joker-big .suit-middle,
|
||||
.card.joker-small .suit-middle {
|
||||
color: black;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
/* 当前出牌区域 */
|
||||
.current-play-area {
|
||||
text-align: center;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
#current-play-info {
|
||||
font-size: 1.2em;
|
||||
margin-bottom: 10px;
|
||||
color: #ffd700;
|
||||
}
|
||||
|
||||
#current-play-cards {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
/* 游戏控制按钮 */
|
||||
.game-controls {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
margin-top: 20px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 12px 24px;
|
||||
font-size: 1.1em;
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s ease;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
background-color: #cccccc;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
/* 叫地主按钮区域 */
|
||||
.landlord-buttons {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
#call-landlord {
|
||||
background-color: #ff9800;
|
||||
}
|
||||
|
||||
#call-landlord:hover {
|
||||
background-color: #f57c00;
|
||||
}
|
||||
|
||||
#no-call {
|
||||
background-color: #f44336;
|
||||
}
|
||||
|
||||
#no-call:hover {
|
||||
background-color: #da190b;
|
||||
}
|
||||
|
||||
/* 出牌按钮区域 */
|
||||
.play-buttons {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
#play-cards {
|
||||
background-color: #2196F3;
|
||||
}
|
||||
|
||||
#play-cards:hover {
|
||||
background-color: #0b7dda;
|
||||
}
|
||||
|
||||
#pass {
|
||||
background-color: #9e9e9e;
|
||||
}
|
||||
|
||||
#pass:hover {
|
||||
background-color: #757575;
|
||||
}
|
||||
|
||||
/* 游戏统计 */
|
||||
.game-stats {
|
||||
margin-left: 30px;
|
||||
font-size: 1.2em;
|
||||
color: #ffd700;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.game-container {
|
||||
width: 98vw;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.game-header h1 {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.card {
|
||||
width: 60px;
|
||||
height: 90px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.player-info {
|
||||
font-size: 1em;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.game-controls {
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 10px 20px;
|
||||
font-size: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.card {
|
||||
width: 50px;
|
||||
height: 75px;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.game-info {
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.game-controls {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user