Compare commits
38 Commits
1145d26e9d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| d0e9f9951d | |||
| 6e546483f9 | |||
| ddcb13cd63 | |||
| 2eb0e64b30 | |||
| 6a5774667e | |||
|
|
3a6bd21b59 | ||
|
|
a5fa460a69 | ||
|
|
694de9e2d6 | ||
| 9fdfe27728 | |||
|
|
ea479f8225 | ||
|
|
6127e53708 | ||
|
|
2f9da6b38e | ||
|
|
2b8dc254bf | ||
|
|
068ad3dd92 | ||
|
|
954af3cded | ||
|
|
cfeded5079 | ||
|
|
ac9ba24857 | ||
|
|
b15ca69499 | ||
|
|
12e8ac34d4 | ||
|
|
4a21f85a37 | ||
|
|
1eb3aec524 | ||
|
|
647e5b9048 | ||
|
|
903a1de933 | ||
|
|
fa1ffc214c | ||
|
|
c1cb862f4c | ||
|
|
6767f55640 | ||
|
|
67d1453c23 | ||
|
|
d988aabac0 | ||
|
|
11d47caf1e | ||
|
|
40fe732caa | ||
|
|
15b585df02 | ||
|
|
f6347feff1 | ||
|
|
c7f0e747b1 | ||
|
|
1d28a75529 | ||
|
|
ecbf7a5b34 | ||
|
|
46660bd921 | ||
|
|
9b1e2ccf6d | ||
|
|
68421a71df |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
Test
|
Test
|
||||||
|
backend/user.db
|
||||||
116
README.md
116
README.md
@@ -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
|

|
||||||
|
|
||||||
- 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
106
backend/Cargo.lock
generated
@@ -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"
|
||||||
|
|||||||
@@ -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 = "*"
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
struct Laser {
|
|
||||||
color : array,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Mirror {
|
|
||||||
color : array,
|
|
||||||
x : int,
|
|
||||||
y : int,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Spawn {
|
|
||||||
x : int,
|
|
||||||
y : int,
|
|
||||||
}
|
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -173,7 +173,7 @@ main {
|
|||||||
|
|
||||||
.laser {
|
.laser {
|
||||||
background-color: #DADEEF;
|
background-color: #DADEEF;
|
||||||
background-image: url("../img/tiles/Laser.svg");
|
background-image: url("/web/assets/img/tiles/Laser.svg");
|
||||||
background-size: 80%;
|
background-size: 80%;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
@@ -182,7 +182,7 @@ main {
|
|||||||
|
|
||||||
.colored-laser {
|
.colored-laser {
|
||||||
background-color: #DADEEF;
|
background-color: #DADEEF;
|
||||||
background-image: url("../img/tiles/Prisme.svg");
|
background-image: url("/web/assets/img/tiles/Prisme.svg");
|
||||||
background-size: 80%;
|
background-size: 80%;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
@@ -202,7 +202,7 @@ main {
|
|||||||
|
|
||||||
.wall {
|
.wall {
|
||||||
background-color: #DADEEF;
|
background-color: #DADEEF;
|
||||||
background-image: url("../img/tiles/Tuile.svg");
|
background-image: url("/web/assets/img/tiles/Tuile.svg");
|
||||||
background-size: 100%;
|
background-size: 100%;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
@@ -210,7 +210,7 @@ main {
|
|||||||
|
|
||||||
.wall-semi-angle {
|
.wall-semi-angle {
|
||||||
background-color: #DADEEF;
|
background-color: #DADEEF;
|
||||||
background-image: url("../img/tiles/BottomLeft.svg");
|
background-image: url("/web/assets/img/tiles/BottomLeft.svg");
|
||||||
background-size: 100%;
|
background-size: 100%;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
@@ -218,7 +218,7 @@ main {
|
|||||||
|
|
||||||
.horizontal-semi {
|
.horizontal-semi {
|
||||||
background-color: #DADEEF;
|
background-color: #DADEEF;
|
||||||
background-image: url("../img/tiles/HorizontaleSemi.svg");
|
background-image: url("/web/assets/img/tiles/HorizontaleSemi.svg");
|
||||||
background-size: 100%;
|
background-size: 100%;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: bottom;
|
background-position: bottom;
|
||||||
@@ -226,7 +226,7 @@ main {
|
|||||||
|
|
||||||
.captor {
|
.captor {
|
||||||
background-color: #DADEEF;
|
background-color: #DADEEF;
|
||||||
background-image: url("../img/tiles/Capteur-1.svg");
|
background-image: url("/web/assets/img/tiles/Capteur-1.svg");
|
||||||
background-size: 100%;
|
background-size: 100%;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
@@ -235,7 +235,7 @@ main {
|
|||||||
|
|
||||||
.captor-turn {
|
.captor-turn {
|
||||||
background-color: #DADEEF;
|
background-color: #DADEEF;
|
||||||
background-image: url("../img/tiles/Capteur-2.svg");
|
background-image: url("/web/assets/img/tiles/Capteur-2.svg");
|
||||||
background-size: 100%;
|
background-size: 100%;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
@@ -243,7 +243,7 @@ main {
|
|||||||
|
|
||||||
.captor-turn-reverse {
|
.captor-turn-reverse {
|
||||||
background-color: #DADEEF;
|
background-color: #DADEEF;
|
||||||
background-image: url("../img/tiles/Capteur-2.svg");
|
background-image: url("/web/assets/img/tiles/Capteur-2.svg");
|
||||||
background-size: 100%;
|
background-size: 100%;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
@@ -252,15 +252,24 @@ main {
|
|||||||
|
|
||||||
.captor-turn-horizontale2 {
|
.captor-turn-horizontale2 {
|
||||||
background-color: #DADEEF;
|
background-color: #DADEEF;
|
||||||
background-image: url("../img/tiles/Capteur-1.svg");
|
background-image: url("/web/assets/img/tiles/Capteur-1.svg");
|
||||||
background-size: 100%;
|
background-size: 100%;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
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("../img/tiles/CableH.svg");
|
background-image: url("/web/assets/img/tiles/CableH.svg");
|
||||||
background-size: 100%;
|
background-size: 100%;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
@@ -268,7 +277,15 @@ main {
|
|||||||
|
|
||||||
.cable-turn {
|
.cable-turn {
|
||||||
background-color: #DADEEF;
|
background-color: #DADEEF;
|
||||||
background-image: url("../img/tiles/CableBottomLeft.svg");
|
background-image: url("/web/assets/img/tiles/CableBottomLeft.svg");
|
||||||
|
background-size: 100%;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cable-turn-horizontale3 {
|
||||||
|
background-color: #DADEEF;
|
||||||
|
background-image: url("/web/assets/img/tiles/CableTopRight.svg");
|
||||||
background-size: 100%;
|
background-size: 100%;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
@@ -276,7 +293,7 @@ main {
|
|||||||
|
|
||||||
.cable-vertical {
|
.cable-vertical {
|
||||||
background-color: #DADEEF;
|
background-color: #DADEEF;
|
||||||
background-image: url("../img/tiles/CableV.svg");
|
background-image: url("/web/assets/img/tiles/CableV.svg");
|
||||||
background-size: 100%;
|
background-size: 100%;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
@@ -284,7 +301,7 @@ main {
|
|||||||
|
|
||||||
.cable-turn-horizontale {
|
.cable-turn-horizontale {
|
||||||
background-color: #DADEEF;
|
background-color: #DADEEF;
|
||||||
background-image: url("../img/tiles/CableTopLeft.svg");
|
background-image: url("/web/assets/img/tiles/CableTopLeft.svg");
|
||||||
background-size: 100%;
|
background-size: 100%;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
@@ -292,7 +309,7 @@ main {
|
|||||||
|
|
||||||
.cable-turn-horizontale2 {
|
.cable-turn-horizontale2 {
|
||||||
background-color: #DADEEF;
|
background-color: #DADEEF;
|
||||||
background-image: url("../img/tiles/CableTopLeft.svg");
|
background-image: url("/web/assets/img/tiles/CableTopLeft.svg");
|
||||||
background-size: 100%;
|
background-size: 100%;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
@@ -301,14 +318,14 @@ main {
|
|||||||
|
|
||||||
.door {
|
.door {
|
||||||
background-color: #DADEEF;
|
background-color: #DADEEF;
|
||||||
background-image: url("../img/tiles/WoodenDoor.svg");
|
background-image: url("/web/assets/img/tiles/WoodenDoor.svg");
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: end;
|
background-position: end;
|
||||||
}
|
}
|
||||||
|
|
||||||
.door-open {
|
.door-open {
|
||||||
background-image: url("../img/tiles/WoodenDoor_openned.svg");
|
background-image: url("/web/assets/img/tiles/WoodenDoor_openned.svg");
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: end;
|
background-position: end;
|
||||||
@@ -316,7 +333,7 @@ main {
|
|||||||
|
|
||||||
.button {
|
.button {
|
||||||
background-color: #DADEEF;
|
background-color: #DADEEF;
|
||||||
background-image: url("../img/tiles/ButtonComplete.svg"), url("../img/tiles/Tuile.svg");
|
background-image: url("/web/assets/img/tiles/ButtonComplete.svg"), url("/web/assets/img/tiles/Tuile.svg");
|
||||||
background-size: 100% 100%;
|
background-size: 100% 100%;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
@@ -324,7 +341,7 @@ main {
|
|||||||
|
|
||||||
.button-2 {
|
.button-2 {
|
||||||
background-color: #DADEEF;
|
background-color: #DADEEF;
|
||||||
background-image: url("../img/tiles/ButtonQuarter.svg"), url("../img/tiles/Tuile.svg");
|
background-image: url("/web/assets/img/tiles/ButtonQuarter.svg"), url("/web/assets/img/tiles/Tuile.svg");
|
||||||
background-size: 100% 100%;
|
background-size: 100% 100%;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
@@ -332,7 +349,7 @@ main {
|
|||||||
|
|
||||||
.button-rotator {
|
.button-rotator {
|
||||||
background-color: #DADEEF;
|
background-color: #DADEEF;
|
||||||
background-image: url("../img/tiles/ButtonProfile.svg"), url("../img/tiles/Tuile.svg");
|
background-image: url("/web/assets/img/tiles/ButtonProfile.svg"), url("/web/assets/img/tiles/Tuile.svg");
|
||||||
background-size: 100% 100%;
|
background-size: 100% 100%;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
@@ -344,7 +361,7 @@ main {
|
|||||||
|
|
||||||
.target {
|
.target {
|
||||||
background-color: #DADEEF;
|
background-color: #DADEEF;
|
||||||
background-image: url("../img/tiles/Trigger.svg");
|
background-image: url("/web/assets/img/tiles/Trigger.svg");
|
||||||
background-size: 80%;
|
background-size: 80%;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
@@ -352,7 +369,7 @@ main {
|
|||||||
|
|
||||||
.demi-wall-corner-up-left {
|
.demi-wall-corner-up-left {
|
||||||
background-color: #DADEEF;
|
background-color: #DADEEF;
|
||||||
background-image: url("../img/tiles/TopLeft.svg");
|
background-image: url("/web/assets/img/tiles/TopLeft.svg");
|
||||||
background-size: 100%;
|
background-size: 100%;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
@@ -360,7 +377,7 @@ main {
|
|||||||
|
|
||||||
.demi-wall-corner-up-right {
|
.demi-wall-corner-up-right {
|
||||||
background-color: #DADEEF;
|
background-color: #DADEEF;
|
||||||
background-image: url("../img/tiles/TopRight.svg");
|
background-image: url("/web/assets/img/tiles/TopRight.svg");
|
||||||
background-size: 100%;
|
background-size: 100%;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
@@ -368,7 +385,7 @@ main {
|
|||||||
|
|
||||||
.demi-wall-corner-down-left {
|
.demi-wall-corner-down-left {
|
||||||
background-color: #DADEEF;
|
background-color: #DADEEF;
|
||||||
background-image: url("../img/tiles/BottomLeftAngle.svg");
|
background-image: url("/web/assets/img/tiles/BottomLeftAngle.svg");
|
||||||
background-size: 100%;
|
background-size: 100%;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
@@ -376,7 +393,7 @@ main {
|
|||||||
|
|
||||||
.demi-wall-corner-down-right {
|
.demi-wall-corner-down-right {
|
||||||
background-color: #DADEEF;
|
background-color: #DADEEF;
|
||||||
background-image: url("../img/tiles/BottomRightAngle.svg");
|
background-image: url("/web/assets/img/tiles/BottomRightAngle.svg");
|
||||||
background-size: 100%;
|
background-size: 100%;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
|
|||||||
253
web/assets/css/index.css
Normal file
253
web/assets/css/index.css
Normal 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;
|
||||||
|
}
|
||||||
@@ -28,6 +28,8 @@ const legend = {
|
|||||||
cableTurnHorizontale2: 24,
|
cableTurnHorizontale2: 24,
|
||||||
captorTurnHorizontal: 25,
|
captorTurnHorizontal: 25,
|
||||||
wallSemiAngle: 26,
|
wallSemiAngle: 26,
|
||||||
|
cableTurnHorizontale3: 27,
|
||||||
|
captorVerticalTop: 28
|
||||||
};
|
};
|
||||||
|
|
||||||
const laserColors = {
|
const laserColors = {
|
||||||
@@ -41,18 +43,28 @@ 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 },
|
||||||
{ 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: 1, currentAmount: 1 },
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
{ color: laserColors.red, maxAmount: 1, currentAmount: 1 },
|
||||||
|
{ color: laserColors.blue, 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 = [
|
||||||
@@ -84,12 +96,12 @@ 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, 7, 10, 6, 6, 11, 0, 0, 0, 0],
|
[0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 11, 0, 0, 0, 0],
|
||||||
[0, 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, 25, 23, 9, 0, 0, 0, 0],
|
[0, 6, 11, 0, 0, 0, 0, 0, 20, 23, 9, 0, 0, 0, 0],
|
||||||
[0, 10, 6, 6, 6, 6, 6, 6, 6, 9, 0, 0, 0, 0, 0],
|
[0, 10, 6, 6, 6, 6, 6, 6, 6, 9, 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, 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, 0, 0, 0],
|
||||||
@@ -108,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;
|
||||||
@@ -131,9 +142,10 @@ const initialMirrorAngles = [
|
|||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
"3,4": 315,
|
"2,9": 225,
|
||||||
"7,8": 0,
|
|
||||||
},
|
},
|
||||||
|
{},
|
||||||
|
{}
|
||||||
];
|
];
|
||||||
|
|
||||||
const buttonGroups = [
|
const buttonGroups = [
|
||||||
@@ -141,7 +153,10 @@ const buttonGroups = [
|
|||||||
"4,6": 1,
|
"4,6": 1,
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
{},
|
{
|
||||||
|
"9,4": 1,
|
||||||
|
},
|
||||||
|
{}
|
||||||
];
|
];
|
||||||
|
|
||||||
const doorGroups = [
|
const doorGroups = [
|
||||||
@@ -152,6 +167,7 @@ const doorGroups = [
|
|||||||
"4,6": 1,
|
"4,6": 1,
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
|
{}
|
||||||
];
|
];
|
||||||
|
|
||||||
const captorGroups = [
|
const captorGroups = [
|
||||||
@@ -160,14 +176,19 @@ const captorGroups = [
|
|||||||
"2,6": 1,
|
"2,6": 1,
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
|
{}
|
||||||
];
|
];
|
||||||
|
|
||||||
const rotatorButtons = [
|
const rotatorButtons = [
|
||||||
{},
|
{},
|
||||||
{},
|
{},
|
||||||
|
{
|
||||||
|
"6,8": { mirrorX: 9, mirrorY: 2, step: 22.5, intervalMs: 1000 },
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"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 };
|
||||||
@@ -703,7 +724,7 @@ function loadGrid() {
|
|||||||
cell.classList.add("mirror");
|
cell.classList.add("mirror");
|
||||||
const currentAngle = mirrorOrientations[`${y},${x}`] || 0;
|
const currentAngle = mirrorOrientations[`${y},${x}`] || 0;
|
||||||
const img = document.createElement("img");
|
const img = document.createElement("img");
|
||||||
img.src = "../../assets/img/tiles/Mirror.svg";
|
img.src = "/web/assets/img/tiles/Mirror.svg";
|
||||||
img.classList.add("mirror-img");
|
img.classList.add("mirror-img");
|
||||||
img.style.transform = `rotate(${currentAngle}deg)`;
|
img.style.transform = `rotate(${currentAngle}deg)`;
|
||||||
|
|
||||||
@@ -803,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}`]);
|
||||||
@@ -891,7 +918,7 @@ function traceLaser() {
|
|||||||
switch (cellType) {
|
switch (cellType) {
|
||||||
case legend.laser:
|
case legend.laser:
|
||||||
case legend.coloredLaser:
|
case legend.coloredLaser:
|
||||||
laserActive = false;
|
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case legend.empty:
|
case legend.empty:
|
||||||
@@ -922,7 +949,27 @@ function traceLaser() {
|
|||||||
laserActive = false;
|
laserActive = false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case legend.wallSemiAngle:
|
||||||
|
if (currentLaserColor === laserColors.blue) {
|
||||||
|
laserDirection = reverseLaser(laserDirection);
|
||||||
|
} else if (currentLaserColor === laserColors.yellow) {
|
||||||
|
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
||||||
|
} else {
|
||||||
|
laserActive = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case legend.horizontalSemi:
|
||||||
|
if (currentLaserColor === laserColors.blue) {
|
||||||
|
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
||||||
|
laserDirection = reflectLaser(laserDirection, 0);
|
||||||
|
} else if (currentLaserColor === laserColors.yellow) {
|
||||||
|
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
||||||
|
} else {
|
||||||
|
laserActive = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case legend.cableVertical:
|
||||||
|
case legend.cableTurn:
|
||||||
case legend.door:
|
case legend.door:
|
||||||
case legend.doorOpen:
|
case legend.doorOpen:
|
||||||
if (openedDoors[`${currentY},${currentX}`] || cellType === legend.doorOpen) {
|
if (openedDoors[`${currentY},${currentX}`] || cellType === legend.doorOpen) {
|
||||||
@@ -951,6 +998,14 @@ function traceLaser() {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case legend.captor:
|
case legend.captor:
|
||||||
|
if (currentLaserColor === laserColors.blue) {
|
||||||
|
laserDirection = reverseLaser(laserDirection);
|
||||||
|
} else if (currentLaserColor === laserColors.yellow) {
|
||||||
|
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
||||||
|
} else {
|
||||||
|
laserActive = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case legend.captorTurn:
|
case legend.captorTurn:
|
||||||
if (currentLaserColor === laserColors.red) {
|
if (currentLaserColor === laserColors.red) {
|
||||||
const captorKey = `${currentY},${currentX}`;
|
const captorKey = `${currentY},${currentX}`;
|
||||||
@@ -1008,6 +1063,15 @@ function traceLaser() {
|
|||||||
laserActive = false;
|
laserActive = false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case legend.captorVerticalTop:
|
||||||
|
if (currentLaserColor === laserColors.blue) {
|
||||||
|
laserDirection = reverseLaser(laserDirection);
|
||||||
|
} else if (currentLaserColor === laserColors.yellow) {
|
||||||
|
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
||||||
|
} else {
|
||||||
|
laserActive = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case legend.rotatorButton:
|
case legend.rotatorButton:
|
||||||
if (currentLaserColor === laserColors.red) {
|
if (currentLaserColor === laserColors.red) {
|
||||||
const rotatorKey = `${currentY},${currentX}`;
|
const rotatorKey = `${currentY},${currentX}`;
|
||||||
@@ -1029,6 +1093,7 @@ function traceLaser() {
|
|||||||
if (currentLaserColor === laserColors.yellow) {
|
if (currentLaserColor === laserColors.yellow) {
|
||||||
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
||||||
} else if (currentLaserColor === laserColors.blue) {
|
} else if (currentLaserColor === laserColors.blue) {
|
||||||
|
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
||||||
laserDirection = reflectLaser(laserDirection, 135);
|
laserDirection = reflectLaser(laserDirection, 135);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1037,6 +1102,7 @@ function traceLaser() {
|
|||||||
if (currentLaserColor === laserColors.yellow) {
|
if (currentLaserColor === laserColors.yellow) {
|
||||||
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
||||||
} else if (currentLaserColor === laserColors.blue) {
|
} else if (currentLaserColor === laserColors.blue) {
|
||||||
|
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
||||||
laserDirection = reflectLaser(laserDirection, 45);
|
laserDirection = reflectLaser(laserDirection, 45);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1045,6 +1111,7 @@ function traceLaser() {
|
|||||||
if (currentLaserColor === laserColors.yellow) {
|
if (currentLaserColor === laserColors.yellow) {
|
||||||
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
||||||
} else if (currentLaserColor === laserColors.blue) {
|
} else if (currentLaserColor === laserColors.blue) {
|
||||||
|
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
||||||
laserDirection = reflectLaser(laserDirection, 225);
|
laserDirection = reflectLaser(laserDirection, 225);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1053,6 +1120,7 @@ function traceLaser() {
|
|||||||
if (currentLaserColor === laserColors.yellow) {
|
if (currentLaserColor === laserColors.yellow) {
|
||||||
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
||||||
} else if (currentLaserColor === laserColors.blue) {
|
} else if (currentLaserColor === laserColors.blue) {
|
||||||
|
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
||||||
laserDirection = reflectLaser(laserDirection, 315);
|
laserDirection = reflectLaser(laserDirection, 315);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1086,17 +1154,17 @@ function goToLevel(levelIndex) {
|
|||||||
isLevelFinished = false;
|
isLevelFinished = false;
|
||||||
stopAllRotatorButtons();
|
stopAllRotatorButtons();
|
||||||
|
|
||||||
|
mirrorOrientations = {};
|
||||||
|
laserSegments = {};
|
||||||
|
activatedButtons = {};
|
||||||
|
openedDoors = {};
|
||||||
|
toggledDoors = {};
|
||||||
|
poweredCaptors = {};
|
||||||
initializeMirrorOrientations();
|
initializeMirrorOrientations();
|
||||||
glassPlacements = {};
|
glassPlacements = {};
|
||||||
resetGlassInventory();
|
resetGlassInventory();
|
||||||
createPalette();
|
createPalette();
|
||||||
loadGrid();
|
loadGrid();
|
||||||
laserSegments = {};
|
|
||||||
mirrorOrientations = {};
|
|
||||||
activatedButtons = {};
|
|
||||||
openedDoors = {};
|
|
||||||
toggledDoors = {};
|
|
||||||
poweredCaptors = {};
|
|
||||||
traceLaser();
|
traceLaser();
|
||||||
|
|
||||||
const winOverlay = document.querySelector(".win-overlay");
|
const winOverlay = document.querySelector(".win-overlay");
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
function rotateMirror(mirror) {
|
|
||||||
let angle = 0;
|
|
||||||
if (mirror.style.transform == "") {
|
|
||||||
angle = 0;
|
|
||||||
} else {
|
|
||||||
angle = parseInt(mirror.style.transform.split("(")[1].split("deg")[0])%360;
|
|
||||||
}
|
|
||||||
mirror.style.transform = `rotate(${angle+45}deg)`;
|
|
||||||
}
|
|
||||||
@@ -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="../../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="../../assets/js/game.js" defer></script>
|
<script src="/web/assets/js/game.js" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
30
web/templates/view/index.html
Normal file
30
web/templates/view/index.html
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user