956 lines
28 KiB
PHP
956 lines
28 KiB
PHP
<?php
|
||
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/session_bootstrap.php';
|
||
|
||
// Sprawdzenie czy użytkownik jest zalogowany
|
||
if (!isset($_SESSION['logged_in']) || !$_SESSION['logged_in']) {
|
||
// Przekierowanie do strony logowania
|
||
header('Location: /login/index.php');
|
||
exit();
|
||
}
|
||
|
||
// Admini też mogą grać w ping-ponga
|
||
?>
|
||
<!--
|
||
Author: Wspólnie
|
||
Author URL: https://togethere.cloud
|
||
-->
|
||
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<title>Ping-pong | kontakt: wspolpraca@togethere.cloud</title>
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<meta charset="utf-8">
|
||
<meta name="keywords" content="projekty przyszłości"/>
|
||
<link rel="stylesheet" href="/css/header.css" type="text/css" media="all"/>
|
||
<link rel="stylesheet" href="/css/footer.css" type="text/css" media="all"/>
|
||
<link href="/css/font-awesome.min.css" rel="stylesheet" type="text/css" media="all">
|
||
<link href="/css/style.css" rel="stylesheet" type="text/css" media="all"/>
|
||
<link href="//fonts.googleapis.com/css?family=Lato:400,500,600,700,800,900" rel="stylesheet">
|
||
|
||
<!-- Game Scripts -->
|
||
<script src="/disciplines/ping-pong/js/anti-tamper.js"></script>
|
||
<script src="/disciplines/ping-pong/js/audio-manager.js"></script>
|
||
<script src="/disciplines/ping-pong/js/bot-ai.js"></script>
|
||
<script src="/disciplines/ping-pong/js/game.js"></script>
|
||
<script src="/disciplines/ping-pong/js/ui-manager.js"></script>
|
||
|
||
<style>
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
body {
|
||
background: linear-gradient(135deg, #0a0a0a 0%, #1a0a2e 100%);
|
||
font-family: 'Lato', sans-serif;
|
||
overflow: hidden;
|
||
height: 100vh;
|
||
margin: 0;
|
||
padding: 0;
|
||
cursor: url('/disciplines/ping-pong/img/cursor-small.png') 10 10,
|
||
url('/disciplines/ping-pong/img/cursor.png') 16 16,
|
||
default;
|
||
}
|
||
|
||
.menu-container,
|
||
.menu-button,
|
||
.back-button {
|
||
cursor: url('/disciplines/ping-pong/img/cursor-small.png') 10 10,
|
||
url('/disciplines/ping-pong/img/cursor.png') 16 16,
|
||
default;
|
||
}
|
||
|
||
body.game-active,
|
||
body.game-active #gameCanvas {
|
||
cursor: url('/disciplines/ping-pong/img/cursor.png') 16 16, crosshair;
|
||
}
|
||
|
||
body.game-active .back-button {
|
||
cursor: url('/disciplines/ping-pong/img/cursor.png') 16 16, default;
|
||
}
|
||
|
||
#gameContainer {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
height: 100vh;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
gap: 5px;
|
||
}
|
||
|
||
h1 {
|
||
color: #00fff7;
|
||
text-shadow: 0 0 20px #00fff7, 0 0 40px #00fff7;
|
||
font-size: 1.5em;
|
||
margin: 0;
|
||
text-align: center;
|
||
animation: neonPulse 2s ease-in-out infinite;
|
||
}
|
||
|
||
@keyframes neonPulse {
|
||
0%, 100% { text-shadow: 0 0 20px #00fff7, 0 0 40px #00fff7; }
|
||
50% { text-shadow: 0 0 30px #00fff7, 0 0 60px #00fff7, 0 0 80px #00fff7; }
|
||
}
|
||
|
||
.menu-container {
|
||
background: linear-gradient(135deg, rgba(26, 10, 46, 0.95) 0%, rgba(10, 10, 10, 0.95) 100%);
|
||
border: 3px solid #00fff7;
|
||
border-radius: 25px;
|
||
padding: 40px 50px;
|
||
box-shadow: 0 0 50px rgba(0, 255, 247, 0.4),
|
||
0 0 100px rgba(0, 128, 255, 0.2),
|
||
inset 0 0 60px rgba(0, 255, 247, 0.05);
|
||
animation: slideInGlow 0.6s ease-out;
|
||
max-height: 80vh;
|
||
overflow-y: auto;
|
||
backdrop-filter: blur(10px);
|
||
}
|
||
|
||
/* Panel z regułami/snapshotem ustawień */
|
||
.rules-panel {
|
||
margin: 0 0 18px 0;
|
||
padding: 12px 14px;
|
||
border-radius: 12px;
|
||
border: 2px solid rgba(0, 255, 247, 0.25);
|
||
background: rgba(0, 255, 247, 0.05);
|
||
color: #dffcff;
|
||
font-size: 0.95em;
|
||
line-height: 1.6;
|
||
}
|
||
.rules-grid { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 10px; }
|
||
.rule-chip {
|
||
background: rgba(0, 255, 247, 0.08);
|
||
border: 1px solid rgba(0, 255, 247, 0.25);
|
||
border-radius: 10px;
|
||
padding: 8px 10px;
|
||
text-align: center;
|
||
font-weight: 700;
|
||
color: #9af6ff;
|
||
}
|
||
.rules-panel .caption { font-weight: 700; margin-bottom: 8px; color: #7ae9ff; }
|
||
.rules-panel .special { margin-top: 10px; opacity: 0.9; }
|
||
|
||
.player-lobby-card {
|
||
display: grid;
|
||
grid-template-columns: minmax(260px, 1.1fr) minmax(0, 1.4fr);
|
||
gap: 14px;
|
||
margin-bottom: 22px;
|
||
padding: 16px;
|
||
border-radius: 18px;
|
||
border: 2px solid rgba(0, 255, 247, 0.18);
|
||
background: linear-gradient(180deg, rgba(0, 255, 247, 0.08) 0%, rgba(0, 0, 0, 0.16) 100%);
|
||
}
|
||
|
||
.player-lobby-head {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 14px;
|
||
}
|
||
|
||
.player-lobby-avatar {
|
||
width: 58px;
|
||
height: 58px;
|
||
border-radius: 16px;
|
||
display: grid;
|
||
place-items: center;
|
||
font-size: 1.45em;
|
||
font-weight: 900;
|
||
color: #0a0a0a;
|
||
background: linear-gradient(135deg, #00fff7 0%, #0080ff 100%);
|
||
box-shadow: 0 10px 24px rgba(0, 255, 247, 0.24);
|
||
}
|
||
|
||
.player-lobby-kicker {
|
||
font-size: 0.72em;
|
||
letter-spacing: .18em;
|
||
text-transform: uppercase;
|
||
color: #8ceef9;
|
||
margin-bottom: 6px;
|
||
}
|
||
|
||
.player-lobby-name {
|
||
font-size: 1.8em;
|
||
font-weight: 900;
|
||
color: #ffffff;
|
||
line-height: 1.05;
|
||
}
|
||
|
||
.player-lobby-meta {
|
||
margin-top: 8px;
|
||
color: rgba(223, 252, 255, 0.8);
|
||
font-size: 0.94em;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.player-lobby-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||
gap: 10px;
|
||
}
|
||
|
||
.player-lobby-stat {
|
||
padding: 12px;
|
||
border-radius: 14px;
|
||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||
background: rgba(255, 255, 255, 0.04);
|
||
}
|
||
|
||
.player-lobby-stat-label {
|
||
font-size: 0.7em;
|
||
letter-spacing: .12em;
|
||
text-transform: uppercase;
|
||
color: rgba(223, 252, 255, 0.62);
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.player-lobby-stat-value {
|
||
font-size: 1.15em;
|
||
font-weight: 800;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.mode-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||
gap: 16px;
|
||
width: 100%;
|
||
}
|
||
|
||
.mode-card {
|
||
width: 100%;
|
||
min-height: 220px;
|
||
padding: 22px 24px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
justify-content: space-between;
|
||
text-align: left;
|
||
}
|
||
|
||
.mode-card-eyebrow {
|
||
font-size: 0.72em;
|
||
letter-spacing: .18em;
|
||
text-transform: uppercase;
|
||
opacity: .72;
|
||
}
|
||
|
||
.mode-card-title {
|
||
font-size: 1.5em;
|
||
line-height: 1.15;
|
||
font-weight: 900;
|
||
margin: 10px 0 8px;
|
||
}
|
||
|
||
.mode-card-copy {
|
||
font-size: 0.95em;
|
||
line-height: 1.65;
|
||
text-transform: none;
|
||
letter-spacing: 0;
|
||
opacity: .92;
|
||
}
|
||
|
||
.mode-card-points {
|
||
margin-top: 14px;
|
||
font-size: 0.88em;
|
||
line-height: 1.7;
|
||
text-transform: none;
|
||
letter-spacing: 0;
|
||
opacity: .92;
|
||
}
|
||
|
||
@keyframes slideInGlow {
|
||
0% {
|
||
opacity: 0;
|
||
transform: scale(0.95);
|
||
box-shadow: 0 0 0 rgba(0, 255, 247, 0);
|
||
}
|
||
100% {
|
||
opacity: 1;
|
||
transform: scale(1);
|
||
box-shadow: 0 0 50px rgba(0, 255, 247, 0.4);
|
||
}
|
||
}
|
||
|
||
.menu-title {
|
||
background: linear-gradient(90deg, #00fff7 0%, #0080ff 50%, #00fff7 100%);
|
||
-webkit-background-clip: text;
|
||
-webkit-text-fill-color: transparent;
|
||
background-clip: text;
|
||
font-size: 1.8em;
|
||
text-align: center;
|
||
margin-bottom: 35px;
|
||
font-weight: bold;
|
||
filter: drop-shadow(0 0 15px rgba(0, 255, 247, 0.8));
|
||
animation: titleShine 3s ease-in-out infinite;
|
||
letter-spacing: 2px;
|
||
}
|
||
|
||
@keyframes titleShine {
|
||
0%, 100% { filter: drop-shadow(0 0 15px rgba(0, 255, 247, 0.8)); }
|
||
50% { filter: drop-shadow(0 0 25px rgba(0, 255, 247, 1)); }
|
||
}
|
||
|
||
.buttons-grid {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 18px;
|
||
align-items: center;
|
||
}
|
||
|
||
.menu-button {
|
||
background: linear-gradient(135deg, #00fff7 0%, #0080ff 100%);
|
||
color: #0a0a0a;
|
||
border: 2px solid rgba(0, 255, 247, 0.3);
|
||
padding: 18px 45px;
|
||
font-size: 1.15em;
|
||
font-weight: bold;
|
||
border-radius: 15px;
|
||
margin: 0;
|
||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||
box-shadow: 0 4px 20px rgba(0, 255, 247, 0.4),
|
||
inset 0 1px 0 rgba(255, 255, 255, 0.2);
|
||
display: block;
|
||
width: 280px;
|
||
position: relative;
|
||
overflow: hidden;
|
||
text-transform: uppercase;
|
||
letter-spacing: 1px;
|
||
}
|
||
|
||
.menu-button::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: -100%;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
|
||
transition: left 0.5s ease;
|
||
}
|
||
|
||
.menu-button:hover::before {
|
||
left: 100%;
|
||
}
|
||
|
||
.menu-button:hover {
|
||
transform: translateY(-3px) scale(1.03);
|
||
box-shadow: 0 8px 30px rgba(0, 255, 247, 0.6),
|
||
0 0 50px rgba(0, 255, 247, 0.3),
|
||
inset 0 1px 0 rgba(255, 255, 255, 0.3);
|
||
border-color: rgba(0, 255, 247, 0.8);
|
||
}
|
||
|
||
.menu-button:active {
|
||
transform: translateY(-1px) scale(1.01);
|
||
box-shadow: 0 4px 15px rgba(0, 255, 247, 0.5);
|
||
}
|
||
|
||
.menu-button.bot {
|
||
background: linear-gradient(135deg, #ff006e 0%, #ff4500 100%);
|
||
border-color: rgba(255, 0, 110, 0.3);
|
||
box-shadow: 0 4px 20px rgba(255, 0, 110, 0.4),
|
||
inset 0 1px 0 rgba(255, 255, 255, 0.2);
|
||
}
|
||
|
||
.menu-button.bot:hover {
|
||
box-shadow: 0 8px 30px rgba(255, 0, 110, 0.6),
|
||
0 0 50px rgba(255, 0, 110, 0.3),
|
||
inset 0 1px 0 rgba(255, 255, 255, 0.3);
|
||
border-color: rgba(255, 0, 110, 0.8);
|
||
}
|
||
|
||
.menu-button.bot:active {
|
||
box-shadow: 0 4px 15px rgba(255, 0, 110, 0.5);
|
||
}
|
||
|
||
.menu-button.extreme {
|
||
background: linear-gradient(135deg, #8b00ff 0%, #ff0000 50%, #000000 100%);
|
||
border-color: rgba(139, 0, 255, 0.5);
|
||
box-shadow: 0 4px 20px rgba(139, 0, 255, 0.6),
|
||
0 0 30px rgba(255, 0, 0, 0.3),
|
||
inset 0 1px 0 rgba(255, 255, 255, 0.2);
|
||
animation: extremePulse 1.5s ease-in-out infinite;
|
||
font-weight: 900;
|
||
text-shadow: 0 0 10px rgba(255, 0, 0, 0.8);
|
||
}
|
||
|
||
@keyframes extremePulse {
|
||
0%, 100% {
|
||
box-shadow: 0 4px 20px rgba(139, 0, 255, 0.6),
|
||
0 0 30px rgba(255, 0, 0, 0.3);
|
||
}
|
||
50% {
|
||
box-shadow: 0 8px 40px rgba(139, 0, 255, 1),
|
||
0 0 60px rgba(255, 0, 0, 0.8),
|
||
0 0 80px rgba(0, 0, 0, 0.5);
|
||
}
|
||
}
|
||
|
||
.menu-button.extreme:hover {
|
||
box-shadow: 0 8px 30px rgba(139, 0, 255, 1),
|
||
0 0 70px rgba(255, 0, 0, 0.9),
|
||
0 0 100px rgba(0, 0, 0, 0.7),
|
||
inset 0 1px 0 rgba(255, 255, 255, 0.3);
|
||
border-color: rgba(139, 0, 255, 1);
|
||
transform: translateY(-3px) scale(1.05);
|
||
}
|
||
|
||
.menu-button.extreme:active {
|
||
box-shadow: 0 4px 15px rgba(139, 0, 255, 0.7);
|
||
}
|
||
|
||
.menu-button:disabled {
|
||
opacity: 0.5;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
@media (max-width: 920px) {
|
||
.player-lobby-card {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.player-lobby-grid {
|
||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||
}
|
||
|
||
.mode-grid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 640px) {
|
||
.player-lobby-grid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.player-lobby-card,
|
||
.menu-container {
|
||
padding: 18px;
|
||
}
|
||
}
|
||
|
||
#gameCanvas {
|
||
border: 3px solid #00fff7;
|
||
border-radius: 10px;
|
||
box-shadow: 0 0 40px rgba(0, 255, 247, 0.5);
|
||
background: #0a0a0a;
|
||
display: none;
|
||
outline: none;
|
||
}
|
||
|
||
#gameCanvas:focus {
|
||
box-shadow: 0 0 50px rgba(0, 255, 247, 0.8);
|
||
}
|
||
|
||
.score-container {
|
||
display: none;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
width: 700px;
|
||
margin: 0;
|
||
gap: 0;
|
||
}
|
||
|
||
.score-wrapper {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 10px;
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
.score-label {
|
||
font-size: 0.85em;
|
||
font-weight: bold;
|
||
text-transform: uppercase;
|
||
letter-spacing: 2px;
|
||
}
|
||
|
||
.score-label.player {
|
||
color: #0080ff;
|
||
text-shadow: 0 0 10px #0080ff;
|
||
}
|
||
|
||
.score-label.bot {
|
||
color: #ff006e;
|
||
text-shadow: 0 0 10px #ff006e;
|
||
}
|
||
|
||
.score-label.timer {
|
||
color: #00fff7;
|
||
text-shadow: 0 0 10px #00fff7;
|
||
}
|
||
|
||
nav,
|
||
footer {
|
||
display: none !important;
|
||
}
|
||
|
||
.score {
|
||
font-size: 2em;
|
||
font-weight: bold;
|
||
text-shadow: 0 0 10px;
|
||
}
|
||
|
||
.score.player {
|
||
color: #0080ff;
|
||
text-shadow: 0 0 20px #0080ff;
|
||
}
|
||
|
||
.score.bot {
|
||
color: #ff006e;
|
||
text-shadow: 0 0 20px #ff006e;
|
||
}
|
||
.score.timer {
|
||
color: #00fff7;
|
||
text-shadow: 0 0 15px #00fff7;
|
||
font-size: 1.8em;
|
||
}
|
||
.back-button {
|
||
background: rgba(0, 255, 247, 0.05);
|
||
color: #00fff7;
|
||
border: 2px solid rgba(0, 255, 247, 0.5);
|
||
padding: 14px 35px;
|
||
font-size: 1em;
|
||
font-weight: bold;
|
||
border-radius: 12px;
|
||
margin-top: 25px;
|
||
box-shadow: 0 0 20px rgba(0, 255, 247, 0.2);
|
||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||
display: none;
|
||
text-transform: uppercase;
|
||
letter-spacing: 1px;
|
||
}
|
||
|
||
.back-button:hover {
|
||
background: rgba(0, 255, 247, 0.15);
|
||
box-shadow: 0 0 30px rgba(0, 255, 247, 0.4);
|
||
border-color: rgba(0, 255, 247, 0.8);
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
.back-button:active {
|
||
transform: translateY(0);
|
||
box-shadow: 0 0 15px rgba(0, 255, 247, 0.3);
|
||
}
|
||
|
||
.modal {
|
||
position: fixed;
|
||
z-index: 1000;
|
||
left: 0;
|
||
top: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: rgba(0, 0, 0, 0.8);
|
||
animation: fadeIn 0.3s ease;
|
||
display: none;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.modal[style*="display: block"] {
|
||
display: flex !important;
|
||
}
|
||
|
||
@keyframes fadeIn {
|
||
from { opacity: 0; }
|
||
to { opacity: 1; }
|
||
}
|
||
|
||
.modal-content {
|
||
background: linear-gradient(135deg, #1a0a2e 0%, #0a0a0a 100%);
|
||
border: 3px solid #00fff7;
|
||
border-radius: 20px;
|
||
padding: 40px;
|
||
width: 90%;
|
||
max-width: 500px;
|
||
text-align: center;
|
||
box-shadow: 0 0 50px rgba(0, 255, 247, 0.5);
|
||
animation: modalSlide 0.5s ease;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
@keyframes modalSlide {
|
||
from { transform: translateY(-100px); opacity: 0; }
|
||
to { transform: translateY(0); opacity: 1; }
|
||
}
|
||
|
||
.modal-title {
|
||
color: #00fff7;
|
||
font-size: 2.5em;
|
||
margin-bottom: 20px;
|
||
text-shadow: 0 0 20px #00fff7;
|
||
width: 100%;
|
||
}
|
||
|
||
.modal-text {
|
||
color: #ffffff;
|
||
font-size: 1.3em;
|
||
margin-bottom: 30px;
|
||
line-height: 1.5;
|
||
width: 100%;
|
||
word-wrap: break-word;
|
||
}
|
||
|
||
.win-modal .modal-title {
|
||
color: #00ff00;
|
||
text-shadow: 0 0 20px #00ff00;
|
||
animation: winPulse 1s ease-in-out infinite;
|
||
}
|
||
|
||
.lose-modal .modal-title {
|
||
color: #ff006e;
|
||
text-shadow: 0 0 20px #ff006e;
|
||
}
|
||
|
||
@keyframes winPulse {
|
||
0%, 100% { transform: scale(1); }
|
||
50% { transform: scale(1.1); }
|
||
}
|
||
|
||
/* Style dla przycisków w modalach */
|
||
.modal-content .menu-button {
|
||
width: 350px;
|
||
padding: 18px 40px;
|
||
font-size: 1.3em;
|
||
margin: 10px 0;
|
||
font-weight: 700;
|
||
letter-spacing: 2px;
|
||
}
|
||
|
||
.modal-content .menu-button:first-of-type {
|
||
margin-top: 20px;
|
||
}
|
||
|
||
.win-modal .menu-button:not(.bot) {
|
||
background: linear-gradient(135deg, #00ff88 0%, #00ccff 100%);
|
||
border-color: rgba(0, 255, 136, 0.5);
|
||
box-shadow: 0 6px 25px rgba(0, 255, 136, 0.5),
|
||
inset 0 1px 0 rgba(255, 255, 255, 0.3);
|
||
}
|
||
|
||
.win-modal .menu-button:not(.bot):hover {
|
||
box-shadow: 0 10px 35px rgba(0, 255, 136, 0.7),
|
||
0 0 60px rgba(0, 255, 136, 0.4),
|
||
inset 0 1px 0 rgba(255, 255, 255, 0.4);
|
||
border-color: rgba(0, 255, 136, 0.9);
|
||
}
|
||
|
||
.lose-modal .menu-button:not(.bot) {
|
||
background: linear-gradient(135deg, #ff006e 0%, #ff0099 100%);
|
||
border-color: rgba(255, 0, 110, 0.5);
|
||
box-shadow: 0 6px 25px rgba(255, 0, 110, 0.5),
|
||
inset 0 1px 0 rgba(255, 255, 255, 0.3);
|
||
}
|
||
|
||
.lose-modal .menu-button:not(.bot):hover {
|
||
box-shadow: 0 10px 35px rgba(255, 0, 110, 0.7),
|
||
0 0 60px rgba(255, 0, 110, 0.4),
|
||
inset 0 1px 0 rgba(255, 255, 255, 0.4);
|
||
border-color: rgba(255, 0, 110, 0.9);
|
||
}
|
||
|
||
.modal-content .menu-button.bot {
|
||
background: linear-gradient(135deg, #6b46c1 0%, #8b5cf6 100%);
|
||
border-color: rgba(139, 92, 246, 0.5);
|
||
box-shadow: 0 6px 25px rgba(139, 92, 246, 0.5),
|
||
inset 0 1px 0 rgba(255, 255, 255, 0.3);
|
||
}
|
||
|
||
.modal-content .menu-button.bot:hover {
|
||
box-shadow: 0 10px 35px rgba(139, 92, 246, 0.7),
|
||
0 0 60px rgba(139, 92, 246, 0.4),
|
||
inset 0 1px 0 rgba(255, 255, 255, 0.4);
|
||
border-color: rgba(139, 92, 246, 0.9);
|
||
}
|
||
|
||
.buttons-grid {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
|
||
.game-controls-hint {
|
||
color: #00fff7;
|
||
font-size: 0.9em;
|
||
margin-top: 10px;
|
||
text-align: center;
|
||
display: none;
|
||
opacity: 0.7;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<!-- Tutaj PHP sprawdza sesje czy zalogowany i wczytuje odpowiednią nawigację -->
|
||
<?php
|
||
if (!empty($_SESSION['logged_in'])) {
|
||
if (isset($_SESSION['role']) && $_SESSION['role'] === 'admin') {
|
||
include __DIR__ . '/../../global/navLoginedAdmin.php';
|
||
} else {
|
||
include __DIR__ . '/../../global/navLogined.php';
|
||
}
|
||
} else {
|
||
include __DIR__ . '/../../global/navNoLogined.php';
|
||
}
|
||
?>
|
||
|
||
<div id="gameContainer">
|
||
<h1>Ping-Pong</h1>
|
||
|
||
<!-- Menu główne -->
|
||
<div id="mainMenu" class="menu-container">
|
||
<div class="menu-title">Wybierz Tryb Gry</div>
|
||
<div class="player-lobby-card" id="playerLobbyCard">
|
||
<div class="player-lobby-head">
|
||
<div class="player-lobby-avatar" id="playerLobbyAvatar"><?php echo strtoupper(substr((string) ($_SESSION['username'] ?? 'G'), 0, 1)); ?></div>
|
||
<div>
|
||
<div class="player-lobby-kicker">Profil aktywnego gracza</div>
|
||
<div class="player-lobby-name" id="playerLobbyName"><?php echo htmlspecialchars((string) ($_SESSION['username'] ?? 'Gracz'), ENT_QUOTES, 'UTF-8'); ?></div>
|
||
<div class="player-lobby-meta" id="playerLobbyMeta">Ładowanie danych konta przed wyborem trybu…</div>
|
||
</div>
|
||
</div>
|
||
<div class="player-lobby-grid" id="playerLobbyGrid"></div>
|
||
</div>
|
||
<div class="rules-panel" id="rulesPanel" style="display:none">
|
||
<div class="caption">Reguły meczu</div>
|
||
<div class="rules-grid">
|
||
<div class="rule-chip" id="chipPoints">Set do 11</div>
|
||
<div class="rule-chip" id="chipSets">Mecz do 3 setów</div>
|
||
<div class="rule-chip" id="chipServe">Serwis co 2</div>
|
||
</div>
|
||
<div class="special" id="specialRulesText"></div>
|
||
</div>
|
||
<div class="buttons-grid">
|
||
<div class="mode-grid">
|
||
<button class="menu-button mode-card" onclick="showOnlineMessage()">
|
||
<span class="mode-card-eyebrow">Matchmaking na żywo</span>
|
||
<span class="mode-card-title">🌐 Graj Online</span>
|
||
<span class="mode-card-copy">Wchodzisz do kolejki 1v1, system dobiera przeciwnika i od razu pokazuje pełny panel Twojego konta: saldo, rozegrane mecze, skuteczność i kolejne statystyki.</span>
|
||
<span class="mode-card-points">10 sekund przygotowania przed startem • set do 11 • mecz do 3 wygranych setów</span>
|
||
</button>
|
||
<button class="menu-button bot mode-card" onclick="showDifficultyMenu()">
|
||
<span class="mode-card-eyebrow">Solo training</span>
|
||
<span class="mode-card-title">🤖 Graj z Botem</span>
|
||
<span class="mode-card-copy">Szybki trening offline z natychmiastowym wejściem do gry. Dobry tryb na rozgrzewkę przed meczem online albo testowanie sterowania i tempa odbić.</span>
|
||
<span class="mode-card-points">4 poziomy trudności • start bez kolejki • nauka refleksu i ustawienia</span>
|
||
</button>
|
||
</div>
|
||
<button class="back-button" style="display: block;" onclick="window.location.href='/disciplines/'">← Wróć do Dyscyplin</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Menu wyboru poziomu trudności -->
|
||
<div id="difficultyMenu" class="menu-container" style="display: none;">
|
||
<div class="menu-title">Wybierz Poziom Trudności</div>
|
||
<div class="buttons-grid">
|
||
<button class="menu-button" onclick="startGame('easy')">🟢 Łatwy</button>
|
||
<button class="menu-button" onclick="startGame('medium')">🟡 Średni</button>
|
||
<button class="menu-button" onclick="startGame('hard')">🔴 Trudny</button>
|
||
<button class="menu-button extreme" onclick="startGame('extreme')">💀 EXTREME</button>
|
||
<button class="back-button" style="display: block;" onclick="backToMainMenu()">← Powrót do Menu</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Wynik -->
|
||
<div class="score-container" id="scoreContainer">
|
||
<div class="score-wrapper">
|
||
<div class="score-label player" id="playerScoreLabel">👤 GRACZ • SETY 0</div>
|
||
<div class="score player" id="playerScore">0</div>
|
||
</div>
|
||
<div class="score-wrapper timer-wrapper">
|
||
<div class="score-label timer">⏱️ CZAS</div>
|
||
<div class="score timer" id="gameTimer">0:00</div>
|
||
</div>
|
||
<div class="score-wrapper">
|
||
<div class="score-label bot" id="botScoreLabel">🤖 BOT • SETY 0</div>
|
||
<div class="score bot" id="botScore">0</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Canvas gry -->
|
||
<canvas id="gameCanvas" width="700" height="450" tabindex="0"></canvas>
|
||
<div class="game-controls-hint" id="controlsHint">
|
||
⌨️ Sterowanie: ⬆️⬇️ Strzałki lub W/S
|
||
</div>
|
||
<button class="back-button" id="gameBackButton" onclick="endGame()">← Zakończ Grę</button>
|
||
</div>
|
||
|
||
<!-- Modal "W przygotowaniu" -->
|
||
<div id="comingSoonModal" class="modal">
|
||
<div class="modal-content">
|
||
<div class="modal-title">W Przygotowaniu</div>
|
||
<div class="modal-text">Ta funkcja jest obecnie w fazie rozwoju. Wkrótce będzie dostępna!</div>
|
||
<button class="menu-button" onclick="closeModal('comingSoonModal')">OK</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Modal wygranej -->
|
||
<div id="winModal" class="modal win-modal">
|
||
<div class="modal-content">
|
||
<div class="modal-title">🎉 WYGRAŁEŚ! 🎉</div>
|
||
<div class="modal-text">Gratulacje! Pokonałeś przeciwnika!</div>
|
||
<div class="modal-text" style="font-size: 32px; margin: 15px 0; color: #00fff7;">Czas: <span id="winTime">0:00</span></div>
|
||
<button class="menu-button" onclick="resetGame()">Zagraj Ponownie</button>
|
||
<button class="menu-button bot" onclick="endGame()">Menu Główne</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Modal przegranej -->
|
||
<div id="loseModal" class="modal lose-modal">
|
||
<div class="modal-content">
|
||
<div class="modal-title">💔 PRZEGRAŁEŚ 💔</div>
|
||
<div class="modal-text">Nie poddawaj się! Spróbuj jeszcze raz!</div>
|
||
<div class="modal-text" style="font-size: 32px; margin: 15px 0; color: #ff006e;">Czas: <span id="loseTime">0:00</span></div>
|
||
<button class="menu-button" onclick="resetGame()">Zagraj Ponownie</button>
|
||
<button class="menu-button bot" onclick="endGame()">Menu Główne</button>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// Inicjalizacja gry po załadowaniu strony
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
// Utwórz instancję gry (globalną)
|
||
window.game = new PingPongGame('gameCanvas');
|
||
|
||
// Utwórz menedżer UI
|
||
window.uiManager = new UIManager(window.game);
|
||
|
||
// Bot: używamy stałych zasad/stylu (snapshoty tylko dla online)
|
||
renderBotDefaultsPanel();
|
||
loadPingPongLobbyProfile();
|
||
|
||
console.log('Ping-Pong Game initialized!');
|
||
});
|
||
|
||
async function loadPingPongLobbyProfile() {
|
||
const avatar = document.getElementById('playerLobbyAvatar');
|
||
const name = document.getElementById('playerLobbyName');
|
||
const meta = document.getElementById('playerLobbyMeta');
|
||
const grid = document.getElementById('playerLobbyGrid');
|
||
if (!grid) return;
|
||
|
||
const fallback = {
|
||
userId: <?php echo (int) ($_SESSION['user_id'] ?? 0); ?>,
|
||
username: <?php echo json_encode((string) ($_SESSION['username'] ?? 'Gracz'), JSON_UNESCAPED_UNICODE); ?>,
|
||
role: <?php echo json_encode((string) ($_SESSION['role'] ?? 'user'), JSON_UNESCAPED_UNICODE); ?>,
|
||
balance: 0,
|
||
matchesPlayed: 0,
|
||
matchesWon: 0,
|
||
matchesLost: 0,
|
||
winRate: 0,
|
||
tournamentsPlayed: 0,
|
||
leaguesParticipated: 0,
|
||
totalTransactions: 0,
|
||
memberSince: '',
|
||
};
|
||
|
||
const render = function(summary) {
|
||
const data = summary || fallback;
|
||
const username = data.username || fallback.username;
|
||
if (avatar) avatar.textContent = username.charAt(0).toUpperCase();
|
||
if (name) name.textContent = username;
|
||
if (meta) {
|
||
const memberSince = data.memberSince ? new Date(data.memberSince).toLocaleDateString('pl-PL') : 'konto aktywne';
|
||
meta.textContent = `ID #${data.userId || fallback.userId} • ${data.role || fallback.role} • ${memberSince}`;
|
||
}
|
||
|
||
const cards = [
|
||
['Saldo', `${Number(data.balance || 0).toFixed(2)} PLN`],
|
||
['Mecze', String(data.matchesPlayed || 0)],
|
||
['Win rate', `${Number(data.winRate || 0).toFixed(1)}%`],
|
||
['Turnieje', String(data.tournamentsPlayed || 0)],
|
||
['Ligi', String(data.leaguesParticipated || 0)],
|
||
['Transakcje', String(data.totalTransactions || 0)],
|
||
['Wygrane', String(data.matchesWon || 0)],
|
||
['Porażki', String(data.matchesLost || 0)],
|
||
];
|
||
|
||
grid.innerHTML = cards.map(function(card) {
|
||
return `<div class="player-lobby-stat"><div class="player-lobby-stat-label">${card[0]}</div><div class="player-lobby-stat-value">${card[1]}</div></div>`;
|
||
}).join('');
|
||
};
|
||
|
||
try {
|
||
const response = await fetch('/api/matches/ping-pong/1v1/player-summary.php', { credentials: 'include' });
|
||
const json = await response.json().catch(() => null);
|
||
if (!response.ok || !json || !json.success || !json.data) {
|
||
throw new Error('player_summary_error');
|
||
}
|
||
render(json.data);
|
||
} catch (error) {
|
||
render(fallback);
|
||
}
|
||
}
|
||
|
||
async function loadDisciplineSettingsSnapshot() {
|
||
const rulesPanel = document.getElementById('rulesPanel');
|
||
if (!rulesPanel) return;
|
||
|
||
const defaults = {
|
||
pointsToWin: 11,
|
||
setsToWin: 3,
|
||
serveRotation: 2,
|
||
specialRules: 'Deuce od 10–10, gramy do 2 przewagi.'
|
||
};
|
||
|
||
let rules = { ...defaults };
|
||
try {
|
||
const res = await fetch('/administration/disciplines/ping-pong/settings/?snapshot=true', { credentials: 'same-origin' });
|
||
if (res.ok) {
|
||
const data = await res.json();
|
||
const snap = data.snapshot || data.data || null;
|
||
if (snap && snap.rules) {
|
||
rules = {
|
||
pointsToWin: parseInt(snap.rules.pointsToWin) || defaults.pointsToWin,
|
||
setsToWin: parseInt(snap.rules.setsToWin) || defaults.setsToWin,
|
||
serveRotation: parseInt(snap.rules.serveRotation) || defaults.serveRotation,
|
||
specialRules: snap.rules.specialRules || ''
|
||
};
|
||
}
|
||
}
|
||
} catch (e) {
|
||
// Fallback na defaults gdy endpoint jest dostępny tylko dla admina
|
||
}
|
||
|
||
// Render chipów
|
||
document.getElementById('chipPoints').textContent = `Set do ${rules.pointsToWin}`;
|
||
document.getElementById('chipSets').textContent = `Mecz do ${rules.setsToWin} setów`;
|
||
document.getElementById('chipServe').textContent = `Serwis co ${rules.serveRotation}`;
|
||
const special = document.getElementById('specialRulesText');
|
||
special.textContent = rules.specialRules ? `Specjalne: ${rules.specialRules}` : '';
|
||
rulesPanel.style.display = 'block';
|
||
}
|
||
|
||
// Panel dla trybu z botem: stałe zasady/styl
|
||
function renderBotDefaultsPanel() {
|
||
const rulesPanel = document.getElementById('rulesPanel');
|
||
if (!rulesPanel) return;
|
||
const defaults = { pointsToWin: 11, setsToWin: 3, serveRotation: 2 };
|
||
const chipPoints = document.getElementById('chipPoints');
|
||
const chipSets = document.getElementById('chipSets');
|
||
const chipServe = document.getElementById('chipServe');
|
||
const special = document.getElementById('specialRulesText');
|
||
if (chipPoints) chipPoints.textContent = `Set do ${defaults.pointsToWin}`;
|
||
if (chipSets) chipSets.textContent = `Mecz do ${defaults.setsToWin} setów`;
|
||
if (chipServe) chipServe.textContent = `Serwis co ${defaults.serveRotation}`;
|
||
if (special) special.textContent = 'Tryb Bot: stałe zasady i styl. Snapshoty działają tylko w trybie online.';
|
||
rulesPanel.style.display = 'block';
|
||
}
|
||
</script>
|
||
|
||
<?php
|
||
if (!empty($_SESSION['logged_in'])) {
|
||
include __DIR__ . '/../../global/footerLogined.php';
|
||
} else {
|
||
include __DIR__ . '/../../global/footerNoLogined.php';
|
||
}
|
||
?>
|
||
</body>
|
||
</html>
|