codebeater - beta launch (#1)

This commit is contained in:
chocomett 2025-12-16 00:28:40 +07:00
commit d63bafa879
66 changed files with 2040 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.docs.txt

96
README.md Normal file
View File

@ -0,0 +1,96 @@
# 👾 codebeater 👾
Codebeater is a programming language quiz game inspired by the storytelling and visual style of Undertale.
Players take on the role of an underground warrior who battles monsters through a series of programming case studies instead of traditional combat.
Each encounter presents a unique coding-related challenge answering correctly deals damage and advances the story.
The more consistently players solve these programming problems, the faster they gain points and climb the leaderboard.
Blending education with adventure, Codebeater transforms learning programming concepts into an engaging and competitive game experience.
An Undertale-inspired coding quiz where logic is your weapon ⚔️
## Contributors 👥
| Name | NRP | Role | Responsibility |
| --------------- | ---------- | ----------------------- | -------------------------------------- |
| **Y Marcel** | 5803025050 | Designer Assets | Assets, Quizzes, questions, narratives |
| **Matthew F** | 5803025047 | PM & Frontend Developer | Documentations and UI/UX |
| **C Bramantyo** | 5803025046 | Backend Developer | API, DB connection, logic |
| **Owen C** | 5803025005 | IT Supports | Help when needed |
## ⚙️ Tech Stack
**Frontend**: HTML, CSS, JavaScript
**Backend**: PHP
**Database**: MySQL
**Server**: Apache
**Containerization**: docker compose / desktop
**Tools**: phpMyAdmin, Git, readme.so, git-eng.ukwms.ac.id, notion, vscode
**Designs**: Asprite
## 📋 Core Features (MVP Scope)
- **User Authentication** Built with PHP and MySQL, allowing users to register, log in, and securely store session data.
- **Quiz Engine** Core gameplay where users answer questions pulled from the database, automatically scored and recorded.
- **Story Integration** Each question is connected to an underground warrior storyline that evolves as the player progresses.
- **Leaderboard System** Displays top player scores in real time, promoting engagement and friendly competition.
- **Profile Page** Shows user details, total score, and gameplay statistics for tracking individual progress.
- **UI/UX Basic Design** Simple pixel-art inspired layout ensuring smooth navigation and lightweight performance.
- **Database Integration** Well-structured MySQL schema for users, quizzes, and results, managed via a modular db.php connection.
- **Dockerized Environment** Fully containerized setup using Docker for consistent deployment with PHPApache, MySQL, and phpMyAdmin.
## 🚀Installation & Setup
#### A. Tanpa Docker 🌐
1. Pastikan sudah menginstall:
- PHP ≥ 8.0
- MySQL ≥ 8.0
- Apache / XAMPP
2. Buat database:
```bash
mysql -u root -p
CREATE DATABASE quizgame;
USE quizgame;
SOURCE dump.sql;
```
3. Letakkan folder proyek di:
- Windows (XAMPP): htdocs/quizgame
- Linux (Apache): /var/www/html/quizgame
4. Akses di browser: http://localhost/8080
#### B. Menggunakan Docker ⛴️
1. Pastikan sudah menginstall:
- Docker
- Docker Compose
2. Jalankan perintah di root folder proyek:
```bash
docker-compose up -d
```
3. Setelah semua container aktif:
- Akses aplikasi: http://localhost:8080
- Akses phpMyAdmin: http://localhost:8081
4. Untuk menghentikan container:
```bash
docker-compose down
```

50
docker-compose.yaml Normal file
View File

@ -0,0 +1,50 @@
version: "3.8"
services:
phpapache:
build:
context: .
dockerfile: Dockerfile
container_name: codebeater_phpapache
ports:
- "8080:80"
volumes:
- ./src:/var/www/html
depends_on:
- db
networks:
- codebeater_net
db:
image: mysql:8.0
container_name: codebeater_mysql
command: --default-authentication-plugin=mysql_native_password
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: codebeater
MYSQL_USER: admin
MYSQL_PASSWORD: admin
volumes:
- db_codebeater:/var/lib/mysql
networks:
- codebeater_net
phpmyadmin:
image: phpmyadmin/phpmyadmin:latest
container_name: codebeater_phpmyadmin
ports:
- "8081:80"
environment:
PMA_HOST: db
PMA_PORT: 3306
depends_on:
- db
networks:
- codebeater_net
volumes:
db_codebeater:
networks:
codebeater_net:
driver: bridge

7
dockerfile Normal file
View File

@ -0,0 +1,7 @@
FROM php:8.2-apache
RUN docker-php-ext-install mysqli
COPY ./src /var/www/html
EXPOSE 80

11
logout.php Normal file
View File

@ -0,0 +1,11 @@
<?php
session_start();
unset($_SESSION['loggedin']);
unset($_SESSION['username']);
unset($_SESSION['id']);
$_SESSION['flash'] = "Logout berhasil!";
header("Location: index.php");
exit();

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 907 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 873 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 741 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 595 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 831 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
src/assets/music/credit.mp3 Normal file

Binary file not shown.

BIN
src/assets/music/game.mp3 Normal file

Binary file not shown.

BIN
src/assets/music/heart.mp3 Normal file

Binary file not shown.

BIN
src/assets/music/hover.mp3 Normal file

Binary file not shown.

BIN
src/assets/music/hurt.mp3 Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
src/assets/music/slash.mp3 Normal file

Binary file not shown.

13
src/config/db.php Normal file
View File

@ -0,0 +1,13 @@
<?php
$hostname = "db";
$username = "admin";
$password = "admin";
$database = "codebeater";
$db = mysqli_connect($hostname, $username, $password, $database);
if(!$db){
die("Koneksi database gagal. Error: " . mysqli_connect_error());
}

197
src/credit.html Normal file
View 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>codebeater</title>
<link rel="icon" type="image/png" href="/assets/Design/mini-icon-cave-new.png">
<link href="https://unpkg.com/aos@2.3.1/dist/aos.css" rel="stylesheet">
<link rel="stylesheet" href="/css/global.css">
<link rel="stylesheet" href="/css/style.css">
<style>
:root {
--height: 500px;
}
</style>
</head>
<body>
<audio id="bgm-crdt" src="/assets/music/credit.mp3" autoplay></audio>
<div class="container-credits" id="credits">
<a class="back" href="onboard.php" data-aos="fade-in" data-aos-duration="3000"></a>
<div class="credits">
<div class="credits-inner" id="creditsInner">
<div class="block">
<h1>👾 codebeater 👾</h1>
<div class="role">lecturer Counselors</div>
<div class="name">Bu Devi</div>
<div class="name">Koh Andrew</div>
<div class="role">Development Team</div>
<div class="name">Owen</div>
<div class="name">Matthew Florentino</div>
<div class="name">Carolus Bramantyo</div>
<div class="name">Yohannes Marcel</div>
<div class="role">Frontend Developer</div>
<div class="name">Yohannes Marcel</div>
<div class="name">Matthew Florentino</div>
<div class="name">Owen</div>
<div class="role">backend Developer</div>
<div class="name">Carolus Bramantyo</div>
<div class="name">Matthew Florentino</div>
<div class="role">Game Designers</div>
<div class="name">Yohannes Marcel</div>
<div class="role">Assets Designers</div>
<div class="name">Yohannes Marcel</div>
<div class="role">Questions Designers</div>
<div class="name">Yohannes Marcel</div>
<div class="name">Owen</div>
<div class="role">CSS Specialist</div>
<div class="name">Matthew Florentino</div>
<div class="role">JS Specialist</div>
<div class="name">Matthew Florentino</div>
<div class="role">PHP Specialist</div>
<div class="name">Carolus Bramantyo</div>
<div class="role">SQL Specialist</div>
<div class="name">Carolus Bramantyo</div>
<div class="role">Advicer</div>
<div class="name">Rafael</div>
<div class="name">Erlang</div>
<div class="name">Bu Devi</div>
<div class="name">koh Andrew</div>
<div class="role">Libary</div>
<div class="name">Animate on Scroll</div>
<div class="role">Font</div>
<div class="name">Jetbrains Mono</div>
<div class="role">Reference Game</div>
<div class="name">Undertale</div>
<div class="name">Soul Knight</div>
<div class="name">Tomb of the Mask</div>
<div class="role">Reference Music</div>
<div style="height:40px;"></div>
<div style="height:40px"></div>
</div>
<div class="block">
<h1>👾 codebeater 👾</h1>
<div class="role">lecturer Counselors</div>
<div class="name">Bu Devi</div>
<div class="name">Koh Andrew</div>
<div class="role">Development Team</div>
<div class="name">Owen</div>
<div class="name">Matthew Florentino</div>
<div class="name">Carolus Bramantyo</div>
<div class="name">Yohannes Marcel</div>
<div class="role">Frontend Developer</div>
<div class="name">Yohannes Marcel</div>
<div class="name">Matthew Florentino</div>
<div class="name">Owen</div>
<div class="role">backend Developer</div>
<div class="name">Carolus Bramantyo</div>
<div class="name">Matthew Florentino</div>
<div class="role">Game Designers</div>
<div class="name">Yohannes Marcel</div>
<div class="role">Assets Designers</div>
<div class="name">Yohannes Marcel</div>
<div class="role">Questions Designers</div>
<div class="name">Yohannes Marcel</div>
<div class="name">Owen</div>
<div class="role">CSS Specialist</div>
<div class="name">Matthew Florentino</div>
<div class="role">JS Specialist</div>
<div class="name">Matthew Florentino</div>
<div class="role">PHP Specialist</div>
<div class="name">Carolus Bramantyo</div>
<div class="role">SQL Specialist</div>
<div class="name">Carolus Bramantyo</div>
<div class="role">Advicer</div>
<div class="name">Rafael</div>
<div class="name">Erlang</div>
<div class="name">Bu Devi</div>
<div class="name">koh Andrew</div>
<div class="role">Libary</div>
<div class="name">Animate on Scroll</div>
<div class="role">Font</div>
<div class="name">Jetbrains Mono</div>
<div class="role">Reference Game</div>
<div class="name">Undertale</div>
<div class="name">Soul Knight</div>
<div class="name">Tomb of the Mask</div>
<div class="role">Reference Music</div>
<div style="height:40px;"></div>
<div style="height:40px"></div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", () => {
const bgm = document.getElementById("bgm-crdt");
bgm.volume = 0.2;
bgm.play()
const inner = document.getElementById("creditsInner");
let y = 0;
let blockH = inner.scrollHeight / 2;
const speed = 40;
function loop() {
y -= speed / 60;
if (y <= -blockH) y = 0;
inner.style.transform = `translateY(${y}px)`;
requestAnimationFrame(loop);
}
window.addEventListener("resize", () => {
blockH = inner.scrollHeight / 2;
y = 0;
});
loop();
});
</script>
<script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>
<script>
AOS.init();
</script>
</body>
</html>

264
src/css/game.css Normal file
View File

@ -0,0 +1,264 @@
.container-third .bg {
width: 100%;
height: 100dvh;
}
.container-third .bg {
position: relative;
}
.container-third .bg img {
width: 100%;
position: absolute;
bottom: 0;
image-rendering: pixelated;
background-repeat: no-repeat;
background-size: cover;
}
.container-third .hero img {
width: 150px;
position: absolute;
bottom: 100px;
left: 50px;
image-rendering: pixelated;
background-repeat: no-repeat;
background-size: cover;
}
.container-third .boss img {
width: 300px;
position: absolute;
bottom: 100px;
right: 50px;
image-rendering: pixelated;
background-repeat: no-repeat;
background-size: cover;
}
.container-first {
background-image: url(/assets/Design/newBGBOSSFIGHT.gif);
background-repeat: no-repeat;
background-size: cover;
image-rendering: pixelated;
background-position: center;
}
.container-first .content .boss {
width: 100%;
height: 100dvh;
position: relative;
animation: beat 2s infinite;
transform-origin: center;
}
.container-first .content .boss img {
width: 600px;
position: absolute;
bottom: 100px;
left: 50%;
transform: translateX(-50%);
image-rendering: pixelated;
background-repeat: no-repeat;
background-size: cover;
}
.container-first .content .boss .dmg {
width: 600px;
position: absolute;
bottom: 100px;
left: 50%;
transform: translateX(-50%);
image-rendering: pixelated;
background-repeat: no-repeat;
background-size: cover;
display: none;
}
@keyframes shake {
0% {
transform: translateX(-50%);
}
25% {
transform: translateX(calc(-50% - 5px));
}
50% {
transform: translateX(calc(-50% + 5px));
}
75% {
transform: translateX(calc(-50% - 5px));
}
100% {
transform: translateX(-50%);
}
}
.animate-shake {
animation: shake 0.3s ease-in-out;
}
.container-first .hearts {
position: absolute;
top: 322px;
left: 50%;
transform: translateX(-50%);
image-rendering: pixelated;
z-index: 999;
}
.container-first .hearts img {
width: 50px;
}
.container-first .content .quiz {
width: 1000px;
height: 200px;
bottom: 40px;
left: 50%;
transform: translateX(-50%);
background-color: #101010f5;
position: absolute;
display: flex;
justify-content: start;
text-align: center;
align-content: center;
padding: 10px;
display: flex;
flex-direction: column;
border: 10px solid white;
border-radius: 10px;
}
.container-first .content .quiz h2 {
margin-bottom: 30px;
padding: 10px;
color: #ffdf12;
font-size: 40px;
}
.container-first #scoreAlert {
text-align: center;
padding: 10px;
color: #ffdf12;
font-size: 40px;
bottom: 22px;
background-color: #101010f5;
left: 50%;
transform: translateX(-50%);
position: absolute;
}
.container-first #scoreAlert.border {
width: 550px;
border: 6px solid white;
border-radius: 10px;
}
.container-first .content .quiz #btn-answer {
color: #ffdf12;
display: flex;
justify-content: center;
}
.container-first .content .quiz #btn-answer button {
padding: 10px;
color: #ffdf12;
font-size: 30px;
background-color: #f7e62e34;
margin: 5px;
border-radius: 10px;
cursor: pointer;
}
@keyframes beat {
0% {
transform: scale(1);
}
50% {
transform: scale(0.95);
}
100% {
transform: scale(1);
}
}
.container-load {
position: relative;
min-height: 100dvh;
}
.container-load .bg {
background-size: cover;
width: 100%;
height: 100dvh;
position: absolute;
z-index: -999;
}
.container-load .anm {
font-size: 25px;
background-size: cover;
position: absolute;
bottom: 50%;
text-align: center;
width: 100%;
}
.container-load .hero {
width: 100px;
position: absolute;
image-rendering: pixelated;
bottom: -5%;
left: 5%;
transform: translate(-50%, -50%);
z-index: 999;
}
.floating-points {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #ffdf12;
font-size: 3rem;
font-weight: bold;
text-shadow: 2px 2px 0px #000;
pointer-events: none;
z-index: 999;
animation: floatUp 1s ease-out forwards;
}
@keyframes floatUp {
0%{
opacity: 1;
transform: translate(-50%,-50%) scale(1);
}
100%{
opacity: 0;
transform: translate(-50%, -150%) scale(1.5)
}
}
.dmg{
width: 800px;
position: absolute;
top: 40%;
left: 50%;
transform: translate(-50%,-50%);
z-index: 9999;
pointer-events: none;
display: none;
image-rendering: pixelated;
background-repeat: no-repeat;
background-size: cover;
}

13
src/css/global.css Normal file
View File

@ -0,0 +1,13 @@
@import url('https://fonts.googleapis.com/css2?family=Jersey+15&family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&display=swap');
*{
font-family: "Jersey 15", sans-serif;
margin: 0;
padding: 0;
}
body {
color: white;
background-color: black;
}

347
src/css/style.css Normal file
View File

@ -0,0 +1,347 @@
input:-webkit-autofill {
-webkit-box-shadow: 0 0 0 1000px #000 inset !important;
-webkit-text-fill-color: #fff !important;
}
.container-login {
display: flex;
width: 100%;
height: 100dvh;
flex-direction: column;
justify-content: center;
align-items: center;
}
.container-login h1 {
font-size: 130px;
font-weight: 400;
}
.container-login form {
text-align: center;
padding: 30px;
}
.container-login form h2 {
margin-bottom: 20px;
font-size: 40px;
text-transform: capitalize;
}
.container-login form input {
margin-bottom: 10px;
width: 300px;
height: 40px;
border-radius: 6px;
font-size: 20px;
text-align: center;
background-color: black;
border: 1px solid white;
color: white;
}
.container-login form input::placeholder {
color: #919191;
}
.container-login form button {
border-radius: 6px;
width: 300px;
height: 40px;
margin-top: 40px;
font-size: 30px
}
.container-login form p {
margin-top: 10px;
}
.container-login form p a {
color: white;
text-decoration: none;
}
.container-login form p a span {
color: yellow;
}
.container-register {
display: flex;
width: 100%;
height: 100dvh;
flex-direction: column;
justify-content: center;
align-items: center;
}
.container-register h1 {
font-size: 120px;
font-weight: 400;
}
.container-register form {
text-align: center;
padding: 30px;
}
.container-register form h2 {
margin-bottom: 10px;
font-size: 30px;
text-transform: capitalize;
}
.container-register form input {
margin-bottom: 10px;
width: 300px;
height: 40px;
border-radius: 6px;
font-size: 20px;
text-align: center;
background-color: black;
border: 1px solid white;
color: white;
}
.container-register form input::placeholder {
color: #919191;
}
.container-register form button {
border-radius: 6px;
width: 300px;
height: 40px;
margin-top: 10px;
font-size: 30px
}
.container-register form p {
margin-top: 10px;
}
.container-register form p a {
color: white;
text-decoration: none;
}
.container-register form p a span {
color: yellow;
}
.container-board {
display: flex;
justify-content: space-around;
align-items: center;
height: 100dvh;
position: relative;
z-index: 1;
}
.container-board::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-image: url(/assets/Design/CampFire8WESFIX!-export.png);
background-repeat: no-repeat;
background-size: cover;
background-position: center;
image-rendering: pixelated;
opacity: 1;
z-index: -1;
}
.container-board .chara {
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
}
.container-board .chara .character {
display: flex;
width: 100%;
justify-content: center;
align-items: center;
flex-direction: column;
}
.container-board .chara .character img {
width: 300px;
image-rendering: pixelated;
margin: 20px;
}
.container-board .menu {
margin: 0 auto;
border-radius: 6px;
width: 400px;
height: 40px;
align-content: center;
}
.container-board .board table {
width: 600px;
margin: 40px auto;
border-collapse: collapse;
/*menghilangkan gap antar border*/
font-family: "Segoe UI", Tahoma, sans-serif;
color: white;
font-weight: 200;
font-size: 20px;
border-radius: 10px;
overflow: hidden;
}
.container-board .board table th,
td {
border: 5px solid #ffffff;
padding: 14px 18px;
white-space: nowrap;
text-align: center;
}
.container-board .board table thead th {
font-weight: 400;
font-size: 20px;
color: #bbd000;
letter-spacing: .4px;
}
.container-board .board table {
background: #0b0e00f3;
}
.container-board .board table tbody tr:hover {
background: #c1fc0032;
}
.container-board .back {
color: #ffff009a;
text-decoration: none;
font-size: 30px;
position: absolute;
z-index: 99999;
left: 2%;
top: 5%;
}
.container-board .back:hover {
color: yellow;
}
.container-onboard {
display: flex;
width: 100%;
height: 100dvh;
flex-direction: column;
justify-content: center;
align-items: center;
background-image: url(/assets/Design/landingPage.png);
background-position: center;
}
.container-onboard h1 {
font-size: 150px;
color: yellow;
font-weight: 400;
margin-bottom: 50px;
}
.container-onboard .menu {
display: flex;
flex-direction: column;
text-align: center;
}
.container-onboard .menu a {
padding: 10px 80px;
background-color: #87871b23;
margin: 8px;
border-radius: 5px;
font-size: 30px;
color: yellow;
border: 1px solid #8170029f;
text-decoration: none;
}
.container-onboard .menu a:hover {
transition: 0.4s ease-out;
color: #dd85fa;
border: 1px solid #dd85fa;
background-color: #871b8323;
}
.container-credits {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
background-image: url(/assets/Design/creditbg.png);
margin: 0;
background-position: center;
background-size:cover ;
}
.container-credits .credits {
width: 100%;
height: var(--height);
overflow: hidden;
border-radius: 8px;
padding: 24px 12px;
position: relative;
}
.container-credits .credits .credits-inner {
display: flex;
flex-direction: column;
transform: translateY(0);
will-change: transform;
}
.container-credits .credits .credits-inner h1 {
font-size: 150px;
color: yellow;
font-weight: 400;
margin-bottom: 50px;
text-align: center;
}
.container-credits .credits .credits-inner .role {
font-weight: 400;
color: yellow;
margin-top: 10px;
text-align: center;
padding: 6px 0;
font-size: 50px;
line-height: 1.25;
text-shadow: 0 1px 0 rgba(0, 0, 0, .6);
}
.container-credits .credits .credits-inner .name {
font-weight: 400;
color: #ffffff;
font-size: 14px;
text-align: center;
padding: 6px 0;
font-size: 30px;
line-height: 1.25;
text-shadow: 0 1px 0 rgba(0, 0, 0, .6);
}
.container-credits .back {
color: #ffff009a;
text-decoration: none;
font-size: 30px;
position: absolute;
z-index: 99999;
left: 2%;
top: 5%;
}
.container-credits .back:hover {
color: yellow;
}

55
src/game/firstperson.html Normal file
View File

@ -0,0 +1,55 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>codebeater</title>
<link rel="icon" type="image/png" href="/assets/Design/mini-icon-cave-new.png">
<link rel="stylesheet" href="/css/global.css">
<link rel="stylesheet" href="/css/game.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"/>
</head>
<body>
<audio id="bgm" src="/assets/music/game.mp3" loop autoplay></audio>
<audio id="sfx" src="/assets/music/slash.mp3"></audio>
<audio id="sfxhrt" src="/assets/music/hurt.mp3"></audio>
<audio id="win" src="/assets/music/Super Mario Bros. Music - Level Complete.mp3"></audio>
<div class="container-first">
<div class="content">
<div class="boss">
<img id="boss" src="/assets/Design/PYTHON-BOSSFIGHTMODEFINALt.gif" alt="">
</div>
<div class="hearts" id="hrt">
<img id="h1" src="/assets/Design/Heartfull.png">
<img id="h2" src="/assets/Design/Heartfull.png">
<img id="h3" src="/assets/Design/Heartfull.png">
</div>
<div class="quiz" id="border">
<h2 id="question"></h2>
<div id="btn-answer">
<button class="btn"></button>
<button class="btn"></button>
<button class="btn"></button>
<button class="btn"></button>
</div>
</div>
<h2 id="scoreAlert"></h2>
<img id="dmg" class="dmg" src="/assets/Design/damageeffectNEWSLASH.gif"/>
</div>
</div>
<script type="module" src="/js/firstperson.js"></script>
<script>
document.addEventListener("DOMContentLoaded", () => {
const bgm = document.getElementById("bgm");
bgm.volume = 0.1;
bgm.loop(true)
bgm.play()
});
</script>
</body>
</html>

28
src/game/loading.html Normal file
View File

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>codebeater</title>
<link rel="icon" type="image/png" href="/assets/Design/mini-icon-cave-new.png">
<link rel="stylesheet" href="/css/global.css">
<link rel="stylesheet" href="/css/game.css">
</head>
<body>
<div class="container-load">
<p id="wellcome-message" class="anm"><span style="color: yellow; font-size: 40px;">Welcome to the Python
Quiz!</span><br>Youll face multiple-choice questions designed to test your logic and Python skills.
<br>Lets see how well you can outsmart the code!</p>
<img id="hero" class="hero" src="/assets/Design/KNIGHTPIXLETPP.gif" alt="">
<img class="bg" src="/assets/Design/LOADSCREEN.gif" alt="">
</div>
<script>
setTimeout(() => {
window.location.href = "/game/thirdperson.html"
}, 5000)
</script>
</body>
</html>

28
src/game/thirdperson.html Normal file
View File

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>codebeater</title>
<link rel="icon" type="image/png" href="/assets/Design/mini-icon-cave-new.png">
<link href="https://unpkg.com/aos@2.3.1/dist/aos.css" rel="stylesheet">
<link rel="stylesheet" href="/css/global.css">
<link rel="stylesheet" href="/css/game.css">
</head>
<body>
<div class="container-third">
<div class="bg">
<img src="/assets/Design/wayland.png" alt="bg">
</div>
<div class="hero">
<img id="hero" src="/assets/Design/KNIGHTPIXLETPP.gif" alt="">
</div>
<div class="boss">
<img id="boss" src="/assets/Design/for-NewPythonGift.gif" alt="">
</div>
</div>
<script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>
<script>AOS.init();</script>
<script src="/js/thirdperson.js"></script>
</body>
</html>

81
src/index.php Normal file
View File

@ -0,0 +1,81 @@
<?php
session_start();
include "config/db.php";
if (isset($_POST['login'])) {
$username = trim($_POST['username']);
$password = trim($_POST['password']);
if ($username === "" || $password === "") {
$_SESSION['flash'] = "Data harus lengkap!";
} else {
$sql = "SELECT * FROM users WHERE username = ?";
$stmt = $db->prepare($sql); // agar aman dari sql injection
$stmt->bind_param("s", $username); // agar rapi tidak muncul di bagian atas query
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows === 1) {
$user = $result->fetch_assoc();
if (password_verify($password, $user['password'])) {
$_SESSION['loggedin'] = true;
$_SESSION['username'] = $user['username'];
$_SESSION['id'] = $user['id'];
$_SESSION['flash'] = "Wellcome to Dungeon, player $username";
header("Location: onboard.php");
exit();
} else {
$_SESSION['flash'] = "Password salah!";
}
} else {
$_SESSION['flash'] = "Username tidak ditemukan!";
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>codebeater</title>
<link rel="icon" type="image/png" href="/assets/Design/mini-icon-cave-new.png">
<link href="https://unpkg.com/aos@2.3.1/dist/aos.css" rel="stylesheet">
<link rel="stylesheet" href="/css/global.css">
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<div class="container-login">
<?php
include "notif.php";
if (isset($_SESSION['flash'])) {
echo "<script>showNotif(" . json_encode($_SESSION['flash']) . ");</script>";
unset($_SESSION['flash']);
}
?>
<h1 data-aos="zoom-out" data-aos-duration="1000">codebeater</h1>
<form class="login-form" action ="index.php" method="POST">
<h2 data-aos="fade-up" data-aos-duration="1000">Login to your account</h2>
<input type="text" name="username" id="username" placeholder="input your username" data-aos="fade-up" data-aos-duration="2000"><br>
<input type="password" name="password" id="password" placeholder="input your password" data-aos="fade-up" data-aos-duration="2000"><br>
<button type="submit" name="login" data-aos="fade-up" data-aos-duration="3000">LOGIN</button>
<p data-aos="fade-up" data-aos-duration="3000"><a href="register.php">don't have account yet?<span> register now!</span></a></p>
</form>
<div id="notif" class="notif"></div>
</div>
<script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>
<script>
AOS.init();
</script>
</body>
</html>

195
src/js/firstperson.js Normal file
View File

@ -0,0 +1,195 @@
import { questions } from "./soal.js";
const questionElement = document.getElementById("question")
const answerBtn = document.getElementById("btn-answer")
const answerBorder = document.getElementById("border")
let currentQuestionIndex = 0;
let score = 0;
let wrongCount = 0;
let maxWrong = 3;
function shuffle(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
function startQuiz(){
currentQuestionIndex = 0;
score = 0;
shuffle(questions);
showQuestion();
}
function showQuestion(){
resetState();
let currentQuestion = questions[currentQuestionIndex]
let questionNumber = currentQuestionIndex + 1
questionElement.innerHTML = questionNumber + ". " + currentQuestion.question;
currentQuestion.answers.forEach(answer => {
const button = document.createElement("button");
button.innerHTML = answer.text;
button.classList.add("btn");
answerBtn.appendChild(button);
button.dataset.correct = answer.correct;
button.addEventListener("click", selectAnswer)
});
}
function resetState(){
while(answerBtn.firstChild){
answerBtn.removeChild(answerBtn.firstChild)
}
}
function updateHearts(){
const hearts = [
document.getElementById("h1"),
document.getElementById("h2"),
document.getElementById("h3")
];
hearts.forEach((h, i) => {
if(i < wrongCount){
h.src = "/assets/Design/HeartEmpty.png";
} else {
h.src = "/assets/Design/Heartfull.png";
}
});
}
function dmg(){
const dmg = document.getElementById("dmg");
dmg.style.display = "block";
setTimeout(() => {
dmg.style.display = "none";
}, 450);
}
function shake(el){
el.classList.remove("animate-shake");
void el.offsetWidth;
el.classList.add("animate-shake");
el.addEventListener("animationend", () => {
el.classList.remove("animate-shake");
}, { once: true });
}
function selectAnswer(e){
const selectedBtn = e.target;
const isCorrect = selectedBtn.dataset.correct === "true";
if(isCorrect){
selectedBtn.classList.add("correct");
score += 10;
showFloatingText();
shake(document.getElementById("boss"))
dmg();
const bgm = document.getElementById("sfx");
bgm.play()
} else {
selectedBtn.classList.add("Incorrect");
wrongCount ++;
shake(document.getElementById("hrt"))
updateHearts();
const bgm = document.getElementById("sfxhrt");
bgm.play()
if(wrongCount >= maxWrong){
showScore();
return;
}
}
Array.from(answerBtn.children).forEach(button => {
if(button.dataset.correct === "true") button.classList.add("correct");
button.disabled = true;
});
setTimeout(nextQuestion, 500)
}
function showScore(){
resetState();
answerBorder.remove();
document.getElementById("h1").remove();
document.getElementById("h2").remove();
document.getElementById("h3").remove();
const bgm = document.getElementById("win");
bgm.play()
setTimeout(()=> {
const alerts = document.getElementById("scoreAlert")
alerts.classList.add("border")
alerts.textContent = `you scored ${score} out of 200 !`;
}, 200);
setTimeout(()=> {
postScore(score);
}, 3200);
}
function handleNextBtn(){
currentQuestionIndex++;
if(currentQuestionIndex < questions.length){
showQuestion();
} else {
showScore();
}
}
function nextQuestion(){
if(currentQuestionIndex < questions.length){
handleNextBtn()
} else {
showScore()
}
}
function postScore(score){
fetch('/score.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ score: score })
})
.then(r => r.json())
.then(data => {
console.log("Server response:", data);
if(data.success || data.message === "Score accumulated") {
setTimeout(() => {
window.location.href = "../leaderboard.php";
}, 3000);
} else {
setTimeout(() => {
window.location.href = "../leaderboard.php";
}, 3000);
}
})
.catch(err => {
console.error("Fetch error:", err);
alert("Terjadi kesalahan koneksi.");
});
}
function showFloatingText(){
const bossContainer = document.querySelector(".boss");
const point = document.createElement("div");
point.textContent = "+10";
point.classList.add("floating-points");
bossContainer.appendChild(point);
setTimeout(()=>{
point.remove();
}, 800);
}
startQuiz()

9
src/js/notif.js Normal file
View File

@ -0,0 +1,9 @@
function showNotif(msg) {
const n = document.getElementById("notif");
n.textContent = msg;
n.classList.add("show");
setTimeout(() => {
n.classList.remove("show");
}, 3000);
}

182
src/js/soal.js Normal file
View File

@ -0,0 +1,182 @@
export const questions = [
{
question: "what is Python ?",
answers: [
{text: "Programming Lang", correct: true},
{text: "Animal", correct: false},
{text: "Danger", correct: false},
{text: "All Answer Correct", correct: false},
]
},
{
question: "Data type at python ?",
answers: [
{text: "Docx", correct: false},
{text: "CSV", correct: false},
{text: "String", correct: true},
{text: "SQL", correct: false},
]
},
{
question: "Index at python start from ?",
answers: [
{text: "1", correct: false},
{text: "-1", correct: false},
{text: "10", correct: false},
{text: "0", correct: true},
]
},
{
question: "How do you start a comment in Python code ?",
answers: [
{text: "// This is a comment", correct: true},
{text: "/* This is a comment */", correct: false},
{text: "`this is comment`", correct: false},
{text: "# This is a comment", correct: false},
]
},
{
question: "What is the result of the following operation ? (print('5'+'5'))",
answers: [
{text: "10", correct: false},
{text: "55", correct: true},
{text: "error", correct: false},
{text: "NaN", correct: false},
]
},
{
question: "Correct string format ?",
answers: [
{text: "'bahlil'", correct: true},
{text: "(bahlil)", correct: false},
{text: "[bahlil]", correct: false},
{text: "{bahlil}", correct: false},
]
},
{
question: "Correct loop syntax ?",
answers: [
{text: "for i in range(5):", correct: true},
{text: "loop 5 time", correct: false},
{text: "foreach i in 5", correct: false},
{text: "repeat", correct: false},
]
},
{
question: "Function to get user input ?",
answers: [
{text: "scan()", correct: false},
{text: "input()", correct: true},
{text: "error", correct: false},
{text: "get()", correct: false},
]
},
{
question: "Convert integer to string ?",
answers: [
{text: "text(5)", correct: false},
{text: "toStr(5)", correct: false},
{text: "str(5)", correct: true},
{text: "char(5)", correct: false},
]
},
{
question: "Stop a loop ?",
answers: [
{text: "break", correct: true},
{text: "stop", correct: false},
{text: "exit", correct: false},
{text: "kill", correct: false},
]
},
{
question: "Symbol for tuples?",
answers: [
{text: "[ ]", correct: false},
{text: "{ }", correct: false},
{text: "( )", correct: true},
{text: "< >", correct: false},
]
},
{
question: "Output of int(3.9) ?",
answers: [
{text: "4", correct: false},
{text: "3,9", correct: false},
{text: "3", correct: true},
{text: "tiga koma sembilan", correct: false},
]
},
{
question: "Find max number ?",
answers: [
{text: "top()", correct: false},
{text: "max()", correct: true},
{text: "high()", correct: false},
{text: "largest()", correct: false},
]
},
{
question: '(3 * "A") output ?',
answers: [
{text: "error", correct: false},
{text: "none", correct: false},
{text: "AAA", correct: true},
{text: "A3A", correct: false},
]
},
{
question: "Logical OR operator ?",
answers: [
{text: "||", correct: false},
{text: "%%", correct: false},
{text: "or", correct: true},
{text: "&&", correct: false},
]
},
{
question: "result of not True ?",
answers: [
{text: "true", correct: false},
{text: "false", correct: true},
{text: "null", correct: false},
{text: "0", correct: false},
]
},
{
question: "Check if item exists ?",
answers: [
{text: "exists", correct: false},
{text: "inside", correct: false},
{text: "in", correct: true},
{text: "has", correct: false},
]
},
{
question: "x += 1 means?",
answers: [
{text: "x = 1", correct: false},
{text: "x = x + 1", correct: true},
{text: "x = x - 1", correct: false},
{text: "idk", correct: false},
]
},
{
question: "Define a class ?",
answers: [
{text: "struct", correct: false},
{text: "object", correct: false},
{text: "type", correct: false},
{text: "class", correct: true},
]
},
{
question: "how much data type in python ?",
answers: [
{text: "5", correct: true},
{text: "1", correct: false},
{text: "2", correct: false},
{text: "100", correct: false},
]
},
];

18
src/js/thirdperson.js Normal file
View File

@ -0,0 +1,18 @@
const hero = document.getElementById('hero');
const boss = document.getElementById('boss');
boss.style.transition = '2s cubic-bezier(0.4, 0, 0.2, 1)';
hero.style.transition = '2s cubic-bezier(0.4, 0, 0.2, 1)';
setTimeout(() => {
hero.style.left = '1000px';
}, 500);
setTimeout(()=> {
hero.src = "/assets/Design/KnightPixLE2-exporgift.gif";
}, 1700);
setTimeout(()=> {
window.location.href = "/game/firstperson.html"
}, 3000)

116
src/leaderboard.php Normal file
View File

@ -0,0 +1,116 @@
<?php
session_start();
require_once "config/db.php";
$sql= "SELECT username, score
FROM users
ORDER BY score DESC
LIMIT 8";
$nama = $_SESSION['username'];
$score = 0;
$getscore = "SELECT score FROM users WHERE username ='$nama'";
$result_me = mysqli_query($db,$getscore);
if ($result_me && mysqli_num_rows($result_me)>0){
$row = mysqli_fetch_assoc($result_me);
$score = $row['score'];
}
$queryrank = "SELECT COUNT(*) as ranking FROM users WHERE score > $score";
$resultrank = mysqli_query($db, $queryrank);
$rowrank = mysqli_fetch_assoc($resultrank);
$realrank = $rowrank['ranking']+1;
$result = mysqli_query($db, $sql);
$leaderboard = [];
if($result){
$leaderboard = mysqli_fetch_all($result, MYSQLI_ASSOC);
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>codebeater</title>
<link rel="icon" type="image/png" href="/assets/Design/mini-icon-cave-new.png">
<link rel="stylesheet" href="css/global.css">
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<audio id="bgm" src="/assets/music/leaderboard.mp3" loop autoplay></audio>
<div class="container-board">
<a class="back" href="onboard.php" data-aos="fade-in" data-aos-duration="3000"></a>
<div class="chara">
<div class="character">
<h1 style="text-align: center; padding-right: 45px; color: yellow;">Knight</h1>
<img src="/assets/Design/KnightPixLE2-exporgift.gif" alt="knight">
</div>
</div>
<div class="board">
<table>
<thead>
<tr>
<th>#</th>
<th>Username</th>
<th>Score</th>
</tr>
</thead>
<tbody>
<?php
$peringkat = 1;
if (!empty($leaderboard)) {
foreach ($leaderboard as $pemain) :
?>
<tr>
<td><?php echo $peringkat; ?></td>
<td><?php echo htmlspecialchars($pemain['username']); ?></td>
<td><?php echo $pemain['score']; ?> PTS</td>
</tr>
<?php
$peringkat++;
endforeach;
} else {
// Jika data kosong
echo '<tr><td colspan="3" style="text-align: center;">Belum ada pemain</td></tr>';
}
?>
</tbody>
<tbody>
<?php
$peringkat = 1;
?>
</tbody>
</table>
<table>
<tbody>
<tr>
<td><?php echo $realrank; ?></td>
<td><?php echo $nama; ?></td>
<td><?php echo $score; ?> PTS</td>
</tr>
</tbody>
</table>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", () => {
const bgm = document.getElementById("bgm");
bgm.loop = true
bgm.volume = 0.2
bgm.play()
});
</script>
</body>
</html>

11
src/logout.php Normal file
View File

@ -0,0 +1,11 @@
<?php
session_start();
unset($_SESSION['loggedin']);
unset($_SESSION['username']);
unset($_SESSION['id']);
$_SESSION['flash'] = "Logout berhasil!";
header("Location: index.php");
exit();

39
src/notif.php Normal file
View File

@ -0,0 +1,39 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>codebeater</title>
<link rel="icon" type="image/png" href="/assets/Design/mini-icon-cave-new.png">
</head>
<style>
.notif {
position: fixed;
top: 50px;
left: 50%;
transform: translateX(-50%);
background-color: #b2db0e1c;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
color: yellow;
padding: 12px 18px;
width: 300px;
height: 25px;
font-size: 20px;
border: 1px solid yellow;
border-radius: 8px;
opacity: 0;
transition: .3s;
z-index: 9999;
}
.notif.show {
opacity: 1;
}
</style>
<body>
<div id="notif" class="notif"></div>
<script src="/js/notif.js"></script>
</body>
</html>

73
src/onboard.php Normal file
View File

@ -0,0 +1,73 @@
<?php
session_start();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>codebeater</title>
<link rel="icon" type="image/png" href="/assets/Design/mini-icon-cave-new.png">
<link href="https://unpkg.com/aos@2.3.1/dist/aos.css" rel="stylesheet">
<link rel="stylesheet" href="css/global.css">
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<audio id="bgm" src="/assets/music/onboard.mp3" loop autoplay></audio>
<audio id="hover-sfx" src="/assets/music/hover.mp3" ></audio>
<div class="container-onboard">
<?php include 'notif.php'; ?>
<?php
if (!empty($_SESSION['flash'])) {
echo "<script>showNotif(" . json_encode($_SESSION['flash']) . ");</script>";
unset($_SESSION['flash']);
}
?>
<h1 data-aos="zoom-out" data-aos-duration="1000">👾 codebeater 👾</h1>
<div class="menu">
<a class="hover-btn" data-aos="zoom-out" data-aos-duration="2000" href="game/loading.html">Game Start</a>
<a class="hover-btn" data-aos="zoom-out" data-aos-duration="2000" href="leaderboard.php">leaderboard</a>
<a class="hover-btn" data-aos="zoom-out" data-aos-duration="2000" href="credit.html">Credit</a>
<a class="hover-btn" data-aos="zoom-out" data-aos-duration="2000" href="logout.php">Exit</a>
</div>
</div>
<script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>
<script>
AOS.init();
</script>
<script>
document.addEventListener("DOMContentLoaded", () => {
const bgm = document.getElementById("bgm");
const hoversfx = document.getElementById('hover-sfx');
const buttons = document.querySelectorAll('.hover-btn');
bgm.volume =0.2;
hoversfx.volume =0.2;
buttons.forEach(button => {
button.addEventListener('mouseenter', () => {
hoversfx.currentTime = 0;
hoversfx.play().catch(error => console.log('menunggu iteraksi user'))
});
});
function playBGM(){
bgm.play().then(()=>{
console.log('BGM Berputar')
}).catch(error => {
console.log('auto diblok')
});
}
playBGM();
document.body.addEventListener("click", () => {
if (bgm.paused) {
bgm.play();
}
}, { once: true });
});
</script>
</body>
</html>

88
src/register.php Normal file
View File

@ -0,0 +1,88 @@
<?php
session_start();
include "config/db.php";
$pesan_sukses="";
$pesan_gagal="";
if(isset($_POST['register'])){
$email = $_POST['email'];
$username = $_POST['username'];
$password = $_POST['password'];
$rpassword = $_POST['rpassword'];
if(empty($email) || empty($username) || empty($password)){
$_SESSION['flash'] = "Semua data harus terisi";
}else if($password !== $rpassword){
$_SESSION['flash']= "password tidak sama";
}
else{
$hashpw = password_hash($password, PASSWORD_DEFAULT);
$sql = "INSERT INTO users (email,username,password,score)VALUES(?,?,?,0)";
$stmt = $db -> prepare($sql);
$stmt->bind_param("sss",$email,$username,$hashpw);
if($stmt->execute()){
header("location:index.php");
exit();
}else{
if($stmt->erno==1602){
$_SESSION['flash'] = "email atau username sudah pernah terdaftar";
}else{
$_SESSION['flash'] = "Regitrasi gagal: ".$stmt->error;
}
}
$stmt -> close();
}
}$db->close()
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>codebeater</title>
<link rel="icon" type="image/png" href="/assets/Design/mini-icon-cave-new.png">
<link href="https://unpkg.com/aos@2.3.1/dist/aos.css" rel="stylesheet">
<link rel="stylesheet" href="css/global.css">
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div class="container-register">
<?php
include "notif.php";
if (isset($_SESSION['flash'])) {
echo "<script>showNotif(" . json_encode($_SESSION['flash']) . ");</script>";
unset($_SESSION['flash']);
}
?>
<h1 data-aos="zoom-out" data-aos-duration="1000">codebeater</h1>
<form action="register.php" method="POST">
<h2 data-aos="fade-up" data-aos-duration="1000">Register to get account</h2>
<input type="email" name="email" id="email" placeholder="input your email" data-aos="fade-up" data-aos-duration="2000"><br>
<input type="text" name="username" id="username" placeholder="input your username" data-aos="fade-up" data-aos-duration="2000"><br>
<input type="password" name="password" id="password" placeholder="input your password" data-aos="fade-up" data-aos-duration="2000"><br>
<input type="password" name="rpassword" id="rpassword" placeholder="repeat your password" data-aos="fade-up" data-aos-duration="2000"><br>
<button type="submit" name="register" data-aos="fade-up" data-aos-duration="3000">REGISTER</button>
<p data-aos="fade-up" data-aos-duration="3000"><a href="index.php" name = "login">have an account? <span> Login now!</span></a></p>
</form>
</div>
<script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>
<script>
AOS.init();
</script>
</body>
</html>

38
src/score.php Normal file
View File

@ -0,0 +1,38 @@
<?php
session_start();
header("Content-Type: application/json");
require_once "config/db.php";
$data = json_decode(file_get_contents("php://input"), true);
if (!isset($data['score'])) {
echo json_encode(["error" => "score missing"]);
exit;
}
$score = (int)$data['score'];
$user_id = isset($_SESSION['id']) ? (int)$_SESSION['id'] : null;
if (!$user_id) {
echo json_encode(["error" => "no session user"]);
exit;
}
$sql = "UPDATE users SET score = score + ? where id =? ";
$stmt = $db ->prepare($sql);
if ($stmt){
$stmt->bind_param("ii",$score,$user_id);
if($stmt->execute()){
echo json_encode(["success" => true, "message" => "Score accumulated"]);
}else{
echo json_encode(["error" => "update failed: " . $stmt->error]);
}
$stmt->close();
}else{
echo json_encode(["error" => "stetment prep failed"]);
}
$db->close();
?>

80
users.sql Normal file
View File

@ -0,0 +1,80 @@
-- phpMyAdmin SQL Dump
-- version 5.2.3
-- https://www.phpmyadmin.net/
--
-- Host: db:3306
-- Generation Time: Dec 01, 2025 at 08:08 AM
-- Server version: 8.0.44
-- PHP Version: 8.3.26
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
START TRANSACTION;
SET time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
--
-- Database: `codebeater`
--
-- --------------------------------------------------------
--
-- Table structure for table `users`
--
CREATE TABLE `users` (
`id` int NOT NULL,
`username` varchar(50) NOT NULL,
`email` varchar(100) NOT NULL,
`password` varchar(255) NOT NULL,
`score` int NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
--
-- Dumping data for table `users`
--
INSERT INTO `users` (`id`, `username`, `email`, `password`, `score`) VALUES
(1, 'bram', 'bahlil@gmail.com', '$2y$10$tOKCS5lO0/lRUW9c.VmsAeMu1M5LxO9F2lq/44yLk3Dl7FvmFUWEu', 100),
(4, 'bahlil', 'bahlil1@gmail.com', '$2y$10$qmU02E22qB6MD5RR8Bopi.nBI38jk2mOht2SHnXb64m30fpyg8cf6', 0),
(8, 'fork', 'bolo@gmail.com', '$2y$10$XZ/uNqSGiZhfCjDEaZUzner0L2tKRBrEVX5cNG0nK4f/IFuf42jCK', 0),
(13, 'adsasd', 'da@gmail.com', '$2y$10$lOWJD1OLc9/pREEvH72b4u7YDZpekwX9W/Wt7mlhSnJuhKNC2dm1i', 0),
(20, 'bobi', 'skibidi@gmail.com', '$2y$10$v2SToxPT3kzfD1BPMJuQGe2kOKqaX7ZcHyuvyUaAWm/GySx2RaAqW', 0),
(21, 'sada', 'sad@gmail.com', '$2y$10$JO0oMbKmJHbUhRNCcyYxnOwK0OFLv69FCItzwCK0h0p6KeAzvekGW', 0),
(24, 'orgil', 'fasfa@gmail.com', '$2y$10$rxkVaEwirysMIiaDgrixQeCbuGpkMdbvA2Kvw.l2t0hk7PO4KzZ4i', 0),
(28, 'bencong', 'bahliltod@gmai.com', '$2y$10$yM13E5kcWDU.FPUpuWkWbuGqpq9tMfS2vKE1Wu8xMPbHleNSCI5pW', 0),
(29, 'adudu', 'adudu@gmail.com', '$2y$10$MW5hI5zKBNDHpEgGOipozOVPaH985N0.J1qxHen15eK1KVI6HfKa.', 0),
(30, 'kkhungke', 'cuki@gmail.com', '$2y$10$6u2vzf6icHzmZeIqDfBHqOEwjGEcpuo0c7QGmZeuzICq71Af0vY6y', 0),
(33, 'koko', 'kkhungke@gmail.com', '$2y$10$ys7rs4PAtl0z74RfgNuu9O50t.ibMgljWEC8x.9Anm3DBw5aB7q8C', 0);
--
-- Indexes for dumped tables
--
--
-- Indexes for table `users`
--
ALTER TABLE `users`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `username` (`username`),
ADD UNIQUE KEY `email` (`email`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `users`
--
ALTER TABLE `users`
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=34;
COMMIT;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;