شهر آزاد
by LuckyLion221321 lines17.3 KB
بزن بریم 😎🎮 این نسخه یکتکه است و منوی شروع، انتخاب شخصیت، شبوروز و ذخیره/بارگذاری بازی دارد.
Writing
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>شهر آزاد</title>
<style>
*{box-sizing:border-box}
html,body{
margin:0;
width:100%;
height:100%;
overflow:hidden;
font-family:Arial;
background:#111;
}
canvas{
display:block;
}
#menu{
position:fixed;
inset:0;
z-index:20;
display:flex;
flex-direction:column;
justify-content:center;
align-items:center;
background:linear-gradient(#111c,#000e);
color:white;
}
#menu h1{
font-size:42px;
margin:5px;
}
#menu p{
color:#ddd;
}
button{
border:0;
border-radius:12px;
padding:12px 24px;
margin:6px;
font-size:17px;
cursor:pointer;
}
.characters{
display:flex;
gap:15px;
}
.character{
width:80px;
height:90px;
border:2px solid #777;
border-radius:12px;
display:flex;
justify-content:center;
align-items:center;
font-size:35px;
cursor:pointer;
}
.character.selected{
border-color:#ffe000;
background:#ffe00022;
}
#hud{
position:fixed;
top:10px;
right:10px;
z-index:10;
background:#000c;
color:white;
padding:12px 15px;
border-radius:14px;
line-height:1.8;
}
#save{
position:fixed;
left:10px;
top:10px;
z-index:10;
display:none;
}
</style>
</head>
<body>
<div id="menu">
<h1>🏙️ شهر آزاد</h1>
<p>شخصیتت را انتخاب کن</p>
<div class="characters">
<div class="character selected"
data-color="#1976d2">
🧑
</div>
<div class="character"
data-color="#e91e63">
👩
</div>
<div class="character"
data-color="#4caf50">
🧑🦱
</div>
</div>
<br>
<button onclick="startGame()">
▶ شروع بازی
</button>
<button onclick="loadGame()">
💾 بارگذاری ذخیره
</button>
</div>
<div id="hud">
❤️ جان:
<span id="health">100</span>
<br>
💰 پول:
<span id="money">500</span>
<br>
⭐ تحت تعقیب:
<span id="wanted">0</span>
<br>
🕐 زمان:
<span id="time">روز</span>
<br>
🎯
<span id="mission">
به علامت زرد برو
</span>
<br>
🚗 E = ماشین
</div>
<button id="save"
onclick="saveGame()">
💾 ذخیره
</button>
<canvas id="game"></canvas>
<script>
// =======================================
// CANVAS
// =======================================
const canvas =
document.getElementById("game");
const ctx =
canvas.getContext("2d");
function resize(){
canvas.width=innerWidth;
canvas.height=innerHeight;
}
resize();
addEventListener("resize",resize);
// =======================================
// MENU
// =======================================
let selectedColor="#1976d2";
document
.querySelectorAll(".character")
.forEach(character=>{
character.onclick=()=>{
document
.querySelectorAll(".character")
.forEach(c=>c.classList.remove("selected"));
character.classList.add("selected");
selectedColor=
character.dataset.color;
};
});
// =======================================
// WORLD
// =======================================
const world={
width:3600,
height:2400
};
// =======================================
// PLAYER
// =======================================
let player={
x:650,
y:650,
radius:16,
speed:4,
health:100,
color:"#1976d2"
};
// =======================================
// MONEY
// =======================================
let money=500;
// =======================================
// WANTED
// =======================================
let wanted=0;
// =======================================
// TIME
// =======================================
let gameTime=8;
// =======================================
// CAR
// =======================================
let cars=[
{
x:800,
y:600,
color:"#e53935",
speed:8
},
{
x:1200,
y:500,
color:"#2196f3",
speed:9
},
{
x:1700,
y:1000,
color:"#f1c40f",
speed:10
}
];
let currentCar=null;
// =======================================
// POLICE
// =======================================
let police=[
{x:300,y:300},
{x:3200,y:300},
{x:300,y:2100}
];
// =======================================
// NPC
// =======================================
let npcs=[];
for(let i=0;i<20;i++){
npcs.push({
x:200+Math.random()*3100,
y:250+Math.random()*1900,
dx:(Math.random()-.5),
dy:(Math.random()-.5)
});
}
// =======================================
// MISSIONS
// =======================================
let missions=[
{x:1000,y:500},
{x:1900,y:800},
{x:2800,y:1700},
{x:600,y:1900}
];
let missionIndex=0;
// =======================================
// BUILDINGS
// =======================================
const buildings=[];
for(let row=0;row<3;row++){
for(let col=0;col<5;col++){
buildings.push({
x:80+col*700,
y:80+row*700,
w:430,
h:330
});
}
}
// =======================================
// KEYS
// =======================================
let keys={};
addEventListener("keydown",e=>{
keys[e.key.toLowerCase()]=true;
if(e.key.toLowerCase()==="e"){
enterCar();
}
});
addEventListener("keyup",e=>{
keys[e.key.toLowerCase()]=false;
});
// =======================================
// COLLISION
// =======================================
function collision(x,y){
for(let b of buildings){
if(
x>b.x-20 &&
x<b.x+b.w+20 &&
y>b.y-20 &&
y<b.y+b.h+20
){
return true;
}
}
return false;
}
// =======================================
// START
// =======================================
function startGame(){
player.color=selectedColor;
document
.getElementById("menu")
.style.display="none";
document
.getElementById("save")
.style.display="block";
}
// =======================================
// CAR
// =======================================
function enterCar(){
if(currentCar){
currentCar=null;
return;
}
let nearest=null;
let best=85;
for(let car of cars){
let d=Math.hypot(
player.x-car.x,
player.y-car.y
);
if(d<best){
best=d;
nearest=car;
}
}
if(nearest){
currentCar=nearest;
wanted=Math.min(5,wanted+1);
}
}
// =======================================
// PLAYER UPDATE
// =======================================
function updatePlayer(){
let dx=0;
let dy=0;
if(keys["w"])dy--;
if(keys["s"])dy++;
if(keys["a"])dx--;
if(keys["d"])dx++;
if(!dx&&!dy)return;
let length=Math.hypot(dx,dy);
dx/=length;
dy/=length;
let speed=
currentCar
?currentCar.speed
:player.speed;
let nx=player.x+dx*speed;
let ny=player.y+dy*speed;
if(!collision(nx,player.y))
player.x=nx;
if(!collision(player.x,ny))
player.y=ny;
player.x=Math.max(
20,
Math.min(
world.width-20,
player.x
)
);
player.y=Math.max(
20,
Math.min(
world.height-20,
player.y
)
);
if(currentCar){
currentCar.x=player.x;
currentCar.y=player.y;
}
}
// =======================================
// NPC
// =======================================
function updateNPC(){
for(let n of npcs){
n.x+=n.dx;
n.y+=n.dy;
if(
n.x<20 ||
n.x>world.width-20
)
n.dx*=-1;
if(
n.y<20 ||
n.y>world.height-20
)
n.dy*=-1;
}
}
// =======================================
// POLICE
// =======================================
function updatePolice(){
if(wanted<=0)return;
for(let p of police){
let dx=player.x-p.x;
let dy=player.y-p.y;
let d=Math.hypot(dx,dy);
if(d<900 && d>1){
p.x+=dx/d*2;
p.y+=dy/d*2;
}
if(d<35){
player.health-=0.08;
}
}
}
// =======================================
// MISSION
// =======================================
function updateMission(){
let m=missions[missionIndex];
if(!m)return;
let d=Math.hypot(
player.x-m.x,
player.y-m.y
);
if(d<65){
money+=500;
wanted=0;
missionIndex++;
if(
missionIndex>=missions.length
){
document
.getElementById("mission")
.textContent=
"🏆 همه مأموریتها تمام شد!";
}else{
document
.getElementById("mission")
.textContent=
"مأموریت بعدی را پیدا کن";
}
}
}
// =======================================
// DAY / NIGHT
// =======================================
function updateTime(){
gameTime+=0.002;
if(gameTime>=24)
gameTime=0;
}
// =======================================
// CITY
// =======================================
function drawCity(){
ctx.fillStyle="#65a653";
ctx.fillRect(
0,
0,
world.width,
world.height
);
ctx.fillStyle="#444";
for(
let y=400;
y<world.height;
y+=700
){
ctx.fillRect(
0,
y,
world.width,
150
);
}
for(
let x=500;
x<world.width;
x+=700
){
ctx.fillRect(
x,
0,
150,
world.height
);
}
for(let b of buildings){
ctx.fillStyle="#777";
ctx.fillRect(
b.x,
b.y,
b.w,
b.h
);
ctx.fillStyle="#a9d8e5";
for(
let x=b.x+30;
x<b.x+b.w-20;
x+=65
){
for(
let y=b.y+30;
y<b.y+b.h-20;
y+=65
){
ctx.fillRect(
x,
y,
25,
25
);
}
}
}
}
// =======================================
// CARS
// =======================================
function drawCars(){
for(let car of cars){
ctx.save();
ctx.translate(
car.x,
car.y
);
ctx.fillStyle=car.color;
ctx.fillRect(
-35,
-18,
70,
36
);
ctx.fillStyle="#222";
ctx.fillRect(
-20,
-13,
18,
14
);
ctx.fillRect(
5,
-13,
18,
14
);
ctx.restore();
}
}
// =======================================
// NPC
// =======================================
function drawNPC(){
for(let n of npcs){
ctx.fillStyle="#ffb74d";
ctx.beginPath();
ctx.arc(
n.x,
n.y,
10,
0,
Math.PI*2
);
ctx.fill();
}
}
// =======================================
// POLICE
// =======================================
function drawPolice(){
for(let p of police){
ctx.save();
ctx.translate(
p.x,
p.y
);
ctx.fillStyle="#eee";
ctx.fillRect(
-30,
-17,
60,
34
);
ctx.fillStyle="red";
ctx.fillRect(
-18,
-23,
16,
6
);
ctx.fillStyle="blue";
ctx.fillRect(
2,
-23,
16,
6
);
ctx.restore();
}
}
// =======================================
// PLAYER
// =======================================
function drawPlayer(){
if(currentCar)return;
ctx.fillStyle=player.color;
ctx.beginPath();
ctx.arc(
player.x,
player.y,
player.radius,
0,
Math.PI*2
);
ctx.fill();
}
// =======================================
// MISSION
// =======================================
function drawMission(){
let m=missions[missionIndex];
if(!m)return;
ctx.strokeStyle="#ffe000";
ctx.lineWidth=7;
ctx.beginPath();
ctx.arc(
m.x,
m.y,
45,
0,
Math.PI*2
);
ctx.stroke();
}
// =======================================
// MINIMAP
// =======================================
function drawMiniMap(){
let size=150;
let x=
canvas.width-size-15;
let y=
canvas.height-size-15;
ctx.fillStyle="#111d";
ctx.fillRect(
x,
y,
size,
size
);
ctx.strokeStyle="#fff";
ctx.strokeRect(
x,
y,
size,
size
);
ctx.fillStyle="#00e5ff";
ctx.beginPath();
ctx.arc(
x+(player.x/world.width)*size,
y+(player.y/world.height)*size,
4,
0,
Math.PI*2
);
ctx.fill();
ctx.fillStyle="red";
for(let p of police){
ctx.fillRect(
x+(p.x/world.width)*size-2,
y+(p.y/world.height)*size-2,
5,
5
);
}
let m=missions[missionIndex];
if(m){
ctx.fillStyle="yellow";
ctx.fillRect(
x+(m.x/world.width)*size-3,
y+(m.y/world.height)*size-3,
6,
6
);
}
}
// =======================================
// NIGHT
// =======================================
function drawNight(){
let darkness=0;
if(gameTime>=19 || gameTime<6){
darkness=0.45;
}else if(
gameTime>=17 ||
gameTime<8
){
darkness=0.2;
}
if(darkness){
ctx.fillStyle=
`rgba(10,20,60,${darkness})`;
ctx.fillRect(
0,
0,
canvas.width,
canvas.height
);
}
}
// =======================================
// DRAW
// =======================================
function draw(){
let camX=
player.x-canvas.width/2;
let camY=
player.y-canvas.height/2;
camX=Math.max(
0,
Math.min(
world.width-canvas.width,
camX
)
);
camY=Math.max(
0,
Math.min(
world.height-canvas.height,
camY
)
);
ctx.clearRect(
0,
0,
canvas.width,
canvas.height
);
ctx.save();
ctx.translate(
-camX,
-camY
);
drawCity();
drawMission();
drawCars();
drawNPC();
drawPolice();
drawPlayer();
ctx.restore();
drawNight();
drawMiniMap();
document
.getElementById("health")
.textContent=
Math.max(
0,
Math.floor(player.health)
);
document
.getElementById("money")
.textContent=money;
document
.getElementById("wanted")
.textContent=
"⭐".repeat(wanted);
let hour=Math.floor(gameTime);
document
.getElementById("time")
.textContent=
hour<6 || hour>=19
?"شب 🌙"
:"روز ☀️";
}
// =======================================
// SAVE
// =======================================
function saveGame(){
const save={
player,
money,
wanted,
gameTime,
missionIndex,
cars
};
localStorage.setItem(
"shahrAzadSave",
JSON.stringify(save)
);
alert("💾 بازی ذخیره شد!");
}
// =======================================
// LOAD
// =======================================
function loadGame(){
let data=
localStorage.getItem(
"shahrAzadSave"
);
if(!data){
alert("ذخیرهای پیدا نشد!");
return;
}
let save=
JSON.parse(data);
player=save.player;
money=save.money;
wanted=save.wanted;
gameTime=save.gameTime;
missionIndex=save.missionIndex;
cars=save.cars;
document
.getElementById("menu")
.style.display="none";
document
.getElementById("save")
.style.display="block";
}
// =======================================
// GAME LOOP
// =======================================
function gameLoop(){
updatePlayer();
updateNPC();
updatePolice();
updateMission();
updateTime();
draw();
requestAnimationFrame(gameLoop);
}
gameLoop();
</script>
</body>
</html>
کنترل: WASD حرکت، E سوار/پیاده شدن، دایرهٔ زرد مأموریت است. 💾 دکمهٔ ذخیره هم پیشرفتت را در مرورگر نگه میدارد.Game Source: شهر آزاد
Creator: LuckyLion22
Libraries: none
Complexity: complex (1321 lines, 17.3 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: game-luckylion22" to link back to the original. Then publish at arcadelab.ai/publish.