33 Commits

Author SHA1 Message Date
ddcb13cd63 Level 5 2026-03-31 17:56:04 +02:00
2eb0e64b30 Level 5 2026-03-31 17:51:18 +02:00
6a5774667e update user.rs file and integration in main file 2026-03-31 16:21:46 +02:00
Sysy's
3a6bd21b59 Update glass counts 2026-03-31 16:12:57 +02:00
Sysy's
a5fa460a69 Merge branch 'main' of https://git.ninolbt.com/Nono/Projet_48h 2026-03-31 16:07:33 +02:00
Sysy's
694de9e2d6 Revamp README and add main menu image
Rewrite README to provide a project overview, features, live demo link, installation and local run instructions, gameplay guide, and tech stack. Add project header, badges, and screenshot embedding. Add web/assets/img/image_game.png and remove placeholder images (web/assets/img/fakeimg.img and web/assets/img/img_test_main_menu.png) to keep assets consistent with the new documentation.
2026-03-31 16:07:26 +02:00
9fdfe27728 update user.rs file 2026-03-31 16:01:59 +02:00
Sysy's
ea479f8225 Remove ads if aspect ratio bad 2026-03-31 15:23:37 +02:00
Sysy's
6127e53708 Fix level 3 2026-03-31 15:19:59 +02:00
M1n-0
2f9da6b38e ishallah V3 2026-03-31 15:00:55 +02:00
M1n-0
2b8dc254bf ishallah V2 2026-03-31 15:00:22 +02:00
M1n-0
068ad3dd92 ishallah 2026-03-31 14:59:26 +02:00
M1n-0
ac9ba24857 fix path error 2026-03-31 14:44:37 +02:00
M1n-0
b15ca69499 fix path error 2026-03-31 14:43:41 +02:00
M1n-0
12e8ac34d4 fix path error 2026-03-31 14:38:43 +02:00
M1n-0
4a21f85a37 fix others mistake 2026-03-31 14:36:14 +02:00
M1n-0
647e5b9048 fix this shitty html 2026-03-31 14:33:40 +02:00
M1n-0
903a1de933 add route /game in backend 2026-03-31 14:31:40 +02:00
M1n-0
fa1ffc214c fix path in html 2026-03-31 14:28:01 +02:00
M1n-0
c1cb862f4c fix error load html (backend) 2026-03-31 14:26:34 +02:00
M1n-0
6767f55640 fix error load html (backend) 2026-03-31 14:17:27 +02:00
M1n-0
67d1453c23 repair loading index (backend) 2026-03-31 14:11:46 +02:00
M1n-0
d988aabac0 modif main backend 2026-03-31 14:09:00 +02:00
M1n-0
11d47caf1e fix merge 2026-03-31 13:56:58 +02:00
M1n-0
40fe732caa fix merge branch 2026-03-31 13:52:42 +02:00
M1n-0
15b585df02 testfile 2026-03-31 13:29:29 +02:00
M1n-0
f6347feff1 testfile 2026-03-31 13:29:29 +02:00
GreyRav
c7f0e747b1 fix : change port 2026-03-31 13:29:29 +02:00
GreyRav
1d28a75529 fix : html + css display 2026-03-31 13:29:29 +02:00
Sysy's
ecbf7a5b34 Add hero page HTML and responsive CSS
Add a new landing view and stylesheet for MirrorGame. Creates web/templates/view/index.html (hero layout with image, play button, and left/right ad banner iframes) and web/assets/css/index.css (CSS variables, Inter font import, hero layout, responsive breakpoints, and .hero-play-button styles). Provides responsive behavior and positioning for ads and hero components.
2026-03-31 13:29:29 +02:00
GreyRav
46660bd921 fix : path to index.html 2026-03-31 13:29:29 +02:00
Sysy's
9b1e2ccf6d Use root-relative path for index.css
Update stylesheet link in view template from '../../assets/css/index.css' to '/assets/css/index.css' so the CSS resolves from the site root. This prevents broken styling when the page is accessed from nested routes or different template contexts.
2026-03-30 15:20:49 +02:00
Sysy's
68421a71df Add hero page HTML and responsive CSS
Add a new landing view and stylesheet for MirrorGame. Creates web/templates/view/index.html (hero layout with image, play button, and left/right ad banner iframes) and web/assets/css/index.css (CSS variables, Inter font import, hero layout, responsive breakpoints, and .hero-play-button styles). Provides responsive behavior and positioning for ads and hero components.
2026-03-30 15:09:09 +02:00
12 changed files with 615 additions and 68 deletions

3
.gitignore vendored
View File

@@ -1 +1,2 @@
Test Test
backend/user.db

116
README.md
View File

@@ -1,35 +1,117 @@
Règles : <div align="center">
<h1 align="center">MirrorGame</h1>
<p align="center">
Browser puzzle game where you guide lasers with mirrors, tinted glass, doors, and triggers.
<br />
<br />
<a href="https://git.ninolbt.com/Nono/Projet_48h/issues">⚠️ Report Bug</a>
·
<a href="https://git.ninolbt.com/Nono/Projet_48h/issues">💡 Request Feature</a>
·
<a href="https://gamegamegame.ninolbt.com/">🎮 Play Online</a>
</p>
<p align="center">
<img src="https://img.shields.io/badge/language-Rust%20%7C%20HTML%20%7C%20CSS%20%7C%20JavaScript-00ADD8?style=for-the-badge&labelColor=000000" />
<img src="https://img.shields.io/badge/platform-Web-6E56CF?style=for-the-badge&labelColor=000000" />
<img src="https://img.shields.io/badge/status-Playable-28A745?style=for-the-badge&labelColor=000000" />
</p>
<p align="center">
<img src="./web/assets/img/image_game.png" alt="MirrorGame main menu" />
</p>
</div>
- Un rayon laser constant ---
- Un ou plusieurs laser ### 🔍 Overview
- Laser non movibles, peut-être allumé/éteins `MirrorGame` is a small laser-reflection puzzle game built during a game jam format.
- Mirroirs orientables The goal is to route a laser beam through each level by rotating mirrors, using tinted glass to change beam color, and interacting with doors, buttons, captors, and other grid-based puzzle elements until the beam reaches the target.
- Prisme (dédouble le laser ou le renvoi selon l'angle) The game is playable online and can also be run locally through the Rust server included in this repository.
> Level progression is stored locally in your browser with `localStorage`.
- Vitre colorée fixe ou placable par le joueur (r,j,b) ### ✨ Features
- Bouton allumable par clique souris (interaction porte, mirroir, etc) - **Laser puzzle gameplay**: Redirect beams across handcrafted levels to activate targets.
- **Mirror rotation**: Rotate mirrors directly on the board to change the laser path.
- **Colored glass placement**: Drag and drop red, blue, and yellow glass tiles onto the grid.
- **Interactive mechanisms**: Trigger doors, buttons, captors, and rotating mirror systems.
- **Multiple levels**: Progress through several unlockable puzzle stages.
- **Browser save data**: Unlocked levels are persisted between sessions with `localStorage`.
- **Rust-powered local server**: Lightweight backend serves the HTML, CSS, JS, and assets.
- Bouton allumable par laser spécifique (interaction porte, mirroir, etc) ### 🎮 Live Demo
Play here: [MirrorGame](https://gamegamegame.ninolbt.com/)
Rayon : ### 🖼️ Screenshot
- Blanc -> Rebondis seulement sur les mirroirs et s'arrête contre les murs Main menu:
- Rouge -> Allumage des récepteurs ![MirrorGame](./web/assets/img/image_game.png)
- Bleu -> Rebondis sur toutes les surfaces (murs compris) ### 🧰 Requirements
- Jaune -> Traverse tout (mirroir compris) - Rust and Cargo
- A modern desktop browser
---
Systeme de placement d'objet par grille ### 🚀 Installation and usage
Comptes utilisateur #### Option 1 — Play online
Optionnel : timer, tableau de score, - Open [**MirrorGame**](https://gamegamegame.ninolbt.com/) in your browser.
#### Option 2 — Run locally from source
1. Install Rust using [rustup](https://rustup.rs/).
2. Clone or download this repository.
3. Open a terminal in the `backend` folder.
4. Start the local server:
```bash
cargo run
```
5. Open your browser at:
```text
http://127.0.0.1:3500
```
---
### 📖 How to play
1. **Launch the game**
- Open the online version or run the project locally.
- Click **Play** from the main menu.
2. **Understand the goal**
- Each level starts with a laser source.
- Your objective is to make the beam reach the target tile.
3. **Rotate mirrors**
- Click mirrors to rotate them and redirect the beam.
- Some mirrors are controlled by puzzle mechanisms instead of direct input.
4. **Use tinted glass**
- Drag red, blue, or yellow glass from the toolbox onto empty cells.
- Double-click a placed glass tile to remove it and get it back.
5. **Solve interactions**
- Red beams can activate buttons and captors.
- Doors, rotators, and other level elements react depending on the puzzle setup.
6. **Progress through levels**
- Completing a level unlocks the next one.
- Use the level menu to revisit unlocked stages.
---
### 🛠️ Tech Stack
- **Backend**: Rust with `axum` and `tower-http`
- **Frontend**: HTML, CSS, and vanilla JavaScript

106
backend/Cargo.lock generated
View File

@@ -90,7 +90,9 @@ dependencies = [
"axum", "axum",
"axum-server", "axum-server",
"serde", "serde",
"sqlite",
"tokio", "tokio",
"tower-http",
"tracing-subscriber", "tracing-subscriber",
] ]
@@ -106,6 +108,16 @@ version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
[[package]]
name = "cc"
version = "1.2.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1e928d4b69e3077709075a938a05ffbedfa53a84c8f766efbf8220bb1ff60e1"
dependencies = [
"find-msvc-tools",
"shlex",
]
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.4" version = "1.0.4"
@@ -134,6 +146,12 @@ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]] [[package]]
name = "fnv" name = "fnv"
version = "1.0.7" version = "1.0.7"
@@ -256,6 +274,12 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
] ]
[[package]]
name = "http-range-header"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c"
[[package]] [[package]]
name = "httparse" name = "httparse"
version = "1.10.1" version = "1.10.1"
@@ -366,6 +390,16 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "mime_guess"
version = "2.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
dependencies = [
"mime",
"unicase",
]
[[package]] [[package]]
name = "mio" name = "mio"
version = "1.2.0" version = "1.2.0"
@@ -433,6 +467,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.106" version = "1.0.106"
@@ -546,6 +586,12 @@ dependencies = [
"lazy_static", "lazy_static",
] ]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]] [[package]]
name = "signal-hook-registry" name = "signal-hook-registry"
version = "1.4.8" version = "1.4.8"
@@ -578,6 +624,34 @@ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]]
name = "sqlite"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f66e9c01a11936154f3910dbba732c01f8b591543bc4d6672bddee79fd9c4783"
dependencies = [
"sqlite3-sys",
]
[[package]]
name = "sqlite3-src"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5b6d3c860886b0a33e69e421796a5f4a27f23597a182c2450f6d7ace5103120"
dependencies = [
"cc",
"pkg-config",
]
[[package]]
name = "sqlite3-sys"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7781d97adc13a1d5081127a9ee29afad8427f3757bd984daf814d8265267039"
dependencies = [
"sqlite3-src",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.117" version = "2.0.117"
@@ -661,6 +735,32 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "tower-http"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8"
dependencies = [
"bitflags",
"bytes",
"futures-core",
"futures-util",
"http",
"http-body",
"http-body-util",
"http-range-header",
"httpdate",
"mime",
"mime_guess",
"percent-encoding",
"pin-project-lite",
"tokio",
"tokio-util",
"tower-layer",
"tower-service",
"tracing",
]
[[package]] [[package]]
name = "tower-layer" name = "tower-layer"
version = "0.3.3" version = "0.3.3"
@@ -719,6 +819,12 @@ dependencies = [
"tracing-log", "tracing-log",
] ]
[[package]]
name = "unicase"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.24" version = "1.0.24"

View File

@@ -9,3 +9,5 @@ axum-server = "0.8.0"
serde = "1.0.228" serde = "1.0.228"
tokio = { version = "1.50.0", features = ["full"] } tokio = { version = "1.50.0", features = ["full"] }
tracing-subscriber = "0.3.23" tracing-subscriber = "0.3.23"
tower-http = { version = "0.6", features = ["fs"] }
sqlite = "*"

View File

@@ -1,14 +0,0 @@
struct Laser {
color : array,
}
struct Mirror {
color : array,
x : int,
y : int,
}
struct Spawn {
x : int,
y : int,
}

View File

@@ -1,14 +1,59 @@
use axum::{routing::get, Router}; use axum::{Router, response::Html, routing::get};
use std::net::SocketAddr; use std::net::SocketAddr;
use std::path::Path;
use tokio::fs::File;
use tokio::io::{self, AsyncReadExt};
use tower_http::services::ServeDir;
mod user;
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
let app = Router::new().route("/", get(|| async { "Hello, world!" })); let db = user::DataBase::open(String::from("user"));
db.create_table("users", "name TEXT, level TEXT, password TEXT");
let app = Router::new()
.route("/", get(handler))
.route("/game", get(game))
.nest_service("/web/assets", ServeDir::new("../web/assets"));
let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); let addr = SocketAddr::from(([0, 0, 0, 0], 3500));
println!("listening on {}", addr); println!("listening on {}", addr);
axum_server::bind(addr) axum_server::bind(addr)
.serve(app.into_make_service()) .serve(app.into_make_service())
.await .await
.unwrap(); .unwrap();
} }
async fn handler() -> Html<String> {
let html_content = read_html_from_file("../web/templates/view/index.html")
.await
.unwrap_or_else(|_| "<h1>Error loading HTML file</h1>".to_string());
Html(html_content)
}
async fn game() -> Html<String> {
let html_content = read_html_from_file("../web/templates/view/game.html")
.await
.unwrap_or_else(|_| "<h1>Error loading HTML file</h1>".to_string());
Html(html_content)
}
async fn register() -> Html<String> {
let html_content = read_html_from_file("../web/templates/view/register.html")
.await
.unwrap_or_else(|_| "<h1>Error loading HTML file</h1>".to_string());
Html(html_content)
}
async fn login() -> Html<String> {
let html_content = read_html_from_file("../web/templates/view/login.html")
.await
.unwrap_or_else(|_| "<h1>Error loading HTML file</h1>".to_string());
Html(html_content)
}
async fn read_html_from_file<P: AsRef<Path>>(path: P) -> io::Result<String> {
let mut file = File::open(path).await?;
let mut contents = String::new();
file.read_to_string(&mut contents).await?;
Ok(contents)
}

View File

@@ -1,16 +1,17 @@
use sqlite::Connection; use sqlite::Connection;
use sqlite::State;
pub struct User { pub struct User {
name: String, name: String,
level: u32, level: u32,
password: String,
} }
impl User { impl User {
pub fn open(name: String, level: u32, password: String) -> User { pub fn open(name: String, level: u32) -> User {
User {name, level, password} User {name, level}
} }
pub fn get_name(&self) -> String{return self.name.clone()}
pub fn get_level(&self) -> u32{return self.level}
} }
@@ -45,11 +46,9 @@ impl DataBase {
pub fn check(&self, table: &str, arg: &str) -> bool { pub fn check(&self, table: &str, arg: &str) -> bool {
let mut end = false; let mut end = false;
self.connection.iterate("Select name FROM ".to_owned() + &table.to_string() + " WHERE name = " + &arg.to_string(), |pairs| { self.connection.iterate("Select name FROM ".to_owned() + &table.to_string() + " WHERE name = " + &format!("'{}'",&arg.to_string()), |pairs| {
for &(name, value) in pairs.iter() { for &(name, value) in pairs.iter() {
println!("{} = {}", name, value.unwrap());
if name == "name" { if name == "name" {
println!("True");
end = true; end = true;
} }
} }

View File

@@ -258,6 +258,15 @@ main {
background-position: center; background-position: center;
} }
.captor-vertical-top {
background-color: #DADEEF;
background-image: url("/web/assets/img/tiles/Capteur-1.svg");
background-size: 100%;
background-repeat: no-repeat;
background-position: center;
transform: rotate(90deg);
}
.cable { .cable {
background-color: #DADEEF; background-color: #DADEEF;
background-image: url("/web/assets/img/tiles/CableH.svg"); background-image: url("/web/assets/img/tiles/CableH.svg");
@@ -274,6 +283,14 @@ main {
background-position: center; background-position: center;
} }
.cable-turn-horizontale3 {
background-color: #DADEEF;
background-image: url("/web/assets/img/tiles/CableTopRight.svg");
background-size: 100%;
background-repeat: no-repeat;
background-position: center;
}
.cable-vertical { .cable-vertical {
background-color: #DADEEF; background-color: #DADEEF;
background-image: url("/web/assets/img/tiles/CableV.svg"); background-image: url("/web/assets/img/tiles/CableV.svg");

253
web/assets/css/index.css Normal file
View File

@@ -0,0 +1,253 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap');
* {
--clr-light-a0: #ffffff;
--clr-light-a10: #f4f4f4;
--clr-light-a20: #e0e0e0;
--clr-light-a30: #c2c2c2;
--clr-light-a40: #a3a3a3;
--clr-light-a50: #858585;
--clr-dark: #000000;
--clr-surface-a0: #FFF6E5;
--clr-surface-a10: #f7f7f7;
--clr-surface-a20: #DADEEF;
--clr-surface-a30: #e0e0e0;
--clr-surface-a40: #d1d1d1;
--clr-surface-a50: #c2c2c2;
box-sizing: border-box;
}
html {
font-size: 100%;
}
body {
background-color: var(--clr-surface-a0);
font-family: 'Inter', sans-serif;
margin: 0;
padding: 0;
}
.hero {
position: relative;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
min-height: 100vh;
gap: 3rem;
padding: 2rem clamp(1rem, 4vw, 3rem);
box-sizing: border-box;
background: var(--clr-surface-a10);
}
.adsbanner {
position: absolute;
top: 50%;
left: clamp(0.75rem, 3vw, 2.5rem);
transform: translateY(-50%);
z-index: 5;
width: clamp(160px, 22vw, 300px);
aspect-ratio: 9 / 16;
box-shadow: 0 18px 40px rgba(0,0,0,0.18);
}
.adsbanner.is-hidden {
display: none;
}
.leftadsbanner {
position: absolute;
top: 50%;
right: clamp(0.75rem, 3vw, 2.5rem);
transform: translateY(-50%);
z-index: 5;
width: clamp(160px, 22vw, 300px);
aspect-ratio: 9 / 16;
}
.leftadsbanner.is-hidden {
display: none;
}
.hero-content {
max-width: 90vw;
text-align: left;
display: flex;
flex-direction: column;
align-items: center;
background: var(--clr-surface-a20);
border-radius: 1.5rem;
padding: 2rem clamp(2rem, 8vw, 10rem);
box-shadow: 0px 0px 27px 14px rgba(0,0,0,0.2);
}
.hero-content-text {
width: 100%;
}
.hero h1 {
color: var(--clr-dark);
font-size: clamp(2.5rem, 7vw, 4rem);
margin: 0 0 1rem 0;
line-height: 1.1;
word-break: break-word;
text-shadow: none;
}
.hero-content button {
margin-top: 2rem;
align-self: center;
}
.hero img {
max-width: 400px;
width: 35vw;
min-width: 180px;
height: auto;
border-radius: 1.5rem;
box-shadow: 0 6px 32px rgba(0,0,0,.10);
flex-shrink: 1;
background: var(--clr-surface-a0);
}
/* --- RESPONSIVE HERO-PLAY-BUTTON FIXES --- */
@media (max-width: 900px) {
.hero {
flex-direction: column;
text-align: center;
gap: 2rem;
padding: 1.5rem;
}
.hero-content {
max-width: 100vw;
text-align: center;
padding: 2rem;
}
.hero-content {
align-items: center;
}
.adsbanner {
left: 1rem;
width: clamp(130px, 24vw, 220px);
}
.leftadsbanner {
right: 1rem;
width: clamp(130px, 24vw, 220px);
}
.hero img {
max-width: 70vw;
min-width: 0;
}
.hero-play-button {
padding: 1rem 4vw;
font-size: 1.1rem;
min-width: min(220px, 70vw);
max-width: 90vw;
}
.hero-play-button svg {
width: 2em;
height: 1em;
min-width: 1.25em;
min-height: 1em;
margin-right: 0.5em;
flex-shrink: 0;
}
}
@media (max-width: 600px) {
.hero {
min-height: 70vh;
padding: 0.5rem;
}
.hero-content {
padding: 1.5rem;
}
.hero h1 {
font-size: clamp(2rem, 9vw, 2.5rem);
}
.hero p {
font-size: clamp(1rem, 5vw, 1.15rem);
}
.adsbanner {
top: 1rem;
left: 0.75rem;
transform: none;
width: clamp(110px, 30vw, 170px);
border-radius: 1rem;
}
.leftadsbanner {
right: 0.75rem;
width: clamp(110px, 30vw, 170px);
border-radius: 1rem;
}
.hero img {
max-width: 100%;
border-radius: 1rem;
box-shadow: 0 2px 12px rgba(0,0,0,.07);
}
.hero-play-button {
padding: 0.85rem 3vw;
min-width: min(120px, 92vw);
max-width: 96vw;
font-size: 1rem;
}
.hero-play-button svg {
width: 1.3em;
height: 1em;
min-width: 1em;
min-height: 1em;
margin-right: 0.45em;
flex-shrink: 0;
}
.adsbanner,
.leftadsbanner {
display: none;
}
}
.hero-play-button {
font-family: inherit;
font-size: 20px;
background: #8ea3fd;
color: white;
padding: 1rem 10rem;
display: flex;
align-items: center;
justify-content: center; /* Ensure SVG/button content is horizontally centered */
border: none;
border-radius: 50px;
overflow: hidden;
transition: all 0.2s;
cursor: pointer;
box-shadow: 0 2px 8px rgba(30, 80, 255, 0.10);
/* Button grows/shrinks with container at low widths */
width: 100%;
max-width: 500px;
min-width: 180px;
box-sizing: border-box;
}
.hero-play-button:hover {
background: #7286e0;
transform: translateY(-2px);
}
.hero-play-button:active {
transform: scale(0.95);
}
.hero-play-button svg {
width: 2em;
height: 1em;
min-width: 1.2em;
min-height: 1em;
margin-right: 0.6em;
color: var(--clr-surface-a0);
flex-shrink: 0;
flex-grow: 0;
display: inline-block;
vertical-align: middle;
}

View File

@@ -24,10 +24,12 @@ const legend = {
rotatorButton: 20, rotatorButton: 20,
cableTurn: 21, cableTurn: 21,
horizontalSemi: 22, horizontalSemi: 22,
cableTurnHorizontale : 23, cableTurnHorizontale: 23,
cableTurnHorizontale2 : 24, cableTurnHorizontale2: 24,
captorTurnHorizontal : 25, captorTurnHorizontal: 25,
wallSemiAngle: 26, wallSemiAngle: 26,
cableTurnHorizontale3: 27,
captorVerticalTop: 28
}; };
const laserColors = { const laserColors = {
@@ -41,7 +43,7 @@ const glassOptions = [
[ [
{ color: laserColors.red, maxAmount: 1, currentAmount: 1 }, { color: laserColors.red, maxAmount: 1, currentAmount: 1 },
{ color: laserColors.blue, maxAmount: 1, currentAmount: 1 }, { color: laserColors.blue, maxAmount: 1, currentAmount: 1 },
{ color: laserColors.yellow, maxAmount: 1, currentAmount: 1 }, { color: laserColors.yellow, maxAmount: 0, currentAmount: 0 },
], ],
[ [
{ color: laserColors.red, maxAmount: 1, currentAmount: 1 }, { color: laserColors.red, maxAmount: 1, currentAmount: 1 },
@@ -49,15 +51,20 @@ const glassOptions = [
{ color: laserColors.yellow, maxAmount: 1, currentAmount: 1 }, { color: laserColors.yellow, maxAmount: 1, currentAmount: 1 },
], ],
[ [
{ color: laserColors.red, maxAmount: 2, currentAmount: 2 }, { color: laserColors.red, maxAmount: 1, currentAmount: 1 },
{ color: laserColors.blue, maxAmount: 2, currentAmount:2}, { color: laserColors.blue, maxAmount: 2, currentAmount: 2 },
{ color: laserColors.yellow, maxAmount: 2, currentAmount: 2 }, { color: laserColors.yellow, maxAmount: 1, currentAmount: 1 },
], ],
[ [
{ color: laserColors.red, maxAmount: 2, currentAmount: 2 }, { color: laserColors.red, maxAmount: 2, currentAmount: 2 },
{ color: laserColors.blue, maxAmount: 1, currentAmount: 1 }, { color: laserColors.blue, maxAmount: 1, currentAmount: 1 },
{ color: laserColors.yellow, maxAmount: 2, currentAmount: 2 }, { color: laserColors.yellow, maxAmount: 2, currentAmount: 2 },
], ],
[
{ color: laserColors.red, maxAmount: 3, currentAmount: 3 },
{ color: laserColors.blue, maxAmount: 2, currentAmount: 2 },
{ color: laserColors.yellow, maxAmount: 1, currentAmount: 1 },
],
]; ];
let levels = [ let levels = [
@@ -89,9 +96,9 @@ let levels = [
[ [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 6, 6, 6, 6, 6, 0, 10, 6, 6, 11, 0, 0, 0, 0], [0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 11, 0, 0, 0, 0],
[7, 1, 0, 0, 0, 4, 0, 0, 0, 3, 21, 0, 0, 0, 0], [7, 1, 0, 0, 0, 4, 0, 0, 0, 3, 21, 0, 0, 0, 0],
[0, 6, 6, 6, 6, 18, 6, 26, 22, 0, 18, 0, 0, 0, 0], [0, 6, 6, 6, 6, 18, 6, 6, 6, 0, 18, 0, 0, 0, 0],
[0, 6, 9, 0, 0, 19, 0, 0, 0, 12, 18, 0, 0, 0, 0], [0, 6, 9, 0, 0, 19, 0, 0, 0, 12, 18, 0, 0, 0, 0],
[0, 6, 0, 6, 6, 6, 6, 6, 6, 24, 23, 0, 0, 0, 0], [0, 6, 0, 6, 6, 6, 6, 6, 6, 24, 23, 0, 0, 0, 0],
[0, 6, 11, 0, 0, 0, 0, 0, 20, 23, 9, 0, 0, 0, 0], [0, 6, 11, 0, 0, 0, 0, 0, 20, 23, 9, 0, 0, 0, 0],
@@ -113,19 +120,18 @@ let levels = [
[0, 0, 0, 0, 10, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0], [0, 0, 0, 0, 10, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0],
], ],
/*
[ [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 12, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 11, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 12, 6, 9, 0, 10, 6, 9, 0, 0, 0, 3, 0, 17, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [12, 6, 9, 0, 0, 0, 6, 0, 7, 0, 0, 0, 0, 18, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [6, 9, 0, 0, 0, 0, 6, 11, 0, 0, 12, 0, 0, 18, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 12, 6, 6, 11, 0, 0, 3, 0, 18, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [10, 6, 11, 0, 0, 6, 0, 0, 10, 6, 6, 6, 6, 18, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 10, 6, 28, 6, 9, 0, 0, 0, 10, 6, 6, 24, 23, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 10, 27, 16, 3, 4, 0, 0, 0, 10, 24, 23, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 10, 6, 18, 6, 11, 0, 12, 18, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 10, 27, 16, 16, 16, 16, 23, 0, 0, 0],
], */ ],
]; ];
let currentLevelIndex = 0; let currentLevelIndex = 0;
@@ -138,6 +144,7 @@ const initialMirrorAngles = [
{ {
"2,9": 225, "2,9": 225,
}, },
{},
{} {}
]; ];
@@ -149,6 +156,7 @@ const buttonGroups = [
{ {
"9,4": 1, "9,4": 1,
}, },
{}
]; ];
const doorGroups = [ const doorGroups = [
@@ -159,6 +167,7 @@ const doorGroups = [
"4,6": 1, "4,6": 1,
}, },
{}, {},
{}
]; ];
const captorGroups = [ const captorGroups = [
@@ -167,6 +176,7 @@ const captorGroups = [
"2,6": 1, "2,6": 1,
}, },
{}, {},
{}
]; ];
const rotatorButtons = [ const rotatorButtons = [
@@ -178,6 +188,7 @@ const rotatorButtons = [
{ {
"3,7": { mirrorX: 7, mirrorY: 7, step: -22.5, intervalMs: 1000 }, "3,7": { mirrorX: 7, mirrorY: 7, step: -22.5, intervalMs: 1000 },
}, },
{}
]; ];
let laserDirection = { dx: 0, dy: 0 }; let laserDirection = { dx: 0, dy: 0 };
@@ -813,9 +824,15 @@ function loadGrid() {
case legend.captorTurnHorizontal: case legend.captorTurnHorizontal:
cell.classList.add("captor-turn-horizontale2"); cell.classList.add("captor-turn-horizontale2");
break; break;
case legend.cableTurnHorizontale3:
cell.classList.add("cable-turn-horizontale3");
break;
case legend.wallSemiAngle: case legend.wallSemiAngle:
cell.classList.add("wall-semi-angle"); cell.classList.add("wall-semi-angle");
break; break;
case legend.captorVerticalTop:
cell.classList.add("captor-vertical-top");
break;
} }
drawLaserInCell(cell, laserSegments[`${y},${x}`]); drawLaserInCell(cell, laserSegments[`${y},${x}`]);
@@ -1002,7 +1019,7 @@ function traceLaser() {
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor); saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
laserActive = false; laserActive = false;
}else if(currentLaserColor === laserColors.yellow) { } else if (currentLaserColor === laserColors.yellow) {
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor); saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
} else { } else {
laserActive = false; laserActive = false;
@@ -1021,7 +1038,7 @@ function traceLaser() {
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor); saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
laserActive = false; laserActive = false;
}else if(currentLaserColor === laserColors.yellow) { } else if (currentLaserColor === laserColors.yellow) {
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor); saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
} else { } else {
laserActive = false; laserActive = false;
@@ -1040,7 +1057,16 @@ function traceLaser() {
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor); saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
laserActive = false; laserActive = false;
}else if(currentLaserColor === laserColors.yellow) { } else if (currentLaserColor === laserColors.yellow) {
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
} else {
laserActive = false;
}
break;
case legend.captorVerticalTop:
if (currentLaserColor === laserColors.blue) {
laserDirection = reverseLaser(laserDirection);
} else if (currentLaserColor === laserColors.yellow) {
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor); saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
} else { } else {
laserActive = false; laserActive = false;

View File

@@ -3,7 +3,7 @@
<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">
<link rel="stylesheet" href="/web/assets/web/assets/assets/css/game.css"> <link rel="stylesheet" href="/web/assets/css/game.css">
<title>Game</title> <title>Game</title>
<script>(function(s){s.dataset.zone='10809858',s.src='https://n6wxm.com/vignette.min.js'})([document.documentElement, document.body].filter(Boolean).pop().appendChild(document.createElement('script')))</script> <script>(function(s){s.dataset.zone='10809858',s.src='https://n6wxm.com/vignette.min.js'})([document.documentElement, document.body].filter(Boolean).pop().appendChild(document.createElement('script')))</script>
<script>(function(s){s.dataset.zone='10809853',s.src='https://nap5k.com/tag.min.js'})([document.documentElement, document.body].filter(Boolean).pop().appendChild(document.createElement('script')))</script> <script>(function(s){s.dataset.zone='10809853',s.src='https://nap5k.com/tag.min.js'})([document.documentElement, document.body].filter(Boolean).pop().appendChild(document.createElement('script')))</script>
@@ -56,6 +56,6 @@
</script> </script>
<script src="https://www.highperformanceformat.com/72b6ba1a1c26b9671167b66063c7e699/invoke.js"></script> <script src="https://www.highperformanceformat.com/72b6ba1a1c26b9671167b66063c7e699/invoke.js"></script>
<script src="/web/assets//eb/assets/assets/js/game.js" defer></script> <script src="/web/assets/js/game.js" defer></script>
</body> </body>
</html> </html>

File diff suppressed because one or more lines are too long