Files
novafarma/main_demo_scene.html
David Kotnik 61ae6c779d 🎮 Dark-Chibi Noir Demo Scene v1.0 - Kaijev Svet
 Glavne Features:
- Main Demo Scene (main_demo_scene.html) s Kaijem, campfire, trees, tent
- Kai Camp Showcase (kai_camp_showcase.html) - isoliran showcase
- Asset cleanup & transparency fix za vse PNG assete
- Nova struktura: 'assets/slike/nova mapa faza 0-1/'
- Glavne reference v 'assets/slike/glavna_referenca/'

🔧 Technical:
- Python scripts: complete_visual_cleanup.py, remove_green_bg.py
- Transparency fix za vse karakterje, trees, props (brez zelenih ozadij)
- Dokumentacija: TENT_FOUND_REPORT, TRANSPARENCY_FIX_REPORT, VISUAL_CLEANUP_REPORT

🎨 Assets:
- Kai z dreadi (standalone sprite)
- Animated campfire (frame1 + frame2)
- Dead trees (10 variants)
- Tent, grass texture, wood logs
- Full Dark-Chibi Noir aesthetic

🚀 Demo Ready: HTML5 Canvas 2D Scene with WASD movement controls
2026-01-21 22:03:57 +01:00

529 lines
16 KiB
HTML

<!DOCTYPE html>
<html lang="sl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kaijev Svet - Dark-Chibi Noir Demo</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: linear-gradient(180deg, #0a0a0a 0%, #1a1a1a 50%, #2a2a2a 100%);
font-family: 'Courier New', monospace;
color: #e0e0e0;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
}
.header {
text-align: center;
padding: 20px;
background: rgba(0, 0, 0, 0.8);
border-bottom: 3px solid #ff6b6b;
width: 100%;
box-shadow: 0 4px 20px rgba(255, 107, 107, 0.4);
position: fixed;
top: 0;
z-index: 1000;
}
h1 {
font-size: 2.5em;
color: #ff6b6b;
text-shadow: 0 0 15px rgba(255, 107, 107, 0.8),
0 0 30px rgba(255, 107, 107, 0.5);
margin-bottom: 5px;
}
.subtitle {
font-size: 1em;
color: #a0a0a0;
font-style: italic;
}
#gameCanvas {
border: 4px solid #ff6b6b;
border-radius: 10px;
box-shadow: 0 0 50px rgba(255, 107, 107, 0.5),
inset 0 0 30px rgba(0, 0, 0, 0.5);
margin-top: 120px;
image-rendering: pixelated;
image-rendering: -moz-crisp-edges;
image-rendering: crisp-edges;
background: #2a3a2a;
}
.controls {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
background: rgba(0, 0, 0, 0.9);
border: 2px solid #ff6b6b;
border-radius: 10px;
padding: 20px;
box-shadow: 0 0 30px rgba(255, 107, 107, 0.4);
display: flex;
gap: 15px;
align-items: center;
z-index: 1000;
}
.controls button {
background: #ff6b6b;
color: white;
border: none;
padding: 12px 24px;
border-radius: 5px;
cursor: pointer;
font-family: 'Courier New', monospace;
font-size: 0.95em;
transition: all 0.3s ease;
font-weight: bold;
}
.controls button:hover {
background: #ff8787;
box-shadow: 0 0 20px rgba(255, 107, 107, 0.7);
transform: translateY(-2px);
}
.controls button:active {
transform: translateY(0);
}
.info {
background: rgba(255, 107, 107, 0.1);
padding: 8px 15px;
border-radius: 5px;
border-left: 4px solid #ff6b6b;
font-size: 0.9em;
}
.loading {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0, 0, 0, 0.95);
border: 3px solid #ff6b6b;
border-radius: 10px;
padding: 40px;
text-align: center;
z-index: 2000;
box-shadow: 0 0 50px rgba(255, 107, 107, 0.6);
}
.loading h2 {
color: #ff6b6b;
margin-bottom: 20px;
font-size: 1.8em;
}
.loading-bar {
width: 300px;
height: 20px;
background: rgba(255, 255, 255, 0.1);
border-radius: 10px;
overflow: hidden;
border: 2px solid #ff6b6b;
}
.loading-fill {
height: 100%;
background: linear-gradient(90deg, #ff6b6b, #ff8787);
width: 0%;
transition: width 0.3s ease;
box-shadow: 0 0 10px rgba(255, 107, 107, 0.8);
}
.hidden {
display: none;
}
</style>
</head>
<body>
<div class="header">
<h1>🏕️ KAIJEV SVET</h1>
<p class="subtitle">Dark-Chibi Noir | Postapokaliptična Farma v Mrtvi Dolini</p>
</div>
<div class="loading" id="loadingScreen">
<h2>⚙️ Nalagam Demo Sceno...</h2>
<div class="loading-bar">
<div class="loading-fill" id="loadingFill"></div>
</div>
<p style="margin-top: 15px; color: #a0a0a0;" id="loadingText">Pripravljam assete...</p>
</div>
<canvas id="gameCanvas" width="1280" height="720"></canvas>
<div class="controls">
<div class="info">🎮 WASD = Gibanje</div>
<button onclick="toggleFire()">🔥 Toggle Fire</button>
<button onclick="resetScene()">🔄 Reset</button>
<div class="info" id="fpsCounter">FPS: 60</div>
</div>
<script>
// Canvas setup
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// Game state
const game = {
width: canvas.width,
height: canvas.height,
loaded: false,
assetsToLoad: 0,
assetsLoaded: 0,
fireActive: true,
campfireFrame: 0,
campfireTimer: 0,
keys: {},
time: 0
};
// Player (Kai)
const player = {
x: canvas.width / 2 - 50,
y: canvas.height / 2 - 50,
width: 120,
height: 120,
speed: 3,
sprite: null
};
// Assets
const assets = {
grass: null,
kai: null,
campfire: [],
trees: [],
tent: null,
woodLog: null
};
// Scene objects
const sceneObjects = [];
// Load image helper
function loadImage(src) {
game.assetsToLoad++;
const img = new Image();
img.onload = () => {
game.assetsLoaded++;
updateLoadingBar();
};
img.onerror = () => {
console.error(`Failed to load: ${src}`);
game.assetsLoaded++;
updateLoadingBar();
};
img.src = src;
return img;
}
// Update loading bar
function updateLoadingBar() {
const progress = (game.assetsLoaded / game.assetsToLoad) * 100;
document.getElementById('loadingFill').style.width = progress + '%';
document.getElementById('loadingText').textContent =
`Nalagam... ${game.assetsLoaded}/${game.assetsToLoad} assetov`;
if (game.assetsLoaded >= game.assetsToLoad) {
setTimeout(() => {
document.getElementById('loadingScreen').classList.add('hidden');
game.loaded = true;
gameLoop();
}, 500);
}
}
// Load all assets
function loadAssets() {
const basePath = 'assets/slike/nova mapa faza 0-1/';
// Grass texture
assets.grass = loadImage(basePath + 'Environment/podlaga/MOJE_SLIKE_KONCNA_teren_grass.png');
// Kai character
assets.kai = loadImage(basePath + 'Characters/kaj/Izdelek brez naslova (2).png');
player.sprite = assets.kai;
// Campfire animation
assets.campfire[0] = loadImage(basePath + 'Environment/props/MOJE_SLIKE_KONCNA_ostalo_campfire_frame1.png');
assets.campfire[1] = loadImage(basePath + 'Environment/props/MOJE_SLIKE_KONCNA_ostalo_campfire_frame2.png');
// Dead trees (load 10 variants)
for (let i = 0; i < 8; i++) {
assets.trees.push(loadImage(basePath + `Environment/narava/unnamed-${i + 1}.png`));
}
assets.trees.push(loadImage(basePath + 'Environment/narava/unnamed.png'));
assets.trees.push(loadImage(basePath + 'Environment/narava/unnamed Kopie.png'));
// Tent
assets.tent = loadImage(basePath + 'Environment/stavbe/MOJE_SLIKE_KONCNA_ostalo_tent_basic_style32.png');
// Wood log
assets.woodLog = loadImage(basePath + 'Environment/props/wood_log.png');
}
// Initialize scene
function initScene() {
// Background trees (far layer)
sceneObjects.push({
type: 'tree',
sprite: assets.trees[0],
x: 100,
y: 80,
width: 200,
height: 200,
layer: 1
});
sceneObjects.push({
type: 'tree',
sprite: assets.trees[1],
x: 900,
y: 100,
width: 220,
height: 220,
layer: 1
});
sceneObjects.push({
type: 'tree',
sprite: assets.trees[2],
x: 1050,
y: 150,
width: 180,
height: 180,
layer: 1
});
// Tent (mid layer)
sceneObjects.push({
type: 'tent',
sprite: assets.tent,
x: 200,
y: 320,
width: 180,
height: 180,
layer: 2
});
// Campfire (center, mid-to-front layer)
sceneObjects.push({
type: 'campfire',
sprite: assets.campfire[0],
x: canvas.width / 2 + 80,
y: canvas.height / 2 - 20,
width: 120,
height: 120,
layer: 3
});
// Wood log (foreground)
sceneObjects.push({
type: 'log',
sprite: assets.woodLog,
x: canvas.width / 2 - 100,
y: canvas.height / 2 + 100,
width: 100,
height: 100,
layer: 4
});
// Foreground trees (close layer)
sceneObjects.push({
type: 'tree',
sprite: assets.trees[3],
x: 50,
y: 450,
width: 240,
height: 240,
layer: 5
});
sceneObjects.push({
type: 'tree',
sprite: assets.trees[4],
x: 950,
y: 400,
width: 260,
height: 260,
layer: 5
});
// Sort objects by layer for proper rendering
sceneObjects.sort((a, b) => a.layer - b.layer);
}
// Draw grass background (tiled)
function drawGrass() {
if (!assets.grass || !assets.grass.complete) return;
const tileSize = 64; // Grass tile size
for (let x = 0; x < canvas.width; x += tileSize) {
for (let y = 0; y < canvas.height; y += tileSize) {
ctx.drawImage(assets.grass, x, y, tileSize, tileSize);
}
}
}
// Draw scene objects
function drawSceneObjects() {
sceneObjects.forEach(obj => {
if (obj.type === 'campfire' && game.fireActive) {
// Draw animated campfire
const frameIndex = Math.floor(game.campfireFrame);
const sprite = assets.campfire[frameIndex % 2];
if (sprite && sprite.complete) {
ctx.drawImage(sprite, obj.x, obj.y, obj.width, obj.height);
}
} else if (obj.sprite && obj.sprite.complete) {
ctx.drawImage(obj.sprite, obj.x, obj.y, obj.width, obj.height);
}
});
}
// Draw player (Kai)
function drawPlayer() {
if (player.sprite && player.sprite.complete) {
ctx.drawImage(player.sprite, player.x, player.y, player.width, player.height);
}
}
// Update player movement
function updatePlayer() {
if (game.keys['w'] || game.keys['W']) player.y -= player.speed;
if (game.keys['s'] || game.keys['S']) player.y += player.speed;
if (game.keys['a'] || game.keys['A']) player.x -= player.speed;
if (game.keys['d'] || game.keys['D']) player.x += player.speed;
// Keep player in bounds
player.x = Math.max(0, Math.min(canvas.width - player.width, player.x));
player.y = Math.max(0, Math.min(canvas.height - player.height, player.y));
}
// Update game state
function update(deltaTime) {
game.time += deltaTime;
// Update player
updatePlayer();
// Update campfire animation
if (game.fireActive) {
game.campfireTimer += deltaTime;
if (game.campfireTimer > 0.5) {
game.campfireFrame = (game.campfireFrame + 1) % 2;
game.campfireTimer = 0;
}
}
}
// Render everything
function render() {
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw grass background
drawGrass();
// Draw scene objects (background to mid-ground)
sceneObjects.forEach(obj => {
if (obj.layer < 4 || obj.y < player.y) {
if (obj.type === 'campfire' && game.fireActive) {
const frameIndex = Math.floor(game.campfireFrame);
const sprite = assets.campfire[frameIndex % 2];
if (sprite && sprite.complete) {
ctx.drawImage(sprite, obj.x, obj.y, obj.width, obj.height);
}
} else if (obj.sprite && obj.sprite.complete) {
ctx.drawImage(obj.sprite, obj.x, obj.y, obj.width, obj.height);
}
}
});
// Draw player
drawPlayer();
// Draw remaining scene objects (foreground, in front of player)
sceneObjects.forEach(obj => {
if (obj.layer >= 4 && obj.y >= player.y) {
if (obj.sprite && obj.sprite.complete) {
ctx.drawImage(obj.sprite, obj.x, obj.y, obj.width, obj.height);
}
}
});
}
// Game loop
let lastTime = 0;
let fpsTime = 0;
let frameCount = 0;
function gameLoop(timestamp = 0) {
if (!game.loaded) return;
const deltaTime = (timestamp - lastTime) / 1000;
lastTime = timestamp;
// Update
update(deltaTime);
// Render
render();
// FPS counter
fpsTime += deltaTime;
frameCount++;
if (fpsTime >= 1) {
document.getElementById('fpsCounter').textContent = `FPS: ${frameCount}`;
frameCount = 0;
fpsTime = 0;
}
requestAnimationFrame(gameLoop);
}
// Controls
function toggleFire() {
game.fireActive = !game.fireActive;
}
function resetScene() {
player.x = canvas.width / 2 - 50;
player.y = canvas.height / 2 - 50;
game.campfireFrame = 0;
game.fireActive = true;
}
// Keyboard input
window.addEventListener('keydown', (e) => {
game.keys[e.key] = true;
});
window.addEventListener('keyup', (e) => {
game.keys[e.key] = false;
});
// Initialize
loadAssets();
initScene();
</script>
</body>
</html>