class Chunk:
surface = 0
bedrock = 16
ground = 3
starts = (surface, ground, bedrock)
levels = dict(zip(starts, (2, 3, 7)))
block_sizes = (1, 3, 4, 0)
def generate(self):
for x, y in product(self.x_range, self.y_range):
y_pos = int(self.pos.y) + y
if y_pos in self.levels:
block = self.levels[y_pos]
else:
i = bisect(self.starts, y_pos) - 1
block = self.block_sizes[i]
block = max(block, 0)
self.update((x, y), block)
import pygame
from bisect import bisect
from itertools import product
BLOCK_SIZE = 64
BLOCK_TEXTURE_NAMES = [
"textures/void.png",
"textures/air.png",
"textures/grass_block.png",
"textures/dirt.png",
"textures/stone.png",
"textures/sandstone.png",
"textures/sand.png",
"textures/bedrock.png",
"textures/oak_log.png",
"textures/oak_leaves.png",
"textures/cobblestone.png",
]
def block_texture(texture):
return pygame.transform.scale(
pygame.image.load(texture), (BLOCK_SIZE, BLOCK_SIZE)
).convert_alpha()
def convert(pos, convert_value_x, convert_value_y):
"""Converts coordinate systems."""
nx, rx = divmod(pos.x, convert_value_x)
ny, ry = divmod(pos.y, convert_value_y)
return ((nx, ny), (rx, ry))
class Chunk:
surface = 0
bedrock = 16
ground = 3
starts = (surface, ground, bedrock)
levels = dict(zip(starts, (2, 3, 7)))
block_sizes = (1, 3, 4, 0)
def __init__(self, pos, size):
self.size = size
self.actual_size = size * BLOCK_SIZE
self.block_textures = [
block_texture(texture) for texture in BLOCK_TEXTURE_NAMES
]
self.backround_texture = block_texture("textures/backround.png")
self.pos = pos
self.actual_pos = pos * BLOCK_SIZE
self.surface = pygame.Surface((self.actual_size, self.actual_size))
self.x_range = range(self.size)
self.y_range = range(self.size)
self.blocks = {}
def update(self, block_pos, new_block=None):
vector = pygame.Vector2(block_pos) * BLOCK_SIZE
self.surface.blit(self.backround_texture, vector)
self.blocks[block_pos] = block = new_block or self.blocks[block_pos]
self.surface.blit(block, vector)
def generate(self):
for x, y in product(self.x_range, self.y_range):
y_pos = int(self.pos.y) + y
if y_pos in self.levels:
block = self.levels[y_pos]
else:
i = bisect(self.starts, y_pos) - 1
block = self.block_sizes[i]
block = max(block, 0)
self.update((x, y), block)
def set_blocks(self, blocks):
self.blocks = blocks
for x, y in product(self.x_range, self.y_range):
self.update((x, y))
def __str__(self):
return str([self.blocks[(x, y)] for x in self.x_range for y in self.y_range])
class Vector(pygame.math.Vector2):
def __hash__(self):
return id(self)
class World:
def __init__(self, loader_pos, loader_distance, chunk_size):
self.chunk_size = chunk_size
self.actual_chunk_size = chunk_size * BLOCK_SIZE
self.loaded_chunks = {}
self.loader_pos = loader_pos
self.set_loader_chunk_pos(loader_pos)
self.loader_distance = loader_distance
self.rendered = pygame.Surface((1, 1)).convert_alpha()
self.innactive_block_data = {}
def handle_chunk_loader(self):
chunks_needed = {Vector(i) for i in self.chunks_to_load()}
loaded_chunks = {Vector(i) for i in self.loaded_chunks}
self.load_chunks(chunks_needed - loaded_chunks)
self.unload_chunks(loaded_chunks - chunks_needed)
def get_data(self, chunk_pos):
return self.innactive_block_data.get(chunk_pos, False)
def load_chunks(self, chunks_pos):
for pos in chunks_pos:
chunk_pos = (pos.x, pos.y)
self.loaded_chunks[chunk_pos] = Chunk(pos, self.chunk_size)
(
self.loaded_chunks[chunk_pos].set_blocks(block_data)
if (block_data := self.get_data(chunk_pos))
else self.loaded_chunks[chunk_pos].generate()
)
def unload_chunks(self, chunks_pos):
for chunk_pos in chunks_pos:
chunk = self.loaded_chunks.pop(chunk_pos)
if self.get_data(chunk_pos) != chunk.blocks:
self.innactive_block_data[chunk_pos] = chunk.blocks
def get_block(self, pos):
chunk_pos, block_pos = convert(pos, self.chunk_size, self.chunk_size)
try:
return self.loaded_chunks[chunk_pos].blocks[block_pos]
except IndexError:
return False
def get_range(self, axis):
n = int(getattr(self.loader_chunk_pos, axis))
return range(
n - self.loader_distance,
n + self.loader_distance + 1,
)
def chunks_to_load(self):
return [
pygame.Vector2(chunk_pos_x * self.chunk_size, chunk_pos_y * self.chunk_size)
for chunk_pos_x, chunk_pos_y in product(
self.get_range("x"), self.get_range("y")
)
]
def set_loader_chunk_pos(self, pos):
self.loader_chunk_pos = pygame.Vector2(
convert(pos, self.actual_chunk_size, self.actual_chunk_size)[0]
)
def change_pos(self, pos):
self.loader_pos += pos
self.set_loader_chunk_pos(self.loader_pos)
def set_pos(self, pos):
self.loader_pos = pos
self.set_loader_chunk_pos(pos)
def render(self, screen, use_pos, other_offset=0):
for ch in self.loaded_chunks.values():
screen.blit(
ch.surface,
(ch.actual_pos - self.loader_pos * int(use_pos)) + other_offset,
)