mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-07-17 07:52:16 -07:00
feature: added socket connection, updated styles
This commit is contained in:
parent
d089e0709a
commit
b99347325c
1 changed files with 191 additions and 58 deletions
|
|
@ -1,27 +1,37 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="game">
|
<div class="game">
|
||||||
|
<!-- GRID SYSTEM, 2 columns -->
|
||||||
<div class="play-area-container">
|
<div class="play-area-container">
|
||||||
<div>
|
<!-- the left side of the screen, the playmats -->
|
||||||
<!-- This div is a container for the main play area -->
|
<div style="display: grid; grid-template-columns: 1fr 1fr; width: 100%; height: 100%">
|
||||||
|
<!-- FOR THE OTHER PLAYER -->
|
||||||
<div class="play-view-area">
|
<div class="play-view-area">
|
||||||
<!-- Render stacks of cards -->
|
THIS AREA IS FOR THE OTHER PLAYER, WEBSOCKETS TO TRANSMIT ARRAY OF CARDS AND THEIR
|
||||||
<div v-for="(stack, index) in cardStacks" :key="index" class="card-stack">
|
POSITIONS
|
||||||
<!-- Cards in stack rendering is handled below, this is commented out old version -->
|
|
||||||
<!-- <div
|
|
||||||
v-for="(card, i) in stack"
|
|
||||||
:key="card.id"
|
|
||||||
class="card"
|
|
||||||
:style="cardStyle(card, i)"
|
|
||||||
@mousedown="startDrag(card, $event)"
|
|
||||||
@dblclick="toggleTap(card)"
|
|
||||||
>
|
|
||||||
{{ card.name }}
|
|
||||||
</div> -->
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="play-view-area">
|
||||||
|
THIS AREA IS FOR THE OTHER PLAYER, WEBSOCKETS TO TRANSMIT ARRAY OF CARDS AND THEIR
|
||||||
|
POSITIONS
|
||||||
|
</div>
|
||||||
|
<div class="play-view-area">
|
||||||
|
THIS AREA IS FOR THE OTHER PLAYER, WEBSOCKETS TO TRANSMIT ARRAY OF CARDS AND THEIR
|
||||||
|
POSITIONS
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- FOR OUR PLAYER -->
|
||||||
<div class="play-area">
|
<div class="play-area">
|
||||||
<div class="bg-black" ref="playArea" style="height: 100%">
|
<div class="playAreaStyles" ref="playArea">
|
||||||
|
<p
|
||||||
|
class="absolute"
|
||||||
|
style="
|
||||||
|
color: white;
|
||||||
|
font-size: 36px;
|
||||||
|
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8);
|
||||||
|
font-weight: bold;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
PLAY AREA
|
||||||
|
</p>
|
||||||
<div v-for="(stack, index) in cardStacks" :key="index" class="card-stack">
|
<div v-for="(stack, index) in cardStacks" :key="index" class="card-stack">
|
||||||
<div
|
<div
|
||||||
v-for="(card, i) in stack"
|
v-for="(card, i) in stack"
|
||||||
|
|
@ -30,34 +40,44 @@
|
||||||
:style="cardStyle(card, i)"
|
:style="cardStyle(card, i)"
|
||||||
@mousedown="startDrag(card, $event)"
|
@mousedown="startDrag(card, $event)"
|
||||||
@dblclick="toggleTap(card)"
|
@dblclick="toggleTap(card)"
|
||||||
@mouseover="card.peek = true"
|
@mouseover="startPeekTimer(card)"
|
||||||
@mouseleave="card.peek = false"
|
@mouseleave="cancelPeekTimer(card)"
|
||||||
>
|
>
|
||||||
{{ card.name }}
|
{{ card.name }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<!-- relative container to make sure absolute draw area stays in bounds -->
|
||||||
class="bg-yellow absolute"
|
<div class="draw-area" ref="drawArea" style="height: 20%; width: 100%">
|
||||||
ref="drawArea"
|
<p
|
||||||
style="overflow-x: scroll; height: 20%; width: 100%; bottom: 0;"
|
style="
|
||||||
></div>
|
font-size: 36px;
|
||||||
|
color: white;
|
||||||
|
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8);
|
||||||
|
font-weight: bold;
|
||||||
|
"
|
||||||
|
class="absolute"
|
||||||
|
>
|
||||||
|
DRAW AREA
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- For each stack in computed cardStacks, render each card -->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bg-green flex justify-center items-center">
|
<!-- This is the other stuff area -->
|
||||||
|
<div class="peek-area flex justify-center items-center">
|
||||||
<!-- Show enlarged "peeked" cards here -->
|
<!-- Show enlarged "peeked" cards here -->
|
||||||
<div
|
<div
|
||||||
v-for="card in peekedCards"
|
v-for="card in peekedCards"
|
||||||
:key="card.id"
|
:key="card.id"
|
||||||
class="card"
|
class="card peeked-card"
|
||||||
:style="{ position: 'relative', width: '200px', height: '260px', zIndex: 100 }"
|
:style="{ position: 'relative', width: '200px', height: '260px', zIndex: 100 }"
|
||||||
>
|
>
|
||||||
<div class="text-center">{{ card.name }}</div>
|
<div class="text-center">{{ card.name }}</div>
|
||||||
<div class="text-center" style="font-size: 12px; color: #666">Peeked</div>
|
<div class="text-center" style="font-size: 12px; color: #888; margin-top: 8px">
|
||||||
|
Peeked
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -65,6 +85,7 @@
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<button @click="addCard()">Add Card P1</button>
|
<button @click="addCard()">Add Card P1</button>
|
||||||
<button @click="clearBoard">Clear</button>
|
<button @click="clearBoard">Clear</button>
|
||||||
|
<button @click="connect()">CONNECT TO SOCKET</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -72,7 +93,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import type { CSSProperties } from 'vue';
|
import type { CSSProperties } from 'vue';
|
||||||
|
import socket from '../socket.js'; // Import your socket connection module
|
||||||
// Define the Card interface, which describes the shape of a card object
|
// Define the Card interface, which describes the shape of a card object
|
||||||
interface Card {
|
interface Card {
|
||||||
id: number;
|
id: number;
|
||||||
|
|
@ -101,11 +122,40 @@ const cardId = ref(1);
|
||||||
|
|
||||||
// Reference to the play area DOM element (used for boundary calculations)
|
// Reference to the play area DOM element (used for boundary calculations)
|
||||||
const playArea = ref<HTMLElement | null>(null);
|
const playArea = ref<HTMLElement | null>(null);
|
||||||
|
|
||||||
const drawArea = ref<HTMLElement | null>(null);
|
const drawArea = ref<HTMLElement | null>(null);
|
||||||
|
|
||||||
// Computed array of cards currently peeked (hovered)
|
const hoverTimers = new Map<number, number>();
|
||||||
const peekedCards = computed(() => cards.value.filter((c) => c.peek));
|
|
||||||
|
const connect = () => {
|
||||||
|
socket.connect();
|
||||||
|
// Here you would typically initialize your WebSocket connection
|
||||||
|
};
|
||||||
|
|
||||||
|
// Computed array of cards currently peeked (hovered), should be limited to only one card at a time
|
||||||
|
const peekedCards = computed(() => {
|
||||||
|
return cards.value.filter((card) => card.peek);
|
||||||
|
});
|
||||||
|
|
||||||
|
const startPeekTimer = (card: Card) => {
|
||||||
|
cancelPeekTimer(card); // Clear any existing timer first
|
||||||
|
|
||||||
|
const timerId = window.setTimeout(() => {
|
||||||
|
for (const c of cards.value) {
|
||||||
|
c.peek = c.id === card.id; // Only one card is peeked
|
||||||
|
}
|
||||||
|
}, 250); // 1 second delay
|
||||||
|
|
||||||
|
hoverTimers.set(card.id, timerId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const cancelPeekTimer = (card: Card) => {
|
||||||
|
const timerId = hoverTimers.get(card.id);
|
||||||
|
if (timerId !== undefined) {
|
||||||
|
clearTimeout(timerId);
|
||||||
|
hoverTimers.delete(card.id);
|
||||||
|
}
|
||||||
|
card.peek = false; // Reset peek on leave
|
||||||
|
};
|
||||||
|
|
||||||
// The array of cards being dragged together (stack)
|
// The array of cards being dragged together (stack)
|
||||||
const draggedStack = ref<Card[]>([]);
|
const draggedStack = ref<Card[]>([]);
|
||||||
|
|
@ -182,7 +232,7 @@ const startDrag = (card: Card, e: MouseEvent) => {
|
||||||
// Get all cards at the same position (stack)
|
// Get all cards at the same position (stack)
|
||||||
const stack = cards.value.filter((c) => c.x === card.x && c.y === card.y);
|
const stack = cards.value.filter((c) => c.x === card.x && c.y === card.y);
|
||||||
|
|
||||||
// The last card in the stack array is the top visually
|
// topCard is the last card in the stack (topmost visually)
|
||||||
const topCard = stack[stack.length - 1];
|
const topCard = stack[stack.length - 1];
|
||||||
|
|
||||||
if (card.id === topCard?.id) {
|
if (card.id === topCard?.id) {
|
||||||
|
|
@ -226,7 +276,7 @@ const dragMove = (e: MouseEvent) => {
|
||||||
const dx = newX - dragging.value.x;
|
const dx = newX - dragging.value.x;
|
||||||
const dy = newY - dragging.value.y;
|
const dy = newY - dragging.value.y;
|
||||||
|
|
||||||
// Move all cards in the dragged stack by the delta
|
// Move all cards in the dragged stack by the delta, this is the actual moving logic, everything else is just setting up the drag
|
||||||
for (const c of draggedStack.value) {
|
for (const c of draggedStack.value) {
|
||||||
c.x += dx;
|
c.x += dx;
|
||||||
c.y += dy;
|
c.y += dy;
|
||||||
|
|
@ -242,7 +292,6 @@ const dragMove = (e: MouseEvent) => {
|
||||||
*/
|
*/
|
||||||
const stopDrag = () => {
|
const stopDrag = () => {
|
||||||
if (!dragging.value) return;
|
if (!dragging.value) return;
|
||||||
|
|
||||||
const draggedCards = draggedStack.value;
|
const draggedCards = draggedStack.value;
|
||||||
|
|
||||||
// Remove dragged cards from original cards array
|
// Remove dragged cards from original cards array
|
||||||
|
|
@ -306,7 +355,7 @@ const cardStacks = computed(() => {
|
||||||
if (!groups[key]) groups[key] = [];
|
if (!groups[key]) groups[key] = [];
|
||||||
groups[key].push(card);
|
groups[key].push(card);
|
||||||
}
|
}
|
||||||
// Return all groups as array of stacks
|
// Return all groups as under a single array called cardStacks
|
||||||
return Object.values(groups);
|
return Object.values(groups);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -335,43 +384,111 @@ const cardStyle = (card: Card, index: number): CSSProperties => ({
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
background: linear-gradient(135deg, #0f0f23 0%, #1a1a2e 50%, #16213e 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Control buttons container */
|
/* Control buttons container */
|
||||||
.controls {
|
.controls {
|
||||||
padding: 10px;
|
padding: 0px;
|
||||||
background: #eee;
|
background: linear-gradient(135deg, #2c3e50 0%, #3498db 100%);
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls button {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px 24px;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls button:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
|
||||||
|
background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls button:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Play area where cards are rendered */
|
/* Play area where cards are rendered */
|
||||||
.play-area {
|
.play-area {
|
||||||
height: 50%;
|
height: 100%;
|
||||||
width: 50%;
|
width: 100%;
|
||||||
background: #4a5568;
|
position: relative;
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
overflow: hidden; /* prevent overflow outside play area */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Upper play view area (red background) */
|
/* Upper play view area (opponent areas) */
|
||||||
.play-view-area {
|
.play-view-area {
|
||||||
background: red;
|
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a24 50%, #c0392b 100%);
|
||||||
position: absolute;
|
border: 2px solid rgba(255, 255, 255, 0.2);
|
||||||
width: 50%;
|
border-radius: 12px;
|
||||||
height: 50%;
|
color: white;
|
||||||
overflow: hidden;
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
box-shadow: inset 0 2px 10px rgba(0, 0, 0, 0.3);
|
||||||
|
font-weight: bold;
|
||||||
|
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Container for play area and other UI elements */
|
/* Container for play area and other UI elements */
|
||||||
.play-area-container {
|
.play-area-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 4fr 1fr;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playAreaStyles {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
|
border: 2px solid rgba(255, 255, 255, 0.2);
|
||||||
|
background: linear-gradient(135deg, #2ecc71 0%, #27ae60 50%, #229954 100%);
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: inset 0 2px 10px rgba(0, 0, 0, 0.3);
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.draw-area {
|
||||||
|
margin-top: auto;
|
||||||
|
background: linear-gradient(135deg, #3498db 0%, #2980b9 50%, #1f4e79 100%);
|
||||||
|
border-radius: 12px 12px 0 0;
|
||||||
|
border: 2px solid rgba(255, 255, 255, 0.2);
|
||||||
|
box-shadow: inset 0 2px 10px rgba(0, 0, 0, 0.3);
|
||||||
|
height: 20%;
|
||||||
|
width: 100%;
|
||||||
|
overflow-x: auto; /* Allow horizontal scrolling */
|
||||||
|
overflow-y: hidden;
|
||||||
|
white-space: nowrap; /* Prevent line breaks so cards line up horizontally */
|
||||||
|
position: relative;
|
||||||
|
padding: 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.peek-area {
|
||||||
|
background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 50%, #663399 100%);
|
||||||
|
border: 2px solid rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: inset 0 2px 10px rgba(0, 0, 0, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Each card stack container is absolutely positioned */
|
/* Each card stack container is absolutely positioned */
|
||||||
|
|
@ -383,14 +500,30 @@ const cardStyle = (card: Card, index: number): CSSProperties => ({
|
||||||
.card {
|
.card {
|
||||||
width: 60px; /* matches CARD_WIDTH */
|
width: 60px; /* matches CARD_WIDTH */
|
||||||
height: 90px; /* matches CARD_HEIGHT */
|
height: 90px; /* matches CARD_HEIGHT */
|
||||||
background: #edf2f7;
|
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 50%, #dee2e6 100%);
|
||||||
border: 1px solid #2d3748;
|
border: 2px solid #495057;
|
||||||
border-radius: 4px;
|
border-radius: 8px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
line-height: 90px; /* vertically center card name */
|
line-height: 90px; /* vertically center card name */
|
||||||
user-select: none; /* disable text selection */
|
user-select: none; /* disable text selection */
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
font-weight: bold;
|
||||||
|
color: #212529;
|
||||||
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
|
||||||
|
border-color: #6c757d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.peeked-card {
|
||||||
|
background: linear-gradient(135deg, #fff3cd 0%, #ffeaa7 50%, #fdcb6e 100%);
|
||||||
|
border: 3px solid #e17055;
|
||||||
|
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.4);
|
||||||
|
transform: scale(1.05);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue