diff --git a/.gitnore b/.gitignore similarity index 54% rename from .gitnore rename to .gitignore index a2eaf75..7d2e9bd 100644 --- a/.gitnore +++ b/.gitignore @@ -1,2 +1,3 @@ __pycache__/ -mods/ \ No newline at end of file +mods/ +*.pyc \ No newline at end of file diff --git a/episode-11-Fire-Loader/camera.py b/episode-11-Fire-Loader/camera.py index ba765c7..399b8d6 100644 --- a/episode-11-Fire-Loader/camera.py +++ b/episode-11-Fire-Loader/camera.py @@ -1,10 +1,19 @@ import math +import os + import matrix WALKING_SPEED = 7 SPRINTING_SPEED = 21 -class Camera: +mods = [] +mods_imported = [] + +if os.path.isdir("mods"): + mods = [os.path.join("mods", f[1], "camera.py").replace(".py", "") for f in [[os.listdir(os.path.join("mods", d)), d] for d in os.listdir("mods") if os.path.isdir(os.path.join("mods", d))] if "camera.py" in f[0]] + mods_imported = [__import__(m.replace("\\", "."), fromlist=[""]) for m in mods] + +class CameraBaseImpl: def __init__(self, shader, width, height): self.width = width self.height = height @@ -28,17 +37,12 @@ def __init__(self, shader, width, height): self.target_speed = WALKING_SPEED self.speed = self.target_speed - - # set variables to read - - self.walking_speed_r = WALKING_SPEED - self.sprinting_speed_r = SPRINTING_SPEED def update_camera(self, delta_time): self.speed += (self.target_speed - self.speed) * delta_time * 20 multiplier = self.speed * delta_time - self.position[1] += (1 if self.input[1] else 0) * multiplier + self.position[1] += self.input[1] * multiplier if self.input[0] or self.input[2]: angle = self.rotation[0] - math.atan2(self.input[2], self.input[0]) + math.tau / 4 @@ -64,4 +68,13 @@ def update_matrices(self): # modelviewprojection matrix mvp_matrix = self.p_matrix * self.mv_matrix - self.shader.uniform_matrix(self.shader_matrix_location, mvp_matrix) \ No newline at end of file + self.shader.uniform_matrix(self.shader_matrix_location, mvp_matrix) + +CameraMixins = [] +for module in mods_imported: + if hasattr(module, "CameraMixin"): + CameraMixins.append(module.CameraMixin) + print("Applying mixin to class camera.Camera") + +class Camera(*CameraMixins, CameraBaseImpl): + """Camera class that handles camera transforms""" \ No newline at end of file diff --git a/episode-11-Fire-Loader/chunk.py b/episode-11-Fire-Loader/chunk.py index 9732e2e..2908da8 100644 --- a/episode-11-Fire-Loader/chunk.py +++ b/episode-11-Fire-Loader/chunk.py @@ -4,12 +4,22 @@ import pyglet.gl as gl import subchunk +import os CHUNK_WIDTH = 16 CHUNK_HEIGHT = 128 CHUNK_LENGTH = 16 -class Chunk: +mods = [] +mods_imported = [] + +if os.path.isdir("mods"): + mods = [os.path.join("mods", f[1], "chunk.py").replace(".py", "") for f in [[os.listdir(os.path.join("mods", d)), d] for d in os.listdir("mods") if os.path.isdir(os.path.join("mods", d))] if "chunk.py" in f[0]] + mods_imported = [__import__(m.replace("\\", "."), fromlist=[""]) for m in mods] + +print(mods) + +class ChunkBaseImpl: def __init__(self, world, chunk_position): self.world = world @@ -186,4 +196,13 @@ def draw(self): gl.GL_TRIANGLES, self.mesh_indices_length, gl.GL_UNSIGNED_INT, - None) \ No newline at end of file + None) + +ChunkMixins = [] +for module in mods_imported: + if hasattr(module, "ChunkMixin"): + ChunkMixins.append(module.ChunkMixin) + print("Applying mixin to class chunk.Chunk") + +class Chunk(*ChunkMixins, ChunkBaseImpl): + """Chunk class that handles storage of blocks and render region""" diff --git a/episode-11-Fire-Loader/main.py b/episode-11-Fire-Loader/main.py index 35e9267..199dc81 100644 --- a/episode-11-Fire-Loader/main.py +++ b/episode-11-Fire-Loader/main.py @@ -13,18 +13,14 @@ import pyglet.gl as gl -import matrix import shader import camera -import block_type -import texture_manager - import world import hit -lines = ["--- Welcome to Fire Loader ---", "Developed by drakeerv", "Modified from obiwac", "--- Starting---", ""] +lines = ["--- Welcome to Fire Loader ---", "Developed by drakeerv and Jukitsu", "Modified from obiwac", "--- Starting---", ""] [print(line) for line in lines] del lines @@ -40,7 +36,7 @@ print("") -class Window(pyglet.window.Window): +class WindowBaseImpl(pyglet.window.Window): def __init__(self, **args): super().__init__(**args) @@ -219,10 +215,19 @@ def on_key_release(self, key, modifiers): try: module.keyboard_release(self) except Exception as e: print(e) +WindowMixins = [] +for module in mods_imported: + if hasattr(module, "WindowMixin"): + WindowMixins.append(module.WindowMixin) + print("Applying mixin to class main.Window") + +class Window(*WindowMixins, WindowBaseImpl): + """Window class that handles the game window and event callbacks""" + class Game: def __init__(self): self.config = gl.Config(major_version = 3, depth_size = 16) - self.window = Window(config = self.config, width = 800, height = 600, caption = "Minecraft clone (Fire Loader)", resizable = True, vsync = False) + self.window = Window(config = self.config, width = 852, height = 480, caption = "Minecraft clone (Fire Loader)", resizable = True, vsync = False) def run(self): pyglet.app.run() diff --git a/episode-11-Fire-Loader/matrix.py b/episode-11-Fire-Loader/matrix.py index f2a5c61..477eb96 100644 --- a/episode-11-Fire-Loader/matrix.py +++ b/episode-11-Fire-Loader/matrix.py @@ -3,6 +3,7 @@ import ctypes import math + def copy_matrix(matrix): return copy.deepcopy(matrix) # we need to use deepcopy since we're dealing with 2D arrays @@ -42,15 +43,18 @@ def __mul__(self, matrix): def __imul__(self, matrix): self.data = multiply_matrices(self.data, matrix.data) - def scale(self, x, y, z): + + def scale(self, scale_x, scale_y, scale_z): for i in range(4): self.data[0][i] *= scale_x for i in range(4): self.data[1][i] *= scale_y for i in range(4): self.data[2][i] *= scale_z + def translate(self, x, y, z): for i in range(4): self.data[3][i] = self.data[3][i] + (self.data[0][i] * x + self.data[1][i] * y + self.data[2][i] * z) + def rotate(self, angle, x, y, z): magnitude = math.sqrt(x * x + y * y + z * z) @@ -114,6 +118,7 @@ def frustum(self, left, right, bottom, top, near, far): self.data = multiply_matrices(self.data, frustum_matrix) + def perspective(self, fovy, aspect, near, far): frustum_y = math.tan(math.radians(fovy) / 2) frustum_x = frustum_y * aspect @@ -137,3 +142,5 @@ def orthographic(self, left, right, bottom, top, near, far): orthographic_matrix[3][2] = -(near + far) / deltaz self.data = multiply_matrices(self.data, orthographic_matrix) + + diff --git a/episode-11-Fire-Loader/models/__pycache__/__init__.cpython-39.pyc b/episode-11-Fire-Loader/models/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index b9d64ac..0000000 Binary files a/episode-11-Fire-Loader/models/__pycache__/__init__.cpython-39.pyc and /dev/null differ diff --git a/episode-11-Fire-Loader/models/__pycache__/button.cpython-39.pyc b/episode-11-Fire-Loader/models/__pycache__/button.cpython-39.pyc deleted file mode 100644 index 3ab734d..0000000 Binary files a/episode-11-Fire-Loader/models/__pycache__/button.cpython-39.pyc and /dev/null differ diff --git a/episode-11-Fire-Loader/models/__pycache__/cactus.cpython-39.pyc b/episode-11-Fire-Loader/models/__pycache__/cactus.cpython-39.pyc deleted file mode 100644 index 89e17ca..0000000 Binary files a/episode-11-Fire-Loader/models/__pycache__/cactus.cpython-39.pyc and /dev/null differ diff --git a/episode-11-Fire-Loader/models/__pycache__/crop.cpython-39.pyc b/episode-11-Fire-Loader/models/__pycache__/crop.cpython-39.pyc deleted file mode 100644 index df6c2ec..0000000 Binary files a/episode-11-Fire-Loader/models/__pycache__/crop.cpython-39.pyc and /dev/null differ diff --git a/episode-11-Fire-Loader/models/__pycache__/cube.cpython-39.pyc b/episode-11-Fire-Loader/models/__pycache__/cube.cpython-39.pyc deleted file mode 100644 index 8464e2c..0000000 Binary files a/episode-11-Fire-Loader/models/__pycache__/cube.cpython-39.pyc and /dev/null differ diff --git a/episode-11-Fire-Loader/models/__pycache__/door.cpython-39.pyc b/episode-11-Fire-Loader/models/__pycache__/door.cpython-39.pyc deleted file mode 100644 index 8a3202c..0000000 Binary files a/episode-11-Fire-Loader/models/__pycache__/door.cpython-39.pyc and /dev/null differ diff --git a/episode-11-Fire-Loader/models/__pycache__/fire.cpython-39.pyc b/episode-11-Fire-Loader/models/__pycache__/fire.cpython-39.pyc deleted file mode 100644 index 44a2da3..0000000 Binary files a/episode-11-Fire-Loader/models/__pycache__/fire.cpython-39.pyc and /dev/null differ diff --git a/episode-11-Fire-Loader/models/__pycache__/flat.cpython-39.pyc b/episode-11-Fire-Loader/models/__pycache__/flat.cpython-39.pyc deleted file mode 100644 index f8f0612..0000000 Binary files a/episode-11-Fire-Loader/models/__pycache__/flat.cpython-39.pyc and /dev/null differ diff --git a/episode-11-Fire-Loader/models/__pycache__/glass.cpython-39.pyc b/episode-11-Fire-Loader/models/__pycache__/glass.cpython-39.pyc deleted file mode 100644 index 59de840..0000000 Binary files a/episode-11-Fire-Loader/models/__pycache__/glass.cpython-39.pyc and /dev/null differ diff --git a/episode-11-Fire-Loader/models/__pycache__/ladder.cpython-39.pyc b/episode-11-Fire-Loader/models/__pycache__/ladder.cpython-39.pyc deleted file mode 100644 index 5ab6500..0000000 Binary files a/episode-11-Fire-Loader/models/__pycache__/ladder.cpython-39.pyc and /dev/null differ diff --git a/episode-11-Fire-Loader/models/__pycache__/leaves.cpython-39.pyc b/episode-11-Fire-Loader/models/__pycache__/leaves.cpython-39.pyc deleted file mode 100644 index 89e9ecb..0000000 Binary files a/episode-11-Fire-Loader/models/__pycache__/leaves.cpython-39.pyc and /dev/null differ diff --git a/episode-11-Fire-Loader/models/__pycache__/lever.cpython-39.pyc b/episode-11-Fire-Loader/models/__pycache__/lever.cpython-39.pyc deleted file mode 100644 index e811391..0000000 Binary files a/episode-11-Fire-Loader/models/__pycache__/lever.cpython-39.pyc and /dev/null differ diff --git a/episode-11-Fire-Loader/models/__pycache__/liquid.cpython-39.pyc b/episode-11-Fire-Loader/models/__pycache__/liquid.cpython-39.pyc deleted file mode 100644 index 525523c..0000000 Binary files a/episode-11-Fire-Loader/models/__pycache__/liquid.cpython-39.pyc and /dev/null differ diff --git a/episode-11-Fire-Loader/models/__pycache__/plant.cpython-39.pyc b/episode-11-Fire-Loader/models/__pycache__/plant.cpython-39.pyc deleted file mode 100644 index f76db35..0000000 Binary files a/episode-11-Fire-Loader/models/__pycache__/plant.cpython-39.pyc and /dev/null differ diff --git a/episode-11-Fire-Loader/models/__pycache__/pressure_plate.cpython-39.pyc b/episode-11-Fire-Loader/models/__pycache__/pressure_plate.cpython-39.pyc deleted file mode 100644 index b24d4cf..0000000 Binary files a/episode-11-Fire-Loader/models/__pycache__/pressure_plate.cpython-39.pyc and /dev/null differ diff --git a/episode-11-Fire-Loader/models/__pycache__/sign.cpython-39.pyc b/episode-11-Fire-Loader/models/__pycache__/sign.cpython-39.pyc deleted file mode 100644 index a32fb99..0000000 Binary files a/episode-11-Fire-Loader/models/__pycache__/sign.cpython-39.pyc and /dev/null differ diff --git a/episode-11-Fire-Loader/models/__pycache__/sign_post.cpython-39.pyc b/episode-11-Fire-Loader/models/__pycache__/sign_post.cpython-39.pyc deleted file mode 100644 index 216b4c4..0000000 Binary files a/episode-11-Fire-Loader/models/__pycache__/sign_post.cpython-39.pyc and /dev/null differ diff --git a/episode-11-Fire-Loader/models/__pycache__/slab.cpython-39.pyc b/episode-11-Fire-Loader/models/__pycache__/slab.cpython-39.pyc deleted file mode 100644 index 877adac..0000000 Binary files a/episode-11-Fire-Loader/models/__pycache__/slab.cpython-39.pyc and /dev/null differ diff --git a/episode-11-Fire-Loader/models/__pycache__/snow.cpython-39.pyc b/episode-11-Fire-Loader/models/__pycache__/snow.cpython-39.pyc deleted file mode 100644 index 7632d1c..0000000 Binary files a/episode-11-Fire-Loader/models/__pycache__/snow.cpython-39.pyc and /dev/null differ diff --git a/episode-11-Fire-Loader/models/__pycache__/soil.cpython-39.pyc b/episode-11-Fire-Loader/models/__pycache__/soil.cpython-39.pyc deleted file mode 100644 index 6c19668..0000000 Binary files a/episode-11-Fire-Loader/models/__pycache__/soil.cpython-39.pyc and /dev/null differ diff --git a/episode-11-Fire-Loader/models/__pycache__/stairs.cpython-39.pyc b/episode-11-Fire-Loader/models/__pycache__/stairs.cpython-39.pyc deleted file mode 100644 index 3613ffd..0000000 Binary files a/episode-11-Fire-Loader/models/__pycache__/stairs.cpython-39.pyc and /dev/null differ diff --git a/episode-11-Fire-Loader/models/__pycache__/torch.cpython-39.pyc b/episode-11-Fire-Loader/models/__pycache__/torch.cpython-39.pyc deleted file mode 100644 index 1b353f1..0000000 Binary files a/episode-11-Fire-Loader/models/__pycache__/torch.cpython-39.pyc and /dev/null differ diff --git a/episode-11-Fire-Loader/save.py b/episode-11-Fire-Loader/save.py index 646b4dc..ae87cca 100644 --- a/episode-11-Fire-Loader/save.py +++ b/episode-11-Fire-Loader/save.py @@ -23,7 +23,7 @@ def load_chunk(self, chunk_position): chunk_path = self.chunk_position_to_path(chunk_position) try: - chunk_blocks = nbt.load(chunk_path).root["Level"]["Blocks"] + chunk_blocks = nbt.load(chunk_path)["Level"]["Blocks"] except FileNotFoundError: return @@ -54,8 +54,8 @@ def save_chunk(self, chunk_position): except FileNotFoundError: chunk_data = nbt.File({"": nbt.Compound({"Level": nbt.Compound()})}) - chunk_data.root["Level"]["xPos"] = x - chunk_data.root["Level"]["zPos"] = z + chunk_data["Level"]["xPos"] = x + chunk_data["Level"]["zPos"] = z # fill the chunk file with the blocks from our chunk @@ -71,7 +71,7 @@ def save_chunk(self, chunk_position): # save the chunk file - chunk_data.root["Level"]["Blocks"] = chunk_blocks + chunk_data["Level"]["Blocks"] = chunk_blocks chunk_data.save(chunk_path, gzipped = True) def load(self): diff --git a/episode-11-Fire-Loader/subchunk.py b/episode-11-Fire-Loader/subchunk.py index f3ac8f9..b25c192 100644 --- a/episode-11-Fire-Loader/subchunk.py +++ b/episode-11-Fire-Loader/subchunk.py @@ -2,7 +2,16 @@ SUBCHUNK_HEIGHT = 4 SUBCHUNK_LENGTH = 4 -class Subchunk: +import os + +mods = [] +mods_imported = [] + +if os.path.isdir("mods"): + mods = [os.path.join("mods", f[1], "subchunk.py").replace(".py", "") for f in [[os.listdir(os.path.join("mods", d)), d] for d in os.listdir("mods") if os.path.isdir(os.path.join("mods", d))] if "subchunk.py" in f[0]] + mods_imported = [__import__(m.replace("\\", "."), fromlist=[""]) for m in mods] + +class SubchunkBaseImpl: def __init__(self, parent, subchunk_position): self.parent = parent self.world = self.parent.world @@ -33,6 +42,31 @@ def __init__(self, parent, subchunk_position): self.subchunk_width_r = SUBCHUNK_WIDTH self.subchunk_height_r = SUBCHUNK_HEIGHT self.subchunk_length_r = SUBCHUNK_LENGTH + + def add_face(self, face, pos, block_type): + x, y, z = pos + vertex_positions = block_type.vertex_positions[face].copy() + + for i in range(4): + vertex_positions[i * 3 + 0] += x + vertex_positions[i * 3 + 1] += y + vertex_positions[i * 3 + 2] += z + + self.mesh_vertex_positions.extend(vertex_positions) + + indices = [0, 1, 2, 0, 2, 3] + for i in range(6): + indices[i] += self.mesh_index_counter + + self.mesh_indices.extend(indices) + self.mesh_index_counter += 4 + + self.mesh_tex_coords.extend(block_type.tex_coords[face]) + self.mesh_shading_values.extend(block_type.shading_values[face]) + + def can_render_face(self, glass, block_number, position): + return not (self.world.is_opaque_block(position) + or (glass and self.world.get_block_number(position) == block_number)) def update_mesh(self): self.mesh_vertex_positions = [] @@ -42,26 +76,6 @@ def update_mesh(self): self.mesh_index_counter = 0 self.mesh_indices = [] - def add_face(face): - vertex_positions = block_type.vertex_positions[face].copy() - - for i in range(4): - vertex_positions[i * 3 + 0] += x - vertex_positions[i * 3 + 1] += y - vertex_positions[i * 3 + 2] += z - - self.mesh_vertex_positions.extend(vertex_positions) - - indices = [0, 1, 2, 0, 2, 3] - for i in range(6): - indices[i] += self.mesh_index_counter - - self.mesh_indices.extend(indices) - self.mesh_index_counter += 4 - - self.mesh_tex_coords.extend(block_type.tex_coords[face]) - self.mesh_shading_values.extend(block_type.shading_values[face]) - for local_x in range(SUBCHUNK_WIDTH): for local_y in range(SUBCHUNK_HEIGHT): for local_z in range(SUBCHUNK_LENGTH): @@ -74,32 +88,35 @@ def add_face(face): if block_number: block_type = self.world.block_types[block_number] - x, y, z = ( + x, y, z = pos = ( self.position[0] + local_x, self.position[1] + local_y, self.position[2] + local_z) - def can_render_face(position): - if not self.world.is_opaque_block(position): - if block_type.glass and self.world.get_block_number(position) == block_number: - return False - - return True - - return False + # if block is cube, we want it to check neighbouring blocks so that we don't uselessly render faces # if block isn't a cube, we just want to render all faces, regardless of neighbouring blocks # since the vast majority of blocks are probably anyway going to be cubes, this won't impact performance all that much; the amount of useless faces drawn is going to be minimal if block_type.is_cube: - if can_render_face((x + 1, y, z)): add_face(0) - if can_render_face((x - 1, y, z)): add_face(1) - if can_render_face((x, y + 1, z)): add_face(2) - if can_render_face((x, y - 1, z)): add_face(3) - if can_render_face((x, y, z + 1)): add_face(4) - if can_render_face((x, y, z - 1)): add_face(5) + if self.can_render_face(block_type.glass, block_number, (x + 1, y, z)): self.add_face(0, pos, block_type) + if self.can_render_face(block_type.glass, block_number, (x - 1, y, z)): self.add_face(1, pos, block_type) + if self.can_render_face(block_type.glass, block_number, (x, y + 1, z)): self.add_face(2, pos, block_type) + if self.can_render_face(block_type.glass, block_number, (x, y - 1, z)): self.add_face(3, pos, block_type) + if self.can_render_face(block_type.glass, block_number, (x, y, z + 1)): self.add_face(4, pos, block_type) + if self.can_render_face(block_type.glass, block_number, (x, y, z - 1)): self.add_face(5, pos, block_type) else: for i in range(len(block_type.vertex_positions)): - add_face(i) \ No newline at end of file + self.add_face(i, pos, block_type) + + +SubchunkMixins = [] +for module in mods_imported: + if hasattr(module, "SubchunkMixin"): + SubchunkMixins.append(module.SubchunkMixin) + print("Applying mixin to class subchunk.Subchunk") + +class Subchunk(*SubchunkMixins, SubchunkBaseImpl): + """Subchunk class that handles subregions of a chunk""" diff --git a/episode-11-Fire-Loader/texture_manager.py b/episode-11-Fire-Loader/texture_manager.py index 6cfc9fd..224c2b1 100644 --- a/episode-11-Fire-Loader/texture_manager.py +++ b/episode-11-Fire-Loader/texture_manager.py @@ -1,9 +1,16 @@ -import ctypes +import os import pyglet import pyglet.gl as gl -class Texture_manager: +mods = [] +mods_imported = [] + +if os.path.isdir("mods"): + mods = [os.path.join("mods", f[1], "texture_manager.py").replace(".py", "") for f in [[os.listdir(os.path.join("mods", d)), d] for d in os.listdir("mods") if os.path.isdir(os.path.join("mods", d))] if "texture_manager.py" in f[0]] + mods_imported = [__import__(m.replace("\\", "."), fromlist=[""]) for m in mods] + +class TextureManagerBaseImpl: def __init__(self, texture_width, texture_height, max_textures): self.texture_width = texture_width self.texture_height = texture_height @@ -39,4 +46,13 @@ def add_texture(self, texture): 0, 0, self.textures.index(texture), self.texture_width, self.texture_height, 1, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, - texture_image.get_data("RGBA", texture_image.width * 4)) \ No newline at end of file + texture_image.get_data("RGBA", texture_image.width * 4)) + +TextureManagerMixins = [] +for module in mods_imported: + if hasattr(module, "TextureManagerMixin"): + TextureManagerMixins.append(module.TextureManagerMixin) + print("Applying mixin to class texture_manager.TextureManager") + +class TextureManager(*TextureManagerMixins, TextureManagerBaseImpl): + """Texture Manager class""" \ No newline at end of file diff --git a/episode-11-Fire-Loader/world.py b/episode-11-Fire-Loader/world.py index 696fc07..33fad19 100644 --- a/episode-11-Fire-Loader/world.py +++ b/episode-11-Fire-Loader/world.py @@ -11,9 +11,18 @@ import models -class World: +import os + +mods = [] +mods_imported = [] + +if os.path.isdir("mods"): + mods = [os.path.join("mods", f[1], "world.py").replace(".py", "") for f in [[os.listdir(os.path.join("mods", d)), d] for d in os.listdir("mods") if os.path.isdir(os.path.join("mods", d))] if "world.py" in f[0]] + mods_imported = [__import__(m.replace("\\", "."), fromlist=[""]) for m in mods] + +class WorldBaseImpl: def __init__(self): - self.texture_manager = texture_manager.Texture_manager(16, 16, 256) + self.texture_manager = texture_manager.TextureManager(16, 16, 256) self.block_types = [None] # parse block type data file @@ -159,4 +168,14 @@ def try_update_chunk_at_position(chunk_position, position): def draw(self): for chunk_position in self.chunks: - self.chunks[chunk_position].draw() \ No newline at end of file + self.chunks[chunk_position].draw() + + +WorldMixins = [] +for module in mods_imported: + if hasattr(module, "WorldMixin"): + WorldMixins.append(module.WorldMixin) + print("Applying mixin to class world.World") + +class World(*WorldMixins, WorldBaseImpl): + """World class that handles the actual minecraft world""" \ No newline at end of file