var canPlayCard = false; const PlayerHandComponent = { data() { return { hand: [], isDogPhase: false, isAwaitingResponse: false, }; }, computed: { isHandInactive() { //TODO: Needs implementation } }, template: `
`, methods: { updateHand(eventData) { this.hand = eventData.hand.map(card => ({ idx: parseInt(card.idx, 10), card: card.card })); this.isDogPhase = false; console.log("Vue Data Updated. Hand size:", this.hand.length); if (this.hand.length > 0) { console.log("First card path check:", this.getCardImagePath(this.hand[0].card)); } }, handlePlayCard(cardidx) { if(this.isAwaitingResponse) return if(!canPlayCard) return canPlayCard = false; this.isAwaitingResponse = true console.debug(`Playing card ${cardidx} from hand`) const wiggleKeyframes = [ { transform: 'translateX(0)' }, { transform: 'translateX(-5px)' }, { transform: 'translateX(5px)' }, { transform: 'translateX(-5px)' }, { transform: 'translateX(0)' } ]; const wiggleTiming = { duration: 400, iterations: 1, easing: 'ease-in-out', fill: 'forwards' }; const targetButton = this.$el.querySelector(`[data-card-id="${cardidx}"]`); const cardElement = targetButton ? targetButton.closest('.handcard') : null; const payload = { cardindex: cardidx.toString(), isDog: false } sendEventAndWait("PlayCard", payload).then( () => { this.hand = this.hand.filter(card => card.idx !== cardidx); this.hand.forEach((card, index) => { card.idx = index; }) this.isAwaitingResponse = false; } ).catch( (err) => { if (cardElement) { cardElement.animate(wiggleKeyframes, wiggleTiming); } else { console.warn(`Could not find DOM element for card index ${cardidx} to wiggle.`); } this.isAwaitingResponse = false; canPlayCard = true; } ) }, handleSkipDogLife() { globalThis.handleSkipDogLife(); }, getCardImagePath(cardName) { return `/assets/images/cards/${cardName}.png`; } } }; const ScoreBoardComponent = { data() { return { trumpsuit: 'N/A', playerScores: [], }; }, template: `

Tricks Won

PLAYER
TRICKS
{{ player.name }}
{{ player.tricks }}
`, methods: { calculateNewScores(players, tricklist) { const playercounts = new Map(); players.forEach(player => { playercounts.set(player, 0) }); tricklist.forEach(playerWonTrick => { if (playerWonTrick !== "Trick in Progress" && playercounts.has(playerWonTrick)) { playercounts.set(playerWonTrick, playercounts.get(playerWonTrick) + 1); } }); const newScores = players.map(name => ({ name: name, tricks: playercounts.get(name) || 0, })); newScores.sort((a, b) => b.tricks - a.tricks); return newScores; }, updateNewRoundData(eventData) { console.log("Vue Scoreboard Data Update Triggered: New Round!"); this.playerScores = eventData.players.map(player => ({ name: player, tricks: 0, })); }, updateTrickEndData(eventData) { const { playerwon, playersin, tricklist } = eventData; console.log(`Vue Scoreboard Data Update Triggered: ${playerwon} won the trick!`); this.playerScores = this.calculateNewScores(playersin, tricklist); } } }; const GameInfoComponent = { data() { return { trumpsuit: 'No Trumpsuit', firstCardImagePath: '/assets/images/cards/1B.png', }; }, template: `

Trumpsuit

{{ trumpsuit }}

First Card
First Card
`, methods: { resetFirstCard(eventData) { console.log("GameInfoComponent: Resetting First Card to placeholder."); this.firstCardImagePath = '/assets/images/cards/1B.png'; }, updateFirstCard(eventData) { const firstCardId = eventData.firstCard; console.log("GameInfoComponent: Updating First Card to:", firstCardId); let imageSource; if (firstCardId === "BLANK" || !firstCardId) { imageSource = "/assets/images/cards/1B.png"; } else { imageSource = `/assets/images/cards/${firstCardId}.png`; } this.firstCardImagePath = imageSource; }, updateTrumpsuit(eventData) { this.trumpsuit = eventData.trumpsuit; } } }; const TrickDisplayComponent = { data() { return { playedCards: [], }; }, template: `
{{ play.player }}
`, methods: { getCardImagePath(cardId) { return `/assets/images/cards/${cardId}.png`; }, clearPlayedCards() { console.log("TrickDisplayComponent: Clearing played cards."); this.playedCards = []; }, updatePlayedCards(eventData) { console.log("TrickDisplayComponent: Updating played cards."); this.playedCards = eventData.playedCards; } } }; function formatPlayerName(player) { let name = player.name; if (player.dog) { name += " 🐶"; } return name; } const TurnComponent = { data() { return { currentPlayerName: 'Waiting...', nextPlayers: [], }; }, template: `

Current Player

{{ currentPlayerName }}

Next Players

{{ name }}

`, methods: { updateTurnData(eventData) { console.log("TurnComponent: Updating turn data."); const { currentPlayer, nextPlayers } = eventData; this.currentPlayerName = formatPlayerName(currentPlayer); this.nextPlayers = nextPlayers.map(player => formatPlayerName(player)); } } }; const LobbyComponent = { data() { return { lobbyName: 'Loading...', lobbyId: 'default', isHost: false, maxPlayers: 0, players: [], showKickedModal: false, kickedEventData: null, showSessionClosedModal: false, sessionClosedEventData: null, }; }, template: `
Lobby-Name: {{ lobbyName }}
Exit
Players: {{ players.length }} / {{ maxPlayers }}
`, methods: { updateLobbyData(eventData) { console.log("LobbyComponent: Received Lobby Update Event."); this.isHost = eventData.host; this.maxPlayers = eventData.maxPlayers; this.players = eventData.players; }, setInitialData(name, id) { this.lobbyName = name; this.lobbyId = id; }, startGame() { globalThis.startGame() }, leaveGame(gameId) { //TODO: Needs implementation }, handleKickPlayer(playerId) { globalThis.handleKickPlayer(playerId) }, showKickModal(eventData) { this.showKickedModal = true; setTimeout(() => { this.kickedEventData = eventData; this.showKickedModal = false; if (typeof globalThis.receiveGameStateChange === 'function') { globalThis.receiveGameStateChange(this.kickedEventData); } else { console.error("FATAL: receiveGameStateChange ist nicht global definiert."); } }, 5000); }, showSessionClosedModal(eventData) { this.sessionClosedEventData = eventData; this.showSessionClosedModal = true; setTimeout(() => { this.showSessionClosedModal = false; if (typeof globalThis.receiveGameStateChange === 'function') { globalThis.receiveGameStateChange(this.sessionClosedEventData); } else { console.error("FATAL: receiveGameStateChange ist nicht global definiert."); } }, 5000); } } }; function requestCardEvent(eventData) { //TODO: Needs correct implementation of setting the inactive class in the PlayerHandComponent } function receiveGameStateChange(eventData) { const content = eventData.content; const title = eventData.title || 'Knockout Whist'; const url = eventData.url || null; exchangeBody(content, title, url); } function receiveRoundEndEvent(eventData) { //TODO: When alert is working, set an alert that shows how won the round and with how much tricks. } let playerHandApp = null; let scoreBoardApp = null; let gameInfoApp = null; let trickDisplayApp = null; let turnApp = null; globalThis.initGameVueComponents = function() { // Initializing PlayerHandComponent const app = Vue.createApp(PlayerHandComponent); playerHandApp = app; const mountedHand = app.mount('#player-hand-container'); if (mountedHand && mountedHand.updateHand) { globalThis.updatePlayerHand = mountedHand.updateHand; onEvent("ReceivedHandEvent", globalThis.updatePlayerHand); console.log("PLAYER HAND SYSTEM: updatePlayerHand successfully exposed."); } else { console.error("FATAL ERROR: PlayerHandComponent mount failed. Check if #player-hand-container exists."); } // Initializing Scoreboard if (scoreBoardApp) return const app2 = Vue.createApp(ScoreBoardComponent) scoreBoardApp = app2 const mountedHand2 = app2.mount('#score-table') if (mountedHand2) { globalThis.updateNewRoundData = mountedHand2.updateNewRoundData; onEvent("NewRoundEvent", handleNewRoundEvent); globalThis.updateTrickEndData = mountedHand2.updateTrickEndData; onEvent("TrickEndEvent", globalThis.updateTrickEndData); console.log("SCOREBOARD: updateNewRoundData successfully exposed."); } else { console.error("FATAL ERROR: Scoreboard mount failed. Check if #score-table exists."); } // Initializing Gameinfo if (gameInfoApp) return const app3 = Vue.createApp(GameInfoComponent) gameInfoApp = app3 const mountedGameInfo = app3.mount('#game-info-component') if(mountedGameInfo) { globalThis.resetFirstCard = mountedGameInfo.resetFirstCard; globalThis.updateFirstCard = mountedGameInfo.updateFirstCard; globalThis.updateTrumpsuit = mountedGameInfo.updateTrumpsuit onEvent("NewTrickEvent", handleNewTrickEvent); console.log("GameInfo: resetFirstCard successfully exposed."); } else { console.error("FATAL ERROR: GameInfo mount failed. Check if #score-table exists."); } // Initializing TrickCardContainer if (trickDisplayApp) return; const app4 = Vue.createApp(TrickDisplayComponent); trickDisplayApp = app4; const mountedTrickDisplay = app4.mount('#trick-cards-container'); if (mountedTrickDisplay) { globalThis.clearPlayedCards = mountedTrickDisplay.clearPlayedCards; globalThis.updatePlayedCards = mountedTrickDisplay.updatePlayedCards; onEvent("CardPlayedEvent", handleCardPlayedEvent) console.log("TRICK DISPLAY: Handlers successfully exposed (clearPlayedCards, updatePlayedCards)."); } else { console.error("FATAL ERROR: TrickDisplay mount failed. Check if #trick-cards-container exists."); } // Initializing TurnContainer if (turnApp) return; const app5 = Vue.createApp(TurnComponent) turnApp = app5; const mountedTurnApp = app5.mount('#turn-component') if(mountedTurnApp) { globalThis.updateTurnData = mountedTurnApp.updateTurnData; onEvent("TurnEvent", globalThis.updateTurnData); console.log("TURN DISPLAY: Handlers successfully exposed (clearPlayedCards, updatePlayedCards)."); } else { console.error("FATAL ERROR: TURNAPP mount failed. Check if #trick-cards-container exists."); } } let lobbyApp = null; globalThis.initLobbyVueComponents = function(initialLobbyName, initialLobbyId, initialIsHost, initialMaxPlayers, initialPlayers) { if (lobbyApp) return; const appLobby = Vue.createApp(LobbyComponent); lobbyApp = appLobby; const mountedLobby = appLobby.mount('#lobby-app-mount'); if (mountedLobby) { mountedLobby.setInitialData(initialLobbyName, initialLobbyId); //Damit beim erstmaligen Betreten der Lobby die Spieler etc. angezeigt werden. mountedLobby.updateLobbyData({ host: initialIsHost, maxPlayers: initialMaxPlayers, players: initialPlayers }); globalThis.updateLobbyData = mountedLobby.updateLobbyData; globalThis.showKickModal = mountedLobby.showKickModal; globalThis.showSessionClosedModal = mountedLobby.showSessionClosedModal; onEvent("LobbyUpdateEvent", globalThis.updateLobbyData); onEvent("KickEvent", globalThis.showKickModal); onEvent("SessionClosed", globalThis.showSessionClosedModal); console.log("LobbyComponent successfully mounted and registered events."); } else { console.error("FATAL ERROR: LobbyComponent mount failed."); } } function handleCardPlayedEvent(eventData) { console.log("CardPlayedEvent received. Updating Game Info and Trick Display."); if (typeof globalThis.updateFirstCard === 'function') { globalThis.updateFirstCard(eventData); } if (typeof globalThis.updatePlayedCards === 'function') { globalThis.updatePlayedCards(eventData); } } function handleNewTrickEvent(eventData) { if (typeof globalThis.resetFirstCard === 'function') { globalThis.resetFirstCard(eventData); } if (typeof globalThis.clearPlayedCards === 'function') { globalThis.clearPlayedCards(); } } function handleNewRoundEvent(eventData) { if (typeof globalThis.updateNewRoundData === 'function') { globalThis.updateNewRoundData(eventData); } if (typeof globalThis.updateTrumpsuit === 'function') { globalThis.updateTrumpsuit(eventData); } } onEvent("GameStateChangeEvent", receiveGameStateChange) onEvent("LeftEvent", receiveGameStateChange) onEvent("RequestCardEvent", requestCardEvent) onEvent("RoundEndEvent", receiveRoundEndEvent)