Advertisement
PaffcioStudio

Untitled

May 3rd, 2025
168
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 89.06 KB | None | 0 0
  1. import pygame
  2. from pygame.locals import *
  3. from OpenGL.GL import *
  4. from OpenGL.GLU import *
  5. import numpy as np
  6. import random
  7. import os
  8. import pickle
  9. import time
  10. from collections import OrderedDict
  11. import zipfile
  12. import io
  13. import json
  14. import shutil
  15. import math
  16. import noise
  17. import traceback
  18.  
  19. pygame.init()
  20. pygame.font.init()
  21.  
  22. info = pygame.display.Info()  # Pobieranie informacji o ekranie
  23. screen_width = info.current_w  # Szerokość ekranu
  24. screen_height = info.current_h  # Wysokość ekranu
  25.  
  26. display = (screen_width, screen_height)  # Ustawienie rozdzielczości okna gry na rozdzielczość ekranu
  27. print(f"Gra uruchomiona w {screen_width}x{screen_height}")
  28.  
  29. pygame.display.set_mode(display, DOUBLEBUF | OPENGL | RESIZABLE)  # Ustawienie okna gry
  30.  
  31. # Ustawienia OpenGL
  32. glClearColor(0.5, 0.7, 1.0, 1.0)  # Jasne niebo jako tło
  33. glEnable(GL_DEPTH_TEST)
  34. glEnable(GL_CULL_FACE)
  35. glEnable(GL_BLEND)
  36. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
  37. glEnable(GL_TEXTURE_2D)
  38.  
  39. WORLD_SIZES = OrderedDict([
  40.     ("Mały (8x8x8)", 8),
  41.     ("Średni (16x16x16)", 16),
  42.     ("Duży (32x32x32)", 32),
  43.     ("Ogromny (64x64x64)", 64),
  44.     ("Nieskończony", None)
  45. ])
  46.  
  47. DEFAULT_SETTINGS = {
  48.     "mouse_sensitivity": 0.1,
  49.     "move_speed": 0.1,
  50.     "jump_force": 0.3,
  51.     "gravity": 0.01,
  52.     "fov": 70,
  53.     "render_distance": 8,
  54.     "resource_pack": "Brak",
  55.     "invert_mouse_x": False,
  56.     "invert_mouse_y": False
  57. }
  58.  
  59. BLOCK_TEXTURE_PATHS = {
  60.     1: "blocks/grass.png",
  61.     2: "blocks/dirt.png",
  62.     3: "blocks/stone.png",
  63. }
  64.  
  65. BLOCK_TYPES_TO_INV_NAMES = {
  66.     1: "Trawa",
  67.     2: "Ziemia",
  68.     3: "Kamień",
  69. }
  70.  
  71. BLOCK_AIR = 0
  72. CHUNK_SIZE = 16
  73. font_small = None
  74. font_medium = None
  75. font_large = None
  76. font_height = 0
  77. settings = DEFAULT_SETTINGS.copy()
  78.  
  79. class ResourceManager:
  80.     def __init__(self, base_asset_dir="assets", rp_dir="resourcepacks"):
  81.         self.base_asset_dir = base_asset_dir
  82.         self.rp_dir = rp_dir
  83.         self.available_packs = ["Brak"]
  84.         self.active_pack_name = "Brak"
  85.         self.active_pack_zip = None
  86.         self._texture_cache = {}
  87.         self.scan_resource_packs()
  88.  
  89.     def scan_resource_packs(self):
  90.         self.available_packs = ["Brak"]
  91.         if not os.path.exists(self.rp_dir):
  92.             os.makedirs(self.rp_dir)
  93.             return
  94.  
  95.         for item_name in os.listdir(self.rp_dir):
  96.             item_path = os.path.join(self.rp_dir, item_name)
  97.             if os.path.isfile(item_path) and item_name.endswith(".zip"):
  98.                 try:
  99.                     with zipfile.ZipFile(item_path, 'r') as zf:
  100.                         if any(name.startswith('assets/minecraft/textures/') for name in zf.namelist()) or 'pack.mcmeta' in zf.namelist():
  101.                             self.available_packs.append(item_name)
  102.                         else:
  103.                             print(f"'{item_name}' nie jest resource packiem, pomijam.")
  104.                 except zipfile.BadZipFile:
  105.                     print(f"'{item_name}' nie jest prawidłowym ZIP, pomijam.")
  106.                 except Exception as e:
  107.                     print(f"Błąd skanowania '{item_name}': {e}")
  108.  
  109.     def activate_resource_pack(self, pack_name):
  110.         if pack_name not in self.available_packs:
  111.             print(f"Nieznany pack: {pack_name}. Używam 'Brak'.")
  112.             pack_name = "Brak"
  113.  
  114.         if self.active_pack_name == pack_name:
  115.             return
  116.  
  117.         print(f"Aktywacja: {pack_name}")
  118.         self.clear_texture_cache()
  119.  
  120.         if self.active_pack_zip:
  121.             try:
  122.                 self.active_pack_zip.close()
  123.             except Exception as e:
  124.                 print(f"Błąd zamykania ZIP '{self.active_pack_name}': {e}")
  125.             self.active_pack_zip = None
  126.  
  127.         self.active_pack_name = pack_name
  128.  
  129.         if pack_name != "Brak":
  130.             item_path = os.path.join(self.rp_dir, pack_name)
  131.             try:
  132.                 self.active_pack_zip = zipfile.ZipFile(item_path, 'r')
  133.             except FileNotFoundError:
  134.                 print(f"Pack '{pack_name}' nie znaleziono.")
  135.                 self.active_pack_name = "Brak"
  136.             except zipfile.BadZipFile:
  137.                 print(f"'{pack_name}' nie jest prawidłowym ZIP.")
  138.                 self.active_pack_name = "Brak"
  139.             except Exception as e:
  140.                 print(f"Błąd otwierania '{pack_name}': {e}")
  141.                 self.active_pack_name = "Brak"
  142.  
  143.     def clear_texture_cache(self):
  144.         texture_ids = [tid for tid in self._texture_cache.values() if tid > 0 and glIsTexture(tid)]
  145.         if texture_ids:
  146.             try:
  147.                 glDeleteTextures(texture_ids)
  148.             except Exception as e:
  149.                 print(f"Błąd usuwania tekstur: {e}")
  150.         self._texture_cache = {}
  151.  
  152.     def _load_texture_opengl_from_surface(self, texture_surface, is_game_asset=True):
  153.         if texture_surface is None:
  154.             return 0
  155.  
  156.         try:
  157.             texture_surface = texture_surface.convert_alpha()
  158.             texture_data = pygame.image.tobytes(texture_surface, "RGBA", False)
  159.             width, height = texture_surface.get_size()
  160.  
  161.             if width == 0 or height == 0:
  162.                 return 0
  163.  
  164.             tex_id = glGenTextures(1)
  165.             glBindTexture(GL_TEXTURE_2D, tex_id)
  166.             filter_type = GL_NEAREST if is_game_asset else GL_LINEAR
  167.             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter_type)
  168.             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter_type)
  169.             wrap_mode = GL_REPEAT if is_game_asset else GL_CLAMP_TO_EDGE
  170.             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_mode)
  171.             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_mode)
  172.             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture_data)
  173.             return tex_id
  174.         except Exception as e:
  175.             print(f"Błąd tworzenia tekstury: {e}")
  176.             return 0
  177.  
  178.     def load_texture_game_asset(self, path_in_textures_folder):
  179.         cache_key = (self.active_pack_name, path_in_textures_folder)
  180.         if cache_key in self._texture_cache:
  181.             print(f"Tekstura z cache: {path_in_textures_folder}, tex_id={self._texture_cache[cache_key]}")
  182.             return self._texture_cache[cache_key]
  183.  
  184.         tex_id = 0
  185.         texture_surface = None
  186.  
  187.         if self.active_pack_zip:
  188.             rp_internal_path = f"assets/minecraft/textures/{path_in_textures_folder}"
  189.             print(f"Próba ładowania z resource pack: {rp_internal_path}")
  190.             try:
  191.                 with self.active_pack_zip.open(rp_internal_path) as texture_file:
  192.                     texture_bytes = io.BytesIO(texture_file.read())
  193.                     texture_surface = pygame.image.load(texture_bytes)
  194.                     print(f"Załadowano z resource pack: {rp_internal_path}")
  195.             except KeyError:
  196.                 print(f"Brak tekstury w resource pack: {rp_internal_path}")
  197.             except Exception as e:
  198.                 print(f"Błąd ładowania '{rp_internal_path}' z '{self.active_pack_name}': {e}")
  199.  
  200.         if texture_surface is None:
  201.             default_path = os.path.join(self.base_asset_dir, "textures", path_in_textures_folder)
  202.             print(f"Próba ładowania domyślnej tekstury: {default_path}")
  203.             if os.path.exists(default_path):
  204.                 try:
  205.                     texture_surface = pygame.image.load(default_path)
  206.                     print(f"Załadowano domyślną teksturę: {default_path}")
  207.                 except pygame.error as e:
  208.                     print(f"Błąd Pygame dla '{default_path}': {e}")
  209.                 except Exception as e:
  210.                     print(f"Nieznany błąd dla '{default_path}': {e}")
  211.             else:
  212.                 print(f"Plik nie istnieje: {default_path}")
  213.  
  214.         if texture_surface is not None:
  215.             tex_id = self._load_texture_opengl_from_surface(texture_surface, is_game_asset=True)
  216.             if tex_id > 0:
  217.                 self._texture_cache[cache_key] = tex_id
  218.                 print(f"Utworzono teksturę: {path_in_textures_folder}, tex_id={tex_id}")
  219.                 return tex_id
  220.             else:
  221.                 print(f"Nie udało się utworzyć tekstury dla '{path_in_textures_folder}'.")
  222.         else:
  223.             print(f"Brak powierzchni tekstury dla: {path_in_textures_folder}")
  224.  
  225.         # Fallback na dummy.png
  226.         dummy_path = os.path.join(self.base_asset_dir, "textures", "dummy.png")
  227.         print(f"Próba ładowania dummy: {dummy_path}")
  228.         if os.path.exists(dummy_path):
  229.             cache_key_dummy = ("UI", dummy_path)
  230.             if cache_key_dummy in self._texture_cache:
  231.                 print(f"Dummy z cache: {dummy_path}, tex_id={self._texture_cache[cache_key_dummy]}")
  232.                 return self._texture_cache[cache_key_dummy]
  233.             try:
  234.                 dummy_surface = pygame.image.load(dummy_path)
  235.                 dummy_tex_id = self._load_texture_opengl_from_surface(dummy_surface, is_game_asset=False)
  236.                 if dummy_tex_id > 0:
  237.                     self._texture_cache[cache_key_dummy] = dummy_tex_id
  238.                     self._texture_cache[cache_key] = dummy_tex_id
  239.                     print(f"Użyto dummy tekstury dla '{path_in_textures_folder}', tex_id={dummy_tex_id}")
  240.                     return dummy_tex_id
  241.             except Exception as e:
  242.                 print(f"Błąd ładowania zastępczej '{dummy_path}': {e}")
  243.         else:
  244.             print(f"Brak dummy tekstury: {dummy_path}")
  245.  
  246.         print(f"Nie udało się załadować '{path_in_textures_folder}' ani dummy.")
  247.         return 0
  248.  
  249.     def load_texture_ui_asset(self, path_in_base_asset_dir):
  250.         cache_key = ("UI", path_in_base_asset_dir)
  251.         if cache_key in self._texture_cache:
  252.             print(f"Tekstura UI z cache: {path_in_base_asset_dir}, tex_id={self._texture_cache[cache_key]}")
  253.             return self._texture_cache[cache_key]
  254.  
  255.         default_path = os.path.join(self.base_asset_dir, path_in_base_asset_dir)
  256.         print(f"Pełna ścieżka do pliku: {default_path}")
  257.         tex_id = 0
  258.         if os.path.exists(default_path):
  259.             try:
  260.                 texture_surface = pygame.image.load(default_path)
  261.                 print(f"Załadowano teksturę UI: {default_path}")
  262.                 tex_id = self._load_texture_opengl_from_surface(texture_surface, is_game_asset=False)
  263.                 if tex_id > 0:
  264.                     self._texture_cache[cache_key] = tex_id
  265.                     print(f"Utworzono teksturę UI: {default_path}, tex_id={tex_id}")
  266.                     return tex_id
  267.                 else:
  268.                     print(f"Błąd tworzenia tekstury UI z {default_path}.")
  269.             except pygame.error as e:
  270.                 print(f"Błąd Pygame przy ładowaniu {default_path}: {e}")
  271.             except Exception as e:
  272.                 print(f"Nieznany błąd dla {default_path}: {e}")
  273.         else:
  274.             print(f"Plik UI nie istnieje: {default_path}")
  275.         return 0
  276.  
  277.  
  278. def load_text_texture(text, font, color):
  279.     if not text:
  280.         return 0, 0, 0
  281.     try:
  282.         # Renderowanie tekstu
  283.         text_surface = font.render(text, True, color).convert_alpha()
  284.         # Odwracamy tylko powierzchnię tekstu w pionie, aby naprawić efekt lustra (w osi Y)
  285.         text_surface = pygame.transform.flip(text_surface, False, True)  # False dla osi X, True dla osi Y
  286.         text_data = pygame.image.tobytes(text_surface, "RGBA", False)
  287.         width, height = text_surface.get_size()
  288.         if width == 0 or height == 0:
  289.             return 0, 0, 0
  290.        
  291.         # Tworzenie tekstury OpenGL
  292.         tex_id = glGenTextures(1)
  293.         glBindTexture(GL_TEXTURE_2D, tex_id)
  294.         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
  295.         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
  296.         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
  297.         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
  298.        
  299.         # Przypisanie danych tekstury
  300.         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, text_data)
  301.         glBindTexture(GL_TEXTURE_2D, 0)
  302.        
  303.         return tex_id, width, height
  304.     except Exception as e:
  305.         print(f"Błąd tworzenia tekstury tekstu '{text}': {e}")
  306.         return 0, 0, 0
  307.  
  308.  
  309.  
  310. class ChunkManager:
  311.     def __init__(self, seed=0, render_distance_chunks=8, world_name_dir="infinite_world_default"):
  312.         self.seed = seed
  313.         self.render_distance_chunks = render_distance_chunks
  314.         self.world_name_dir = world_name_dir
  315.         self.chunks = {}
  316.         self._chunks_to_save = set()
  317.         self.world_dir = os.path.join("worlds", world_name_dir)
  318.         self.region_dir = os.path.join(self.world_dir, "region")
  319.         os.makedirs(self.region_dir, exist_ok=True)
  320.  
  321.     def get_block(self, x, y, z):
  322.         BUILD_HEIGHT_LIMIT = 256
  323.         if y < 0 or y >= BUILD_HEIGHT_LIMIT:
  324.             return BLOCK_AIR
  325.         cx, cy, cz = x // CHUNK_SIZE, y // CHUNK_SIZE, z // CHUNK_SIZE
  326.         lx, ly, lz = x % CHUNK_SIZE, y % CHUNK_SIZE, z % CHUNK_SIZE
  327.         lx = lx if lx >= 0 else lx + CHUNK_SIZE
  328.         ly = ly if ly >= 0 else ly + CHUNK_SIZE
  329.         lz = lz if lz >= 0 else lz + CHUNK_SIZE
  330.         chunk_coords = (cx, cy, cz)
  331.         if chunk_coords in self.chunks:
  332.             if 0 <= lx < CHUNK_SIZE and 0 <= ly < CHUNK_SIZE and 0 <= lz < CHUNK_SIZE:
  333.                 return self.chunks[chunk_coords][lx][ly][lz]
  334.             else:
  335.                 print(f"Nieprawidłowe współrzędne ({lx}, {ly}, {lz}) dla chunka ({cx}, {cy}, {cz}).")
  336.         return BLOCK_AIR
  337.  
  338.     def set_block(self, x, y, z, block_type):
  339.         BUILD_HEIGHT_LIMIT = 256
  340.         if y < 0 or y >= BUILD_HEIGHT_LIMIT:
  341.             print(f"Blok poza limitem wysokości ({y}).")
  342.             return
  343.         cx, cy, cz = x // CHUNK_SIZE, y // CHUNK_SIZE, z // CHUNK_SIZE
  344.         lx, ly, lz = x % CHUNK_SIZE, y % CHUNK_SIZE, z % CHUNK_SIZE
  345.         lx = lx if lx >= 0 else lx + CHUNK_SIZE
  346.         ly = ly if ly >= 0 else ly + CHUNK_SIZE
  347.         lz = lz if lz >= 0 else lz + CHUNK_SIZE
  348.         chunk_coords = (cx, cy, cz)
  349.         if chunk_coords in self.chunks:
  350.             self.chunks[chunk_coords][lx][ly][lz] = block_type
  351.             self._chunks_to_save.add(chunk_coords)
  352.             if lx == 0: self._chunks_to_save.add((cx-1, cy, cz))
  353.             if lx == CHUNK_SIZE - 1: self._chunks_to_save.add((cx+1, cy, cz))
  354.             if ly == 0: self._chunks_to_save.add((cx, cy-1, cz))
  355.             if ly == CHUNK_SIZE - 1: self._chunks_to_save.add((cx, cy+1, cz))
  356.             if lz == 0: self._chunks_to_save.add((cx, cy, cz-1))
  357.             if lz == CHUNK_SIZE - 1: self._chunks_to_save.add((cx, cy, cz+1))
  358.         else:
  359.             print(f"Próba ustawienia bloku w niezaładowanym chunku: {chunk_coords}")
  360.  
  361.     def _generate_and_load_chunk(self, cx, cy, cz):
  362.         chunk_coords = (cx, cy, cz)
  363.         if chunk_coords in self.chunks:
  364.             return
  365.         chunk_file = os.path.join(self.region_dir, f"chunk_{cx}_{cy}_{cz}.dat")
  366.         chunk = None
  367.         if os.path.exists(chunk_file):
  368.             try:
  369.                 with open(chunk_file, "rb") as f:
  370.                     chunk = pickle.load(f)
  371.             except Exception as e:
  372.                 print(f"Błąd wczytywania chunka {chunk_coords}: {e}")
  373.         if chunk is None:
  374.             chunk = np.zeros((CHUNK_SIZE, CHUNK_SIZE, CHUNK_SIZE), dtype=int)
  375.             scale = 30.0
  376.             height_multiplier = 20
  377.             ground_level_base = 60
  378.             for x in range(CHUNK_SIZE):
  379.                 for z in range(CHUNK_SIZE):
  380.                     world_x = cx * CHUNK_SIZE + x
  381.                     world_z = cz * CHUNK_SIZE + z
  382.                     noise_val = noise.pnoise2(world_x / scale + self.seed*10, world_z / scale + self.seed*10,
  383.                                               octaves=6, persistence=0.5, lacunarity=2.0, repeatx=100000, repeaty=100000, base=self.seed*1000)
  384.                     height = int(noise_val * height_multiplier) + ground_level_base
  385.                     height = max(1, min(height, 255))
  386.                     for y in range(CHUNK_SIZE):
  387.                         world_y = cy * CHUNK_SIZE + y
  388.                         if world_y <= height:
  389.                             chunk[x][y][z] = 1 if world_y == height else 2 if world_y >= height - random.randint(2, 4) else 3
  390.             try:
  391.                 with open(chunk_file, "wb") as f:
  392.                     pickle.dump(chunk, f)
  393.             except Exception as e:
  394.                 print(f"Błąd zapisywania chunka {chunk_coords}: {e}")
  395.         self.chunks[chunk_coords] = chunk
  396.  
  397.     def _unload_chunk(self, chunk_coords):
  398.         if chunk_coords in self.chunks and chunk_coords in self._chunks_to_save:
  399.             try:
  400.                 chunk_file = os.path.join(self.region_dir, f"chunk_{chunk_coords[0]}_{chunk_coords[1]}_{chunk_coords[2]}.dat")
  401.                 with open(chunk_file, "wb") as f:
  402.                     pickle.dump(self.chunks[chunk_coords], f)
  403.                 self._chunks_to_save.discard(chunk_coords)
  404.             except Exception as e:
  405.                 print(f"Błąd zapisywania chunka {chunk_coords}: {e}")
  406.         if chunk_coords in self.chunks:
  407.             del self.chunks[chunk_coords]
  408.  
  409.     def update_loaded_chunks(self, player_pos):
  410.         px, py, pz = player_pos
  411.         player_chunk_x = int(np.floor(px / CHUNK_SIZE))
  412.         player_chunk_y = int(np.floor(py / CHUNK_SIZE))
  413.         player_chunk_z = int(np.floor(pz / CHUNK_SIZE))
  414.         min_cx = player_chunk_x - self.render_distance_chunks
  415.         max_cx = player_chunk_x + self.render_distance_chunks
  416.         min_cy = player_chunk_y - self.render_distance_chunks
  417.         max_cy = player_chunk_y + self.render_distance_chunks
  418.         min_cz = player_chunk_z - self.render_distance_chunks
  419.         max_cz = player_chunk_z + self.render_distance_chunks
  420.         chunks_to_load_or_keep = set((cx, cy, cz) for cx in range(min_cx, max_cx + 1) for cy in range(min_cy, max_cy + 1) for cz in range(min_cz, max_cz + 1))
  421.         for cc in [cc for cc in self.chunks if cc not in chunks_to_load_or_keep]:
  422.             self._unload_chunk(cc)
  423.         for cc in [cc for cc in chunks_to_load_or_keep if cc not in self.chunks]:
  424.             self._generate_and_load_chunk(*cc)
  425.  
  426.     def get_loaded_chunks_coords(self):
  427.         return list(self.chunks.keys())
  428.  
  429.     def save_all_chunks(self):
  430.         print(f"Zapisuję {len(self._chunks_to_save)} chunków...")
  431.         saved_count = 0
  432.         for chunk_coords in list(self._chunks_to_save):
  433.             if chunk_coords in self.chunks:
  434.                 try:
  435.                     chunk_file = os.path.join(self.region_dir, f"chunk_{chunk_coords[0]}_{chunk_coords[1]}_{chunk_coords[2]}.dat")
  436.                     with open(chunk_file, "wb") as f:
  437.                         pickle.dump(self.chunks[chunk_coords], f)
  438.                     self._chunks_to_save.discard(chunk_coords)
  439.                     saved_count += 1
  440.                 except Exception as e:
  441.                     print(f"Błąd zapisywania chunka {chunk_coords}: {e}")
  442.             else:
  443.                 self._chunks_to_save.discard(chunk_coords)
  444.         print(f"Zapisano {saved_count} chunków.")
  445.  
  446. def save_fixed_world(world, world_name, size):
  447.     if not world_name:
  448.         print("Brak nazwy świata!")
  449.         return False
  450.     safe_world_name_dir = "".join(c for c in world_name if c.isalnum() or c in (' ', '-', '_')).strip()
  451.     if not safe_world_name_dir:
  452.         safe_world_name_dir = "saved_world_" + str(int(time.time()))
  453.         print(f"Używam domyślnej nazwy: '{safe_world_name_dir}'")
  454.     world_dir = os.path.join("worlds", safe_world_name_dir)
  455.     try:
  456.         os.makedirs(world_dir, exist_ok=True)
  457.         with open(os.path.join(world_dir, "world.dat"), "wb") as f:
  458.             pickle.dump(world, f)
  459.         with open(os.path.join(world_dir, "settings.dat"), "wb") as f:
  460.             pickle.dump({"size": size, "name": world_name, "type": "fixed"}, f)
  461.         print(f"Świat '{world_name}' zapisany jako '{safe_world_name_dir}'!")
  462.         return True
  463.     except Exception as e:
  464.         print(f"Błąd zapisywania '{world_name}': {e}")
  465.         return False
  466.  
  467. def load_world(world_name_dir):
  468.     try:
  469.         world_dir = os.path.join("worlds", world_name_dir)
  470.         settings_file = os.path.join(world_dir, "settings.dat")
  471.         if not os.path.exists(settings_file):
  472.             print(f"Brak ustawień dla '{world_name_dir}'.")
  473.             return None, None, None, None
  474.         with open(settings_file, "rb") as f:
  475.             settings_data = pickle.load(f)
  476.         world_type = settings_data.get("type", "fixed")
  477.         world_name_display = settings_data.get("name", world_name_dir)
  478.         size = settings_data.get("size")
  479.         if world_type == "fixed":
  480.             if size is None:
  481.                 print("Brak rozmiaru dla świata fixed!")
  482.                 return None, world_name_display, None, False
  483.             world_file = os.path.join(world_dir, "world.dat")
  484.             if os.path.exists(world_file):
  485.                 with open(world_file, "rb") as f:
  486.                     world = pickle.load(f)
  487.                 if isinstance(world, np.ndarray) and world.shape == (size, size, size):
  488.                     return world, world_name_display, size, False
  489.                 else:
  490.                     print(f"Niepoprawne dane świata: {getattr(world, 'shape', 'N/A')}")
  491.                     return None, world_name_display, None, False
  492.             else:
  493.                 print(f"Brak pliku świata '{world_file}'.")
  494.                 return None, world_name_display, None, False
  495.         elif world_type == "infinite":
  496.             seed = settings_data.get("seed", 0)
  497.             print(f"Świat '{world_name_display}' wczytany (nieskończony, seed {seed})!")
  498.             return (seed, world_name_dir), world_name_display, None, True
  499.         else:
  500.             print(f"Nieznany typ świata '{world_type}'.")
  501.             return None, world_name_display, None, None
  502.     except Exception as e:
  503.         print(f"Błąd wczytywania '{world_name_dir}': {e}")
  504.         traceback.print_exc()
  505.         return None, None, None, None
  506.  
  507. from perlin_noise import PerlinNoise
  508.  
  509. def generate_fixed_world(size, seed=0):
  510.     print(f"Generowanie świata {size}x{size}x{size} z seedem {seed}...")
  511.     world = np.zeros((size, size, size), dtype=int)
  512.     np.random.seed(seed)
  513.     random.seed(seed)
  514.     scale = max(10, size / 8.0)
  515.     height_multiplier = max(5, size // 10)
  516.     ground_level_base = size // 4
  517.     noise_gen = PerlinNoise(octaves=4, seed=seed)
  518.     try:
  519.         for x in range(size):
  520.             for z in range(size):
  521.                 noise_val = noise_gen([x / scale, z / scale])
  522.                 height = int(noise_val * height_multiplier) + ground_level_base
  523.                 height = max(1, min(height, size - 1))
  524.                 for y in range(height + 1):
  525.                     world[x][y][z] = 1 if y == height else 2 if y >= height - random.randint(2, 4) else 3
  526.                 if x % (size // 4) == 0 and z == 0:
  527.                     print(f"Postęp generowania: x={x}/{size}")
  528.     except Exception as e:
  529.         print(f"Błąd w generowaniu świata: {e}")
  530.         traceback.print_exc()
  531.         return None
  532.     print("Generowanie zakończone.")
  533.     return world
  534.  
  535. def is_block_in_render_distance(x, y, z, player_pos, render_distance_blocks):
  536.     px, py, pz = player_pos
  537.     dist_sq = (x + 0.5 - px)**2 + (y + 0.5 - py)**2 + (z + 0.5 - pz)**2
  538.     return dist_sq <= render_distance_blocks**2
  539.  
  540. def draw_single_block(x, y, z, block_type, get_neighbor_block_func, resource_manager, block_texture_paths):
  541.     vertices = [
  542.         (0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0),
  543.         (0, 0, 1), (1, 0, 1), (1, 1, 1), (0, 1, 1)
  544.     ]
  545.     normals = [(0, 0, -1), (0, 0, 1), (0, 1, 0), (0, -1, 0), (1, 0, 0), (-1, 0, 0)]
  546.     faces = [(0, 1, 2, 3), (5, 4, 7, 6), (3, 2, 6, 7), (1, 0, 4, 5), (1, 5, 6, 2), (4, 0, 3, 7)]
  547.     texcoords = [(0, 0), (1, 0), (1, 1), (0, 1)]
  548.     neighbors_offset = [(0, 0, -1), (0, 0, 1), (0, 1, 0), (0, -1, 0), (1, 0, 0), (-1, 0, 0)]
  549.    
  550.     # Ładowanie tekstury przed renderowaniem
  551.     texture_path = block_texture_paths.get(block_type)
  552.     tex_id = resource_manager.load_texture_game_asset(texture_path) if texture_path else 0
  553.     print(f"Ładowanie tekstury dla bloku {block_type} ({texture_path}): tex_id={tex_id}")  # Debug
  554.  
  555.     # Ustawienie stanu OpenGL przed glBegin()
  556.     glPushAttrib(GL_ENABLE_BIT)  # Zachowaj stan
  557.     if tex_id > 0 and glIsTexture(tex_id):
  558.         glEnable(GL_TEXTURE_2D)
  559.         glBindTexture(GL_TEXTURE_2D, tex_id)
  560.         glColor3f(1.0, 1.0, 1.0)
  561.     else:
  562.         glDisable(GL_TEXTURE_2D)
  563.         glColor3f(0.2, 0.8, 0.2) if block_type == 1 else (0.5, 0.3, 0.1) if block_type == 2 else (0.5, 0.5, 0.5)
  564.  
  565.     # Renderowanie bloku
  566.     glBegin(GL_QUADS)
  567.     for i, face_indices in enumerate(faces):
  568.         if get_neighbor_block_func(x + neighbors_offset[i][0], y + neighbors_offset[i][1], z + neighbors_offset[i][2]) == BLOCK_AIR:
  569.             glNormal3fv(normals[i])
  570.             for j, vertex_index in enumerate(face_indices):
  571.                 if tex_id > 0 and glIsTexture(tex_id):
  572.                     glTexCoord2fv(texcoords[j])
  573.                 vx, vy, vz = vertices[vertex_index]
  574.                 glVertex3f(vx + x, vy + y, vz + z)
  575.     glEnd()
  576.  
  577.     # Przywrócenie stanu
  578.     glPopAttrib()  # Oczyść stan
  579.  
  580. def draw_world(world_data, player_pos, settings, resource_manager, block_texture_paths, is_infinite):
  581.     print(f"Przed draw_world - tryb macierzy: {glGetIntegerv(GL_MATRIX_MODE)}")
  582.     render_distance_blocks = settings.get("render_distance", DEFAULT_SETTINGS["render_distance"]) * CHUNK_SIZE
  583.     glPushAttrib(GL_ENABLE_BIT | GL_TEXTURE_BIT)
  584.     glEnable(GL_TEXTURE_2D)
  585.     glColor3f(1.0, 1.0, 1.0)
  586.     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)  # Czyszczenie bufora
  587.  
  588.     vertices = [(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0),
  589.                 (0, 0, 1), (1, 0, 1), (1, 1, 1), (0, 1, 1)]
  590.     normals = [(0, 0, -1), (0, 0, 1), (0, 1, 0), (0, -1, 0), (1, 0, 0), (-1, 0, 0)]
  591.     faces = [(0, 1, 2, 3), (5, 4, 7, 6), (3, 2, 6, 7), (1, 0, 4, 5), (1, 5, 6, 2), (4, 0, 3, 7)]
  592.     texcoords = [(0, 0), (1, 0), (1, 1), (0, 1)]
  593.     neighbors_offset = [(0, 0, -1), (0, 0, 1), (0, 1, 0), (0, -1, 0), (1, 0, 0), (-1, 0, 0)]
  594.  
  595.     if is_infinite:
  596.         chunk_manager = world_data
  597.         loaded_chunks = chunk_manager.get_loaded_chunks_coords()
  598.         print(f"Załadowane chunki: {len(loaded_chunks)}: {loaded_chunks}")
  599.         for chunk_coords in loaded_chunks:
  600.             cx, cy, cz = chunk_coords
  601.             chunk = chunk_manager.chunks.get(chunk_coords)
  602.             if chunk is None:
  603.                 print(f"Brak danych chunka: {chunk_coords}")
  604.                 continue
  605.             glPushAttrib(GL_ENABLE_BIT)
  606.             block_count = 0
  607.             for x in range(CHUNK_SIZE):
  608.                 for y in range(CHUNK_SIZE):
  609.                     for z in range(CHUNK_SIZE):
  610.                         block_type = chunk[x][y][z]
  611.                         if block_type != BLOCK_AIR:
  612.                             block_world_x = cx * CHUNK_SIZE + x
  613.                             block_world_y = cy * CHUNK_SIZE + y
  614.                             block_world_z = cz * CHUNK_SIZE + z
  615.                             if is_block_in_render_distance(block_world_x, block_world_y, block_world_z, player_pos, render_distance_blocks):
  616.                                 block_count += 1
  617.                                 texture_path = block_texture_paths.get(block_type)
  618.                                 tex_id = resource_manager.load_texture_game_asset(texture_path) if texture_path else 0
  619.                                 print(f"Renderowanie bloku {block_type} na ({block_world_x}, {block_world_y}, {block_world_z}), tex_id={tex_id}")
  620.                                 if tex_id > 0 and glIsTexture(tex_id):
  621.                                     glBindTexture(GL_TEXTURE_2D, tex_id)
  622.                                 else:
  623.                                     glBindTexture(GL_TEXTURE_2D, 0)
  624.                                     glColor3f(0.2, 0.8, 0.2) if block_type == 1 else (0.5, 0.3, 0.1)
  625.                                 glBegin(GL_QUADS)
  626.                                 for i, face_indices in enumerate(faces):
  627.                                     if chunk_manager.get_block(block_world_x + neighbors_offset[i][0], block_world_y + neighbors_offset[i][1], block_world_z + neighbors_offset[i][2]) == BLOCK_AIR:
  628.                                         glNormal3fv(normals[i])
  629.                                         for j, vertex_index in enumerate(face_indices):
  630.                                             if tex_id > 0:
  631.                                                 glTexCoord2fv(texcoords[j])
  632.                                             vx, vy, vz = vertices[vertex_index]
  633.                                             glVertex3f(vx + block_world_x, vy + block_world_y, vz + block_world_z)
  634.                                 glEnd()
  635.                                 glBindTexture(GL_TEXTURE_2D, 0)
  636.             print(f"Chunek {chunk_coords}: renderowano {block_count} bloków")
  637.             glPopAttrib()
  638.     else:
  639.         world = world_data
  640.         if world is None or not isinstance(world, np.ndarray) or world.ndim != 3:
  641.             print("Nieprawidłowe dane świata fixed.")
  642.             glPopAttrib()
  643.             return
  644.         size = len(world)
  645.         def get_neighbor_block_fixed(nx, ny, nz):
  646.             return world[nx][ny][nz] if 0 <= nx < size and 0 <= ny < size and 0 <= nz < size else BLOCK_AIR
  647.         actual_render_distance_blocks = min(render_distance_blocks, size)
  648.         glPushAttrib(GL_ENABLE_BIT)
  649.         for block_type in block_texture_paths.keys():
  650.             texture_path = block_texture_paths.get(block_type)
  651.             tex_id = resource_manager.load_texture_game_asset(texture_path) if texture_path else 0
  652.             if tex_id > 0:
  653.                 glBindTexture(GL_TEXTURE_2D, tex_id)
  654.             else:
  655.                 glBindTexture(GL_TEXTURE_2D, 0)
  656.                 glColor3f(0.2, 0.8, 0.2) if block_type == 1 else (0.5, 0.3, 0.1)
  657.             glBegin(GL_QUADS)
  658.             for x in range(size):
  659.                 for y in range(size):
  660.                     for z in range(size):
  661.                         if world[x][y][z] == block_type and is_block_in_render_distance(x, y, z, player_pos, actual_render_distance_blocks):
  662.                             for i, face_indices in enumerate(faces):
  663.                                 if get_neighbor_block_fixed(x + neighbors_offset[i][0], y + neighbors_offset[i][1], z + neighbors_offset[i][2]) == BLOCK_AIR:
  664.                                     glNormal3fv(normals[i])
  665.                                     for j, vertex_index in enumerate(face_indices):
  666.                                         if tex_id > 0:
  667.                                             glTexCoord2fv(texcoords[j])
  668.                                         vx, vy, vz = vertices[vertex_index]
  669.                                         glVertex3f(vx + x, vy + y, vz + z)
  670.             glEnd()
  671.             glBindTexture(GL_TEXTURE_2D, 0)
  672.         glPopAttrib()
  673.  
  674.     glBindTexture(GL_TEXTURE_2D, 0)
  675.     glPopAttrib()
  676.     print(f"Po draw_world - tryb macierzy: {glGetIntegerv(GL_MATRIX_MODE)}")
  677.  
  678. def draw_quad_2d(x, y, width, height, color=None, texture_id=None, alpha=1.0, texture_coords=((0,1),(1,1),(1,0),(0,0))):
  679.     if color:
  680.         glDisable(GL_TEXTURE_2D)
  681.         glColor4f(*color, alpha)
  682.     elif texture_id is not None and texture_id > 0:
  683.         glEnable(GL_TEXTURE_2D)
  684.         glBindTexture(GL_TEXTURE_2D, texture_id)
  685.         glColor4f(1.0, 1.0, 1.0, alpha)
  686.     else:
  687.         return
  688.     glBegin(GL_QUADS)
  689.     if texture_id is not None and texture_id > 0:
  690.         for i, (tx, ty) in enumerate(texture_coords):
  691.             glTexCoord2f(tx, ty)
  692.             glVertex2f(x + [0, width, width, 0][i], y + [0, 0, height, height][i])
  693.     else:
  694.         for i in range(4):
  695.             glVertex2f(x + [0, width, width, 0][i], y + [0, 0, height, height][i])
  696.     glEnd()
  697.  
  698.  
  699. def draw_line_loop_2d(x, y, width, height, color, line_width=1.0, alpha=1.0):
  700.     glDisable(GL_TEXTURE_2D)
  701.     glColor4f(*color, alpha)
  702.     glLineWidth(line_width)
  703.     glBegin(GL_LINE_LOOP)
  704.     for vx, vy in [(x, y), (x + width, y), (x + width, y + height), (x, y + height)]:
  705.         glVertex2f(vx, vy)
  706.     glEnd()
  707.  
  708. def draw_lines_2d(points, color, line_width=1.0, alpha=1.0):
  709.     glDisable(GL_TEXTURE_2D)
  710.     glColor4f(*color, alpha)
  711.     glLineWidth(line_width)
  712.     glBegin(GL_LINES)
  713.     for point in points:
  714.         glVertex2f(*point)
  715.     glEnd()
  716.  
  717. def draw_text_2d(x, y, text, font, color=(255, 255, 255), alpha=1.0):
  718.     text_tex, text_width, text_height = load_text_texture(text, font, color)
  719.     if text_tex > 0:
  720.         # Poprawiamy kolejność współrzędnych tekstury, żeby tekst nie był odwrócony
  721.         text_coords = ((0, 1), (1, 1), (1, 0), (0, 0))  # Poprawione współrzędne UV
  722.         draw_quad_2d(x, y, text_width, text_height, texture_id=text_tex, alpha=alpha, texture_coords=text_coords)
  723.         glDeleteTextures([text_tex])
  724.     return text_width, text_height
  725.  
  726.  
  727. def setup_2d_overlay():
  728.     glDisable(GL_DEPTH_TEST)
  729.     glDisable(GL_CULL_FACE)
  730.     glMatrixMode(GL_PROJECTION)
  731.     glPushMatrix()
  732.     glLoadIdentity()
  733.     glOrtho(0, display[0], display[1], 0, -1, 1)
  734.     glMatrixMode(GL_MODELVIEW)
  735.     glPushMatrix()
  736.     glLoadIdentity()
  737.     glEnable(GL_BLEND)
  738.     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
  739.     glDisable(GL_TEXTURE_2D)
  740.  
  741. def restore_3d_projection():
  742.     glMatrixMode(GL_MODELVIEW)
  743.     glPopMatrix()
  744.     glMatrixMode(GL_PROJECTION)
  745.     glPopMatrix()
  746.     glEnable(GL_DEPTH_TEST)
  747.     glEnable(GL_CULL_FACE)
  748.  
  749. def draw_background(resource_manager, alpha=1.0):
  750.     menu_background_path = os.path.join("textures", "menu", "background.png")
  751.    
  752.     # Sprawdzamy, czy plik istnieje przed próbą załadowania tekstury
  753.     if not os.path.isfile(menu_background_path):
  754.         print(f"Plik UI nie istnieje: {menu_background_path}")
  755.         # Możesz zainicjować jakąś domyślną teksturę lub inne tło, jeśli plik nie istnieje
  756.         menu_background_tex_id = None
  757.     else:
  758.         menu_background_tex_id = resource_manager.load_texture_ui_asset(menu_background_path)
  759.    
  760.     print(f"Renderowanie tła, tex_id={menu_background_tex_id}")
  761.    
  762.     # Jeśli tekstura się nie załadowała (tex_id == 0 lub None), używamy koloru
  763.     if not menu_background_tex_id:
  764.         print(f"Nie udało się załadować {menu_background_path}, używam koloru.")
  765.    
  766.     # Przekazujemy None lub ID tekstury w zależności od tego, czy tekstura została załadowana
  767.     draw_quad_2d(0, 0, display[0], display[1], texture_id=menu_background_tex_id if menu_background_tex_id else None, color=(0.2, 0.2, 0.2), alpha=alpha)
  768.  
  769.  
  770. def draw_button(x, y, width, height, text, font, mouse_pos, clicked, animation_alpha=1.0):
  771.     is_hovered = x < mouse_pos[0] < x + width and y < mouse_pos[1] < y + height
  772.     btn_color = (0.3, 0.3, 0.3) if is_hovered else (0.2, 0.2, 0.2)
  773.     draw_quad_2d(x + 5, y + 5, width, height, color=(0.1, 0.1, 0.1), alpha=0.5 * animation_alpha)
  774.     draw_quad_2d(x, y, width, height, color=btn_color, alpha=animation_alpha)
  775.     draw_line_loop_2d(x, y, width, height, color=(0.5, 0.5, 0.5), alpha=animation_alpha)
  776.     text_width = font.size(text)[0]
  777.     text_x = x + (width - text_width) // 2
  778.     text_y = y + (height - font_height) // 2
  779.     draw_text_2d(text_x, text_y, text, font, alpha=animation_alpha)
  780.     return is_hovered and clicked
  781.    
  782. # Rysuje suwak do ustawień (np. czułość myszy, FOV)
  783. def draw_slider(x, y, width, height, value, min_val, max_val, font, mouse_pos, mouse_clicked, is_dragging, animation_alpha=1.0):
  784.     # Tło suwaka
  785.     draw_quad_2d(x, y, width, height, color=(0.2, 0.2, 0.2), alpha=animation_alpha)
  786.     draw_line_loop_2d(x, y, width, height, color=(0.5, 0.5, 0.5), alpha=animation_alpha)
  787.    
  788.     # Wskaźnik suwaka
  789.     slider_width = 10
  790.     norm_value = (value - min_val) / (max_val - min_val)  # Normalizacja wartości do 0-1
  791.     slider_x = x + norm_value * (width - slider_width)
  792.     draw_quad_2d(slider_x, y, slider_width, height, color=(0.4, 0.4, 0.4), alpha=animation_alpha)
  793.    
  794.     # Wyświetl wartość
  795.     value_text = f"{value:.2f}" if isinstance(value, float) else str(value)
  796.     text_width = font.size(value_text)[0]
  797.     draw_text_2d(x + (width - text_width) // 2, y + (height - font.get_linesize()) // 2, value_text, font, alpha=animation_alpha)
  798.    
  799.     # Obsługa myszy
  800.     is_hovered = x < mouse_pos[0] < x + width and y < mouse_pos[1] < y + height
  801.     if is_hovered and mouse_clicked:
  802.         is_dragging = True
  803.     if not pygame.mouse.get_pressed()[0]:
  804.         is_dragging = False
  805.     if is_dragging:
  806.         norm_pos = (mouse_pos[0] - x) / width
  807.         norm_pos = max(0, min(1, norm_pos))  # Ogranicz do 0-1
  808.         value = min_val + norm_pos * (max_val - min_val)
  809.         if isinstance(min_val, int):
  810.             value = int(round(value))
  811.    
  812.     return value, is_dragging
  813.  
  814. # Rysuje pole tekstowe (np. do nazwy świata, seeda)
  815. def draw_text_field(x, y, width, height, text, font, is_active, animation_alpha=1.0):
  816.     # Tło pola
  817.     bg_color = (0.3, 0.3, 0.3) if is_active else (0.2, 0.2, 0.2)
  818.     draw_quad_2d(x, y, width, height, color=bg_color, alpha=animation_alpha)
  819.     draw_line_loop_2d(x, y, width, height, color=(0.5, 0.5, 0.5), alpha=animation_alpha)
  820.    
  821.     # Tekst
  822.     display_text = text if text else "Wpisz..." if not is_active else ""
  823.     text_x = x + 5
  824.     text_y = y + (height - font.get_linesize()) // 2
  825.     draw_text_2d(text_x, text_y, display_text, font, color=(255, 255, 255) if text else (150, 150, 150), alpha=animation_alpha)
  826.  
  827. # Rysuje dropdown (np. do wyboru rozmiaru świata)
  828. def draw_dropdown(x, y, width, height, options, selected_option, font, mouse_pos, mouse_clicked, is_open, animation_alpha=1.0):
  829.     list_spacing = 5
  830.     # Główne pole dropdown
  831.     draw_quad_2d(x, y, width, height, color=(0.2, 0.2, 0.2), alpha=animation_alpha)
  832.     draw_line_loop_2d(x, y, width, height, color=(0.5, 0.5, 0.5), alpha=animation_alpha)
  833.     text_x = x + 5
  834.     text_y = y + (height - font.get_linesize()) // 2
  835.     draw_text_2d(text_x, text_y, selected_option, font, alpha=animation_alpha)
  836.    
  837.     # Strzałka
  838.     arrow_size = 10
  839.     draw_quad_2d(x + width - arrow_size - 5, y + (height - arrow_size) // 2, arrow_size, arrow_size, color=(0.4, 0.4, 0.4), alpha=animation_alpha)
  840.    
  841.     was_clicked = False
  842.     is_hovered = x < mouse_pos[0] < x + width and y < mouse_pos[1] < y + height
  843.     if is_hovered and mouse_clicked:
  844.         is_open = not is_open
  845.         was_clicked = True
  846.    
  847.     # Lista opcji
  848.     if is_open:
  849.         for i, option in enumerate(options):
  850.             item_y = y + height + (i * (height + list_spacing))
  851.             item_color = (0.3, 0.3, 0.3) if option != selected_option else (0.4, 0.4, 0.4)
  852.             is_hovered = x < mouse_pos[0] < x + width and item_y < mouse_pos[1] < item_y + height
  853.             draw_quad_2d(x, item_y, width, height, color=item_color, alpha=animation_alpha)
  854.             if is_hovered:
  855.                 draw_line_loop_2d(x, item_y, width, height, color=(0.6, 0.6, 0.6), alpha=animation_alpha)
  856.             draw_text_2d(text_x, item_y + (height - font.get_linesize()) // 2, option, font, alpha=animation_alpha)
  857.             if is_hovered and mouse_clicked:
  858.                 selected_option = option
  859.                 is_open = False
  860.                 was_clicked = True
  861.    
  862.     return selected_option, is_open, was_clicked
  863.  
  864. # Rysuje checkbox (np. do odwracania osi myszy)
  865. def draw_checkbox(x, y, width, height, label, is_checked, font, mouse_pos, mouse_clicked, animation_alpha=1.0):
  866.     # Pole checkbox
  867.     draw_quad_2d(x, y, width, height, color=(0.2, 0.2, 0.2), alpha=animation_alpha)
  868.     draw_line_loop_2d(x, y, width, height, color=(0.5, 0.5, 0.5), alpha=animation_alpha)
  869.     if is_checked:
  870.         draw_quad_2d(x + 5, y + 5, width - 10, height - 10, color=(0.4, 0.4, 0.4), alpha=animation_alpha)
  871.    
  872.     # Etykieta
  873.     text_x = x + width + 10
  874.     text_y = y + (height - font.get_linesize()) // 2
  875.     draw_text_2d(text_x, text_y, label, font, alpha=animation_alpha)
  876.    
  877.     # Obsługa kliknięcia
  878.     is_hovered = x < mouse_pos[0] < x + width and y < mouse_pos[1] < y + height
  879.     if is_hovered and mouse_clicked:
  880.         is_checked = not is_checked
  881.    
  882.     return is_checked
  883.  
  884. # Rysuje okno potwierdzenia (np. do usuwania świata)
  885. def draw_confirm_window(x, y, width, height, animation_alpha=1.0):
  886.     draw_quad_2d(x, y, width, height, color=(0.2, 0.2, 0.2), alpha=animation_alpha)
  887.     draw_line_loop_2d(x, y, width, height, color=(0.5, 0.5, 0.5), line_width=2, alpha=animation_alpha)
  888.  
  889. def show_main_menu(resource_manager):
  890.     global display, settings, font_small, font_medium, font_large, font_height
  891.     pygame.mouse.set_visible(True)
  892.     pygame.event.set_grab(False)
  893.     print("Menu główne: kursor włączony")
  894.     clock = pygame.time.Clock()
  895.     menu_state = "main"
  896.     selected_world = None
  897.     create_world_name_text = ""
  898.     create_world_seed_text = ""
  899.     selected_world_size_key = list(WORLD_SIZES.keys())[0]
  900.     resource_pack_selected_pack = settings.get("resource_pack", "Brak")
  901.     active_text_field = None
  902.     dropdown_open_state = {}
  903.     menu_running = True
  904.     menu_alpha = 0.0
  905.     fade_in = True
  906.     while menu_running:
  907.         mouse_pos = pygame.mouse.get_pos()
  908.         mouse_clicked = False
  909.         for event in pygame.event.get():
  910.             if event.type == QUIT:
  911.                 print("Wyjście z aplikacji.")
  912.                 return None
  913.             if event.type == VIDEORESIZE:
  914.                 display = (event.w, event.h)
  915.                 if display[0] > 0 and display[1] > 0:
  916.                     pygame.display.set_mode(display, DOUBLEBUF | OPENGL | RESIZABLE)
  917.                 else:
  918.                     print(f"Niepoprawny rozmiar okna: {display}")
  919.             if event.type == MOUSEBUTTONDOWN and event.button == 1:
  920.                 mouse_clicked = True
  921.             if event.type == KEYDOWN:
  922.                 if event.key == K_ESCAPE:
  923.                     if menu_state == "main":
  924.                         print("Wyjście z menu (ESC).")
  925.                         return None
  926.                     elif menu_state in ("singleplayer", "settings"):
  927.                         menu_state = "main"
  928.                         dropdown_open_state = {}
  929.                         active_text_field = None
  930.                     elif menu_state == "create_world":
  931.                         menu_state = "singleplayer"
  932.                         dropdown_open_state = {}
  933.                         active_text_field = None
  934.                 elif active_text_field and menu_state == "create_world":
  935.                     if event.key == K_BACKSPACE:
  936.                         if active_text_field == "name":
  937.                             create_world_name_text = create_world_name_text[:-1]
  938.                         elif active_text_field == "seed":
  939.                             create_world_seed_text = create_world_seed_text[:-1]
  940.                     elif event.key == K_RETURN:
  941.                         active_text_field = None
  942.                     elif event.unicode.isprintable():
  943.                         if active_text_field == "name":
  944.                             create_world_name_text += event.unicode
  945.                         elif active_text_field == "seed":
  946.                             if event.unicode.isdigit() or (event.unicode == '-' and not create_world_seed_text):
  947.                                 create_world_seed_text += event.unicode
  948.         if fade_in:
  949.             menu_alpha = min(menu_alpha + 0.05, 1.0)
  950.             if menu_alpha >= 1.0:
  951.                 fade_in = False
  952.         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
  953.         draw_background(resource_manager, alpha=menu_alpha)
  954.         setup_2d_overlay()
  955.         center_x = display[0] // 2
  956.         center_y = display[1] // 2
  957.         button_width = 200
  958.         button_height = 50
  959.         button_spacing = 15
  960.         if menu_state == "main":
  961.             title_text = "Minecraft Clone"
  962.             title_width = font_large.size(title_text)[0]
  963.             draw_text_2d(center_x - title_width // 2, center_y - 150, title_text, font_large, alpha=menu_alpha)
  964.             if draw_button(center_x - button_width // 2, center_y - button_height - button_spacing, button_width, button_height, "Gra jednoosobowa", font_medium, mouse_pos, mouse_clicked, animation_alpha=menu_alpha):
  965.                 menu_state = "singleplayer"
  966.                 dropdown_open_state = {}
  967.                 active_text_field = None
  968.             if draw_button(center_x - button_width // 2, center_y, button_width, button_height, "Ustawienia", font_medium, mouse_pos, mouse_clicked, animation_alpha=menu_alpha):
  969.                 menu_state = "settings"
  970.                 dropdown_open_state = {}
  971.                 active_text_field = None
  972.             if draw_button(center_x - button_width // 2, center_y + button_height + button_spacing, button_width, button_height, "Wyjście", font_medium, mouse_pos, mouse_clicked, animation_alpha=menu_alpha):
  973.                 print("Wyjście z menu.")
  974.                 return None
  975.         elif menu_state == "singleplayer":
  976.             title_text = "Wybierz świat"
  977.             title_width = font_large.size(title_text)[0]
  978.             draw_text_2d(center_x - title_width // 2, center_y - 200, title_text, font_large, alpha=menu_alpha)
  979.             list_x = center_x - button_width // 2
  980.             list_y = center_y - 100
  981.             list_width = button_width + 100
  982.             list_item_height = 40
  983.             list_spacing = 5
  984.             existing_worlds = []
  985.             if os.path.exists("worlds"):
  986.                 existing_worlds = [d for d in os.listdir("worlds") if os.path.isdir(os.path.join("worlds", d))]
  987.             max_visible_worlds = 6
  988.             actual_display_worlds = min(max_visible_worlds, len(existing_worlds))
  989.             if existing_worlds:
  990.                 for i in range(actual_display_worlds):
  991.                     world_dir_name = existing_worlds[i]
  992.                     _, world_name_display, _, _ = load_world(world_dir_name)
  993.                     display_name = world_name_display or world_dir_name
  994.                     item_y = list_y + i * (list_item_height + list_spacing)
  995.                     item_color = (0.3, 0.3, 0.3) if world_dir_name != selected_world else (0.4, 0.4, 0.4)
  996.                     is_hovered = list_x < mouse_pos[0] < list_x + list_width and item_y < mouse_pos[1] < item_y + list_item_height
  997.                     draw_quad_2d(list_x, item_y, list_width, list_item_height, color=item_color, alpha=menu_alpha)
  998.                     if is_hovered:
  999.                         draw_line_loop_2d(list_x, item_y, list_width, list_item_height, color=(0.6, 0.6, 0.6), alpha=menu_alpha)
  1000.                     text_x = list_x + 10
  1001.                     text_y = item_y + (list_item_height - font_small.get_linesize()) // 2
  1002.                     draw_text_2d(text_x, text_y, display_name, font_small, alpha=menu_alpha)
  1003.                     if is_hovered and mouse_clicked:
  1004.                         selected_world = world_dir_name
  1005.             else:
  1006.                 empty_list_y = list_y
  1007.                 draw_text_2d(list_x, empty_list_y, "Brak zapisanych światów", font_small, color=(150,150,150), alpha=menu_alpha)
  1008.             button_y_start = list_y + max_visible_worlds * (list_item_height + list_spacing) + 20
  1009.             if draw_button(center_x - button_width // 2, button_y_start, button_width, button_height, "Graj", font_medium, mouse_pos, mouse_clicked and selected_world is not None, animation_alpha=menu_alpha):
  1010.                 world_data, world_name_display, size, is_infinite = load_world(selected_world)
  1011.                 if world_data is not None:
  1012.                     menu_running = False
  1013.                     dropdown_open_state = {}
  1014.                     active_text_field = None
  1015.                     return world_data, world_name_display, size, is_infinite
  1016.             if draw_button(center_x - button_width // 2, button_y_start + button_height + button_spacing, button_width, button_height, "Utwórz nowy świat", font_medium, mouse_pos, mouse_clicked, animation_alpha=menu_alpha):
  1017.                 menu_state = "create_world"
  1018.                 create_world_name_text = ""
  1019.                 create_world_seed_text = ""
  1020.                 selected_world_size_key = list(WORLD_SIZES.keys())[0]
  1021.                 dropdown_open_state = {}
  1022.                 active_text_field = None
  1023.             if draw_button(center_x - button_width // 2, button_y_start + 2*(button_height + button_spacing), button_width, button_height, "Powrót", font_medium, mouse_pos, mouse_clicked, animation_alpha=menu_alpha):
  1024.                 menu_state = "main"
  1025.                 dropdown_open_state = {}
  1026.                 active_text_field = None
  1027.         elif menu_state == "create_world":
  1028.             title_text = "Utwórz nowy świat"
  1029.             title_width = font_large.size(title_text)[0]
  1030.             draw_text_2d(center_x - title_width // 2, center_y - 200, title_text, font_large, alpha=menu_alpha)
  1031.             field_width = 300
  1032.             field_height = 40
  1033.             field_spacing = 15
  1034.             field_x = center_x - field_width // 2
  1035.             label_height = font_medium.get_linesize()
  1036.             name_label_y = center_y - 100 - label_height - 5
  1037.             draw_text_2d(field_x, name_label_y, "Nazwa świata:", font_medium, color=(200,200,200), alpha=menu_alpha)
  1038.             name_field_y = center_y - 100
  1039.             draw_text_field(field_x, name_field_y, field_width, field_height, create_world_name_text, font_medium, active_text_field == "name")
  1040.             name_field_rect = (field_x, name_field_y, field_width, field_height)
  1041.             if mouse_clicked and name_field_rect[0] < mouse_pos[0] < name_field_rect[0] + name_field_rect[2] and name_field_rect[1] < mouse_pos[1] < name_field_rect[1] + name_field_rect[3]:
  1042.                 active_text_field = "name"
  1043.             size_label_y = center_y - 30 - label_height - 5
  1044.             draw_text_2d(field_x, size_label_y, "Rozmiar świata:", font_medium, color=(200,200,200), alpha=menu_alpha)
  1045.             size_dropdown_y = center_y - 30
  1046.             dropdown_width = field_width
  1047.             dropdown_height = field_height
  1048.             selected_world_size_key, size_dropdown_is_open, size_dropdown_clicked = draw_dropdown(
  1049.                 field_x, size_dropdown_y, dropdown_width, dropdown_height, list(WORLD_SIZES.keys()),
  1050.                 selected_world_size_key, font_medium, mouse_pos, mouse_clicked, dropdown_open_state.get("size_dropdown", False), animation_alpha=menu_alpha
  1051.             )
  1052.             dropdown_open_state["size_dropdown"] = size_dropdown_is_open
  1053.             dropdown_vertical_offset = (len(WORLD_SIZES) * (dropdown_height + list_spacing) if dropdown_open_state.get("size_dropdown", False) else 0)
  1054.             seed_label_y = size_dropdown_y + dropdown_height + field_spacing + dropdown_vertical_offset - label_height - 5
  1055.             draw_text_2d(field_x, seed_label_y, "Seed (puste = losowy):", font_medium, color=(200,200,200), alpha=menu_alpha)
  1056.             seed_field_y = size_dropdown_y + dropdown_height + field_spacing + dropdown_vertical_offset
  1057.             draw_text_field(field_x, seed_field_y, field_width, field_height, create_world_seed_text, font_medium, active_text_field == "seed")
  1058.             seed_field_rect = (field_x, seed_field_y, field_width, field_height)
  1059.             if mouse_clicked and seed_field_rect[0] < mouse_pos[0] < seed_field_rect[0] + seed_field_rect[2] and seed_field_rect[1] < mouse_pos[1] < seed_field_rect[1] + seed_field_rect[3]:
  1060.                 active_text_field = "seed"
  1061.             if mouse_clicked:
  1062.                 clicked_on_field = (name_field_rect[0] < mouse_pos[0] < name_field_rect[0] + name_field_rect[2] and name_field_rect[1] < mouse_pos[1] < name_field_rect[1] + name_field_rect[3]) or \
  1063.                                   (seed_field_rect[0] < mouse_pos[0] < seed_field_rect[0] + seed_field_rect[2] and seed_field_rect[1] < mouse_pos[1] < seed_field_rect[1] + seed_field_rect[3])
  1064.                 if not clicked_on_field and not size_dropdown_clicked:
  1065.                     active_text_field = None
  1066.             button_y_start = seed_field_y + field_height + 30
  1067.             create_button_enabled = bool(create_world_name_text.strip())
  1068.             if draw_button(center_x - button_width // 2, button_y_start, button_width, button_height, "Utwórz świat", font_medium, mouse_pos, mouse_clicked if create_button_enabled else False, animation_alpha=menu_alpha):
  1069.                 print(f"Utwórz świat '{create_world_name_text}'")
  1070.                 world_size_val = WORLD_SIZES[selected_world_size_key]
  1071.                 try:
  1072.                     seed_input_str = create_world_seed_text.strip()
  1073.                     world_seed = random.randint(1, 1000000) if seed_input_str == '-' or not seed_input_str else int(seed_input_str)
  1074.                     create_world_seed_text = str(world_seed)
  1075.                 except ValueError:
  1076.                     print("Niepoprawny seed! Losowy.")
  1077.                     world_seed = random.randint(1, 1000000)
  1078.                     create_world_seed_text = str(world_seed)
  1079.                 world_dir_name = create_world_name_text.strip().replace(' ', '_')
  1080.                 world_dir_name = "".join(c for c in world_dir_name if c.isalnum() or c in ('-', '_')).strip() or "world_" + str(int(time.time()))
  1081.                 # Sprawdzenie istniejących światów
  1082.                 existing_worlds_dirs = []
  1083.                 if os.path.exists("worlds"):
  1084.                     existing_worlds_dirs = [d for d in os.listdir("worlds") if os.path.isdir(os.path.join("worlds", d))]
  1085.                 i = 1
  1086.                 original_dir_name = world_dir_name
  1087.                 while world_dir_name in existing_worlds_dirs:
  1088.                     world_dir_name = f"{original_dir_name}_{i}"
  1089.                     i += 1
  1090.                 if world_size_val is None:
  1091.                     world_dir_path = os.path.join("worlds", world_dir_name)
  1092.                     try:
  1093.                         os.makedirs(world_dir_path, exist_ok=True)
  1094.                         with open(os.path.join(world_dir_path, "settings.dat"), "wb") as f:
  1095.                             pickle.dump({"seed": world_seed, "name": create_world_name_text.strip(), "type": "infinite"}, f)
  1096.                         print(f"Ustawienia świata zapisane do '{world_dir_path}/settings.dat'")
  1097.                         menu_running = False
  1098.                         dropdown_open_state = {}
  1099.                         active_text_field = None
  1100.                         return (world_seed, world_dir_name), create_world_name_text.strip(), None, True
  1101.                     except Exception as e:
  1102.                         print(f"Błąd zapisywania ustawień: {e}")
  1103.                 else:
  1104.                     world_data = generate_fixed_world(world_size_val, world_seed)
  1105.                     if save_fixed_world(world_data, create_world_name_text.strip(), world_size_val):
  1106.                         menu_running = False
  1107.                         dropdown_open_state = {}
  1108.                         active_text_field = None
  1109.                         return world_data, create_world_name_text.strip(), world_size_val, False
  1110.             if draw_button(center_x - button_width // 2, button_y_start + button_height + field_spacing, button_width, button_height, "Powrót", font_medium, mouse_pos, mouse_clicked, animation_alpha=menu_alpha):
  1111.                 menu_state = "singleplayer"
  1112.                 active_text_field = None
  1113.                 dropdown_open_state = {}
  1114.         elif menu_state == "settings":
  1115.             title_text = "Ustawienia"
  1116.             title_width = font_large.size(title_text)[0]
  1117.             draw_text_2d(center_x - title_width // 2, center_y - 200, title_text, font_large, alpha=menu_alpha)
  1118.             settings_menu_state = settings_menu_state if 'settings_menu_state' in locals() else "main_settings"
  1119.             if settings_menu_state == "main_settings":
  1120.                 setting_width = 300
  1121.                 setting_height = 40
  1122.                 setting_spacing = 15
  1123.                 setting_x = center_x - setting_width // 2
  1124.                 setting_y_start = center_y - 100
  1125.                 label_height = font_medium.get_linesize()
  1126.                 label_text = "Czułość myszy:"
  1127.                 label_y = setting_y_start - label_height - 5
  1128.                 draw_text_2d(setting_x, label_y, label_text, font_medium, color=(200,200,200), alpha=menu_alpha)
  1129.                 settings["mouse_sensitivity"], mouse_sensitivity_dragging = draw_slider(
  1130.                     setting_x, setting_y_start, setting_width, setting_height, settings.get("mouse_sensitivity", DEFAULT_SETTINGS["mouse_sensitivity"]),
  1131.                     0.01, 1.0, font_medium, mouse_pos, mouse_clicked, mouse_sensitivity_dragging if 'mouse_sensitivity_dragging' in locals() else False, animation_alpha=menu_alpha
  1132.                 )
  1133.                 if mouse_sensitivity_dragging or (not mouse_sensitivity_dragging and pygame.mouse.get_pressed()[0] and
  1134.                     setting_x < mouse_pos[0] < setting_x + setting_width and setting_y_start < mouse_pos[1] < setting_y_start + setting_height):
  1135.                     mouse_clicked = False
  1136.                 fov_y = setting_y_start + setting_height + setting_spacing
  1137.                 label_text = "FOV:"
  1138.                 label_y = fov_y - label_height - 5
  1139.                 draw_text_2d(setting_x, label_y, label_text, font_medium, color=(200,200,200), alpha=menu_alpha)
  1140.                 settings["fov"], fov_dragging = draw_slider(
  1141.                     setting_x, fov_y, setting_width, setting_height, settings.get("fov", DEFAULT_SETTINGS["fov"]),
  1142.                     30.0, 110.0, font_medium, mouse_pos, mouse_clicked, fov_dragging if 'fov_dragging' in locals() else False, animation_alpha=menu_alpha
  1143.                 )
  1144.                 if fov_dragging or (not fov_dragging and pygame.mouse.get_pressed()[0] and
  1145.                     setting_x < mouse_pos[0] < setting_x + setting_width and fov_y < mouse_pos[1] < fov_y + setting_height):
  1146.                     mouse_clicked = False
  1147.                 render_dist_y = fov_y + setting_height + setting_spacing
  1148.                 label_text = "Odległość renderowania (chunks):"
  1149.                 label_y = render_dist_y - label_height - 5
  1150.                 draw_text_2d(setting_x, label_y, label_text, font_medium, color=(200,200,200), alpha=menu_alpha)
  1151.                 current_render_dist = settings.get("render_distance", DEFAULT_SETTINGS["render_distance"])
  1152.                 rendered_val, render_distance_dragging = draw_slider(
  1153.                     setting_x, render_dist_y, setting_width, setting_height, current_render_dist,
  1154.                     2, 32, font_medium, mouse_pos, mouse_clicked, render_distance_dragging if 'render_distance_dragging' in locals() else False, animation_alpha=menu_alpha
  1155.                 )
  1156.                 settings["render_distance"] = int(round(rendered_val))
  1157.                 if render_distance_dragging or (not render_distance_dragging and pygame.mouse.get_pressed()[0] and
  1158.                     setting_x < mouse_pos[0] < setting_x + setting_width and render_dist_y < mouse_pos[1] < render_dist_y + setting_height):
  1159.                     mouse_clicked = False
  1160.                 checkbox_y_start = render_dist_y + setting_height + setting_spacing * 2
  1161.                 checkbox_width = 30
  1162.                 checkbox_height = 30
  1163.                 settings["invert_mouse_x"] = draw_checkbox(
  1164.                     setting_x, checkbox_y_start, checkbox_width, checkbox_height, "Odwróć oś X myszy",
  1165.                     settings.get("invert_mouse_x", DEFAULT_SETTINGS["invert_mouse_x"]), font_medium, mouse_pos, mouse_clicked, animation_alpha=menu_alpha
  1166.                 )
  1167.                 invert_y_y = checkbox_y_start + checkbox_height + setting_spacing
  1168.                 settings["invert_mouse_y"] = draw_checkbox(
  1169.                     setting_x, invert_y_y, checkbox_width, checkbox_height, "Odwróć oś Y myszy",
  1170.                     settings.get("invert_mouse_y", DEFAULT_SETTINGS["invert_mouse_y"]), font_medium, mouse_pos, mouse_clicked, animation_alpha=menu_alpha
  1171.                 )
  1172.                 button_y_start = invert_y_y + checkbox_height + setting_spacing * 2
  1173.                 if draw_button(center_x - button_width // 2, button_y_start, button_width, button_height, "Paczki zasobów", font_medium, mouse_pos, mouse_clicked, animation_alpha=menu_alpha):
  1174.                     settings_menu_state = "resource_packs"
  1175.                     resource_manager.scan_resource_packs()
  1176.                     dropdown_open_state = {}
  1177.                 if draw_button(center_x - button_width // 2, button_y_start + button_height + button_spacing, button_width, button_height, "Gotowe", font_medium, mouse_pos, mouse_clicked, animation_alpha=menu_alpha):
  1178.                     try:
  1179.                         with open("settings.dat", "wb") as f:
  1180.                             pickle.dump(settings, f)
  1181.                     except Exception as e:
  1182.                         print(f"Błąd zapisywania ustawień: {e}")
  1183.                     menu_state = "main"
  1184.                     dropdown_open_state = {}
  1185.             elif settings_menu_state == "resource_packs":
  1186.                 title_text = "Paczki zasobów"
  1187.                 title_width = font_large.size(title_text)[0]
  1188.                 draw_text_2d(center_x - title_width // 2, center_y - 200, title_text, font_large, alpha=menu_alpha)
  1189.                 list_x = center_x - button_width // 2
  1190.                 list_y = center_y - 100
  1191.                 list_width = button_width + 100
  1192.                 list_item_height = 40
  1193.                 list_spacing = 5
  1194.                 draw_text_2d(list_x, list_y - 25, "Dostępne paczki:", font_small, color=(200,200,200), alpha=menu_alpha)
  1195.                 max_visible_list_items = 6
  1196.                 actual_display_packs = min(max_visible_list_items, len(resource_manager.available_packs))
  1197.                 if resource_manager.available_packs:
  1198.                     for i in range(actual_display_packs):
  1199.                         pack_name = resource_manager.available_packs[i]
  1200.                         item_y = list_y + i * (list_item_height + list_spacing)
  1201.                         item_color = (0.3, 0.3, 0.3) if pack_name != resource_pack_selected_pack else (0.4, 0.4, 0.4)
  1202.                         is_hovered = list_x < mouse_pos[0] < list_x + list_width and item_y < mouse_pos[1] < item_y + list_item_height
  1203.                         draw_quad_2d(list_x, item_y, list_width, list_item_height, color=item_color, alpha=menu_alpha)
  1204.                         if is_hovered:
  1205.                             draw_line_loop_2d(list_x, item_y, list_width, list_item_height, color=(0.6, 0.6, 0.6), alpha=menu_alpha)
  1206.                         text_x = list_x + 10
  1207.                         text_y = item_y + (list_item_height - font_small.get_linesize()) // 2
  1208.                         draw_text_2d(text_x, text_y, pack_name, font_small, alpha=menu_alpha)
  1209.                         if is_hovered and mouse_clicked:
  1210.                             resource_pack_selected_pack = pack_name
  1211.                 else:
  1212.                     draw_text_2d(list_x, list_y, "Brak innych paczek zasobów", font_small, color=(150,150,150), alpha=menu_alpha)
  1213.                 button_y_start = list_y + max_visible_list_items * (list_item_height + list_spacing) + 20
  1214.                 if draw_button(center_x - button_width // 2, button_y_start, button_width, button_height, "Aktywuj", font_medium, mouse_pos, mouse_clicked, animation_alpha=menu_alpha):
  1215.                     settings["resource_pack"] = resource_pack_selected_pack
  1216.                     resource_manager.activate_resource_pack(resource_pack_selected_pack)
  1217.                     print(f"Aktywowano: {resource_pack_selected_pack}")
  1218.                     settings_menu_state = "main_settings"
  1219.                     dropdown_open_state = {}
  1220.                     try:
  1221.                         with open("settings.dat", "wb") as f:
  1222.                             pickle.dump(settings, f)
  1223.                     except Exception as e:
  1224.                         print(f"Błąd zapisywania ustawień: {e}")
  1225.                 if draw_button(center_x - button_width // 2, button_y_start + button_height + button_spacing, button_width, button_height, "Powrót", font_medium, mouse_pos, mouse_clicked, animation_alpha=menu_alpha):
  1226.                     settings_menu_state = "main_settings"
  1227.                     dropdown_open_state = {}
  1228.         restore_3d_projection()
  1229.         pygame.display.flip()
  1230.         clock.tick(60)
  1231.     return None
  1232.  
  1233. def show_pause_menu(world_data, world_name_display, size, resource_manager, is_infinite):
  1234.     global display, settings, font_large, font_medium
  1235.     clock = pygame.time.Clock()
  1236.     pygame.mouse.set_visible(True)
  1237.     pygame.event.set_grab(False)
  1238.     pause_menu_state = "main_pause"
  1239.     mouse_sensitivity_dragging = False
  1240.     fov_dragging = False
  1241.     render_distance_dragging = False
  1242.     pause_running = True
  1243.     while pause_running:
  1244.         mouse_pos = pygame.mouse.get_pos()
  1245.         mouse_clicked = False
  1246.         for event in pygame.event.get():
  1247.             if event.type == QUIT:
  1248.                 print("Wyjście z aplikacji.")
  1249.                 if is_infinite and isinstance(world_data, ChunkManager):
  1250.                     world_data.save_all_chunks()
  1251.                 elif world_data is not None and size is not None:
  1252.                     save_fixed_world(world_data, world_name_display, size)
  1253.                 try:
  1254.                     with open("settings.dat", "wb") as f:
  1255.                         pickle.dump(settings, f)
  1256.                 except Exception as e:
  1257.                     print(f"Błąd zapisywania ustawień: {e}")
  1258.                 return False
  1259.             if event.type == VIDEORESIZE:
  1260.                 display = (event.w, event.h)
  1261.                 if display[0] > 0 and display[1] > 0:
  1262.                     pygame.display.set_mode(display, DOUBLEBUF | OPENGL | RESIZABLE)
  1263.                 else:
  1264.                     print(f"Niepoprawny rozmiar okna: {display}")
  1265.             if event.type == MOUSEBUTTONDOWN and event.button == 1:
  1266.                 mouse_clicked = True
  1267.                 if pause_menu_state == "settings_pause" and (mouse_sensitivity_dragging or fov_dragging or render_distance_dragging):
  1268.                     mouse_clicked = False
  1269.             if event.type == KEYDOWN and event.key == K_ESCAPE:
  1270.                 if pause_menu_state == "main_pause":
  1271.                     print("Wznów grę.")
  1272.                     pause_running = False
  1273.                 elif pause_menu_state == "settings_pause":
  1274.                     try:
  1275.                         with open("settings.dat", "wb") as f:
  1276.                             pickle.dump(settings, f)
  1277.                     except Exception as e:
  1278.                         print(f"Błąd zapisywania ustawień: {e}")
  1279.                     pause_menu_state = "main_pause"
  1280.         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
  1281.         draw_background(resource_manager, alpha=1.0)
  1282.         setup_2d_overlay()
  1283.         draw_quad_2d(0, 0, display[0], display[1], color=(0.1, 0.1, 0.1), alpha=0.7)
  1284.         center_x = display[0] // 2
  1285.         center_y = display[1] // 2
  1286.         button_width = 200
  1287.         button_height = 50
  1288.         button_spacing = 15
  1289.         if pause_menu_state == "main_pause":
  1290.             title_text = "Gra wstrzymana"
  1291.             title_width = font_large.size(title_text)[0]
  1292.             draw_text_2d(center_x - title_width // 2, center_y - 150, title_text, font_large)
  1293.             if draw_button(center_x - button_width // 2, center_y - button_height - button_spacing // 2, button_width, button_height, "Wznów grę", font_medium, mouse_pos, mouse_clicked):
  1294.                 pause_running = False
  1295.             if draw_button(center_x - button_width // 2, center_y + button_spacing // 2, button_width, button_height, "Ustawienia", font_medium, mouse_pos, mouse_clicked):
  1296.                 pause_menu_state = "settings_pause"
  1297.             if draw_button(center_x - button_width // 2, center_y + button_height + button_spacing * 3 // 2, button_width, button_height, "Zapisz i wyjdź", font_medium, mouse_pos, mouse_clicked):
  1298.                 print("Zapisz i wróć do menu.")
  1299.                 if is_infinite and isinstance(world_data, ChunkManager):
  1300.                     world_data.save_all_chunks()
  1301.                 elif world_data is not None and size is not None:
  1302.                     save_fixed_world(world_data, world_name_display, size)
  1303.                 try:
  1304.                     with open("settings.dat", "wb") as f:
  1305.                         pickle.dump(settings, f)
  1306.                 except Exception as e:
  1307.                     print(f"Błąd zapisywania ustawień: {e}")
  1308.                 pause_running = False
  1309.                 return True
  1310.         elif pause_menu_state == "settings_pause":
  1311.             title_text = "Ustawienia"
  1312.             title_width = font_large.size(title_text)[0]
  1313.             draw_text_2d(center_x - title_width // 2, center_y - 200, title_text, font_large)
  1314.             setting_width = 300
  1315.             setting_height = 40
  1316.             setting_spacing = 15
  1317.             setting_x = center_x - setting_width // 2
  1318.             setting_y_start = center_y - 100
  1319.             label_height = font_medium.get_linesize()
  1320.             label_text = "Czułość myszy:"
  1321.             label_y = setting_y_start - label_height - 5
  1322.             draw_text_2d(setting_x, label_y, label_text, font_medium, color=(200,200,200))
  1323.             settings["mouse_sensitivity"], mouse_sensitivity_dragging = draw_slider(
  1324.                 setting_x, setting_y_start, setting_width, setting_height, settings.get("mouse_sensitivity", DEFAULT_SETTINGS["mouse_sensitivity"]),
  1325.                 0.01, 1.0, font_medium, mouse_pos, mouse_clicked, mouse_sensitivity_dragging
  1326.             )
  1327.             if mouse_sensitivity_dragging or (not mouse_sensitivity_dragging and pygame.mouse.get_pressed()[0] and
  1328.                 setting_x < mouse_pos[0] < setting_x + setting_width and setting_y_start < mouse_pos[1] < setting_y_start + setting_height):
  1329.                 mouse_clicked = False
  1330.             fov_y = setting_y_start + setting_height + setting_spacing
  1331.             label_text = "FOV:"
  1332.             label_y = fov_y - label_height - 5
  1333.             draw_text_2d(setting_x, label_y, label_text, font_medium, color=(200,200,200))
  1334.             settings["fov"], fov_dragging = draw_slider(
  1335.                 setting_x, fov_y, setting_width, setting_height, settings.get("fov", DEFAULT_SETTINGS["fov"]),
  1336.                 30.0, 110.0, font_medium, mouse_pos, mouse_clicked, fov_dragging
  1337.             )
  1338.             if fov_dragging or (not fov_dragging and pygame.mouse.get_pressed()[0] and
  1339.                 setting_x < mouse_pos[0] < setting_x + setting_width and fov_y < mouse_pos[1] < fov_y + setting_height):
  1340.                 mouse_clicked = False
  1341.             if is_infinite:
  1342.                 render_dist_y = fov_y + setting_height + setting_spacing
  1343.                 label_text = "Odległość renderowania (chunks):"
  1344.                 label_y = render_dist_y - label_height - 5
  1345.                 draw_text_2d(setting_x, label_y, label_text, font_medium, color=(200,200,200))
  1346.                 current_render_dist = settings.get("render_distance", DEFAULT_SETTINGS["render_distance"])
  1347.                 rendered_val, render_distance_dragging = draw_slider(
  1348.                     setting_x, render_dist_y, setting_width, setting_height, current_render_dist,
  1349.                     2, 32, font_medium, mouse_pos, mouse_clicked, render_distance_dragging
  1350.                 )
  1351.                 settings["render_distance"] = int(round(rendered_val))
  1352.                 if render_distance_dragging or (not render_distance_dragging and pygame.mouse.get_pressed()[0] and
  1353.                     setting_x < mouse_pos[0] < setting_x + setting_width and render_dist_y < mouse_pos[1] < render_dist_y + setting_height):
  1354.                     mouse_clicked = False
  1355.                 checkbox_y_start = render_dist_y + setting_height + setting_spacing * 2
  1356.             else:
  1357.                 checkbox_y_start = fov_y + setting_height + setting_spacing * 2
  1358.             checkbox_width = 30
  1359.             checkbox_height = 30
  1360.             settings["invert_mouse_x"] = draw_checkbox(
  1361.                 setting_x, checkbox_y_start, checkbox_width, checkbox_height, "Odwróć oś X myszy",
  1362.                 settings.get("invert_mouse_x", DEFAULT_SETTINGS["invert_mouse_x"]), font_medium, mouse_pos, mouse_clicked
  1363.             )
  1364.             invert_y_y = checkbox_y_start + checkbox_height + setting_spacing
  1365.             settings["invert_mouse_y"] = draw_checkbox(
  1366.                 setting_x, invert_y_y, checkbox_width, checkbox_height, "Odwróć oś Y myszy",
  1367.                 settings.get("invert_mouse_y", DEFAULT_SETTINGS["invert_mouse_y"]), font_medium, mouse_pos, mouse_clicked
  1368.             )
  1369.             button_y_start = invert_y_y + checkbox_height + setting_spacing * 2
  1370.             if draw_button(center_x - button_width // 2, button_y_start, button_width, button_height, "Powrót", font_medium, mouse_pos, mouse_clicked):
  1371.                 try:
  1372.                     with open("settings.dat", "wb") as f:
  1373.                         pickle.dump(settings, f)
  1374.                 except Exception as e:
  1375.                     print(f"Błąd zapisywania ustawień: {e}")
  1376.                 pause_menu_state = "main_pause"
  1377.         restore_3d_projection()
  1378.         pygame.display.flip()
  1379.         clock.tick(60)
  1380.     return False
  1381.  
  1382. def show_delete_confirmation(world_name, resource_manager):
  1383.     global display, font_medium
  1384.     clock = pygame.time.Clock()
  1385.     print(f"POTWIERDZENIE USUWANIA: {world_name}")
  1386.     while True:
  1387.         mouse_pos = pygame.mouse.get_pos()
  1388.         mouse_clicked = False
  1389.         for event in pygame.event.get():
  1390.             if event.type == QUIT:
  1391.                 print("Wyjście z aplikacji.")
  1392.                 return None
  1393.             if event.type == VIDEORESIZE:
  1394.                 display = (event.w, event.h)
  1395.                 if display[0] > 0 and display[1] > 0:
  1396.                     pygame.display.set_mode(display, DOUBLEBUF | OPENGL | RESIZABLE)
  1397.                 else:
  1398.                     print(f"Niepoprawny rozmiar okna: {display}")
  1399.             if event.type == MOUSEBUTTONDOWN and event.button == 1:
  1400.                 mouse_clicked = True
  1401.             if event.type == KEYDOWN and event.key == K_ESCAPE:
  1402.                 print("Anuluj usuwanie.")
  1403.                 return False
  1404.         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
  1405.         setup_2d_overlay()
  1406.         draw_quad_2d(0, 0, display[0], display[1], color=(0.1, 0.1, 0.1), alpha=0.7)
  1407.         window_width = 400
  1408.         window_height = 200
  1409.         window_x = (display[0] - window_width) // 2
  1410.         window_y = (display[1] - window_height) // 2
  1411.         draw_confirm_window(window_x, window_y, window_width, window_height)
  1412.         question_text = "Czy usunąć świat?"
  1413.         question_width = font_medium.size(question_text)[0]
  1414.         draw_text_2d(window_x + (window_width - question_width) // 2, window_y + 40, question_text, font_medium)
  1415.         max_name_width = window_width - 40
  1416.         truncated_world_name = world_name
  1417.         if font_medium.size(truncated_world_name)[0] > max_name_width:
  1418.             while font_medium.size(truncated_world_name + "...")[0] > max_name_width and len(truncated_world_name) > 0:
  1419.                 truncated_world_name = truncated_world_name[:-1]
  1420.             truncated_world_name += "..."
  1421.         name_text = f"'{truncated_world_name}'?"
  1422.         name_width = font_medium.size(name_text)[0]
  1423.         draw_text_2d(window_x + (window_width - name_width) // 2, window_y + 80, name_text, font_medium)
  1424.         button_width = 100
  1425.         button_height = 40
  1426.         button_spacing = 20
  1427.         button_start_x = window_x + (window_width - 2 * button_width - button_spacing) // 2
  1428.         button_y = window_y + window_height - 60
  1429.         if draw_button(button_start_x, button_y, button_width, button_height, "Tak", font_medium, mouse_pos, mouse_clicked):
  1430.             print(f"Usuwam '{world_name}'")
  1431.             restore_3d_projection()
  1432.             return True
  1433.         if draw_button(button_start_x + button_width + button_spacing, button_y, button_width, button_height, "Nie", font_medium, mouse_pos, mouse_clicked):
  1434.             print("Anuluj usuwanie")
  1435.             restore_3d_projection()
  1436.             return False
  1437.         restore_3d_projection()
  1438.         pygame.display.flip()
  1439.         clock.tick(60)
  1440.  
  1441. dropdown_open_state = {}
  1442.  
  1443. def main():
  1444.     global settings, display, font_small, font_medium, font_large, font_height, dropdown_open_state
  1445.     try:
  1446.         font_small = pygame.font.Font(None, 24)
  1447.         font_medium = pygame.font.Font(None, 32)
  1448.         font_large = pygame.font.Font(None, 48)
  1449.         font_height = font_medium.get_linesize()
  1450.     except pygame.error as e:
  1451.         print(f"Błąd ładowania czcionki: {e}")
  1452.  
  1453.     resource_manager = ResourceManager()
  1454.     settings_file = "settings.dat"
  1455.     if os.path.exists(settings_file):
  1456.         try:
  1457.             with open(settings_file, "rb") as f:
  1458.                 loaded_settings = pickle.load(f)
  1459.                 for key in DEFAULT_SETTINGS:
  1460.                     if key in loaded_settings:
  1461.                         expected_type = type(DEFAULT_SETTINGS[key])
  1462.                         loaded_value = loaded_settings[key]
  1463.                         if isinstance(loaded_value, expected_type):
  1464.                             if isinstance(DEFAULT_SETTINGS[key], (int, float)) and loaded_value >= 0:
  1465.                                 settings[key] = loaded_value
  1466.                         elif isinstance(loaded_value, (int, float)) and isinstance(DEFAULT_SETTINGS[key], (int, float)):
  1467.                             settings[key] = expected_type(loaded_value)
  1468.         except Exception as e:
  1469.             print(f"Błąd wczytywania ustawień: {e}")
  1470.  
  1471.     for key, default_value in DEFAULT_SETTINGS.items():
  1472.         if key not in settings:
  1473.             settings[key] = default_value
  1474.  
  1475.     resource_pack_name = settings.get("resource_pack", "Brak")
  1476.     if resource_pack_name not in resource_manager.available_packs:
  1477.         print(f"Pack '{resource_pack_name}' nie znaleziono. Używam 'Brak'.")
  1478.         settings["resource_pack"] = "Brak"
  1479.         resource_pack_name = "Brak"
  1480.     resource_manager.activate_resource_pack(resource_pack_name)
  1481.  
  1482.     running = True
  1483.     while running:
  1484.         dropdown_open_state = {}
  1485.         world_data_tuple = show_main_menu(resource_manager)
  1486.         if world_data_tuple is None:
  1487.             running = False
  1488.             break
  1489.  
  1490.         world_data, world_name_display, size, is_infinite = world_data_tuple
  1491.         if world_data is None:
  1492.             print("Nie udało się wczytać świata. Wracam do menu.")
  1493.             continue
  1494.  
  1495.         # Inicjalizacja gry
  1496.         clock = pygame.time.Clock()
  1497.         pygame.mouse.set_visible(False)
  1498.         pygame.event.set_grab(True)
  1499.  
  1500.         # Pozycja i rotacja gracza
  1501.         player_pos = [size / 2, size / 2 + 2, size / 2] if not is_infinite else [0, 64, 0]
  1502.         player_velocity = [0, 0, 0]
  1503.         player_on_ground = False
  1504.         yaw, pitch = 0, 0
  1505.         selected_block_type = 1  # Domyślny blok do stawiania
  1506.  
  1507.         # Ustawienia kamery
  1508.         gluPerspective(settings["fov"], display[0] / display[1], 0.1, settings["render_distance"] * CHUNK_SIZE * 2)
  1509.         glMatrixMode(GL_MODELVIEW)
  1510.         glLoadIdentity()
  1511.  
  1512.         # Chunk manager dla światów nieskończonych
  1513.         chunk_manager = None
  1514.         if is_infinite:
  1515.             seed, world_dir_name = world_data
  1516.             chunk_manager = ChunkManager(seed=seed, render_distance_chunks=settings["render_distance"], world_name_dir=world_dir_name)
  1517.             world_data = chunk_manager
  1518.             start_y = 60
  1519.             for y in range(255, -1, -1):
  1520.                 block = chunk_manager.get_block(0, y, 0)
  1521.                 print(f"Sprawdzam y={y}, block={block}")
  1522.                 if block != BLOCK_AIR:
  1523.                     start_y = y + 2
  1524.                     break
  1525.             player_pos = [0, start_y, 0]
  1526.             print(f"Pozycja startowa gracza: {player_pos}")
  1527.  
  1528.         game_running = True
  1529.         while game_running:
  1530.             dt = clock.tick(60) / 1000.0  # Delta time w sekundach
  1531.             mouse_dx, mouse_dy = pygame.mouse.get_rel()
  1532.             mouse_sensitivity = settings["mouse_sensitivity"]
  1533.             invert_x = settings["invert_mouse_x"]
  1534.             invert_y = settings["invert_mouse_y"]
  1535.             yaw -= mouse_dx * mouse_sensitivity * (-1 if invert_x else 1)
  1536.             pitch -= mouse_dy * mouse_sensitivity * (-1 if invert_y else 1)
  1537.             pitch = max(-89.9, min(89.9, pitch))  # Ogranicz pitch
  1538.  
  1539.             # Input gracza
  1540.             keys = pygame.key.get_pressed()
  1541.             move_dir = [0, 0, 0]
  1542.             if keys[pygame.K_w]:
  1543.                 move_dir[0] -= math.cos(math.radians(yaw)) * settings["move_speed"]
  1544.                 move_dir[2] -= math.sin(math.radians(yaw)) * settings["move_speed"]
  1545.             if keys[pygame.K_s]:
  1546.                 move_dir[0] += math.cos(math.radians(yaw)) * settings["move_speed"]
  1547.                 move_dir[2] += math.sin(math.radians(yaw)) * settings["move_speed"]
  1548.             if keys[pygame.K_a]:
  1549.                 move_dir[0] -= math.cos(math.radians(yaw + 90)) * settings["move_speed"]
  1550.                 move_dir[2] -= math.sin(math.radians(yaw + 90)) * settings["move_speed"]
  1551.             if keys[pygame.K_d]:
  1552.                 move_dir[0] += math.cos(math.radians(yaw + 90)) * settings["move_speed"]
  1553.                 move_dir[2] += math.sin(math.radians(yaw + 90)) * settings["move_speed"]
  1554.             if keys[pygame.K_SPACE] and player_on_ground:
  1555.                 player_velocity[1] = settings["jump_force"]
  1556.                 player_on_ground = False
  1557.  
  1558.             # Grawitacja i ruch
  1559.             player_velocity[1] -= settings["gravity"] * dt * 60
  1560.             player_velocity[0] = move_dir[0]
  1561.             player_velocity[2] = move_dir[2]
  1562.  
  1563.             # Kolizje
  1564.             def is_colliding(pos):
  1565.                 for dx in [-0.3, 0, 0.3]:
  1566.                     for dy in [0, 1.8]:
  1567.                         for dz in [-0.3, 0, 0.3]:
  1568.                             block_x = int(math.floor(pos[0] + dx))
  1569.                             block_y = int(math.floor(pos[1] + dy))
  1570.                             block_z = int(math.floor(pos[2] + dz))
  1571.                             block = chunk_manager.get_block(block_x, block_y, block_z) if is_infinite else \
  1572.                                     world_data[block_x][block_y][block_z] if 0 <= block_x < size and 0 <= block_y < size and 0 <= block_z < size else BLOCK_AIR
  1573.                             if block != BLOCK_AIR:
  1574.                                 return True
  1575.                 return False
  1576.  
  1577.             for i in range(3):
  1578.                 new_pos = player_pos.copy()
  1579.                 new_pos[i] += player_velocity[i] * dt * 60
  1580.                 if not is_colliding(new_pos):
  1581.                     player_pos[i] = new_pos[i]
  1582.                 else:
  1583.                     player_velocity[i] = 0
  1584.                     if i == 1 and player_velocity[1] <= 0:
  1585.                         player_on_ground = True
  1586.  
  1587.             # Aktualizacja chunków
  1588.             if is_infinite:
  1589.                 chunk_manager.update_loaded_chunks(player_pos)
  1590.  
  1591.             # Raycasting do interakcji z blokami
  1592.             def raycast(start_pos, direction, max_distance=5):
  1593.                 pos = start_pos.copy()
  1594.                 step = [1 if d > 0 else -1 for d in direction]
  1595.                 t_delta = [abs(1 / d) if d != 0 else float('inf') for d in direction]
  1596.                 max_steps = int(max_distance / 0.1)
  1597.                 block_pos = [int(math.floor(p)) for p in pos]
  1598.                 t_max = [(1 - (p - block_pos[i])) / direction[i] if direction[i] > 0 else (p - block_pos[i]) / -direction[i] if direction[i] < 0 else float('inf') for i, p in enumerate(pos)]
  1599.                 for _ in range(max_steps):
  1600.                     block = chunk_manager.get_block(*block_pos) if is_infinite else \
  1601.                             world_data[block_pos[0]][block_pos[1]][block_pos[2]] if 0 <= block_pos[0] < size and 0 <= block_pos[1] < size and 0 <= block_pos[2] < size else BLOCK_AIR
  1602.                     if block != BLOCK_AIR:
  1603.                         return block_pos, [block_pos[i] + (0 if direction[i] > 0 else 1) for i in range(3)]
  1604.                     min_t = min(enumerate(t_max), key=lambda x: x[1])[0]
  1605.                     block_pos[min_t] += step[min_t]
  1606.                     t_max[min_t] += t_delta[min_t]
  1607.                 return None, None
  1608.  
  1609.             direction = [
  1610.                 -math.cos(math.radians(yaw)) * math.cos(math.radians(pitch)),
  1611.                 -math.sin(math.radians(pitch)),
  1612.                 -math.sin(math.radians(yaw)) * math.cos(math.radians(pitch))
  1613.             ]
  1614.  
  1615.             # Zdarzenia
  1616.             for event in pygame.event.get():
  1617.                 if event.type == QUIT:
  1618.                     if is_infinite:
  1619.                         chunk_manager.save_all_chunks()
  1620.                     elif size is not None:
  1621.                         save_fixed_world(world_data, world_name_display, size)
  1622.                     running = False
  1623.                     game_running = False
  1624.                 if event.type == VIDEORESIZE:
  1625.                     display = (event.w, event.h)
  1626.                     if display[0] > 0 and display[1] > 0:
  1627.                         pygame.display.set_mode(display, DOUBLEBUF | OPENGL | RESIZABLE)
  1628.                         gluPerspective(settings["fov"], display[0] / display[1], 0.1, settings["render_distance"] * CHUNK_SIZE * 2)
  1629.                     else:
  1630.                         print(f"Niepoprawny rozmiar okna: {display}")
  1631.                 if event.type == KEYDOWN:
  1632.                     if event.key == K_ESCAPE:
  1633.                         return_to_menu = show_pause_menu(world_data, world_name_display, size, resource_manager, is_infinite)
  1634.                         if return_to_menu:
  1635.                             game_running = False
  1636.                         pygame.mouse.set_visible(True)
  1637.                         pygame.event.set_grab(False)
  1638.                         print("Po pauzie: kursor włączony")
  1639.                     if event.key == K_e:
  1640.                         print("Otwarto ekwipunek (placeholder).")
  1641.                 if event.type == MOUSEBUTTONDOWN:
  1642.                     block_pos, adjacent_pos = raycast(player_pos, direction)
  1643.                     if event.button == 1 and block_pos:  # LPM - usuń blok
  1644.                         if is_infinite:
  1645.                             chunk_manager.set_block(*block_pos, BLOCK_AIR)
  1646.                         elif 0 <= block_pos[0] < size and 0 <= block_pos[1] < size and 0 <= block_pos[2] < size:
  1647.                             world_data[block_pos[0]][block_pos[1]][block_pos[2]] = BLOCK_AIR
  1648.                         print(f"Usunięto blok na {block_pos}")
  1649.                     if event.button == 3 and adjacent_pos:  # PPM - postaw blok
  1650.                         if is_infinite:
  1651.                             if not is_colliding([adjacent_pos[0] + 0.5, adjacent_pos[1] + 0.5, adjacent_pos[2] + 0.5]):
  1652.                                 chunk_manager.set_block(*adjacent_pos, selected_block_type)
  1653.                                 print(f"Postawiono blok {BLOCK_TYPES_TO_INV_NAMES[selected_block_type]} na {adjacent_pos}")
  1654.                         elif 0 <= adjacent_pos[0] < size and 0 <= adjacent_pos[1] < size and 0 <= adjacent_pos[2] < size:
  1655.                             if not is_colliding([adjacent_pos[0] + 0.5, adjacent_pos[1] + 0.5, adjacent_pos[2] + 0.5]):
  1656.                                 world_data[adjacent_pos[0]][adjacent_pos[1]][adjacent_pos[2]] = selected_block_type
  1657.                                 print(f"Postawiono blok {BLOCK_TYPES_TO_INV_NAMES[selected_block_type]} na {adjacent_pos}")
  1658.  
  1659.             # Renderowanie
  1660.             glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
  1661.             print(f"Stos macierzy przed: {glGetIntegerv(GL_MATRIX_MODE)}")
  1662.             glLoadIdentity()
  1663.             gluLookAt(
  1664.                 player_pos[0], player_pos[1] + 1.6, player_pos[2],
  1665.                 player_pos[0] + direction[0], player_pos[1] + 1.6 + direction[1], player_pos[2] + direction[2],
  1666.                 0, 1, 0
  1667.             )
  1668.             print(f"Stos macierzy po kamery: {glGetIntegerv(GL_MATRIX_MODE)}")
  1669.             draw_world(world_data, player_pos, settings, resource_manager, BLOCK_TEXTURE_PATHS, is_infinite)
  1670.  
  1671.             # HUD
  1672.             setup_2d_overlay()
  1673.             crosshair_size = 10
  1674.             draw_lines_2d(
  1675.                 [
  1676.                     (display[0] / 2 - crosshair_size, display[1] / 2),
  1677.                     (display[0] / 2 + crosshair_size, display[1] / 2),
  1678.                     (display[0] / 2, display[1] / 2 - crosshair_size),
  1679.                     (display[0] / 2, display[1] / 2 + crosshair_size)
  1680.                 ],
  1681.                 color=(1, 1, 1), line_width=2
  1682.             )
  1683.             fps_text = f"FPS: {int(clock.get_fps())}"
  1684.             draw_text_2d(10, 10, fps_text, font_small)
  1685.             restore_3d_projection()
  1686.  
  1687.             pygame.display.flip()
  1688.  
  1689.         # Zapis świata przy wyjściu z gry
  1690.         if is_infinite:
  1691.             chunk_manager.save_all_chunks()
  1692.         elif size is not None:
  1693.             save_fixed_world(world_data, world_name_display, size)
  1694.         try:
  1695.             with open("settings.dat", "wb") as f:
  1696.                 pickle.dump(settings, f)
  1697.         except Exception as e:
  1698.             print(f"Błąd zapisywania ustawień: {e}")
  1699.  
  1700.     # Zamykanie gry
  1701.     resource_manager.clear_texture_cache()
  1702.     pygame.quit()
  1703.  
  1704. if __name__ == "__main__":
  1705.     try:
  1706.         main()
  1707.     except Exception as e:
  1708.         print(f"Błąd: {e}")
  1709.         traceback.print_exc()
  1710.         pygame.quit()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement