#ifdef WIN32 // FUCK WINDOWS
    #include "external/fix_win32_compatibility.h"
    #undef PlaySound
#endif

#include <enet/enet.h>

#ifdef WIN32 // FUCK WINDOWS 2
    #undef PlaySound
    #undef Sound
#endif
#include "glfw_keycodes_to_string.h"
#include "../PerlinNoise.hpp"
#include "raylib.h"
#include "raymath.h"
#include <stdint.h>
#include <bitset>
#include <cstdint>
#include <list>
#define FOV 90
#define WIDTH 1024.0
#define HEIGHT 600.0
#define ASPECT WIDTH / HEIGHT
#define CHUNK_SIZE 16 // W & H
#define WORLD_HEIGHT 128
#define CHUNK_DATA_SIZE CHUNK_SIZE*CHUNK_SIZE*WORLD_HEIGHT
#define CHUNK_SIZE_SQR CHUNK_SIZE*CHUNK_SIZE
#define MAX_WORLD_SIZE 16
#define MAX_WORLD_AREA MAX_WORLD_SIZE*MAX_WORLD_SIZE
#define WORLD_SIZE_BLOCKS MAX_WORLD_SIZE * CHUNK_SIZE
#define ATLAS_SIZE_W 16
#define ATLAS_SIZE_H 16
#define ITEM_ATLAS_SIZE 16
#define ITEM_SIZE 16
#define PLAYER_HEIGHT 1.75f
#define PLAYER_RADIUS 0.25f
#define GRAVITY 20.0f
#define MOVE_SPEED 6.0f
#define JUMP_SPEED 8.0f
#define DELTA_SMOOTH 0.016f
#define MOUSE_SENS 0.0025f
#define PITCH_MIN -1.47f     // ~-83 degrees
#define PITCH_MAX 1.47f     // ~ 83 degrees
#define SMOOTH_FACTOR 12.0f  // larger = snappier
#define BLOCK_AIR 0
#define BLOCK_STONE 1
#define BLOCK_GRASS 2
#define BLOCK_DIRT 3
#define BLOCK_SAND 4
#define BLOCK_WATERS 5
#define MAX_BLOCK_ID 28
#define CLOUD_FADE_BLOCKS CHUNK_SIZE*8
#define PORT 25565
#define MAX_MESSAGE_LENGTH 64
#define NET_ARG_NONE        0x00 // Invalid
#define NET_ARG_CHUNKF      0x01 // Byte 1 - chunk ID, uint8_t blocks[16*64*16], uint8_t highestBlock[16*16]
#define NET_ARG_BLOCK       0x02 // Player ID, block X, block Y, block Z, block type
#define NET_ARG_PLRCON      0x03 // Byte 1 - ID
#define NET_ARG_PLRDCN      0x04 // Byte 1 - ID
#define NET_ARG_PLRID       0x05 // Byte 1 - Server-assigned ID to the player
#define NET_ARG_ECHOMOVE    0x06 // Byte 1 - player ID, bytes 2-3 - echoed player X, bytes 4-5 - echoed player Y, bytes 6-7 - echoed player Z, byte 8 - player yaw
#define NET_ARG_PLRREG      0x07 // Same as ECHOMOVE, used as an answer to REQPLAYERS
#define NET_ARG_ECHOYAW     0x08 // Byte 1 - ID, byte 2 - yaw
#define NET_ARG_MESSAGEE    0x09 // Byte 1 - sender ID, byte 2 - length, rest is message (128b max)
#define NET_ARG_PLAYERMOVE  0x80 // Byte 1-2 - player X, byte 3-4 - player Y, byte 5-6 - player Z, byte 7 - player yaw
#define NET_ARG_BLOCKDELTA  0x81 // Block X, block Y, block Z, block type
#define NET_ARG_REQWORLD    0x82 // No additional data
#define NET_ARG_REQPLAYERS  0x83 // No additional data
#define NET_ARG_PLAYERYAW   0x84 // Byte 1 - yaw
#define NET_ARG_MESSAGEC    0x85 // All is message (64b max)
#define NET_ERROR_DSCN      0x00
#define NET_ERROR_NOTFOUND  0x01
#define NET_ERROR_FAILED    0x02
#define NET_ERROR_INTERNAL  0x03
#define GAME_NAME "CubeGame"
#define GAME_VERSION_INT 0
#define DEFAULT_FONT_SIZE 16
#define TPS 30.0
#define TPS_DIV 1.0 / TPS
#define RENDER_DISTANCE 16
#define RENDER_DISTANCE_SQUARED RENDER_DISTANCE*RENDER_DISTANCE
#define MAX_HP 100
#define PATH_APPEND "resources/"
#define PATH_APPEND_USER "user/"
#define ITEMS 9
#define INVENTORY_WIDTH 8
#define INVENTORY_HEIGHT 6
#define INVENTORY_SIZE INVENTORY_WIDTH*INVENTORY_HEIGHT
enum BlockType {
    B_Wood,
    B_Stone,
    B_Grass,
    B_Dirt
};
typedef enum BlockType BlockType;
typedef struct BlockDef BlockDef;
enum BlockRendering {
    R_None,
    R_Normal,
    R_TriSide,
    R_Translucent,
    R_TranslucentTriSide,
    R_TranslucentAllSides
};
typedef enum BlockRendering BlockRendering;
struct BlockDef {
    const std::string name;
    BlockRendering render;
    uint8_t textureTop;
    uint8_t textureSide;
    uint8_t textureBottom;
    int hardness;
    int min_tier;
    BlockType type;
    // Constructor with initializer list; textureSide/textureBottom default to textureTop if not provided.
    BlockDef(
             std::string _name,
             BlockRendering render_ = R_None,
             BlockType btype = B_Stone,
             int _hardness = 1,
             int _min_tier = 0,
             uint8_t textureTop_ = 255,
             uint8_t textureSide_ = 255,
             uint8_t textureBottom_ = 255)
        : name(_name),
          render(render_),
          hardness(_hardness),
          min_tier(_min_tier),
          textureTop(textureTop_),
          textureSide(textureSide_ == 255 ? textureTop_ : textureSide_),
          textureBottom(textureBottom_ == 255 ? textureTop_ : textureBottom_),
          type(btype)
    {}
};
enum GameState {
    GAME_STATE_LOADING,
    GAME_STATE_GAME,
    GAME_STATE_MAINMENU,
    GAME_STATE_WORLDSELECT,
    GAME_STATE_MULTIPLAYERSELECT,
    GAME_STATE_TRANSITION,
    GAME_STATE_WORLD_CREATE,
    GAME_STATE_OPTIONS
};
typedef enum GameState GameState;
typedef struct Chunk Chunk;
struct Chunk {
    uint8_t x;
    uint8_t z;
    bool isDirty;
    uint8_t blocks[CHUNK_DATA_SIZE];
    uint16_t opaqueMask[CHUNK_DATA_SIZE/16];
    uint16_t airMask[CHUNK_DATA_SIZE/16];
    uint8_t highestBlock[CHUNK_SIZE*CHUNK_SIZE];
    
};
#if !defined(SERVER)
typedef struct ChunkRenderData ChunkRenderData;
struct ChunkRenderData {
    uint8_t sides[CHUNK_DATA_SIZE];
};
typedef struct MeshData MeshData;
struct MeshData {
    float *positions;   // xyz xyz ...
    float *normals;     // xyz ...
    float *uvs;         // uv uv ...
    unsigned char *colors; // rgba rgba ...
    unsigned short *indices;  // triangles
    unsigned short vertexCount;
    unsigned short indexCount;
};
typedef struct ChunkMeshData ChunkMeshData;
struct ChunkMeshData {
    uint8_t *positions;   // xyz xyz ...
    uint8_t *luminosity; // l l ...
    float *uvs;         // uv uv ...
    unsigned short *indices;  // triangles
    unsigned short vertexCount;
    unsigned short indexCount;
};
typedef struct UVCorners UVCorners;
struct UVCorners {
    Vector2 corner0;
    Vector2 corner1;
    Vector2 corner2;
    Vector2 corner3;
};
typedef struct Quad2D Quad2D;
struct Quad2D {
    int x;
    int y;
    int w;
    int h;
};
#endif
typedef struct Vector3I Vector3I;
struct Vector3I {
    int x;
    int y;
    int z;
};
typedef struct Entity Entity;
struct Entity {
    uint8_t type;
    uint8_t id;
    uint8_t damage_flash;
    uint8_t data[16];
    float smoothing;
    float health;
    float lifetime;
    Vector3 position;
    Vector3 velocity;
    unsigned short yaw;
    bool free;
};
typedef struct NetPlayer NetPlayer;
struct NetPlayer {
    ENetPeer* peer;
    int addr;
    int port;
    int id;
    int yaw;
    float x;
    float y;
    float z;
};
typedef struct MemInfo MemInfo;
struct MemInfo {
    uint64_t resident;
    uint64_t virtualSize;
    uint64_t privateBytes;
};
enum ItemType {
    IType_Generic,
    IType_Block,
    IType_Edible,
    IType_Mining,
    IType_Weapon,
    IType_Special_Tag
};
typedef enum ItemType ItemType;
enum EntityPhysics {
    Phys_Falling,
    Phys_Floating
};
typedef enum EntityPhysics EntityPhysics;
typedef struct Item Item;
struct Item {
    std::string id;
    ItemType type;
    int texture;
    int tier;
    Item(
            std::string _id,
            ItemType _type,
            int _texture,
            int _tier = 0)
        : id(_id),
        type(_type),
        texture(_texture),
        tier(_tier)
    {}
};
typedef struct InventoryItem InventoryItem;
struct InventoryItem {
    Item* item = nullptr;
    int damage = -1;
    int amount = 0;
};
typedef struct RecipeItem RecipeItem;
struct RecipeItem {
    ItemType type;
    std::string id;
    int quantity = 1;
};
typedef struct Recipe Recipe;
struct Recipe {
    std::string requiredStation;
    std::list<RecipeItem> inputs;
    std::list<RecipeItem> outputs;
};
typedef struct Particle Particle;
struct Particle {
    Vector3 position;
    Vector3 velocity;
    EntityPhysics physicsType;
    Texture2D texture;
    int lifetime;
    int data[8];
};
extern const BlockDef BLOCKS[];
bool CanTick(int blockType);
bool GetCloud(int x,int z);
bool IsTranslucent(int blockType);
void SetBlockChunk(Chunk *c,int x,int y,int z,int t);
void RebuildHeightmap(Chunk *c);
int GetBiome(int sx,int sy);
void SetBlockChunkFast(Chunk *c,int i,int t);
void SetBlock(Chunk world[],int x,int y,int z,int t);
void InitWorldgen(int worldType);
Vector3 WorldPosition(int cx,int cz,int localX,int y,int localZ);
Chunk GenerateChunk(int _x,int _y);
void GenerateWorldAdditional(Chunk world[]);
void TickBlock(Chunk world[],int wx,int wy,int wz);
void TickChunk(Chunk *chunk,Chunk *world,int wx,int wz);
void RebuildOpaqueMask(Chunk *chunk);
#if !defined(SERVER)
ChunkRenderData GenerateCRDOpaque(const Chunk&chunk,Chunk world[]);
ChunkRenderData GenerateCRDTranslucent(Chunk chunk,Chunk world[]);
#endif
void SaveWorld(Chunk world[],const char *path);
bool LoadWorld(Chunk world[],const char *path);
extern long nsTotal;
void Debug_StartMeasure();
#if defined(WIN32)
bool Debug_GetProcessMemory(MemInfo&out);
#endif
#if defined(PLAYSTATION2)
bool Debug_GetProcessMemory(MemInfo&out);
#endif
#if !defined(WIN32)
bool Debug_GetProcessMemory(MemInfo&out);
#endif
#if !defined(SERVER)
extern Camera camera;
#endif
extern Camera3D camera;
#if !defined(SERVER)
extern Camera camera;
extern Font fnt;
extern Font fnt;
extern Texture2D terrain;
#endif
extern Texture2D terrain;
#if !defined(SERVER)
extern Texture2D terrain;
extern Texture2D terrain;
extern Texture2D playerTex;
extern Texture2D playerTex;
extern Texture2D bomb;
extern Sound nekit_idle;
void EntitiesInit();
extern bool online;
extern bool online;
#endif
#if !(!defined(SERVER))
extern bool online;
#endif
#if !defined(SERVER)
extern Vector3 player_position;
#endif
extern Vector3 player_position;
#if !defined(SERVER)
extern Vector3 player_velocity;
#endif
extern Vector3 player_velocity;
#if !defined(SERVER)
void TakeDamage(float amount);
void SetBlockNetwork(Chunk world[],int bx,int by,int bz,int t);
#endif
Entity NewEntity();
void AddEntity(Entity ent);
void UpdateEntities(Chunk world[],float dt);
extern float globalCounter;
#if !defined(SERVER)
void DrawEntities(float dt);
extern bool swap_mouse;
bool InputGetLMB();
bool InputGetLMBDown();
bool InputGetRMB();
Vector2 InputGetWalkAxis();
Vector2 InputGetLookAxis();
Vector2 InputGetDPAD();
void InputWriteControls();
void InputInit();
void InputUpdate();
extern int currentlySelected;
extern GameState state;
extern Texture2D cubemap;
int TryConnect(const char *hostname);
void GameModeCleanup();
void DrawBackground();
void InitMenu();
void DrawTextB(const char *text,int posX,int posY,int fontSize,Color color);
bool Button(const char *text,int x,int y,int width,int id);
bool ButtonInventory(int x,int y,int width,int height);
extern char buffer[64];
bool TextField(int id,char *buffer,int bufferSize,int x,int y,int width);
extern const char *error;
void UIBegin();
void UIResetMousePos();
void UIHandleControls();
void DrawControls();
void DrawMainMenu();
void _LoadWorld(const char *path);
void DrawWorldSelect();
void DrawMultiplayer();
void DrawPauseMenu();
extern char bufferChat[MAX_MESSAGE_LENGTH];
char *DrawChat();
void DrawLogoCenter();
#endif
#if !defined(LEGACY_GL) && !defined(SERVER)
MeshData GenerateChunkMesh(ChunkRenderData chunkRenderData,Chunk chunk,int chunkWorldX,int chunkWorldY,uint8_t sideMask);
MeshData MergeMeshData(const MeshData&a,const MeshData&b);
MeshData CreateCubeMeshData(const BlockDef&def,float cx,float cy,float cz);
Mesh GenChunkMesh(MeshData meshData);
#endif
int NetworkingStart();
ENetHost *StartServer();
ENetHost *StartClient();
void SendPacket(ENetPeer *peer,void *data,int len);
void SendPacketR(ENetPeer *peer,void *data,int len);
void BroadcastPacketR(ENetHost *server,void *data,int len);
void SerializeFloat2Data(float v,void *target,int offset);
float DeserializeFloat(int b0,int b1);
const char *Pathify(const char *src);
const char *PathifyUser(const char *src);
#if !defined(SERVER)
void DrawCubeTexture(Texture2D texture,Vector3 position,float width,float height,float length,Color color,int offsetX,int offsetY,int sizeX,int sizeY);
void DrawCubeBlock(Texture2D texture,Vector3 position,float width,float height,float length,BlockDef def);
void DrawTextCodepoint3D(Font font,int codepoint,Vector3 position,float fontSize,bool backface,Color tint);
void DrawText3D(Font font,const char *text,Vector3 position,float fontSize,float fontSpacing,float lineSpacing,bool backface,Color tint);
#endif
inline uint8_t pack6(bool b0,bool b1,bool b2,bool b3,bool b4,bool b5){
    return (uint8_t)(
        ((uint8_t)(b0 ? 1 : 0) << 0) |
        ((uint8_t)(b1 ? 1 : 0) << 1) |
        ((uint8_t)(b2 ? 1 : 0) << 2) |
        ((uint8_t)(b3 ? 1 : 0) << 3) |
        ((uint8_t)(b4 ? 1 : 0) << 4) |
        ((uint8_t)(b5 ? 1 : 0) << 5)
    );
};
inline void unpack6(uint8_t v,bool *b0,bool *b1,bool *b2,bool *b3,bool *b4,bool *b5){
    *b0 = (v >> 0) & 1;
    *b1 = (v >> 1) & 1;
    *b2 = (v >> 2) & 1;
    *b3 = (v >> 3) & 1;
    *b4 = (v >> 4) & 1;
    *b5 = (v >> 5) & 1;
};
inline void GetXYZFromIndex(int index,int *x,int *y,int *z){
    *x = index & 15;
    int tmp = index << 4;
    *z = tmp & 15;
    *y = tmp << 4;
};
inline int GetIndexChunk(int x,int y,int z){
    if(x < 0 || y < 0 || z < 0 || x >= CHUNK_SIZE || y >= WORLD_HEIGHT || z >= CHUNK_SIZE) return 0;
    return x + CHUNK_SIZE * (z + CHUNK_SIZE* y);
};
inline int GetIndexChunk2D(int x,int z){
    if(x < 0 || z < 0 || x >= CHUNK_SIZE || z >= CHUNK_SIZE) return 0;
    return x + CHUNK_SIZE * z;
};
inline int GetIndexWorld(int x,int z){
    return x + MAX_WORLD_SIZE * z;
};
inline void GetXZFromIndex(int index,int *x,int *z){
    *z = index / MAX_WORLD_SIZE;
    *x = index % MAX_WORLD_SIZE;
};
inline bool TestMask(uint16_t *maskArray,int position){
    int index = position >> 4;
    int bit = position & 15;
    int maskArrayLen = CHUNK_DATA_SIZE << 4;
    return (maskArray[index] & (1u << bit)) != 0;
};
inline bool CheckOOBWorld(int x,int y,int z){
    return (x < 0 || y < 0 || z < 0 || x >= WORLD_SIZE_BLOCKS || y >= WORLD_HEIGHT || z >= WORLD_SIZE_BLOCKS);
};
inline bool CheckOOBChunk(int x,int y,int z){
    return (x < 0 || y < 0 || z < 0 || x >= CHUNK_SIZE || y >= WORLD_HEIGHT || z >= CHUNK_SIZE);
};
inline bool TestOpaqueMaskWorld(Chunk world[],int x,int y,int z){
    if(CheckOOBWorld(x, y, z)) return true;
    Chunk* c = &world[GetIndexWorld(x >> 4, z >> 4)];
    int localIndex = GetIndexChunk((uint8_t)x & 15, y, (uint8_t)z & 15);
    return TestMask(c->opaqueMask, localIndex);
};
inline bool TestAirMaskWorld(Chunk world[],int x,int y,int z){
    if(CheckOOBWorld(x, y, z)) return true;
    Chunk* c = &world[GetIndexWorld(x >> 4, z >> 4)];
    int localIndex = GetIndexChunk((uint8_t)x & 15, y, (uint8_t)z & 15);
    return TestMask(c->airMask, localIndex);
};
inline int fetch_block(Chunk chunk,int x,int y,int z){
    if(x<0||x>=CHUNK_SIZE||y<0||y>=WORLD_HEIGHT||z<0||z>=CHUNK_SIZE) return 0;
    return chunk.blocks[GetIndexChunk(x, y, z)];
};
inline int fetch_block_ptr(Chunk *chunk,int x,int y,int z){
    if(x<0||x>=CHUNK_SIZE||y<0||y>=WORLD_HEIGHT||z<0||z>=CHUNK_SIZE) return 0;
    return chunk->blocks[GetIndexChunk(x, y, z)];
};
inline int GetBlock(Chunk world[],int x,int y,int z){
    if(CheckOOBWorld(x, y, z)) return 0;
    int locX = x % CHUNK_SIZE;
    int locZ = z % CHUNK_SIZE;
    int cx = x / CHUNK_SIZE;
    int cz = z / CHUNK_SIZE;
    return fetch_block(world[GetIndexWorld(cx, cz)], locX, y, locZ);
};
inline void *grow(void *ptr,size_t oldBytes,size_t newBytes){
    if (!ptr) return malloc(newBytes);
    return realloc(ptr, newBytes);
};
inline void pushFloats(float *&buf,int&count,const float *vals,int n){
    for (int i=0;i<n;i++) buf[count+i] = vals[i];
    count += n;
};
inline void pushU8(unsigned char *&buf,int&count,const unsigned char *vals,int n){
    for (int i=0;i<n;i++) buf[count+i] = vals[i];
    count += n;
};
inline void pushU32(uint32_t *&buf,int&count,const uint32_t *vals,int n){
    for (int i=0;i<n;i++) buf[count+i] = vals[i];
    count += n;
};
inline void pushU16(unsigned short *&buf,int&count,const unsigned short *vals,int n){
    for (int i=0;i<n;i++) buf[count+i] = vals[i];
    count += n;
};
#if !defined(SERVER)
inline UVCorners GetUVTex(int texture){
    const float uv_mod_x = 1.0f / static_cast<float>(ATLAS_SIZE_W);
    const float uv_mod_y = 1.0f / static_cast<float>(ATLAS_SIZE_H);
    const float uv_x = (texture % ATLAS_SIZE_W) * uv_mod_x;
    const float uv_y = (texture / ATLAS_SIZE_W) * uv_mod_y;
    UVCorners corners = { 0 };
    corners.corner2 = Vector2{uv_x,uv_y};
    corners.corner3 = Vector2{uv_x+uv_mod_x,uv_y};
    corners.corner0 = Vector2{uv_x+uv_mod_x,uv_y+uv_mod_y};
    corners.corner1 = Vector2{uv_x,uv_y+uv_mod_y};
    return corners;
};
inline UVCorners GetUVTex(int w,int h,int offsetX,int offsetY,int sizeX,int sizeY){
    const float uv_mod_x = 1.0f / static_cast<float>(w);
    const float uv_mod_y = 1.0f / static_cast<float>(h);
    const float uv_x = offsetX * uv_mod_x;
    const float uv_y = offsetY * uv_mod_y;
    const float uvsx = sizeX * uv_mod_x;
    const float uvsy = sizeY * uv_mod_y;
    UVCorners corners = { 0 };
    corners.corner2 = Vector2{uv_x,uv_y};
    corners.corner3 = Vector2{uv_x+uvsx,uv_y};
    corners.corner0 = Vector2{uv_x+uvsx,uv_y+uvsy};
    corners.corner1 = Vector2{uv_x,uv_y+uvsy};
    return corners;
};
inline UVCorners GetUVTex(int texture,float offsetX,float offsetY,float sizeModX,float sizeModY){
    const float uv_mod_x = 1.0f / static_cast<float>(ATLAS_SIZE_W);
    const float uv_mod_y = 1.0f / static_cast<float>(ATLAS_SIZE_H);
    const float uv_mod1_x = sizeModX / static_cast<float>(ATLAS_SIZE_W);
    const float uv_mod1_y = sizeModY / static_cast<float>(ATLAS_SIZE_H);
    const float uv_off_x = offsetX / static_cast<float>(ATLAS_SIZE_W);
    const float uv_off_y = offsetY / static_cast<float>(ATLAS_SIZE_H);
    const float uv_x = (texture % ATLAS_SIZE_W) * uv_mod_x;
    const float uv_y = (texture / ATLAS_SIZE_W) * uv_mod_y;
    UVCorners corners = { 0 };
    corners.corner2 = Vector2{uv_x+uv_off_x,uv_y+uv_off_y};
    corners.corner3 = Vector2{uv_x+uv_off_x+uv_mod1_x,uv_y+uv_off_y};
    corners.corner0 = Vector2{uv_x+uv_off_x+uv_mod1_x,uv_y+uv_off_y+uv_mod1_y};
    corners.corner1 = Vector2{uv_x+uv_off_x,uv_y+uv_off_y+uv_mod1_y};
    return corners;
};
inline UVCorners GetUVTexFlipX(int w,int h,int offsetX,int offsetY,int sizeX,int sizeY){
    const float uv_mod_x = 1.0f / static_cast<float>(w);
    const float uv_mod_y = 1.0f / static_cast<float>(h);
    const float uv_x = offsetX * uv_mod_x;
    const float uv_y = offsetY * uv_mod_y;
    const float uvsx = sizeX * uv_mod_x;
    const float uvsy = sizeY * uv_mod_y;
    UVCorners corners = { 0 };
    corners.corner2 = Vector2{uv_x+uvsx,uv_y};
    corners.corner3 = Vector2{uv_x,uv_y};
    corners.corner0 = Vector2{uv_x,uv_y+uvsy};
    corners.corner1 = Vector2{uv_x+uvsx,uv_y+uvsy};
    return corners;
};
extern InventoryItem hotbar[ITEMS];
#endif
extern InventoryItem hotbar[ITEMS];
#if !defined(SERVER)
extern InventoryItem inventory[INVENTORY_SIZE];
#endif
extern InventoryItem inventory[INVENTORY_SIZE];
#if !defined(SERVER)
extern bool inventoryOpen;
extern InventoryItem *movingItem;
void DrawItem(InventoryItem item,int x,int y);
void InventoryRemoveItem(int slot);
void HotbarRemoveItem(int slot);
void HotbarRemoveSelected();
void HotbarAddItem(Item *registry,int damage);
void HotbarAddItemLots(Item *registry,int damage,int amount);
void InitHotbar();
void DrawHotbar();
InventoryItem HotbarGetSelected();
void DrawSelectedItem();
void CloseInventory();
void UpdateHotbar();
void LocaleUpdate();
void LoadRecipes();
#endif
void ParticlesUpdate(float dt,Chunk world[]);
void ParticlesDraw();
void AddParticle(Particle part);
Particle NewParticle(Vector3 position);
extern bool creative_mode;
extern float player_health;
extern float player_attack_time;
extern float player_place_time;
extern float player_break_parts_time;
void SavePlayerData();
bool LoadPlayerData();
void MS_PlaySound(Sound src,float pitch,float volume,Vector3 position);
void MS_Update(float dt,Camera camera);
void ServerNetworkUpdate();
void StopInternalServer();
#define INTERFACE 0
#define EXPORT_INTERFACE 0
#define LOCAL_INTERFACE 0
#define EXPORT
#define LOCAL static
#define PUBLIC
#define PRIVATE
#define PROTECTED
