diff --git a/button.py b/button.py index a77e1d9..ef07527 100644 --- a/button.py +++ b/button.py @@ -1,3 +1,11 @@ +"""Provides a `ButtonManager` for drawing and interacting with buttons. + +A `Button` has an ID, a position, and some text or a background. +Multiple buttons can be added to a ButtonManager, and then they are all +checked at once when the mouse is pressed. The button manager then returns +the ID of the button that was pressed. +""" + from typing import Callable, List, Optional from PIL import Image import math diff --git a/canvas.py b/canvas.py index 0f37eb6..1b74751 100644 --- a/canvas.py +++ b/canvas.py @@ -1,3 +1,20 @@ +"""Provides a wrapper struct that is meant to simulate a TKinter canvas. + +This canvas allows for using the same GUI/text drawing code between both +the TKinter and OpenGL backends. +It draws on a PIL image and then renders to an OpenGL texture. + +In theory any set of drawing calls to this canvas should appear identical +to the same set of drawing calls to a TKinter canvas... +...but it's not perfect. + +Due to a limitation in PIL drawing functions, this canvas does not have +an alpha channel that works properly. Instead, any pixels that are +ALPHA_COLOR are removed from the image. + +I miss the TKinter canvas. +""" + from shader import ShaderProgram import typing from PIL import Image, ImageDraw, ImageFont diff --git a/craft.py b/craft.py index 920d3f5..ef05636 100644 --- a/craft.py +++ b/craft.py @@ -1,3 +1,35 @@ +"""The main entry point of the app. + +All of the different parts of the game are divided into "Modes". +Any events that occur are delegated to the current mode to be handled. +The modes act like an FSM. For example, pressing 'E' in `PlayingMode` will +result in a transition to the `InventoryMode` state. + +# WorldLoadMode # +Displays a loading screen while a World is generated or loaded from disk. + +# WorldListMode # +Displays a list of the currently saved worlds and allows the player to +select one or create a new world. + +# CreateWorldMode # +Displays various options the player can set to create a new world. + +# TitleMode # +Used at the title screen. Not very exciting. + +# ChatMode # +Used when the chat box is opened during gameplay. + +# PlayingMode # +The 'normal' state of gameplay. The player can walk, look around, etc. + +# InventoryMode # +Used when the player has an inventory window opened. This can include +when the player right clicks on, for example, a furnace or a crafting table. +This mode keeps a reference to what kind of GUI is opened. +""" + import openglapp from PIL import Image from PIL import ImageDraw diff --git a/entity.py b/entity.py index 9018c07..316a0c4 100644 --- a/entity.py +++ b/entity.py @@ -1,4 +1,17 @@ -from os import X_OK +"""Rendering, representation, and behavior of entities is handled here. + +Entity models are made up of bones. Each bone is individually posable. +Each bone is made up of cubes, which stay in a fixed arrangement. + +Every entity has a `kind`, which determines its attributes and behavior. + +For behavior, an `Ai` is used. This simply stores a list of `Task`s. +Tasks run every tick until they are interrupted by a task with a higher +priority, or when they mark themselves as finished. +For example, most entities have a `WanderTask` with low priority, so when +they do not have any other goals, they will occasionally move around. +""" + from typing import List, Tuple, Optional, Any import json import numpy as np @@ -346,8 +359,6 @@ def __init__(self, app, kind: str, x: float, y: float, z: float): self.kind = app.entityKinds[kind] - data: EntityKind = app.entityKinds - self.radius = self.kind.radius self.height = self.kind.height self.walkSpeed = self.kind.walkSpeed @@ -355,7 +366,6 @@ def __init__(self, app, kind: str, x: float, y: float, z: float): self.ai = copy.deepcopy(self.kind.ai) def hit(self, damage: float, knockback: Tuple[float, float]): - print("got hit") if self.immunity == 0: self.health -= damage @@ -587,20 +597,23 @@ def makePathFromChain(prevDirs, end: BlockPos) -> List[BlockPos]: return result -def findPath(start: BlockPos, end: BlockPos, world) -> Optional[List[BlockPos]]: - print(f"Start pos: {start} End pos: {end}") +def approxDistance(start: BlockPos, end: BlockPos): + # Chebyshev distance + + xDist = abs(start.x - end.x) + yDist = abs(start.y - end.y) + zDist = abs(start.z - end.z) - def heuristic(start: BlockPos, end: BlockPos): - # Chebyshev distance + return (max(xDist, zDist) + yDist) - xDist = abs(start.x - end.x) - yDist = abs(start.y - end.y) - zDist = abs(start.z - end.z) - return (max(xDist, zDist) + yDist) +def findPath(start: BlockPos, end: BlockPos, world, maxDist=1) -> Optional[List[BlockPos]]: + print(f"Start pos: {start} End pos: {end}") # https://en.wikipedia.org/wiki/A*_search_algorithm + heuristic = approxDistance + prevDirs = dict() realCosts = { start: 0 } @@ -622,7 +635,7 @@ def heuristic(start: BlockPos, end: BlockPos): openSet.remove(current) - if current == end: + if heuristic(current, end) <= maxDist: return makePathFromChain(prevDirs, end) for nextPos in destinations(current, world): diff --git a/openglapp.py b/openglapp.py index a7e73e5..346e488 100644 --- a/openglapp.py +++ b/openglapp.py @@ -1,3 +1,5 @@ +"""This simulates the events and functionality of `cmu_112_graphics`.""" + import glfw import inspect import time @@ -42,6 +44,7 @@ def __init__(self, width=300, height=300): self.run() + # From cmu_112_graphics def _callFn(self, fn, *args): if (fn in self._callersGlobals): self._callersGlobals[fn](*args) diff --git a/perlin.py b/perlin.py index fca1cda..dba8c67 100644 --- a/perlin.py +++ b/perlin.py @@ -1,3 +1,10 @@ +"""This module is used for sampling perlin noise. + +getPerlinNoise returns a single level of the noise. +getPerlinFractal returns several layers of noise added together, which is +used for the height of terrain during worldgen. +""" + import random, math from functools import lru_cache diff --git a/player.py b/player.py index 267f0bd..b8c6123 100644 --- a/player.py +++ b/player.py @@ -1,3 +1,15 @@ +"""Simply represents the player and their inventory. + +Slot *should* probably be titled `ItemStack` but I haven't gotten +around to that yet. It represents an item and an amount of that item. + +Most of the player's behavior actually happens in `craft.py`, +which I may move in the future. + +A player is simply an `Entity` with a few extra attributes, so there's +not that much too this module. +""" + from entity import Entity from typing import List, Optional from dataclasses import dataclass @@ -33,10 +45,6 @@ def tryMergeWith(self, other: 'Slot') -> Optional['Slot']: return None class Player(Entity): - height: float - radius: float - - walkSpeed: float reach: float hotbarIdx: int = 0 diff --git a/profilecraft.py b/profilecraft.py index 1a82d49..89ac063 100644 --- a/profilecraft.py +++ b/profilecraft.py @@ -1,3 +1,5 @@ +"""A simple shim script to profile 112craft""" + import cProfile import craft import pstats diff --git a/render.py b/render.py index a1ca90b..e97f6ff 100644 --- a/render.py +++ b/render.py @@ -1,3 +1,15 @@ +"""This module does all of the drawing for both backends. + +In particualar, it has: +- View and camera matrix calculation +- The entire process of drawing models +- Hidden surface/view frustrum/back face culling +- GUI drawing helper functions + +This module does NOT handle mesh/buffer creation. Those are created in +the modules where they are needed. This one is only for drawing them. +""" + import math import time from tkinter.constants import X diff --git a/resources.py b/resources.py index 563c90a..36cadb9 100644 --- a/resources.py +++ b/resources.py @@ -1,3 +1,9 @@ +"""This module is used to manage most of the game's assets. + +Textures, block attributes, mob kinds, etc. are all loaded into the `app`. +This also creates the texture atlas used for rendering chunks. +""" + import numpy as np import world import render diff --git a/shader.py b/shader.py index f0c3ffe..844f709 100644 --- a/shader.py +++ b/shader.py @@ -1,3 +1,5 @@ +"""This module represents a vertex+fragment shader pair.""" + from OpenGL.GL import * #type:ignore def readFile(path): diff --git a/tick.py b/tick.py index 6f4f7fe..287141c 100644 --- a/tick.py +++ b/tick.py @@ -1,3 +1,9 @@ +"""Provides the `tick` function, which does all of the game's updates. + +At regular intervals, the AI of all entities are processed, gravity is applied, +new entities are spawned, other entities are removed, collisions occur, etc. +""" + from entity import Entity from util import BlockPos, roundHalfUp, rayAABBIntersect import world diff --git a/util.py b/util.py index c279208..25b56b8 100644 --- a/util.py +++ b/util.py @@ -1,4 +1,7 @@ -"""Types and functions useful for the whole codebase.""" +"""Various types and functions useful for the whole codebase. + +Very important are BlockPos, ChunkPos, BlockId, and ItemId. +""" import decimal from typing import NamedTuple, Tuple, Optional