Medieval Warrior
by StormBear97522 lines12.5 KB
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Medieval Warrior</title>
<style>
* {
box-sizing: border-box;
-webkit-user-select: none;
user-select: none;
touch-action: none;
}
body {
margin: 0;
overflow: hidden;
background: #111;
font-family: Georgia, serif;
color: white;
}
#game {
position: relative;
width: 100vw;
height: 100vh;
max-width: 900px;
margin: auto;
overflow: hidden;
background:
linear-gradient(rgba(30,20,10,.25), rgba(0,0,0,.55)),
linear-gradient(#6687a1 0 48%, #405a2d 49% 72%, #292014 73%);
}
#game:before {
content: "";
position: absolute;
width: 100px;
height: 100px;
border-radius: 50%;
background: #ffd66b;
top: 70px;
right: 12%;
box-shadow: 0 0 50px #ffb52e;
}
.ground {
position: absolute;
bottom: 0;
height: 28%;
width: 100%;
background:
repeating-linear-gradient(
100deg,
#342618 0px,
#342618 12px,
#3f2d1c 13px,
#3f2d1c 25px
);
border-top: 5px solid #21180f;
}
#hud {
position: absolute;
z-index: 20;
top: 12px;
left: 12px;
right: 12px;
display: flex;
justify-content: space-between;
font-weight: bold;
text-shadow: 2px 2px #000;
}
.bar {
width: 130px;
height: 18px;
background: #300;
border: 2px solid white;
border-radius: 10px;
overflow: hidden;
}
.hp {
height: 100%;
width: 100%;
background: linear-gradient(#55ff55,#168b16);
}
/* ===== KARAKTER - BENTUK BARU SESUAI GAMBAR ===== */
.character {
position: absolute;
z-index: 10;
width: 84px;
height: 128px;
bottom: 27%;
}
#player { left: 18%; }
#enemy { right: 18%; }
.shadow {
position: absolute;
bottom: -2px;
left: 50%;
transform: translateX(-50%);
width: 72px;
height: 16px;
background: rgba(0,0,0,.45);
border-radius: 50%;
z-index: 0;
}
/* Pedang putih vertikal di belakang badan - seperti di gambar */
.sword-char {
position: absolute;
width: 10px;
height: 78px;
background: linear-gradient(#ffffff, #d7dce3);
border: 1px solid #8a8f98;
border-radius: 3px;
top: -6px;
z-index: 1;
}
#player .sword-char {
right: -2px;
transform: rotate(7deg);
}
#enemy .sword-char {
left: -2px;
transform: rotate(-7deg);
}
/* gagang pedang */
.sword-char:after {
content: "";
position: absolute;
width: 16px;
height: 8px;
background: #3a2a1a;
border-radius: 3px;
bottom: -6px;
left: -3px;
}
/* Kaki - kecil, gelap, seperti di gambar */
.leg {
position: absolute;
width: 18px;
height: 30px;
background: #1a0f0a;
bottom: 4px;
border-radius: 0 0 9px 9px;
z-index: 1;
}
.leg.l { left: 20px; }
.leg.r { right: 20px; }
/* Badan */
.body-char {
position: absolute;
top: 46px;
left: 50%;
transform: translateX(-50%);
width: 62px;
height: 60px;
border-radius: 16px 16px 10px 10px;
z-index: 2;
}
.tunic-inner {
position: absolute;
left: 50%;
transform: translateX(-50%);
top: 7px;
width: 26px;
height: 46px;
border-radius: 8px;
z-index: 2;
}
/* Bantalan bahu bulat abu-abu */
.shoulder {
position: absolute;
width: 29px;
height: 29px;
background: #c9ced6;
border: 2px solid #8a8f98;
border-radius: 50%;
top: -3px;
z-index: 3;
box-shadow: inset -3px -3px 0 rgba(0,0,0,.12);
}
.shoulder.left { left: -18px; }
.shoulder.right { right: -18px; }
/* ===== PEMAIN - Gambar 1 ===== */
#player .body-char {
background: #1e3a8a;
border: 3px solid #0f1e4a;
}
#player .tunic-inner {
background: #3aa0ff;
box-shadow: inset 0 -6px 0 rgba(0,0,0,.08);
}
/* Perisai / bulatan oranye di sisi kanan pemain */
#player .shield {
position: absolute;
width: 27px;
height: 27px;
background: #f07f1a;
border: 3px solid #b85a0a;
border-radius: 50%;
right: -17px;
top: 16px;
z-index: 4;
}
/* Kepala Pemain */
.head-char {
position: absolute;
top: 0;
left: 50%;
transform: translateX(-50%);
width: 54px;
height: 50px;
z-index: 3;
}
.face {
position: absolute;
width: 44px;
height: 38px;
background: #eab886;
border-radius: 12px 12px 18px 18px;
top: 12px;
left: 5px;
z-index: 2;
}
#player .helmet {
position: absolute;
width: 56px;
height: 32px;
background: #9aa0a8;
border: 2px solid #6b7078;
border-radius: 28px 28px 8px 8px;
top: 0;
left: -1px;
z-index: 3;
box-shadow: inset -7px -5px 0 rgba(0,0,0,.15);
}
#player .visor {
position: absolute;
width: 37px;
height: 9px;
background: #111111;
border-radius: 4px;
top: 23px;
left: 9px;
z-index: 4;
}
/* ===== MUSUH - Gambar 2 ===== */
#enemy .body-char {
background: #6b1a1a;
border: 3px solid #3a0d0d;
}
#enemy .tunic-inner {
background: #e83232;
box-shadow: inset 0 -6px 0 rgba(0,0,0,.08);
}
#enemy .hair {
position: absolute;
width: 54px;
height: 22px;
background: #8b5a2b;
border-radius: 20px 20px 4px 4px;
top: 0;
left: 0;
z-index: 3;
box-shadow: inset -6px -4px 0 rgba(0,0,0,.15);
}
#enemy .blindfold {
position: absolute;
width: 58px;
height: 16px;
background: #c1272d;
border: 2px solid #7a1414;
border-radius: 4px;
top: 16px;
left: -2px;
z-index: 4;
}
/* Animasi serangan */
.attack { animation: attack .25s ease; }
@keyframes attack {
50% { transform: translateX(35px) rotate(-8deg); }
}
.enemyAttack { animation: enemyAttack .25s ease; }
@keyframes enemyAttack {
50% { transform: translateX(-35px); }
}
.hit { animation: hit .25s; }
@keyframes hit {
50% { filter: brightness(4); transform: translateX(8px); }
}
/* Tombol */
.controls {
position: absolute;
z-index: 30;
bottom: 20px;
left: 0;
right: 0;
display: flex;
justify-content: space-around;
align-items: center;
}
button {
border: 3px solid #c79b50;
background: linear-gradient(#694622,#2d1a0d);
color: white;
font-size: 18px;
font-weight: bold;
border-radius: 50%;
width: 72px;
height: 72px;
box-shadow: 0 5px 0 #170d06;
}
button:active {
transform: translateY(4px);
box-shadow: 0 1px 0 #170d06;
}
#attack {
width: 100px;
height: 100px;
background: radial-gradient(#e44325,#7b160d);
border-color: #ffcf55;
}
#startScreen,
#gameOver {
position: absolute;
z-index: 100;
inset: 0;
background: rgba(0,0,0,.85);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
}
#startScreen h1,
#gameOver h1 {
color: #e8b85c;
font-size: 38px;
text-shadow: 3px 3px #000;
}
.startBtn {
border-radius: 15px;
width: 190px;
height: 60px;
margin-top: 20px;
}
#message {
position: absolute;
z-index: 40;
top: 30%;
width: 100%;
text-align: center;
font-size: 32px;
color: #ffd35a;
text-shadow: 3px 3px #000;
pointer-events: none;
}
</style>
</head>
<body>
<div id="game">
<div id="hud">
<div>
⚔ KSATRIA
<div class="bar"><div id="playerHP" class="hp"></div></div>
</div>
<div>🏆 <span id="score">0</span></div>
<div>
👹 MUSUH
<div class="bar"><div id="enemyHP" class="hp"></div></div>
</div>
</div>
<div class="ground"></div>
<!-- PEMAIN - sesuai gambar 1 -->
<div id="player" class="character">
<div class="shadow"></div>
<div class="sword-char"></div>
<div class="leg l"></div>
<div class="leg r"></div>
<div class="body-char">
<div class="tunic-inner"></div>
<div class="shoulder left"></div>
<div class="shoulder right"></div>
<div class="shield"></div>
</div>
<div class="head-char">
<div class="face"></div>
<div class="helmet"></div>
<div class="visor"></div>
</div>
</div>
<!-- MUSUH - sesuai gambar 2 -->
<div id="enemy" class="character">
<div class="shadow"></div>
<div class="sword-char"></div>
<div class="leg l"></div>
<div class="leg r"></div>
<div class="body-char">
<div class="tunic-inner"></div>
<div class="shoulder left"></div>
<div class="shoulder right"></div>
</div>
<div class="head-char">
<div class="face"></div>
<div class="hair"></div>
<div class="blindfold"></div>
</div>
</div>
<div id="message"></div>
<div class="controls">
<button id="left">◀</button>
<button id="attack">⚔<br>SERANG</button>
<button id="right">▶</button>
</div>
<div id="startScreen">
<h1>⚔ MEDIEVAL<br>WARRIOR</h1>
<p>Hadapi para ksatria musuh!</p>
<button class="startBtn" id="start">MULAI PERANG</button>
</div>
<div id="gameOver" style="display:none">
<h1 id="result">KALAH!</h1>
<p>Skor: <span id="finalScore">0</span></p>
<button class="startBtn" onclick="location.reload()">MAIN LAGI</button>
</div>
</div>
<script>
let audioCtx;
function initAudio() { audioCtx = new (window.AudioContext || window.webkitAudioContext)(); }
function sound(type) {
if (!audioCtx) return;
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.connect(gain); gain.connect(audioCtx.destination);
let freq = 200; let duration = .15;
if (type === "sword") { freq = 180; duration = .12; osc.type = "sawtooth"; }
if (type === "hit") { freq = 90; duration = .18; osc.type = "square"; }
if (type === "win") { freq = 600; duration = .5; osc.type = "triangle"; }
if (type === "lose") { freq = 100; duration = .5; osc.type = "sawtooth"; }
osc.frequency.value = freq;
gain.gain.setValueAtTime(.15, audioCtx.currentTime);
gain.gain.exponentialRampToValueAtTime(.001, audioCtx.currentTime + duration);
osc.start(); osc.stop(audioCtx.currentTime + duration);
}
const player = document.getElementById("player");
const enemy = document.getElementById("enemy");
const playerHPBar = document.getElementById("playerHP");
const enemyHPBar = document.getElementById("enemyHP");
let playerHP = 100; let enemyHP = 100; let score = 0; let playing = false;
let playerX = 18; let enemyX = 72;
function updatePosition() { player.style.left = playerX + "%"; enemy.style.right = (100 - enemyX) + "%"; }
function attack() {
if (!playing) return;
player.classList.remove("attack"); void player.offsetWidth; player.classList.add("attack");
sound("sword");
if (Math.abs(playerX - enemyX) < 32) {
enemyHP -= 20; if (enemyHP < 0) enemyHP = 0;
enemyHPBar.style.width = enemyHP + "%";
enemy.classList.add("hit"); sound("hit");
setTimeout(() => { enemy.classList.remove("hit"); }, 250);
if (enemyHP <= 0) win();
}
}
function enemyAttack() {
if (!playing) return;
if (Math.abs(playerX - enemyX) < 32) {
enemy.classList.remove("enemyAttack"); void enemy.offsetWidth; enemy.classList.add("enemyAttack");
playerHP -= 10; if (playerHP < 0) playerHP = 0;
playerHPBar.style.width = playerHP + "%";
player.classList.add("hit"); sound("hit");
setTimeout(() => { player.classList.remove("hit"); }, 250);
if (playerHP <= 0) lose();
}
}
function moveLeft() { if (!playing) return; playerX -= 5; if (playerX < 2) playerX = 2; updatePosition(); }
function moveRight() { if (!playing) return; playerX += 5; if (playerX > 75) playerX = 75; updatePosition(); }
document.getElementById("left").addEventListener("touchstart", e => { e.preventDefault(); moveLeft(); });
document.getElementById("right").addEventListener("touchstart", e => { e.preventDefault(); moveRight(); });
document.getElementById("attack").addEventListener("touchstart", e => { e.preventDefault(); attack(); });
document.getElementById("left").onclick = moveLeft;
document.getElementById("right").onclick = moveRight;
document.getElementById("attack").onclick = attack;
document.addEventListener("keydown", e => {
if (e.key === "ArrowLeft") moveLeft();
if (e.key === "ArrowRight") moveRight();
if (e.key === " " || e.key === "Enter") attack();
});
let enemyTimer;
function startEnemy() { enemyTimer = setInterval(() => { enemyAttack(); }, 1200); }
function win() {
playing = false; clearInterval(enemyTimer); score += 100;
document.getElementById("score").textContent = score;
document.getElementById("finalScore").textContent = score;
sound("win");
document.getElementById("result").textContent = "🏆 MENANG!";
document.getElementById("gameOver").style.display = "flex";
}
function lose() {
playing = false; clearInterval(enemyTimer); sound("lose");
document.getElementById("result").textContent = "💀 KALAH!";
document.getElementById("finalScore").textContent = score;
document.getElementById("gameOver").style.display = "flex";
}
document.getElementById("start").onclick = async function() {
initAudio();
if (audioCtx.state === "suspended") { await audioCtx.resume(); }
sound("win");
document.getElementById("startScreen").style.display = "none";
playing = true; startEnemy();
};
</script>
</body>
</html>
Game Source: Medieval Warrior
Creator: StormBear97
Libraries: none
Complexity: complex (522 lines, 12.5 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: medieval-battle-out-become-enemy-stormbear97" to link back to the original. Then publish at arcadelab.ai/publish.