Spaces:
Running
Running
needs to be exactly like the mechanics and graphics and gameplay as wormate.io, save it as wormate.io
Browse files- README.md +8 -5
- components/game-footer.js +100 -0
- components/game-nav.js +96 -0
- components/wormate-nav.js +78 -0
- index.html +30 -19
- script.js +160 -0
- style.css +83 -18
- wormate.js +242 -0
README.md
CHANGED
|
@@ -1,10 +1,13 @@
|
|
| 1 |
---
|
| 2 |
-
title: Wormate
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
|
|
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
-
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: Wormate.io Clone π
|
| 3 |
+
colorFrom: green
|
| 4 |
+
colorTo: pink
|
| 5 |
+
emoji: π³
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
| 8 |
+
tags:
|
| 9 |
+
- deepsite-v3
|
| 10 |
---
|
| 11 |
|
| 12 |
+
# Welcome to your new DeepSite project!
|
| 13 |
+
This project was created with [DeepSite](https://huggingface.co/deepsite).
|
components/game-footer.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class GameFooter extends HTMLElement {
|
| 2 |
+
connectedCallback() {
|
| 3 |
+
this.attachShadow({ mode: 'open' });
|
| 4 |
+
this.shadowRoot.innerHTML = `
|
| 5 |
+
<style>
|
| 6 |
+
footer {
|
| 7 |
+
background: linear-gradient(145deg, #166534, #14532d);
|
| 8 |
+
color: white;
|
| 9 |
+
padding: 2rem 1rem;
|
| 10 |
+
margin-top: 3rem;
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
.footer-container {
|
| 14 |
+
max-width: 1200px;
|
| 15 |
+
margin: 0 auto;
|
| 16 |
+
display: grid;
|
| 17 |
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
| 18 |
+
gap: 2rem;
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
.footer-section h3 {
|
| 22 |
+
font-size: 1.2rem;
|
| 23 |
+
margin-bottom: 1rem;
|
| 24 |
+
display: flex;
|
| 25 |
+
align-items: center;
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
.footer-section h3 i {
|
| 29 |
+
margin-right: 0.5rem;
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
.footer-section p {
|
| 33 |
+
margin-bottom: 0.5rem;
|
| 34 |
+
opacity: 0.8;
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
.social-links {
|
| 38 |
+
display: flex;
|
| 39 |
+
gap: 1rem;
|
| 40 |
+
margin-top: 1rem;
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
.social-link {
|
| 44 |
+
color: white;
|
| 45 |
+
transition: all 0.3s;
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
.social-link:hover {
|
| 49 |
+
transform: translateY(-3px);
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
.copyright {
|
| 53 |
+
text-align: center;
|
| 54 |
+
margin-top: 2rem;
|
| 55 |
+
padding-top: 1rem;
|
| 56 |
+
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
| 57 |
+
opacity: 0.7;
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
@media (max-width: 640px) {
|
| 61 |
+
.footer-container {
|
| 62 |
+
grid-template-columns: 1fr;
|
| 63 |
+
}
|
| 64 |
+
}
|
| 65 |
+
</style>
|
| 66 |
+
|
| 67 |
+
<footer>
|
| 68 |
+
<div class="footer-container">
|
| 69 |
+
<div class="footer-section">
|
| 70 |
+
<h3><i data-feather="info"></i> About</h3>
|
| 71 |
+
<p>Wiggly Worm Warriors is a fun game for kids of all ages. Grow your worm by eating colorful dots!</p>
|
| 72 |
+
</div>
|
| 73 |
+
|
| 74 |
+
<div class="footer-section">
|
| 75 |
+
<h3><i data-feather="help-circle"></i> Help</h3>
|
| 76 |
+
<p>Use arrow keys to move</p>
|
| 77 |
+
<p>Eat dots to grow longer</p>
|
| 78 |
+
<p>Avoid walls and yourself</p>
|
| 79 |
+
</div>
|
| 80 |
+
|
| 81 |
+
<div class="footer-section">
|
| 82 |
+
<h3><i data-feather="heart"></i> Made For</h3>
|
| 83 |
+
<p>Special edition for 8-year-old worm lovers!</p>
|
| 84 |
+
<div class="social-links">
|
| 85 |
+
<a href="#"><i data-feather="facebook"></i></a>
|
| 86 |
+
<a href="#"><i data-feather="twitter"></i></a>
|
| 87 |
+
<a href="#"><i data-feather="youtube"></i></a>
|
| 88 |
+
</div>
|
| 89 |
+
</div>
|
| 90 |
+
</div>
|
| 91 |
+
|
| 92 |
+
<div class="copyright">
|
| 93 |
+
© ${new Date().getFullYear()} Wiggly Worm Warriors - All rights reserved
|
| 94 |
+
</div>
|
| 95 |
+
</footer>
|
| 96 |
+
`;
|
| 97 |
+
}
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
customElements.define('game-footer', GameFooter);
|
components/game-nav.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class GameNav extends HTMLElement {
|
| 2 |
+
connectedCallback() {
|
| 3 |
+
this.attachShadow({ mode: 'open' });
|
| 4 |
+
this.shadowRoot.innerHTML = `
|
| 5 |
+
<style>
|
| 6 |
+
nav {
|
| 7 |
+
background: linear-gradient(145deg, #22c55e, #16a34a);
|
| 8 |
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
.nav-container {
|
| 12 |
+
display: flex;
|
| 13 |
+
justify-content: space-between;
|
| 14 |
+
align-items: center;
|
| 15 |
+
padding: 1rem 2rem;
|
| 16 |
+
max-width: 1200px;
|
| 17 |
+
margin: 0 auto;
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
.logo {
|
| 21 |
+
display: flex;
|
| 22 |
+
align-items: center;
|
| 23 |
+
font-weight: bold;
|
| 24 |
+
font-size: 1.5rem;
|
| 25 |
+
color: white;
|
| 26 |
+
text-decoration: none;
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
.logo-icon {
|
| 30 |
+
margin-right: 0.5rem;
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
.nav-links {
|
| 34 |
+
display: flex;
|
| 35 |
+
gap: 1.5rem;
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
.nav-link {
|
| 39 |
+
color: white;
|
| 40 |
+
text-decoration: none;
|
| 41 |
+
font-weight: 600;
|
| 42 |
+
transition: all 0.3s;
|
| 43 |
+
display: flex;
|
| 44 |
+
align-items: center;
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
.nav-link:hover {
|
| 48 |
+
transform: translateY(-2px);
|
| 49 |
+
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
.nav-link-icon {
|
| 53 |
+
margin-right: 0.3rem;
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
@media (max-width: 640px) {
|
| 57 |
+
.nav-container {
|
| 58 |
+
flex-direction: column;
|
| 59 |
+
padding: 1rem;
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
.nav-links {
|
| 63 |
+
margin-top: 1rem;
|
| 64 |
+
gap: 1rem;
|
| 65 |
+
}
|
| 66 |
+
}
|
| 67 |
+
</style>
|
| 68 |
+
|
| 69 |
+
<nav>
|
| 70 |
+
<div class="nav-container">
|
| 71 |
+
<a href="#" class="logo">
|
| 72 |
+
<i data-feather="wind" class="logo-icon"></i>
|
| 73 |
+
Wiggly Worms
|
| 74 |
+
</a>
|
| 75 |
+
|
| 76 |
+
<div class="nav-links">
|
| 77 |
+
<a href="#" class="nav-link">
|
| 78 |
+
<i data-feather="home" class="nav-link-icon"></i>
|
| 79 |
+
Home
|
| 80 |
+
</a>
|
| 81 |
+
<a href="#" class="nav-link">
|
| 82 |
+
<i data-feather="award" class="nav-link-icon"></i>
|
| 83 |
+
Scores
|
| 84 |
+
</a>
|
| 85 |
+
<a href="#" class="nav-link">
|
| 86 |
+
<i data-feather="settings" class="nav-link-icon"></i>
|
| 87 |
+
Settings
|
| 88 |
+
</a>
|
| 89 |
+
</div>
|
| 90 |
+
</div>
|
| 91 |
+
</nav>
|
| 92 |
+
`;
|
| 93 |
+
}
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
customElements.define('game-nav', GameNav);
|
components/wormate-nav.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class WormateNav extends HTMLElement {
|
| 2 |
+
connectedCallback() {
|
| 3 |
+
this.attachShadow({ mode: 'open' });
|
| 4 |
+
this.shadowRoot.innerHTML = `
|
| 5 |
+
<style>
|
| 6 |
+
nav {
|
| 7 |
+
background: #1a1a2e;
|
| 8 |
+
color: white;
|
| 9 |
+
padding: 1rem;
|
| 10 |
+
position: fixed;
|
| 11 |
+
top: 0;
|
| 12 |
+
left: 0;
|
| 13 |
+
right: 0;
|
| 14 |
+
z-index: 100;
|
| 15 |
+
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
.nav-container {
|
| 19 |
+
display: flex;
|
| 20 |
+
justify-content: space-between;
|
| 21 |
+
align-items: center;
|
| 22 |
+
max-width: 1200px;
|
| 23 |
+
margin: 0 auto;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
.logo {
|
| 27 |
+
font-family: 'Press Start 2P', cursive;
|
| 28 |
+
font-size: 1.2rem;
|
| 29 |
+
color: #ff6b6b;
|
| 30 |
+
text-decoration: none;
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
.nav-links {
|
| 34 |
+
display: flex;
|
| 35 |
+
gap: 1.5rem;
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
.nav-link {
|
| 39 |
+
color: white;
|
| 40 |
+
text-decoration: none;
|
| 41 |
+
font-family: 'Press Start 2P', cursive;
|
| 42 |
+
font-size: 0.8rem;
|
| 43 |
+
transition: all 0.3s;
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
.nav-link:hover {
|
| 47 |
+
color: #ff6b6b;
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
@media (max-width: 768px) {
|
| 51 |
+
.nav-container {
|
| 52 |
+
flex-direction: column;
|
| 53 |
+
gap: 1rem;
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
.nav-links {
|
| 57 |
+
gap: 1rem;
|
| 58 |
+
}
|
| 59 |
+
}
|
| 60 |
+
</style>
|
| 61 |
+
|
| 62 |
+
<nav>
|
| 63 |
+
<div class="nav-container">
|
| 64 |
+
<a href="#" class="logo">WORMATE.IO</a>
|
| 65 |
+
|
| 66 |
+
<div class="nav-links">
|
| 67 |
+
<a href="#" class="nav-link">HOME</a>
|
| 68 |
+
<a href="#" class="nav-link">PLAY</a>
|
| 69 |
+
<a href="#" class="nav-link">SKINS</a>
|
| 70 |
+
<a href="#" class="nav-link">LEADERBOARD</a>
|
| 71 |
+
</div>
|
| 72 |
+
</div>
|
| 73 |
+
</nav>
|
| 74 |
+
`;
|
| 75 |
+
}
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
customElements.define('wormate-nav', WormateNav);
|
index.html
CHANGED
|
@@ -1,19 +1,30 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
<!DOCTYPE html>
|
| 3 |
+
<html lang="en">
|
| 4 |
+
<head>
|
| 5 |
+
<meta charset="UTF-8">
|
| 6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 7 |
+
<title>Wormate.io Clone</title>
|
| 8 |
+
<link rel="stylesheet" href="style.css">
|
| 9 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.7.2/socket.io.js"></script>
|
| 10 |
+
<script src="components/wormate-nav.js"></script>
|
| 11 |
+
<style>
|
| 12 |
+
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');
|
| 13 |
+
</style>
|
| 14 |
+
</head>
|
| 15 |
+
<body>
|
| 16 |
+
<wormate-nav></wormate-nav>
|
| 17 |
+
|
| 18 |
+
<div id="game-container">
|
| 19 |
+
<canvas id="gameCanvas"></canvas>
|
| 20 |
+
<div id="game-ui">
|
| 21 |
+
<div id="score-display">Score: 0</div>
|
| 22 |
+
<div id="leaderboard"></div>
|
| 23 |
+
<button id="play-btn" class="wormate-btn">PLAY</button>
|
| 24 |
+
</div>
|
| 25 |
+
</div>
|
| 26 |
+
|
| 27 |
+
<script src="wormate.js"></script>
|
| 28 |
+
<script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
|
| 29 |
+
</body>
|
| 30 |
+
</html>
|
script.js
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
document.addEventListener('DOMContentLoaded', () => {
|
| 2 |
+
const canvas = document.getElementById('gameCanvas');
|
| 3 |
+
const ctx = canvas.getContext('2d');
|
| 4 |
+
|
| 5 |
+
// Set canvas size
|
| 6 |
+
canvas.width = canvas.parentElement.clientWidth;
|
| 7 |
+
canvas.height = canvas.parentElement.clientHeight;
|
| 8 |
+
|
| 9 |
+
// Game variables
|
| 10 |
+
const gridSize = 20;
|
| 11 |
+
const tileCount = canvas.width / gridSize;
|
| 12 |
+
let speed = 7;
|
| 13 |
+
let score = 0;
|
| 14 |
+
|
| 15 |
+
// Worm
|
| 16 |
+
let worm = [];
|
| 17 |
+
worm[0] = {x: Math.floor(tileCount/2) * gridSize, y: Math.floor(tileCount/2) * gridSize};
|
| 18 |
+
|
| 19 |
+
// Food
|
| 20 |
+
let food = {
|
| 21 |
+
x: Math.floor(Math.random() * tileCount) * gridSize,
|
| 22 |
+
y: Math.floor(Math.random() * tileCount) * gridSize,
|
| 23 |
+
color: getRandomColor()
|
| 24 |
+
};
|
| 25 |
+
|
| 26 |
+
// Direction
|
| 27 |
+
let xVelocity = 0;
|
| 28 |
+
let yVelocity = 0;
|
| 29 |
+
|
| 30 |
+
// Game loop
|
| 31 |
+
function gameLoop() {
|
| 32 |
+
// Clear canvas
|
| 33 |
+
ctx.fillStyle = '#f0fdf4';
|
| 34 |
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
| 35 |
+
|
| 36 |
+
// Move worm
|
| 37 |
+
let head = {x: worm[0].x + xVelocity, y: worm[0].y + yVelocity};
|
| 38 |
+
worm.unshift(head);
|
| 39 |
+
|
| 40 |
+
// Check if worm eats food
|
| 41 |
+
if(head.x === food.x && head.y === food.y) {
|
| 42 |
+
score++;
|
| 43 |
+
food = {
|
| 44 |
+
x: Math.floor(Math.random() * tileCount) * gridSize,
|
| 45 |
+
y: Math.floor(Math.random() * tileCount) * gridSize,
|
| 46 |
+
color: getRandomColor()
|
| 47 |
+
};
|
| 48 |
+
} else {
|
| 49 |
+
worm.pop();
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
// Draw worm
|
| 53 |
+
for(let i = 0; i < worm.length; i++) {
|
| 54 |
+
ctx.fillStyle = i === 0 ? '#22c55e' : '#4ade80';
|
| 55 |
+
ctx.beginPath();
|
| 56 |
+
ctx.arc(worm[i].x + gridSize/2, worm[i].y + gridSize/2, gridSize/2, 0, Math.PI*2);
|
| 57 |
+
ctx.fill();
|
| 58 |
+
|
| 59 |
+
// Add eyes to head
|
| 60 |
+
if(i === 0) {
|
| 61 |
+
ctx.fillStyle = 'white';
|
| 62 |
+
ctx.beginPath();
|
| 63 |
+
ctx.arc(worm[i].x + gridSize/3, worm[i].y + gridSize/3, gridSize/8, 0, Math.PI*2);
|
| 64 |
+
ctx.arc(worm[i].x + 2*gridSize/3, worm[i].y + gridSize/3, gridSize/8, 0, Math.PI*2);
|
| 65 |
+
ctx.fill();
|
| 66 |
+
|
| 67 |
+
ctx.fillStyle = 'black';
|
| 68 |
+
ctx.beginPath();
|
| 69 |
+
ctx.arc(worm[i].x + gridSize/3, worm[i].y + gridSize/3, gridSize/12, 0, Math.PI*2);
|
| 70 |
+
ctx.arc(worm[i].x + 2*gridSize/3, worm[i].y + gridSize/3, gridSize/12, 0, Math.PI*2);
|
| 71 |
+
ctx.fill();
|
| 72 |
+
}
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
// Draw food
|
| 76 |
+
ctx.fillStyle = food.color;
|
| 77 |
+
ctx.beginPath();
|
| 78 |
+
ctx.arc(food.x + gridSize/2, food.y + gridSize/2, gridSize/2, 0, Math.PI*2);
|
| 79 |
+
ctx.fill();
|
| 80 |
+
|
| 81 |
+
// Check collision with walls
|
| 82 |
+
if(head.x < 0 || head.x >= canvas.width || head.y < 0 || head.y >= canvas.height) {
|
| 83 |
+
gameOver();
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
// Check collision with self
|
| 87 |
+
for(let i = 1; i < worm.length; i++) {
|
| 88 |
+
if(head.x === worm[i].x && head.y === worm[i].y) {
|
| 89 |
+
gameOver();
|
| 90 |
+
}
|
| 91 |
+
}
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
// Game controls
|
| 95 |
+
document.addEventListener('keydown', (e) => {
|
| 96 |
+
// Prevent reverse direction
|
| 97 |
+
switch(e.key) {
|
| 98 |
+
case 'ArrowUp':
|
| 99 |
+
if(yVelocity === 0) {
|
| 100 |
+
xVelocity = 0;
|
| 101 |
+
yVelocity = -gridSize;
|
| 102 |
+
}
|
| 103 |
+
break;
|
| 104 |
+
case 'ArrowDown':
|
| 105 |
+
if(yVelocity === 0) {
|
| 106 |
+
xVelocity = 0;
|
| 107 |
+
yVelocity = gridSize;
|
| 108 |
+
}
|
| 109 |
+
break;
|
| 110 |
+
case 'ArrowLeft':
|
| 111 |
+
if(xVelocity === 0) {
|
| 112 |
+
xVelocity = -gridSize;
|
| 113 |
+
yVelocity = 0;
|
| 114 |
+
}
|
| 115 |
+
break;
|
| 116 |
+
case 'ArrowRight':
|
| 117 |
+
if(xVelocity === 0) {
|
| 118 |
+
xVelocity = gridSize;
|
| 119 |
+
yVelocity = 0;
|
| 120 |
+
}
|
| 121 |
+
break;
|
| 122 |
+
}
|
| 123 |
+
});
|
| 124 |
+
|
| 125 |
+
// Game over function
|
| 126 |
+
function gameOver() {
|
| 127 |
+
clearInterval(gameInterval);
|
| 128 |
+
|
| 129 |
+
// Show game over screen
|
| 130 |
+
const gameOverDiv = document.createElement('div');
|
| 131 |
+
gameOverDiv.className = 'game-over';
|
| 132 |
+
gameOverDiv.innerHTML = `
|
| 133 |
+
<h2 class="text-4xl font-bold text-white mb-4">Game Over!</h2>
|
| 134 |
+
<p class="text-2xl text-white mb-6">Your score: ${score}</p>
|
| 135 |
+
<button onclick="location.reload()" class="game-button text-xl">
|
| 136 |
+
Play Again <i data-feather="refresh-cw" class="ml-2"></i>
|
| 137 |
+
</button>
|
| 138 |
+
`;
|
| 139 |
+
canvas.parentElement.appendChild(gameOverDiv);
|
| 140 |
+
feather.replace();
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
// Random color generator for food
|
| 144 |
+
function getRandomColor() {
|
| 145 |
+
const colors = [
|
| 146 |
+
'#ef4444', '#f97316', '#f59e0b', '#10b981',
|
| 147 |
+
'#3b82f6', '#6366f1', '#8b5cf6', '#ec4899'
|
| 148 |
+
];
|
| 149 |
+
return colors[Math.floor(Math.random() * colors.length)];
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
// Start game
|
| 153 |
+
let gameInterval = setInterval(gameLoop, 1000/speed);
|
| 154 |
+
|
| 155 |
+
// Responsive canvas
|
| 156 |
+
window.addEventListener('resize', () => {
|
| 157 |
+
canvas.width = canvas.parentElement.clientWidth;
|
| 158 |
+
canvas.height = canvas.parentElement.clientHeight;
|
| 159 |
+
});
|
| 160 |
+
});
|
style.css
CHANGED
|
@@ -1,28 +1,93 @@
|
|
| 1 |
body {
|
| 2 |
-
|
| 3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
}
|
| 5 |
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
|
|
|
| 9 |
}
|
| 10 |
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
margin-top: 5px;
|
| 16 |
}
|
| 17 |
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
}
|
| 25 |
|
| 26 |
-
|
| 27 |
-
|
|
|
|
|
|
|
|
|
|
| 28 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
body {
|
| 2 |
+
margin: 0;
|
| 3 |
+
padding: 0;
|
| 4 |
+
overflow: hidden;
|
| 5 |
+
font-family: 'Press Start 2P', cursive;
|
| 6 |
+
background-color: #0f0f1a;
|
| 7 |
+
color: white;
|
| 8 |
}
|
| 9 |
|
| 10 |
+
#game-container {
|
| 11 |
+
position: relative;
|
| 12 |
+
width: 100vw;
|
| 13 |
+
height: 100vh;
|
| 14 |
}
|
| 15 |
|
| 16 |
+
#gameCanvas {
|
| 17 |
+
display: block;
|
| 18 |
+
width: 100%;
|
| 19 |
+
height: 100%;
|
|
|
|
| 20 |
}
|
| 21 |
|
| 22 |
+
#game-ui {
|
| 23 |
+
position: absolute;
|
| 24 |
+
top: 0;
|
| 25 |
+
left: 0;
|
| 26 |
+
padding: 1rem;
|
| 27 |
+
pointer-events: none;
|
| 28 |
}
|
| 29 |
|
| 30 |
+
#score-display {
|
| 31 |
+
font-size: 1.2rem;
|
| 32 |
+
margin-bottom: 1rem;
|
| 33 |
+
color: #ff6b6b;
|
| 34 |
+
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
|
| 35 |
}
|
| 36 |
+
|
| 37 |
+
#leaderboard {
|
| 38 |
+
background: rgba(26, 26, 46, 0.8);
|
| 39 |
+
padding: 1rem;
|
| 40 |
+
border-radius: 5px;
|
| 41 |
+
max-width: 200px;
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
#leaderboard h3 {
|
| 45 |
+
margin: 0 0 0.5rem 0;
|
| 46 |
+
font-size: 0.8rem;
|
| 47 |
+
color: #ff6b6b;
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
.player-row {
|
| 51 |
+
display: flex;
|
| 52 |
+
justify-content: space-between;
|
| 53 |
+
margin-bottom: 0.3rem;
|
| 54 |
+
font-size: 0.7rem;
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
.player-row.you {
|
| 58 |
+
color: #69f0ae;
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
.wormate-btn {
|
| 62 |
+
position: absolute;
|
| 63 |
+
top: 50%;
|
| 64 |
+
left: 50%;
|
| 65 |
+
transform: translate(-50%, -50%);
|
| 66 |
+
padding: 1rem 2rem;
|
| 67 |
+
font-family: 'Press Start 2P', cursive;
|
| 68 |
+
font-size: 1rem;
|
| 69 |
+
background: #ff6b6b;
|
| 70 |
+
color: white;
|
| 71 |
+
border: none;
|
| 72 |
+
border-radius: 5px;
|
| 73 |
+
cursor: pointer;
|
| 74 |
+
pointer-events: auto;
|
| 75 |
+
transition: all 0.3s;
|
| 76 |
+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
.wormate-btn:hover {
|
| 80 |
+
background: #ff5252;
|
| 81 |
+
transform: translate(-50%, -50%) scale(1.05);
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
/* Animations */
|
| 85 |
+
@keyframes pulse {
|
| 86 |
+
0% { transform: scale(1); }
|
| 87 |
+
50% { transform: scale(1.1); }
|
| 88 |
+
100% { transform: scale(1); }
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
.pulse {
|
| 92 |
+
animation: pulse 1s infinite;
|
| 93 |
+
}
|
wormate.js
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Wormate.io clone game logic
|
| 2 |
+
const socket = io('https://wormate.io'); // Connect to wormate.io server
|
| 3 |
+
|
| 4 |
+
const canvas = document.getElementById('gameCanvas');
|
| 5 |
+
const ctx = canvas.getContext('2d');
|
| 6 |
+
const scoreDisplay = document.getElementById('score-display');
|
| 7 |
+
const leaderboard = document.getElementById('leaderboard');
|
| 8 |
+
const playBtn = document.getElementById('play-btn');
|
| 9 |
+
|
| 10 |
+
// Set canvas to full window size
|
| 11 |
+
function resizeCanvas() {
|
| 12 |
+
canvas.width = window.innerWidth;
|
| 13 |
+
canvas.height = window.innerHeight;
|
| 14 |
+
}
|
| 15 |
+
window.addEventListener('resize', resizeCanvas);
|
| 16 |
+
resizeCanvas();
|
| 17 |
+
|
| 18 |
+
// Game state
|
| 19 |
+
let gameStarted = false;
|
| 20 |
+
let playerId = null;
|
| 21 |
+
let players = {};
|
| 22 |
+
let foods = [];
|
| 23 |
+
let powerUps = [];
|
| 24 |
+
let score = 0;
|
| 25 |
+
|
| 26 |
+
// Player controls
|
| 27 |
+
const keys = {
|
| 28 |
+
ArrowUp: false,
|
| 29 |
+
ArrowDown: false,
|
| 30 |
+
ArrowLeft: false,
|
| 31 |
+
ArrowRight: false
|
| 32 |
+
};
|
| 33 |
+
|
| 34 |
+
// Handle keyboard input
|
| 35 |
+
window.addEventListener('keydown', (e) => {
|
| 36 |
+
if (keys.hasOwnProperty(e.key)) {
|
| 37 |
+
keys[e.key] = true;
|
| 38 |
+
e.preventDefault();
|
| 39 |
+
}
|
| 40 |
+
});
|
| 41 |
+
|
| 42 |
+
window.addEventListener('keyup', (e) => {
|
| 43 |
+
if (keys.hasOwnProperty(e.key)) {
|
| 44 |
+
keys[e.key] = false;
|
| 45 |
+
e.preventDefault();
|
| 46 |
+
}
|
| 47 |
+
});
|
| 48 |
+
|
| 49 |
+
// Socket event handlers
|
| 50 |
+
socket.on('connect', () => {
|
| 51 |
+
playerId = socket.id;
|
| 52 |
+
console.log('Connected to server with ID:', playerId);
|
| 53 |
+
});
|
| 54 |
+
|
| 55 |
+
socket.on('gameState', (gameState) => {
|
| 56 |
+
players = gameState.players;
|
| 57 |
+
foods = gameState.foods;
|
| 58 |
+
powerUps = gameState.powerUps;
|
| 59 |
+
|
| 60 |
+
if (players[playerId]) {
|
| 61 |
+
score = players[playerId].score;
|
| 62 |
+
scoreDisplay.textContent = `Score: ${score}`;
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
updateLeaderboard();
|
| 66 |
+
renderGame();
|
| 67 |
+
});
|
| 68 |
+
|
| 69 |
+
socket.on('playerDied', (data) => {
|
| 70 |
+
if (data.playerId === playerId) {
|
| 71 |
+
gameStarted = false;
|
| 72 |
+
playBtn.style.display = 'block';
|
| 73 |
+
alert(`Game Over! Your score: ${score}`);
|
| 74 |
+
}
|
| 75 |
+
});
|
| 76 |
+
|
| 77 |
+
// Game loop
|
| 78 |
+
function gameLoop() {
|
| 79 |
+
if (gameStarted) {
|
| 80 |
+
const direction = getDirection();
|
| 81 |
+
socket.emit('playerInput', direction);
|
| 82 |
+
}
|
| 83 |
+
requestAnimationFrame(gameLoop);
|
| 84 |
+
}
|
| 85 |
+
gameLoop();
|
| 86 |
+
|
| 87 |
+
// Helper functions
|
| 88 |
+
function getDirection() {
|
| 89 |
+
if (keys.ArrowUp) return 'up';
|
| 90 |
+
if (keys.ArrowDown) return 'down';
|
| 91 |
+
if (keys.ArrowLeft) return 'left';
|
| 92 |
+
if (keys.ArrowRight) return 'right';
|
| 93 |
+
return null;
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
function updateLeaderboard() {
|
| 97 |
+
const sortedPlayers = Object.values(players).sort((a, b) => b.score - a.score);
|
| 98 |
+
leaderboard.innerHTML = '<h3>Leaderboard</h3>' +
|
| 99 |
+
sortedPlayers.map(p =>
|
| 100 |
+
`<div class="player-row ${p.id === playerId ? 'you' : ''}">
|
| 101 |
+
<span class="player-name">${p.name}</span>
|
| 102 |
+
<span class="player-score">${p.score}</span>
|
| 103 |
+
</div>`
|
| 104 |
+
).join('');
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
function renderGame() {
|
| 108 |
+
// Clear canvas
|
| 109 |
+
ctx.fillStyle = '#1a1a2e';
|
| 110 |
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
| 111 |
+
|
| 112 |
+
// Draw foods
|
| 113 |
+
foods.forEach(food => {
|
| 114 |
+
ctx.fillStyle = food.color;
|
| 115 |
+
ctx.beginPath();
|
| 116 |
+
ctx.arc(food.x, food.y, food.radius, 0, Math.PI * 2);
|
| 117 |
+
ctx.fill();
|
| 118 |
+
});
|
| 119 |
+
|
| 120 |
+
// Draw power-ups
|
| 121 |
+
powerUps.forEach(powerUp => {
|
| 122 |
+
ctx.save();
|
| 123 |
+
ctx.translate(powerUp.x, powerUp.y);
|
| 124 |
+
ctx.rotate(Date.now() / 200);
|
| 125 |
+
ctx.fillStyle = powerUp.color;
|
| 126 |
+
ctx.fillRect(-powerUp.size/2, -powerUp.size/2, powerUp.size, powerUp.size);
|
| 127 |
+
ctx.restore();
|
| 128 |
+
});
|
| 129 |
+
|
| 130 |
+
// Draw players
|
| 131 |
+
Object.values(players).forEach(player => {
|
| 132 |
+
// Draw worm segments
|
| 133 |
+
player.segments.forEach((segment, i) => {
|
| 134 |
+
const gradient = ctx.createRadialGradient(
|
| 135 |
+
segment.x, segment.y, 0,
|
| 136 |
+
segment.x, segment.y, segment.radius
|
| 137 |
+
);
|
| 138 |
+
gradient.addColorStop(0, player.color);
|
| 139 |
+
gradient.addColorStop(1, darkenColor(player.color, 0.3));
|
| 140 |
+
|
| 141 |
+
ctx.fillStyle = gradient;
|
| 142 |
+
ctx.beginPath();
|
| 143 |
+
ctx.arc(segment.x, segment.y, segment.radius, 0, Math.PI * 2);
|
| 144 |
+
ctx.fill();
|
| 145 |
+
|
| 146 |
+
// Draw eyes on head
|
| 147 |
+
if (i === 0) {
|
| 148 |
+
const angle = Math.atan2(
|
| 149 |
+
player.segments[1].y - segment.y,
|
| 150 |
+
player.segments[1].x - segment.x
|
| 151 |
+
);
|
| 152 |
+
|
| 153 |
+
const eyeRadius = segment.radius * 0.3;
|
| 154 |
+
const eyeOffset = segment.radius * 0.6;
|
| 155 |
+
|
| 156 |
+
// Left eye
|
| 157 |
+
ctx.fillStyle = 'white';
|
| 158 |
+
ctx.beginPath();
|
| 159 |
+
ctx.arc(
|
| 160 |
+
segment.x + Math.cos(angle + Math.PI/2) * eyeOffset,
|
| 161 |
+
segment.y + Math.sin(angle + Math.PI/2) * eyeOffset,
|
| 162 |
+
eyeRadius, 0, Math.PI * 2
|
| 163 |
+
);
|
| 164 |
+
ctx.fill();
|
| 165 |
+
|
| 166 |
+
// Right eye
|
| 167 |
+
ctx.beginPath();
|
| 168 |
+
ctx.arc(
|
| 169 |
+
segment.x + Math.cos(angle - Math.PI/2) * eyeOffset,
|
| 170 |
+
segment.y + Math.sin(angle - Math.PI/2) * eyeOffset,
|
| 171 |
+
eyeRadius, 0, Math.PI * 2
|
| 172 |
+
);
|
| 173 |
+
ctx.fill();
|
| 174 |
+
|
| 175 |
+
// Pupils
|
| 176 |
+
ctx.fillStyle = 'black';
|
| 177 |
+
ctx.beginPath();
|
| 178 |
+
ctx.arc(
|
| 179 |
+
segment.x + Math.cos(angle + Math.PI/2) * eyeOffset + Math.cos(angle) * eyeRadius/2,
|
| 180 |
+
segment.y + Math.sin(angle + Math.PI/2) * eyeOffset + Math.sin(angle) * eyeRadius/2,
|
| 181 |
+
eyeRadius/2, 0, Math.PI * 2
|
| 182 |
+
);
|
| 183 |
+
ctx.fill();
|
| 184 |
+
|
| 185 |
+
ctx.beginPath();
|
| 186 |
+
ctx.arc(
|
| 187 |
+
segment.x + Math.cos(angle - Math.PI/2) * eyeOffset + Math.cos(angle) * eyeRadius/2,
|
| 188 |
+
segment.y + Math.sin(angle - Math.PI/2) * eyeOffset + Math.sin(angle) * eyeRadius/2,
|
| 189 |
+
eyeRadius/2, 0, Math.PI * 2
|
| 190 |
+
);
|
| 191 |
+
ctx.fill();
|
| 192 |
+
}
|
| 193 |
+
});
|
| 194 |
+
|
| 195 |
+
// Draw player name
|
| 196 |
+
if (player.segments.length > 0) {
|
| 197 |
+
const head = player.segments[0];
|
| 198 |
+
ctx.fillStyle = 'white';
|
| 199 |
+
ctx.font = '12px "Press Start 2P", cursive';
|
| 200 |
+
ctx.textAlign = 'center';
|
| 201 |
+
ctx.fillText(player.name, head.x, head.y - head.radius - 10);
|
| 202 |
+
}
|
| 203 |
+
});
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
function darkenColor(color, amount) {
|
| 207 |
+
// Convert hex to RGB
|
| 208 |
+
let r = parseInt(color.substr(1, 2), 16);
|
| 209 |
+
let g = parseInt(color.substr(3, 2), 16);
|
| 210 |
+
let b = parseInt(color.substr(5, 2), 16);
|
| 211 |
+
|
| 212 |
+
// Darken each component
|
| 213 |
+
r = Math.max(0, Math.floor(r * (1 - amount)));
|
| 214 |
+
g = Math.max(0, Math.floor(g * (1 - amount)));
|
| 215 |
+
b = Math.max(0, Math.floor(b * (1 - amount)));
|
| 216 |
+
|
| 217 |
+
// Convert back to hex
|
| 218 |
+
return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`;
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
// Start game
|
| 222 |
+
playBtn.addEventListener('click', () => {
|
| 223 |
+
const playerName = prompt('Enter your name:', 'Player' + Math.floor(Math.random() * 1000));
|
| 224 |
+
if (playerName) {
|
| 225 |
+
socket.emit('joinGame', {
|
| 226 |
+
name: playerName,
|
| 227 |
+
color: getRandomColor()
|
| 228 |
+
});
|
| 229 |
+
gameStarted = true;
|
| 230 |
+
playBtn.style.display = 'none';
|
| 231 |
+
}
|
| 232 |
+
});
|
| 233 |
+
|
| 234 |
+
function getRandomColor() {
|
| 235 |
+
const colors = [
|
| 236 |
+
'#FF5252', '#FF4081', '#E040FB', '#7C4DFF',
|
| 237 |
+
'#536DFE', '#448AFF', '#40C4FF', '#18FFFF',
|
| 238 |
+
'#64FFDA', '#69F0AE', '#B2FF59', '#EEFF41',
|
| 239 |
+
'#FFFF00', '#FFD740', '#FFAB40', '#FF6E40'
|
| 240 |
+
];
|
| 241 |
+
return colors[Math.floor(Math.random() * colors.length)];
|
| 242 |
+
}
|