Compare commits
No commits in common. "e934e3132fb44ca6524d9ecc4f854674b6f9ea4a" and "35aad0589eff3b6ed14ab02d2eca2ddf83c2ac61" have entirely different histories.
e934e3132f
...
35aad0589e
1
.gitignore
vendored
|
|
@ -1 +0,0 @@
|
||||||
node_modules/
|
|
||||||
2
README.md
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
# pages
|
||||||
|
|
||||||
61
dropdown.js
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0
|
||||||
|
|
||||||
|
/*
|
||||||
|
* InterstellarDevelopment website
|
||||||
|
* Copyright (C) 2024 interstellar_development
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const menu = document.querySelector(".menu");
|
||||||
|
const burgerMenu = document.querySelector(".burger-menu");
|
||||||
|
|
||||||
|
if (!menu || !burgerMenu) {
|
||||||
|
console.warn(
|
||||||
|
"Menu or burger menu element not found. Ensure they exist in the DOM."
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle the menu visibility
|
||||||
|
function toggleMenu() {
|
||||||
|
menu.classList.toggle("active");
|
||||||
|
|
||||||
|
if (menu.classList.contains("active")) {
|
||||||
|
// Add click listener to close menu when clicking outside
|
||||||
|
document.addEventListener("click", closeMenu);
|
||||||
|
} else {
|
||||||
|
// Remove the click listener when menu is closed
|
||||||
|
document.removeEventListener("click", closeMenu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the menu if clicking outside of it
|
||||||
|
function closeMenu(event) {
|
||||||
|
if (
|
||||||
|
!menu.contains(event.target) &&
|
||||||
|
!event.target.classList.contains("burger-menu")
|
||||||
|
) {
|
||||||
|
menu.classList.remove("active");
|
||||||
|
document.removeEventListener("click", closeMenu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attach click event to the burger menu button
|
||||||
|
burgerMenu.addEventListener("click", (event) => {
|
||||||
|
event.stopPropagation(); // Prevent click from immediately triggering closeMenu
|
||||||
|
toggleMenu();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// @license-end
|
||||||
6
favicon_io/about.txt
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
This favicon was generated using the following graphics from Twitter Twemoji:
|
||||||
|
|
||||||
|
- Graphics Title: 1f680.svg
|
||||||
|
- Graphics Author: Copyright 2020 Twitter, Inc and other contributors (https://github.com/twitter/twemoji)
|
||||||
|
- Graphics Source: https://github.com/twitter/twemoji/blob/master/assets/svg/1f680.svg
|
||||||
|
- Graphics License: CC-BY 4.0 (https://creativecommons.org/licenses/by/4.0/)
|
||||||
BIN
favicon_io/android-chrome-192x192.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
favicon_io/android-chrome-512x512.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
favicon_io/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
favicon_io/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 751 B |
BIN
favicon_io/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
favicon_io/favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
1
favicon_io/site.webmanifest
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
|
||||||
68
footer.js
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0
|
||||||
|
|
||||||
|
/*
|
||||||
|
* interstellar_development website
|
||||||
|
* Copyright (C) 2024 interstellar_development
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Footer extends HTMLElement {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this.innerHTML = `
|
||||||
|
<center>
|
||||||
|
<footer>
|
||||||
|
<div class="footer-content">
|
||||||
|
<p>2024 Interstellar Development</p>
|
||||||
|
<!-- Hidden Button -->
|
||||||
|
<button class="secret-button">👀</button>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</center>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Add event listener for button click
|
||||||
|
this.querySelector(".secret-button").addEventListener("click", () => {
|
||||||
|
window.open("secret/index.html", "_blank");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("footer-component", Footer);
|
||||||
|
|
||||||
|
// CSS for the hidden button
|
||||||
|
const style = document.createElement("style");
|
||||||
|
style.textContent = `
|
||||||
|
.secret-button {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 10px;
|
||||||
|
right: 10px;
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
font-size: 24px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-content:hover .secret-button {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
document.head.appendChild(style);
|
||||||
|
|
||||||
|
// @license-end
|
||||||
46
header.js
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0
|
||||||
|
|
||||||
|
/*
|
||||||
|
* interstellar_development website
|
||||||
|
* Copyright (C) 2024 interstellar_development
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
class Header extends HTMLElement {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this.innerHTML = `
|
||||||
|
<header>
|
||||||
|
<div class="header-content">
|
||||||
|
<div><a href="index.html" class="project-name">Interstellar Development</a></div>
|
||||||
|
<button class="burger-menu" onclick="toggleMenu()">☰</button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div class="div-menu">
|
||||||
|
<ul class="menu">
|
||||||
|
<li><a href="index.html#project">Our Projects</a></li>
|
||||||
|
<li><a href="index.html#cards">Our Team</a></li>
|
||||||
|
<li><a href="index.html#about">About us</a></li>
|
||||||
|
<li><a href="index.html#vision">Our Vision</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("header-component", Header);
|
||||||
|
// @license-end
|
||||||
|
Before Width: | Height: | Size: 173 KiB After Width: | Height: | Size: 173 KiB |
BIN
images/nicolas.png
Normal file
|
After Width: | Height: | Size: 156 KiB |
|
Before Width: | Height: | Size: 385 KiB After Width: | Height: | Size: 385 KiB |
BIN
images/star.jpg
Normal file
|
After Width: | Height: | Size: 545 KiB |
647
index.html
|
|
@ -1,496 +1,233 @@
|
||||||
|
<!--
|
||||||
|
interstellar development website
|
||||||
|
Copyright (C) 2024 interstellar_development
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Interstellar Development | Free and Open Source Software</title>
|
<script src="dropdown.js" type="text/javascript" defer></script>
|
||||||
<meta
|
<script src="header.js" type="text/javascript" defer></script>
|
||||||
name="description"
|
<script src="footer.js" type="text/javascript" defer></script>
|
||||||
content="We develop high-quality free and open source software, with a focus on gaming solutions that respect user freedom."
|
<link rel="stylesheet" href="styles.css" />
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Font Awesome -->
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
|
|
||||||
/>
|
|
||||||
<!-- Google Fonts -->
|
|
||||||
<link
|
|
||||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap"
|
|
||||||
rel="stylesheet"
|
|
||||||
/>
|
|
||||||
<!-- Main CSS -->
|
|
||||||
<link rel="stylesheet" href="src/styles/styles.css" />
|
|
||||||
|
|
||||||
<link
|
<link
|
||||||
rel="apple-touch-icon"
|
rel="apple-touch-icon"
|
||||||
sizes="180x180"
|
sizes="180x180"
|
||||||
href="src/favicon/apple-touch-icon.png"
|
href="favicon_io/apple-touch-icon.png"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="icon"
|
rel="icon"
|
||||||
type="image/png"
|
type="image/png"
|
||||||
sizes="32x32"
|
sizes="32x32"
|
||||||
href="src/favicon/favicon-32x32.png"
|
href="favicon_io/favicon-32x32.png"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="icon"
|
rel="icon"
|
||||||
type="image/png"
|
type="image/png"
|
||||||
sizes="16x16"
|
sizes="16x16"
|
||||||
href="src/favicon/favicon-16x16.png"
|
href="favicon_io/favicon-16x16.png"
|
||||||
/>
|
/>
|
||||||
|
<link rel="manifest" href="favicon_io/site.webmanifest" />
|
||||||
|
<title>Interstellar Development</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<!-- Animated background -->
|
<!-- Custom header component -->
|
||||||
<div class="animated-bg"></div>
|
<header-component></header-component>
|
||||||
<div class="stars" id="stars"></div>
|
|
||||||
|
|
||||||
<!-- Navigation -->
|
<article>
|
||||||
<nav id="navigation">
|
<section id="project">
|
||||||
<div class="nav-container">
|
<h1>Our Projects</h1>
|
||||||
<div class="logo">
|
|
||||||
<a href="#welcome-screen">Interstellar Development</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button
|
<h2>Our Games</h2>
|
||||||
class="mobile-menu-btn"
|
|
||||||
id="burger-menu"
|
|
||||||
aria-label="Toggle navigation"
|
|
||||||
>
|
|
||||||
<i class="fas fa-bars"></i>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<ul class="nav-links" id="main-nav">
|
|
||||||
<li><a href="#projects">Projects</a></li>
|
|
||||||
<li><a href="#team">Team</a></li>
|
|
||||||
<li><a href="#about">About</a></li>
|
|
||||||
<li><a href="#vision">Vision</a></li>
|
|
||||||
<li><a href="#contact">Contact</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<!-- Hero Section -->
|
|
||||||
<section class="hero" id="welcome-screen">
|
|
||||||
<div class="container">
|
|
||||||
<div class="hero-content">
|
|
||||||
<h1>
|
|
||||||
Advancing <span class="highlight">Free Software</span> Development
|
|
||||||
</h1>
|
|
||||||
<p>
|
|
||||||
We develop high-quality free and open source software solutions,
|
|
||||||
with a focus on gaming and applications that respect user freedom.
|
|
||||||
</p>
|
|
||||||
<div class="hero-buttons">
|
|
||||||
<a href="#projects" class="btn btn-primary">
|
|
||||||
<i class="fas fa-rocket"></i>
|
|
||||||
View Our Projects
|
|
||||||
</a>
|
|
||||||
<a href="#about" class="btn btn-secondary">
|
|
||||||
<i class="fas fa-info-circle"></i>
|
|
||||||
Learn More
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Projects Section -->
|
|
||||||
<section id="projects" class="section">
|
|
||||||
<div class="container">
|
|
||||||
<div class="section-header">
|
|
||||||
<h2 class="section-title">Our Projects</h2>
|
|
||||||
<p class="section-subtitle">
|
|
||||||
High-quality free software solutions built with passion and
|
|
||||||
dedication
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Portfolio Filter Controls -->
|
|
||||||
<div class="portfolio-controls">
|
|
||||||
<div class="filter-buttons">
|
|
||||||
<button class="filter-btn active" data-filter="all">All</button>
|
|
||||||
<button class="filter-btn" data-filter="gaming">Gaming</button>
|
|
||||||
<button class="filter-btn" data-filter="software">
|
|
||||||
Software
|
|
||||||
</button>
|
|
||||||
<button class="filter-btn" data-filter="web">Web</button>
|
|
||||||
</div>
|
|
||||||
<div class="search-box">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="project-search"
|
|
||||||
placeholder="Search projects..."
|
|
||||||
/>
|
|
||||||
<i class="fas fa-search"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="portfolio-grid">
|
|
||||||
<!-- Gaming Projects -->
|
|
||||||
<div class="portfolio-grid">
|
|
||||||
<portfolio-card
|
|
||||||
icon="fas fa-rocket"
|
|
||||||
title="Projects Coming Soon"
|
|
||||||
desc="We're currently working on exciting new free and open source software projects. Stay tuned for updates as we develop high-quality solutions that respect user freedom."
|
|
||||||
tags="all"
|
|
||||||
collaboration="Internal Project"
|
|
||||||
></portfolio-card>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Team Section -->
|
|
||||||
<section id="team" class="section">
|
|
||||||
<div class="container">
|
|
||||||
<div class="section-header">
|
|
||||||
<h2 class="section-title">Our Team</h2>
|
|
||||||
<p class="section-subtitle">
|
|
||||||
A small but dedicated international team committed to free
|
|
||||||
software
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="team-grid">
|
|
||||||
<div class="team-member">
|
|
||||||
<div class="member-avatar">
|
|
||||||
<i class="fas fa-server"></i>
|
|
||||||
</div>
|
|
||||||
<h3>Patrick</h3>
|
|
||||||
<div class="team-role">
|
|
||||||
Lead Programmer & System Administrator
|
|
||||||
</div>
|
|
||||||
<p>
|
|
||||||
Patrick focuses on backend development and system
|
|
||||||
administration, managing our infrastructure and core application
|
|
||||||
logic with expertise in server-side technologies and DevOps.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="team-member">
|
|
||||||
<div class="member-avatar">
|
|
||||||
<i class="fas fa-palette"></i>
|
|
||||||
</div>
|
|
||||||
<h3>Sage</h3>
|
|
||||||
<div class="team-role">Frontend Developer & Communications</div>
|
|
||||||
<p>
|
|
||||||
Sage focuses on frontend development and is in charge of
|
|
||||||
communication and UI/UX design, ensuring our projects have
|
|
||||||
intuitive interfaces and excellent user experiences.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- About Section -->
|
|
||||||
<section id="about" class="section">
|
|
||||||
<div class="container">
|
|
||||||
<div class="section-header">
|
|
||||||
<h2 class="section-title">About Us</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="about-content">
|
|
||||||
<p>
|
|
||||||
Interstellar Development is a small international team dedicated
|
|
||||||
to advancing free and open source software. Our diverse
|
|
||||||
backgrounds and shared commitment to user freedom drive our
|
|
||||||
development philosophy.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
We began with a vision to create high-quality free software,
|
|
||||||
particularly in gaming where such options are often limited. We
|
|
||||||
recognized that many free software games either lack polish or
|
|
||||||
simply don't exist, inspiring us to focus on this under-served
|
|
||||||
area.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
At Interstellar Development, we believe true digital freedom can
|
|
||||||
only be achieved through free software. All our projects are
|
|
||||||
released under copyleft licenses, ensuring they remain free for
|
|
||||||
everyone to use, modify, and distribute.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
While we don't currently accept donations, we welcome feedback and
|
|
||||||
suggestions. If you wish to support the free software movement, we
|
|
||||||
encourage donations to the Free Software Foundation, whose work
|
|
||||||
continues to inspire our journey.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Vision Section -->
|
|
||||||
<section id="vision" class="section">
|
|
||||||
<div class="container">
|
|
||||||
<div class="section-header">
|
|
||||||
<h2 class="section-title">Our Vision</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="vision-content">
|
|
||||||
<div class="vision-text">
|
|
||||||
<p>
|
|
||||||
Our mission is to create high-quality free software
|
|
||||||
alternatives, particularly for games that are unavailable on
|
|
||||||
free operating systems like GNU/Linux and FreeBSD. We aim to
|
|
||||||
address gaps in the software ecosystem where free alternatives
|
|
||||||
are lacking.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="principles-grid">
|
|
||||||
<div class="principle-card">
|
|
||||||
<div class="principle-icon">
|
|
||||||
<i class="fas fa-unlock"></i>
|
|
||||||
</div>
|
|
||||||
<h4>Software Freedom</h4>
|
|
||||||
<p>
|
|
||||||
All our projects are released under copyleft licenses to
|
|
||||||
ensure user freedoms are protected and preserved.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="principle-card">
|
|
||||||
<div class="principle-icon">
|
|
||||||
<i class="fas fa-users"></i>
|
|
||||||
</div>
|
|
||||||
<h4>Community Collaboration</h4>
|
|
||||||
<p>
|
|
||||||
We believe in building software with and for the community,
|
|
||||||
welcoming contributions and feedback.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="principle-card">
|
|
||||||
<div class="principle-icon">
|
|
||||||
<i class="fas fa-star"></i>
|
|
||||||
</div>
|
|
||||||
<h4>Quality Standards</h4>
|
|
||||||
<p>
|
|
||||||
We strive to create software that matches or exceeds the
|
|
||||||
quality of proprietary alternatives.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Stats Section -->
|
|
||||||
<section class="stats-section">
|
|
||||||
<div class="container">
|
|
||||||
<div class="stats-container">
|
|
||||||
<div class="stat-item">
|
|
||||||
<div class="stat-number" data-count="0">0</div>
|
|
||||||
<div class="stat-label">Projects</div>
|
|
||||||
</div>
|
|
||||||
<div class="stat-item">
|
|
||||||
<div class="stat-number" data-count="2">0</div>
|
|
||||||
<div class="stat-label">Team Members</div>
|
|
||||||
</div>
|
|
||||||
<div class="stat-item">
|
|
||||||
<div class="stat-number" data-count="3">0</div>
|
|
||||||
<div class="stat-label">Years Active</div>
|
|
||||||
</div>
|
|
||||||
<div class="stat-item">
|
|
||||||
<div class="stat-number" data-count="100">0</div>
|
|
||||||
<div class="stat-label">% Free Software</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Contact Section -->
|
|
||||||
<section id="contact" class="section">
|
|
||||||
<div class="container">
|
|
||||||
<div class="section-header">
|
|
||||||
<h2 class="section-title">Get In Touch</h2>
|
|
||||||
<p class="section-subtitle">
|
|
||||||
Have questions about our projects or want to contribute? We'd love
|
|
||||||
to hear from you.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="contact-container">
|
|
||||||
<div class="contact-info">
|
|
||||||
<div class="contact-methods">
|
|
||||||
<div class="contact-item">
|
|
||||||
<div class="contact-icon">
|
|
||||||
<i class="fas fa-envelope"></i>
|
|
||||||
</div>
|
|
||||||
<div class="contact-details">
|
|
||||||
<h4>Email</h4>
|
|
||||||
<a href="mailto:info@interstellardevelopment.org"
|
|
||||||
>info@interstellardevelopment.org</a
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="contact-item">
|
|
||||||
<div class="contact-icon">
|
|
||||||
<i class="fas fa-code-branch"></i>
|
|
||||||
</div>
|
|
||||||
<div class="contact-details">
|
|
||||||
<h4>Source Code</h4>
|
|
||||||
<a
|
|
||||||
href="https://interstellardevelopment.org/code/interstellar_development"
|
|
||||||
target="_blank"
|
|
||||||
>View Our Projects</a
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form class="contact-form" id="contact-form">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="name">Your Name</label>
|
|
||||||
<input type="text" id="name" name="name" required />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="email">Email Address</label>
|
|
||||||
<input type="email" id="email" name="email" required />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="subject">Subject</label>
|
|
||||||
<select id="subject" name="subject" required>
|
|
||||||
<option value="">Select a subject</option>
|
|
||||||
<option value="contribution">Project Contribution</option>
|
|
||||||
<option value="feedback">Feedback</option>
|
|
||||||
<option value="inquiry">General Inquiry</option>
|
|
||||||
<option value="bug">Bug Report</option>
|
|
||||||
<option value="other">Other</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="message">Message</label>
|
|
||||||
<textarea
|
|
||||||
id="message"
|
|
||||||
name="message"
|
|
||||||
rows="5"
|
|
||||||
required
|
|
||||||
></textarea>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary">
|
|
||||||
<i class="fas fa-paper-plane"></i>
|
|
||||||
Send Message
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Footer -->
|
|
||||||
<footer class="footer">
|
|
||||||
<div class="container">
|
|
||||||
<div class="footer-content">
|
|
||||||
<div class="footer-brand">
|
|
||||||
<div class="logo">Interstellar Development</div>
|
|
||||||
<p>
|
|
||||||
Developing free and open source software with a focus on user
|
|
||||||
freedom and quality.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="footer-social">
|
|
||||||
<a
|
|
||||||
href="https://interstellardevelopment.org/code/interstellar_development"
|
|
||||||
class="social-link"
|
|
||||||
target="_blank"
|
|
||||||
aria-label="Source Code"
|
|
||||||
>
|
|
||||||
<i class="fas fa-code-branch"></i>
|
|
||||||
</a>
|
|
||||||
<a href="#" class="social-link" aria-label="Discord">
|
|
||||||
<i class="fab fa-discord"></i>
|
|
||||||
</a>
|
|
||||||
<a href="#" class="social-link" aria-label="Mastodon">
|
|
||||||
<i class="fab fa-mastodon"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="footer-links">
|
|
||||||
<h4>Navigation</h4>
|
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="#projects">Projects</a></li>
|
<a href="webGames/index.html" target="_blank" class="listElement">
|
||||||
<li><a href="#team">Team</a></li>
|
<li>Web game collection</li>
|
||||||
<li><a href="#about">About</a></li>
|
</a>
|
||||||
<li><a href="#vision">Vision</a></li>
|
|
||||||
<li><a href="#contact">Contact</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="footer-links">
|
|
||||||
<h4>Projects</h4>
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<a href="" target="_blank">Coming soon</a>
|
<p>Previously we had more unfinished Games listed here.</p>
|
||||||
|
<p>
|
||||||
|
We decided against displaying them and giving people the
|
||||||
|
impression we are working on them currently.
|
||||||
|
</p>
|
||||||
|
<p>In the Future we will display the released games here</p>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="footer-links">
|
<h2>Our Other Projects</h2>
|
||||||
<h4>Resources</h4>
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<a href="foss_alternatives/" target="_blank" class="listElement">
|
||||||
<a href="https://www.fsf.org/" target="_blank"
|
<li>FOSS Alternatives</li>
|
||||||
>Free Software Foundation</a
|
</a>
|
||||||
>
|
<a href="react/" target="_blank" class="listElement">
|
||||||
</li>
|
<li>React</li>
|
||||||
<li>
|
</a>
|
||||||
<a href="https://www.gnu.org/" target="_blank">GNU Project</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
href="https://interstellardevelopment.org/code/interstellar_development"
|
|
||||||
target="_blank"
|
|
||||||
>Source Code</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="mailto:info@interstellardevelopment.org"
|
|
||||||
>Contact Us</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</section>
|
||||||
|
|
||||||
|
<!-- Cards section with team members -->
|
||||||
|
<h1>Our Team</h1>
|
||||||
|
<section class="cards" id="cards">
|
||||||
|
<div class="card">
|
||||||
|
<a
|
||||||
|
href="https://interstellardevelopment.org/code/Patrick_Pluto"
|
||||||
|
class="card-link"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<img src="images/Patrick.png" alt="Patrick" />
|
||||||
|
<h3>Patrick_Pluto</h3>
|
||||||
|
<p>
|
||||||
|
The system administrator and our lead coder. He is the one you
|
||||||
|
will need to blame for bugs in the games
|
||||||
|
</p>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="footer-bottom">
|
<div class="card">
|
||||||
<div class="footer-bottom-content">
|
<a
|
||||||
<p>© 2025 Interstellar Development. All rights reserved.</p>
|
href="https://interstellardevelopment.org/code/sageTheDm"
|
||||||
<div class="footer-badges">
|
class="card-link"
|
||||||
<span class="badge">
|
target="_blank"
|
||||||
<i class="fas fa-heart"></i>
|
>
|
||||||
Committed to Free Software
|
<img src="images/sage.png" alt="Sage" />
|
||||||
</span>
|
<h3>sageTheDM</h3>
|
||||||
|
<p>
|
||||||
|
Our mostly competent web developer and secondary coder, if you
|
||||||
|
experience any bugs on the website or spelling mistake he is to
|
||||||
|
blame
|
||||||
|
</p>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<!-- General scripts -->
|
<div class="card">
|
||||||
<script src="src/js/main.js"></script>
|
<a
|
||||||
<script src="src/js/navigation.js"></script>
|
href="https://interstellardevelopment.org/code/Patrick_Pluto"
|
||||||
<script src="src/js/form.js"></script>
|
class="card-link"
|
||||||
<script src="src/js/portfolioCard.js"></script>
|
target="_blank"
|
||||||
<script src="src/js/portfolioFilter.js"></script>
|
>
|
||||||
<script src="src/js/overview.js"></script>
|
<img src="images/nicolas.png" alt="Patrick" />
|
||||||
<script src="src/js/sectionTracker.js"></script>
|
<h3>St. Nicolaus</h3>
|
||||||
|
<p>
|
||||||
|
Our game level and asset designer. He is responsible for all
|
||||||
|
assets in our FreeFTF game. So if an asset looks ugly be mad at
|
||||||
|
him. Also, we are not sure if he is human or just a drunk wizard
|
||||||
|
cat but one thing is very clear he is still a novice at his job.
|
||||||
|
</p>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<!-- Animation scripts -->
|
<!-- About Us section -->
|
||||||
<script type="module" src="src/js/animation/starBackground.js"></script>
|
<section id="about">
|
||||||
|
<h2>About Us</h2>
|
||||||
|
<p>
|
||||||
|
Welcome to Interstellar Development! We are a small, passionate
|
||||||
|
international team dedicated to transforming the programming world
|
||||||
|
into a free-and-open-source future. Our diverse backgrounds and
|
||||||
|
experiences fuel our commitment to creating free and open-source
|
||||||
|
software, particularly in the realm of gaming.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Our journey began with a shared vision: to better organize our efforts
|
||||||
|
in making free and open-source games more efficient and accessible. We
|
||||||
|
recognized that many current free software games are either lacking in
|
||||||
|
quality or simply do not exist. This realization inspired us to focus
|
||||||
|
on developing games that are unplayable on GNU/Linux and FreeBSD
|
||||||
|
systems, as well as creating free software alternatives for those in
|
||||||
|
need.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
At Interstellar Development, we believe that true freedom for computer
|
||||||
|
users can only be achieved through the use of free software. That’s
|
||||||
|
why we are committed to licensing all of our projects under copyleft
|
||||||
|
free and open-source software licenses, ensuring that our games remain
|
||||||
|
free for everyone to enjoy, forever.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
While we are not currently accepting donations, we welcome your
|
||||||
|
support in the form of feedback and suggestions for improvements. If
|
||||||
|
you wish to contribute financially, we encourage you to donate to the
|
||||||
|
Free Software Foundation, as without them, we would have never started
|
||||||
|
this.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Join us as we strive to create a vibrant community around free
|
||||||
|
software and gaming. Together, we can make a difference and pave the
|
||||||
|
way for a free future!
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="vision">
|
||||||
|
<h2>Our Vision</h2>
|
||||||
|
<h2>Interstellar Development: Free Software is Our Passion</h2>
|
||||||
|
<p>
|
||||||
|
At Interstellar Development, we embarked on this journey to create a
|
||||||
|
more organized and efficient approach to developing free and
|
||||||
|
open-source software, with a particular focus on gaming. We recognized
|
||||||
|
a significant gap in the availability and quality of free software
|
||||||
|
games, which often fail to meet the expectations of users or simply do
|
||||||
|
not exist. Our mission is to fill this void and elevate the standards
|
||||||
|
of free gaming experiences.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Our primary goal is to target games that are currently unplayable on
|
||||||
|
GNU/Linux and FreeBSD systems. We aim to develop high-quality
|
||||||
|
alternatives that not only provide enjoyable gameplay but also adhere
|
||||||
|
to the principles of free software. Additionally, we are committed to
|
||||||
|
identifying and addressing other areas within the software ecosystem
|
||||||
|
that lack free alternatives, ensuring that users have access to a
|
||||||
|
diverse range of tools and applications that respect their freedom.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
At Interstellar Development, we firmly believe that the path to true
|
||||||
|
freedom for computer users lies in the adoption and promotion of free
|
||||||
|
software. To this end, we are dedicated to licensing all of our
|
||||||
|
projects under copyleft free and open-source software licenses. This
|
||||||
|
commitment ensures that our games and software remain free for
|
||||||
|
everyone to play, modify, and share, fostering a culture of
|
||||||
|
collaboration and innovation within the community.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
We understand that community involvement is crucial to our success.
|
||||||
|
While we are not currently accepting donations, we invite you to
|
||||||
|
support us by providing feedback, reporting issues, and suggesting
|
||||||
|
improvements. Your insights are invaluable in helping us refine our
|
||||||
|
projects and enhance the user experience.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
If you feel compelled to contribute financially, we encourage you to
|
||||||
|
consider donating to the Free Software Foundation. Their unwavering
|
||||||
|
support and advocacy for free software principles have been
|
||||||
|
instrumental in our journey, and your contributions to them help
|
||||||
|
sustain the broader movement that enables us to create these games.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Together, we can build a vibrant community around free software and
|
||||||
|
gaming, paving the way for a future where everyone has access to
|
||||||
|
high-quality, open-source alternatives. Join us in our mission to make
|
||||||
|
a difference and champion the cause of free software for all!
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<!-- Custom footer component -->
|
||||||
|
<footer-component></footer-component>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
47
secret/asteroidDestroyer/explenation.html
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Game Landing Page</title>
|
||||||
|
<link rel="stylesheet" href="styles.css" />
|
||||||
|
<link
|
||||||
|
rel="apple-touch-icon"
|
||||||
|
sizes="180x180"
|
||||||
|
href="../../favicon_io/apple-touch-icon.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="32x32"
|
||||||
|
href="../../favicon_io/favicon-32x32.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="16x16"
|
||||||
|
href="../../favicon_io/favicon-16x16.png"
|
||||||
|
/>
|
||||||
|
<link rel="manifest" href="../../favicon_io/site.webmanifest" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="landing-page">
|
||||||
|
<div class="game-background">
|
||||||
|
<!-- Game is embedded or shown as background -->
|
||||||
|
<canvas id="gameCanvas"></canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
<h1>Welcome to the Asteroid Game!</h1>
|
||||||
|
<p>
|
||||||
|
In this game, you control a spaceship that shoots at asteroids to
|
||||||
|
avoid destruction and collect items for power-ups.
|
||||||
|
</p>
|
||||||
|
<p>Your goal is to survive as long as possible while scoring points!</p>
|
||||||
|
<button onclick="window.location.href='secret.html'">Play Game</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="app.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
414
secret/asteroidDestroyer/game.js
Normal file
|
|
@ -0,0 +1,414 @@
|
||||||
|
"use strict";
|
||||||
|
// Canvas setup
|
||||||
|
const canvas = document.getElementById("gameCanvas");
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
const targetFPS = 60;
|
||||||
|
const targetFrameTime = 1000 / targetFPS;
|
||||||
|
canvas.width = window.innerWidth;
|
||||||
|
canvas.height = window.innerHeight;
|
||||||
|
|
||||||
|
let lastFrameTime = performance.now();
|
||||||
|
|
||||||
|
// Game elements
|
||||||
|
const player = {
|
||||||
|
x: canvas.width / 2,
|
||||||
|
y: canvas.height - 60,
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
color: "white",
|
||||||
|
speed: 5,
|
||||||
|
dx: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let bullets = [];
|
||||||
|
let asteroids = [];
|
||||||
|
let items = [];
|
||||||
|
let score = 0;
|
||||||
|
let totalBulletsFired = 0;
|
||||||
|
let isGameOver = false;
|
||||||
|
let lastBulletTime = 0;
|
||||||
|
let ammo = 100; // Ammo counter
|
||||||
|
|
||||||
|
// Difficulty control
|
||||||
|
let asteroidSpawnInterval = 800; // Faster spawn rate
|
||||||
|
let asteroidSpeedMultiplier = 1.5; // Faster asteroids from the start
|
||||||
|
let difficultyIncreaseRate = 0.2; // Faster scaling every 10 seconds
|
||||||
|
|
||||||
|
// Controls
|
||||||
|
let canShoot = true; // Flag to control shooting
|
||||||
|
let rapidFireActive = false;
|
||||||
|
let shotgunActive = false;
|
||||||
|
let rainbowActive = false;
|
||||||
|
let lastShotgunTime = 0;
|
||||||
|
|
||||||
|
// Controls for sphere effects
|
||||||
|
let blueSphereCooldown = 0;
|
||||||
|
let yellowSphereCooldown = 0;
|
||||||
|
let greenSphereCooldown = 0;
|
||||||
|
let rainbowSphereCooldown = 0;
|
||||||
|
|
||||||
|
// Sphere types
|
||||||
|
const sphereTypes = ["blue", "yellow", "green", "rainbow"];
|
||||||
|
|
||||||
|
/// Control for left button press and release
|
||||||
|
function btnMoveLeft(isPressed) {
|
||||||
|
if (isPressed) {
|
||||||
|
player.dx = -player.speed; // Start moving left
|
||||||
|
} else {
|
||||||
|
player.dx = 0; // Stop moving when button is released
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Control for shoot button click (simulates spacebar press)
|
||||||
|
function btnShoot() {
|
||||||
|
if (canShoot && !isGameOver) {
|
||||||
|
shootBullet();
|
||||||
|
canShoot = false; // Prevent shooting until the button is "released"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Control for right button press and release
|
||||||
|
function btnMoveRight(isPressed) {
|
||||||
|
if (isPressed) {
|
||||||
|
player.dx = player.speed; // Start moving right
|
||||||
|
} else {
|
||||||
|
player.dx = 0; // Stop moving when button is released
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById("shootBtn").addEventListener("mouseup", () => {
|
||||||
|
canShoot = true; // Allow shooting again when button is released
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("keydown", (e) => {
|
||||||
|
if (e.key === "ArrowLeft" || e.key === "a") player.dx = -player.speed;
|
||||||
|
if (e.key === "ArrowRight" || e.key === "d") player.dx = player.speed;
|
||||||
|
|
||||||
|
// Shoot only if it's not a hold, and we can shoot
|
||||||
|
if (e.key === " " && canShoot && !isGameOver) {
|
||||||
|
shootBullet();
|
||||||
|
canShoot = false; // Prevent shooting until the key is released
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.key === "r" && isGameOver) restartGame();
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("keyup", (e) => {
|
||||||
|
// Stop moving when either the arrow keys or the 'a'/'d' keys are released
|
||||||
|
if (
|
||||||
|
e.key === "ArrowLeft" ||
|
||||||
|
e.key === "ArrowRight" ||
|
||||||
|
e.key === "a" ||
|
||||||
|
e.key === "d"
|
||||||
|
) {
|
||||||
|
player.dx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow shooting again when the space key is released
|
||||||
|
if (e.key === " ") {
|
||||||
|
canShoot = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Bullet mechanics with cooldown
|
||||||
|
function shootBullet() {
|
||||||
|
const now = Date.now();
|
||||||
|
if (now - lastBulletTime < 100) return; // Enforce cooldown of 0.1 seconds
|
||||||
|
if (ammo <= 0) return; // Prevent shooting if ammo is empty
|
||||||
|
lastBulletTime = now;
|
||||||
|
ammo--; // Decrease ammo
|
||||||
|
|
||||||
|
totalBulletsFired++; // Increment total bullets fired
|
||||||
|
|
||||||
|
if (rapidFireActive) {
|
||||||
|
// If rapid fire is active, fire bullets continuously with a short delay
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
setTimeout(() => {
|
||||||
|
bullets.push({
|
||||||
|
x: player.x + player.width / 2 - 2.5,
|
||||||
|
y: player.y,
|
||||||
|
width: 5,
|
||||||
|
height: 10,
|
||||||
|
color: "yellow",
|
||||||
|
speed: 7,
|
||||||
|
});
|
||||||
|
}, i * 50); // Fire bullets with 50ms delay between shots
|
||||||
|
}
|
||||||
|
} else if (shotgunActive) {
|
||||||
|
// Shotgun effect, firing 3 bullets with a spread
|
||||||
|
for (let i = -1; i <= 1; i++) {
|
||||||
|
bullets.push({
|
||||||
|
x: player.x + player.width / 2 - 2.5,
|
||||||
|
y: player.y,
|
||||||
|
width: 5,
|
||||||
|
height: 10,
|
||||||
|
color: "yellow",
|
||||||
|
speed: 7,
|
||||||
|
angle: i * 10, // Spray the bullets at different angles
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Normal bullet
|
||||||
|
bullets.push({
|
||||||
|
x: player.x + player.width / 2 - 2.5,
|
||||||
|
y: player.y,
|
||||||
|
width: 5,
|
||||||
|
height: 10,
|
||||||
|
color: "yellow",
|
||||||
|
speed: 7,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate random color
|
||||||
|
function getRandomColor() {
|
||||||
|
const colors = ["red", "blue", "green", "orange", "purple", "pink"];
|
||||||
|
return colors[Math.floor(Math.random() * colors.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Asteroid mechanics
|
||||||
|
function createAsteroid() {
|
||||||
|
const size = Math.random() * 40 + 30; // Bigger asteroids (min: 30, max: 70)
|
||||||
|
asteroids.push({
|
||||||
|
x: Math.random() * canvas.width,
|
||||||
|
y: -size,
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
color: getRandomColor(),
|
||||||
|
speed: (Math.random() * 3 + 2) * asteroidSpeedMultiplier, // Faster initial speed
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Item mechanics
|
||||||
|
function createItem() {
|
||||||
|
const randomType =
|
||||||
|
sphereTypes[Math.floor(Math.random() * sphereTypes.length)];
|
||||||
|
const size = 30;
|
||||||
|
const x = Math.random() * canvas.width;
|
||||||
|
items.push({
|
||||||
|
x: x,
|
||||||
|
y: -size,
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
type: randomType,
|
||||||
|
color:
|
||||||
|
randomType === "blue"
|
||||||
|
? "blue"
|
||||||
|
: randomType === "yellow"
|
||||||
|
? "yellow"
|
||||||
|
: randomType === "green"
|
||||||
|
? "green"
|
||||||
|
: "rainbow",
|
||||||
|
speed: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update game elements
|
||||||
|
function updatePlayer() {
|
||||||
|
player.x += player.dx;
|
||||||
|
if (player.x < 0) player.x = 0;
|
||||||
|
if (player.x + player.width > canvas.width)
|
||||||
|
player.x = canvas.width - player.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateBullets() {
|
||||||
|
bullets.forEach((bullet, index) => {
|
||||||
|
bullet.y -= bullet.speed;
|
||||||
|
if (bullet.y + bullet.height < 0) bullets.splice(index, 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateAsteroids() {
|
||||||
|
asteroids.forEach((asteroid, index) => {
|
||||||
|
asteroid.y += asteroid.speed;
|
||||||
|
if (asteroid.y > canvas.height) asteroids.splice(index, 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateItems() {
|
||||||
|
items.forEach((item, index) => {
|
||||||
|
item.y += item.speed;
|
||||||
|
if (item.y > canvas.height) items.splice(index, 1);
|
||||||
|
// Check if player collects the item
|
||||||
|
if (
|
||||||
|
player.x < item.x + item.width &&
|
||||||
|
player.x + player.width > item.x &&
|
||||||
|
player.y < item.y + item.height &&
|
||||||
|
player.y + player.height > item.y
|
||||||
|
) {
|
||||||
|
applyItemEffect(item.type);
|
||||||
|
items.splice(index, 1); // Remove the item after it is collected
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyItemEffect(type) {
|
||||||
|
let points = Math.floor(Math.random() * 5) + 1; // Random points between 1 and 5
|
||||||
|
if (type === "blue") {
|
||||||
|
rapidFireActive = true;
|
||||||
|
setTimeout(() => (rapidFireActive = false), 15000); // 15 seconds of rapid fire
|
||||||
|
} else if (type === "yellow") {
|
||||||
|
shotgunActive = true;
|
||||||
|
setTimeout(() => (shotgunActive = false), 30000); // 30 seconds of shotgun
|
||||||
|
} else if (type === "green") {
|
||||||
|
ammo = 100; // Refill ammo
|
||||||
|
} else if (type === "rainbow") {
|
||||||
|
rapidFireActive = true;
|
||||||
|
shotgunActive = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
rapidFireActive = false;
|
||||||
|
shotgunActive = false;
|
||||||
|
}, 15000); // 15 seconds with all effects
|
||||||
|
}
|
||||||
|
score += points; // Add points when an item is collected
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collision detection
|
||||||
|
function checkCollisions() {
|
||||||
|
bullets.forEach((bullet, bIndex) => {
|
||||||
|
asteroids.forEach((asteroid, aIndex) => {
|
||||||
|
if (
|
||||||
|
bullet.x < asteroid.x + asteroid.width &&
|
||||||
|
bullet.x + bullet.width > asteroid.x &&
|
||||||
|
bullet.y < asteroid.y + asteroid.height &&
|
||||||
|
bullet.y + bullet.height > asteroid.y
|
||||||
|
) {
|
||||||
|
bullets.splice(bIndex, 1);
|
||||||
|
asteroids.splice(aIndex, 1);
|
||||||
|
score += Math.floor(Math.random() * 5) + 1; // Add points when an asteroid is destroyed
|
||||||
|
// Visual feedback for destroyed asteroid
|
||||||
|
createExplosion(asteroid.x, asteroid.y);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
asteroids.forEach((asteroid) => {
|
||||||
|
if (
|
||||||
|
player.x < asteroid.x + asteroid.width &&
|
||||||
|
player.x + player.width > asteroid.x &&
|
||||||
|
player.y < asteroid.y + asteroid.height &&
|
||||||
|
player.y + player.height > asteroid.y
|
||||||
|
) {
|
||||||
|
isGameOver = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Explosion effect
|
||||||
|
function createExplosion(x, y) {
|
||||||
|
ctx.fillStyle = "yellow";
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(x, y, 20, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
setTimeout(() => ctx.clearRect(x - 20, y - 20, 40, 40), 200); // Clear explosion after 200ms
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw elements
|
||||||
|
function drawPlayer() {
|
||||||
|
ctx.fillStyle = player.color;
|
||||||
|
ctx.fillRect(player.x, player.y, player.width, player.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawBullets() {
|
||||||
|
bullets.forEach((bullet) => {
|
||||||
|
ctx.fillStyle = bullet.color;
|
||||||
|
ctx.fillRect(bullet.x, bullet.y, bullet.width, bullet.height);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawAsteroids() {
|
||||||
|
asteroids.forEach((asteroid) => {
|
||||||
|
ctx.fillStyle = asteroid.color;
|
||||||
|
ctx.fillRect(asteroid.x, asteroid.y, asteroid.width, asteroid.height);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawItems() {
|
||||||
|
items.forEach((item) => {
|
||||||
|
ctx.fillStyle = item.color;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(
|
||||||
|
item.x + item.width / 2,
|
||||||
|
item.y + item.height / 2,
|
||||||
|
item.width / 2,
|
||||||
|
0,
|
||||||
|
Math.PI * 2
|
||||||
|
);
|
||||||
|
ctx.fill();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawScore() {
|
||||||
|
ctx.fillStyle = "white";
|
||||||
|
ctx.font = "24px Arial";
|
||||||
|
ctx.fillText(`Score: ${score}`, 20, 40); // Score at top-left corner
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawAmmo() {
|
||||||
|
ctx.fillStyle = "white";
|
||||||
|
ctx.font = "24px Arial";
|
||||||
|
ctx.fillText(`Ammo: ${ammo}`, 20, 70); // Ammo at top-left corner
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawGameOver() {
|
||||||
|
if (isGameOver) {
|
||||||
|
ctx.fillStyle = "white";
|
||||||
|
ctx.font = "40px Arial";
|
||||||
|
ctx.textAlign = "center";
|
||||||
|
ctx.fillText("Game Over!", canvas.width / 2, canvas.height / 2 - 40);
|
||||||
|
ctx.font = "24px Arial";
|
||||||
|
ctx.fillText(`Total Score: ${score}`, canvas.width / 2, canvas.height / 2);
|
||||||
|
ctx.fillText(
|
||||||
|
`Bullets Fired: ${totalBulletsFired}`,
|
||||||
|
canvas.width / 2,
|
||||||
|
canvas.height / 2 + 40
|
||||||
|
);
|
||||||
|
ctx.fillText(
|
||||||
|
'Press "R" to Restart',
|
||||||
|
canvas.width / 2,
|
||||||
|
canvas.height / 2 + 80
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restart game
|
||||||
|
function restartGame() {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main game loop
|
||||||
|
function gameLoop() {
|
||||||
|
const currentTime = performance.now();
|
||||||
|
const elapsedTime = currentTime - lastFrameTime;
|
||||||
|
|
||||||
|
if (elapsedTime >= targetFrameTime) {
|
||||||
|
lastFrameTime = currentTime - (elapsedTime % targetFrameTime);
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
if (!isGameOver) {
|
||||||
|
updatePlayer();
|
||||||
|
updateBullets();
|
||||||
|
updateAsteroids();
|
||||||
|
updateItems();
|
||||||
|
checkCollisions();
|
||||||
|
}
|
||||||
|
|
||||||
|
drawPlayer();
|
||||||
|
drawBullets();
|
||||||
|
drawAsteroids();
|
||||||
|
drawItems();
|
||||||
|
drawScore();
|
||||||
|
drawAmmo();
|
||||||
|
drawGameOver();
|
||||||
|
|
||||||
|
if (!isGameOver) {
|
||||||
|
if (Math.random() < 0.01) createAsteroid(); // 1% chance every frame to spawn an asteroid
|
||||||
|
if (Math.random() < 0.005) createItem(); // 0.5% chance to spawn an item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
requestAnimationFrame(gameLoop);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start game loop
|
||||||
|
requestAnimationFrame(gameLoop);
|
||||||
41
secret/asteroidDestroyer/secret.html
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Asteroid Shooter</title>
|
||||||
|
<link rel="stylesheet" href="style.css" />
|
||||||
|
<link
|
||||||
|
rel="apple-touch-icon"
|
||||||
|
sizes="180x180"
|
||||||
|
href="../../favicon_io/apple-touch-icon.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="32x32"
|
||||||
|
href="../../favicon_io/favicon-32x32.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="16x16"
|
||||||
|
href="../../favicon_io/favicon-16x16.png"
|
||||||
|
/>
|
||||||
|
<link rel="manifest" href="../../favicon_io/site.webmanifest" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<canvas id="gameCanvas"></canvas>
|
||||||
|
|
||||||
|
<!-- Virtual buttons for mobile -->
|
||||||
|
<div class="controls">
|
||||||
|
<button id="leftBtn" class="control-btn">Left</button>
|
||||||
|
<button id="shootBtn" class="control-btn" onclick="btnShoot()">
|
||||||
|
Shoot
|
||||||
|
</button>
|
||||||
|
<button id="rightBtn" class="control-btn">Right</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="game.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
43
secret/asteroidDestroyer/style.css
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
display: block;
|
||||||
|
background: black;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 20px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-btn {
|
||||||
|
display: none;
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 18px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.6);
|
||||||
|
color: white;
|
||||||
|
border: 1px solid #fff;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
flex-grow: 1;
|
||||||
|
margin: 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.control-btn {
|
||||||
|
display: block;
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
76
secret/asteroidDestroyer/styles.css
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body,
|
||||||
|
html {
|
||||||
|
height: 100%;
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #000;
|
||||||
|
color: white;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.landing-page {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.game-background {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.7); /* Dark overlay */
|
||||||
|
backdrop-filter: blur(8px); /* Apply blur effect to the background */
|
||||||
|
z-index: -1; /* Ensures it's in the background */
|
||||||
|
pointer-events: none; /* Prevent interaction with the blurred background */
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
text-align: center;
|
||||||
|
z-index: 1; /* Ensure content appears above the game background */
|
||||||
|
padding: 20px;
|
||||||
|
max-width: 600px; /* Limit the width of the content */
|
||||||
|
position: relative;
|
||||||
|
color: white;
|
||||||
|
backdrop-filter: blur(
|
||||||
|
8px
|
||||||
|
); /* Ensure content has some blur as well for contrast */
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2rem;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 12px 24px;
|
||||||
|
background-color: #ffcc00;
|
||||||
|
color: black;
|
||||||
|
border: none;
|
||||||
|
font-size: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 5px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background-color: #ff9900;
|
||||||
|
}
|
||||||
33
secret/endlessRunner/index.html
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Endless runner</title>
|
||||||
|
<link
|
||||||
|
rel="apple-touch-icon"
|
||||||
|
sizes="180x180"
|
||||||
|
href="../../favicon_io/apple-touch-icon.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="32x32"
|
||||||
|
href="../../favicon_io/favicon-32x32.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="16x16"
|
||||||
|
href="../../favicon_io/favicon-16x16.png"
|
||||||
|
/>
|
||||||
|
<link rel="manifest" href="../../favicon_io/site.webmanifest" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="game-container">
|
||||||
|
<canvas id="gameCanvas"></canvas>
|
||||||
|
<button id="restartBtn" onclick="restartGame()">Restart</button>
|
||||||
|
</div>
|
||||||
|
<script src="script.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
90
secret/endlessRunner/script.js
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
|
export default function EndlessRunner() {
|
||||||
|
const canvasRef = useRef(null);
|
||||||
|
const [running, setRunning] = useState(true);
|
||||||
|
const player = { x: 50, y: 150, width: 20, height: 20, dy: 0 };
|
||||||
|
const gravity = 0.5;
|
||||||
|
let obstacles = [];
|
||||||
|
let score = 0;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const canvas = canvasRef.current;
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
canvas.width = window.innerWidth;
|
||||||
|
canvas.height = 300;
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
if (!running) return;
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
// Player physics
|
||||||
|
player.dy += gravity;
|
||||||
|
player.y += player.dy;
|
||||||
|
if (player.y > 150) {
|
||||||
|
player.y = 150;
|
||||||
|
player.dy = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw player
|
||||||
|
ctx.fillStyle = "blue";
|
||||||
|
ctx.fillRect(player.x, player.y, player.width, player.height);
|
||||||
|
|
||||||
|
// Obstacles
|
||||||
|
if (Math.random() < 0.02) {
|
||||||
|
obstacles.push({ x: canvas.width, y: 150, width: 20, height: 20 });
|
||||||
|
}
|
||||||
|
obstacles = obstacles.map((obstacle) => ({
|
||||||
|
...obstacle,
|
||||||
|
x: obstacle.x - 5,
|
||||||
|
}));
|
||||||
|
obstacles = obstacles.filter(
|
||||||
|
(obstacle) => obstacle.x + obstacle.width > 0
|
||||||
|
);
|
||||||
|
|
||||||
|
obstacles.forEach((obstacle) => {
|
||||||
|
ctx.fillStyle = "red";
|
||||||
|
ctx.fillRect(obstacle.x, obstacle.y, obstacle.width, obstacle.height);
|
||||||
|
|
||||||
|
// Collision detection
|
||||||
|
if (
|
||||||
|
player.x < obstacle.x + obstacle.width &&
|
||||||
|
player.x + player.width > obstacle.x &&
|
||||||
|
player.y < obstacle.y + obstacle.height &&
|
||||||
|
player.y + player.height > obstacle.y
|
||||||
|
) {
|
||||||
|
setRunning(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Score
|
||||||
|
score++;
|
||||||
|
ctx.fillStyle = "black";
|
||||||
|
ctx.fillText("Score: " + score, 10, 20);
|
||||||
|
|
||||||
|
requestAnimationFrame(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
update();
|
||||||
|
}, [running]);
|
||||||
|
|
||||||
|
function jump() {
|
||||||
|
if (player.y >= 150) {
|
||||||
|
player.dy = -10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center">
|
||||||
|
<canvas ref={canvasRef} className="border" onClick={jump}></canvas>
|
||||||
|
{!running && (
|
||||||
|
<button
|
||||||
|
onClick={() => window.location.reload()}
|
||||||
|
className="mt-4 bg-blue-500 text-white px-4 py-2 rounded"
|
||||||
|
>
|
||||||
|
Restart
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
34
secret/endlessRunner/styles.css
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.game-container {
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
border: 2px solid black;
|
||||||
|
background-color: white;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#restartBtn {
|
||||||
|
margin-top: 10px;
|
||||||
|
padding: 10px 20px;
|
||||||
|
font-size: 16px;
|
||||||
|
background-color: #007bff;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#restartBtn:hover {
|
||||||
|
background-color: #0056b3;
|
||||||
|
}
|
||||||
404
secret/game.js
Normal file
|
|
@ -0,0 +1,404 @@
|
||||||
|
"use strict";
|
||||||
|
// Canvas setup
|
||||||
|
const canvas = document.getElementById("gameCanvas");
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
canvas.width = window.innerWidth;
|
||||||
|
canvas.height = window.innerHeight;
|
||||||
|
|
||||||
|
// Game elements
|
||||||
|
const player = {
|
||||||
|
x: canvas.width / 2,
|
||||||
|
y: canvas.height - 60,
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
color: "white",
|
||||||
|
speed: 5,
|
||||||
|
dx: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let bullets = [];
|
||||||
|
let asteroids = [];
|
||||||
|
let items = [];
|
||||||
|
let score = 0;
|
||||||
|
let totalBulletsFired = 0;
|
||||||
|
let isGameOver = false;
|
||||||
|
let lastBulletTime = 0;
|
||||||
|
let ammo = 100; // Ammo counter
|
||||||
|
|
||||||
|
// Difficulty control
|
||||||
|
let asteroidSpawnInterval = 800; // Faster spawn rate
|
||||||
|
let asteroidSpeedMultiplier = 1.5; // Faster asteroids from the start
|
||||||
|
let difficultyIncreaseRate = 0.2; // Faster scaling every 10 seconds
|
||||||
|
|
||||||
|
// Controls
|
||||||
|
let canShoot = true; // Flag to control shooting
|
||||||
|
let rapidFireActive = false;
|
||||||
|
let shotgunActive = false;
|
||||||
|
let rainbowActive = false;
|
||||||
|
let lastShotgunTime = 0;
|
||||||
|
|
||||||
|
// Controls for sphere effects
|
||||||
|
let blueSphereCooldown = 0;
|
||||||
|
let yellowSphereCooldown = 0;
|
||||||
|
let greenSphereCooldown = 0;
|
||||||
|
let rainbowSphereCooldown = 0;
|
||||||
|
|
||||||
|
// Sphere types
|
||||||
|
const sphereTypes = ["blue", "yellow", "green", "rainbow"];
|
||||||
|
|
||||||
|
/// Control for left button press and release
|
||||||
|
function btnMoveLeft(isPressed) {
|
||||||
|
if (isPressed) {
|
||||||
|
player.dx = -player.speed; // Start moving left
|
||||||
|
} else {
|
||||||
|
player.dx = 0; // Stop moving when button is released
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Control for shoot button click (simulates spacebar press)
|
||||||
|
function btnShoot() {
|
||||||
|
if (canShoot && !isGameOver) {
|
||||||
|
shootBullet();
|
||||||
|
canShoot = false; // Prevent shooting until the button is "released"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Control for right button press and release
|
||||||
|
function btnMoveRight(isPressed) {
|
||||||
|
if (isPressed) {
|
||||||
|
player.dx = player.speed; // Start moving right
|
||||||
|
} else {
|
||||||
|
player.dx = 0; // Stop moving when button is released
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById("shootBtn").addEventListener("mouseup", () => {
|
||||||
|
canShoot = true; // Allow shooting again when button is released
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("keydown", (e) => {
|
||||||
|
if (e.key === "ArrowLeft" || e.key === "a") player.dx = -player.speed;
|
||||||
|
if (e.key === "ArrowRight" || e.key === "d") player.dx = player.speed;
|
||||||
|
|
||||||
|
// Shoot only if it's not a hold, and we can shoot
|
||||||
|
if (e.key === " " && canShoot && !isGameOver) {
|
||||||
|
shootBullet();
|
||||||
|
canShoot = false; // Prevent shooting until the key is released
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.key === "r" && isGameOver) restartGame();
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("keyup", (e) => {
|
||||||
|
// Stop moving when either the arrow keys or the 'a'/'d' keys are released
|
||||||
|
if (
|
||||||
|
e.key === "ArrowLeft" ||
|
||||||
|
e.key === "ArrowRight" ||
|
||||||
|
e.key === "a" ||
|
||||||
|
e.key === "d"
|
||||||
|
) {
|
||||||
|
player.dx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow shooting again when the space key is released
|
||||||
|
if (e.key === " ") {
|
||||||
|
canShoot = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Bullet mechanics with cooldown
|
||||||
|
function shootBullet() {
|
||||||
|
const now = Date.now();
|
||||||
|
if (now - lastBulletTime < 100) return; // Enforce cooldown of 0.1 seconds
|
||||||
|
if (ammo <= 0) return; // Prevent shooting if ammo is empty
|
||||||
|
lastBulletTime = now;
|
||||||
|
ammo--; // Decrease ammo
|
||||||
|
|
||||||
|
totalBulletsFired++; // Increment total bullets fired
|
||||||
|
|
||||||
|
if (rapidFireActive) {
|
||||||
|
// If rapid fire is active, fire bullets continuously with a short delay
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
setTimeout(() => {
|
||||||
|
bullets.push({
|
||||||
|
x: player.x + player.width / 2 - 2.5,
|
||||||
|
y: player.y,
|
||||||
|
width: 5,
|
||||||
|
height: 10,
|
||||||
|
color: "yellow",
|
||||||
|
speed: 7,
|
||||||
|
});
|
||||||
|
}, i * 50); // Fire bullets with 50ms delay between shots
|
||||||
|
}
|
||||||
|
} else if (shotgunActive) {
|
||||||
|
// Shotgun effect, firing 3 bullets with a spread
|
||||||
|
for (let i = -1; i <= 1; i++) {
|
||||||
|
bullets.push({
|
||||||
|
x: player.x + player.width / 2 - 2.5,
|
||||||
|
y: player.y,
|
||||||
|
width: 5,
|
||||||
|
height: 10,
|
||||||
|
color: "yellow",
|
||||||
|
speed: 7,
|
||||||
|
angle: i * 10, // Spray the bullets at different angles
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Normal bullet
|
||||||
|
bullets.push({
|
||||||
|
x: player.x + player.width / 2 - 2.5,
|
||||||
|
y: player.y,
|
||||||
|
width: 5,
|
||||||
|
height: 10,
|
||||||
|
color: "yellow",
|
||||||
|
speed: 7,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate random color
|
||||||
|
function getRandomColor() {
|
||||||
|
const colors = ["red", "blue", "green", "orange", "purple", "pink"];
|
||||||
|
return colors[Math.floor(Math.random() * colors.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Asteroid mechanics
|
||||||
|
function createAsteroid() {
|
||||||
|
const size = Math.random() * 40 + 30; // Bigger asteroids (min: 30, max: 70)
|
||||||
|
asteroids.push({
|
||||||
|
x: Math.random() * canvas.width,
|
||||||
|
y: -size,
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
color: getRandomColor(),
|
||||||
|
speed: (Math.random() * 3 + 2) * asteroidSpeedMultiplier, // Faster initial speed
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Item mechanics
|
||||||
|
function createItem() {
|
||||||
|
const randomType =
|
||||||
|
sphereTypes[Math.floor(Math.random() * sphereTypes.length)];
|
||||||
|
const size = 30;
|
||||||
|
const x = Math.random() * canvas.width;
|
||||||
|
items.push({
|
||||||
|
x: x,
|
||||||
|
y: -size,
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
type: randomType,
|
||||||
|
color:
|
||||||
|
randomType === "blue"
|
||||||
|
? "blue"
|
||||||
|
: randomType === "yellow"
|
||||||
|
? "yellow"
|
||||||
|
: randomType === "green"
|
||||||
|
? "green"
|
||||||
|
: "rainbow",
|
||||||
|
speed: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update game elements
|
||||||
|
function updatePlayer() {
|
||||||
|
player.x += player.dx;
|
||||||
|
if (player.x < 0) player.x = 0;
|
||||||
|
if (player.x + player.width > canvas.width)
|
||||||
|
player.x = canvas.width - player.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateBullets() {
|
||||||
|
bullets.forEach((bullet, index) => {
|
||||||
|
bullet.y -= bullet.speed;
|
||||||
|
if (bullet.y + bullet.height < 0) bullets.splice(index, 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateAsteroids() {
|
||||||
|
asteroids.forEach((asteroid, index) => {
|
||||||
|
asteroid.y += asteroid.speed;
|
||||||
|
if (asteroid.y > canvas.height) asteroids.splice(index, 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateItems() {
|
||||||
|
items.forEach((item, index) => {
|
||||||
|
item.y += item.speed;
|
||||||
|
if (item.y > canvas.height) items.splice(index, 1);
|
||||||
|
// Check if player collects the item
|
||||||
|
if (
|
||||||
|
player.x < item.x + item.width &&
|
||||||
|
player.x + player.width > item.x &&
|
||||||
|
player.y < item.y + item.height &&
|
||||||
|
player.y + player.height > item.y
|
||||||
|
) {
|
||||||
|
applyItemEffect(item.type);
|
||||||
|
items.splice(index, 1); // Remove the item after it is collected
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyItemEffect(type) {
|
||||||
|
let points = Math.floor(Math.random() * 5) + 1; // Random points between 1 and 5
|
||||||
|
if (type === "blue") {
|
||||||
|
rapidFireActive = true;
|
||||||
|
setTimeout(() => (rapidFireActive = false), 15000); // 15 seconds of rapid fire
|
||||||
|
} else if (type === "yellow") {
|
||||||
|
shotgunActive = true;
|
||||||
|
setTimeout(() => (shotgunActive = false), 30000); // 30 seconds of shotgun
|
||||||
|
} else if (type === "green") {
|
||||||
|
ammo = 100; // Refill ammo
|
||||||
|
} else if (type === "rainbow") {
|
||||||
|
rapidFireActive = true;
|
||||||
|
shotgunActive = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
rapidFireActive = false;
|
||||||
|
shotgunActive = false;
|
||||||
|
}, 15000); // 15 seconds with all effects
|
||||||
|
}
|
||||||
|
score += points; // Add points when an item is collected
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collision detection
|
||||||
|
function checkCollisions() {
|
||||||
|
bullets.forEach((bullet, bIndex) => {
|
||||||
|
asteroids.forEach((asteroid, aIndex) => {
|
||||||
|
if (
|
||||||
|
bullet.x < asteroid.x + asteroid.width &&
|
||||||
|
bullet.x + bullet.width > asteroid.x &&
|
||||||
|
bullet.y < asteroid.y + asteroid.height &&
|
||||||
|
bullet.y + bullet.height > asteroid.y
|
||||||
|
) {
|
||||||
|
bullets.splice(bIndex, 1);
|
||||||
|
asteroids.splice(aIndex, 1);
|
||||||
|
score += Math.floor(Math.random() * 5) + 1; // Add points when an asteroid is destroyed
|
||||||
|
// Visual feedback for destroyed asteroid
|
||||||
|
createExplosion(asteroid.x, asteroid.y);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
asteroids.forEach((asteroid) => {
|
||||||
|
if (
|
||||||
|
player.x < asteroid.x + asteroid.width &&
|
||||||
|
player.x + player.width > asteroid.x &&
|
||||||
|
player.y < asteroid.y + asteroid.height &&
|
||||||
|
player.y + player.height > asteroid.y
|
||||||
|
) {
|
||||||
|
isGameOver = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Explosion effect
|
||||||
|
function createExplosion(x, y) {
|
||||||
|
ctx.fillStyle = "yellow";
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(x, y, 20, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
setTimeout(() => ctx.clearRect(x - 20, y - 20, 40, 40), 200); // Clear explosion after 200ms
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw elements
|
||||||
|
function drawPlayer() {
|
||||||
|
ctx.fillStyle = player.color;
|
||||||
|
ctx.fillRect(player.x, player.y, player.width, player.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawBullets() {
|
||||||
|
bullets.forEach((bullet) => {
|
||||||
|
ctx.fillStyle = bullet.color;
|
||||||
|
ctx.fillRect(bullet.x, bullet.y, bullet.width, bullet.height);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawAsteroids() {
|
||||||
|
asteroids.forEach((asteroid) => {
|
||||||
|
ctx.fillStyle = asteroid.color;
|
||||||
|
ctx.fillRect(asteroid.x, asteroid.y, asteroid.width, asteroid.height);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawItems() {
|
||||||
|
items.forEach((item) => {
|
||||||
|
ctx.fillStyle = item.color;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(
|
||||||
|
item.x + item.width / 2,
|
||||||
|
item.y + item.height / 2,
|
||||||
|
item.width / 2,
|
||||||
|
0,
|
||||||
|
Math.PI * 2
|
||||||
|
);
|
||||||
|
ctx.fill();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawScore() {
|
||||||
|
ctx.fillStyle = "white";
|
||||||
|
ctx.font = "24px Arial";
|
||||||
|
ctx.fillText(`Score: ${score}`, 20, 40); // Score at top-left corner
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawAmmo() {
|
||||||
|
ctx.fillStyle = "white";
|
||||||
|
ctx.font = "24px Arial";
|
||||||
|
ctx.fillText(`Ammo: ${ammo}`, 20, 70); // Ammo at top-left corner
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawGameOver() {
|
||||||
|
if (isGameOver) {
|
||||||
|
ctx.fillStyle = "white";
|
||||||
|
ctx.font = "40px Arial";
|
||||||
|
ctx.textAlign = "center";
|
||||||
|
ctx.fillText("Game Over!", canvas.width / 2, canvas.height / 2 - 40);
|
||||||
|
ctx.font = "24px Arial";
|
||||||
|
ctx.fillText(`Total Score: ${score}`, canvas.width / 2, canvas.height / 2);
|
||||||
|
ctx.fillText(
|
||||||
|
`Bullets Fired: ${totalBulletsFired}`,
|
||||||
|
canvas.width / 2,
|
||||||
|
canvas.height / 2 + 40
|
||||||
|
);
|
||||||
|
ctx.fillText(
|
||||||
|
'Press "R" to Restart',
|
||||||
|
canvas.width / 2,
|
||||||
|
canvas.height / 2 + 80
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restart game
|
||||||
|
function restartGame() {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main game loop
|
||||||
|
function gameLoop() {
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
if (!isGameOver) {
|
||||||
|
updatePlayer();
|
||||||
|
updateBullets();
|
||||||
|
updateAsteroids();
|
||||||
|
updateItems();
|
||||||
|
checkCollisions();
|
||||||
|
}
|
||||||
|
|
||||||
|
drawPlayer();
|
||||||
|
drawBullets();
|
||||||
|
drawAsteroids();
|
||||||
|
drawItems();
|
||||||
|
drawScore();
|
||||||
|
drawAmmo();
|
||||||
|
drawGameOver();
|
||||||
|
|
||||||
|
if (!isGameOver) {
|
||||||
|
if (Math.random() < 0.01) createAsteroid(); // 1% chance every frame to spawn an asteroid
|
||||||
|
if (Math.random() < 0.005) createItem(); // 0.5% chance to spawn an item
|
||||||
|
}
|
||||||
|
|
||||||
|
requestAnimationFrame(gameLoop);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start game loop
|
||||||
|
gameLoop();
|
||||||
65
secret/guessMyNumber/game.js
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const targetNum = Math.trunc(Math.random() * 20) + 1;
|
||||||
|
let highScore = Number(localStorage.getItem("highscore")) || 0;
|
||||||
|
let userGuess = 10; // Default guess
|
||||||
|
let currScore = 20;
|
||||||
|
|
||||||
|
const screenEl = document.querySelector(".screen");
|
||||||
|
const msgEl = document.querySelector(".message");
|
||||||
|
const guessInput = document.querySelector("#guess");
|
||||||
|
const scoreEl = document.querySelector(".score");
|
||||||
|
const highScoreEl = document.querySelector(".highScore");
|
||||||
|
const checkBtn = document.querySelector("#check");
|
||||||
|
const restartBtn = document.querySelector("#restart");
|
||||||
|
const incBtn = document.querySelector("#up");
|
||||||
|
const decBtn = document.querySelector("#down");
|
||||||
|
|
||||||
|
const setMsg = (msg) => (msgEl.textContent = msg);
|
||||||
|
const setScore = (score) =>
|
||||||
|
(scoreEl.textContent = `Score: ${(currScore = score)}`);
|
||||||
|
const setHighScore = () => {
|
||||||
|
highScoreEl.textContent = `Highscore: ${highScore}`;
|
||||||
|
localStorage.setItem("highscore", highScore);
|
||||||
|
};
|
||||||
|
const changeColor = (color) => (screenEl.style.backgroundColor = color);
|
||||||
|
|
||||||
|
checkBtn.addEventListener("click", () => {
|
||||||
|
userGuess = Number(guessInput.textContent);
|
||||||
|
if (!userGuess || userGuess < 1 || userGuess > 20) {
|
||||||
|
setMsg("Please enter a valid number between 1 and 20.");
|
||||||
|
} else if (userGuess === targetNum) {
|
||||||
|
highScore = Math.max(highScore, currScore);
|
||||||
|
setHighScore();
|
||||||
|
setMsg(
|
||||||
|
currScore !== 20 ? "Correct Number!" : "Are you sure you didn't cheat?"
|
||||||
|
);
|
||||||
|
changeColor(currScore !== 20 ? "#1ba100" : "#FFC300");
|
||||||
|
} else {
|
||||||
|
setMsg(userGuess > targetNum ? "Too High!" : "Too Low!");
|
||||||
|
if (currScore > 1) {
|
||||||
|
setScore(currScore - 1);
|
||||||
|
} else {
|
||||||
|
setScore(1);
|
||||||
|
setMsg("You lost the game!");
|
||||||
|
changeColor("#a10000");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
restartBtn.addEventListener("click", () => location.reload());
|
||||||
|
incBtn.addEventListener(
|
||||||
|
"click",
|
||||||
|
() =>
|
||||||
|
(guessInput.textContent = Math.min(Number(guessInput.textContent) + 1, 20))
|
||||||
|
);
|
||||||
|
decBtn.addEventListener(
|
||||||
|
"click",
|
||||||
|
() =>
|
||||||
|
(guessInput.textContent = Math.max(Number(guessInput.textContent) - 1, 1))
|
||||||
|
);
|
||||||
|
|
||||||
|
guessInput.textContent = userGuess;
|
||||||
|
setMsg("Guess a number");
|
||||||
|
setScore(currScore);
|
||||||
|
setHighScore();
|
||||||
72
secret/guessMyNumber/index.html
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Guess My Number</title>
|
||||||
|
<link rel="stylesheet" href="styles.css" />
|
||||||
|
<link
|
||||||
|
rel="apple-touch-icon"
|
||||||
|
sizes="180x180"
|
||||||
|
href="../../favicon_io/apple-touch-icon.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="32x32"
|
||||||
|
href="../../favicon_io/favicon-32x32.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="16x16"
|
||||||
|
href="../../favicon_io/favicon-16x16.png"
|
||||||
|
/>
|
||||||
|
<link rel="manifest" href="../../favicon_io/site.webmanifest" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="gameboy">
|
||||||
|
<!-- Game Boy Screen -->
|
||||||
|
<div class="screen">
|
||||||
|
<article class="game">
|
||||||
|
<h1>Guess My Number - Game</h1>
|
||||||
|
<p class="message"></p>
|
||||||
|
<div class="guess-display">
|
||||||
|
<span id="guess"></span>
|
||||||
|
</div>
|
||||||
|
<p class="score"></p>
|
||||||
|
<p class="highScore"></p>
|
||||||
|
|
||||||
|
<div class="description">
|
||||||
|
<h2>Description</h2>
|
||||||
|
<p>Guess a number between 1 and 20</p>
|
||||||
|
<p>A = check</p>
|
||||||
|
<p>B = Reload</p>
|
||||||
|
<p>▲ = increases guess by one</p>
|
||||||
|
<p>▼ = decreases guess by one</p>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Controls -->
|
||||||
|
<div class="controls">
|
||||||
|
<!-- D-Pad on the left -->
|
||||||
|
<div class="dpad">
|
||||||
|
<button class="dpad-btn up" id="up">▲</button>
|
||||||
|
<button class="dpad-btn left" id="left">◀</button>
|
||||||
|
<div class="dpad-center"></div>
|
||||||
|
<button class="dpad-btn right" id="right">▶</button>
|
||||||
|
<button class="dpad-btn down" id="down">▼</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- A and B Buttons on the right -->
|
||||||
|
<div class="action-buttons">
|
||||||
|
<button class="btn" id="check">A</button>
|
||||||
|
<button class="btn" id="restart">B</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="game.js"></script>
|
||||||
|
<script src="styles.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
189
secret/guessMyNumber/styles.css
Normal file
|
|
@ -0,0 +1,189 @@
|
||||||
|
/* Base Reset */
|
||||||
|
body,
|
||||||
|
html {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: monospace;
|
||||||
|
background-color: #3a2d56;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GameBoy Layout */
|
||||||
|
.gameboy {
|
||||||
|
background-color: #5f4c82; /* Game Boy Color purple shell */
|
||||||
|
width: 441px;
|
||||||
|
height: 735px;
|
||||||
|
border-radius: 20px;
|
||||||
|
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.6);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.gameboy {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Screen */
|
||||||
|
.screen {
|
||||||
|
background-color: #306230; /* Game Boy green screen */
|
||||||
|
border: 4px solid #0f380f;
|
||||||
|
width: 90%;
|
||||||
|
height: 55%;
|
||||||
|
margin-top: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
box-shadow: inset 0 4px 8px rgba(0, 0, 0, 0.5);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.game {
|
||||||
|
text-align: center;
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Titles */
|
||||||
|
h1 {
|
||||||
|
font-size: 2rem; /* Increased font size */
|
||||||
|
margin-bottom: 10px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Guess Display */
|
||||||
|
.guess-display {
|
||||||
|
background-color: #9bbc0f;
|
||||||
|
color: #0f380f;
|
||||||
|
border: 2px solid #0f380f;
|
||||||
|
font-size: 2rem; /* Increased font size */
|
||||||
|
width: 80px; /* Increased width */
|
||||||
|
text-align: center;
|
||||||
|
margin: 10px auto;
|
||||||
|
padding: 10px; /* Increased padding */
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Messages */
|
||||||
|
.message,
|
||||||
|
.score,
|
||||||
|
.highScore {
|
||||||
|
font-size: 1.4rem; /* Increased font size */
|
||||||
|
margin: 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description,
|
||||||
|
.description p {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Controls Section */
|
||||||
|
.controls {
|
||||||
|
margin-top: 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 80%;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* D-Pad */
|
||||||
|
.dpad {
|
||||||
|
position: relative;
|
||||||
|
width: 120px; /* Increased size */
|
||||||
|
height: 120px; /* Increased size */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Base Styling for D-Pad Buttons */
|
||||||
|
.dpad-btn {
|
||||||
|
background-color: #0f380f;
|
||||||
|
color: #9bbc0f;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
position: absolute;
|
||||||
|
width: 42px;
|
||||||
|
height: 42px;
|
||||||
|
font-size: 1.5rem; /* Increased size */
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dpad-btn.up {
|
||||||
|
top: 0;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dpad-btn.down {
|
||||||
|
bottom: 0;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dpad-btn.left {
|
||||||
|
top: 50%;
|
||||||
|
left: 0;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dpad-btn.right {
|
||||||
|
top: 50%;
|
||||||
|
right: 0;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* D-Pad Center to Connect Buttons */
|
||||||
|
.dpad-center {
|
||||||
|
background-color: #0f380f;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border: 2px solid #9cbc0f00;
|
||||||
|
z-index: 0;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A and B Buttons */
|
||||||
|
.action-buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 140px; /* Increased height */
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
background-color: #0f380f;
|
||||||
|
color: #9bbc0f;
|
||||||
|
border: 2px solid #9bbc0f;
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
font-size: 1.8rem; /* Increased font size */
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 0.1s, background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:hover {
|
||||||
|
background-color: #9bbc0f;
|
||||||
|
color: #0f380f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:active {
|
||||||
|
transform: scale(0.9);
|
||||||
|
}
|
||||||
148
secret/guessMyNumber/styles.js
Normal file
|
|
@ -0,0 +1,148 @@
|
||||||
|
"use strict";
|
||||||
|
const left = document.querySelector("#left");
|
||||||
|
const right = document.querySelector("#right");
|
||||||
|
const gameboy = document.querySelector(".gameboy");
|
||||||
|
const html = document.documentElement;
|
||||||
|
const body = document.body;
|
||||||
|
const screen = document.querySelector(".screen");
|
||||||
|
const dpadButtons = document.querySelectorAll(".dpad-btn");
|
||||||
|
const dpadCenter = document.querySelector(".dpad-center"); // Darker variant
|
||||||
|
const actionButtons = document.querySelectorAll(".btn");
|
||||||
|
|
||||||
|
const colors = [
|
||||||
|
{
|
||||||
|
gameboyColor: "#B39DDB",
|
||||||
|
htmlColor: "#D1C4E9",
|
||||||
|
screenColor: "#E1BEE7",
|
||||||
|
buttonColor: "#673AB7",
|
||||||
|
buttonTextColor: "#FFFFFF",
|
||||||
|
dpadCenterColor: "#5E35B1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameboyColor: "#FFC107",
|
||||||
|
htmlColor: "#FFF9C4",
|
||||||
|
screenColor: "#FFEB3B",
|
||||||
|
buttonColor: "#FF9800",
|
||||||
|
buttonTextColor: "#000000",
|
||||||
|
dpadCenterColor: "#EF6C00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameboyColor: "#8BC34A",
|
||||||
|
htmlColor: "#C5E1A5",
|
||||||
|
screenColor: "#A5D6A7",
|
||||||
|
buttonColor: "#FF5722",
|
||||||
|
buttonTextColor: "#FFFFFF",
|
||||||
|
dpadCenterColor: "#E64A19",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameboyColor: "#F44336",
|
||||||
|
htmlColor: "#FFCDD2",
|
||||||
|
screenColor: "#EF9A9A",
|
||||||
|
buttonColor: "#E91E63",
|
||||||
|
buttonTextColor: "#FFFFFF",
|
||||||
|
dpadCenterColor: "#C2185B",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameboyColor: "#03A9F4",
|
||||||
|
htmlColor: "#BBDEFB",
|
||||||
|
screenColor: "#90CAF9",
|
||||||
|
buttonColor: "#FFEB3B",
|
||||||
|
buttonTextColor: "#000000",
|
||||||
|
dpadCenterColor: "#0277BD",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameboyColor: "#FF7043",
|
||||||
|
htmlColor: "#FFCCBC",
|
||||||
|
screenColor: "#FFAB91",
|
||||||
|
buttonColor: "#FF5722",
|
||||||
|
buttonTextColor: "#FFFFFF",
|
||||||
|
dpadCenterColor: "#D84315",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameboyColor: "#9C27B0",
|
||||||
|
htmlColor: "#E1BEE7",
|
||||||
|
screenColor: "#D1C4E9",
|
||||||
|
buttonColor: "#7B1FA2",
|
||||||
|
buttonTextColor: "#FFFFFF",
|
||||||
|
dpadCenterColor: "#6A1B9A",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameboyColor: "#FFD700",
|
||||||
|
htmlColor: "#FFF9C4",
|
||||||
|
screenColor: "#FFF59D",
|
||||||
|
buttonColor: "#FF9800",
|
||||||
|
buttonTextColor: "#FFFFFF",
|
||||||
|
dpadCenterColor: "#F57F17",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameboyColor: "#009688",
|
||||||
|
htmlColor: "#B2DFDB",
|
||||||
|
screenColor: "#80CBC4",
|
||||||
|
buttonColor: "#4CAF50",
|
||||||
|
buttonTextColor: "#FFFFFF",
|
||||||
|
dpadCenterColor: "#00796B",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameboyColor: "#795548",
|
||||||
|
htmlColor: "#D7CCC8",
|
||||||
|
screenColor: "#A1887F",
|
||||||
|
buttonColor: "#9E9E9E",
|
||||||
|
buttonTextColor: "#000000",
|
||||||
|
dpadCenterColor: "#5D4037",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameboyColor: "#FF5733",
|
||||||
|
htmlColor: "#FFCCCB",
|
||||||
|
screenColor: "#FFABAB",
|
||||||
|
buttonColor: "#C70039",
|
||||||
|
buttonTextColor: "#FFFFFF",
|
||||||
|
dpadCenterColor: "#B71C1C",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameboyColor: "#00BCD4",
|
||||||
|
htmlColor: "#B2EBF2",
|
||||||
|
screenColor: "#80DEEA",
|
||||||
|
buttonColor: "#00ACC1",
|
||||||
|
buttonTextColor: "#FFFFFF",
|
||||||
|
dpadCenterColor: "#00838F",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let currentColorIndex = localStorage.getItem("gameboyColorIndex")
|
||||||
|
? parseInt(localStorage.getItem("gameboyColorIndex"))
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
function updateGameBoyColor() {
|
||||||
|
gameboy.style.backgroundColor = colors[currentColorIndex].gameboyColor;
|
||||||
|
html.style.backgroundColor = colors[currentColorIndex].htmlColor;
|
||||||
|
body.style.backgroundColor = colors[currentColorIndex].htmlColor;
|
||||||
|
screen.style.backgroundColor = colors[currentColorIndex].screenColor;
|
||||||
|
|
||||||
|
dpadButtons.forEach((button) => {
|
||||||
|
button.style.backgroundColor = colors[currentColorIndex].buttonColor;
|
||||||
|
button.style.color = colors[currentColorIndex].buttonTextColor;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Using darker dpad center color
|
||||||
|
dpadCenter.style.backgroundColor = colors[currentColorIndex].dpadCenterColor;
|
||||||
|
dpadCenter.style.color = colors[currentColorIndex].buttonTextColor;
|
||||||
|
|
||||||
|
actionButtons.forEach((button) => {
|
||||||
|
button.style.backgroundColor = colors[currentColorIndex].buttonColor;
|
||||||
|
button.style.color = colors[currentColorIndex].buttonTextColor;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
left.addEventListener("click", () => {
|
||||||
|
currentColorIndex = (currentColorIndex - 1 + colors.length) % colors.length;
|
||||||
|
localStorage.setItem("gameboyColorIndex", currentColorIndex);
|
||||||
|
updateGameBoyColor();
|
||||||
|
});
|
||||||
|
|
||||||
|
right.addEventListener("click", () => {
|
||||||
|
currentColorIndex = (currentColorIndex + 1) % colors.length;
|
||||||
|
localStorage.setItem("gameboyColorIndex", currentColorIndex);
|
||||||
|
updateGameBoyColor();
|
||||||
|
});
|
||||||
|
|
||||||
|
updateGameBoyColor();
|
||||||
BIN
secret/images/asteroid.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
secret/images/background.jpg
Normal file
|
After Width: | Height: | Size: 949 KiB |
BIN
secret/images/blackjack.jpg
Normal file
|
After Width: | Height: | Size: 9 KiB |
BIN
secret/images/default.jpeg
Normal file
|
After Width: | Height: | Size: 573 KiB |
BIN
secret/images/endless_runner.png
Normal file
|
After Width: | Height: | Size: 455 KiB |
BIN
secret/images/minesweeper.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
secret/images/number.jpeg
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
secret/images/snake.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
secret/images/solitaire.png
Normal file
|
After Width: | Height: | Size: 176 KiB |
118
secret/index.html
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Secret Game Collection</title>
|
||||||
|
<link rel="stylesheet" href="styles.css" />
|
||||||
|
<link
|
||||||
|
rel="apple-touch-icon"
|
||||||
|
sizes="180x180"
|
||||||
|
href="../favicon_io/apple-touch-icon.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="32x32"
|
||||||
|
href="../favicon_io/favicon-32x32.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="16x16"
|
||||||
|
href="../favicon_io/favicon-16x16.png"
|
||||||
|
/>
|
||||||
|
<link rel="manifest" href="../favicon_io/site.webmanifest" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<h1>Secret Game Collection</h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<div class="grid-container">
|
||||||
|
<a
|
||||||
|
href="asteroidDestroyer/explenation.html"
|
||||||
|
target="_blank"
|
||||||
|
class="item"
|
||||||
|
>
|
||||||
|
<img src="images/asteroid.png" alt="Image can't be displayed" />
|
||||||
|
<h2>Secret Asteroid Shooter</h2>
|
||||||
|
<div class="description">
|
||||||
|
<p>
|
||||||
|
In this game, you control a spaceship that shoots at asteroids to
|
||||||
|
avoid destruction and collect items for power-ups.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Your goal is to survive as long as possible while scoring points!
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a href="#" target="_blank" class="item">
|
||||||
|
<img src="images/default.jpeg" alt="Image can't be displayed" />
|
||||||
|
<h2>Secret Blackjack</h2>
|
||||||
|
<div class="description">
|
||||||
|
<p>
|
||||||
|
Try to beat the dealer by getting a hand value as close to 21 as
|
||||||
|
possible without going over.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a href="#" target="_blank" class="item">
|
||||||
|
<img src="images/default.jpeg" alt="Image can't be displayed" />
|
||||||
|
<h2>Snake</h2>
|
||||||
|
<div class="description">
|
||||||
|
<p>
|
||||||
|
Guide the snake to eat food and grow longer while avoiding
|
||||||
|
collisions with the walls and itself.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a href="#" target="_blank" class="item">
|
||||||
|
<img src="images/default.jpeg" alt="Image can't be displayed" />
|
||||||
|
<h2>Solitaire</h2>
|
||||||
|
<div class="description">
|
||||||
|
<p>
|
||||||
|
A classic card game where the objective is to move all cards to
|
||||||
|
foundation piles in ascending order.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a href="mineSweeper/index.html" target="_blank" class="item">
|
||||||
|
<img src="images/minesweeper.png" alt="Image can't be displayed" />
|
||||||
|
<h2>Minesweeper</h2>
|
||||||
|
<div class="description">
|
||||||
|
<p>
|
||||||
|
Uncover squares on a grid while avoiding hidden mines, using
|
||||||
|
numbers to deduce safe spots.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a href="guessMyNumber/index.html" target="_blank" class="item">
|
||||||
|
<img src="images/number.jpeg" alt="Image can't be displayed" />
|
||||||
|
<h2>Guess My Number</h2>
|
||||||
|
<div class="description">
|
||||||
|
<p>
|
||||||
|
A simple game where you try to guess a randomly chosen number
|
||||||
|
within a certain range.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a href="#" target="_blank" class="item">
|
||||||
|
<img src="images/default.jpeg" alt="Image can't be displayed" />
|
||||||
|
<h2>Endless Runner</h2>
|
||||||
|
<div class="description">
|
||||||
|
<p>
|
||||||
|
Run through an endless landscape, avoiding obstacles and
|
||||||
|
collecting items to score points.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<p>© 2025 Game Collection</p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
60
secret/mineSweeper/index.html
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Minesweeper</title>
|
||||||
|
<link rel="stylesheet" href="styles.css" />
|
||||||
|
<link
|
||||||
|
rel="apple-touch-icon"
|
||||||
|
sizes="180x180"
|
||||||
|
href="../../favicon_io/apple-touch-icon.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="32x32"
|
||||||
|
href="../../favicon_io/favicon-32x32.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="16x16"
|
||||||
|
href="../../favicon_io/favicon-16x16.png"
|
||||||
|
/>
|
||||||
|
<link rel="manifest" href="../../favicon_io/site.webmanifest" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="settings">
|
||||||
|
<h1>Minesweeper</h1>
|
||||||
|
<label for="gridSize">Grid Size:</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
id="gridSize"
|
||||||
|
min="6"
|
||||||
|
max="25"
|
||||||
|
value="9"
|
||||||
|
aria-label="Grid Size"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<label for="bombs">Number of Bombs:</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
id="bombs"
|
||||||
|
min="1"
|
||||||
|
max="300"
|
||||||
|
value="9"
|
||||||
|
aria-label="Number of Bombs"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<button id="startGame">Start Game</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<canvas id="game"></canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="script.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
192
secret/mineSweeper/script.js
Normal file
|
|
@ -0,0 +1,192 @@
|
||||||
|
document.getElementById("startGame").addEventListener("click", function () {
|
||||||
|
const gridSize = parseInt(document.getElementById("gridSize").value);
|
||||||
|
const bombs = parseInt(document.getElementById("bombs").value);
|
||||||
|
|
||||||
|
document.getElementById("settings").style.display = "none";
|
||||||
|
document.getElementById("game").style.display = "block";
|
||||||
|
|
||||||
|
renderGame(gridSize, bombs);
|
||||||
|
});
|
||||||
|
|
||||||
|
const canvas = document.getElementById("game");
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
|
||||||
|
class Minesweeper {
|
||||||
|
constructor(width, height, bombs) {
|
||||||
|
this.size = 25;
|
||||||
|
this.field = new Array(width);
|
||||||
|
this.bombs = new Array(width);
|
||||||
|
for (let x = 0; x < this.field.length; x++) {
|
||||||
|
this.field[x] = new Array(height);
|
||||||
|
this.bombs[x] = new Array(height);
|
||||||
|
for (let y = 0; y < this.field[x].length; y++) {
|
||||||
|
this.field[x][y] = 1;
|
||||||
|
this.bombs[x][y] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.bombAmount =
|
||||||
|
bombs > (width * height) / 2 ? (width * height) / 2 : bombs;
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
this.firstClick = false;
|
||||||
|
this.drawField();
|
||||||
|
}
|
||||||
|
|
||||||
|
generateBombs() {
|
||||||
|
for (let i = 0; i < this.bombAmount; i++) {
|
||||||
|
let x = Math.floor(Math.random() * this.width);
|
||||||
|
let y = Math.floor(Math.random() * this.height);
|
||||||
|
this.bombs[x][y] || this.field[x][y] == 0
|
||||||
|
? i--
|
||||||
|
: (this.bombs[x][y] = true);
|
||||||
|
}
|
||||||
|
this.firstClick = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
getNearbyBombs(x, y) {
|
||||||
|
let counter = 0;
|
||||||
|
for (let newX = x - 1; newX <= x + 1; newX++) {
|
||||||
|
for (let newY = y - 1; newY <= y + 1; newY++) {
|
||||||
|
if (
|
||||||
|
newX < this.field.length &&
|
||||||
|
newX >= 0 &&
|
||||||
|
newY < this.field[0].length &&
|
||||||
|
newY >= 0
|
||||||
|
) {
|
||||||
|
this.bombs[newX][newY] ? counter++ : {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkWin() {
|
||||||
|
for (let x = 0; x < this.field.length; x++) {
|
||||||
|
for (let y = 0; y < this.field[x].length; y++) {
|
||||||
|
if (this.field[x][y] != 0 && !this.bombs[x][y]) {
|
||||||
|
return;
|
||||||
|
} else if (this.field[x][y] == 0 && this.bombs[x][y]) {
|
||||||
|
alert(`[ERROR]: Square (${x}|${y}) should not be a bomb!`);
|
||||||
|
this.bombs[x][y] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.drawField();
|
||||||
|
alert("You won!");
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
markSquare(x, y) {
|
||||||
|
if (x < this.field.length && y < this.field[0].length) {
|
||||||
|
switch (this.field[x][y]) {
|
||||||
|
case 1:
|
||||||
|
this.field[x][y]++;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
this.field[x][y]++;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
this.field[x][y] = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.drawField();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uncoverSquare(x, y) {
|
||||||
|
if (x < this.field.length && x >= 0 && y < this.field[0].length && y >= 0) {
|
||||||
|
if (this.bombs[x][y] && this.field[x][y] == 1) {
|
||||||
|
alert("You lost!");
|
||||||
|
window.location.reload();
|
||||||
|
} else if (this.field[x][y] == 1) {
|
||||||
|
this.field[x][y] = 0;
|
||||||
|
!this.firstClick ? this.generateBombs() : {};
|
||||||
|
if (this.getNearbyBombs(x, y) == 0) {
|
||||||
|
for (let newX = x - 1; newX <= x + 1; newX++) {
|
||||||
|
for (let newY = y - 1; newY <= y + 1; newY++) {
|
||||||
|
if (
|
||||||
|
newX < this.field.length &&
|
||||||
|
newX >= 0 &&
|
||||||
|
newY < this.field[0].length &&
|
||||||
|
newY >= 0
|
||||||
|
) {
|
||||||
|
this.field[newX][newY] == 1
|
||||||
|
? this.uncoverSquare(newX, newY)
|
||||||
|
: {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.checkWin();
|
||||||
|
this.drawField();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drawSquare(x, y, type) {
|
||||||
|
ctx.lineWidth = 3;
|
||||||
|
let uncovered = (x + y) % 2 === 0 ? "#D3D3D3" : "#A9A9A9";
|
||||||
|
let covered = (x + y) % 2 === 0 ? "#4CAF50" : "#81C784";
|
||||||
|
ctx.fillStyle = type != 0 ? covered : uncovered;
|
||||||
|
ctx.fillRect(x * this.size, y * this.size, this.size, this.size);
|
||||||
|
ctx.strokeStyle = "#000";
|
||||||
|
ctx.strokeRect(x * this.size, y * this.size, this.size, this.size);
|
||||||
|
|
||||||
|
if (type != 1) {
|
||||||
|
const fontSize = this.size / 2;
|
||||||
|
const number = this.getNearbyBombs(x, y);
|
||||||
|
let finalPrint;
|
||||||
|
ctx.font = `${fontSize}px sans-serif`;
|
||||||
|
ctx.fillStyle = "#000";
|
||||||
|
type == 0 ? (finalPrint = number ? number : " ") : {};
|
||||||
|
type == 2 ? (finalPrint = "🚩") : {};
|
||||||
|
type == 3 ? (finalPrint = "❓") : {};
|
||||||
|
ctx.fillText(
|
||||||
|
finalPrint,
|
||||||
|
x * this.size + fontSize / (type == 0 ? 1.5 : 1.8),
|
||||||
|
y * this.size + fontSize * 1.3
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drawField() {
|
||||||
|
if (window.innerWidth > window.innerHeight) {
|
||||||
|
this.size = window.innerHeight / (this.field[0].length + 4);
|
||||||
|
} else {
|
||||||
|
this.size = window.innerWidth / (this.field.length + 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.width = this.size * this.field.length;
|
||||||
|
canvas.height = this.size * this.field[0].length;
|
||||||
|
|
||||||
|
const offsetX = (canvas.width - this.field.length * this.size) / 2;
|
||||||
|
const offsetY = (canvas.height - this.field[0].length * this.size) / 2;
|
||||||
|
|
||||||
|
for (let x = 0; x < this.field.length; x++) {
|
||||||
|
for (let y = 0; y < this.field[x].length; y++) {
|
||||||
|
this.drawSquare(x, y, this.field[x][y], offsetX, offsetY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderGame = (gridSize, bombs) => {
|
||||||
|
let field = new Minesweeper(gridSize, gridSize, bombs);
|
||||||
|
window.addEventListener("resize", () => field.drawField());
|
||||||
|
canvas.addEventListener("click", (event) => {
|
||||||
|
const rect = canvas.getBoundingClientRect();
|
||||||
|
const x = Math.floor((event.clientX - rect.left) / field.size);
|
||||||
|
const y = Math.floor((event.clientY - rect.top) / field.size);
|
||||||
|
field.uncoverSquare(x, y);
|
||||||
|
});
|
||||||
|
canvas.addEventListener("contextmenu", (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
const rect = canvas.getBoundingClientRect();
|
||||||
|
const x = Math.floor((event.clientX - rect.left) / field.size);
|
||||||
|
const y = Math.floor((event.clientY - rect.top) / field.size);
|
||||||
|
field.markSquare(x, y);
|
||||||
|
});
|
||||||
|
};
|
||||||
104
secret/mineSweeper/styles.css
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body,
|
||||||
|
html {
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #121212;
|
||||||
|
color: #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#settings {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
margin: auto;
|
||||||
|
background-color: #1e1e1e;
|
||||||
|
padding: 40px;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.5);
|
||||||
|
width: 100%;
|
||||||
|
max-width: 600px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2.5em;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
color: #007bff;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
font-size: 20px;
|
||||||
|
color: #d1d1d1;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="number"],
|
||||||
|
input[type="range"],
|
||||||
|
span {
|
||||||
|
padding: 12px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
|
text-align: center;
|
||||||
|
border: 1px solid #444;
|
||||||
|
border-radius: 6px;
|
||||||
|
background-color: #333;
|
||||||
|
color: #e0e0e0;
|
||||||
|
font-size: 18px;
|
||||||
|
transition: border-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="number"]:focus,
|
||||||
|
input[type="range"]:focus {
|
||||||
|
border-color: #007bff;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 12px 24px;
|
||||||
|
background-color: #007bff;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s ease, transform 0.2s ease;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background-color: #0056b3;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
button:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
#settings {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="number"],
|
||||||
|
input[type="range"],
|
||||||
|
button {
|
||||||
|
width: 90%;
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
secret/snake/audio/dead.mp3
Normal file
BIN
secret/snake/audio/down.mp3
Normal file
BIN
secret/snake/audio/eat.mp3
Normal file
BIN
secret/snake/audio/left.mp3
Normal file
BIN
secret/snake/audio/right.mp3
Normal file
BIN
secret/snake/audio/up.mp3
Normal file
95
secret/snake/game.js
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const cvs = document.getElementById("snake");
|
||||||
|
const ctx = cvs.getContext("2d");
|
||||||
|
const box = 20;
|
||||||
|
|
||||||
|
let snake = [{ x: 9 * box, y: 10 * box }];
|
||||||
|
let food = {
|
||||||
|
x: Math.floor(Math.random() * 19 + 1) * box,
|
||||||
|
y: Math.floor(Math.random() * 19 + 1) * box,
|
||||||
|
};
|
||||||
|
let score = 0;
|
||||||
|
let d;
|
||||||
|
let game;
|
||||||
|
|
||||||
|
document.addEventListener("keydown", direction);
|
||||||
|
function direction(event) {
|
||||||
|
let key = event.keyCode;
|
||||||
|
if ((key == 37 || key == 65) && d != "RIGHT") d = "LEFT";
|
||||||
|
else if ((key == 38 || key == 87) && d != "DOWN") d = "UP";
|
||||||
|
else if ((key == 39 || key == 68) && d != "LEFT") d = "RIGHT";
|
||||||
|
else if ((key == 40 || key == 83) && d != "UP") d = "DOWN";
|
||||||
|
}
|
||||||
|
|
||||||
|
function collision(head, array) {
|
||||||
|
return array.some((part) => head.x == part.x && head.y == part.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
ctx.fillStyle = "#0f380f";
|
||||||
|
ctx.fillRect(0, 0, cvs.width, cvs.height);
|
||||||
|
|
||||||
|
ctx.fillStyle = "red";
|
||||||
|
ctx.fillRect(food.x, food.y, box, box);
|
||||||
|
|
||||||
|
for (let i = 0; i < snake.length; i++) {
|
||||||
|
ctx.fillStyle = i == 0 ? "lime" : "white";
|
||||||
|
ctx.fillRect(snake[i].x, snake[i].y, box, box);
|
||||||
|
ctx.strokeStyle = "black";
|
||||||
|
ctx.strokeRect(snake[i].x, snake[i].y, box, box);
|
||||||
|
}
|
||||||
|
|
||||||
|
let snakeX = snake[0].x;
|
||||||
|
let snakeY = snake[0].y;
|
||||||
|
|
||||||
|
if (d == "LEFT") snakeX -= box;
|
||||||
|
if (d == "UP") snakeY -= box;
|
||||||
|
if (d == "RIGHT") snakeX += box;
|
||||||
|
if (d == "DOWN") snakeY += box;
|
||||||
|
|
||||||
|
if (snakeX == food.x && snakeY == food.y) {
|
||||||
|
score++;
|
||||||
|
food = {
|
||||||
|
x: Math.floor(Math.random() * 19 + 1) * box,
|
||||||
|
y: Math.floor(Math.random() * 19 + 1) * box,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
snake.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
let newHead = { x: snakeX, y: snakeY };
|
||||||
|
|
||||||
|
if (
|
||||||
|
snakeX < 0 ||
|
||||||
|
snakeX >= cvs.width ||
|
||||||
|
snakeY < 0 ||
|
||||||
|
snakeY >= cvs.height ||
|
||||||
|
collision(newHead, snake)
|
||||||
|
) {
|
||||||
|
clearInterval(game);
|
||||||
|
document.getElementById("restartBtn").style.display = "block";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
snake.unshift(newHead);
|
||||||
|
|
||||||
|
ctx.fillStyle = "white";
|
||||||
|
ctx.font = "20px Arial";
|
||||||
|
ctx.fillText("Score: " + score, 10, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
function restartGame() {
|
||||||
|
snake = [{ x: 9 * box, y: 10 * box }];
|
||||||
|
food = {
|
||||||
|
x: Math.floor(Math.random() * 19 + 1) * box,
|
||||||
|
y: Math.floor(Math.random() * 19 + 1) * box,
|
||||||
|
};
|
||||||
|
score = 0;
|
||||||
|
d = "";
|
||||||
|
document.getElementById("restartBtn").style.display = "none";
|
||||||
|
game = setInterval(draw, 150);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById("restartBtn").addEventListener("click", restartGame);
|
||||||
|
game = setInterval(draw, 150);
|
||||||
BIN
secret/snake/img/food.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
secret/snake/img/ground.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
67
secret/snake/index.html
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Snake - Game</title>
|
||||||
|
<link rel="stylesheet" href="styles.css" />
|
||||||
|
<link
|
||||||
|
rel="apple-touch-icon"
|
||||||
|
sizes="180x180"
|
||||||
|
href="../../favicon_io/apple-touch-icon.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="32x32"
|
||||||
|
href="../../favicon_io/favicon-32x32.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="16x16"
|
||||||
|
href="../../favicon_io/favicon-16x16.png"
|
||||||
|
/>
|
||||||
|
<link rel="manifest" href="../../favicon_io/site.webmanifest" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="gameboy">
|
||||||
|
<!-- Game Boy Screen -->
|
||||||
|
<div class="screen">
|
||||||
|
<article class="game">
|
||||||
|
<h1 class="title" id="title">Snake - Game</h1>
|
||||||
|
<div class="description" id="description">
|
||||||
|
<h2>Description</h2>
|
||||||
|
<p>Eat as many apples and grow as much as possible</p>
|
||||||
|
<p>◀ or A or arrow left = move left</p>
|
||||||
|
<p>▶ or D or arrow right = move right</p>
|
||||||
|
<p>▲ or W or arrow up = move up</p>
|
||||||
|
<p>▼ or S or arrow down = move down</p>
|
||||||
|
</div>
|
||||||
|
<canvas id="snake" width="400" height="400"></canvas>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Controls -->
|
||||||
|
<div class="controls">
|
||||||
|
<!-- D-Pad on the left -->
|
||||||
|
<div class="dpad">
|
||||||
|
<button class="dpad-btn up" id="up">▲</button>
|
||||||
|
<button class="dpad-btn left" id="left">◀</button>
|
||||||
|
<div class="dpad-center"></div>
|
||||||
|
<button class="dpad-btn right" id="right">▶</button>
|
||||||
|
<button class="dpad-btn down" id="down">▼</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- A, B and start button on the right -->
|
||||||
|
<div class="action-buttons">
|
||||||
|
<button class="start-btn btn" id="start">Start</button>
|
||||||
|
<button class="btn" id="a">A</button>
|
||||||
|
<button class="btn" id="b">B</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="game.js"></script>
|
||||||
|
<script src="styles.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
238
secret/snake/styles.css
Normal file
|
|
@ -0,0 +1,238 @@
|
||||||
|
/* Base Reset */
|
||||||
|
body,
|
||||||
|
html {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: monospace;
|
||||||
|
background-color: #3a2d56;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GameBoy Layout */
|
||||||
|
.gameboy {
|
||||||
|
background-color: #5f4c82;
|
||||||
|
width: 441px;
|
||||||
|
height: 735px;
|
||||||
|
border-radius: 20px;
|
||||||
|
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.6);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.gameboy {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Screen */
|
||||||
|
.screen {
|
||||||
|
background-color: black;
|
||||||
|
border: 4px solid #0f380f;
|
||||||
|
width: 90%;
|
||||||
|
height: 55%;
|
||||||
|
margin-top: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
box-shadow: inset 0 4px 8px rgba(0, 0, 0, 0.5);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.game {
|
||||||
|
text-align: center;
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Titles */
|
||||||
|
h1 {
|
||||||
|
font-size: 2rem;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #9bbc0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description,
|
||||||
|
.description p {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 auto;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grid container */
|
||||||
|
#grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(10, 1fr); /* Adjust to match gridSize */
|
||||||
|
grid-template-rows: repeat(10, 1fr); /* Adjust to match gridSize */
|
||||||
|
width: 400px; /* Adjust as needed */
|
||||||
|
height: 400px; /* Adjust as needed */
|
||||||
|
border: 2px solid #0f380f;
|
||||||
|
margin: 20px auto;
|
||||||
|
/* initially hide */
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Individual cells */
|
||||||
|
.cell {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell.light-green {
|
||||||
|
background-color: #9bbc0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell.dark-green {
|
||||||
|
background-color: #0f380f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Snake styling */
|
||||||
|
.snake {
|
||||||
|
background-color: #e600ff; /* Snake color */
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Apple styling */
|
||||||
|
.apple {
|
||||||
|
background-color: red; /* Apple color */
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Controls Section */
|
||||||
|
.controls {
|
||||||
|
margin-top: 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 80%;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* D-Pad */
|
||||||
|
.dpad {
|
||||||
|
position: relative;
|
||||||
|
width: 120px;
|
||||||
|
height: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Base Styling for D-Pad Buttons */
|
||||||
|
.dpad-btn {
|
||||||
|
background-color: #0f380f;
|
||||||
|
color: #9bbc0f;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
position: absolute;
|
||||||
|
width: 42px;
|
||||||
|
height: 42px;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dpad-btn.up {
|
||||||
|
top: 0;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dpad-btn.down {
|
||||||
|
bottom: 0;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dpad-btn.left {
|
||||||
|
top: 50%;
|
||||||
|
left: 0;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dpad-btn.right {
|
||||||
|
top: 50%;
|
||||||
|
right: 0;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* D-Pad Center to Connect Buttons */
|
||||||
|
.dpad-center {
|
||||||
|
background-color: #0f380f;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
z-index: 0;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A and B Buttons */
|
||||||
|
.action-buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
background-color: #0f380f;
|
||||||
|
color: #9bbc0f;
|
||||||
|
border: 2px solid #9bbc0f;
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
font-size: 1.8rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 0.1s, background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:hover {
|
||||||
|
background-color: #9bbc0f;
|
||||||
|
color: #0f380f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:active {
|
||||||
|
transform: scale(0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start Button */
|
||||||
|
.start-btn {
|
||||||
|
background-color: #0f380f;
|
||||||
|
color: #9bbc0f;
|
||||||
|
border: 2px solid #9bbc0f;
|
||||||
|
border-radius: 5px;
|
||||||
|
width: 100px;
|
||||||
|
height: 40px;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 0.1s, background-color 0.2s;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.start-btn:hover {
|
||||||
|
background-color: #9bbc0f;
|
||||||
|
color: #0f380f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.start-btn:active {
|
||||||
|
transform: scale(0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hidden Canvas for Debugging or Fallback */
|
||||||
|
canvas {
|
||||||
|
display: none;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
134
secret/snake/styles.js
Normal file
|
|
@ -0,0 +1,134 @@
|
||||||
|
"use strict";
|
||||||
|
const aBtn = document.querySelector("#a");
|
||||||
|
const bBtn = document.querySelector("#b");
|
||||||
|
const gameboy = document.querySelector(".gameboy");
|
||||||
|
const html = document.documentElement;
|
||||||
|
const body = document.body;
|
||||||
|
const dpadButtons = document.querySelectorAll(".dpad-btn");
|
||||||
|
const dpadCenter = document.querySelector(".dpad-center"); // Darker variant
|
||||||
|
const actionButtons = document.querySelectorAll(".btn");
|
||||||
|
|
||||||
|
const colors = [
|
||||||
|
{
|
||||||
|
gameboyColor: "#B39DDB",
|
||||||
|
htmlColor: "#D1C4E9",
|
||||||
|
buttonColor: "#673AB7",
|
||||||
|
buttonTextColor: "#FFFFFF",
|
||||||
|
dpadCenterColor: "#5E35B1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameboyColor: "#FFC107",
|
||||||
|
htmlColor: "#FFF9C4",
|
||||||
|
buttonColor: "#FF9800",
|
||||||
|
buttonTextColor: "#000000",
|
||||||
|
dpadCenterColor: "#EF6C00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameboyColor: "#8BC34A",
|
||||||
|
htmlColor: "#C5E1A5",
|
||||||
|
buttonColor: "#FF5722",
|
||||||
|
buttonTextColor: "#FFFFFF",
|
||||||
|
dpadCenterColor: "#E64A19",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameboyColor: "#F44336",
|
||||||
|
htmlColor: "#FFCDD2",
|
||||||
|
buttonColor: "#E91E63",
|
||||||
|
buttonTextColor: "#FFFFFF",
|
||||||
|
dpadCenterColor: "#C2185B",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameboyColor: "#03A9F4",
|
||||||
|
htmlColor: "#BBDEFB",
|
||||||
|
buttonColor: "#FFEB3B",
|
||||||
|
buttonTextColor: "#000000",
|
||||||
|
dpadCenterColor: "#0277BD",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameboyColor: "#FF7043",
|
||||||
|
htmlColor: "#FFCCBC",
|
||||||
|
buttonColor: "#FF5722",
|
||||||
|
buttonTextColor: "#FFFFFF",
|
||||||
|
dpadCenterColor: "#D84315",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameboyColor: "#9C27B0",
|
||||||
|
htmlColor: "#E1BEE7",
|
||||||
|
buttonColor: "#7B1FA2",
|
||||||
|
buttonTextColor: "#FFFFFF",
|
||||||
|
dpadCenterColor: "#6A1B9A",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameboyColor: "#FFD700",
|
||||||
|
htmlColor: "#FFF9C4",
|
||||||
|
buttonColor: "#FF9800",
|
||||||
|
buttonTextColor: "#FFFFFF",
|
||||||
|
dpadCenterColor: "#F57F17",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameboyColor: "#009688",
|
||||||
|
htmlColor: "#B2DFDB",
|
||||||
|
buttonColor: "#4CAF50",
|
||||||
|
buttonTextColor: "#FFFFFF",
|
||||||
|
dpadCenterColor: "#00796B",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameboyColor: "#795548",
|
||||||
|
htmlColor: "#D7CCC8",
|
||||||
|
buttonColor: "#9E9E9E",
|
||||||
|
buttonTextColor: "#000000",
|
||||||
|
dpadCenterColor: "#5D4037",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameboyColor: "#FF5733",
|
||||||
|
htmlColor: "#FFCCCB",
|
||||||
|
buttonColor: "#C70039",
|
||||||
|
buttonTextColor: "#FFFFFF",
|
||||||
|
dpadCenterColor: "#B71C1C",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameboyColor: "#00BCD4",
|
||||||
|
htmlColor: "#B2EBF2",
|
||||||
|
buttonColor: "#00ACC1",
|
||||||
|
buttonTextColor: "#FFFFFF",
|
||||||
|
dpadCenterColor: "#00838F",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let currentColorIndex = localStorage.getItem("gameboyColorIndex")
|
||||||
|
? parseInt(localStorage.getItem("gameboyColorIndex"))
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
function updateGameBoyColor() {
|
||||||
|
gameboy.style.backgroundColor = colors[currentColorIndex].gameboyColor;
|
||||||
|
html.style.backgroundColor = colors[currentColorIndex].htmlColor;
|
||||||
|
body.style.backgroundColor = colors[currentColorIndex].htmlColor;
|
||||||
|
|
||||||
|
dpadButtons.forEach((button) => {
|
||||||
|
button.style.backgroundColor = colors[currentColorIndex].buttonColor;
|
||||||
|
button.style.color = colors[currentColorIndex].buttonTextColor;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Using darker dpad center color
|
||||||
|
dpadCenter.style.backgroundColor = colors[currentColorIndex].dpadCenterColor;
|
||||||
|
dpadCenter.style.color = colors[currentColorIndex].buttonTextColor;
|
||||||
|
|
||||||
|
actionButtons.forEach((button) => {
|
||||||
|
button.style.backgroundColor = colors[currentColorIndex].buttonColor;
|
||||||
|
button.style.color = colors[currentColorIndex].buttonTextColor;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
aBtn.addEventListener("click", () => {
|
||||||
|
currentColorIndex = (currentColorIndex - 1 + colors.length) % colors.length;
|
||||||
|
localStorage.setItem("gameboyColorIndex", currentColorIndex);
|
||||||
|
updateGameBoyColor();
|
||||||
|
});
|
||||||
|
|
||||||
|
bBtn.addEventListener("click", () => {
|
||||||
|
currentColorIndex = (currentColorIndex + 1) % colors.length;
|
||||||
|
localStorage.setItem("gameboyColorIndex", currentColorIndex);
|
||||||
|
updateGameBoyColor();
|
||||||
|
});
|
||||||
|
|
||||||
|
updateGameBoyColor();
|
||||||
197
secret/snake/test.html
Normal file
|
|
@ -0,0 +1,197 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Snake Game - GameBoy Style</title>
|
||||||
|
<style>
|
||||||
|
/* Base Reset */
|
||||||
|
body,
|
||||||
|
html {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: monospace;
|
||||||
|
background-color: #3a2d56;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GameBoy Layout */
|
||||||
|
.gameboy {
|
||||||
|
background-color: #5f4c82;
|
||||||
|
width: 441px;
|
||||||
|
height: 735px;
|
||||||
|
border-radius: 20px;
|
||||||
|
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.6);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.gameboy {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Screen */
|
||||||
|
.screen {
|
||||||
|
background-color: black;
|
||||||
|
border: 4px solid #0f380f;
|
||||||
|
width: 90%;
|
||||||
|
height: 55%;
|
||||||
|
margin-top: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
box-shadow: inset 0 4px 8px rgba(0, 0, 0, 0.5);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
border: 1px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Restart Button */
|
||||||
|
#restartBtn {
|
||||||
|
padding: 10px 20px;
|
||||||
|
font-size: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-top: 20px;
|
||||||
|
background-color: #0f380f;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#restartBtn:hover {
|
||||||
|
background-color: #9bbc0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Titles */
|
||||||
|
h1 {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #9bbc0f;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="gameboy">
|
||||||
|
<h1>Snake Game</h1>
|
||||||
|
<div class="screen">
|
||||||
|
<canvas id="snake" width="400" height="400"></canvas>
|
||||||
|
</div>
|
||||||
|
<button id="restartBtn">Restart</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const cvs = document.getElementById("snake");
|
||||||
|
const ctx = cvs.getContext("2d");
|
||||||
|
const box = 20;
|
||||||
|
|
||||||
|
let snake = [{ x: 9 * box, y: 10 * box }];
|
||||||
|
let food = {
|
||||||
|
x: Math.floor(Math.random() * 19 + 1) * box,
|
||||||
|
y: Math.floor(Math.random() * 19 + 1) * box,
|
||||||
|
};
|
||||||
|
let score = 0;
|
||||||
|
let d;
|
||||||
|
let game;
|
||||||
|
|
||||||
|
document.addEventListener("keydown", direction);
|
||||||
|
function direction(event) {
|
||||||
|
let key = event.keyCode;
|
||||||
|
if ((key == 37 || key == 65) && d != "RIGHT") d = "LEFT";
|
||||||
|
else if ((key == 38 || key == 87) && d != "DOWN") d = "UP";
|
||||||
|
else if ((key == 39 || key == 68) && d != "LEFT") d = "RIGHT";
|
||||||
|
else if ((key == 40 || key == 83) && d != "UP") d = "DOWN";
|
||||||
|
}
|
||||||
|
|
||||||
|
function collision(head, array) {
|
||||||
|
return array.some((part) => head.x == part.x && head.y == part.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
ctx.fillStyle = "#0f380f";
|
||||||
|
ctx.fillRect(0, 0, cvs.width, cvs.height);
|
||||||
|
|
||||||
|
ctx.fillStyle = "red";
|
||||||
|
ctx.fillRect(food.x, food.y, box, box);
|
||||||
|
|
||||||
|
for (let i = 0; i < snake.length; i++) {
|
||||||
|
ctx.fillStyle = i == 0 ? "lime" : "white";
|
||||||
|
ctx.fillRect(snake[i].x, snake[i].y, box, box);
|
||||||
|
ctx.strokeStyle = "black";
|
||||||
|
ctx.strokeRect(snake[i].x, snake[i].y, box, box);
|
||||||
|
}
|
||||||
|
|
||||||
|
let snakeX = snake[0].x;
|
||||||
|
let snakeY = snake[0].y;
|
||||||
|
|
||||||
|
if (d == "LEFT") snakeX -= box;
|
||||||
|
if (d == "UP") snakeY -= box;
|
||||||
|
if (d == "RIGHT") snakeX += box;
|
||||||
|
if (d == "DOWN") snakeY += box;
|
||||||
|
|
||||||
|
if (snakeX == food.x && snakeY == food.y) {
|
||||||
|
score++;
|
||||||
|
food = {
|
||||||
|
x: Math.floor(Math.random() * 19 + 1) * box,
|
||||||
|
y: Math.floor(Math.random() * 19 + 1) * box,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
snake.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
let newHead = { x: snakeX, y: snakeY };
|
||||||
|
|
||||||
|
if (
|
||||||
|
snakeX < 0 ||
|
||||||
|
snakeX >= cvs.width ||
|
||||||
|
snakeY < 0 ||
|
||||||
|
snakeY >= cvs.height ||
|
||||||
|
collision(newHead, snake)
|
||||||
|
) {
|
||||||
|
clearInterval(game);
|
||||||
|
document.getElementById("restartBtn").style.display = "block";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
snake.unshift(newHead);
|
||||||
|
|
||||||
|
ctx.fillStyle = "white";
|
||||||
|
ctx.font = "20px Arial";
|
||||||
|
ctx.fillText("Score: " + score, 10, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
function restartGame() {
|
||||||
|
snake = [{ x: 9 * box, y: 10 * box }];
|
||||||
|
food = {
|
||||||
|
x: Math.floor(Math.random() * 19 + 1) * box,
|
||||||
|
y: Math.floor(Math.random() * 19 + 1) * box,
|
||||||
|
};
|
||||||
|
score = 0;
|
||||||
|
d = "";
|
||||||
|
document.getElementById("restartBtn").style.display = "none";
|
||||||
|
game = setInterval(draw, 150);
|
||||||
|
}
|
||||||
|
|
||||||
|
document
|
||||||
|
.getElementById("restartBtn")
|
||||||
|
.addEventListener("click", restartGame);
|
||||||
|
game = setInterval(draw, 150);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
145
secret/styles.css
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: "Courier New", Courier, monospace;
|
||||||
|
background-color: #0d0d0d;
|
||||||
|
color: #b0b0b0;
|
||||||
|
margin: 0;
|
||||||
|
line-height: 1.6;
|
||||||
|
background-image: url("images/background.jpg");
|
||||||
|
background-size: cover; /* Adjust size for tape appearance */
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
background-color: #222; /* Fully opaque background */
|
||||||
|
color: #b0b0b0;
|
||||||
|
text-align: center;
|
||||||
|
padding: 1em 0;
|
||||||
|
font-size: 2rem;
|
||||||
|
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.7);
|
||||||
|
animation: neonFlicker 1.5s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create the flickering neon light effect */
|
||||||
|
@keyframes neonFlicker {
|
||||||
|
0% {
|
||||||
|
text-shadow: 0 0 5px #ffcc00, 0 0 10px #ffcc00, 0 0 15px #ffcc00,
|
||||||
|
0 0 20px #ffcc00, 0 0 30px #ffcc00, 0 0 40px #ffcc00, 0 0 50px #ffcc00;
|
||||||
|
}
|
||||||
|
20% {
|
||||||
|
text-shadow: 0 0 3px #ffcc00, 0 0 7px #ffcc00, 0 0 10px #ffcc00,
|
||||||
|
0 0 15px #ffcc00, 0 0 20px #ffcc00;
|
||||||
|
}
|
||||||
|
40% {
|
||||||
|
text-shadow: 0 0 5px #ffcc00, 0 0 15px #ffcc00, 0 0 25px #ffcc00;
|
||||||
|
}
|
||||||
|
60% {
|
||||||
|
text-shadow: 0 0 5px #ffcc00, 0 0 10px #ffcc00, 0 0 15px #ffcc00,
|
||||||
|
0 0 20px #ffcc00, 0 0 30px #ffcc00;
|
||||||
|
}
|
||||||
|
80% {
|
||||||
|
text-shadow: 0 0 3px #ffcc00, 0 0 7px #ffcc00, 0 0 10px #ffcc00;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
text-shadow: 0 0 5px #ffcc00, 0 0 10px #ffcc00, 0 0 15px #ffcc00,
|
||||||
|
0 0 20px #ffcc00, 0 0 30px #ffcc00, 0 0 40px #ffcc00;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
background-color: #111;
|
||||||
|
color: #b0b0b0;
|
||||||
|
text-align: center;
|
||||||
|
padding: 1em 0;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 20px;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
position: relative;
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.8);
|
||||||
|
transition: transform 0.3s ease, box-shadow 0.3s ease, filter 0.3s ease;
|
||||||
|
width: 100%;
|
||||||
|
height: 400px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
filter: brightness(0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.item .description {
|
||||||
|
padding: 30px;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: #ccc;
|
||||||
|
background-color: rgba(0, 0, 0, 0.8);
|
||||||
|
border-radius: 0 0 10px 10px;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item:hover {
|
||||||
|
transform: scale(1.05);
|
||||||
|
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.9);
|
||||||
|
filter: brightness(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.item:hover img {
|
||||||
|
transform: scale(1.1);
|
||||||
|
filter: brightness(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.item h2 {
|
||||||
|
position: absolute;
|
||||||
|
top: 10%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 1.8rem;
|
||||||
|
background-color: rgba(0, 0, 0, 0.9);
|
||||||
|
padding: 5px 15px;
|
||||||
|
border-radius: 5px;
|
||||||
|
text-align: center;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease, transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item:hover h2 {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(-50%) translateY(-10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 800px) {
|
||||||
|
header {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
height: auto;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-container {
|
||||||
|
grid-template-columns: repeat(1, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 795 B |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
|
@ -1,311 +0,0 @@
|
||||||
class Meteor {
|
|
||||||
constructor(container) {
|
|
||||||
this.element = document.createElement("div");
|
|
||||||
this.element.style.position = "absolute";
|
|
||||||
this.container = container;
|
|
||||||
this.speed = Math.random() * 3 + 4;
|
|
||||||
this.life = 1.0;
|
|
||||||
this.particles = [];
|
|
||||||
|
|
||||||
this.colorType = Math.floor(Math.random() * 4);
|
|
||||||
this.colors = this.getMeteorColors();
|
|
||||||
|
|
||||||
this.containerRect = container.getBoundingClientRect();
|
|
||||||
|
|
||||||
// Start from random edge with PIXEL-based positioning
|
|
||||||
const edge = Math.floor(Math.random() * 4);
|
|
||||||
switch (edge) {
|
|
||||||
case 0:
|
|
||||||
// Top
|
|
||||||
this.x = Math.random() * this.containerRect.width;
|
|
||||||
this.y = -20;
|
|
||||||
case 1:
|
|
||||||
// Right
|
|
||||||
this.x = this.containerRect.width + 20;
|
|
||||||
this.y = Math.random() * this.containerRect.height;
|
|
||||||
case 2:
|
|
||||||
// Bottom
|
|
||||||
this.x = Math.random() * this.containerRect.width;
|
|
||||||
this.y = this.containerRect.height + 20;
|
|
||||||
case 3:
|
|
||||||
// Left
|
|
||||||
this.x = -20;
|
|
||||||
this.y = Math.random() * this.containerRect.height;
|
|
||||||
default:
|
|
||||||
console.log("Error creating Meteor direction");
|
|
||||||
}
|
|
||||||
|
|
||||||
const centerX = this.containerRect.width / 2 + (Math.random() - 0.5) * 200;
|
|
||||||
const centerY = this.containerRect.height / 2 + (Math.random() - 0.5) * 200;
|
|
||||||
this.angle =
|
|
||||||
Math.atan2(centerY - this.y, centerX - this.x) +
|
|
||||||
(Math.random() - 0.5) * 0.8;
|
|
||||||
this.dx = Math.cos(this.angle) * this.speed;
|
|
||||||
this.dy = Math.sin(this.angle) * this.speed;
|
|
||||||
|
|
||||||
this.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
getMeteorColors() {
|
|
||||||
const colorSchemes = [
|
|
||||||
{
|
|
||||||
head: "rgba(255, 255, 255, 1)",
|
|
||||||
core: "rgba(255, 230, 150, 0.9)",
|
|
||||||
mid: "rgba(255, 180, 80, 0.7)",
|
|
||||||
outer: "rgba(255, 100, 50, 0.5)",
|
|
||||||
glow: "rgba(255, 200, 100, 0.9)",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
head: "rgba(255, 255, 255, 1)",
|
|
||||||
core: "rgba(180, 220, 255, 0.9)",
|
|
||||||
mid: "rgba(100, 180, 255, 0.7)",
|
|
||||||
outer: "rgba(50, 100, 255, 0.5)",
|
|
||||||
glow: "rgba(100, 180, 255, 0.9)",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
head: "rgba(255, 255, 255, 1)",
|
|
||||||
core: "rgba(220, 180, 255, 0.9)",
|
|
||||||
mid: "rgba(200, 100, 255, 0.7)",
|
|
||||||
outer: "rgba(180, 50, 255, 0.5)",
|
|
||||||
glow: "rgba(200, 150, 255, 0.9)",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
head: "rgba(255, 255, 255, 1)",
|
|
||||||
core: "rgba(180, 255, 200, 0.9)",
|
|
||||||
mid: "rgba(80, 255, 150, 0.7)",
|
|
||||||
outer: "rgba(50, 200, 100, 0.5)",
|
|
||||||
glow: "rgba(150, 255, 180, 0.9)",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
return colorSchemes[this.colorType];
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
|
||||||
const size = Math.random() * 4 + 10;
|
|
||||||
const colors = this.colors;
|
|
||||||
|
|
||||||
const head = document.createElement("div");
|
|
||||||
head.style.width = `${size}px`;
|
|
||||||
head.style.height = `${size}px`;
|
|
||||||
head.style.borderRadius = "50%";
|
|
||||||
head.style.background = `
|
|
||||||
radial-gradient(
|
|
||||||
circle at 30% 30%,
|
|
||||||
${colors.head} 0%,
|
|
||||||
${colors.core} 30%,
|
|
||||||
${colors.mid} 60%,
|
|
||||||
${colors.outer} 100%
|
|
||||||
)
|
|
||||||
`;
|
|
||||||
head.style.boxShadow = `
|
|
||||||
0 0 ${size * 4}px ${colors.glow},
|
|
||||||
0 0 ${size * 2}px rgba(255, 255, 255, 0.8),
|
|
||||||
inset 0 0 ${size * 0.5}px rgba(255, 255, 255, 0.9)
|
|
||||||
`;
|
|
||||||
head.style.position = "absolute";
|
|
||||||
head.style.left = "50%";
|
|
||||||
head.style.top = "50%";
|
|
||||||
head.style.transform = "translate(-50%, -50%)";
|
|
||||||
head.style.zIndex = "3";
|
|
||||||
head.style.filter = "blur(0.5px)";
|
|
||||||
|
|
||||||
// Create multi-layer tail
|
|
||||||
this.createTailLayer(size * 3, size * 1.2, colors.core, 1, "1px");
|
|
||||||
this.createTailLayer(size * 5, size * 2, colors.mid, 0.6, "2px");
|
|
||||||
this.createTailLayer(size * 8, size * 3, colors.outer, 0.3, "3px");
|
|
||||||
|
|
||||||
// Assemble meteor
|
|
||||||
this.element.appendChild(head);
|
|
||||||
this.element.style.left = `${this.x}px`;
|
|
||||||
this.element.style.top = `${this.y}px`;
|
|
||||||
this.element.style.zIndex = "10";
|
|
||||||
this.element.style.filter = "blur(0.5px)";
|
|
||||||
|
|
||||||
this.head = head;
|
|
||||||
this.size = size;
|
|
||||||
|
|
||||||
this.container.appendChild(this.element);
|
|
||||||
|
|
||||||
this.createTrailParticles();
|
|
||||||
}
|
|
||||||
|
|
||||||
createTailLayer(length, width, color, opacity, blur) {
|
|
||||||
const tail = document.createElement("div");
|
|
||||||
const tailAngle = Math.atan2(this.dy, this.dx) + Math.PI;
|
|
||||||
|
|
||||||
tail.style.width = `${length}px`;
|
|
||||||
tail.style.height = `${width}px`;
|
|
||||||
tail.style.position = "absolute";
|
|
||||||
tail.style.left = "50%";
|
|
||||||
tail.style.top = "50%";
|
|
||||||
tail.style.transformOrigin = "left center";
|
|
||||||
tail.style.transform = `translate(0, -50%) rotate(${tailAngle}rad)`;
|
|
||||||
tail.style.background = `linear-gradient(to right,
|
|
||||||
${color} 0%,
|
|
||||||
${color.replace(")", ", 0.8)").replace("rgba", "rgba")} 20%,
|
|
||||||
${color.replace(")", ", 0.4)").replace("rgba", "rgba")} 50%,
|
|
||||||
${color.replace(")", ", 0.1)").replace("rgba", "rgba")} 80%,
|
|
||||||
transparent 100%)`;
|
|
||||||
tail.style.opacity = opacity;
|
|
||||||
tail.style.filter = `blur(${blur})`;
|
|
||||||
tail.style.pointerEvents = "none";
|
|
||||||
tail.style.zIndex = "2";
|
|
||||||
tail.style.borderRadius = "0 50% 50% 0";
|
|
||||||
|
|
||||||
this.element.appendChild(tail);
|
|
||||||
this.tail = tail;
|
|
||||||
}
|
|
||||||
|
|
||||||
createTrailParticles() {
|
|
||||||
for (let i = 0; i < 5; i++) {
|
|
||||||
this.addTrailParticle();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addTrailParticle() {
|
|
||||||
const particle = document.createElement("div");
|
|
||||||
const size = Math.random() * 2 + 1;
|
|
||||||
const colors = this.colors;
|
|
||||||
|
|
||||||
particle.style.width = `${size}px`;
|
|
||||||
particle.style.height = `${size}px`;
|
|
||||||
particle.style.borderRadius = "50%";
|
|
||||||
particle.style.background = colors.mid;
|
|
||||||
particle.style.position = "absolute";
|
|
||||||
particle.style.left = `${this.x}px`;
|
|
||||||
particle.style.top = `${this.y}px`;
|
|
||||||
particle.style.boxShadow = `0 0 ${size * 3}px ${colors.glow}`;
|
|
||||||
particle.style.opacity = "0.7";
|
|
||||||
particle.style.zIndex = "1";
|
|
||||||
particle.style.pointerEvents = "none";
|
|
||||||
|
|
||||||
this.container.appendChild(particle);
|
|
||||||
|
|
||||||
this.particles.push({
|
|
||||||
element: particle,
|
|
||||||
life: 1.0,
|
|
||||||
x: this.x,
|
|
||||||
y: this.y,
|
|
||||||
vx: (Math.random() - 0.5) * 0.5,
|
|
||||||
vy: (Math.random() - 0.5) * 0.5,
|
|
||||||
size: size,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
update() {
|
|
||||||
this.x += this.dx;
|
|
||||||
this.y += this.dy;
|
|
||||||
this.life -= 0.005;
|
|
||||||
|
|
||||||
this.element.style.left = `${this.x}px`;
|
|
||||||
this.element.style.top = `${this.y}px`;
|
|
||||||
this.element.style.opacity = this.life;
|
|
||||||
|
|
||||||
// Update tail direction
|
|
||||||
const tailAngle = Math.atan2(this.dy, this.dx) + Math.PI;
|
|
||||||
const tails = this.element.querySelectorAll("div:not(.particle)");
|
|
||||||
tails.forEach((tail) => {
|
|
||||||
if (tail !== this.head) {
|
|
||||||
tail.style.transform = `translate(0, -50%) rotate(${tailAngle}rad)`;
|
|
||||||
tail.style.opacity = this.life;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (Math.random() < 0.3) {
|
|
||||||
this.addTrailParticle();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update existing particles with pixel positioning
|
|
||||||
for (let i = this.particles.length - 1; i >= 0; i--) {
|
|
||||||
const p = this.particles[i];
|
|
||||||
p.life -= 0.02;
|
|
||||||
p.x += p.vx;
|
|
||||||
p.y += p.vy;
|
|
||||||
|
|
||||||
p.element.style.left = `${p.x}px`;
|
|
||||||
p.element.style.top = `${p.y}px`;
|
|
||||||
p.element.style.opacity = p.life;
|
|
||||||
p.element.style.transform = `scale(${p.life})`;
|
|
||||||
|
|
||||||
if (p.life <= 0) {
|
|
||||||
p.element.remove();
|
|
||||||
this.particles.splice(i, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
this.x < -50 ||
|
|
||||||
this.x > this.containerRect.width + 50 ||
|
|
||||||
this.y < -50 ||
|
|
||||||
this.y > this.containerRect.height + 50 ||
|
|
||||||
this.life <= 0
|
|
||||||
) {
|
|
||||||
// Fade out particles
|
|
||||||
this.particles.forEach((p) => {
|
|
||||||
p.element.style.transition = "opacity 0.5s";
|
|
||||||
p.element.style.opacity = "0";
|
|
||||||
setTimeout(() => p.element.remove(), 500);
|
|
||||||
});
|
|
||||||
this.remove();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
remove() {
|
|
||||||
if (this.life > 0.3) {
|
|
||||||
this.createExplosion();
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
this.element.remove();
|
|
||||||
}, 300);
|
|
||||||
}
|
|
||||||
|
|
||||||
createExplosion() {
|
|
||||||
for (let i = 0; i < 8; i++) {
|
|
||||||
const particle = document.createElement("div");
|
|
||||||
const size = Math.random() * 3 + 2;
|
|
||||||
const colors = this.colors;
|
|
||||||
|
|
||||||
particle.style.width = `${size}px`;
|
|
||||||
particle.style.height = `${size}px`;
|
|
||||||
particle.style.borderRadius = "50%";
|
|
||||||
particle.style.background = colors.core;
|
|
||||||
particle.style.position = "absolute";
|
|
||||||
particle.style.left = `${this.x}px`;
|
|
||||||
particle.style.top = `${this.y}px`;
|
|
||||||
particle.style.boxShadow = `0 0 ${size * 4}px ${colors.glow}`;
|
|
||||||
particle.style.opacity = "0.8";
|
|
||||||
particle.style.zIndex = "2";
|
|
||||||
|
|
||||||
this.container.appendChild(particle);
|
|
||||||
|
|
||||||
// Animate explosion
|
|
||||||
const angle = (i / 8) * Math.PI * 2;
|
|
||||||
const distance = Math.random() * 20 + 10;
|
|
||||||
const duration = Math.random() * 500 + 500;
|
|
||||||
|
|
||||||
particle.animate(
|
|
||||||
[
|
|
||||||
{
|
|
||||||
transform: "translate(0, 0) scale(1)",
|
|
||||||
opacity: 0.8,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
transform: `translate(${Math.cos(angle) * distance}px, ${
|
|
||||||
Math.sin(angle) * distance
|
|
||||||
}px) scale(0.1)`,
|
|
||||||
opacity: 0,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
{
|
|
||||||
duration: duration,
|
|
||||||
easing: "cubic-bezier(0.4, 0, 0.2, 1)",
|
|
||||||
}
|
|
||||||
).onfinish = () => particle.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Meteor;
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
class Star {
|
|
||||||
constructor(container) {
|
|
||||||
this.element = document.createElement("div");
|
|
||||||
this.element.classList.add("star");
|
|
||||||
this.container = container;
|
|
||||||
this.speed = Math.random() * 0.5 + 0.1;
|
|
||||||
|
|
||||||
// Random direction angle (0 to 360 degrees)
|
|
||||||
this.angle = Math.random() * Math.PI * 2;
|
|
||||||
this.dx = Math.cos(this.angle) * this.speed;
|
|
||||||
this.dy = Math.sin(this.angle) * this.speed;
|
|
||||||
|
|
||||||
this.x = Math.random() * 100;
|
|
||||||
this.y = Math.random() * 100;
|
|
||||||
|
|
||||||
this.color = this.getRandomStarColor();
|
|
||||||
this.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
getRandomStarColor() {
|
|
||||||
const colors = [
|
|
||||||
"rgba(255, 255, 255, 0.9)", // Pure white
|
|
||||||
"rgba(255, 250, 200, 0.9)", // Warm white
|
|
||||||
"rgba(200, 220, 255, 0.9)", // Cool blue-white
|
|
||||||
"rgba(255, 220, 180, 0.9)", // Yellow-white
|
|
||||||
"rgba(180, 200, 255, 0.9)", // Blue-white
|
|
||||||
];
|
|
||||||
return colors[Math.floor(Math.random() * colors.length)];
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
|
||||||
const size = Math.random() * 3 + 0.5;
|
|
||||||
this.element.style.width = `${size}px`;
|
|
||||||
this.element.style.height = `${size}px`;
|
|
||||||
this.element.style.left = `${this.x}%`;
|
|
||||||
this.element.style.top = `${this.y}%`;
|
|
||||||
this.element.style.background = this.color;
|
|
||||||
this.element.style.boxShadow = `0 0 ${size * 2}px ${this.color}`;
|
|
||||||
this.element.style.animationDelay = `${Math.random() * 5}s`;
|
|
||||||
this.element.style.animationDuration = `${3 + Math.random() * 4}s`;
|
|
||||||
this.container.appendChild(this.element);
|
|
||||||
}
|
|
||||||
|
|
||||||
update() {
|
|
||||||
this.x += this.dx * 0.05;
|
|
||||||
this.y += this.dy * 0.05;
|
|
||||||
|
|
||||||
// Wrap around when star goes off screen
|
|
||||||
if (this.y > 100) this.y = 0;
|
|
||||||
if (this.y < 0) this.y = 100;
|
|
||||||
if (this.x > 100) this.x = 0;
|
|
||||||
if (this.x < 0) this.x = 100;
|
|
||||||
|
|
||||||
this.element.style.left = `${this.x}%`;
|
|
||||||
this.element.style.top = `${this.y}%`;
|
|
||||||
}
|
|
||||||
|
|
||||||
remove() {
|
|
||||||
this.element.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Star;
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
import Star from "./Star.js";
|
|
||||||
import Meteor from "./Meteor.js";
|
|
||||||
|
|
||||||
const starInstances = [];
|
|
||||||
const meteorInstances = [];
|
|
||||||
|
|
||||||
function createStars() {
|
|
||||||
const stars = document.getElementById("stars");
|
|
||||||
const count = 400;
|
|
||||||
stars.innerHTML = "";
|
|
||||||
starInstances.length = 0;
|
|
||||||
|
|
||||||
for (let i = 0; i < count; i++) {
|
|
||||||
const star = new Star(stars);
|
|
||||||
starInstances.push(star);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function injectStarCSS() {
|
|
||||||
const style = document.createElement("style");
|
|
||||||
style.textContent = `
|
|
||||||
.star {
|
|
||||||
position: absolute;
|
|
||||||
border-radius: 50%;
|
|
||||||
animation: twinkle ease-in-out infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes twinkle {
|
|
||||||
0%, 100% { opacity: 1; }
|
|
||||||
50% { opacity: 0.3; }
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
document.head.appendChild(style);
|
|
||||||
}
|
|
||||||
|
|
||||||
function trySpawnMeteor() {
|
|
||||||
const spawnChance = 0.12;
|
|
||||||
if (Math.random() < spawnChance) {
|
|
||||||
const stars = document.getElementById("stars");
|
|
||||||
const newMeteor = new Meteor(stars);
|
|
||||||
meteorInstances.push(newMeteor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function animateStars() {
|
|
||||||
starInstances.forEach((star) => star.update());
|
|
||||||
|
|
||||||
// Update meteors and remove dead ones
|
|
||||||
for (let i = meteorInstances.length - 1; i >= 0; i--) {
|
|
||||||
if (!meteorInstances[i].update()) {
|
|
||||||
meteorInstances.splice(i, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
requestAnimationFrame(animateStars);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createMeteorBurst() {
|
|
||||||
for (let i = 0; i < 3; i++) {
|
|
||||||
setTimeout(() => {
|
|
||||||
const stars = document.getElementById("stars");
|
|
||||||
const newMeteor = new Meteor(stars);
|
|
||||||
meteorInstances.push(newMeteor);
|
|
||||||
}, i * 200);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function init() {
|
|
||||||
injectStarCSS();
|
|
||||||
createStars();
|
|
||||||
animateStars();
|
|
||||||
|
|
||||||
setInterval(trySpawnMeteor, 2000);
|
|
||||||
setInterval(createMeteorBurst, 15000);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (document.readyState === "loading") {
|
|
||||||
document.addEventListener("DOMContentLoaded", init);
|
|
||||||
} else {
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
// Form submission
|
|
||||||
function setupForm() {
|
|
||||||
const form = document.getElementById("contact-form");
|
|
||||||
if (form) {
|
|
||||||
form.addEventListener("submit", function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
// Simple form validation
|
|
||||||
const name = document.getElementById("name").value;
|
|
||||||
const email = document.getElementById("email").value;
|
|
||||||
const message = document.getElementById("message").value;
|
|
||||||
|
|
||||||
if (name && email && message) {
|
|
||||||
// In a real implementation, you would send the form data to a server
|
|
||||||
alert("Thank you for your message! We will get back to you soon.");
|
|
||||||
form.reset();
|
|
||||||
} else {
|
|
||||||
alert("Please fill in all required fields.");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
// Initialize everything when DOM is loaded
|
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
|
||||||
// Initialize components
|
|
||||||
if (typeof createStars === "function") createStars();
|
|
||||||
if (typeof updateNavigation === "function") updateNavigation();
|
|
||||||
if (typeof setupForm === "function") setupForm();
|
|
||||||
if (typeof setupMobileNavigation === "function") setupMobileNavigation();
|
|
||||||
|
|
||||||
// Set up event listeners
|
|
||||||
window.addEventListener("scroll", updateNavigation);
|
|
||||||
|
|
||||||
// Smooth scrolling for navigation links
|
|
||||||
document.querySelectorAll('a[href^="#"]').forEach((anchor) => {
|
|
||||||
anchor.addEventListener("click", function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const targetId = this.getAttribute("href");
|
|
||||||
if (targetId === "#") return;
|
|
||||||
|
|
||||||
const targetElement = document.querySelector(targetId);
|
|
||||||
if (targetElement) {
|
|
||||||
targetElement.scrollIntoView({
|
|
||||||
behavior: "smooth",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -1,106 +0,0 @@
|
||||||
// Navigation JavaScript
|
|
||||||
let lastScrollY = window.scrollY;
|
|
||||||
let ticking = false;
|
|
||||||
|
|
||||||
function updateNavigation() {
|
|
||||||
const navigation = document.getElementById("navigation");
|
|
||||||
const scrolled = window.scrollY > 50;
|
|
||||||
|
|
||||||
if (scrolled) {
|
|
||||||
navigation.classList.add("scrolled");
|
|
||||||
|
|
||||||
// Hide/show nav on scroll
|
|
||||||
if (window.scrollY > lastScrollY && window.scrollY > 100) {
|
|
||||||
navigation.classList.add("hidden");
|
|
||||||
} else {
|
|
||||||
navigation.classList.remove("hidden");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
navigation.classList.remove("scrolled", "hidden");
|
|
||||||
}
|
|
||||||
|
|
||||||
lastScrollY = window.scrollY;
|
|
||||||
ticking = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function requestTick() {
|
|
||||||
if (!ticking) {
|
|
||||||
requestAnimationFrame(updateNavigation);
|
|
||||||
ticking = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mobile navigation functionality
|
|
||||||
function setupMobileNavigation() {
|
|
||||||
const burgerMenu = document.getElementById("burger-menu");
|
|
||||||
const mainNav = document.getElementById("main-nav");
|
|
||||||
const body = document.body;
|
|
||||||
|
|
||||||
// Create overlay for mobile menu
|
|
||||||
const overlay = document.createElement("div");
|
|
||||||
overlay.className = "nav-overlay";
|
|
||||||
document.body.appendChild(overlay);
|
|
||||||
|
|
||||||
if (burgerMenu && mainNav) {
|
|
||||||
burgerMenu.addEventListener("click", function () {
|
|
||||||
const isActive = mainNav.classList.contains("active");
|
|
||||||
|
|
||||||
if (isActive) {
|
|
||||||
// Close menu
|
|
||||||
mainNav.classList.remove("active");
|
|
||||||
burgerMenu.classList.remove("active");
|
|
||||||
overlay.classList.remove("active");
|
|
||||||
body.classList.remove("nav-open");
|
|
||||||
} else {
|
|
||||||
// Open menu
|
|
||||||
mainNav.classList.add("active");
|
|
||||||
burgerMenu.classList.add("active");
|
|
||||||
overlay.classList.add("active");
|
|
||||||
body.classList.add("nav-open");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Close menu when clicking on overlay
|
|
||||||
overlay.addEventListener("click", function () {
|
|
||||||
mainNav.classList.remove("active");
|
|
||||||
burgerMenu.classList.remove("active");
|
|
||||||
overlay.classList.remove("active");
|
|
||||||
body.classList.remove("nav-open");
|
|
||||||
});
|
|
||||||
|
|
||||||
// Close menu when clicking on a link
|
|
||||||
const navLinks = mainNav.querySelectorAll("a");
|
|
||||||
navLinks.forEach((link) => {
|
|
||||||
link.addEventListener("click", function () {
|
|
||||||
mainNav.classList.remove("active");
|
|
||||||
burgerMenu.classList.remove("active");
|
|
||||||
overlay.classList.remove("active");
|
|
||||||
body.classList.remove("nav-open");
|
|
||||||
|
|
||||||
// Update active state
|
|
||||||
navLinks.forEach((l) => l.classList.remove("active"));
|
|
||||||
this.classList.add("active");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Set initial active state based on current hash
|
|
||||||
function setActiveNavLink() {
|
|
||||||
const currentHash = window.location.hash || "#home";
|
|
||||||
navLinks.forEach((link) => {
|
|
||||||
if (link.getAttribute("href") === currentHash) {
|
|
||||||
link.classList.add("active");
|
|
||||||
} else {
|
|
||||||
link.classList.remove("active");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update active state on hash change
|
|
||||||
window.addEventListener("hashchange", setActiveNavLink);
|
|
||||||
setActiveNavLink();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize navigation
|
|
||||||
window.addEventListener("scroll", requestTick);
|
|
||||||
window.addEventListener("load", updateNavigation);
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
// Counter animation for stats
|
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
|
||||||
const counters = document.querySelectorAll(".stat-number");
|
|
||||||
let hasCounted = false;
|
|
||||||
|
|
||||||
function animateCounters() {
|
|
||||||
if (hasCounted) return;
|
|
||||||
|
|
||||||
counters.forEach((counter) => {
|
|
||||||
const target = parseInt(counter.getAttribute("data-count"));
|
|
||||||
const duration = 2000; // 2 seconds
|
|
||||||
const frameDuration = 1000 / 60; // 60 frames per second
|
|
||||||
const totalFrames = Math.round(duration / frameDuration);
|
|
||||||
let frame = 0;
|
|
||||||
|
|
||||||
const counterInterval = setInterval(() => {
|
|
||||||
frame++;
|
|
||||||
const progress = frame / totalFrames;
|
|
||||||
const currentCount = Math.round(target * progress);
|
|
||||||
|
|
||||||
counter.textContent = currentCount;
|
|
||||||
|
|
||||||
if (frame === totalFrames) {
|
|
||||||
clearInterval(counterInterval);
|
|
||||||
}
|
|
||||||
}, frameDuration);
|
|
||||||
|
|
||||||
counter.classList.add("animated");
|
|
||||||
});
|
|
||||||
|
|
||||||
hasCounted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if element is in viewport
|
|
||||||
function isInViewport(element) {
|
|
||||||
const rect = element.getBoundingClientRect();
|
|
||||||
return (
|
|
||||||
rect.top >= 0 &&
|
|
||||||
rect.left >= 0 &&
|
|
||||||
rect.bottom <=
|
|
||||||
(window.innerHeight || document.documentElement.clientHeight) &&
|
|
||||||
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check on scroll and on load
|
|
||||||
function checkCounters() {
|
|
||||||
const statsContainer = document.querySelector(".stats-container");
|
|
||||||
if (statsContainer && isInViewport(statsContainer)) {
|
|
||||||
animateCounters();
|
|
||||||
window.removeEventListener("scroll", checkCounters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("scroll", checkCounters);
|
|
||||||
checkCounters(); // Check on page load
|
|
||||||
});
|
|
||||||
|
|
@ -1,228 +0,0 @@
|
||||||
class PortfolioCard extends HTMLElement {
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
const shadow = this.attachShadow({ mode: "open" });
|
|
||||||
|
|
||||||
const icon = this.getAttribute("icon") || "fas fa-code";
|
|
||||||
const title = this.getAttribute("title") || "Project Title";
|
|
||||||
const desc = this.getAttribute("desc") || "Project description goes here.";
|
|
||||||
const tags = (this.getAttribute("tags") || "").split(",");
|
|
||||||
const link = this.getAttribute("link") || "#";
|
|
||||||
const collaboration = this.getAttribute("collaboration") || "";
|
|
||||||
|
|
||||||
shadow.innerHTML = `
|
|
||||||
<!-- Font Awesome inside Shadow DOM -->
|
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
||||||
<!-- Use global styles.css from index.html -->
|
|
||||||
<link rel="stylesheet" href="src/styles/styles.css">
|
|
||||||
|
|
||||||
<style>
|
|
||||||
/* Collaboration badge styling */
|
|
||||||
.collaboration-badge {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.4rem;
|
|
||||||
background: linear-gradient(135deg, #3b82f6, #2563eb);
|
|
||||||
color: white;
|
|
||||||
padding: 0.3rem 0.8rem;
|
|
||||||
border-radius: 20px;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
animation: pulse 2s infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes pulse {
|
|
||||||
0% {
|
|
||||||
box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.4);
|
|
||||||
}
|
|
||||||
70% {
|
|
||||||
box-shadow: 0 0 0 10px rgba(59, 130, 246, 0);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
box-shadow: 0 0 0 0 rgba(59, 130, 246, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Enhanced card animations */
|
|
||||||
.portfolio-item {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(20px);
|
|
||||||
animation: fadeInUp 0.6s ease forwards;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes fadeInUp {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(20px);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Portfolio title with icon */
|
|
||||||
.portfolio-title {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.portfolio-content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
flex: 1;
|
|
||||||
padding: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.portfolio-tags {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 0.5rem;
|
|
||||||
margin-bottom: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Full-width Enhanced Button Styles */
|
|
||||||
.portfolio-btn-container {
|
|
||||||
margin-top: auto;
|
|
||||||
padding: 0 1.5rem 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.portfolio-btn {
|
|
||||||
width: 100%;
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
z-index: 1;
|
|
||||||
padding: 1rem 1.8rem;
|
|
||||||
border-radius: 8px;
|
|
||||||
font-weight: 600;
|
|
||||||
letter-spacing: 0.5px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
transition: all 0.4s cubic-bezier(0.165, 0.84, 0.44, 1);
|
|
||||||
border: 2px solid transparent;
|
|
||||||
background: linear-gradient(135deg, var(--purple), var(--purple-dark));
|
|
||||||
color: white;
|
|
||||||
box-shadow: 0 4px 15px var(--shadow-purple);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.8rem;
|
|
||||||
text-decoration: none;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.portfolio-btn::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: linear-gradient(135deg, var(--purple-dark), var(--purple-muted));
|
|
||||||
z-index: -1;
|
|
||||||
transition: transform 0.6s cubic-bezier(0.165, 0.84, 0.44, 1);
|
|
||||||
transform: scaleX(0);
|
|
||||||
transform-origin: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.portfolio-btn:hover::before {
|
|
||||||
transform: scaleX(1);
|
|
||||||
transform-origin: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.portfolio-btn:hover {
|
|
||||||
transform: translateY(-3px);
|
|
||||||
box-shadow: 0 8px 25px rgba(168, 85, 247, 0.4);
|
|
||||||
border-color: var(--purple-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
.portfolio-btn:active {
|
|
||||||
transform: translateY(0);
|
|
||||||
box-shadow: 0 4px 15px var(--shadow-purple);
|
|
||||||
}
|
|
||||||
|
|
||||||
.portfolio-btn i {
|
|
||||||
font-size: 1rem;
|
|
||||||
transition: transform 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.portfolio-btn:hover i {
|
|
||||||
transform: translateX(4px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.portfolio-btn::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: -50%;
|
|
||||||
left: -50%;
|
|
||||||
width: 200%;
|
|
||||||
height: 200%;
|
|
||||||
background: linear-gradient(
|
|
||||||
to bottom right,
|
|
||||||
rgba(255, 255, 255, 0.2),
|
|
||||||
rgba(255, 255, 255, 0.1) 20%,
|
|
||||||
rgba(255, 255, 255, 0) 50%,
|
|
||||||
rgba(255, 255, 255, 0) 100%
|
|
||||||
);
|
|
||||||
transform: rotate(30deg) translateY(-150%);
|
|
||||||
transition: transform 0.6s cubic-bezier(0.165, 0.84, 0.44, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.portfolio-btn:hover::after {
|
|
||||||
transform: rotate(30deg) translateY(150%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.portfolio-btn:focus {
|
|
||||||
outline: 2px solid var(--purple-light);
|
|
||||||
outline-offset: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Responsive adjustments */
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.portfolio-btn {
|
|
||||||
padding: 0.9rem 1.5rem;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="portfolio-item">
|
|
||||||
<div class="portfolio-img">
|
|
||||||
<i class="${icon}"></i>
|
|
||||||
</div>
|
|
||||||
<div class="portfolio-content">
|
|
||||||
<h3 class="portfolio-title">
|
|
||||||
<i class="${icon}"></i> ${title}
|
|
||||||
</h3>
|
|
||||||
<p class="portfolio-desc">${desc}</p>
|
|
||||||
${
|
|
||||||
collaboration
|
|
||||||
? `
|
|
||||||
<div class="collaboration-badge">
|
|
||||||
<i class="fas fa-users"></i> ${collaboration}
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
: ""
|
|
||||||
}
|
|
||||||
<div class="portfolio-tags">
|
|
||||||
${tags
|
|
||||||
.map((tag) => `<span class="tag">${tag.trim()}</span>`)
|
|
||||||
.join("")}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="portfolio-btn-container">
|
|
||||||
<a href="${link}" target="_blank" class="portfolio-btn">
|
|
||||||
<i class="fas fa-arrow-right"></i> Explore Project
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("portfolio-card", PortfolioCard);
|
|
||||||
|
|
@ -1,95 +0,0 @@
|
||||||
// Portfolio filtering functionality
|
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
|
||||||
// Get all portfolio items
|
|
||||||
const portfolioItems = document.querySelectorAll("portfolio-card");
|
|
||||||
const filterButtons = document.querySelectorAll(".filter-btn");
|
|
||||||
const searchInput = document.getElementById("project-search");
|
|
||||||
|
|
||||||
// Create a container for no results message
|
|
||||||
const noResults = document.createElement("div");
|
|
||||||
noResults.className = "no-results";
|
|
||||||
noResults.innerHTML = `
|
|
||||||
<i class="fas fa-search"></i>
|
|
||||||
<h3>No projects found</h3>
|
|
||||||
<p>Try adjusting your search or filter criteria</p>
|
|
||||||
`;
|
|
||||||
|
|
||||||
// Function to filter portfolio items
|
|
||||||
function filterPortfolio() {
|
|
||||||
const activeFilter =
|
|
||||||
document.querySelector(".filter-btn.active").dataset.filter;
|
|
||||||
const searchTerm = searchInput.value.toLowerCase();
|
|
||||||
let visibleItems = 0;
|
|
||||||
|
|
||||||
portfolioItems.forEach((item) => {
|
|
||||||
const tags = item.getAttribute("tags").toLowerCase();
|
|
||||||
const title = item.getAttribute("title").toLowerCase();
|
|
||||||
const desc = item.getAttribute("desc").toLowerCase();
|
|
||||||
|
|
||||||
// Check if item matches the active filter
|
|
||||||
const matchesFilter =
|
|
||||||
activeFilter === "all" ||
|
|
||||||
tags.includes(activeFilter) ||
|
|
||||||
title.includes(activeFilter) ||
|
|
||||||
desc.includes(activeFilter);
|
|
||||||
|
|
||||||
// Check if item matches the search term
|
|
||||||
const matchesSearch =
|
|
||||||
searchTerm === "" ||
|
|
||||||
title.includes(searchTerm) ||
|
|
||||||
desc.includes(searchTerm) ||
|
|
||||||
tags.includes(searchTerm);
|
|
||||||
|
|
||||||
// Show or hide the item based on filters
|
|
||||||
if (matchesFilter && matchesSearch) {
|
|
||||||
item.style.display = "block";
|
|
||||||
visibleItems++;
|
|
||||||
|
|
||||||
// Add animation for appearing items
|
|
||||||
item.style.animation = "fadeInUp 0.5s ease forwards";
|
|
||||||
} else {
|
|
||||||
item.style.display = "none";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Show no results message if needed
|
|
||||||
const portfolioGrid = document.querySelector(".portfolio-grid");
|
|
||||||
const existingNoResults = portfolioGrid.querySelector(".no-results");
|
|
||||||
|
|
||||||
if (visibleItems === 0) {
|
|
||||||
if (!existingNoResults) {
|
|
||||||
portfolioGrid.appendChild(noResults);
|
|
||||||
}
|
|
||||||
} else if (existingNoResults) {
|
|
||||||
portfolioGrid.removeChild(existingNoResults);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add click event listeners to filter buttons
|
|
||||||
filterButtons.forEach((button) => {
|
|
||||||
button.addEventListener("click", function () {
|
|
||||||
// Remove active class from all buttons
|
|
||||||
filterButtons.forEach((btn) => btn.classList.remove("active"));
|
|
||||||
|
|
||||||
// Add active class to clicked button
|
|
||||||
this.classList.add("active");
|
|
||||||
|
|
||||||
// Filter portfolio items
|
|
||||||
filterPortfolio();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add input event listener to search field
|
|
||||||
searchInput.addEventListener("input", filterPortfolio);
|
|
||||||
|
|
||||||
// Add keyboard shortcut for search (Ctrl/Cmd + F)
|
|
||||||
document.addEventListener("keydown", function (e) {
|
|
||||||
if ((e.ctrlKey || e.metaKey) && e.key === "f") {
|
|
||||||
e.preventDefault();
|
|
||||||
searchInput.focus();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Initialize filter on page load
|
|
||||||
filterPortfolio();
|
|
||||||
});
|
|
||||||
|
|
@ -1,111 +0,0 @@
|
||||||
// Section tracking and navigation highlighting
|
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
|
||||||
const sections = document.querySelectorAll("section[id]");
|
|
||||||
const navLinks = document.querySelectorAll(".nav-links a");
|
|
||||||
|
|
||||||
// Configuration
|
|
||||||
const offset = 100; // Offset for when section becomes "active" (accounts for fixed nav)
|
|
||||||
let isScrolling = false;
|
|
||||||
|
|
||||||
function getCurrentSection() {
|
|
||||||
let currentSection = "";
|
|
||||||
const scrollPosition = window.scrollY + offset;
|
|
||||||
|
|
||||||
// Find which section we're currently in
|
|
||||||
sections.forEach((section) => {
|
|
||||||
const sectionTop = section.offsetTop;
|
|
||||||
const sectionHeight = section.offsetHeight;
|
|
||||||
const sectionId = section.getAttribute("id");
|
|
||||||
|
|
||||||
if (
|
|
||||||
scrollPosition >= sectionTop &&
|
|
||||||
scrollPosition < sectionTop + sectionHeight
|
|
||||||
) {
|
|
||||||
currentSection = sectionId;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Special case: if we're at the very top, activate the first section
|
|
||||||
if (window.scrollY < 100) {
|
|
||||||
currentSection = sections[0]?.getAttribute("id") || "";
|
|
||||||
}
|
|
||||||
|
|
||||||
return currentSection;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateActiveNavLink() {
|
|
||||||
const currentSection = getCurrentSection();
|
|
||||||
|
|
||||||
navLinks.forEach((link) => {
|
|
||||||
const href = link.getAttribute("href");
|
|
||||||
|
|
||||||
// Remove active class from all links
|
|
||||||
link.classList.remove("active");
|
|
||||||
|
|
||||||
// Add active class to matching link
|
|
||||||
if (href === `#${currentSection}`) {
|
|
||||||
link.classList.add("active");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Throttle scroll events for better performance
|
|
||||||
function handleScroll() {
|
|
||||||
if (!isScrolling) {
|
|
||||||
window.requestAnimationFrame(() => {
|
|
||||||
updateActiveNavLink();
|
|
||||||
isScrolling = false;
|
|
||||||
});
|
|
||||||
isScrolling = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Listen for scroll events
|
|
||||||
window.addEventListener("scroll", handleScroll);
|
|
||||||
|
|
||||||
// Update on page load
|
|
||||||
updateActiveNavLink();
|
|
||||||
|
|
||||||
// Also update when clicking nav links (for smooth scroll)
|
|
||||||
navLinks.forEach((link) => {
|
|
||||||
link.addEventListener("click", function (e) {
|
|
||||||
// Remove active from all
|
|
||||||
navLinks.forEach((l) => l.classList.remove("active"));
|
|
||||||
// Add active to clicked link
|
|
||||||
this.classList.add("active");
|
|
||||||
|
|
||||||
// Let the scroll handler update it properly after scroll completes
|
|
||||||
setTimeout(updateActiveNavLink, 100);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle hash changes (browser back/forward)
|
|
||||||
window.addEventListener("hashchange", function () {
|
|
||||||
setTimeout(updateActiveNavLink, 100);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Intersection Observer for more precise tracking (progressive enhancement)
|
|
||||||
if ("IntersectionObserver" in window) {
|
|
||||||
const observerOptions = {
|
|
||||||
rootMargin: "-20% 0px -70% 0px", // Trigger when section is roughly in the middle of viewport
|
|
||||||
threshold: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
const observer = new IntersectionObserver((entries) => {
|
|
||||||
entries.forEach((entry) => {
|
|
||||||
if (entry.isIntersecting) {
|
|
||||||
const sectionId = entry.target.getAttribute("id");
|
|
||||||
navLinks.forEach((link) => {
|
|
||||||
link.classList.remove("active");
|
|
||||||
if (link.getAttribute("href") === `#${sectionId}`) {
|
|
||||||
link.classList.add("active");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, observerOptions);
|
|
||||||
|
|
||||||
// Observe all sections
|
|
||||||
sections.forEach((section) => observer.observe(section));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
432
styles.css
Normal file
|
|
@ -0,0 +1,432 @@
|
||||||
|
/*
|
||||||
|
interstellar_development website
|
||||||
|
Copyright (C) 2024 interstellar_development
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--background-color: #0b0c1d;
|
||||||
|
--text-color: #c7d5e0;
|
||||||
|
--accent-color: #ffdd55;
|
||||||
|
--accent-hover-color: #ffd700;
|
||||||
|
--dark-blue: rgba(31, 42, 64, 1);
|
||||||
|
--dark-blue-translucent: rgba(31, 42, 64, 0.9);
|
||||||
|
--light-blue: rgba(46, 58, 95, 0.8);
|
||||||
|
--border-radius: 8px;
|
||||||
|
--transition-speed: 0.3s;
|
||||||
|
--box-shadow: 0 2px 15px rgba(0, 0, 0, 0.7);
|
||||||
|
--font-size-large: 2.5em;
|
||||||
|
--font-size-medium: 1.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset and normalize */
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Body styling */
|
||||||
|
body {
|
||||||
|
background-color: var(--background-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
font-family: "Arial", sans-serif;
|
||||||
|
line-height: 1.6;
|
||||||
|
padding: 0 20px;
|
||||||
|
background: url("images/star.jpg") no-repeat center center fixed;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header Styling */
|
||||||
|
/* Header Styling */
|
||||||
|
header {
|
||||||
|
background-color: var(--dark-blue);
|
||||||
|
height: 5em;
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
padding: 15px 20px;
|
||||||
|
box-shadow: var(--box-shadow);
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
|
z-index: 100;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Burger Menu Styling */
|
||||||
|
.burger-menu {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 1.8em;
|
||||||
|
cursor: pointer;
|
||||||
|
display: block;
|
||||||
|
padding: 0;
|
||||||
|
z-index: 110;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dropdown Menu (Hidden by Default) */
|
||||||
|
.div-menu {
|
||||||
|
z-index: 1;
|
||||||
|
background-color: var(--light-blue);
|
||||||
|
width: 100%;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||||
|
padding: 0;
|
||||||
|
margin-top: 0px;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.div-menu li {
|
||||||
|
margin: 0;
|
||||||
|
padding: 1em;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.div-menu a {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px 0;
|
||||||
|
text-align: center;
|
||||||
|
color: #ffffff;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.div-menu a:hover {
|
||||||
|
background-color: #34495e;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Menu Animation and Styling */
|
||||||
|
.menu {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: var(--light-blue);
|
||||||
|
position: absolute;
|
||||||
|
top: -50vh;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 5;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
list-style: none;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bolder;
|
||||||
|
font-size: large;
|
||||||
|
transition: top 0.5s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu.active {
|
||||||
|
display: flex;
|
||||||
|
top: 4em;
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header Content Container */
|
||||||
|
.header-content {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 20px;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Project Name Styling */
|
||||||
|
.project-name {
|
||||||
|
font-size: 2em;
|
||||||
|
color: var(--accent-color);
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color var(--transition-speed), text-shadow var(--transition-speed);
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-name:hover {
|
||||||
|
color: var(--accent-hover-color);
|
||||||
|
text-shadow: 0 0 10px var(--accent-hover-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Article styling */
|
||||||
|
article {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 6.25em auto;
|
||||||
|
padding: 20px;
|
||||||
|
background-color: var(--dark-blue-translucent);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
box-shadow: var(--box-shadow);
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
article h1 {
|
||||||
|
font-size: var(--font-size-large);
|
||||||
|
margin-bottom: 20px;
|
||||||
|
color: var(--accent-color);
|
||||||
|
text-shadow: 0 0 15px var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
article p {
|
||||||
|
color: var(--text-color);
|
||||||
|
margin-bottom: 20px;
|
||||||
|
hyphens: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Download list */
|
||||||
|
article h2 {
|
||||||
|
font-size: var(--font-size-medium);
|
||||||
|
margin: 20px 0 10px;
|
||||||
|
color: var(--accent-color);
|
||||||
|
text-shadow: 0 0 10px var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
article ul {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
article ul a li {
|
||||||
|
background-color: var(--light-blue);
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
padding: 10px;
|
||||||
|
transition: background-color var(--transition-speed),
|
||||||
|
box-shadow var(--transition-speed);
|
||||||
|
}
|
||||||
|
|
||||||
|
article ul a li:hover {
|
||||||
|
background-color: rgba(68, 80, 124, 0.9);
|
||||||
|
box-shadow: 0 0 10px var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
article ul a li {
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--accent-color);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footer styling */
|
||||||
|
footer {
|
||||||
|
background-color: var(--dark-blue);
|
||||||
|
padding: 10px 20px;
|
||||||
|
color: var(--text-color);
|
||||||
|
width: 100%;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
box-shadow: 0 -2px 15px rgba(0, 0, 0, 0.7);
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-content {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Card container styles */
|
||||||
|
.cards {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr); /* Display 3 cards per line */
|
||||||
|
gap: 20px;
|
||||||
|
margin-top: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure the <a> tag covers the entire card */
|
||||||
|
section .card a {
|
||||||
|
display: flex; /* Use flex to make <a> fill the card and align content */
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center; /* Vertically center the content */
|
||||||
|
align-items: center; /* Horizontally center the content */
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Card styles */
|
||||||
|
section .card {
|
||||||
|
text-align: center;
|
||||||
|
list-style: none;
|
||||||
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgba(0, 0, 50, 0.9),
|
||||||
|
rgba(10, 10, 100, 0.9),
|
||||||
|
rgba(30, 30, 150, 0.9)
|
||||||
|
);
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 5px 20px rgba(0, 0, 50, 0.8), 0 0 10px rgba(255, 255, 255, 0.1);
|
||||||
|
border: 1px solid #2e3a60;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
transition: background 0.5s ease, transform 0.4s ease, box-shadow 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hover effect */
|
||||||
|
section .card:hover {
|
||||||
|
transform: translateY(-8px);
|
||||||
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgba(30, 30, 150, 0.9),
|
||||||
|
rgba(40, 0, 100, 0.9),
|
||||||
|
rgba(100, 0, 150, 0.9)
|
||||||
|
);
|
||||||
|
box-shadow: 0 10px 30px rgba(0, 0, 100, 0.7), 0 0 20px rgba(255, 221, 85, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
section .card img {
|
||||||
|
height: 80px;
|
||||||
|
width: 80px;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin: 0 auto 15px;
|
||||||
|
box-shadow: 0 0 15px rgba(255, 221, 85, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
section .card h3 {
|
||||||
|
margin: 10px 0;
|
||||||
|
font-size: 1.4em;
|
||||||
|
font-weight: bold;
|
||||||
|
color: rgba(255, 221, 85, 1);
|
||||||
|
text-shadow: 0 0 15px rgba(255, 221, 85, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
section .card p {
|
||||||
|
flex-grow: 1;
|
||||||
|
color: rgba(200, 220, 255, 0.8);
|
||||||
|
margin-bottom: 10px;
|
||||||
|
text-shadow: 0 0 8px rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
section .card::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: -20px;
|
||||||
|
right: -20px;
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: radial-gradient(
|
||||||
|
circle,
|
||||||
|
rgba(255, 255, 255, 0.1),
|
||||||
|
rgba(255, 255, 255, 0.02)
|
||||||
|
);
|
||||||
|
box-shadow: 0 0 50px rgba(255, 255, 255, 0.5);
|
||||||
|
animation: spin 8s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
section .card .suit-icon {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 10px;
|
||||||
|
right: 10px;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
opacity: 0.7;
|
||||||
|
transition: opacity var(--transition-speed);
|
||||||
|
}
|
||||||
|
|
||||||
|
section .card:hover .suit-icon {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Keyframes for spinning element */
|
||||||
|
@keyframes spin {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Form styling */
|
||||||
|
form {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
background: var(--dark-blue-translucent);
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
box-shadow: var(--box-shadow);
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
form label,
|
||||||
|
form input,
|
||||||
|
form textarea {
|
||||||
|
color: var(--text-color);
|
||||||
|
background-color: var(--light-blue);
|
||||||
|
border: 1px solid #3a4b7f;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
padding: 10px;
|
||||||
|
margin: 10px 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
form input[type="submit"] {
|
||||||
|
background-color: var(--accent-color);
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color var(--transition-speed);
|
||||||
|
}
|
||||||
|
|
||||||
|
form input[type="submit"]:hover {
|
||||||
|
background-color: var(--accent-hover-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footer Styling */
|
||||||
|
footer {
|
||||||
|
background-color: var(--dark-blue);
|
||||||
|
padding: 10px 20px;
|
||||||
|
color: var(--text-color);
|
||||||
|
text-align: center;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.cards {
|
||||||
|
grid-template-columns: 1fr; /* 1 card per line on smaller screens */
|
||||||
|
}
|
||||||
|
|
||||||
|
header ul {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
article {
|
||||||
|
margin: 12em 10px;
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
section .card {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
section .card img {
|
||||||
|
height: 60px;
|
||||||
|
width: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-name {
|
||||||
|
font-size: 1.3em;
|
||||||
|
}
|
||||||
|
}
|
||||||
110
webGames/index.html
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Game Collection</title>
|
||||||
|
<link rel="stylesheet" href="styles.css" />
|
||||||
|
<link
|
||||||
|
rel="apple-touch-icon"
|
||||||
|
sizes="180x180"
|
||||||
|
href="../favicon_io/apple-touch-icon.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="32x32"
|
||||||
|
href="../favicon_io/favicon-32x32.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="16x16"
|
||||||
|
href="../favicon_io/favicon-16x16.png"
|
||||||
|
/>
|
||||||
|
<link rel="manifest" href="../favicon_io/site.webmanifest" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<h1>Game Collection</h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<div class="grid-container">
|
||||||
|
<a href="" target="_blank" class="item">
|
||||||
|
<img
|
||||||
|
src="../secret/../secret/images/default.jpeg"
|
||||||
|
alt="Image can't be displayed"
|
||||||
|
/>
|
||||||
|
<h2>Snake</h2>
|
||||||
|
<div class="description">
|
||||||
|
<p>
|
||||||
|
Guide the snake to eat food and grow longer while avoiding
|
||||||
|
collisions with the walls and itself.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a href="" target="_blank" class="item">
|
||||||
|
<img
|
||||||
|
src="../secret/images/default.jpeg"
|
||||||
|
alt="Image can't be displayed"
|
||||||
|
/>
|
||||||
|
<h2>Solitaire</h2>
|
||||||
|
<div class="description">
|
||||||
|
<p>
|
||||||
|
A classic card game where the objective is to move all cards to
|
||||||
|
foundation piles in ascending order.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a href="../secret/mineSweeper/index.html" target="_blank" class="item">
|
||||||
|
<img
|
||||||
|
src="../secret/images/minesweeper.png"
|
||||||
|
alt="Image can't be displayed"
|
||||||
|
/>
|
||||||
|
<h2>Minesweeper</h2>
|
||||||
|
<div class="description">
|
||||||
|
<p>
|
||||||
|
Uncover squares on a grid while avoiding hidden mines, using
|
||||||
|
numbers to deduce safe spots.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="../secret/guessMyNumber/index.html"
|
||||||
|
target="_blank"
|
||||||
|
class="item"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="../secret/images/number.jpeg"
|
||||||
|
alt="Image can't be displayed"
|
||||||
|
/>
|
||||||
|
<h2>Guess My Number</h2>
|
||||||
|
<div class="description">
|
||||||
|
<p>
|
||||||
|
A simple game where you try to guess a randomly chosen number
|
||||||
|
within a certain range.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a href="" target="_blank" class="item">
|
||||||
|
<img
|
||||||
|
src="../secret/images/default.jpeg"
|
||||||
|
alt="Image can't be displayed"
|
||||||
|
/>
|
||||||
|
<h2>Endless Runner</h2>
|
||||||
|
<div class="description">
|
||||||
|
<p>
|
||||||
|
Run through an endless landscape, avoiding obstacles and
|
||||||
|
collecting items to score points.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<p>© 2025 Game Collection</p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
120
webGames/styles.css
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
/* Reset and box-sizing */
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* General Styles */
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
background-color: #282c34;
|
||||||
|
color: #ffffff;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
background-color: #4caf50;
|
||||||
|
color: white;
|
||||||
|
text-align: center;
|
||||||
|
padding: 1em 0;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
background-color: #333;
|
||||||
|
color: white;
|
||||||
|
text-align: center;
|
||||||
|
padding: 1em 0;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grid Styles */
|
||||||
|
.grid-container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 20px; /* Space between items */
|
||||||
|
padding: 20px; /* Space around the grid */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Game Item */
|
||||||
|
.item {
|
||||||
|
position: relative;
|
||||||
|
background-color: #444;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||||
|
transition: transform 0.3s ease, box-shadow 0.3s ease, filter 0.3s ease;
|
||||||
|
width: 100%; /* Ensure it takes full width of the column */
|
||||||
|
height: 400px; /* Set a fixed height for all items */
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column; /* Stack children vertically */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure the image takes the top part of the card */
|
||||||
|
.item img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%; /* Set a height for the image */
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item .description {
|
||||||
|
padding: 30px;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: #ddd;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
border-radius: 0 0 10px 10px;
|
||||||
|
flex-grow: 1; /* Allow description to take remaining space */
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hover effect for scaling and glowing */
|
||||||
|
.item:hover {
|
||||||
|
transform: scale(1.05);
|
||||||
|
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.4);
|
||||||
|
filter: brightness(1.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.item:hover img {
|
||||||
|
transform: scale(1.1); /* Slight zoom-in effect for the image */
|
||||||
|
filter: brightness(1.1); /* Increase image brightness */
|
||||||
|
}
|
||||||
|
|
||||||
|
.item h2 {
|
||||||
|
position: absolute;
|
||||||
|
top: 10%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
color: white;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
background-color: rgba(0, 0, 0, 0.6);
|
||||||
|
padding: 5px 15px;
|
||||||
|
border-radius: 5px;
|
||||||
|
text-align: center;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease, transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item:hover h2 {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(-50%) translateY(-10px); /* Move the title upwards with hover */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile Optimization */
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
header {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
height: auto; /* Allow auto height on mobile for better responsiveness */
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-container {
|
||||||
|
grid-template-columns: repeat(1, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||