Загрузка данных


const canvas = document.getElementById("renderCanvas");
const engine = new BABYLON.Engine(canvas, true);
const CONFIG = {
    MAP_HALF_SIZE: 28,
    PLAYER_SPEED: 0.18,
    PLAYER_ROT_SPEED: 0.05,
    CAMERA_HEIGHT_OFFSET: 12,
    CAMERA_Z_OFFSET: -12,
    COIN_COUNT: 5,
    COIN_SCORE_VALUE: 10,
    WIN_SCORE: 50,
    ENEMY_ORBIT_SPEED: 0.02,
    GROUND_SIZE: 60,
};
const ENEMIES_CONFIG = [
    {
        name: "enemy1",
        color: new BABYLON.Color3(1, 0.2, 0.2),
        startX: 10,
        startZ: 10,
        orbitRadius: 12,
        orbitSpeedX: 1.0,
        orbitSpeedZ: 1.0,
        rotSpeed: 0.03,
    },
    {
        name: "enemy2",
        color: new BABYLON.Color3(1, 0.5, 0.1),
        startX: -10,
        startZ: -10,
        orbitRadius: 15,
        orbitSpeedX: 0.7,
        orbitSpeedZ: 1.2,
        rotSpeed: 0.02,
    },
];
const COINS_CONFIG = Array.from({ length: CONFIG.COIN_COUNT }, (_, i) => ({
    x: -12 + i * 6,
    z: 12 - i * 3,
}));
const gameState = {
    hp: 100,
    score: 0,
    seconds: 0,
    orbitTime: 0,
    isGameOver: false,
    useFollowCamera: false,
    keys: {},
};
function createColoredBox(name, size, color, scene) {
    const mesh = BABYLON.MeshBuilder.CreateBox(name, { size }, scene);
    const mat = new BABYLON.StandardMaterial(name + "_mat", scene);
    mat.diffuseColor = color;
    mesh.material = mat;
    return mesh;
}
function createColoredSphere(name, diameter, color, scene) {
    const mesh = BABYLON.MeshBuilder.CreateSphere(name, { diameter }, scene);
    const mat = new BABYLON.StandardMaterial(name + "_mat", scene);
    mat.diffuseColor = color;
    mesh.material = mat;
    return mesh;
}
function createColoredCylinder(name, diameter, height, color, scene) {
    const mesh = BABYLON.MeshBuilder.CreateCylinder(name, { diameter, height }, scene);
    const mat = new BABYLON.StandardMaterial(name + "_mat", scene);
    mat.diffuseColor = color;
    mesh.material = mat;
    return mesh;
}
function createGround(scene) {
    const ground = BABYLON.MeshBuilder.CreateGround(
        "ground",
        { width: CONFIG.GROUND_SIZE, height: CONFIG.GROUND_SIZE },
        scene
    );
    const mat = new BABYLON.StandardMaterial("groundMat", scene);
    mat.diffuseColor = new BABYLON.Color3(0.3, 0.7, 0.3);
    ground.material = mat;
    return ground;
}
function createPlayer(scene) {
    const player = createColoredBox(
        "player",
        2,
        new BABYLON.Color3(0.2, 0.4, 1),
        scene
    );
    player.position.y = 1;
    return player;
}
function createEnemies(scene) {
    return ENEMIES_CONFIG.map((cfg) => {
        const enemy = createColoredSphere(cfg.name, 2, cfg.color, scene);
        enemy.position.set(cfg.startX, 1, cfg.startZ);
        return enemy;
    });
}
function createCoins(scene) {
    return COINS_CONFIG.map((cfg, i) => {
        const coin = createColoredCylinder(
            "coin" + i,
            1,
            0.3,
            new BABYLON.Color3(1, 0.85, 0.1),
            scene
        );
        coin.position.set(cfg.x, 1, cfg.z);
        return coin;
    });
}
function createCameras(scene) {
    const orbitCamera = new BABYLON.ArcRotateCamera(
        "orbitCam",
        Math.PI / 2,
        1.1,
        30,
        BABYLON.Vector3.Zero(),
        scene
    );
    orbitCamera.attachControl(canvas, true);

    const followCamera = new BABYLON.FreeCamera(
        "followCam",
        new BABYLON.Vector3(0, 15, -20),
        scene
    );
    followCamera.setTarget(BABYLON.Vector3.Zero());

    return { orbitCamera, followCamera };
}
function createLights(scene) {
    const hemi = new BABYLON.HemisphericLight(
        "hemiLight",
        new BABYLON.Vector3(0, 1, 0),
        scene
    );
    hemi.intensity = 0.95;

    const dir = new BABYLON.DirectionalLight(
        "dirLight",
        new BABYLON.Vector3(-1, -2, -1),
        scene
    );
    dir.position = new BABYLON.Vector3(20, 30, 20);
}
function createUILabel({ top, left = "-40%", color = "white", initialText }) {
    const block = new BABYLON.GUI.TextBlock();
    block.text = initialText;
    block.color = color;
    block.fontSize = 24;
    block.top = top;
    block.left = left;
    block.width = "200px";
    block.height = "40px";
    return block;
}
function createUI(scene) {
    const ui = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI("ui", true, scene);

    const hpText = createUILabel({ top: "-45%", initialText: `HP: ${gameState.hp}` });
    const scoreText = createUILabel({ top: "-40%", initialText: `Score: ${gameState.score}` });
    const timerText = createUILabel({ top: "-35%", initialText: "Time: 0" });

    const infoText = new BABYLON.GUI.TextBlock();
    infoText.text = "WASD — move | C — switch camera";
    infoText.color = "yellow";
    infoText.fontSize = 22;
    infoText.top = "45%";
    infoText.left = "0%";
    infoText.width = "500px";
    infoText.height = "40px";

    [hpText, scoreText, timerText, infoText].forEach((c) => ui.addControl(c));

    return { hpText, scoreText, timerText, infoText };
}
function setupInput(cameras, uiLabels) {
    const { orbitCamera, followCamera } = cameras;
    const { infoText } = uiLabels;

    window.addEventListener("keydown", (ev) => {
        const key = ev.key.toLowerCase();
        gameState.keys[key] = true;

        if (key === "c") {
            gameState.useFollowCamera = !gameState.useFollowCamera;

            if (gameState.useFollowCamera) {
                scene.activeCamera = followCamera;
                followCamera.attachControl(canvas, true);
                infoText.text = "Camera: follow";
            } else {
                scene.activeCamera = orbitCamera;
                orbitCamera.attachControl(canvas, true);
                infoText.text = "Camera: orbit";
            }
        }
    });

    window.addEventListener("keyup", (ev) => {
        gameState.keys[ev.key.toLowerCase()] = false;
    });
}
function startTimer(timerText) {
    setInterval(() => {
        if (!gameState.isGameOver) {
            gameState.seconds++;
            timerText.text = `Time: ${gameState.seconds}`;
        }
    }, 1000);
}
function updatePlayer(player) {
    const { keys } = gameState;
    if (keys["w"]) player.position.z += CONFIG.PLAYER_SPEED;
    if (keys["s"]) player.position.z -= CONFIG.PLAYER_SPEED;
    if (keys["a"]) player.rotation.y -= CONFIG.PLAYER_ROT_SPEED;
    if (keys["d"]) player.rotation.y += CONFIG.PLAYER_ROT_SPEED;

    const limit = CONFIG.MAP_HALF_SIZE;
    player.position.x = Math.max(-limit, Math.min(limit, player.position.x));
    player.position.z = Math.max(-limit, Math.min(limit, player.position.z));
}
function updateEnemies(enemies) {
    gameState.orbitTime += CONFIG.ENEMY_ORBIT_SPEED;
    const t = gameState.orbitTime;

    enemies.forEach((enemy, idx) => {
        const cfg = ENEMIES_CONFIG[idx];
        enemy.position.x = Math.sin(t * cfg.orbitSpeedX) * cfg.orbitRadius;
        enemy.position.z = Math.cos(t * cfg.orbitSpeedZ) * cfg.orbitRadius;
        enemy.rotation.y += cfg.rotSpeed;
    });
}
function updateFollowCamera(followCamera, player) {
    if (!gameState.useFollowCamera) return;
    followCamera.position.x = player.position.x;
    followCamera.position.y = player.position.y + CONFIG.CAMERA_HEIGHT_OFFSET;
    followCamera.position.z = player.position.z + CONFIG.CAMERA_Z_OFFSET;
    followCamera.setTarget(player.position);
}

function updateCoinCollection(player, coins, uiLabels) {
    coins.forEach((coin) => {
        if (!coin.isVisible) return;
        if (!player.intersectsMesh(coin, false)) return;

        coin.isVisible = false;
        gameState.score += CONFIG.COIN_SCORE_VALUE;
        uiLabels.scoreText.text = `Score: ${gameState.score}`;
    });
}

function applyEnemyDamage(player, enemies, uiLabels) {
    enemies.forEach((enemy) => {
        if (!player.intersectsMesh(enemy, false)) return;

        gameState.hp -= 1;
        uiLabels.hpText.text = `HP: ${gameState.hp}`;
    });
}

function checkEndConditions(uiLabels) {
    if (gameState.hp <= 0) {
        gameState.isGameOver = true;
        uiLabels.infoText.text = "Game over. Reload page.";
        return;
    }

    if (gameState.score >= CONFIG.WIN_SCORE) {
        gameState.isGameOver = true;
        uiLabels.infoText.text = "You win! Reload page.";
    }
}
function setupGameLoop(scene, player, enemies, coins, cameras, uiLabels) {
    scene.onBeforeRenderObservable.add(() => {
        if (gameState.isGameOver) return;

        updatePlayer(player);
        updateEnemies(enemies);
        updateFollowCamera(cameras.followCamera, player);
        updateCoinCollection(player, coins, uiLabels);
        applyEnemyDamage(player, enemies, uiLabels);
        checkEndConditions(uiLabels);
    });
}
function createScene() {
    const scene = new BABYLON.Scene(engine);
    scene.clearColor = new BABYLON.Color4(0.75, 0.88, 1, 1);

    const cameras = createCameras(scene);
    createLights(scene);
    createGround(scene);

    const player = createPlayer(scene);
    const enemies = createEnemies(scene);
    const coins = createCoins(scene);
    const uiLabels = createUI(scene);

    setupInput(cameras, uiLabels);
    startTimer(uiLabels.timerText);
    setupGameLoop(scene, player, enemies, coins, cameras, uiLabels);

    return scene;
}
const scene = createScene();
engine.runRenderLoop(() => scene.render());
window.addEventListener("resize", () => engine.resize());