"""
Global settings and configuration constants for the Pyrite voxel engine.
This module acts as the central repository for engine configurations, including
OpenGL parameters, camera constraints, player physics, terrain thresholds, block IDs,
color palettes, and performance caps (e.g., ThreadPool limits and VRAM pool sizes).
It also provides a helper for resolving absolute asset paths in bundled PyInstaller builds.
"""
import math
import os
import sys
from typing import Any, Dict, Set, Tuple
import pygame
from pyglm import glm
[docs]
def get_path(relative_path: str) -> str:
"""Get absolute path to resource"""
# Error handling
try:
# Variable assignments
base_path: str = sys._MEIPASS # type: ignore[attr-defined]
except AttributeError:
# Variable assignments
base_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
# Return result
return os.path.join(base_path, relative_path)
# OpenGL settings
MAJOR_VERSION: int = 3
MINOR_VERSION: int = 3
DEPTH_SIZE: int = 24
NUM_SAMPLES: int = 1 # antialiasing
# resolution
# Initialize pygame briefly just to grab the current display information from the OS
pygame.init()
info: Any = pygame.display.Info()
# Create a 2D vector representing the screen width and height in pixels
WINDOW_RESOLUTION: Any = glm.vec2(info.current_w, info.current_h)
pygame.quit()
# ray casting
# The maximum distance in blocks/meters that the player's crosshair can interact with
MAX_RAY_DISTANCE: int = 6
# chunk
# The number of blocks along each axis (X, Y, Z) in a single chunk
CHUNK_SIZE: int = 48
# Half the chunk size, often used as an offset to find the center of a chunk
HALF_CHUNK_SIZE: int = CHUNK_SIZE // 2
# The 2D area (X * Z) of a chunk slice, useful for iterating over vertical columns
CHUNK_AREA: int = CHUNK_SIZE * CHUNK_SIZE
# The total number of blocks in a chunk (X * Y * Z), used for sizing flat arrays
CHUNK_VOLUME: int = CHUNK_AREA * CHUNK_SIZE
# The radius of a bounding sphere that perfectly encapsulates a cubic chunk.
# Calculated using the 3D Pythagorean theorem: r = sqrt((L/2)^2 + (W/2)^2 + (H/2)^2)
# Since chunks are perfect cubes, this simplifies to r = (Size/2) * sqrt(3).
# Used for frustum culling to quickly check if a chunk is visible on screen.
CHUNK_SPHERE_RADIUS: float = HALF_CHUNK_SIZE * math.sqrt(3)
# world
# The number of chunks along the X axis
WORLD_WIDTH: int = 30
# The number of chunks along the Y (vertical) axis
WORLD_HEIGHT: int = 5
# The number of chunks along the Z axis (typically same as width for a square world)
WORLD_DEPTH: int = WORLD_WIDTH
# The total number of chunks in a horizontal slice of the world
WORLD_AREA: int = WORLD_WIDTH * WORLD_DEPTH
# The total number of chunks loaded in the entire world
WORLD_VOLUME: int = WORLD_AREA * WORLD_HEIGHT
# The exact center point of the world on the X and Z axes, calculated in block coordinates
CENTER_XZ: float = WORLD_WIDTH * HALF_CHUNK_SIZE
# The base vertical level for flat terrain generation
CENTER_Y: int = 48
# camera
# The ratio of the window's width to its height, required to prevent the rendered image from stretching
ASPECT_RATIO: float = WINDOW_RESOLUTION.x / WINDOW_RESOLUTION.y
# The base vertical field of view in degrees, defining how much the camera can see up and down
FOV_DEGREE: int = 50
# ModernGL and GLM require angles in radians, so we convert the degree value here
VERTICAL_FOV: float = glm.radians(FOV_DEGREE)
# Calculates the horizontal FOV based on the vertical FOV and screen aspect ratio.
# This uses basic trigonometry: tan(fov_h / 2) = tan(fov_v / 2) * aspect_ratio.
# We multiply by 2 at the end because the tangent calculation only gives half the angle.
HORIZONTAL_FOV: float = 2 * math.atan(math.tan(VERTICAL_FOV * 0.5) * ASPECT_RATIO)
# The closest distance the camera will render objects; anything closer is clipped
NEAR: float = 0.1
# The maximum distance the camera will render objects; anything further is clipped
FAR: float = 2000.0
# The maximum angle in radians the camera can look up or down, capped just shy of 90 degrees to prevent gimbal lock
PITCH_MAX: float = glm.radians(89)
# player
PLAYER_SPEED: float = 0.005
PLAYER_ROT_SPEED: float = 0.003
PLAYER_SPRINT_MULTIPLIER: float = 1.5
PLAYER_WATER_DRAG_MULTIPLIER: float = 0.5
PLAYER_DOLPHIN_LEAP_MULTIPLIER: float = 1.05
PLAYER_UNDERWATER_GRAVITY_MULTIPLIER: float = 0.2
PLAYER_VERTICAL_WATER_DRAG: float = 0.005
PLAYER_POS: Any = glm.vec3(CENTER_XZ, CHUNK_SIZE, CENTER_XZ)
MOUSE_SENSITIVITY: float = 0.002
SPAWN_SEARCH_RADIUS: int = 500
# View Bobbing
VIEW_BOBBING_STEP_FREQUENCY: float = 2.5
VIEW_BOBBING_AMPLITUDE: float = 0.05
SPRINT_FOV_BOOST: float = 10.0 # degrees
SPRINT_FOV_LERP_SPEED: float = 0.01
# Mining
PICKAXE_MINING_MULTIPLIER: float = 5.0
BAREHAND_MINING_PENALTY: float = 5.0
# Queue Processing
MESH_BUILD_LIMIT_INGAME: int = 4
MESH_BUILD_LIMIT_LOADING: int = 64
MAIN_THREAD_MESH_PROCESS_LIMIT_INGAME: int = 2
MAIN_THREAD_MESH_PROCESS_LIMIT_LOADING: int = 10
MAIN_THREAD_CHUNK_PROCESS_LIMIT_INGAME: int = 1
MAIN_THREAD_CHUNK_PROCESS_LIMIT_LOADING: int = 10
# Memory Caps
VBO_POOL_CAP: int = 150
LIGHTING_QUEUE_SIZE: int = 5_000_000
ITEM_ENTITY_CAP: int = 256
# RENDERING & ENVIRONMENT
DAY_NIGHT_SPEED: float = 0.01
FOG_DENSITY_BASE: float = 0.002
CLOUD_FOG_DENSITY_BASE: float = 0.000036
UNDERWATER_FOG_COLOR: Any = glm.vec3(0.0, 0.1, 0.4)
UNDERWATER_FOG_DENSITY: float = 0.0015
UNDERWATER_FOG_MAX_OPACITY: float = 0.85
ITEM_RENDER_DISTANCE_SQUARED: float = 1024.0 # 32 * 32
ITEM_SPAWN_VELOCITY_MULTIPLIER: float = 0.002
# Held Item / Viewmodel
HELD_ITEM_POS: Any = glm.vec3(0.5, -0.4, -1.0)
HELD_ITEM_BOB_OFFSET_Y_MULT: float = 0.03
HELD_ITEM_BOB_OFFSET_X_MULT: float = 0.02
HELD_ITEM_SWING_ROT_X: float = 0.4
HELD_ITEM_SWING_OFFSET_Y: float = 0.15
HELD_ITEM_SWING_OFFSET_Z: float = -0.1
HELD_ITEM_PLACE_SWING_ROTATION_X: float = 0.3
HELD_ITEM_PLACE_SWING_OFFSET_Y: float = 0.2
HELD_STICK_POS_OFFSET: Any = glm.vec3(0.0, 0.15, 0.0)
HELD_STICK_ROT_X: float = glm.radians(-45.0)
HELD_STICK_ROT_Z: float = glm.radians(90.0)
HELD_STICK_SCALE: Any = glm.vec3(0.5)
HELD_PICKAXE_POS_OFFSET: Any = glm.vec3(0.0, 0.15, 0.0)
HELD_PICKAXE_ROT_X: float = glm.radians(10.0)
HELD_PICKAXE_ROT_Z: float = glm.radians(90.0)
HELD_PICKAXE_SCALE: Any = glm.vec3(0.2)
HELD_BLOCK_ROT_X: float = glm.radians(-15.0)
HELD_BLOCK_ROT_Y: float = glm.radians(45.0)
HELD_BLOCK_SCALE: Any = glm.vec3(0.35)
# colors
BG_COLOR: Any = glm.vec3(0.58, 0.83, 0.99)
# UIDs for blocks and items
AIR: int = 0
SAND: int = 1
GRASS: int = 2
DIRT: int = 3
STONE: int = 4
SNOW: int = 5
LEAVES: int = 6
WOOD: int = 7
GRAVEL: int = 8
WOOD_PLANKS: int = 9
COBBELSTONE: int = 10
WATER: int = 11
GLOWSTONE: int = 12
GLASS: int = 13
CACTUS: int = 14
STONE_BRICKS: int = 15
# 16-32 are reserved for other blocks, 33+ are items
STICK: int = 33
WOODEN_PICKAXE: int = 34
NON_PLACEABLE: Set[int] = {STICK, WOODEN_PICKAXE}
# Texture Array Mapping (Global UID -> Row in tex_array_2.png)
TEXTURE_MAP: Dict[int, int] = {
SAND: 1,
GRASS: 2,
DIRT: 3,
STONE: 4,
SNOW: 5,
LEAVES: 6,
WOOD: 7,
GRAVEL: 8,
WOOD_PLANKS: 9,
COBBELSTONE: 10,
WATER: 11,
GLOWSTONE: 12,
GLASS: 13,
CACTUS: 14,
STONE_BRICKS: 15,
}
# terrain levels
SNOW_LVL: int = 54
STONE_LVL: int = 49
DIRT_LVL: int = 40
GRASS_LVL: int = 8
SAND_LVL: int = 7
# tree settings
TREE_PROBABILITY: float = 0.02
TREE_WIDTH: int = 4
TREE_HEIGHT: int = 8
TREE_H_WIDTH: int = TREE_WIDTH // 2
TREE_H_HEIGHT: int = TREE_HEIGHT // 2
# block hardness (ms to break)
BLOCK_HARDNESS: Dict[int, int] = {
SAND: 300,
GRASS: 450,
DIRT: 400,
STONE: 1500,
SNOW: 200,
LEAVES: 150,
WOOD: 1000,
WATER: 0, # Water can't be normally mined
GLOWSTONE: 100,
GLASS: 100,
CACTUS: 150,
STONE_BRICKS: 1500,
}
# game modes
CREATIVE: int = 0
SURVIVAL: int = 1
# player interaction
INTERACTION_DELAY: int = 150 # ms delay for continuous mining/placing
# ui
HOTBAR_SCALE: float = 0.045
SLOT_SCALE: float = 0.05
HOTBAR_SPACING: float = 0.1
HOTBAR_Y: float = -0.85
# water
WATER_LINE: int = 6
WATER_AREA: int = 5 * CHUNK_SIZE * WORLD_WIDTH
# cloud
CLOUD_SCALE: int = 25
CLOUD_HEIGHT: int = 200
PLAYER_WIDTH: float = 0.6
PLAYER_HEIGHT: float = 1.8
PLAYER_HALF_W: float = PLAYER_WIDTH / 2
PLAYER_EYE_HEIGHT: float = 1.6
GRAVITY: float = -0.000025
JUMP_VELOCITY: float = 0.0095
# survival mechanics
MAX_HEALTH: int = 20
MAX_HUNGER: int = 20
MAX_OXYGEN: int = 20
FALL_DAMAGE_THRESHOLD: float = 3.0
VOID_DEATH_Y: int = -20
VOID_DAMAGE: int = 4
VOID_DAMAGE_INTERVAL: int = 500
HUNGER_DRAIN_SPRINT: float = 0.002
HUNGER_DRAIN_WALK: float = 0.0005
OXYGEN_LOSE_TIMER: int = 1000
OXYGEN_GAIN_TIMER: int = 200
# inventory
INVENTORY_SIZE: int = 41 # 36 main + 4 crafting grid + 1 output
HOTBAR_SIZE: int = 9
# item drops
ITEM_PICKUP_RADIUS: float = 2.5
ITEM_PICKUP_DELAY: int = 200
ITEM_SCALE: float = 0.25
# ui palette & fonts
FONT_SIZE_STATS: int = 20
FONT_SIZE_SLIDERS: int = 30
FONT_SIZE_BUTTONS: int = 40
FONT_SIZE_SUBTITLE: int = 60
FONT_SIZE_LOADING: int = 140
FONT_SIZE_PAUSED: int = 160
FONT_SIZE_TITLE: int = 180
FONT_SIZE_DEBUG: int = 18
UI_BG_COLOR: Tuple[float, float, float, float] = (0.85, 0.85, 0.85, 0.5)
# UI_BG_COLOR = (0.1, 0.12, 0.15, 0.7)
UI_HOVER_COLOR: Tuple[float, float, float, float] = (0.85, 0.65, 0.13, 0.9) # Pyrite Gold
UI_BUTTON_COLOR: Tuple[float, float, float, float] = (0.15, 0.20, 0.25, 0.9) # Slate Blue
UI_SLOT_BG_COLOR: Tuple[float, float, float, float] = (0.2, 0.2, 0.2, 0.6)
UI_SLOT_HOVER_COLOR: Tuple[float, float, float, float] = (0.9, 0.9, 0.9, 0.5)
UI_SLOT_SELECTED_FRAME_COLOR: Tuple[float, float, float, float] = (0.9, 0.9, 0.9, 0.9)
UI_SLOT_SELECTED_BG_COLOR: Tuple[float, float, float, float] = (0.5, 0.5, 0.5, 0.7)
# UI_SLOT_BG_COLOR = (0.1, 0.12, 0.15, 0.7)
# UI_SLOT_HOVER_COLOR = (0.85, 0.65, 0.13, 0.4)
# UI_SLOT_SELECTED_FRAME_COLOR = (0.85, 0.65, 0.13, 1.0)
# UI_SLOT_SELECTED_BG_COLOR = (0.15, 0.20, 0.25, 0.8)
UI_TEXT_COLOR: Tuple[int, int, int] = (245, 245, 245)
UI_SHADOW_COLOR: Tuple[int, int, int] = (15, 20, 25)