今日目標,結算結果。
這邊就只差最後一步了~~
getAllPlayersHands()
:
public HashMap<String, Object> getAllPlayersHands(String roomId) {
HashMap<String, Object> result = new HashMap<>();
Player[] players = this.game.get(roomId).getPlayers();
for (Player player : players) {
result.put(player.getName(), player.getHands());
}
return result;
}
play()
,定義結束遊戲的訊息,函數修改後的完整內容為:
public void play(String roomId) {
Player[] players = gameStatus.getPlayers(roomId);
int index = 0;
for (int i=0; i<4; i++) {
if (players[i].getName().equals(this.gameStatus.getCurrentPlayer(roomId))) {
index = i;
break;
}
}
String currentPlayer;
String previousPlayer;
GameTimer timer = new GameTimer();
this.gameTimerList.add(roomId, timer);
try {
Thread.sleep(5000);
}
catch(Exception e) {
System.out.println(e.getMessage());
}
while (true) {
previousPlayer = this.gameStatus.getPreviousPlayer(roomId);
currentPlayer = players[index].getName();
this.gameStatus.setCurrentPlayer(roomId, currentPlayer);
if (currentPlayer.equals(previousPlayer)) {
this.gameStatus.setPreviousPlayer(roomId, null);
this.gameStatus.setPreviousPlayedCards(roomId, null);
}
timer.init(8);
String finalCurrentPlayer = currentPlayer;
String finalPreviousPlayer = previousPlayer;
timer.countDown((n) -> {
Map<String, Object> status = new HashMap<>();
status.put("currentPlayer", finalCurrentPlayer);
PlayedCards tmp = this.gameStatus.getPreviousPlayedCards(roomId);
if (tmp == null) {
status.put("previousPlayedCards", null);
}
else {
status.put("previousPlayedCards", tmp.get());
}
status.put("timer", n);
roomService.sendMessageToRoom(roomId, "/queue/game", status);
if (n.equals("1")) {
boolean isFirstPlayer = this.findFirstPlayer(roomId) != null;
if (isFirstPlayer) {
ArrayList<Card> cards = new ArrayList<>();
cards.add(new Card(Suit.CLUB, Number.THREE));
this.autoPlay(roomId, finalCurrentPlayer, cards);
sendMyHands(finalCurrentPlayer);
sendHandsInfo(roomId);
return;
}
if (finalCurrentPlayer.equals(finalPreviousPlayer)) {
this.autoPlay(roomId, finalCurrentPlayer);
sendMyHands(finalCurrentPlayer);
sendHandsInfo(roomId);
}
}
});
timer.await(25);
if (gameStatus.getHandsByPlayerName(currentPlayer).size() == 0) {
Map<String, Object> endMessage = new HashMap<>();
endMessage.put("winner", currentPlayer);
endMessage.put("hands", this.gameStatus.getAllPlayersHands(roomId));
roomService.sendMessageToRoom(roomId, "/queue/end", endMessage);
this.gameStatus.remove(roomId);
this.gameTimerList.remove(roomId);
for (Player player : players) {
this.userStatus.setUserReady(player.getName(), false);
}
return;
}
index = (index + 1) % 4;
}
}
game.css
,加入這段:
.black-cover {
position: absolute;
background-color: #000;
opacity: 0.5;
height: 100%;
width: 100%;
}
.end-window {
position: absolute;
margin: 65px 40px;
height: 450px;
}
.end-hands .m-card {
margin-top: 3px;
height: 50px;
width: 30px;
}
#button-end {
position: absolute;
top: 480px;
left: 500px;
}
game.html
,加入小視窗顯示的部分,修改後的完整內容為:
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout.html}"
>
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div layout:fragment="content" class="card game-window">
<input id="my-username" type="hidden" th:value="${username}">
<div class="timer text-center">..</div>
<div class="other-hands-90 other-hands-left" id="user-3"></div>
<div class="other-hands" id="user-4"></div>
<div class="my-hands" id="user-1"></div>
<div class="other-hands-90 other-hands-right" id="user-2"></div>
<div class="card-type"></div>
<div class="action">
<button type="button" class="btn btn-outline-success btn-lg" id="button-play" onclick="play()" disabled>出牌</button>
<button type="button" class="btn btn-outline-danger btn-lg" id="button-pass" onclick="pass()" disabled>PASS</button>
</div>
<div class="black-cover d-none"></div>
<div class="end-window card-deck d-none"></div>
</div>
<div layout:fragment="js-and-css">
<!-- websockets -->
<script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.js"
integrity="sha512-tL4PIUsPy+Rks1go4kQG8M8/ItpRMvKnbBjQm4d2DQnFwgcBYRRN00QdyQnWSCwNMsoY/MfJY8nHp2CzlNdtZA=="
crossorigin="anonymous"
referrerpolicy="no-referrer"></script>
<!-- custom -->
<script type="text/javascript" th:src="@{/js/WebSockets.js}"></script>
<script type="text/javascript" th:src="@{/js/game.js}"></script>
<link th:href="@{/css/game.css}" rel="stylesheet">
</div>
</body>
</html>
game.js
,加入 subscribeEnd()
、endGame()
:
function subscribeEnd() {
websocket.subscribe("/user/queue/end", (response) => {
response = JSON.parse(response.body);
endGame(response);
});
}
function endGame(data) {
let winner = data.winner;
delete data.hands[winner];
// 產生 winner 的部分
$(".end-window").empty();
$(".end-window").append(`
<div class="card" id="player-${winner}">
<div class="card-body">
<h5 class="card-title text-center">${winner}</h5>
<br>
<img class="card-img-top" src="https://picsum.photos/200/200" alt="Card image cap">
<div class="end-hands">
<h3 class="text-danger text-center" style="margin-top: 10px;">Winner</h3>
</div>
</div>
</div>
`)
// 產生其他人的部分
for (let name in data.hands) {
$(".end-window").append(`
<div class="card" id="player-${name}">
<div class="card-body">
<h5 class="card-title text-center">${name}</h5>
<br>
<img class="card-img-top" src="https://picsum.photos/200/200" alt="Card image cap">
<div class="end-hands"></div>
</div>
</div>
`);
data.hands[name].sort(compareByGeneral);
data.hands[name].map((card) => {
card = convertCard(card);
let tmp = card.split("-");
let color = (tmp[0] == SUITS.SPADE || tmp[0] == SUITS.CLUB) ? "#000000" : "#FF0000";
$(`#player-${name} .end-hands`).append(`
<div class="m-card text-center" style="color: ${color};">
<div>
${tmp[0]}
<br>
${tmp[1]}
</div>
</div>
`)
})
}
// 加入回到房間的按鈕
$(".end-window").append(`
<button type="button" class="btn btn-success btn-lg" id="button-end">回房間</button>
`);
$("#button-end").click(() => {
let roomId = window.location.href.split("/").slice(-1)[0];
window.location.href = `/room/${roomId}`;
})
// 顯示後面的黑色遮罩
$(".black-cover").removeClass("d-none");
// 顯示結算頁面
$(".end-window").removeClass("d-none");
}
這次鐵人賽算是分了兩階段,第一階段是原本的專案開發,第二階段因為要寫文章,所以又開了新的專案在同步原本專案,剪剪貼貼難免會出問題,然後就花比較多的時間在 Debug... 如果還有哪裡有錯,還請各位大大不吝指教,我會盡快修復!
經過這次開發,我覺得以後不要再嘗試用網頁寫遊戲好了,這真的不是個好主意,雖然我在第一天就知道了... 不過在意料之外的是,我居然能連續寫完 30 天,畢竟我到第 10 天就開始覺得膩了,但還是硬著頭皮寫完,加上最近還有其他事情在忙,每天都在燃燒,不過完成鐵人賽還是滿有成就感的~~
小弟還留了些東西讓讀者自由發揮的空間,比如:房間和結算頁面的頭貼、錯誤訊息的反饋等,因為不是在這次功能裡特別重要的,所以就先跳過~~
最後,說下小弟在技術相關的疑問好了,希望有大大能解釋~~
1. Component 的實例,究竟還需不需要 Service 負責存取?
2. 不太確定為什麼每輪出牌都會有這麼高的延遲,是 WebSocket 的問題嗎?