from ursina import *
from ursina.prefabs.first_person_controller import FirstPersonController
import random
import time

app = Ursina(fullscreen=True)
window.size = (2560),(1440)
# Отключаем стандартный указатель
window.exit_button.visible = False
mouse.visible = False

# Создание неба
sky = Sky()

# Загрузка текстуры и звуков
block_texture = load_texture('textures/block_texture.png')
break_sounds = [
    Audio('sounds/break_stone1.ogg', autoplay=False),
    Audio('sounds/break_stone2.ogg', autoplay=False),
    Audio('sounds/break_stone3.ogg', autoplay=False),
    Audio('sounds/break_stone4.ogg', autoplay=False)
]
place_sounds = [
    Audio('sounds/place_stone1.ogg', autoplay=False),
    Audio('sounds/place_stone2.ogg', autoplay=False),
    Audio('sounds/place_stone3.ogg', autoplay=False),
    Audio('sounds/place_stone4.ogg', autoplay=False)
]

# Создание класса блока
class Block(Entity):
    def __init__(self, position=(0, 0, 0), block_type='Stone'):
        super().__init__(
            model='cube',
            texture=block_texture,
            position=position,
            collider='box'
        )
        self.block_type = block_type

# Функция проверки наличия блока в указанной позиции
def is_block_at(position):
    for entity in scene.entities:
        if entity.position == position and isinstance(entity, Block):
            return True
    return False

# Функция проверки, можно ли ставить блок перед игроком
def can_place_block(position):
    block_size = Vec3(1, 1, 1)
    player_size = Vec3(1, 1.8, 1)  # размер игрока (примерно 1x1 блок в основании и 1.8 блока высотой)
    
    block_center = position + block_size / 2
    player_center = player.position + player_size / 2
    
    return not (
        abs(block_center.x - player_center.x) < (block_size.x + player_size.x) / 2 and
        abs(block_center.y - player_center.y) < (block_size.y + player_size.y) / 2 and
        abs(block_center.z - player_center.z) < (block_size.z + player_size.z) / 2
    )

# Создание пола из блоков
for x in range(-15, 16):
    for z in range(-15, 16):
        Block(position=(x, 0, z))

class CustomFirstPersonController(FirstPersonController):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.is_running = False  # Переменная для контроля бега
        self.walk_speed = 5.3  # Скорость ходьбы
        self.run_speed = 8.5  # Скорость бега

    def input(self, key):
        super().input(key)  # Важно вызвать метод родительского класса для обработки других клавиш
        if key == 'left control' or key == 'right control':
            self.is_running = not self.is_running  # Переключаем состояние бега по нажатию на CTRL

    def update(self):
        super().update()
        self.speed = self.run_speed if self.is_running else self.walk_speed  # Устанавливаем скорость в зависимости от состояния бега

        if not held_keys['w']:
            self.is_running = False  # Останавливаем бег, если клавиша "W" не удерживается

# Используем нашу кастомную версию контроллера
player = CustomFirstPersonController()
player.mouse_sensitivity = Vec2(150, 150)  # Увеличиваем чувствительность мыши

# Освещение
DirectionalLight(rotation=(45, -45, 45))
AmbientLight(color=color.rgba(100, 100, 100, 0.5))

highlight_block = Entity(
    model=Mesh(
        vertices=[
            Vec3(-0.5, -0.5, -0.5), Vec3(0.5, -0.5, -0.5),
            Vec3(0.5, -0.5, 0.5), Vec3(-0.5, -0.5, 0.5),
            Vec3(-0.5, 0.5, -0.5), Vec3(0.5, 0.5, -0.5),
            Vec3(0.5, 0.5, 0.5), Vec3(-0.5, 0.5, 0.5)
        ],
        triangles=[
            (0, 1, 2), (2, 3, 0), (4, 5, 6), (6, 7, 4),
            (0, 4, 5), (5, 1, 0), (1, 5, 6), (6, 2, 1),
            (2, 6, 7), (7, 3, 2), (3, 7, 4), (4, 0, 3)
        ],
        mode='line'
    ),
    color=color.black,
    scale=(1, 1, 1),
    visible=False
)

# Крестик по центру экрана
crosshair_size = 0.02
crosshair_thickness = 0.002

# Горизонтальная линия крестика
crosshair_horizontal = Entity(
    parent=camera.ui,
    model='quad',
    color=color.gray,
    scale=(crosshair_size, crosshair_thickness),
    position=(0, 0)
)

# Вертикальная линия крестика
crosshair_vertical = Entity(
    parent=camera.ui,
    model='quad',
    color=color.gray,
    scale=(crosshair_thickness, crosshair_size),
    position=(0, 0)
)

# Переменные для контроля задержки
place_block_delay = 0.2
last_place_block_time = time.time()
right_mouse_held = False

# Класс частицы с гравитацией
class Particle(Entity):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.velocity = Vec3(random.uniform(-0.5, 0.5), random.uniform(0.5, 1.0), random.uniform(-0.5, 0.5))
        self.gravity = Vec3(0, -0.1, 0)

    def update(self):
        self.velocity += self.gravity
        self.position += self.velocity * time.dt

        # Уничтожаем частицу, если она находится ниже определенного уровня
        if self.position.y < -10:
            destroy(self)

# Функция для создания эффекта частиц при разрушении блока
def create_break_particles(position):
    for _ in range(20):
        particle = Particle(
            model='cube',
            texture=block_texture,
            scale=0.1,
            position=position + Vec3(random.uniform(-0.5, 0.5), random.uniform(-0.5, 0.5), random.uniform(-0.5, 0.5)),
            color=color.white
        )

# Обработка ввода для выхода из игры и взаимодействия с блоками
def input(key):
    global last_place_block_time, right_mouse_held
    if key == 'escape':
        application.quit()
    if key == 'left mouse down':  # ломать блоки
        hit_info = mouse.hovered_entity
        if hit_info and isinstance(hit_info, Block) and distance(player.position, hit_info.position) < 5:
            random.choice(break_sounds).play()  # проигрываем случайный звук разрушения
            create_break_particles(hit_info.position)  # Создаем эффект частиц
            destroy(hit_info)
    if key == 'right mouse down':
        right_mouse_held = True
        place_block()
    if key == 'right mouse up':
        right_mouse_held = False

def place_block():
    global last_place_block_time
    hit_info = mouse.hovered_entity
    if hit_info and isinstance(hit_info, Block):
        position = hit_info.position + mouse.normal
        if not is_block_at(position) and distance(player.position, position) < 4 and can_place_block(position):
            Block(position=position)
            random.choice(place_sounds).play()  # проигрываем случайный звук установки
            last_place_block_time = time.time()

def update():
    global last_place_block_time
    highlight_block.visible = False

    if mouse.hovered_entity and isinstance(mouse.hovered_entity, Block):
        if distance(player.position, mouse.hovered_entity.position) < 4:
            highlight_block.position = mouse.hovered_entity.position
            highlight_block.visible = True

    if right_mouse_held:
        current_time = time.time()
        if current_time - last_place_block_time > place_block_delay:
            place_block()

camera.fov = 110  # Установка FOV после инициализации игры, но до app.run()

app.run()

Сделай чтобы я зажимал левую кнопку мышки и ломал блок (с задержкой 0.2) до тех пор пока мышка не будет отпущена