Here is my file:
<?php
// dashboard.php
session_start();
// 1. SIMULERAT SALDO & DATABASDATA
$balance = 1450;
// Din besättning (Här simulerar vi resultatet från en databasfråga)
$stableHerd = [
// ... dina tidigare hästar (Brimir, Sleipner, Broder Isak, Munkens Fröjd) ...
[
'id' => 105,
'name' => 'Isfrun',
'status' => 'Glasklar blackis', // Den nya unika diagnosen!
'color' => [0.6, 0.7, 0.9], // Glasklar, isblå färgkod
'x' => 5, 'z' => 5
]
];
// Räkna hur många som ligger på lasarettet för varningsrutan
$sickCount = 0;
foreach ($stableHerd as $horse) {
if (strpos($horse['status'], 'Smittad') !== false) {
$sickCount++;
}
}
?>
<!DOCTYPE html>
<html lang="sv">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MonkAcres - Dashboard</title>
<!-- Bulma CSS & FontAwesome (Ikoner) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- Babylon.js Core Engine -->
<script src="https://cdn.babylonjs.com/babylon.js"></script>
<style>
body { background-color: #fcfaf2; }
#mmo-canvas-container {
width: 100%; height: 600px;
background-color: #111;
border-radius: 8px; border: 2px solid #d4c5a9;
position: relative; overflow: hidden;
box-shadow: 0 8px 16px rgba(0,0,0,0.1);
}
canvas { width: 100%; height: 100%; touch-action: none; }
.mmo-ui-overlay {
position: absolute; bottom: 20px; left: 20px;
background: rgba(255, 253, 249, 0.9);
padding: 15px; border-radius: 6px; border: 1px solid #d4c5a9;
pointer-events: none;
}
</style>
</head>
<body>
<div class="section">
<div class="container is-fluid">
<!-- HEADER -->
<div class="columns is-vcentered mb-5">
<div class="column">
<h1 class="title has-text-grey-darker">🏰 Klosterledningen & Lasarett</h1>
<h2 class="subtitle has-text-grey">Realtidsöversikt över dina ägor, din hingst och besättningens hälsa.</h2>
</div>
<div class="column is-narrow">
<div class="box p-3 has-text-centered" style="background-color: #fffdf9; border: 1px solid #d4c5a9;">
<p class="heading has-text-grey mb-0">Aktuell Balans</p>
<p class="title is-4 has-text-dark"><?php echo number_format($balance); ?> silvermynt</p>
</div>
</div>
</div>
<!-- UTGÅNGSPUNKT FÖR SIDA MED TVÅ KOLUMNER -->
<div class="columns">
<!-- VÄNSTER KOLUMN: 3D-Världen (Tar upp 9 av 12 block) -->
<div class="column is-9">
<div id="mmo-canvas-container">
<canvas id="babylon-canvas"></canvas>
<div class="mmo-ui-overlay">
<p class="heading">Kontroller & Indikatorer</p>
<p class="is-size-7"><strong>WASD / Piltangenter:</strong> Rid runt | <strong>Mellanslag:</strong> Hoppa</p>
<p class="is-size-7">🟢 Novishagen (Friska) | 🔴 Klosterlasarettet (Karantän)</p>
</div>
</div>
</div>
<!-- HÖGER KOLUMN: Sidopanel & Journaler (Tar upp 3 av 12 block) -->
<div class="column is-3">
<div class="box" style="background-color: #fffdf9; border: 1px solid #d4c5a9; height: 100%;">
<!-- SIDOMENYN (<aside>) -->
<aside class="menu">
<p class="menu-label">Navigation</p>
<ul class="menu-list">
<li><a class="is-active" style="background-color: #8b5a2b; color: white;"><i class="fas fa-map-marked-alt mr-2"></i> Ägorna (3D)</a></li>
<li><a href="breed_Rats.php"><i class="fas fa-paw mr-2"></i> Råtteckling (breed_Rats.php)</a></li>
<li>
<a id="open-feedback-btn" class="has-text-weight-bold has-text-brown">
<i class="fas fa-comment-alt mr-2"></i> Lämna Feedback / Rapport
</a>
</li>
</ul>
<!-- MEDICINSK JOURNAL -->
<p class="menu-label mt-5">Medicinsk Journal</p>
<?php if ($sickCount > 0): ?>
<div class="notification is-danger is-light is-size-7 p-2 mb-3">
<i class="fas fa-exclamation-triangle mr-1"></i> <strong>Smitta upptäckt!</strong><br>
<?php echo $sickCount; ?> djur är isolerade i lasarettet.
</div>
<?php endif; ?>
<table class="table is-fullwidth is-striped is-narrow is-size-7">
<thead>
<tr>
<th>Namn</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<?php foreach ($stableHerd as $horse): ?>
<tr>
<td><strong><?php echo htmlspecialchars($horse['name']); ?></strong></td>
<td>
<?php
if ($horse['status'] === 'Frisk') {
echo '<span class="tag is-success is-light">Frisk</span>';
} elseif ($horse['status'] === 'Hälta') {
echo '<span class="tag is-warning is-light">Hälta</span>';
} else {
echo '<span class="tag is-danger is-light">Karantän</span>';
}
?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</aside>
</div>
</div>
</div> <!-- ■■■■ på kolumner -->
</div>
</div>
<!-- BULMA MODAL FÖR FEEDBACK / INQUIRIES -->
<div id="feedback-modal" class="modal">
<div class="modal-background"></div>
<div class="modal-card">
<header class="modal-card-head" style="background-color: #fffdf9; border-bottom: 1px solid #d4c5a9;">
<p class="modal-card-title has-text-grey-darker">🏰 Meddelande till klosterledningen</p>
<button class="delete close-modal" aria-label="close"></button>
</header>
<form action="submit_inquiry.php" method="POST">
<section class="modal-card-body" style="background-color: #fcfaf2;">
<div class="field">
<label class="label has-text-grey">Typ av ärende</label>
<div class="control has-icons-left">
<div class="select is-fullwidth">
<select name="category" required>
<option value="suggestion">💡 Förslag & Idéer</option>
<option value="bug">🐛 Buggrapport</option>
<option value="balance">⚖️ Balansering (Silvermynt/Ekonomi)</option>
<option value="other">📜 Övriga frågor</option>
</select>
</div>
<div class="icon is-small is-left">
<i class="fas fa-filter"></i>
</div>
</div>
</div>
<div class="field">
<label class="label has-text-grey">Ditt meddelande</label>
<div class="control">
<textarea class="textarea" name="message" placeholder="Beskriv ditt ärende, så kommer vår självlöpande AI-instans att analysera det..." rows="5" required></textarea>
</div>
</div>
</section>
<footer class="modal-card-foot" style="background-color: #fffdf9; border-top: 1px solid #d4c5a9; justify-content: flex-end;">
<button type="button" class="button close-modal">Avbryt</button>
<button type="submit" class="button is-success" style="background-color: #8b5a2b; color: white;">Skicka till AI-loopen</button>
</footer>
</form>
</div>
</div>
<!-- BABYLON.JS JAVASCRIPT LOGIK -->
<script>
// Skicka besättningsdatan från PHP direkt till JavaScript
const serverHerd = <?php echo json_encode($stableHerd); ?>;
const canvas = document.getElementById('babylon-canvas');
const engine = new BABYLON.Engine(canvas, true);
const createScene = function () {
const scene = new BABYLON.Scene(engine);
// Atmosfär och dimma (Retro-känsla)
scene.clearColor = new BABYLON.Color3(0.82, 0.85, 0.88);
scene.fogMode = BABYLON.Scene.FOGMODE_EXP2;
scene.fogColor = new BABYLON.Color3(0.82, 0.85, 0.88);
scene.fogDensity = 0.012;
// Kamera & Ljus
const camera = new BABYLON.UniversalCamera("ThirdPersonCamera", new BABYLON.Vector3(0, 8, -15), scene);
const ambientLight = new BABYLON.HemisphericLight("ambient", new BABYLON.Vector3(0, 1, 0), scene);
ambientLight.intensity = 0.45;
const sunLight = new BABYLON.DirectionalLight("sunlight", new BABYLON.Vector3(-1, -2, -1), scene);
sunLight.position = new BABYLON.Vector3(20, 40, 20);
sunLight.intensity = 0.75;
const shadowGenerator = new BABYLON.ShadowGenerator(1024, sunLight);
shadowGenerator.useBlurExponentialShadowMap = true;
// Gräsmarken
const ground = BABYLON.MeshBuilder.CreateGround("ground", {width: 120, height: 120}, scene);
const groundMat = new BABYLON.StandardMaterial("groundMat", scene);
groundMat.diffuseColor = new BABYLON.Color3(0.31, 0.43, 0.26);
groundMat.specularColor = new BABYLON.Color3(0, 0, 0);
ground.material = groundMat;
ground.receiveShadow = true;
// --- STRUKTURER / HAGAR ---
// Grön hage (Friska djur)
const normalPen = BABYLON.MeshBuilder.CreateBox("normalPen", {width: 14, height: 0.3, depth: 14}, scene);
normalPen.position.set(10, 0.1, -8);
const normalMat = new BABYLON.StandardMaterial("normalMat", scene);
normalMat.wireframe = true;
normalMat.diffuseColor = new BABYLON.Color3(0.2, 0.6, 0.2);
normalPen.material = normalMat;
// Röd hage (Klosterlasarettet / Karantän för smittade)
const infirmaryPen = BABYLON.MeshBuilder.CreateBox("infirmaryPen", {width: 10, height: 0.5, depth: 10}, scene);
infirmaryPen.position.set(-10, 0.1, 10);
const infirmaryMat = new BABYLON.StandardMaterial("infirmaryMat", scene);
infirmaryMat.wireframe = true;
infirmaryMat.diffuseColor = new BABYLON.Color3(0.8, 0.1, 0.1);
infirmaryPen.material = infirmaryMat;
// --- MATERIAL FÖR VOXELMODELLERNA ---
const coatMat = new BABYLON.StandardMaterial("coatMat", scene);
coatMat.diffuseColor = new BABYLON.Color3(0.54, 0.35, 0.17); // Brun standard
coatMat.specularColor = new BABYLON.Color3(0, 0, 0);
const maneMat = new BABYLON.StandardMaterial("maneMat", scene);
maneMat.diffuseColor = new BABYLON.Color3(0.15, 0.1, 0.07); // Mörk man/hovar
maneMat.specularColor = new BABYLON.Color3(0, 0, 0);
// --- SPELARENS KARAKTÄR (Sammansatt Voxel-hingst) ---
const playerGroup = new BABYLON.TransformNode("playerGroup", scene);
// Kropp (Centrerad, nollpunkten på playerGroup hålls på marken y=0)
const body = BABYLON.MeshBuilder.CreateBox("pBody", {width: 1.0, height: 0.9, depth: 2.0}, scene);
body.position.y = 1.25;
body.parent = playerGroup;
body.material = coatMat;
shadowGenerator.addShadowCaster(body);
// Hals & Huvud
const neck = BABYLON.MeshBuilder.CreateBox("pNeck", {width: 0.5, height: 0.9, depth: 0.6}, scene);
neck.position.set(0, 1.9, 0.7);
neck.rotation.x = -0.2;
neck.parent = playerGroup;
neck.material = coatMat;
const head = BABYLON.MeshBuilder.CreateBox("pHead", {width: 0.5, height: 0.5, depth: 0.9}, scene);
head.position.set(0, 2.3, 0.9);
head.parent = playerGroup;
head.material = coatMat;
shadowGenerator.addShadowCaster(head);
// Ben med pivotsystem (för ren och skarp animation)
const legs = [];
const legPositions = [
{ name: "FL", x: -0.35, z: 0.7 }, // Fram Vänster
{ name: "FR", x: 0.35, z: 0.7 }, // Fram Höger
{ name: "BL", x: -0.35, z: -0.7 }, // Bak Vänster
{ name: "BR", x: 0.35, z: -0.7 } // Bak Höger
];
legPositions.forEach((pos) => {
const legPivot = new BABYLON.TransformNode("pLegPivot_" + pos.name, scene);
legPivot.position.set(pos.x, 0.8, pos.z);
legPivot.parent = playerGroup;
const legMesh = BABYLON.MeshBuilder.CreateBox("pLegMesh_" + pos.name, {width: 0.25, height: 0.8, depth: 0.25}, scene);
legMesh.position.y = -0.4;
legMesh.parent = legPivot;
legMesh.material = coatMat;
shadowGenerator.addShadowCaster(legMesh);
const hoof = BABYLON.MeshBuilder.CreateBox("pHoof_" + pos.name, {width: 0.27, height: 0.15, depth: 0.27}, scene);
hoof.position.y = -0.8;
hoof.parent = legPivot;
hoof.material = maneMat;
legs.push({ name: pos.name, node: legPivot });
});
playerGroup.position = new BABYLON.Vector3(0, 0, 0);
// --- HÄMTAR OCH RENDERA DJURBESTÅNDET FRÅN PHP ---
const npcContainer = [];
serverHerd.forEach((data) => {
const npcGroup = new BABYLON.TransformNode("npc_" + data.id, scene);
// Unikt material baserat på färgvektorn från servern
const npcMat = new BABYLON.StandardMaterial("mat_" + data.id, scene);
npcMat.diffuseColor = new BABYLON.Color3(data.color[0], data.color[1], data.color[2]);
npcMat.specularColor = new BABYLON.Color3(0, 0, 0);
const nBody = BABYLON.MeshBuilder.CreateBox("b_" + data.id, {width: 1.0, height: 0.9, depth: 2.0}, scene);
nBody.position.y = 1.25;
nBody.parent = npcGroup;
nBody.material = npcMat;
shadowGenerator.addShadowCaster(nBody);
const nHead = BABYLON.MeshBuilder.CreateBox("h_" + data.id, {width: 0.5, height: 0.5, depth: 0.9}, scene);
nHead.position.set(0, 2.3, 0.9);
nHead.parent = npcGroup;
nHead.material = npcMat;
// Sätt koordinater utifrån serverns data
npcGroup.position.set(data.x, 0, data.z);
npcContainer.push({
node: npcGroup,
status: data.status
});
});
// INPUTHANTERING
const inputMap = {};
window.addEventListener("keydown", (evt) => {
inputMap[evt.key.toLowerCase()] = true;
if (evt.code === "Space") inputMap["space"] = true;
});
window.addEventListener("keyup", (evt) => {
inputMap[evt.key.toLowerCase()] = false;
if (evt.code === "Space") inputMap["space"] = false;
});
// FYSIK & MOTION-VARIABLER
let moveSpeed = 0.15;
let rotationSpeed = 0.04;
let velocityY = 0;
let gravity = 0.01;
let isJumping = false;
let animTime = 0;
// REALTIDSLOOP (RENDER TICK)
scene.onBeforeRenderObservable.add(() => {
let isMoving = false;
// Styrning (Rotation & Rörelse)
if (inputMap["a"] || inputMap["arrowleft"]) playerGroup.rotation.y += rotationSpeed;
if (inputMap["d"] || inputMap["arrowright"]) playerGroup.rotation.y -= rotationSpeed;
if (inputMap["w"] || inputMap["arrowup"]) {
playerGroup.position.x += Math.sin(playerGroup.rotation.y) * moveSpeed;
playerGroup.position.z += Math.cos(playerGroup.rotation.y) * moveSpeed;
isMoving = true;
}
if (inputMap["s"] || inputMap["arrowdown"]) {
playerGroup.position.x -= Math.sin(playerGroup.rotation.y) * moveSpeed;
playerGroup.position.z -= Math.cos(playerGroup.rotation.y) * moveSpeed;
isMoving = true;
}
// Gång-animation via sinusvågor på benens pivotpunkter
if (isMoving && !isJumping) {
animTime += 0.2;
legs.forEach(leg => {
if (leg.name === "FL" || leg.name === "BR") {
leg.node.rotation.x = Math.sin(animTime) * 0.4;
} else {
leg.node.rotation.x = -Math.sin(animTime) * 0.4;
}
});
} else if (!isJumping) {
legs.forEach(leg => leg.node.rotation.x = 0); // Nollställ vid stopp
}
// Hopp-fysik (Mellanslag)
if (inputMap["space"] && !isJumping) {
velocityY = 0.22;
isJumping = true;
legs.forEach(leg => {
leg.node.rotation.x = leg.name.startsWith("F") ? 0.2 : -0.2; // Dra upp benen snyggt
});
}
if (isJumping) {
playerGroup.position.y += velocityY;
velocityY -= gravity;
if (playerGroup.position.y <= 0) {
playerGroup.position.y = 0;
isJumping = false;
velocityY = 0;
}
}
// SIMULERA DIAGNOSBASERAT BETEANDE HOS REPTIL-/HÄSTBESTÅNDET
npcContainer.forEach(npc => {
if (npc.status === 'Frisk') {
// Friska går runt i Novishagen normalt
if (Math.random() < 0.02) npc.node.rotation.y += (Math.random() - 0.5) * 0.5;
npc.node.position.x += Math.sin(npc.node.rotation.y) * 0.02;
npc.node.position.z += Math.cos(npc.node.rotation.y) * 0.02;
}
else if (npc.status === 'Hälta') {
// Halta rör sig extremt trögt (hastighet 0.003)
if (Math.random() < 0.01) npc.node.rotation.y += (Math.random() - 0.5) * 0.2;
npc.node.position.x += Math.sin(npc.node.rotation.y) * 0.003;
npc.node.position.z += Math.cos(npc.node.rotation.y) * 0.003;
}
else if (npc.status.startsWith('Smittad')) {
// Smittade står helt stilla på lasarettet och skakar i feber (mikro-offsets på x)
npc.node.position.x = -10 + (Math.random() - 0.5) * 0.01;
}
});
// Tredjepersonkamera med Lerp-slätning som följer spelaren bakifrån
const targetCameraPos = playerGroup.position.add(new BABYLON.Vector3(
-Math.sin(playerGroup.rotation.y) * 13, 6, -Math.cos(playerGroup.rotation.y) * 13
));
camera.position = BABYLON.Vector3.Lerp(camera.position, targetCameraPos, 0.05);
camera.setTarget(playerGroup.position.add(new BABYLON.Vector3(0, 1.3, 0)));
});
return scene;
};
const scene = createScene();
engine.runRenderLoop(() => { scene.render(); });
window.addEventListener("resize", () => { engine.resize(); });
// MODAL JAVASCRIPT (Styr feedback-fönstret)
const modal = document.getElementById('feedback-modal');
document.getElementById('open-feedback-btn').addEventListener('click', () => modal.classList.add('is-active'));
document.querySelectorAll('.close-modal, .modal-background').forEach(element => {
element.addEventListener('click', () => modal.classList.remove('is-active'));
});
</script>
</body>
</html>
I want to implement this:
') {
if (Math.random() < 0.02) npc.node.rotation.y += (Math.random() - 0.5) * 0.5;
npc.node.position.x += Math.sin(npc.node.rotation.y) * 0.02;
npc.node.position.z += Math.cos(npc.node.rotation.y) * 0.02;
}
else if (npc.status === 'Hälta') {
if (Math.random() < 0.01) npc.node.rotation.y += (Math.random() - 0.5) * 0.2;
npc.node.position.x += Math.sin(npc.node.rotation.y) * 0.003;
npc.node.position.z += Math.cos(npc.node.rotation.y) * 0.003;
}
else if (npc.status.startsWith('Smittad')) {
npc.node.position.x = -10 + (Math.random() - 0.5) * 0.01;
}
// --- NY MEKANIK: GLASKLAR BLACKIS ---
else if (npc.status === 'Glasklar blackis') {
// Initiera unika variabler för denna häst om de inte redan finns
if (npc.isBlackout === undefined) {
npc.isBlackout = false;
npc.blackoutTimer = 0;
}
if (npc.isBlackout) {
// Hästen har en "blackout" – står helt blixtstilla och stirrar tomt framåt
npc.blackoutTimer--;
if (npc.blackoutTimer <= 0) {
npc.isBlackout = false; // Den vaknar upp!
}
} else {
// Hästen är "glasklar" – rör sig helt normalt och piggt
if (Math.random() < 0.02) npc.node.rotation.y += (Math.random() - 0.5) * 0.6;
npc.node.position.x += Math.sin(npc.node.rotation.y) * 0.03;
npc.node.position.z += Math.cos(npc.node.rotation.y) * 0.03;
// 0.5% risk varje frame att den plötsligt drabbas av en blackis
if (Math.random() < 0.005) {
npc.isBlackout = true;
npc.blackoutTimer = Math.floor(Math.random() * 120) + 60; // Fryser fast i 1-3 sekunder (60-180 frames)
}
}
}
});